Refactor logic from gerrit.sh into gerrit/filters.  Add modular usage/help

Change-Id: I2ff1d0eda2bb7e80dab8d345721f39c621357774
diff --git a/bin/gerrit.sh b/bin/gerrit.sh
index 8f6eac3..f436a44 120000
--- a/bin/gerrit.sh
+++ b/bin/gerrit.sh
@@ -1 +1 @@
-../gerrit/gerrit.sh
\ No newline at end of file
+../gerrit/bin/gerrit.sh
\ No newline at end of file
diff --git a/bin/jira-search.sh b/bin/jira-search.sh
new file mode 120000
index 0000000..03efcb2
--- /dev/null
+++ b/bin/jira-search.sh
@@ -0,0 +1 @@
+../jira/bin/jira-search.sh
\ No newline at end of file
diff --git a/gerrit/gerrit.sh b/gerrit/bin/gerrit.sh
similarity index 80%
rename from gerrit/gerrit.sh
rename to gerrit/bin/gerrit.sh
index adabf93..4900ab1 100755
--- a/gerrit/gerrit.sh
+++ b/gerrit/bin/gerrit.sh
@@ -6,30 +6,36 @@
 { # loader
     declare pgm=''
     pgm="$(realpath --canonicalize-existing "$0")"
-    root="${pgm%/*}" # path -= /bin/
-    root="${root%/*}" # path is parent
-    source "$root/lf/onf-common/common.sh" '--common-args-begin--'
+    readonly pgm
 
-    # lib_root="${pgm/.sh}" gerrit.sh => gerrit/
+    declare libdir="${pgm%/*}"
+    libdir="${libdir%/*}/gerrit"
+    readonly libdir
+
+    declare root=''
+    root="${pgm%%/gerrit/bin/gerrit.sh}"
+    source "$root/lf/onf-common/common.sh" '--common-args-begin--'
 }
 
 ##-------------------##
 ##---]  GLOBALS  [---##
 ##-------------------##
 declare -g serv='gerrit.opencord.org'
-declare -x -g BROWSER="${BROWSER:-opera}"
+declare -x -g BROWSER="${BROWSER:-/usr/local/bin/firefox}"
+declare -g -a me=()
+
+##--------------------##
+##---]  INCLUDES  [---##
+##--------------------##
+source "${libdir}/filters/status.sh"
+source "${libdir}/usage/include.sh"
+# source "${libdir}/usage/main.sh"
+
 
 ## -----------------------------------------------------------------------
 ## -----------------------------------------------------------------------
-function error_orig()
-{
-    echo "${FUNCNAME[1]} ERROR: $*"
-    exit 1
-}
-
-## -----------------------------------------------------------------------
-## -----------------------------------------------------------------------
-function join_by_orig()
+# function join_by_orig()
+function join_by()
 {
     local d=${1-} f=${2-}; if shift 2; then printf %s "$f" "${@/#/$d}"; fi;
 }
@@ -43,35 +49,41 @@
     local -n ref_repos=$1   ; shift
     local -n ref_status=$1 ; shift
 
+    ref_urls=()
+
     # local stem='https://gerrit.opencord.org/q/project:voltha-go+status:open'
     local stem='https://gerrit.opencord.org/q/'
 
+    local -a common=()
+    [[ -v me ]] && { common+=("${me[@]}"); }
+    common+=("${ref_status[@]}")
+
     local repo
     for repo in "${ref_repos[@]}";
-    do 
-       local -a args=()
+    do
+        local -a args=()
         args+=("project:${repo}")
-
-        if [[ -v argv_me ]]; then
-            args+=("owner:${USER}@opennetworking.org")
-        fi
-
-        local status
-        for status in "${ref_status[@]}";
-        do
-            args+=("$status")
-        done
+        args+=("${common[@]}")
 
         local url="$stem"
         url+="$(join_by '+' "${args[@]}")"
         ref_urls+=("$url")
     done
 
+    if [[ ${#ref_urls[@]} -eq 0 ]]; then
+        local -a args=()
+        args=("${common[@]}")
 
-    
+        local url="$stem"
+        url+="$(join_by '+' "${args[@]}")"
+        ref_urls+=("$url")
+    fi
+
 	# --debug)  declare -g -i debug=1 ;;
     # --search) declare -g -i search=1 ;;
     # --wip)    declare -g -i status_is_open=1 ;;
+
+    return
 }
 
 
@@ -100,12 +112,12 @@
                     ;;
                 *) error "Unknown --admin type [$admin]" ;;
             esac
-                
+
         done # admins[@]
     done # repos[@]
     return
 }
