loki-mode 7.16.1 → 7.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/autonomy/loki CHANGED
@@ -613,6 +613,7 @@ show_help() {
613
613
  echo " loki quick \"add dark mode\" # Single-task mode (3 iters max)"
614
614
  echo " loki demo # 60-second interactive demo"
615
615
  echo " loki init -t saas-starter # Scaffold from template"
616
+ echo " loki template install <src> # Install a community PRD template"
616
617
  echo ""
617
618
  echo " # Session ops + observability"
618
619
  echo " loki status [--json] # Current status"
@@ -621,6 +622,7 @@ show_help() {
621
622
  echo " loki doctor [--json] # System prereq + skill symlinks"
622
623
  echo " loki logs # Tail recent log output"
623
624
  echo " loki export json|markdown|csv|timeline # Export session"
625
+ echo " loki assets export team.tgz # Export shareable team assets (redacted)"
624
626
  echo " loki cleanup # Kill orphaned processes"
625
627
  echo ""
626
628
  echo " # Providers + model routing"
@@ -6298,6 +6300,187 @@ cmd_export() {
6298
6300
  esac
6299
6301
  }
6300
6302
 
6303
+ # R8: Shareable team assets. Bundles a team's invested, reusable assets
6304
+ # (cross-project + project memory/learnings, the agent registry, PRD
6305
+ # templates, council config, optionally the R5 wiki) into a portable,
6306
+ # REDACTED tarball, and re-imports them into another project or fresh clone.
6307
+ #
6308
+ # Scope vs cmd_export: cmd_export emits a per-run SESSION SNAPSHOT
6309
+ # (json/md/csv/timeline) and is NOT portable or redacted. cmd_assets emits a
6310
+ # portable, redacted TEAM-ASSET tarball -- different scope, different artifact.
6311
+ # Redaction REUSES the single proof_redact chokepoint via the
6312
+ # autonomy/lib/assets_bundle.py helper. No second redactor or parallel exporter.
6313
+ cmd_assets() {
6314
+ local subcommand="${1:-help}"
6315
+ shift 2>/dev/null || true
6316
+
6317
+ local helper="$_LOKI_SCRIPT_DIR/lib/assets_bundle.py"
6318
+ if [ ! -f "$helper" ]; then
6319
+ echo -e "${RED}Error: assets helper not found at $helper${NC}" >&2
6320
+ return 1
6321
+ fi
6322
+ if ! command -v python3 &>/dev/null; then
6323
+ echo -e "${RED}Error: python3 is required for 'loki assets'${NC}" >&2
6324
+ return 1
6325
+ fi
6326
+
6327
+ # Project dir holds .loki/ (memory, council, wiki). Default to cwd.
6328
+ local project_dir
6329
+ project_dir="$(pwd)"
6330
+
6331
+ # repo_root holds agents/types.json + templates/. The export and import
6332
+ # roots are DELIBERATELY ASYMMETRIC (not a bug):
6333
+ # - EXPORT reads from the loki install ($SKILL_DIR), where a team's
6334
+ # edited registry / templates actually live and are read at runtime
6335
+ # (autonomy/loki:9781, 8840 read templates from $SKILL_DIR; cmd_agent
6336
+ # reads agents/types.json from the install first).
6337
+ # - IMPORT writes to the current working directory (the root of the
6338
+ # target clone the user is setting up), NEVER back into the install.
6339
+ # Writing into $SKILL_DIR would clobber the live install's shipped
6340
+ # registry / templates with redacted copies.
6341
+ # Honest limitation: agents/templates restore relative to cwd, so run
6342
+ # `loki assets import` from the root of the target clone. A global-install
6343
+ # user must copy the restored agents/ + templates/ into their install for
6344
+ # loki to read them at runtime (memory/learnings/council/wiki are unaffected
6345
+ # -- those restore to $HOME/.loki and <project>/.loki and take effect
6346
+ # immediately).
6347
+ local export_repo_root="${SKILL_DIR:-$(cd "$_LOKI_SCRIPT_DIR/.." && pwd)}"
6348
+ local import_repo_root="$project_dir"
6349
+
6350
+ case "$subcommand" in
6351
+ --help|-h|help|"")
6352
+ echo -e "${BOLD}loki assets${NC} - Export/import shareable team assets (R8)"
6353
+ echo ""
6354
+ echo "Bundles a team's reusable assets into a portable, redacted"
6355
+ echo "tarball so setup compounds into shared, importable value."
6356
+ echo ""
6357
+ echo "Usage:"
6358
+ echo " loki assets export <bundle.tgz> [--wiki] [--categories a,b,c]"
6359
+ echo " loki assets import <bundle.tgz> [--no-merge] [--into-install]"
6360
+ echo " loki assets inspect <bundle.tgz>"
6361
+ echo ""
6362
+ echo "Assets bundled (default):"
6363
+ echo " learnings ~/.loki/learnings/*.jsonl (cross-project)"
6364
+ echo " memory <project>/.loki/memory/** (episodic/semantic/skills)"
6365
+ echo " agents agents/types.json (agent registry)"
6366
+ echo " templates templates/*.md (PRD templates)"
6367
+ echo " council <project>/.loki/council/*.json (council config/state)"
6368
+ echo " wiki <project>/.loki/wiki/** (opt-in via --wiki)"
6369
+ echo ""
6370
+ echo "All bundled content is redacted via the proof_redact chokepoint:"
6371
+ echo "secrets, keys, tokens, and absolute home/repo paths are stripped"
6372
+ echo "before the bundle is written. Originals on disk are never changed."
6373
+ echo ""
6374
+ echo "Restore locations on import:"
6375
+ echo " learnings -> \$HOME/.loki/learnings (effective immediately)"
6376
+ echo " memory -> <cwd>/.loki/memory (effective immediately)"
6377
+ echo " council -> <cwd>/.loki/council (effective immediately)"
6378
+ echo " wiki -> <cwd>/.loki/wiki (effective immediately)"
6379
+ echo " agents -> <cwd>/agents/types.json"
6380
+ echo " templates -> <cwd>/templates/*.md"
6381
+ echo "Run import from the ROOT of the target clone. Note: agents and"
6382
+ echo "templates are read by loki from its install dir at runtime. On a"
6383
+ echo "global install, pass --into-install so agents/templates restore"
6384
+ echo "into the install dir and are read at runtime (default is cwd,"
6385
+ echo "which suits a repo-clone where loki runs from the clone root)."
6386
+ echo ""
6387
+ echo "Examples:"
6388
+ echo " loki assets export team-assets.tgz"
6389
+ echo " loki assets export team-assets.tgz --wiki"
6390
+ echo " loki assets import team-assets.tgz # merge learnings, overwrite rest"
6391
+ echo " loki assets import team-assets.tgz --no-merge"
6392
+ echo " loki assets inspect team-assets.tgz # show manifest only"
6393
+ return 0
6394
+ ;;
6395
+ export)
6396
+ local out_path="${1:-}"
6397
+ if [ -z "$out_path" ]; then
6398
+ echo -e "${RED}Usage: loki assets export <bundle.tgz> [--wiki] [--categories a,b,c]${NC}" >&2
6399
+ return 1
6400
+ fi
6401
+ shift 2>/dev/null || true
6402
+ if ! _export_check_overwrite "$out_path"; then
6403
+ return 1
6404
+ fi
6405
+ echo "Bundling team assets (redacting secrets and paths)..."
6406
+ if LOKI_ASSETS_HOME="$HOME" LOKI_ASSETS_REPO="$export_repo_root" \
6407
+ LOKI_ASSETS_PROJECT="$project_dir" \
6408
+ python3 "$helper" export "$out_path" "$@"; then
6409
+ echo -e "${GREEN}Bundle written: $out_path${NC}"
6410
+ else
6411
+ echo -e "${RED}Export failed${NC}" >&2
6412
+ return 1
6413
+ fi
6414
+ ;;
6415
+ import)
6416
+ local bundle_path="${1:-}"
6417
+ if [ -z "$bundle_path" ]; then
6418
+ echo -e "${RED}Usage: loki assets import <bundle.tgz> [--no-merge] [--into-install]${NC}" >&2
6419
+ return 1
6420
+ fi
6421
+ shift 2>/dev/null || true
6422
+ if [ ! -f "$bundle_path" ]; then
6423
+ echo -e "${RED}Error: bundle not found: $bundle_path${NC}" >&2
6424
+ return 1
6425
+ fi
6426
+ # --into-install: restore agents/templates into the loki install
6427
+ # ($SKILL_DIR) so a global-install user has them read at runtime,
6428
+ # instead of into cwd. Strip the flag before passing the rest to
6429
+ # the helper (the helper does not understand it). The install dir
6430
+ # is the same source export reads from, so this is the symmetric
6431
+ # round-trip for a global install. Off by default (cwd is safe).
6432
+ local _import_args=()
6433
+ local arg
6434
+ for arg in "$@"; do
6435
+ if [ "$arg" = "--into-install" ]; then
6436
+ import_repo_root="$export_repo_root"
6437
+ else
6438
+ _import_args+=("$arg")
6439
+ fi
6440
+ done
6441
+ if [ "$import_repo_root" = "$export_repo_root" ]; then
6442
+ echo "Importing team assets (agents/templates -> install dir: $import_repo_root)..."
6443
+ else
6444
+ echo "Importing team assets into this project/clone (cwd)..."
6445
+ fi
6446
+ # Bash 3.2 (macOS) errors on "${arr[@]}" when the array is empty
6447
+ # and set -u is active; guard with the count.
6448
+ if [ "${#_import_args[@]}" -gt 0 ]; then
6449
+ LOKI_ASSETS_HOME="$HOME" LOKI_ASSETS_REPO="$import_repo_root" \
6450
+ LOKI_ASSETS_PROJECT="$project_dir" \
6451
+ python3 "$helper" import "$bundle_path" "${_import_args[@]}"
6452
+ else
6453
+ LOKI_ASSETS_HOME="$HOME" LOKI_ASSETS_REPO="$import_repo_root" \
6454
+ LOKI_ASSETS_PROJECT="$project_dir" \
6455
+ python3 "$helper" import "$bundle_path"
6456
+ fi
6457
+ if [ $? -eq 0 ]; then
6458
+ echo -e "${GREEN}Import complete${NC}"
6459
+ else
6460
+ echo -e "${RED}Import failed${NC}" >&2
6461
+ return 1
6462
+ fi
6463
+ ;;
6464
+ inspect)
6465
+ local bundle_path="${1:-}"
6466
+ if [ -z "$bundle_path" ]; then
6467
+ echo -e "${RED}Usage: loki assets inspect <bundle.tgz>${NC}" >&2
6468
+ return 1
6469
+ fi
6470
+ if [ ! -f "$bundle_path" ]; then
6471
+ echo -e "${RED}Error: bundle not found: $bundle_path${NC}" >&2
6472
+ return 1
6473
+ fi
6474
+ python3 "$helper" inspect "$bundle_path"
6475
+ ;;
6476
+ *)
6477
+ echo -e "${RED}Unknown subcommand: $subcommand${NC}" >&2
6478
+ echo "Run 'loki assets --help' for usage."
6479
+ return 1
6480
+ ;;
6481
+ esac
6482
+ }
6483
+
6301
6484
  # Guard: check if output file exists and warn before overwriting
