ndomo 0.1.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.
Files changed (247) hide show
  1. package/.bun-version +1 -0
  2. package/.dockerignore +79 -0
  3. package/.editorconfig +18 -0
  4. package/.env.example +19 -0
  5. package/.github/CODEOWNERS +8 -0
  6. package/.github/ISSUE_TEMPLATE/bug_report.yml +62 -0
  7. package/.github/ISSUE_TEMPLATE/config.yml +2 -0
  8. package/.github/ISSUE_TEMPLATE/feature_request.yml +34 -0
  9. package/.github/dependabot.yml +36 -0
  10. package/.github/pull_request_template.md +24 -0
  11. package/.github/release.yml +30 -0
  12. package/.github/workflows/gitleaks.yml +28 -0
  13. package/.github/workflows/release-please.yml +27 -0
  14. package/.github/workflows/smoke.yml +29 -0
  15. package/.husky/commit-msg +1 -0
  16. package/CHANGELOG.md +114 -0
  17. package/Dockerfile +32 -0
  18. package/README.es.md +174 -0
  19. package/README.md +187 -0
  20. package/agents/chronicler.md +98 -0
  21. package/agents/ci-smith.md +136 -0
  22. package/agents/craftsman.md +341 -0
  23. package/agents/deploy-smith.md +138 -0
  24. package/agents/foreman.md +377 -0
  25. package/agents/go-smith.md +164 -0
  26. package/agents/guild.md +188 -0
  27. package/agents/inspector.md +83 -0
  28. package/agents/js-smith.md +127 -0
  29. package/agents/ops-scout.md +173 -0
  30. package/agents/painter.md +200 -0
  31. package/agents/python-smith.md +120 -0
  32. package/agents/ranger.md +307 -0
  33. package/agents/release-smith.md +165 -0
  34. package/agents/rust-smith.md +159 -0
  35. package/agents/sage.md +178 -0
  36. package/agents/scout.md +144 -0
  37. package/agents/scribe.md +156 -0
  38. package/agents/smith.md +201 -0
  39. package/agents/vue-smith.md +155 -0
  40. package/agents/warden.md +216 -0
  41. package/agents/zig-smith.md +156 -0
  42. package/bin/ndomo-analyses.ts +4 -0
  43. package/bin/ndomo-status.ts +4 -0
  44. package/biome.json +57 -0
  45. package/bun.lock +514 -0
  46. package/commitlint.config.js +3 -0
  47. package/config/ndomo.config.json +258 -0
  48. package/config/ndomo.schema.json +166 -0
  49. package/docs/agents.md +375 -0
  50. package/docs/bugs/plan-create-orphan-fk.md +131 -0
  51. package/docs/bugs/task_create_batch-order-index-collision.md +158 -0
  52. package/docs/configuration.md +276 -0
  53. package/docs/database.md +364 -0
  54. package/docs/features/feature-flexible-builder-v1.md +724 -0
  55. package/docs/features/feature-flexible-builder-v2.md +882 -0
  56. package/docs/features/feature-flexible-builder.md +974 -0
  57. package/docs/http-server.md +244 -0
  58. package/docs/installation.md +259 -0
  59. package/docs/integrations.md +129 -0
  60. package/docs/operations/anti-pattern-sub-agent-verify-2026-06-21.md +32 -0
  61. package/docs/operations/audit-v1.md +417 -0
  62. package/docs/operations/audit-v2.md +197 -0
  63. package/docs/operations/audit-v3.md +306 -0
  64. package/docs/operations/db-optimize-foundations.md +123 -0
  65. package/docs/operations/verify-gate-architecture.md +82 -0
  66. package/docs/workflows.md +448 -0
  67. package/opencode.json +5 -0
  68. package/package.json +65 -0
  69. package/release-please-config.json +11 -0
  70. package/scripts/dev-bust-cache.sh +164 -0
  71. package/scripts/install.sh +688 -0
  72. package/scripts/smoke-e2e.ts +704 -0
  73. package/scripts/smoke-hot.ts +417 -0
  74. package/scripts/smoke-http.sh +228 -0
  75. package/scripts/smoke-v4.ts +256 -0
  76. package/scripts/smoke-v5.ts +397 -0
  77. package/scripts/smoke.sh +9 -0
  78. package/scripts/uninstall.sh +224 -0
  79. package/skills/api-security-best-practices/SKILL.md +915 -0
  80. package/skills/bash-scripting/SKILL.md +201 -0
  81. package/skills/bun/SKILL.md +313 -0
  82. package/skills/cavecrew/SKILL.md +82 -0
  83. package/skills/caveman/SKILL.md +74 -0
  84. package/skills/caveman-review/README.md +33 -0
  85. package/skills/caveman-review/SKILL.md +55 -0
  86. package/skills/find-skills/SKILL.md +142 -0
  87. package/skills/frontend-design/LICENSE.txt +177 -0
  88. package/skills/frontend-design/SKILL.md +55 -0
  89. package/skills/golang-patterns/SKILL.md +674 -0
  90. package/skills/golang-security/SKILL.md +185 -0
  91. package/skills/golang-security/evals/evals.json +595 -0
  92. package/skills/golang-security/references/architecture.md +268 -0
  93. package/skills/golang-security/references/checklist.md +80 -0
  94. package/skills/golang-security/references/cookies.md +200 -0
  95. package/skills/golang-security/references/cryptography.md +424 -0
  96. package/skills/golang-security/references/filesystem.md +285 -0
  97. package/skills/golang-security/references/injection.md +315 -0
  98. package/skills/golang-security/references/logging.md +163 -0
  99. package/skills/golang-security/references/memory-safety.md +241 -0
  100. package/skills/golang-security/references/network.md +253 -0
  101. package/skills/golang-security/references/secrets.md +189 -0
  102. package/skills/golang-security/references/third-party.md +159 -0
  103. package/skills/golang-security/references/threat-modeling.md +189 -0
  104. package/skills/golang-testing/SKILL.md +720 -0
  105. package/skills/grill-me/SKILL.md +7 -0
  106. package/skills/javascript-testing-patterns/SKILL.md +537 -0
  107. package/skills/javascript-testing-patterns/references/advanced-testing-patterns.md +513 -0
  108. package/skills/modern-javascript-patterns/SKILL.md +43 -0
  109. package/skills/modern-javascript-patterns/references/advanced-patterns.md +487 -0
  110. package/skills/modern-javascript-patterns/references/details.md +457 -0
  111. package/skills/python-anti-patterns/SKILL.md +349 -0
  112. package/skills/python-design-patterns/SKILL.md +85 -0
  113. package/skills/python-design-patterns/references/details.md +353 -0
  114. package/skills/python-error-handling/SKILL.md +193 -0
  115. package/skills/python-error-handling/references/details.md +171 -0
  116. package/skills/python-testing-patterns/SKILL.md +278 -0
  117. package/skills/python-testing-patterns/references/advanced-patterns.md +411 -0
  118. package/skills/python-testing-patterns/references/details.md +349 -0
  119. package/skills/rust-patterns/SKILL.md +500 -0
  120. package/skills/rust-testing/SKILL.md +501 -0
  121. package/skills/security-review/SKILL.md +504 -0
  122. package/skills/security-review/cloud-infrastructure-security.md +361 -0
  123. package/skills/vue-best-practices/SKILL.md +154 -0
  124. package/skills/vue-best-practices/references/animation-class-based-technique.md +254 -0
  125. package/skills/vue-best-practices/references/animation-state-driven-technique.md +291 -0
  126. package/skills/vue-best-practices/references/component-async.md +97 -0
  127. package/skills/vue-best-practices/references/component-data-flow.md +307 -0
  128. package/skills/vue-best-practices/references/component-fallthrough-attrs.md +174 -0
  129. package/skills/vue-best-practices/references/component-keep-alive.md +137 -0
  130. package/skills/vue-best-practices/references/component-slots.md +216 -0
  131. package/skills/vue-best-practices/references/component-suspense.md +228 -0
  132. package/skills/vue-best-practices/references/component-teleport.md +108 -0
  133. package/skills/vue-best-practices/references/component-transition-group.md +128 -0
  134. package/skills/vue-best-practices/references/component-transition.md +125 -0
  135. package/skills/vue-best-practices/references/composables.md +290 -0
  136. package/skills/vue-best-practices/references/directives.md +162 -0
  137. package/skills/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +159 -0
  138. package/skills/vue-best-practices/references/perf-v-once-v-memo-directives.md +182 -0
  139. package/skills/vue-best-practices/references/perf-virtualize-large-lists.md +187 -0
  140. package/skills/vue-best-practices/references/plugins.md +166 -0
  141. package/skills/vue-best-practices/references/reactivity.md +344 -0
  142. package/skills/vue-best-practices/references/render-functions.md +201 -0
  143. package/skills/vue-best-practices/references/sfc.md +310 -0
  144. package/skills/vue-best-practices/references/state-management.md +135 -0
  145. package/skills/vue-best-practices/references/updated-hook-performance.md +187 -0
  146. package/skills/vue-pinia-best-practices/SKILL.md +21 -0
  147. package/skills/vue-pinia-best-practices/reference/pinia-no-active-pinia-error.md +248 -0
  148. package/skills/vue-pinia-best-practices/reference/pinia-setup-store-return-all-state.md +227 -0
  149. package/skills/vue-pinia-best-practices/reference/pinia-store-destructuring-breaks-reactivity.md +193 -0
  150. package/skills/vue-pinia-best-practices/reference/state-url-for-ephemeral-filters.md +238 -0
  151. package/skills/vue-pinia-best-practices/reference/state-use-pinia-for-large-apps.md +262 -0
  152. package/skills/vue-pinia-best-practices/reference/store-method-binding-parentheses.md +191 -0
  153. package/skills/zig-0.16/SKILL.md +840 -0
  154. package/skills/zig-0.16/scripts/check-zig-version.sh +21 -0
  155. package/src/cli/analyses.ts +280 -0
  156. package/src/cli/index.ts +108 -0
  157. package/src/cli/serve.ts +192 -0
  158. package/src/cli/smoke.ts +131 -0
  159. package/src/cli/status.test.ts +204 -0
  160. package/src/cli/status.ts +263 -0
  161. package/src/cli/vacuum.test.ts +82 -0
  162. package/src/cli/vacuum.ts +96 -0
  163. package/src/config/schema.test.ts +88 -0
  164. package/src/config/schema.ts +64 -0
  165. package/src/db/analyses-migration.test.ts +210 -0
  166. package/src/db/analyses.test.ts +466 -0
  167. package/src/db/analyses.ts +375 -0
  168. package/src/db/auto-checkpoint.ts +131 -0
  169. package/src/db/client.test.ts +129 -0
  170. package/src/db/client.ts +55 -0
  171. package/src/db/fts-escape.ts +20 -0
  172. package/src/db/incidents.test.ts +201 -0
  173. package/src/db/incidents.ts +93 -0
  174. package/src/db/index.ts +86 -0
  175. package/src/db/migrations-v13.test.ts +141 -0
  176. package/src/db/migrations-v8.test.ts +301 -0
  177. package/src/db/migrations.ts +147 -0
  178. package/src/db/plan-archive.test.ts +180 -0
  179. package/src/db/plan-archive.ts +274 -0
  180. package/src/db/plan-create.test.ts +276 -0
  181. package/src/db/plan-create.ts +78 -0
  182. package/src/db/plan-files.test.ts +289 -0
  183. package/src/db/plan-update-status.ts +287 -0
  184. package/src/db/plans.test.ts +490 -0
  185. package/src/db/plans.ts +534 -0
  186. package/src/db/resolve-project-dir.test.ts +143 -0
  187. package/src/db/resolve-project-dir.ts +75 -0
  188. package/src/db/rollbacks.test.ts +150 -0
  189. package/src/db/rollbacks.ts +67 -0
  190. package/src/db/schema.ts +907 -0
  191. package/src/db/sessions.test.ts +80 -0
  192. package/src/db/sessions.ts +135 -0
  193. package/src/db/shutdown.test.ts +147 -0
  194. package/src/db/shutdown.ts +45 -0
  195. package/src/db/tasks.test.ts +921 -0
  196. package/src/db/tasks.ts +747 -0
  197. package/src/db/types.ts +619 -0
  198. package/src/http/__tests__/auth.test.ts +196 -0
  199. package/src/http/__tests__/routes.test.ts +465 -0
  200. package/src/http/__tests__/sse.test.ts +317 -0
  201. package/src/http/auth.ts +72 -0
  202. package/src/http/middleware/cors.ts +53 -0
  203. package/src/http/middleware/security-headers.ts +21 -0
  204. package/src/http/routes/events.ts +112 -0
  205. package/src/http/routes/health.ts +51 -0
  206. package/src/http/routes/plans.ts +66 -0
  207. package/src/http/routes/sessions.ts +50 -0
  208. package/src/http/routes/tasks.ts +60 -0
  209. package/src/http/server.ts +95 -0
  210. package/src/http/sse.ts +116 -0
  211. package/src/index.ts +37 -0
  212. package/src/lib.ts +65 -0
  213. package/src/mem/scoped.ts +65 -0
  214. package/src/orchestrator/background.test.ts +268 -0
  215. package/src/orchestrator/background.ts +293 -0
  216. package/src/orchestrator/memory-hook.ts +182 -0
  217. package/src/orchestrator/reconciler.ts +123 -0
  218. package/src/orchestrator/scheduler.test.ts +300 -0
  219. package/src/orchestrator/scheduler.ts +243 -0
  220. package/src/plugin.test.ts +2574 -0
  221. package/src/plugin.ts +1690 -0
  222. package/src/sdk/client.ts +66 -0
  223. package/src/worktrees/manager.ts +236 -0
  224. package/src/worktrees/state.ts +87 -0
  225. package/tests/integration/ranger-flow.test.ts +257 -0
  226. package/tools/analysis_archive.ts +28 -0
  227. package/tools/analysis_create.ts +55 -0
  228. package/tools/analysis_get.ts +33 -0
  229. package/tools/analysis_link_plan.ts +44 -0
  230. package/tools/analysis_list.ts +48 -0
  231. package/tools/analysis_search.ts +36 -0
  232. package/tools/analysis_update.ts +44 -0
  233. package/tools/plan_approve.ts +31 -0
  234. package/tools/plan_create.ts +58 -0
  235. package/tools/plan_get.ts +40 -0
  236. package/tools/plan_list.ts +37 -0
  237. package/tools/plan_search.ts +34 -0
  238. package/tools/plan_update_status.ts +71 -0
  239. package/tools/session_checkpoint.ts +31 -0
  240. package/tools/session_end.ts +26 -0
  241. package/tools/session_start.ts +43 -0
  242. package/tools/task_create_batch.ts +70 -0
  243. package/tools/task_list.ts +35 -0
  244. package/tools/task_next_for_agent.ts +30 -0
  245. package/tools/task_search.ts +34 -0
  246. package/tools/task_update_status.ts +37 -0
  247. package/tsconfig.json +31 -0