-     
+
 ## -----------------------------------------------------------------------
 ## Intent: Iterate over --admin values and generate repositories
 ## -----------------------------------------------------------------------
@@ -119,16 +131,14 @@
     local stem0="https://gerrit.opencord.org/plugins/gitiles"
 
     # https://gerrit.opencord.org/plugins/gitiles/ofagent-go
-    
+
     local repo
     for repo in "${_repos[@]}";
     do
-        declare -p repo
         local stem="${stem0}/$repo"
         local view
         for view in "${view_keys[@]}";
         do
-            declare -p view
             # https://gerrit.opencord.org/admin/repos/voltha-go
             case "$view" in
                 # https://gerrit.opencord.org/plugins/gitiles/voltha-system-tests
@@ -139,11 +149,11 @@
                   master)
                       ref+=("$stem/+/refs/heads/master")
                       ;;
-                     
+
                   voltha-*)
                       echo "STEM: $stem"
                       if false; then
-                          ref+=("$stem/+/refs/tags/${view}") 
+                          ref+=("$stem/+/refs/tags/${view}")
                           ref+=("$stem/+/refs/heads/${view}")
                       fi
                       # ref+=("$stem/+/refs/heads/${view}")
@@ -163,7 +173,7 @@
 
     return
 }
-     
+
 ## -----------------------------------------------------------------------
 ## -----------------------------------------------------------------------
 function get_urls()
@@ -198,9 +208,6 @@
 function browse()
 {
     local -n _urls=$1; shift
-
-    # declare -p _urls
-    # echo "${_urls[@]}"
     "${BROWSER}" "${_urls[@]}" >/dev/null 2>/dev/null &
     return
 }
@@ -239,74 +246,12 @@
 function do_gerrit_dashboard()
 {
     local -n ref=$1; shift
-    
+
     declare -A data=()
     get_urls data "$serv"
     ref+=("${data['dashboard']}")
     return
 }
- 
-## -----------------------------------------------------------------------
-## -----------------------------------------------------------------------
-function usage()
-{
-    cat <<EOH
-Usage: $0
-  --all r          Load all metadata for a repository
-  --reviews        Load pending code review requests
-
-  --access         Display gerrit ACLs
-  --dash(boad)     Display my patch dashboard
-  --branch         Admin branch URL
-
-  --crowd          View crowd access roles for group
-  --crowd-all      Group search (crowd)
-
-  --groups [g]     View access roles for group.
-  --groups-all     Group search (gerrit)
-
-  --repo           Repository name
-  --serv           Gerrit server name
-
-  --me             Query for my patches
-  --wip
-  --search
-
-  --admins a
-    a=branches     View repository branches
-    a=tags         View repository tags
-
-  --patch [repo]    Search for repository patches
-  --version v       Artifact versions(s) to act on
-     v=master       Mainline development
-     v=voltha-2.12  Artifact branch from a release
-     v=v2.12.0      Artifact tag derived from a release
-     v=v3.5.4       Artifact tag derived from VERSION file
-
-  --view v          View a code repository:
-    repo             View top level gerrit repository page (w/branch & tag)
-    search           Gerrit repo search page
-    master           View a branch
-    voltha-2.12      View a branch
-    v2.3.3           View a tag
-
-[FILTERS]
-  --status s         Filter query by open/merge/closed status
-    s=open (is:open)
-    s=merged
-
-  --debug
-  --help
-
-% gerrit.sh --repo voltha-go --admin tags --admin branches
-
-## Search for umerged patches in repo:voltha-go
-% gerrit.sh --repo voltha-go --search --wip
-EOH
-    return
-}
-
-# 
 
 # https://gerrit.opencord.org/q/status:open+-is:wip
 
@@ -322,6 +267,13 @@
     arg="$1"; shift
     case "$arg" in
         --help) usage; exit 0 ;;
+        --help-*)
+            case "$arg" in
+                --help-status) usage_verbose "$arg" ;;
+                *) usage ;;
+            esac
+            exit 0
+            ;;
 
         ## Modes
 	    --debug)
@@ -329,11 +281,19 @@
             declare -g -i debug=1
             ;;
         --search) declare -g -i argv_search=1 ;;