6302
6485
  _export_check_overwrite() {
6303
6486
  local output="$1"
@@ -9633,6 +9816,22 @@ cmd_init() {
9633
9816
  echo -e "Selected: ${CYAN}$template_name${NC} ($(_get_template_label "$template_name"))"
9634
9817
  fi
9635
9818
 
9819
+ # R10: a hub-installed template (.loki/templates/<name>.md) is also valid.
9820
+ # Resolve it via agents/hub_install.py (validates the name as a safe id).
9821
+ local _installed_tpl=""
9822
+ local _hub_py_init="$SKILL_DIR/agents/hub_install.py"
9823
+ [ -f "$_hub_py_init" ] || _hub_py_init="agents/hub_install.py"
9824
+ if [[ -f "$_hub_py_init" ]]; then
9825
+ _installed_tpl=$(TPL_NAME="$template_name" HUB_PY="$_hub_py_init" python3 -c "
9826
+ import os
9827
+ import importlib.util as ilu
9828
+ sp = ilu.spec_from_file_location('loki_hub_install', os.environ.get('HUB_PY'))
9829
+ m = ilu.module_from_spec(sp); sp.loader.exec_module(m)
9830
+ p = m.installed_template_path(os.environ['TPL_NAME'])
9831
+ print(p or '')
9832
+ " 2>/dev/null)
9833
+ fi
9834
+
9636
9835
  # Validate template_name against known list before filesystem lookup
9637
9836
  local _tpl_valid=false
9638
9837
  for _tpl_check in "${TEMPLATE_NAMES[@]}"; do
@@ -9641,6 +9840,9 @@ cmd_init() {
9641
9840
  break
9642
9841
  fi
9643
9842
  done
9843
+ if [[ -n "$_installed_tpl" ]]; then
9844
+ _tpl_valid=true
9845
+ fi
9644
9846
  if ! $_tpl_valid; then
9645
9847
  echo -e "${RED}Unknown template: $template_name${NC}"
9646
9848
  echo ""
@@ -9649,18 +9851,23 @@ cmd_init() {
9649
9851
  echo " $tname"
9650
9852
  done
9651
9853
  echo ""
9652
- echo "Run 'loki init --list' for details."
9854
+ echo "Run 'loki init --list' for built-in details, 'loki template list' for installed."
9653
9855
  exit 1
9654
9856
  fi
9655
9857
 
9656
9858
  # Resolve template file
9657
9859
  local template_file=""
9658
- for dir in "$SKILL_DIR/templates" "$SKILL_DIR/examples"; do
9659
- if [[ -f "$dir/${template_name}.md" ]]; then
9660
- template_file="$dir/${template_name}.md"
9661
- break
9662
- fi
9663
- done
9860
+ if [[ -n "$_installed_tpl" && -f "$_installed_tpl" ]]; then
9861
+ template_file="$_installed_tpl"
9862
+ fi
9863
+ if [[ -z "$template_file" ]]; then
9864
+ for dir in "$SKILL_DIR/templates" "$SKILL_DIR/examples"; do
9865
+ if [[ -f "$dir/${template_name}.md" ]]; then
9866
+ template_file="$dir/${template_name}.md"
9867
+ break
9868
+ fi
9869
+ done
9870
+ fi
9664
9871
 
9665
9872
  if [[ -z "$template_file" ]]; then
9666
9873
  echo -e "${RED}Unknown template: $template_name${NC}"
@@ -13094,6 +13301,9 @@ main() {
13094
13301
  export)
13095
13302
  cmd_export "$@"
13096
13303
  ;;
13304
+ assets)
13305
+ cmd_assets "$@"
13306
+ ;;
13097
13307
  config)
13098
13308
  cmd_config "$@"
13099
13309
  ;;
@@ -13173,6 +13383,9 @@ main() {
13173
13383
  agent)
13174
13384
  cmd_agent "$@"
13175
13385
  ;;
13386
+ template)
13387
+ cmd_template "$@"
13388
+ ;;
13176
13389
  state)
13177
13390
  cmd_state "$@"
13178
13391
  ;;
@@ -19562,6 +19775,104 @@ cmd_completions() {
19562
19775
  esac
19563
19776
  }
19564
19777
 
19778
+ cmd_template() {
19779
+ local subcommand="${1:-list}"
19780
+ shift 2>/dev/null || true
19781
+
19782
+ local script_dir
19783
+ script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
19784
+ local hub_py="${script_dir}/../agents/hub_install.py"
19785
+ [ -f "$hub_py" ] || hub_py="agents/hub_install.py"
19786
+
19787
+ case "$subcommand" in
19788
+ install)
19789
+ local source="${1:-}"
19790
+ if [ -z "$source" ]; then
19791
+ echo -e "${RED}Usage: loki template install <source>${NC}"
19792
+ echo " source: local path, git repo URL, or raw manifest URL"
19793
+ return 1
19794
+ fi
19795
+ if [ ! -f "$hub_py" ]; then
19796
+ echo -e "${RED}Error: agents/hub_install.py not found${NC}"
19797
+ return 1
19798
+ fi
19799
+ local result
19800
+ if result=$(python3 "$hub_py" install-template "$source" 2>&1); then
19801
+ TPL_RESULT="$result" python3 - << 'PYEOF'
19802
+ import json, os
19803
+ r = json.loads(os.environ["TPL_RESULT"])
19804
+ print(f"Installed template: {r['name']} ({r.get('label','')})")
19805
+ desc = r.get("description") or ""
19806
+ if desc:
19807
+ print(f" {desc}")
19808
+ print(f" body: {r.get('path','')}")
19809
+ print(f" source: {r.get('source','')}")
19810
+ ig = r.get("_ignored_executable_fields") or []
19811
+ if ig:
19812
+ print(f" NOTE: ignored executable-looking fields (never run): {', '.join(ig)}")
19813
+ print(f"Use with: loki init --template {r['name']}")
19814
+ PYEOF
19815
+ else
19816
+ echo -e "${RED}Install failed:${NC} $result"
19817
+ return 1
19818
+ fi
19819
+ ;;
19820
+
19821
+ list|installed)
19822
+ echo -e "${BOLD}Hub-installed Templates${NC}"
19823
+ echo ""
19824
+ if [ ! -f "$hub_py" ]; then
19825
+ echo " (none)"
19826
+ else
19827
+ local _tpls_json
19828
+ _tpls_json="$(python3 "$hub_py" list-templates 2>/dev/null)"
19829
+ HUB_ITEMS="$_tpls_json" python3 - << 'PYEOF'
19830
+ import json, os
19831
+ try:
19832
+ items = json.loads(os.environ.get("HUB_ITEMS", "") or "[]")
19833
+ except Exception:
19834
+ items = []
19835
+ if not items:
19836
+ print(" (none)")
19837
+ else:
19838
+ for t in items:
19839
+ print(f" {t.get('name',''):24s} {t.get('label','')}")
19840
+ print(f"\n Total: {len(items)} installed template(s)")
19841
+ PYEOF
19842
+ fi
19843
+ ;;
19844
+
19845
+ --help|-h|help)
19846
+ echo -e "${BOLD}loki template${NC} - PRD template marketplace (R10)"
19847
+ echo ""
19848
+ echo "Usage: loki template <command> [args]"
19849
+ echo ""
19850
+ echo "Commands:"
19851
+ echo " install <source> Install a community template (local/git/url manifest)"
19852
+ echo " list List templates installed from a hub source"
19853
+ echo " help Show this help"
19854
+ echo ""
19855
+ echo "Examples:"
19856
+ echo " loki template install ./my-template/manifest.json"
19857
+ echo " loki template install https://github.com/owner/loki-template-rust.git"
19858
+ echo " loki template list"
19859
+ echo ""
19860
+ echo "Installed templates are usable via 'loki init --template <name>'."
19861
+ echo "Built-in templates ship in templates/ (see 'loki init --list')."
19862
+ echo ""
19863
+ echo "Note: install is install-from-source (git/local/url). A hosted"
19864
+ echo "central hub registry is future work. Manifests are validated as"
19865
+ echo "DATA only; no code from a manifest is ever executed."
19866
+ echo ""
19867
+ ;;
19868
+ *)
19869
+ echo -e "${RED}Unknown template command: $subcommand${NC}"
19870
+ echo "Run 'loki template help' for usage."
19871
+ return 1
19872
+ ;;
19873
+ esac
19874
+ }
19875
+
19565
19876
  # Agent type dispatch (v6.7.0)
