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.
- package/.bun-version +1 -0
- package/.dockerignore +79 -0
- package/.editorconfig +18 -0
- package/.env.example +19 -0
- package/.github/CODEOWNERS +8 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +62 -0
- package/.github/ISSUE_TEMPLATE/config.yml +2 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +34 -0
- package/.github/dependabot.yml +36 -0
- package/.github/pull_request_template.md +24 -0
- package/.github/release.yml +30 -0
- package/.github/workflows/gitleaks.yml +28 -0
- package/.github/workflows/release-please.yml +27 -0
- package/.github/workflows/smoke.yml +29 -0
- package/.husky/commit-msg +1 -0
- package/CHANGELOG.md +114 -0
- package/Dockerfile +32 -0
- package/README.es.md +174 -0
- package/README.md +187 -0
- package/agents/chronicler.md +98 -0
- package/agents/ci-smith.md +136 -0
- package/agents/craftsman.md +341 -0
- package/agents/deploy-smith.md +138 -0
- package/agents/foreman.md +377 -0
- package/agents/go-smith.md +164 -0
- package/agents/guild.md +188 -0
- package/agents/inspector.md +83 -0
- package/agents/js-smith.md +127 -0
- package/agents/ops-scout.md +173 -0
- package/agents/painter.md +200 -0
- package/agents/python-smith.md +120 -0
- package/agents/ranger.md +307 -0
- package/agents/release-smith.md +165 -0
- package/agents/rust-smith.md +159 -0
- package/agents/sage.md +178 -0
- package/agents/scout.md +144 -0
- package/agents/scribe.md +156 -0
- package/agents/smith.md +201 -0
- package/agents/vue-smith.md +155 -0
- package/agents/warden.md +216 -0
- package/agents/zig-smith.md +156 -0
- package/bin/ndomo-analyses.ts +4 -0
- package/bin/ndomo-status.ts +4 -0
- package/biome.json +57 -0
- package/bun.lock +514 -0
- package/commitlint.config.js +3 -0
- package/config/ndomo.config.json +258 -0
- package/config/ndomo.schema.json +166 -0
- package/docs/agents.md +375 -0
- package/docs/bugs/plan-create-orphan-fk.md +131 -0
- package/docs/bugs/task_create_batch-order-index-collision.md +158 -0
- package/docs/configuration.md +276 -0
- package/docs/database.md +364 -0
- package/docs/features/feature-flexible-builder-v1.md +724 -0
- package/docs/features/feature-flexible-builder-v2.md +882 -0
- package/docs/features/feature-flexible-builder.md +974 -0
- package/docs/http-server.md +244 -0
- package/docs/installation.md +259 -0
- package/docs/integrations.md +129 -0
- package/docs/operations/anti-pattern-sub-agent-verify-2026-06-21.md +32 -0
- package/docs/operations/audit-v1.md +417 -0
- package/docs/operations/audit-v2.md +197 -0
- package/docs/operations/audit-v3.md +306 -0
- package/docs/operations/db-optimize-foundations.md +123 -0
- package/docs/operations/verify-gate-architecture.md +82 -0
- package/docs/workflows.md +448 -0
- package/opencode.json +5 -0
- package/package.json +65 -0
- package/release-please-config.json +11 -0
- package/scripts/dev-bust-cache.sh +164 -0
- package/scripts/install.sh +688 -0
- package/scripts/smoke-e2e.ts +704 -0
- package/scripts/smoke-hot.ts +417 -0
- package/scripts/smoke-http.sh +228 -0
- package/scripts/smoke-v4.ts +256 -0
- package/scripts/smoke-v5.ts +397 -0
- package/scripts/smoke.sh +9 -0
- package/scripts/uninstall.sh +224 -0
- package/skills/api-security-best-practices/SKILL.md +915 -0
- package/skills/bash-scripting/SKILL.md +201 -0
- package/skills/bun/SKILL.md +313 -0
- package/skills/cavecrew/SKILL.md +82 -0
- package/skills/caveman/SKILL.md +74 -0
- package/skills/caveman-review/README.md +33 -0
- package/skills/caveman-review/SKILL.md +55 -0
- package/skills/find-skills/SKILL.md +142 -0
- package/skills/frontend-design/LICENSE.txt +177 -0
- package/skills/frontend-design/SKILL.md +55 -0
- package/skills/golang-patterns/SKILL.md +674 -0
- package/skills/golang-security/SKILL.md +185 -0
- package/skills/golang-security/evals/evals.json +595 -0
- package/skills/golang-security/references/architecture.md +268 -0
- package/skills/golang-security/references/checklist.md +80 -0
- package/skills/golang-security/references/cookies.md +200 -0
- package/skills/golang-security/references/cryptography.md +424 -0
- package/skills/golang-security/references/filesystem.md +285 -0
- package/skills/golang-security/references/injection.md +315 -0
- package/skills/golang-security/references/logging.md +163 -0
- package/skills/golang-security/references/memory-safety.md +241 -0
- package/skills/golang-security/references/network.md +253 -0
- package/skills/golang-security/references/secrets.md +189 -0
- package/skills/golang-security/references/third-party.md +159 -0
- package/skills/golang-security/references/threat-modeling.md +189 -0
- package/skills/golang-testing/SKILL.md +720 -0
- package/skills/grill-me/SKILL.md +7 -0
- package/skills/javascript-testing-patterns/SKILL.md +537 -0
- package/skills/javascript-testing-patterns/references/advanced-testing-patterns.md +513 -0
- package/skills/modern-javascript-patterns/SKILL.md +43 -0
- package/skills/modern-javascript-patterns/references/advanced-patterns.md +487 -0
- package/skills/modern-javascript-patterns/references/details.md +457 -0
- package/skills/python-anti-patterns/SKILL.md +349 -0
- package/skills/python-design-patterns/SKILL.md +85 -0
- package/skills/python-design-patterns/references/details.md +353 -0
- package/skills/python-error-handling/SKILL.md +193 -0
- package/skills/python-error-handling/references/details.md +171 -0
- package/skills/python-testing-patterns/SKILL.md +278 -0
- package/skills/python-testing-patterns/references/advanced-patterns.md +411 -0
- package/skills/python-testing-patterns/references/details.md +349 -0
- package/skills/rust-patterns/SKILL.md +500 -0
- package/skills/rust-testing/SKILL.md +501 -0
- package/skills/security-review/SKILL.md +504 -0
- package/skills/security-review/cloud-infrastructure-security.md +361 -0
- package/skills/vue-best-practices/SKILL.md +154 -0
- package/skills/vue-best-practices/references/animation-class-based-technique.md +254 -0
- package/skills/vue-best-practices/references/animation-state-driven-technique.md +291 -0
- package/skills/vue-best-practices/references/component-async.md +97 -0
- package/skills/vue-best-practices/references/component-data-flow.md +307 -0
- package/skills/vue-best-practices/references/component-fallthrough-attrs.md +174 -0
- package/skills/vue-best-practices/references/component-keep-alive.md +137 -0
- package/skills/vue-best-practices/references/component-slots.md +216 -0
- package/skills/vue-best-practices/references/component-suspense.md +228 -0
- package/skills/vue-best-practices/references/component-teleport.md +108 -0
- package/skills/vue-best-practices/references/component-transition-group.md +128 -0
- package/skills/vue-best-practices/references/component-transition.md +125 -0
- package/skills/vue-best-practices/references/composables.md +290 -0
- package/skills/vue-best-practices/references/directives.md +162 -0
- package/skills/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +159 -0
- package/skills/vue-best-practices/references/perf-v-once-v-memo-directives.md +182 -0
- package/skills/vue-best-practices/references/perf-virtualize-large-lists.md +187 -0
- package/skills/vue-best-practices/references/plugins.md +166 -0
- package/skills/vue-best-practices/references/reactivity.md +344 -0
- package/skills/vue-best-practices/references/render-functions.md +201 -0
- package/skills/vue-best-practices/references/sfc.md +310 -0
- package/skills/vue-best-practices/references/state-management.md +135 -0
- package/skills/vue-best-practices/references/updated-hook-performance.md +187 -0
- package/skills/vue-pinia-best-practices/SKILL.md +21 -0
- package/skills/vue-pinia-best-practices/reference/pinia-no-active-pinia-error.md +248 -0
- package/skills/vue-pinia-best-practices/reference/pinia-setup-store-return-all-state.md +227 -0
- package/skills/vue-pinia-best-practices/reference/pinia-store-destructuring-breaks-reactivity.md +193 -0
- package/skills/vue-pinia-best-practices/reference/state-url-for-ephemeral-filters.md +238 -0
- package/skills/vue-pinia-best-practices/reference/state-use-pinia-for-large-apps.md +262 -0
- package/skills/vue-pinia-best-practices/reference/store-method-binding-parentheses.md +191 -0
- package/skills/zig-0.16/SKILL.md +840 -0
- package/skills/zig-0.16/scripts/check-zig-version.sh +21 -0
- package/src/cli/analyses.ts +280 -0
- package/src/cli/index.ts +108 -0
- package/src/cli/serve.ts +192 -0
- package/src/cli/smoke.ts +131 -0
- package/src/cli/status.test.ts +204 -0
- package/src/cli/status.ts +263 -0
- package/src/cli/vacuum.test.ts +82 -0
- package/src/cli/vacuum.ts +96 -0
- package/src/config/schema.test.ts +88 -0
- package/src/config/schema.ts +64 -0
- package/src/db/analyses-migration.test.ts +210 -0
- package/src/db/analyses.test.ts +466 -0
- package/src/db/analyses.ts +375 -0
- package/src/db/auto-checkpoint.ts +131 -0
- package/src/db/client.test.ts +129 -0
- package/src/db/client.ts +55 -0
- package/src/db/fts-escape.ts +20 -0
- package/src/db/incidents.test.ts +201 -0
- package/src/db/incidents.ts +93 -0
- package/src/db/index.ts +86 -0
- package/src/db/migrations-v13.test.ts +141 -0
- package/src/db/migrations-v8.test.ts +301 -0
- package/src/db/migrations.ts +147 -0
- package/src/db/plan-archive.test.ts +180 -0
- package/src/db/plan-archive.ts +274 -0
- package/src/db/plan-create.test.ts +276 -0
- package/src/db/plan-create.ts +78 -0
- package/src/db/plan-files.test.ts +289 -0
- package/src/db/plan-update-status.ts +287 -0
- package/src/db/plans.test.ts +490 -0
- package/src/db/plans.ts +534 -0
- package/src/db/resolve-project-dir.test.ts +143 -0
- package/src/db/resolve-project-dir.ts +75 -0
- package/src/db/rollbacks.test.ts +150 -0
- package/src/db/rollbacks.ts +67 -0
- package/src/db/schema.ts +907 -0
- package/src/db/sessions.test.ts +80 -0
- package/src/db/sessions.ts +135 -0
- package/src/db/shutdown.test.ts +147 -0
- package/src/db/shutdown.ts +45 -0
- package/src/db/tasks.test.ts +921 -0
- package/src/db/tasks.ts +747 -0
- package/src/db/types.ts +619 -0
- package/src/http/__tests__/auth.test.ts +196 -0
- package/src/http/__tests__/routes.test.ts +465 -0
- package/src/http/__tests__/sse.test.ts +317 -0
- package/src/http/auth.ts +72 -0
- package/src/http/middleware/cors.ts +53 -0
- package/src/http/middleware/security-headers.ts +21 -0
- package/src/http/routes/events.ts +112 -0
- package/src/http/routes/health.ts +51 -0
- package/src/http/routes/plans.ts +66 -0
- package/src/http/routes/sessions.ts +50 -0
- package/src/http/routes/tasks.ts +60 -0
- package/src/http/server.ts +95 -0
- package/src/http/sse.ts +116 -0
- package/src/index.ts +37 -0
- package/src/lib.ts +65 -0
- package/src/mem/scoped.ts +65 -0
- package/src/orchestrator/background.test.ts +268 -0
- package/src/orchestrator/background.ts +293 -0
- package/src/orchestrator/memory-hook.ts +182 -0
- package/src/orchestrator/reconciler.ts +123 -0
- package/src/orchestrator/scheduler.test.ts +300 -0
- package/src/orchestrator/scheduler.ts +243 -0
- package/src/plugin.test.ts +2574 -0
- package/src/plugin.ts +1690 -0
- package/src/sdk/client.ts +66 -0
- package/src/worktrees/manager.ts +236 -0
- package/src/worktrees/state.ts +87 -0
- package/tests/integration/ranger-flow.test.ts +257 -0
- package/tools/analysis_archive.ts +28 -0
- package/tools/analysis_create.ts +55 -0
- package/tools/analysis_get.ts +33 -0
- package/tools/analysis_link_plan.ts +44 -0
- package/tools/analysis_list.ts +48 -0
- package/tools/analysis_search.ts +36 -0
- package/tools/analysis_update.ts +44 -0
- package/tools/plan_approve.ts +31 -0
- package/tools/plan_create.ts +58 -0
- package/tools/plan_get.ts +40 -0
- package/tools/plan_list.ts +37 -0
- package/tools/plan_search.ts +34 -0
- package/tools/plan_update_status.ts +71 -0
- package/tools/session_checkpoint.ts +31 -0
- package/tools/session_end.ts +26 -0
- package/tools/session_start.ts +43 -0
- package/tools/task_create_batch.ts +70 -0
- package/tools/task_list.ts +35 -0
- package/tools/task_next_for_agent.ts +30 -0
- package/tools/task_search.ts +34 -0
- package/tools/task_update_status.ts +37 -0
- 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 ""
|