-        --me) declare -g -i argv_me=1 ;;
-        
+
+        --me)
+            # declare -g -i argv_me=1
+            me+=("owner:${USER}@opennetworking.org")
+            ;;
+
+        --todo) source "$root/gerrit/todo.sh" ;;
+
         --wip)
-            [[ ! -v argv_status ]] && { declare -g -a argv_status=(); }
-            argv_status+=('status:open')
+            declare -a args=()
+            args=('--status' 'open')
+            [[ $# -gt 0 ]] && { args+=("$@"); }
+            set -- "${args[@]}"
             ;;
 
         --admin)
@@ -352,7 +312,7 @@
             repo="$1"; shift
             declare -a args=()
             args+=('--repo'  "$repo")
-            # 
+            #
             args+=('--admin' 'branches')
             args+=('--admin' 'tags')
 
@@ -367,14 +327,14 @@
             set -- "${args[@]}" "$@"
             ;;
 
-        --repo) 
+        --repo)
             repo="$1"; shift
             [[ ! -v repos ]] && { declare -a repos=(); }
             repos+=("$repo")
             ;;
-        
+
         --serv) serv="$1"; shift
-                case "$serv" in 
+                case "$serv" in
                     *cord*) serv='gerrit.opencord.org'    ;;
                     *onos*) serv='gerrit.onosproject.org' ;;
                     *) error "Detected invalid --serv $serv" ;;
@@ -409,9 +369,10 @@
             esac
             ;;
 
+        --*desk) error "Try --dashboard instead" ;;
         --dash*) do_gerrit_dashboard urls ;;
-    
-        # 
+
+        #
         --group*)
             case "$arg" in
                 --group*)
@@ -432,8 +393,11 @@
             ;;
 
         --status)
-            status="$1"; shift # open, closed, merged
-            urls[-1]+="+status:${status}"
+            arg="$1"; shift # open, closed, merged
+            case "$arg" in
+                closed|open|merged) add_filter_status "$arg";;
+                *) error "Detected invalid --status [$arg]" ;;
+            esac
             ;;
 
         --patch*)
@@ -465,7 +429,7 @@
                     ;;
 
                 repo) views['repository']=1 ;;
-                
+
                 # https://gerrit.opencord.org/plugins/gitiles/voltha-go
                 master) views['master']=1 ;;
                 voltha*) views["$arg"]=1  ;;
@@ -477,19 +441,19 @@
                     ;;
             esac
             ;;
-    
-	    -*) 
+
+	    -*)
 	        echo "ERROR: Unknown argument [$arg]"
 	        exit 1
 	        ;;
-    
+
 	    [[:alnum:]]*)
             declare -a what=()
             # whats+=('patches')
             whats+=('versions')
 
             stem="https://gerrit.opencord.org/q/${arg}"
-            
+
             for what in "${whats[@]}";
             do
                 case "$what" in
@@ -507,18 +471,24 @@
 done
 
 [[ -v argv_search ]] && { do_gerrit_search urls repos argv_status; }
-
 [[ -v admins ]] && { gen_admin_urls urls repos admins; }
 
 ## are --views working ?
 [[ "${#views[@]}" -gt 0 ]] && { gen_view_urls  urls repos views;  }
 # [[ ! -v views  ]] && { echo 'called'; gen_view_urls  urls repos views;  }
 