19566
19877
  cmd_agent() {
19567
19878
  local subcommand="${1:-list}"
@@ -19592,6 +19903,20 @@ import json, sys, os
19592
19903
  with open(os.environ["TYPES_FILE"]) as f:
19593
19904
  agents = json.load(f)
19594
19905
 
19906
+ # R10: union built-in agents with hub-installed agents (.loki/agents/installed.json).
19907
+ try:
19908
+ import importlib.util as _ilu
19909
+ _hub_path = os.path.join(os.path.dirname(os.path.abspath(os.environ["TYPES_FILE"])), "hub_install.py")
19910
+ _spec = _ilu.spec_from_file_location("loki_hub_install", _hub_path)
19911
+ _hub = _ilu.module_from_spec(_spec)
19912
+ _spec.loader.exec_module(_hub)
19913
+ _seen = {a.get("type") for a in agents}
19914
+ for _inst in _hub.installed_agent_list():
19915
+ if _inst.get("type") not in _seen:
19916
+ agents.append(_inst)
19917
+ except Exception:
19918
+ pass
19919
+
19595
19920
  filter_swarm = os.environ.get("FILTER_SWARM", "")
19596
19921
  if filter_swarm and filter_swarm.startswith("--swarm"):
19597
19922
  # Handle --swarm=X or --swarm X
@@ -19634,6 +19959,20 @@ import json, sys, os
19634
19959
  with open(os.environ["TYPES_FILE"]) as f:
19635
19960
  agents = json.load(f)
19636
19961
 
19962
+ # R10: include hub-installed agents in the lookup.
19963
+ try:
19964
+ import importlib.util as _ilu
19965
+ _hub_path = os.path.join(os.path.dirname(os.path.abspath(os.environ["TYPES_FILE"])), "hub_install.py")
19966
+ _spec = _ilu.spec_from_file_location("loki_hub_install", _hub_path)
19967
+ _hub = _ilu.module_from_spec(_spec)
19968
+ _spec.loader.exec_module(_hub)
19969
+ _seen = {a.get("type") for a in agents}
19970
+ for _inst in _hub.installed_agent_list():
19971
+ if _inst.get("type") not in _seen:
19972
+ agents.append(_inst)
19973
+ except Exception:
19974
+ pass
19975
+
19637
19976
  target = os.environ["AGENT_TYPE"]
19638
19977
  found = None
19639
19978
  for a in agents:
@@ -19671,6 +20010,17 @@ PYEOF
19671
20010
  import json, os
19672
20011
  with open(os.environ['TYPES_FILE']) as f:
19673
20012
  agents = json.load(f)
20013
+ try:
20014
+ import importlib.util as _ilu
20015
+ _hp = os.path.join(os.path.dirname(os.path.abspath(os.environ['TYPES_FILE'])), 'hub_install.py')
20016
+ _sp = _ilu.spec_from_file_location('loki_hub_install', _hp)
20017
+ _hub = _ilu.module_from_spec(_sp); _sp.loader.exec_module(_hub)
20018
+ _seen = {a.get('type') for a in agents}
20019
+ for _i in _hub.installed_agent_list():
20020
+ if _i.get('type') not in _seen:
20021
+ agents.append(_i)
20022
+ except Exception:
20023
+ pass
19674
20024
  for a in agents:
19675
20025
  if a['type'] == os.environ['AGENT_TYPE']:
19676
20026
  print(a['persona'])
@@ -19742,6 +20092,17 @@ USER TASK: ${prompt}"
19742
20092
  import json, os
19743
20093
  with open(os.environ['TYPES_FILE']) as f:
19744
20094
  agents = json.load(f)
20095
+ try:
20096
+ import importlib.util as _ilu
20097
+ _hp = os.path.join(os.path.dirname(os.path.abspath(os.environ['TYPES_FILE'])), 'hub_install.py')
20098
+ _sp = _ilu.spec_from_file_location('loki_hub_install', _hp)
20099
+ _hub = _ilu.module_from_spec(_sp); _sp.loader.exec_module(_hub)
20100
+ _seen = {a.get('type') for a in agents}
20101
+ for _i in _hub.installed_agent_list():
20102
+ if _i.get('type') not in _seen:
20103
+ agents.append(_i)
20104
+ except Exception:
20105
+ pass
19745
20106
  for a in agents:
19746
20107
  if a['type'] == os.environ['AGENT_TYPE']:
19747
20108
  print(a['persona'])
@@ -19785,6 +20146,17 @@ for a in agents:
19785
20146
  import json, os
19786
20147
  with open(os.environ['TYPES_FILE']) as f:
19787
20148
  agents = json.load(f)
20149
+ try:
20150
+ import importlib.util as _ilu
20151
+ _hp = os.path.join(os.path.dirname(os.path.abspath(os.environ['TYPES_FILE'])), 'hub_install.py')
20152
+ _sp = _ilu.spec_from_file_location('loki_hub_install', _hp)
20153
+ _hub = _ilu.module_from_spec(_sp); _sp.loader.exec_module(_hub)
20154
+ _seen = {a.get('type') for a in agents}
20155
+ for _i in _hub.installed_agent_list():
20156
+ if _i.get('type') not in _seen:
20157
+ agents.append(_i)
20158
+ except Exception:
20159
+ pass
19788
20160
  for a in agents:
19789
20161
  if a['type'] == os.environ['AGENT_TYPE']:
19790
20162
  print(a['persona'])
@@ -19822,17 +20194,81 @@ $diff"
19822
20194
  esac
19823
20195
  ;;
19824
20196
 
20197
+ install)
20198
+ # R10: install a community agent from a source (local path /
20199
+ # git repo / raw URL containing a manifest). DATA-ONLY install --
20200
+ # never executes code from the manifest. See agents/hub_install.py.
20201
+ local source="${1:-}"
20202
+ if [ -z "$source" ]; then
20203
+ echo -e "${RED}Usage: loki agent install <source>${NC}"
20204
+ echo " source: local path, git repo URL, or raw manifest URL"
20205
+ return 1
20206
+ fi
20207
+ local hub_py="${script_dir}/../agents/hub_install.py"
20208
+ [ -f "$hub_py" ] || hub_py="agents/hub_install.py"
20209
+ if [ ! -f "$hub_py" ]; then
20210
+ echo -e "${RED}Error: agents/hub_install.py not found${NC}"
20211
+ return 1
20212
+ fi
20213
+ local result
20214
+ if result=$(python3 "$hub_py" install-agent "$source" 2>&1); then
20215
+ AGENT_RESULT="$result" python3 - << 'PYEOF'
20216
+ import json, os
20217
+ r = json.loads(os.environ["AGENT_RESULT"])
20218
+ print(f"Installed agent: {r['type']} ({r.get('name','')})")
20219
+ print(f" swarm: {r.get('swarm','')}")
20220
+ print(f" source: {r.get('source','')}")
20221
+ ig = r.get("_ignored_executable_fields") or []
20222
+ if ig:
20223
+ print(f" NOTE: ignored executable-looking fields (never run): {', '.join(ig)}")
20224
+ print("Stored in .loki/agents/installed.json -- visible to 'loki agent list/info/run'.")
20225
+ PYEOF
20226
+ else
20227
+ echo -e "${RED}Install failed:${NC} $result"
20228
+ return 1
20229
+ fi
20230
+ ;;
20231
+
20232
+ installed)
20233
+ # R10: list agents installed from a hub source.
20234
+ local hub_py="${script_dir}/../agents/hub_install.py"
20235
+ [ -f "$hub_py" ] || hub_py="agents/hub_install.py"
20236
+ echo -e "${BOLD}Hub-installed Agents${NC}"
20237
+ echo ""
20238
+ if [ ! -f "$hub_py" ]; then
20239
+ echo " (none)"
20240
+ else
20241
+ local _agents_json
20242
+ _agents_json="$(python3 "$hub_py" list-agents 2>/dev/null)"
20243
+ HUB_ITEMS="$_agents_json" python3 - << 'PYEOF'
20244
+ import json, os
20245
+ try:
20246
+ items = json.loads(os.environ.get("HUB_ITEMS", "") or "[]")
20247
+ except Exception:
20248
+ items = []
20249
+ if not items:
20250
+ print(" (none)")
20251
+ else:
20252
+ for a in items:
20253
+ print(f" {a.get('type',''):24s} {a.get('name','')} [{a.get('swarm','')}]")
20254
+ print(f"\n Total: {len(items)} installed agent(s)")
20255
+ PYEOF
20256
+ fi
20257
+ ;;
20258
+
19825
20259
  --help|-h|help)
