diff --git a/bin/dm-get-proposal b/bin/dm-get-proposal
new file mode 100755
index 0000000000000000000000000000000000000000..0d4065ca0edb3798b06ee489e409a332afb749d7
--- /dev/null
+++ b/bin/dm-get-proposal
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+# Run command
+
+if [ -z $DM_ROOT_DIR ]; then
+    cd `dirname $0` && myDir=`pwd`
+    setupFile=$myDir/../setup.sh
+    if [ ! -f $setupFile ]; then
+        echo "Cannot find setup file: $setupFile"
+        exit 1
+    fi
+    source $setupFile > /dev/null
+fi
+
+$DM_ROOT_DIR/src/python/dm/aps_bss/cli/getProposalCli.py $@
+
+
diff --git a/bin/dm-list-proposals b/bin/dm-list-proposals
new file mode 100755
index 0000000000000000000000000000000000000000..48c8a6dfcd2e56f0d8f3615c66c7d2a7d0850f8e
--- /dev/null
+++ b/bin/dm-list-proposals
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+# Run command
+
+if [ -z $DM_ROOT_DIR ]; then
+    cd `dirname $0` && myDir=`pwd`
+    setupFile=$myDir/../setup.sh
+    if [ ! -f $setupFile ]; then
+        echo "Cannot find setup file: $setupFile"
+        exit 1
+    fi
+    source $setupFile > /dev/null
+fi
+
+$DM_ROOT_DIR/src/python/dm/aps_bss/cli/listProposalsCli.py $@
+
+
diff --git a/bin/dm-list-runs b/bin/dm-list-runs
new file mode 100755
index 0000000000000000000000000000000000000000..fd86d9edc206e2da84ce51b6b6484e63e2b3da0e
--- /dev/null
+++ b/bin/dm-list-runs
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+# Run command
+
+if [ -z $DM_ROOT_DIR ]; then
+    cd `dirname $0` && myDir=`pwd`
+    setupFile=$myDir/../setup.sh
+    if [ ! -f $setupFile ]; then
+        echo "Cannot find setup file: $setupFile"
+        exit 1
+    fi
+    source $setupFile > /dev/null
+fi
+
+$DM_ROOT_DIR/src/python/dm/aps_bss/cli/listRunsCli.py $@
+
+
diff --git a/src/python/dm/aps_bss/api/apsBssApi.py b/src/python/dm/aps_bss/api/apsBssApi.py
index 7a9f8e63ca84c287975ba9cdc26e622bf489490c..fb87f28c963292f0ebe99c4236db02d1e404d485 100755
--- a/src/python/dm/aps_bss/api/apsBssApi.py
+++ b/src/python/dm/aps_bss/api/apsBssApi.py
@@ -7,17 +7,20 @@ from dm.aps_bss.impl.bssClient import BssClient
 
 class ApsBssApi(DmApi):
     ''' Data Management API for APS Beamline Scheduling System. '''
-    def __init__(self, beamlineName=None, username=None, password=None):
+    def __init__(self, beamlineName=None, username=None, password=None, loginFile=None):
         '''
         Constructor.
 
         :param beamlineName: beamline name (if not provided, environment variable DM_BEAMLINE_NAME must be set)
         :type beamlineName: str
 
-        :param username: BSS username (if not provided, environment variable DM_BSS_LOGIN_FILE must be set)
+        :param username: BSS username (if not provided, loginFile must be specified, or environment variable DM_BSS_LOGIN_FILE must be set)
         :type username: str
 
-        :param password: BSS password (if not provided, environment variable DM_BSS_LOGIN_FILE must be set)
+        :param password: BSS password (if not provided, loginFile must be specified, environment variable DM_BSS_LOGIN_FILE must be set)
+        :type password: str
+
+        :param loginFile: BSS login file (not used if username/password are provided; it can be replaced by environment variable DM_BSS_LOGIN_FILE)
         :type password: str
 
         :raises ConfigurationError: in case beamline name or username/password are not provided, and corresponding environment variables are not set
@@ -30,7 +33,8 @@ class ApsBssApi(DmApi):
         '''
         DmApi.__init__(self)
         if not username or not password:
-            loginFile = os.environ.get('DM_BSS_LOGIN_FILE')
+            if not loginFile:
+                loginFile = os.environ.get('DM_BSS_LOGIN_FILE')
             if loginFile:
                 try:
                     # Assume form <username>|<password>
@@ -60,9 +64,9 @@ class ApsBssApi(DmApi):
 
         :raises DmException: for any error
 
-        >> runs = api.listRuns()
-        >> for run in runs:
-        >>     print run['name']
+        >>> runs = api.listRuns()
+        >>> for run in runs:
+        >>>     print run['name']
         '''
         return self.bssClient.listRuns()
 
@@ -75,7 +79,7 @@ class ApsBssApi(DmApi):
 
         :raises DmException: for any error
 
-        >> run = api.getCurrentRun()
+        >>> run = api.getCurrentRun()
         '''
         return self.bssClient.getCurrentRun()
 
@@ -91,9 +95,9 @@ class ApsBssApi(DmApi):
 
         :raises DmException: for any error
 