+if [[ ${#urls[@]} -eq 0 ]]; then
+    declare -a show_all=() # show all for visiblity
+    do_gerrit_search urls repos show_all
+fi
+
+
 [[ -v debug ]] && { echo "$BROWSER" "${urls[@]}"; }
-# "$BROWSER" "${urls[@]}" >/dev/null 2>/dev/null
-[[ -v argv_debug ]] && { set -x; }
-"$BROWSER" "${urls[@]}"
-[[ -v argv_debug ]] && { set +x; }
+
+if [[ ${#urls[@]} -gt 0 ]]; then
+    apply_filter_status urls
+    browse urls
+fi
 
 ## -----------------------------------------------------------------------
 ## [TODO]
@@ -531,3 +501,17 @@
 ## -----------------------------------------------------------------------
 
 # [EOF]
+
+
+# 1) gerrit.sh --search --me --wip
+#    query> owner:joey@opennetworking.org status:open
+
+# --search --me
+#            args+=("owner:${USER}@opennetworking.org")
+
+#        --wip)
+#            argv_status+=('status:open')
+#            ;;
+
+
+# [EOF]
diff --git a/gerrit/gerrit/filters/status.sh b/gerrit/gerrit/filters/status.sh
new file mode 100644
index 0000000..01ae340
--- /dev/null
+++ b/gerrit/gerrit/filters/status.sh
@@ -0,0 +1,66 @@
+#!/bin/bash
+# -----------------------------------------------------------------------
+# Copyright 2024 Open Networking Foundation Contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# -----------------------------------------------------------------------
+# SPDX-FileCopyrightText: 2024 Open Networking Foundation Contributors
+# SPDX-License-Identifier: Apache-2.0
+# -----------------------------------------------------------------------
+
+## -----------------------------------------------------------------------
+## Intent: Update gerrit query to filter based on status.
+## -----------------------------------------------------------------------
+function add_filter_status()
+{
+    local val="$1"; shift
+
+    case "$val" in
+        closed|open|merged)
+            [[ ! -v argv_status ]] && { declare -g -a argv_status=(); }
+            argv_status+=("$val")
+            ;;
+        *) error "Detected invalid --status [$arg]" ;;
+    esac
+
+    return
+} # add_status_filter()
+
+## -----------------------------------------------------------------------
+## Intent: Merge status attributes into a URL query
+## -----------------------------------------------------------------------
+function apply_filter_status()
+{
+    local -n ref=$1; shift
+
+    [[ ! -v argv_status ]] && { return; }
+    
+    local status
+    for status in "${argv_status[@]}";
+    do
+        if [[ "${ref[-1]: -1:1}" != '/' ]]; then
+           ref[-1]+='+' # "+status:${status}"
+        fi
+
+        # https://gerrit.opencord.org/q/status:open
+        # TODO: Do not apply + when
+        # ref[-1]+="+status:${status}"
+        ref[-1]+="status:${status}"
+    done
+
+    return
+}
+
+: # ($?==0) for source script
+
+# [EOF]
diff --git a/gerrit/gerrit/todo.sh b/gerrit/gerrit/todo.sh
new file mode 100644
index 0000000..93521ee
--- /dev/null
+++ b/gerrit/gerrit/todo.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+function show_todo()
+{
+    cat <<EOT
+
+** -----------------------------------------------------------------------
+** [TODO]
+** -----------------------------------------------------------------------
+1) gerrit.sh --search --me --wip
+   query> owner:joey@opennetworking.org status:open
+
+EOT
+}
+show_todo
+unset todo
+
+: # ($?==0)
+
+# [EOF]
diff --git a/gerrit/gerrit/usage/include.sh b/gerrit/gerrit/usage/include.sh
new file mode 100644
index 0000000..5dbcefa
--- /dev/null
+++ b/gerrit/gerrit/usage/include.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+# -----------------------------------------------------------------------
+# Copyright 2024 Open Networking Foundation Contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# -----------------------------------------------------------------------
+# SPDX-FileCopyrightText: 2024 Open Networking Foundation Contributors
+# SPDX-License-Identifier: Apache-2.0
+# -----------------------------------------------------------------------
+
+##--------------------##
+##---]  INCLUDES  [---##
+##--------------------##
+source "${libdir}/usage/main.sh"
+source "${libdir}/usage/status.sh"
+
+# [EOF]
diff --git a/gerrit/gerrit/usage/main.sh b/gerrit/gerrit/usage/main.sh
new file mode 100644
index 0000000..94f822b
--- /dev/null
+++ b/gerrit/gerrit/usage/main.sh
@@ -0,0 +1,112 @@
+#!/bin/bash
+# -----------------------------------------------------------------------
+# Copyright 2024 Open Networking Foundation Contributors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http:#www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# -----------------------------------------------------------------------
+# SPDX-FileCopyrightText: 2024 Open Networking Foundation Contributors
+# SPDX-License-Identifier: Apache-2.0
+# -----------------------------------------------------------------------
+# Intent: Display command help
+# -----------------------------------------------------------------------
+
+## -----------------------------------------------------------------------
+## -----------------------------------------------------------------------
+function usage()
+{
+    cat <<EOH
+Usage: $0
+  --all r          Load all metadata for a repository
+  --reviews        Load pending code review requests
+
+  --access         Display gerrit ACLs
+  --dash(boad)     Display my patch dashboard
+  --branch         Admin branch URL
+
+  --crowd          View crowd access roles for group
+  --crowd-all      Group search (crowd)
+
+  --groups [g]     View access roles for group.
+  --groups-all     Group search (gerrit)
+
+  --me             Alias for --user $USER
+
+  --repo           Repository name
+  --serv           Gerrit server name
+
+  --search
+  --wip            Query for open/unmerged patches.
+
+  --admins a
+    a=branches     View repository branches
+    a=tags         View repository tags
+
+  --patch [repo]    Search for repository patches
+  --version v       Artifact versions(s) to act on
+     v=master       Mainline development
+     v=voltha-2.12  Artifact branch from a release
+     v=v2.12.0      Artifact tag derived from a release
+     v=v3.5.4       Artifact tag derived from VERSION file
+
+  --view v          View a code repository:
+    repo             View top level gerrit repository page (w/branch & tag)
+    search           Gerrit repo search page
+    master           View a branch
+    voltha-2.12      View a branch
+    v2.3.3           View a tag
+
+[FILTER(s)]
+  --status [s]       Filter query results by status={open,merged,closed}
+    s=open (is:open)
+    s=merged
+  -- user [u]        Filter results by a list of users
+
+[MODE(s)]
+  --debug
+  --help
+
+
+[USAGE]
+  % gerrit.sh --wip
+      Display unmerged patches
+
+  % gerrit.sh --wip --me
+      Display my unmerged patches
+
+  % gerrit.sh --repo voltha-go --admin tags --admin branches
+
+  ## Search for umerged patches in repo:voltha-go
+  % gerrit.sh --repo voltha-go --search --wip
+
+[HELP: Extended]
+  --help-status
+
+EOH
+    return
+}
+
+## -----------------------------------------------------------------------
+## -----------------------------------------------------------------------
+function usage_verbose()
+{
+    local switch="$1"; shift
+
+    case "$switch" in
+        --help-status) usage_status ;;
+        *) usage ;;
+    esac
+
+    return
+}
+
+# [EOF]
diff --git a/gerrit/gerrit/usage/status.sh b/gerrit/gerrit/usage/status.sh
new file mode 100644
index 0000000..5ceaa0f
--- /dev/null
+++ b/gerrit/gerrit/usage/status.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+## -----------------------------------------------------------------------
+## Intent: Display command use cases
+## -----------------------------------------------------------------------
+
+## -----------------------------------------------------------------------
+## -----------------------------------------------------------------------
+function usage_status()
+{
+    cat <<EOH
+
+[SHOW: My unmerged patches]
+  % gerrit.sh --me --status open
+
+[SHOW: All records of type status]
+  % gerrit.sh --status open
+  % gerrit.sh --status merged
+  % gerrit.sh --status closed
+
+  % gerrit.sh --status merged --status closed
+EOH
+    return
+}
+
+# [EOF]
diff --git a/gerrit/makefile b/gerrit/makefile
new file mode 100644
index 0000000..73f6aec
--- /dev/null
+++ b/gerrit/makefile
@@ -0,0 +1,11 @@
+# -*- makefile -*-
+
+all:
+
+test ::
+
+clean ::
+
+sterile :: clean
+
+# [EOF]
diff --git a/jira/jira-search.sh b/jira/bin/jira-search.sh
similarity index 93%
rename from jira/jira-search.sh
rename to jira/bin/jira-search.sh
index 31c22ce..97b8cc2 100755
--- a/jira/jira-search.sh
+++ b/jira/bin/jira-search.sh
@@ -3,6 +3,21 @@
 ## Intent: Construct a jira ticket query with attributes
 ## --------------------------------------------------------------------
 
+{ # loader
+    declare pgm=''
+    pgm="$(realpath --canonicalize-existing "$0")"
+
+    declare root=''
+    root="${pgm%%/jira/bin/jira-search.sh}"
+    source "$root/lf/onf-common/common.sh" '--common-args-begin--'
+
+    pgm_lib="${root}/jira/jira-search"
+    readonly pgm_lib
+
+    pgm_bin="${root}/bin"
+    readonly pgm_bin
+}
+
 # set -euo pipefail
 #source ~/.sandbox/trainlab-common/common.sh '--common-args-begin--'
 
@@ -22,9 +37,12 @@
 declare -g -a projects=()
 
 path="$(realpath $0 --canonicalize-existing)"
-source "${path%\.sh}/utils.sh"
-source "$pgmlib/fixversion.sh"
-source "$pgmlib/resolved.sh"
+# source "${path%\.sh}/utils.sh"
+source "${pgm_lib}/utils.sh"
+#source "$pgmlib/fixversion.sh"
+#source "$pgmlib/resolved.sh"
+source "${pgm_lib}/fixversion.sh"
+source "${pgm_lib}/resolved.sh"
 
 ## --------------------------------------------------------------------
 ## --------------------------------------------------------------------
@@ -55,7 +73,7 @@
 function conjunction()
 {
     return
-    
+
     local -n ref=$1; shift
     [[ $# -gt 0 ]] && { local literal="$1"; shift; }
 
@@ -96,7 +114,7 @@
     declare -n ans=$1; shift
 
     # [ -z ${args+word} ] && { args=(); }
-    
+
     if [[ ${#args[@]} -gt 0 ]]; then
 
         local modifier
@@ -270,7 +288,10 @@
     do
         _tmp+=("$val" 'AND')
     done
-    unset _tmp[-1]
+
+    if [[ ${#_tmp[@]} -gt 0 ]]; then
+        unset _tmp[-1]
+    fi
 
     ## -----------------------
     ## Massage with html codes
@@ -304,10 +325,12 @@
 Usage: $0 VOL-xxxx
   --debug       Enable script debug mode
   --dry-run     Simulate
+  --todo        Display future enhancements
 
   VOL-{xxxx}    View a jira ticket by ID
 
 [SERVER]
+  --server {cord,onf}
   --onf         jira.opennetworking.org (default)
   --opencord    jira.opencord.org
 
@@ -430,8 +453,17 @@
         ##------------------##
         ##---]  SERVER  [---##
         ##------------------##
-        -*onf) declare server='jira.opennetworking.org'; error "FOUND --onf" ;;
-        -*cord) declare server='jira.opencord.org' ;;
+        --serv*)
+            arg="$1"; shift
+            case "$arg" in
+                *cord*) server='jira.opencord.org' ;;
+                 *onf*) server='jira.opennetworking.org' ;;
+                 *) error "--server [$arg] expected opencord or onf" ;;
+            esac
+            ;;
+
+         --onf) declare server='jira.opennetworking.org' ;;
+        --cord) declare server='jira.opencord.org'     ;;
 
         ##---------------------##
         ##---]  SEARCH-BY  [---##
@@ -474,6 +506,7 @@
             ;;
 
         --all) set -- '--resolved-is-none' "$@" ;; # alias: --[un-]resolved
+        --todo) source "${pgm_lib}/todo.sh" ;;
 
         --proj*) projects+=("$1"); shift ;;
 