19826
20260
  echo -e "${BOLD}loki agent${NC} - Agent type dispatch (v6.7.0)"
19827
20261
  echo ""
19828
20262
  echo "Usage: loki agent <command> [args]"
19829
20263
  echo ""
19830
20264
  echo "Commands:"
19831
- echo " list [--swarm NAME] List all agent types (optionally filter by swarm)"
20265
+ echo " list [--swarm NAME] List all agent types (built-in + installed)"
19832
20266
  echo " info <type> Show agent type details"
19833
20267
  echo " run <type> \"<prompt>\" Single-shot: run prompt with agent persona"
19834
20268
  echo " start <type> <prd|prompt> Full autonomous loop with agent persona"
19835
20269
  echo " review [type] Code review with agent persona (default: ops-security)"
20270
+ echo " install <source> Install a community agent (local/git/url manifest)"
20271
+ echo " installed List agents installed from a hub source"
19836
20272
  echo " help Show this help"
19837
20273
  echo ""
19838
20274
  echo "Examples:"
@@ -19842,6 +20278,12 @@ $diff"
19842
20278
  echo " loki agent run eng-frontend \"Create a responsive navbar\""
19843
20279
  echo " loki agent start eng-backend ./api-prd.md"
19844
20280
  echo " loki agent review ops-security"
20281
+ echo " loki agent install ./my-agent/manifest.json"
20282
+ echo " loki agent install https://github.com/owner/loki-agent-rust.git"
20283
+ echo ""
20284
+ echo "Note: 'install' is install-from-source (git/local/url). A hosted"
20285
+ echo "central hub registry is future work. Manifests are validated as"
20286
+ echo "DATA only; no code from a manifest is ever executed."
19845
20287
  echo ""
19846
20288
  ;;
19847
20289
  *)
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "7.16.1"
10
+ __version__ = "7.17.0"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v7.16.1
5
+ **Version:** v7.17.0
6
6
 
7
7
  ---
8
8