-        >> proposals = api.listBeamlineProposals()
-        >> for proposal in proposals:
-        >>     print proposal['title']
+        >>> proposals = api.listBeamlineProposals()
+        >>> for proposal in proposals:
+        >>>     print proposal['title']
         '''
         if not runName:
             runName = self.getCurrentRun()['name']
@@ -116,9 +120,9 @@ class ApsBssApi(DmApi):
 
         :raises DmException: for any other error
 
-        >> proposal = api.getBeamlineProposal(42096)
-        >> for experimenter in proposal['experimenters']:
-        >>     print experimenter['badge'], experimenter['lastName'] 
+        >>> proposal = api.getBeamlineProposal(42096)
+        >>> for experimenter in proposal['experimenters']:
+        >>>     print experimenter['badge'], experimenter['lastName'] 
         '''
         if not runName:
             runName = self.getCurrentRun()['name']
diff --git a/src/python/dm/aps_bss/cli/__init__.py b/src/python/dm/aps_bss/cli/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/src/python/dm/aps_bss/cli/getProposalCli.py b/src/python/dm/aps_bss/cli/getProposalCli.py
new file mode 100755
index 0000000000000000000000000000000000000000..ad13b645073281e9bd81582cb9210a77636d1605
--- /dev/null
+++ b/src/python/dm/aps_bss/cli/getProposalCli.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+
+from dm.common.exceptions.invalidRequest import InvalidRequest
+from dm.common.cli.dmCli import DmCli
+from dm.aps_bss.api.apsBssApi import ApsBssApi
+
+class GetProposalCli(DmCli):
+    def __init__(self):
+        DmCli.__init__(self)
+        self.addOption('', '--id', dest='proposalId', help='Proposal id.')
+        self.addOption('', '--run', dest='runName', help='Run name. If not provided, current run will be used.')
+        self.addOption('', '--login-file', dest='loginFile', help='BSS login file. It may be provided via environment variable DM_BSS_LOGIN_FILE.')
+
+    def checkArgs(self):
+        if not self.options.proposalId:
+            raise InvalidRequest('Missing proposal id.')
+
+    def runCommand(self):
+        self.parseArgs(usage="""
+    dm-get-proposal --id=PROPOSALID
+        [--run=RUNNAME]
+        [--login-file=LOGINFILE]
+
+Description:
+    Retrieves beamline proposal for the given id.
+        """)
+        self.checkArgs()
+        proposalId = int(self.options.proposalId)
+        api = ApsBssApi(loginFile=self.options.loginFile)
+        proposal = api.getBeamlineProposal(proposalId=proposalId, runName=self.options.runName)
+        print proposal.getDisplayString(self.getDisplayKeys(), self.getDisplayFormat())
+
+#######################################################################
+# Run command.
+if __name__ == '__main__':
+    cli = GetProposalCli()
+    cli.run()
diff --git a/src/python/dm/aps_bss/cli/listProposalsCli.py b/src/python/dm/aps_bss/cli/listProposalsCli.py
new file mode 100755
index 0000000000000000000000000000000000000000..05b1f4301cd36ce7d4385e6d540aa0b754978325
--- /dev/null
+++ b/src/python/dm/aps_bss/cli/listProposalsCli.py
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+
+from dm.common.cli.dmCli import DmCli
+from dm.aps_bss.api.apsBssApi import ApsBssApi
+
+class ListProposalsCli(DmCli):
+    def __init__(self):
+        DmCli.__init__(self)
+        self.addOption('', '--run', dest='runName', help='Run name. If not provided, current run will be used.')
+        self.addOption('', '--login-file', dest='loginFile', help='BSS login file. It may be provided via environment variable DM_BSS_LOGIN_FILE.')
+
+    def checkArgs(self):
+        pass
+
+    def runCommand(self):
+        self.parseArgs(usage="""
+    dm-list-proposals 
+        [--run=RUNNAME]
+        [--login-file=LOGINFILE]
+
+Description:
+    Retrieves list of beamline proposals for the given run.
+        """)
+        self.checkArgs()
+        api = ApsBssApi(loginFile=self.options.loginFile)
+        proposals = api.listBeamlineProposals(runName=self.options.runName)
+        for proposal in proposals:
+            print proposal.getDisplayString(self.getDisplayKeys(), self.getDisplayFormat())
+
+#######################################################################
+# Run command.
+if __name__ == '__main__':
+    cli = ListProposalsCli()
+    cli.run()
diff --git a/src/python/dm/aps_bss/cli/listRunsCli.py b/src/python/dm/aps_bss/cli/listRunsCli.py
new file mode 100755
index 0000000000000000000000000000000000000000..2bdcf21cfcbe831c876a3c8055dc44943efae3a9
--- /dev/null
+++ b/src/python/dm/aps_bss/cli/listRunsCli.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+
+from dm.common.cli.dmCli import DmCli
+from dm.aps_bss.api.apsBssApi import ApsBssApi
+
+class ListRunsCli(DmCli):
+    def __init__(self):
+        DmCli.__init__(self)
+        self.addOption('', '--login-file', dest='loginFile', help='BSS login file. It may be provided via environment variable DM_BSS_LOGIN_FILE.')
+
+    def checkArgs(self):
+        pass
+
+    def runCommand(self):
+        self.parseArgs(usage="""
+    dm-list-runs [--login-file=LOGINFILE]
+
+Description:
+    Retrieves list of available runs.
+        """)
+        self.checkArgs()
+        api = ApsBssApi(loginFile=self.options.loginFile)
+        runs = api.listRuns()
+        for run in runs:
+            print run.getDisplayString(self.getDisplayKeys(), self.getDisplayFormat())
+
+#######################################################################
+# Run command.
+if __name__ == '__main__':
+    cli = ListRunsCli()
+    cli.run()