@@ -583,6 +616,11 @@
     esac
 done
 
+## --------------
+## Required check
+## --------------
+[[ ! -v server ]] && { error "--server={cord,onf} is required"; }
+
 ## ----------------------
 ## Construct query filter
 ## ----------------------
diff --git a/jira/resolved.sh b/jira/bin/resolved.sh
similarity index 100%
rename from jira/resolved.sh
rename to jira/bin/resolved.sh
diff --git a/jira/jira-search/todo.sh b/jira/jira-search/todo.sh
new file mode 100644
index 0000000..c083b77
--- /dev/null
+++ b/jira/jira-search/todo.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+## -----------------------------------------------------------------------
+## -----------------------------------------------------------------------
+function __anonymous()
+{
+    cat <<EOT
+
+** -----------------------------------------------------------------------
+** [TODO]
+** -----------------------------------------------------------------------
+    o --component bbsim --component ci-management
+      # Component not working unless project passed
+    project = VOL AND component in (bbsim, bbsim-sadis-server, ci-management)
+EOT
+    return
+}
+
+__anonymous
+unset __anonymous
+
+: # ($?==0) for source script return
+# [EOF]
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..262c76f
--- /dev/null
+++ b/makefile
@@ -0,0 +1,15 @@
+# -*- makefile -*-
+## -----------------------------------------------------------------------
+## -----------------------------------------------------------------------
+
+common-sh := lf/onf-common/common.sh
+
+all : $(common-sh)
+
+## -----------------------------------------------------------------------
+## Intent: On-demand submodule checkout
+## -----------------------------------------------------------------------
+$(common-sh) :
+	git submodule update --init --recursive
+
+# [EOF]