@@ -0,0 +1,688 @@
1
+ #!/usr/bin/env bash
2
+ # ndomo installer — copies agents, skills, and config into ~/.config/opencode/
3
+ # Usage: ./install.sh [OPTIONS]
4
+ # curl -fsSL https://raw.githubusercontent.com/darrenhinde/OpenAgentsControl/main/install.sh | bash
5
+
6
+ # ── Pipe detection (must be first, before set -e) ──────────────────────────────
7
+ # When invoked via pipe/stdin, clone repo to temp dir and re-exec from disk.
8
+ if [[ -z "${BASH_SOURCE[0]:-}" || "${BASH_SOURCE[0]}" == "bash" || "${BASH_SOURCE[0]}" == "/dev/stdin" ]]; then
9
+ PIPED=true
10
+
11
+ REPO_URL="${NDOMO_REPO_URL:-https://github.com/darrenhinde/OpenAgentsControl}"
12
+ REPO_BRANCH="${NDOMO_REPO_BRANCH:-main}"
13
+
14
+ # Parse --repo= and --branch= from args early (for piped mode)
15
+ for arg in "$@"; do
16
+ case "$arg" in
17
+ --repo=*) REPO_URL="${arg#--repo=}" ;;
18
+ --branch=*) REPO_BRANCH="${arg#--branch=}" ;;
19
+ esac
20
+ done
21
+
22
+ TMPDIR=$(mktemp -d -t ndomo-install-XXXXXX)
23
+ trap "rm -rf $TMPDIR" EXIT
24
+
25
+ if command -v git &>/dev/null; then
26
+ git clone --depth=1 --branch "$REPO_BRANCH" "$REPO_URL" "$TMPDIR/repo" 2>/dev/null
27
+ elif command -v curl &>/dev/null; then
28
+ curl -fsSL "${REPO_URL}/archive/refs/heads/${REPO_BRANCH}.tar.gz" | tar -xz -C "$TMPDIR"
29
+ mv "$TMPDIR/OpenAgentsControl-${REPO_BRANCH}" "$TMPDIR/repo" 2>/dev/null || mv "$TMPDIR"/*-main "$TMPDIR/repo"
30
+ else
31
+ echo "[error] Need git or curl to install from URL" >&2
32
+ exit 1
33
+ fi
34
+
35
+ exec bash "${TMPDIR}/repo/scripts/install.sh" "$@"
36
+ fi
37
+ PIPED=false
38
+
39
+ set -euo pipefail
40
+
41
+ # ── Colors ────────────────────────────────────────────────────────────────────
42
+ RED='\033[0;31m'
43
+ GREEN='\033[0;32m'
44
+ YELLOW='\033[1;33m'
45
+ BLUE='\033[0;34m'
46
+ BOLD='\033[1m'
47
+ NC='\033[0m' # No Color
48
+
49
+ # ── Helpers ───────────────────────────────────────────────────────────────────
50
+ info() { printf "${BLUE}[info]${NC} %s\n" "$*"; }
51
+ ok() { printf "${GREEN}[ok]${NC} %s\n" "$*"; }
52
+ warn() { printf "${YELLOW}[warn]${NC} %s\n" "$*"; }
53
+ err() { printf "${RED}[error]${NC} %s\n" "$*" >&2; }
54
+ die() { err "$*"; exit 1; }
55
+
56
+ # Escape characters that have special meaning in sed replacement strings.
57
+ # Usage: sed_escape <value>
58
+ sed_escape() {
59
+ # Order matters: escape backslashes first, then & and the delimiter.
60
+ printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/|/\\|/g' -e 's/&/\\&/g'
61
+ }
62
+
63
+ # ── Provider selection ─────────────────────────────────────────────────────────
64
+ select_provider() {
65
+ local catalog_url="https://models.dev/catalog.json"
66
+ local cache="${HOME}/.cache/ndomo/models-catalog.json"
67
+ local catalog=""
68
+
69
+ # Try network first
70
+ if command -v curl &>/dev/null; then
71
+ catalog=$(curl -fsSL --max-time 10 "$catalog_url" 2>/dev/null) || catalog=""
72
+ fi
73
+
74
+ # Cache fallback
75
+ if [[ -z "$catalog" && -f "$cache" ]]; then
76
+ catalog=$(cat "$cache")
77
+ fi
78
+
79
+ # No catalog at all: skip with warning
80
+ if [[ -z "$catalog" ]]; then
81
+ warn "Could not fetch models.dev catalog (no network or curl missing)"
82
+ warn "Skipping provider selection — agents will use preset models"
83
+ return 1
84
+ fi
85
+
86
+ # Cache for next time
87
+ mkdir -p "$(dirname "$cache")"
88
+ echo "$catalog" > "$cache"
89
+
90
+ # Parse and display
91
+ local providers
92
+ providers=$(echo "$catalog" | jq -r '.providers | to_entries | sort_by(.value.name) | .[] | "\(.key)\t\(.value.name)"') || true
93
+
94
+ # Show top 20 with numbers
95
+ echo ""
96
+ printf "${BOLD}Select a model provider:${NC}\n"
97
+ printf "${BOLD} %-4s %-30s %s${NC}\n" "#" "ID" "Name"
98
+ local i=1
99
+ echo "$providers" | head -20 | while IFS=$'\t' read -r id name; do
100
+ printf " %-4s %-30s %s\n" "$i" "$id" "$name"
101
+ i=$((i+1))
102
+ done
103
+
104
+ # Get user choice
105
+ read -rp "$(printf "${BOLD}Provider [1-20, or type id]: ${NC}")" choice
106
+
107
+ # Resolve
108
+ local chosen_id
109
+ if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le 20 ]]; then
110
+ chosen_id=$(echo "$providers" | sed -n "${choice}p" | cut -f1)
111
+ else
112
+ chosen_id="$choice"
113
+ fi
114
+
115
+ if [[ -z "$chosen_id" ]]; then
116
+ return 1
117
+ fi
118
+
119
+ PROVIDER="$chosen_id"
120
+ ok "Provider set to: $PROVIDER"
121
+ }
122
+
123
+ # ── Preset application ───────────────────────────────────────────────────────
124
+ # Apply preset models/temperature from ndomo.config.json to agent .md files
125
+ apply_preset() {
126
+ local preset="$1"
127
+ local config_json="$2"
128
+ local agent_dst="$3"
129
+ local updated=0
130
+ local skipped=0
131
+ local f base name model temp effort
132
+
133
+ for f in "${agent_dst}"/*.md; do
134
+ [[ -f "$f" ]] || continue
135
+ base="$(basename "$f")"
136
+ name="${base%.md}"
137
+
138
+ # Prevent path traversal: reject names with slashes, dots, or special chars
139
+ if [[ ! "$name" =~ ^[a-zA-Z0-9_-]+$ ]]; then
140
+ warn "Skipping invalid agent name in filename: '$name'"
141
+ skipped=$((skipped + 1))
142
+ continue
143
+ fi
144
+
145
+ model=$(jq -r --arg preset "$preset" --arg name "$name" \
146
+ '.presets[$preset][$name].model // empty' "$config_json" 2>/dev/null || true)
147
+ temp=$(jq -r --arg preset "$preset" --arg name "$name" \
148
+ '.presets[$preset][$name].temperature // empty' "$config_json" 2>/dev/null || true)
149
+ effort=$(jq -r --arg preset "$preset" --arg name "$name" \
150
+ '.presets[$preset][$name].reasoning_effort // empty' "$config_json" 2>/dev/null || true)
151
+
152
+ if [[ -z "$model" ]]; then
153
+ warn "Agent '${name}' has no entry in preset '${preset}', skipping"
154
+ skipped=$((skipped + 1))
155
+ continue
156
+ fi
157
+
158
+ local esc_model esc_temp esc_effort
159
+ esc_model=$(sed_escape "$model")
160
+ esc_temp=$(sed_escape "$temp")
161
+ esc_effort=$(sed_escape "$effort")
162
+ sed -i.bak -E "0,/^model:/{s|^model:.*|model: ${esc_model}|}" "$f"
163
+ if [[ -n "$temp" ]]; then
164
+ sed -i.bak -E "0,/^temperature:/{s|^temperature:.*|temperature: ${esc_temp}|}" "$f"
165
+ fi
166
+ # reasoning_effort: snake_case in ndomo config -> camelCase reasoningEffort in agent frontmatter
167
+ if [[ -n "$effort" ]]; then
168
+ if grep -qE '^reasoningEffort:[[:space:]]' "$f"; then
169
+ sed -i.bak -E "0,/^reasoningEffort:[[:space:]].*\$/{s|^reasoningEffort:[[:space:]].*\$|reasoningEffort: ${esc_effort}|;}" "$f" && rm -f "${f}.bak"
170
+ else
171
+ if grep -qE '^temperature:[[:space:]]' "$f"; then
172
+ sed -i.bak -E "0,/^temperature:[[:space:]].*\$/{s|(^temperature:[[:space:]].*\$)|\1\nreasoningEffort: ${esc_effort}|;}" "$f" && rm -f "${f}.bak"
173
+ elif grep -qE '^model:[[:space:]]' "$f"; then
174
+ sed -i.bak -E "0,/^model:[[:space:]].*\$/{s|(^model:[[:space:]].*\$)|\1\nreasoningEffort: ${esc_effort}|;}" "$f" && rm -f "${f}.bak"
175
+ else
176
+ sed -i.bak -E "0,/^---\$/{s|(^---\$)|\1\nreasoningEffort: ${esc_effort}|;}" "$f" && rm -f "${f}.bak"
177
+ fi
178
+ fi
179
+ fi
180
+ rm -f "${f}.bak"
181
+ updated=$((updated + 1))
182
+ done
183
+
184
+ echo "$updated $skipped"
185
+ }
186
+
187
+ # Swap only the provider/ prefix on model: lines — preserves model ID from preset
188
+ apply_provider_prefix() {
189
+ local provider="$1"
190
+ local agent_dst="$2"
191
+ local updated=0
192
+ local f
193
+
194
+ for f in "${agent_dst}"/*.md; do
195
+ [[ -f "$f" ]] || continue
196
+ local esc_provider
197
+ esc_provider=$(sed_escape "$provider")
198
+ if sed -i.bak -E "0,/^model:/{s|^model: [^/]+/|model: ${esc_provider}/|}" "$f" 2>/dev/null; then
199
+ rm -f "${f}.bak"
200
+ updated=$((updated + 1))
201
+ fi
202
+ done
203
+
204
+ echo "$updated"
205
+ }
206
+
207
+ # Print a table: agent | preset model | current provider prefix (for TTY flow)
208
+ show_preset_table() {
209
+ local preset="$1"
210
+ local config_json="$2"
211
+ local agent_dst="$3"
212
+ local name preset_model current_prefix sep
213
+
214
+ sep="$(printf '%.0s─' {1..20})"
215
+ printf "\n${BOLD}Preset '${preset}' — agent model overview:${NC}\n"
216
+ printf " ${BOLD}%-20s %-35s %s${NC}\n" "Agent" "Preset Model" "Current Prefix"
217
+ printf " %-20s %-35s %s\n" "$sep" "$(printf '%.0s─' {1..35})" "$(printf '%.0s─' {1..20})"
218
+
219
+ for f in "${agent_dst}"/*.md; do
220
+ [[ -f "$f" ]] || continue
221
+ name="$(basename "$f" .md)"
222
+ preset_model=$(jq -r --arg preset "$preset" --arg name "$name" \
223
+ '.presets[$preset][$name].model // empty' "$config_json" 2>/dev/null || true)
224
+ current_prefix=$(sed -n 's/^model: *\([^/]*\)\/.*/\1/p' "$f" 2>/dev/null || echo "none")
225
+ [[ -n "$preset_model" ]] && printf " %-20s %-35s %s\n" "$name" "$preset_model" "${current_prefix:-none}"
226
+ done
227
+ }
228
+
229
+ # Install ndomo package into ~/.config/opencode/node_modules/
230
+ # Strategy order (avoids Bun cache stale with symlinks — see docs/installation.md):
231
+ # 1. file: dep + bun install → real copy in node_modules (best, no symlink)
232
+ # 2. bun link → managed symlink (second best, bun-tracked)
233
+ # 3. manual symlink → last resort only (may cause Bun cache stale)
234
+ install_ndomo_package() {
235
+ local project_root="$1"
236
+ local config_dir="$2"
237
+ local pkg_json="$config_dir/package.json"
238
+ local nm_ndomo="$config_dir/node_modules/ndomo"
239
+
240
+ # Skip if user opted out
241
+ if [[ "${NDOMO_SKIP_PACKAGE_INSTALL:-0}" == "1" ]]; then
242
+ info "skipping ndomo package install (NDOMO_SKIP_PACKAGE_INSTALL=1)"
243
+ return 0
244
+ fi
245
+
246
+ # If existing install is a symlink, remove it — symlinks cause Bun cache stale
247
+ if [[ -L "$nm_ndomo" ]]; then
248
+ warn "existing ndomo install is a symlink (causes Bun cache stale in dev)"
249
+ info "removing symlink, will reinstall as real copy..."
250
+ rm -f "$nm_ndomo"
251
+ elif [[ -e "$nm_ndomo" ]]; then
252
+ info "ndomo already installed at $nm_ndomo"
253
+ return 0
254
+ fi
255
+
256
+ # Need package.json in config dir for bun-based strategies
257
+ if [[ ! -f "$pkg_json" ]]; then
258
+ warn "$pkg_json not found, falling back to manual symlink"
259
+ mkdir -p "$config_dir/node_modules"
260
+ ln -sfn "$project_root" "$nm_ndomo"
261
+ ok "ndomo symlinked at $nm_ndomo (last resort — no package.json)"
262
+ warn "symlink install may cause Bun cache stale — run 'bun run dev:bust' to recover"
263
+ return 0
264
+ fi
265
+
266
+ # Strategy 1: file: dep + bun install → real copy (no symlink, no cache stale)
267
+ if command -v bun >/dev/null 2>&1; then
268
+ info "adding ndomo file: dep to $pkg_json"
269
+ if jq --arg path "$project_root" '.dependencies.ndomo = ("file://" + $path)' "$pkg_json" > "$pkg_json.tmp" && mv "$pkg_json.tmp" "$pkg_json"; then
270
+ if (cd "$config_dir" && bun install --no-frozen-lockfile 2>&1); then
271
+ if [[ -e "$nm_ndomo" ]] && [[ ! -L "$nm_ndomo" ]]; then
272
+ ok "ndomo installed via bun (file: dep) — real copy, no symlink"
273
+ return 0
274
+ fi
275
+ fi
276
+ warn "bun install did not materialize ndomo as real copy, trying bun link..."
277
+ else
278
+ warn "jq update of $pkg_json failed, trying bun link..."
279
+ fi
280
+ else
281
+ warn "bun not found in PATH, using symlink fallback"
282
+ fi
283
+
284
+ # Strategy 2: bun link → managed symlink (bun-tracked, better than manual)
285
+ if command -v bun >/dev/null 2>&1; then
286
+ info "trying bun link..."
287
+ if (cd "$project_root" && bun link 2>&1) && (cd "$config_dir" && bun link ndomo 2>&1); then
288
+ if [[ -e "$nm_ndomo" ]]; then
289
+ ok "ndomo linked via bun link (managed symlink)"
290
+ warn "bun link uses symlinks — run 'bun run dev:bust' if cache goes stale"
291
+ return 0
292
+ fi
293
+ fi
294
+ warn "bun link failed, falling back to manual symlink"
295
+ fi
296
+
297
+ # Strategy 3: manual symlink (last resort)
298
+ info "creating manual symlink: $nm_ndomo -> $project_root"
299
+ mkdir -p "$config_dir/node_modules"
300
+ if ln -sfn "$project_root" "$nm_ndomo"; then
301
+ ok "ndomo symlinked at $nm_ndomo (last resort)"
302
+ warn "manual symlink may cause Bun cache stale — run 'bun run dev:bust' to recover"
303
+ else
304
+ die "failed to install ndomo package"
305
+ fi
306
+ }
307
+
308
+ # Symlink project tools/ -> ~/.config/opencode/tools/ for OpenCode custom tools
309
+ install_custom_tools_symlink() {
310
+ local project_root="$1"
311
+ local config_dir="$2"
312
+ local src="$project_root/tools"
313
+ local dst="$config_dir/tools"
314
+
315
+ if [[ ! -d "$src" ]]; then
316
+ warn "No tools/ directory found at $src — skipping"
317
+ return 0
318
+ fi
319
+
320
+ if [[ -e "$dst" ]]; then
321
+ if [[ -L "$dst" ]] && [[ "$(readlink "$dst")" == "$src" ]]; then
322
+ ok "Custom tools symlink already in place: $dst -> $src"
323
+ return 0
324
+ fi
325
+ if [[ -d "$dst" ]] && [[ ! -L "$dst" ]]; then
326
+ warn "Custom tools directory already exists at $dst (not a symlink) — skipping"
327
+ return 0
328
+ fi
329
+ fi
330
+
331
+ mkdir -p "$config_dir"
332
+ ln -sfn "$src" "$dst"
333
+ ok "Symlinked custom tools: $dst -> $src"
334
+ }
335
+
336
+ usage() {
337
+ cat <<EOF
338
+ ${BOLD}ndomo installer — agent preset & provider tool${NC}
339
+
340
+ ${BOLD}Usage:${NC}
341
+ ./install.sh [OPTIONS]
342
+ curl -fsSL https://raw.githubusercontent.com/darrenhinde/OpenAgentsControl/main/install.sh | bash -s [-- OPTIONS]
343
+
344
+ ${BOLD}Options:${NC}
345
+ --with-dcp Also install @tarquinen/opencode-dcp (AGPL-3.0 peer)
346
+ --preset=NAME Use preset from ndomo.config.json (default: "default", options: "default", "budget")
347
+ --provider=ID Override provider prefix on preset models (e.g., opencode, anthropic, openai)
348
+ --no-provider-prompt Skip interactive provider override prompt
349
+ --repo=URL Override repository URL (for piped installs)
350
+ --branch=NAME Override repository branch (for piped installs)
351
+ --uninstall Run uninstaller instead
352
+ --help Show this help
353
+
354
+ ${BOLD}Environment:${NC}
355
+ NDOMO_SKIP_PACKAGE_INSTALL=1 Skip automatic ndomo package installation into
356
+ ~/.config/opencode/ (advanced users only)
357
+
358
+ ${BOLD}Examples:${NC}
359
+ ./install.sh # apply presets.default from ndomo.config.json
360
+ ./install.sh --preset=budget # apply presets.budget
361
+ ./install.sh --provider=opencode # apply preset, swap provider prefix to opencode/
362
+ ./install.sh --with-dcp # include DCP plugin
363
+ ./install.sh --uninstall # remove ndomo
364
+ curl -fsSL https://raw.githubusercontent.com/darrenhinde/OpenAgentsControl/main/install.sh | bash
365
+ EOF
366
+ }
367
+
368
+ # ── Parse flags ───────────────────────────────────────────────────────────────
369
+ WITH_DCP=false
370
+ PRESET="default"
371
+ RUN_UNINSTALL=false
372
+ PROVIDER=""
373
+ PROVIDER_PROMPT=true
374
+
375
+ for arg in "$@"; do
376
+ case "$arg" in
377
+ --with-dcp) WITH_DCP=true ;;
378
+ --preset=*) PRESET="${arg#--preset=}" ;;
379
+ --uninstall) RUN_UNINSTALL=true ;;
380
+ --provider=*) PROVIDER="${arg#--provider=}" ;;
381
+ --no-provider-prompt) PROVIDER_PROMPT=false ;;
382
+ --repo=*) NDOMO_REPO_URL="${arg#--repo=}" ;;
383
+ --branch=*) NDOMO_REPO_BRANCH="${arg#--branch=}" ;;
384
+ --help|-h) usage; exit 0 ;;
385
+ *) die "Unknown option: $arg (try --help)" ;;
386
+ esac
387
+ done
388
+
389
+ # ── Uninstall shortcut ────────────────────────────────────────────────────────
390
+ if [[ "$RUN_UNINSTALL" == true ]]; then
391
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
392
+ exec bash "${SCRIPT_DIR}/uninstall.sh"
393
+ fi
394
+
395
+ # ── Validate preset ──────────────────────────────────────────────────────────
396
+ case "$PRESET" in
397
+ default|budget) ;;
398
+ *) die "Unknown preset: $PRESET (options: default, budget)" ;;
399
+ esac
400
+
401
+ # ── Detect paths ──────────────────────────────────────────────────────────────
402
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
403
+ PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
404
+ CONFIG_DIR="${HOME}/.config/opencode"
405
+
406
+ # ── Prerequisite: bun ─────────────────────────────────────────────────────────
407
+ if ! command -v bun &>/dev/null; then
408
+ die "bun is not installed. Install it with:\n curl -fsSL https://bun.sh/install | bash"
409
+ fi
410
+ ok "bun $(bun --version) found"
411
+
412
+ # ── Step 1: Install dependencies ──────────────────────────────────────────────
413
+ info "Installing dependencies..."
414
+ (cd "$PROJECT_ROOT" && bun install --frozen-lockfile 2>/dev/null || bun install)
415
+ ok "Dependencies installed"
416
+
417
+ # ── Step 2: Build TypeScript ──────────────────────────────────────────────────
418
+ info "Building TypeScript..."
419
+ if grep -q '"build"' "${PROJECT_ROOT}/package.json" 2>/dev/null; then
420
+ (cd "$PROJECT_ROOT" && bun run build)
421
+ else
422
+ (cd "$PROJECT_ROOT" && bun run --bun tsc)
423
+ fi
424
+ ok "Build complete"
425
+
426
+ # ── Step 3: Create config directory ──────────────────────────────────────────
427
+ mkdir -p "${CONFIG_DIR}/agent"
428
+ mkdir -p "${CONFIG_DIR}/skills"
429
+
430
+ # Shared backup directory for all overwritten files
431
+ BACKUP_DIR="${CONFIG_DIR}/.backup-$(date +%Y%m%d-%H%M%S)"
432
+
433
+ # ── Step 4: Backup existing agents + Copy new ones ───────────────────────────
434
+ AGENT_SRC="${PROJECT_ROOT}/agents"
435
+ AGENT_DST="${CONFIG_DIR}/agent"
436
+ AGENT_COUNT=0
437
+ BACKED_UP=0
438
+
439
+ if [[ -d "$AGENT_SRC" ]]; then
440
+ # Backup existing ndomo agents before overwriting
441
+ for f in "${AGENT_SRC}"/*.md; do
442
+ [[ -f "$f" ]] || continue
443
+ base="$(basename "$f")"
444
+ if [[ -f "${AGENT_DST}/${base}" ]]; then
445
+ if [[ $BACKED_UP -eq 0 ]]; then
446
+ mkdir -p "${BACKUP_DIR}"
447
+ info "Backing up existing agents to ${BACKUP_DIR}"
448
+ fi
449
+ cp "${AGENT_DST}/${base}" "${BACKUP_DIR}/${base}"
450
+ BACKED_UP=$((BACKED_UP + 1))
451
+ fi
452
+ done
453
+ if [[ $BACKED_UP -gt 0 ]]; then
454
+ ok "Backed up ${BACKED_UP} existing agent(s)"
455
+ fi
456
+
457
+ # Copy new agents
458
+ for f in "${AGENT_SRC}"/*.md; do
459
+ [[ -f "$f" ]] || continue
460
+ base="$(basename "$f")"
461
+ cp "$f" "${AGENT_DST}/${base}"
462
+ AGENT_COUNT=$((AGENT_COUNT + 1))
463
+ done
464
+ ok "Copied ${AGENT_COUNT} agent(s) to ${AGENT_DST}"
465
+ else
466
+ warn "No agents/ directory found in project root"
467
+ fi
468
+
469
+ # ── Step 5: Backup existing skills + Copy new ones ───────────────────────────
470
+ SKILL_SRC="${PROJECT_ROOT}/skills"
471
+ SKILL_DST="${CONFIG_DIR}/skills"
472
+ SKILL_COUNT=0
473
+ SKILL_BACKED_UP=0
474
+
475
+ if [[ -d "$SKILL_SRC" ]]; then
476
+ for d in "${SKILL_SRC}"/*/; do
477
+ [[ -d "$d" ]] || continue
478
+ name="$(basename "$d")"
479
+ if [[ -d "${SKILL_DST}/${name}" ]]; then
480
+ if [[ $SKILL_BACKED_UP -eq 0 ]]; then
481
+ mkdir -p "${BACKUP_DIR}/skills"
482
+ info "Backing up existing skills to ${BACKUP_DIR}/skills"
483
+ fi
484
+ cp -r "${SKILL_DST}/${name}" "${BACKUP_DIR}/skills/${name}"
485
+ SKILL_BACKED_UP=$((SKILL_BACKED_UP + 1))
486
+ rm -rf "${SKILL_DST}/${name}"
487
+ fi
488
+ cp -r "$d" "${SKILL_DST}/${name}"
489
+ SKILL_COUNT=$((SKILL_COUNT + 1))
490
+ done
491
+ if [[ $SKILL_BACKED_UP -gt 0 ]]; then
492
+ ok "Backed up ${SKILL_BACKED_UP} existing skill(s)"
493
+ fi
494
+ ok "Copied ${SKILL_COUNT} skill(s) to ${SKILL_DST}"
495
+ else
496
+ warn "No skills/ directory found in project root"
497
+ fi
498
+
499
+ # ── Step 5.5: Apply preset + optional provider prefix override ──────────────
500
+ # Always apply preset models/temperature from ndomo.config.json
501
+ CONFIG_JSON="${PROJECT_ROOT}/config/ndomo.config.json"
502
+ if [[ -d "$AGENT_DST" ]]; then
503
+ if ! command -v jq &>/dev/null; then
504
+ warn "jq not found, skipping preset application"
505
+ else
506
+ should_apply_preset=true
507
+ provider_prefix=""
508
+
509
+ if [[ -n "$PROVIDER" ]]; then
510
+ # --provider=ID: apply preset + prefix override
511
+ info "Provider prefix '${PROVIDER}' specified via --provider"
512
+ provider_prefix="$PROVIDER"
513
+ elif [[ -t 0 && "$PROVIDER_PROMPT" == true && "${PIPED:-false}" != "true" ]]; then
514
+ # TTY interactive: show table and ask
515
+ show_preset_table "$PRESET" "$CONFIG_JSON" "$AGENT_DST"
516
+ echo ""
517
+ read -rp "$(printf "${BOLD}Apply preset '${PRESET}' as configured? [Y/n/override]: ${NC}")" user_choice
518
+
519
+ case "${user_choice,,}" in
520
+ n|no)
521
+ warn "Preset application skipped by user"
522
+ should_apply_preset=false
523
+ ;;
524
+ override|o)
525
+ info "Opening provider selector for prefix override..."
526
+ select_provider || true
527
+ if [[ -n "${PROVIDER:-}" ]]; then
528
+ provider_prefix="$PROVIDER"
529
+ else
530
+ warn "Provider selection failed — applying preset without prefix override"
531
+ fi
532
+ ;;
533
+ *) # Y/yes/enter → apply preset, no prefix override
534
+ ;;
535
+ esac
536
+ fi
537
+
538
+ if [[ "$should_apply_preset" == true ]]; then
539
+ # Apply preset models + temperatures
540
+ result=$(apply_preset "$PRESET" "$CONFIG_JSON" "$AGENT_DST")
541
+ updated="${result%% *}"
542
+ skipped="${result##* }"
543
+ ok "Applied preset '${PRESET}' — ${updated} agent(s) updated"
544
+ if [[ "$skipped" -gt 0 ]]; then
545
+ warn "${skipped} agent(s) skipped (no entry in preset '${PRESET}')"
546
+ fi
547
+
548
+ # Apply provider prefix override if requested
549
+ if [[ -n "$provider_prefix" ]]; then
550
+ updated_p=$(apply_provider_prefix "$provider_prefix" "$AGENT_DST")
551
+ ok "Provider prefix override '${provider_prefix}/' applied to ${updated_p} agent(s)"
552
+ fi
553
+ fi
554
+ fi
555
+ fi
556
+
557
+ # ── Step 6: Backup existing config + Copy new ones ───────────────────────────
558
+ CONFIG_JSON="${PROJECT_ROOT}/config/ndomo.config.json"
559
+ SCHEMA_JSON="${PROJECT_ROOT}/config/ndomo.schema.json"
560
+
561
+ if [[ -f "$CONFIG_JSON" ]]; then
562
+ if [[ -f "${CONFIG_DIR}/ndomo.json" ]]; then
563
+ mkdir -p "${BACKUP_DIR}" 2>/dev/null
564
+ cp "${CONFIG_DIR}/ndomo.json" "${BACKUP_DIR}/ndomo.json"
565
+ info "Backed up existing ndomo.json"
566
+ fi
567
+ cp "$CONFIG_JSON" "${CONFIG_DIR}/ndomo.json"
568
+ ok "Copied config.json -> ndomo.json"
569
+ else
570
+ warn "No config/ndomo.config.json found"
571
+ fi
572
+
573
+ if [[ -f "$SCHEMA_JSON" ]]; then
574
+ if [[ -f "${CONFIG_DIR}/ndomo.schema.json" ]]; then
575
+ mkdir -p "${BACKUP_DIR}" 2>/dev/null
576
+ cp "${CONFIG_DIR}/ndomo.schema.json" "${BACKUP_DIR}/ndomo.schema.json"
577
+ info "Backed up existing ndomo.schema.json"
578
+ fi
579
+ cp "$SCHEMA_JSON" "${CONFIG_DIR}/ndomo.schema.json"
580
+ ok "Copied ndomo.schema.json"
581
+ else
582
+ warn "No config/ndomo.schema.json found"
583
+ fi
584
+
585
+ # ── Step 6.5: Register ndomo plugins in opencode.json ──────────────────────
586
+ NDOMO_JSON_PATH="${CONFIG_DIR}/ndomo.json"
587
+ OPENCODE_JSON_PATH="${CONFIG_DIR}/opencode.json"
588
+
589
+ if command -v jq &>/dev/null; then
590
+ # Create opencode.json if missing
591
+ [[ -f "$OPENCODE_JSON_PATH" ]] || echo '{}' > "$OPENCODE_JSON_PATH"
592
+
593
+ # Backup opencode.json if not already backed up
594
+ if [[ -f "$OPENCODE_JSON_PATH" ]]; then
595
+ if [[ ! -f "${BACKUP_DIR}/opencode.json" ]]; then
596
+ mkdir -p "${BACKUP_DIR}" 2>/dev/null
597
+ cp "$OPENCODE_JSON_PATH" "${BACKUP_DIR}/opencode.json"
598
+ info "Backed up opencode.json"
599
+ fi
600
+ fi
601
+
602
+ if [[ -f "$NDOMO_JSON_PATH" ]]; then
603
+ # Extract plugin list (deduped union of plugins + optionalPlugins)
604
+ PLUGIN_LIST=$(jq -r '
605
+ ((.plugins // []) + (.optionalPlugins // [])) | unique | .[]
606
+ ' "$NDOMO_JSON_PATH" 2>/dev/null) || true
607
+
608
+ if [[ -n "$PLUGIN_LIST" ]]; then
609
+ # Build JSON array
610
+ NEW_PLUGINS_JSON=$(printf '%s\n' "$PLUGIN_LIST" | jq -R . | jq -s 'map(select(. != "" and . != null))')
611
+
612
+ # Merge (unique handles idempotency)
613
+ jq --argjson new "$NEW_PLUGINS_JSON" '
614
+ .plugin = ((.plugin // []) + $new | unique)
615
+ ' "$OPENCODE_JSON_PATH" > "${OPENCODE_JSON_PATH}.tmp" \
616
+ && mv "${OPENCODE_JSON_PATH}.tmp" "$OPENCODE_JSON_PATH"
617
+
618
+ ok "Registered $(echo "$PLUGIN_LIST" | wc -l) ndomo plugin(s) in opencode.json"
619
+ else
620
+ info "No ndomo plugins found to register"
621
+ fi
622
+ else
623
+ warn "ndomo.json not found, skipping plugin registration"
624
+ fi
625
+ else
626
+ warn "jq not found, skipping opencode.json plugin registration"
627
+ fi
628
+
629
+ # ── Step 6.6: Install ndomo package in ~/.config/opencode/ ──────────────────
630
+ install_ndomo_package "$PROJECT_ROOT" "$CONFIG_DIR"
631
+
632
+ # ── Step 6.7: Symlink custom tools ──────────────────────────────────────────
633
+ install_custom_tools_symlink "$PROJECT_ROOT" "$CONFIG_DIR"
634
+
635
+ # ── Step 7: Inject preset name into ndomo.json ──────────────────────────────
636
+ NDOMO_JSON="${CONFIG_DIR}/ndomo.json"
637
+ if [[ -f "$NDOMO_JSON" ]]; then
638
+ if command -v jq &>/dev/null; then
639
+ jq --arg p "$PRESET" '. + {preset: $p}' "$NDOMO_JSON" > "${NDOMO_JSON}.tmp" \
640
+ && mv "${NDOMO_JSON}.tmp" "$NDOMO_JSON"
641
+ else
642
+ # Fallback: insert after the opening brace
643
+ sed -i "s/^{/{\n \"preset\": \"${PRESET}\",/" "$NDOMO_JSON"
644
+ fi
645
+ ok "Preset '${PRESET}' written to ndomo.json"
646
+ fi
647
+
648
+ # ── Step 8: Optional DCP install ─────────────────────────────────────────────
649
+ if [[ "$WITH_DCP" == true ]]; then
650
+ info "Installing @tarquinen/opencode-dcp (AGPL-3.0)..."
651
+ opencode plugin @tarquinen/opencode-dcp --global
652
+ ok "DCP plugin installed"
653
+ fi
654
+
655
+ # ── Summary ──────────────────────────────────────────────────────────────────
656
+ echo ""
657
+ printf "${GREEN}${BOLD}════════════════════════════════════════${NC}\n"
658
+ printf "${GREEN}${BOLD} ndomo installed successfully!${NC}\n"
659
+ printf "${GREEN}${BOLD}════════════════════════════════════════${NC}\n"
660
+ echo ""
661
+ printf "${BOLD}Installed agents:${NC}\n"
662
+ printf " %-20s %s\n" "Agent" "File"
663
+ printf " %-20s %s\n" "─────" "────"
664
+ for f in "${AGENT_DST}"/*.md; do
665
+ [[ -f "$f" ]] || continue
666
+ base="$(basename "$f")"
667
+ name="${base%.md}"
668
+ printf " %-20s %s\n" "$name" "$base"
669
+ done
670
+ echo ""
671
+ printf "${BOLD}Installed skills:${NC} "
672
+ # shellcheck disable=SC2012
673
+ ls -1 "${SKILL_DST}" 2>/dev/null | tr '\n' ', ' | sed 's/, $//'
674
+ echo ""
675
+ echo ""
676
+ printf "${BOLD}Config:${NC} ${CONFIG_DIR}/ndomo.json\n"
677
+ printf "${BOLD}OpenCode config:${NC} ${CONFIG_DIR}/opencode.json (ndomo registered)\n"
678
+ printf "${BOLD}Preset:${NC} ${PRESET}\n"
679
+ if [[ -n "${PROVIDER:-}" ]]; then
680
+ printf "${BOLD}Provider:${NC} ${PROVIDER}\n"
681
+ fi
682
+ if [[ "$WITH_DCP" == true ]]; then
683
+ printf "${BOLD}DCP:${NC} installed\n"
684
+ fi
685
+ echo ""
686
+ printf "${BOLD}Next steps:${NC}\n"
687
+ printf " Run ${BLUE}opencode${NC} then ${BLUE}ping all agents${NC} to verify.\n"
688
+ echo ""