@ainova-systems/intelligence 0.11.0-rc.6 → 0.11.0-rc.8
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/README.md +15 -16
- package/cli/commands/adapter.sh +153 -0
- package/cli/commands/init.sh +113 -22
- package/cli/commands/package.sh +30 -0
- package/cli/commands/registry.sh +5 -2
- package/cli/commands/status.sh +12 -4
- package/cli/commands/sync.sh +8 -21
- package/cli/commands/update.sh +76 -61
- package/cli/engine-package.yaml +4 -4
- package/cli/intelligence +37 -27
- package/cli/{commands/doctor.sh → internal/check.sh} +32 -14
- package/cli/{commands/migrate.sh → internal/migrate-v1.sh} +161 -42
- package/cli/{commands/add.sh → internal/package-add.sh} +11 -10
- package/cli/{commands/list.sh → internal/package-list.sh} +9 -4
- package/cli/{commands/remove.sh → internal/package-remove.sh} +3 -3
- package/cli/{commands/search.sh → internal/package-search.sh} +4 -4
- package/cli/internal/package-update.sh +97 -0
- package/cli/{commands/install.sh → internal/restore.sh} +11 -4
- package/cli/internal/target-state.sh +57 -0
- package/cli/{commands/upgrade.sh → internal/upgrade-v2.sh} +48 -8
- package/cli/lib/cli-common.sh +167 -14
- package/cli/lib/lockfile.sh +15 -9
- package/cli/lib/manifest.sh +106 -1
- package/cli/lib/registry.sh +29 -15
- package/cli/lib/semver.sh +4 -2
- package/engine/ENGINE_SHA +1 -0
- package/engine/{scripts/adapters → adapters}/_template.sh +10 -9
- package/engine/{scripts/adapters → adapters}/agents.sh +20 -25
- package/engine/{scripts/adapters → adapters}/opencode.sh +1 -1
- package/engine/{scripts/lib → lib}/common.sh +29 -541
- package/engine/lib/contract.sh +120 -0
- package/engine/sync.sh +238 -0
- package/package.json +6 -5
- package/{engine → packages/sync}/agents/intelligence-architect.md +5 -3
- package/{engine → packages/sync}/agents/intelligence-operator.md +10 -13
- package/packages/sync/references/adapters.md +252 -0
- package/packages/sync/references/conventions.md +385 -0
- package/{engine → packages/sync}/rules/intelligence-authoring.md +6 -6
- package/{engine → packages/sync}/skills/intelligence-add-agent/SKILL.md +5 -5
- package/{engine → packages/sync}/skills/intelligence-add-rule/SKILL.md +3 -3
- package/{engine → packages/sync}/skills/intelligence-add-skill/SKILL.md +3 -3
- package/{engine → packages/sync}/skills/intelligence-extract-skill/SKILL.md +2 -2
- package/packages/sync/skills/intelligence-install-adapter/SKILL.md +38 -0
- package/{engine → packages/sync}/skills/intelligence-learn-from-context/SKILL.md +3 -3
- package/packages/sync/skills/intelligence-learn-from-repository/SKILL.md +55 -0
- package/{engine → packages/sync}/skills/intelligence-review-skills/SKILL.md +6 -6
- package/packages/sync/skills/intelligence-sync/SKILL.md +20 -0
- package/packages/sync/skills/intelligence-uninstall-adapter/SKILL.md +24 -0
- package/packages/sync/skills/intelligence-update/SKILL.md +43 -0
- package/engine/INIT.md +0 -500
- package/engine/docs/ADAPTERS.md +0 -214
- package/engine/docs/CLI.md +0 -90
- package/engine/docs/CONVENTIONS.md +0 -456
- package/engine/scripts/ENGINE_SHA +0 -1
- package/engine/scripts/lib/layout.sh +0 -51
- package/engine/scripts/lib/migrations.sh +0 -708
- package/engine/scripts/sync.sh +0 -311
- package/engine/scripts/update.sh +0 -237
- package/engine/skills/intelligence-install-adapter/SKILL.md +0 -31
- package/engine/skills/intelligence-sync/SKILL.md +0 -16
- package/engine/skills/intelligence-uninstall-adapter/SKILL.md +0 -42
- package/engine/skills/intelligence-update/SKILL.md +0 -165
- /package/engine/{scripts/VERSION → VERSION} +0 -0
- /package/engine/{scripts/adapters → adapters}/claude.sh +0 -0
- /package/engine/{scripts/adapters → adapters}/codex.sh +0 -0
- /package/engine/{scripts/adapters → adapters}/copilot.sh +0 -0
- /package/engine/{scripts/adapters → adapters}/cursor.sh +0 -0
- /package/engine/{scripts/adapters → adapters}/pi.sh +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
#
|
|
2
|
+
# Internal v1-to-v2 migration operation.
|
|
3
3
|
# to the CLI setup: root intelligence.yaml, .intelligence/ store,
|
|
4
4
|
# intelligence.lock, no vendored engine.
|
|
5
5
|
#
|
|
6
|
-
# Transactional
|
|
6
|
+
# Transactional: STAGE everything next to the
|
|
7
7
|
# project, VERIFY the staged state, then COMMIT in an order where every
|
|
8
8
|
# destructive step happens only after a real sync of the new state reported
|
|
9
9
|
# IS_STATUS=ok. Any earlier failure leaves the project untouched.
|
|
@@ -24,7 +24,7 @@ done
|
|
|
24
24
|
detect_project
|
|
25
25
|
case "$IP_MODE" in
|
|
26
26
|
legacy) ;;
|
|
27
|
-
v2) die "already on the
|
|
27
|
+
v2) die "already on the v2 setup ($IP_ROOT/intelligence.yaml)" ;;
|
|
28
28
|
*) die "no vendored setup found here — 'intelligence init' starts a fresh project" ;;
|
|
29
29
|
esac
|
|
30
30
|
root="$IP_ROOT"
|
|
@@ -32,41 +32,101 @@ umbrella="$IP_UMBRELLA"
|
|
|
32
32
|
module_dir="$IP_MODULE_DIR"
|
|
33
33
|
config="$umbrella/config.yaml"
|
|
34
34
|
module_rel="${module_dir#"$root"/}"
|
|
35
|
+
umbrella_rel="${umbrella#"$root"/}"
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
for existing in intelligence.yaml intelligence.lock .intelligence; do
|
|
38
|
+
if [ -e "$root/$existing" ] || [ -L "$root/$existing" ]; then
|
|
39
|
+
die "$existing already exists at the root — conflicting v2 state; move it aside or restore the project before migrating"
|
|
40
|
+
fi
|
|
41
|
+
done
|
|
37
42
|
if [ "$dry_run" -eq 0 ] && [ "$force" -eq 0 ]; then
|
|
38
43
|
if [ -n "$(git -C "$root" status --porcelain 2>/dev/null)" ]; then
|
|
39
44
|
die "working tree is not clean — commit or stash first (or --force); migrate wants a one-commit diff you can review and revert"
|
|
40
45
|
fi
|
|
41
46
|
fi
|
|
42
47
|
|
|
43
|
-
|
|
48
|
+
# `sync_version` belongs to the archived v1 format. The v2 contract uses
|
|
49
|
+
# `schema_version`, so conversion reads the legacy scalar explicitly.
|
|
50
|
+
stamp="$(top_scalar "$config" "sync_version")"
|
|
44
51
|
eng="$(bundled_engine_version)"
|
|
45
52
|
[ -n "$stamp" ] && _ver_gt "$stamp" "$eng" && die "project schema $stamp is newer than this CLI's engine $eng — update the CLI first"
|
|
46
53
|
|
|
54
|
+
# The v1 `packs:` block is the one v1 shape migrate still has to read, and the
|
|
55
|
+
# engine no longer knows it — the reader lives here, with its only consumer.
|
|
56
|
+
get_pack_field() {
|
|
57
|
+
get_nested_yaml_value "$1" "packs" "$2" "$3"
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
paths_overlap() {
|
|
61
|
+
case "$1" in "$2"|"$2"/*) return 0 ;; esac
|
|
62
|
+
case "$2" in "$1"|"$1"/*) return 0 ;; esac
|
|
63
|
+
return 1
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
validate_mirror_path() {
|
|
67
|
+
local pack="$1" mirror="$2" canon rel src src_canon src_rel target output protected mirror_phys
|
|
68
|
+
[ -n "$mirror" ] || return 0
|
|
69
|
+
case "$mirror" in
|
|
70
|
+
.|/*|*..*|*\\*|[A-Za-z]:*) die "pack '$pack' has unsafe mirror path '$mirror'" ;;
|
|
71
|
+
esac
|
|
72
|
+
canon="$(normalize_path "$root/$mirror")"
|
|
73
|
+
case "$canon" in "$root"/*) ;; *) die "pack '$pack' mirror escapes the repository: '$mirror'" ;; esac
|
|
74
|
+
rel="${canon#"$root"/}"
|
|
75
|
+
|
|
76
|
+
# A v1 mirror may live below the content directory (the normal location is
|
|
77
|
+
# intelligence/external/<pack>), but it must never own the directory itself,
|
|
78
|
+
# the vendored module, CLI state, authored sources, or generated outputs.
|
|
79
|
+
case "$umbrella_rel" in "$rel"|"$rel"/*) die "pack '$pack' mirror '$mirror' contains the v1 content directory" ;; esac
|
|
80
|
+
for protected in .git .intelligence "$module_rel"; do
|
|
81
|
+
paths_overlap "$rel" "$protected" && die "pack '$pack' mirror '$mirror' overlaps protected path '$protected'"
|
|
82
|
+
done
|
|
83
|
+
for section in rules agents skills; do
|
|
84
|
+
while IFS= read -r src; do
|
|
85
|
+
case "$src" in ""|git+*|@*) continue ;; esac
|
|
86
|
+
src_canon="$(normalize_path "$root/$src")"
|
|
87
|
+
case "$src_canon" in "$root"/*) src_rel="${src_canon#"$root"/}" ;; *) continue ;; esac
|
|
88
|
+
paths_overlap "$rel" "$src_rel" \
|
|
89
|
+
&& die "pack '$pack' mirror '$mirror' overlaps configured source '$src'"
|
|
90
|
+
done < <(read_yaml_list "$config" "$section")
|
|
91
|
+
done
|
|
92
|
+
while IFS= read -r target; do
|
|
93
|
+
[ -n "$target" ] || continue
|
|
94
|
+
output="$(get_target_output "$config" "$target")"
|
|
95
|
+
[ -n "$output" ] || continue
|
|
96
|
+
case "$output" in /*|*..*) continue ;; esac
|
|
97
|
+
paths_overlap "$rel" "${output%/}" \
|
|
98
|
+
&& die "pack '$pack' mirror '$mirror' overlaps target '$target' output '$output'"
|
|
99
|
+
done < <(read_yaml_keys "$config" "targets")
|
|
100
|
+
|
|
101
|
+
if [ -d "$canon" ]; then
|
|
102
|
+
mirror_phys="$(cd "$canon" && pwd -P)"
|
|
103
|
+
case "$mirror_phys" in "$root"|"$root"/*) ;; *) die "pack '$pack' mirror resolves outside the repository: '$mirror'" ;; esac
|
|
104
|
+
[ -f "$canon/.pack" ] || die "pack '$pack' mirror '$mirror' has no .pack ownership stamp — refusing to copy or delete project-owned content"
|
|
105
|
+
fi
|
|
106
|
+
}
|
|
107
|
+
|
|
47
108
|
# ---- Stage ----------------------------------------------------------------
|
|
48
|
-
|
|
49
|
-
|
|
109
|
+
if [ "$dry_run" -eq 1 ]; then
|
|
110
|
+
stage="$(mktemp -d -t intelligence-migrate-XXXXXX 2>/dev/null || mktemp -d)"
|
|
111
|
+
else
|
|
112
|
+
stage="$(mktemp -d "$root/.intelligence-migrate-stage.XXXXXX")"
|
|
113
|
+
fi
|
|
50
114
|
cleanup_stage() { rm -rf "$stage"; }
|
|
51
115
|
trap cleanup_stage EXIT INT TERM
|
|
52
116
|
mkdir -p "$stage/.intelligence/packages"
|
|
53
117
|
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
echo "== applying the engine migration chain ($stamp -> $eng) =="
|
|
67
|
-
run_migrations "$umbrella" "$(basename "$module_dir")" "" || die "engine migration chain failed (IS_STATUS above) — nothing converted"
|
|
68
|
-
stamp_version "$config" "$eng"
|
|
69
|
-
fi
|
|
118
|
+
# v2 carries no v1 migration chain: v1 is archived, and the archived engine is
|
|
119
|
+
# what knows how to walk an old project forward. So migrate accepts only a
|
|
120
|
+
# project already at the final v1 schema and names the exact remedy otherwise —
|
|
121
|
+
# a remedy that keeps working indefinitely, because the archive stays at the
|
|
122
|
+
# URL the project's own update.sh already clones.
|
|
123
|
+
V1_FINAL_SCHEMA="0.10.0"
|
|
124
|
+
if [ -z "$stamp" ] || _ver_gt "$V1_FINAL_SCHEMA" "$stamp"; then
|
|
125
|
+
echo "ERROR: project schema is ${stamp:-absent (pre-0.3.1)}, older than the final v1 schema $V1_FINAL_SCHEMA." >&2
|
|
126
|
+
echo " Bring it forward with its own engine first — that still works:" >&2
|
|
127
|
+
echo " bash $module_rel/scripts/update.sh --yes" >&2
|
|
128
|
+
echo " then re-run: intelligence init" >&2
|
|
129
|
+
exit 1
|
|
70
130
|
fi
|
|
71
131
|
|
|
72
132
|
echo "== staging =="
|
|
@@ -78,12 +138,22 @@ sync_pkg_sha="$(fetch_package "$SYNC_PKG_URL" "v$eng" "$SYNC_PKG_PATH" "$stage/$
|
|
|
78
138
|
# per-pack source facts. Recorded as rows for the config rewrite and the lock.
|
|
79
139
|
pack_rows="$stage/packs.rows"
|
|
80
140
|
: > "$pack_rows"
|
|
141
|
+
seen_names=" "
|
|
142
|
+
seen_mirrors=" "
|
|
81
143
|
while IFS= read -r pack; do
|
|
82
144
|
[ -n "$pack" ] || continue
|
|
83
145
|
url="$(get_pack_field "$config" "$pack" "url")"
|
|
84
146
|
ref="$(get_pack_field "$config" "$pack" "ref")"
|
|
85
147
|
mirror="$(get_pack_field "$config" "$pack" "mirror")"
|
|
86
148
|
[ -n "$url" ] || die "pack '$pack' has no url in config.yaml"
|
|
149
|
+
assert_safe_source_url "$url"
|
|
150
|
+
assert_safe_ref "$ref"
|
|
151
|
+
validate_mirror_path "$pack" "$mirror"
|
|
152
|
+
if [ -n "$mirror" ]; then
|
|
153
|
+
mirror_key="$(normalize_path "$root/$mirror")"
|
|
154
|
+
case "$seen_mirrors" in *" $mirror_key "*) die "pack '$pack' shares mirror '$mirror' with another pack" ;; esac
|
|
155
|
+
seen_mirrors="$seen_mirrors$mirror_key "
|
|
156
|
+
fi
|
|
87
157
|
base="${url%.git}"; base="${base%/}"
|
|
88
158
|
repo="$(basename "$base")"
|
|
89
159
|
org="$(basename "$(dirname "$base")")"
|
|
@@ -91,6 +161,12 @@ while IFS= read -r pack; do
|
|
|
91
161
|
case "$repo" in ""|*[!A-Za-z0-9._-]*) repo="$(printf '%s' "$repo" | tr -c 'A-Za-z0-9._-' '-')" ;; esac
|
|
92
162
|
name="@$org/$repo"
|
|
93
163
|
assert_valid_pkg_name "$name"
|
|
164
|
+
# Two packs deriving the same @org/repo would overwrite one store dir and
|
|
165
|
+
# duplicate the lock key — fail closed, the user renames a source.
|
|
166
|
+
case "$seen_names" in
|
|
167
|
+
*" $name "*) die "packs '$pack' and another both derive package name '$name' — sources must map to distinct names" ;;
|
|
168
|
+
esac
|
|
169
|
+
seen_names="$seen_names $name "
|
|
94
170
|
printf '%s\n' "$pack$LOCK_SEP$name$LOCK_SEP$url$LOCK_SEP$ref$LOCK_SEP$mirror"
|
|
95
171
|
done < <(read_yaml_keys "$config" "packs") >> "$pack_rows"
|
|
96
172
|
|
|
@@ -116,7 +192,7 @@ while IFS="$LOCK_SEP" read -r pack name url ref mirror; do
|
|
|
116
192
|
echo " pack '$pack' -> $name (fetched)"
|
|
117
193
|
fi
|
|
118
194
|
if [ -z "$sha" ] || [ "$sha" = "unknown" ]; then
|
|
119
|
-
sha="$(GIT_TERMINAL_PROMPT=0 git ls-remote "$url" "$ref" 2>/dev/null | awk '{print $1; exit}')"
|
|
195
|
+
sha="$(GIT_TERMINAL_PROMPT=0 git ls-remote -- "$url" "$ref" 2>/dev/null | awk '{print $1; exit}')"
|
|
120
196
|
fi
|
|
121
197
|
printf '%s\037%s\037%s\037%s\037%s\037%s\n' \
|
|
122
198
|
"$name" "" "$url" "" "$ref" "$sha" >> "$lock_rows"
|
|
@@ -164,15 +240,26 @@ while IFS= read -r line || [ -n "$line" ]; do
|
|
|
164
240
|
*"- "*)
|
|
165
241
|
indent="${line%%-*}"
|
|
166
242
|
v="${line#*- }"
|
|
243
|
+
# A trailing inline comment survives the rewrite — split it
|
|
244
|
+
# off before touching quotes, or it would be mangled into the
|
|
245
|
+
# value.
|
|
246
|
+
trail=""
|
|
247
|
+
case "$v" in
|
|
248
|
+
*[[:space:]]"#"*)
|
|
249
|
+
trail=" #${v#*[[:space:]]#}"
|
|
250
|
+
v="${v%%[[:space:]]#*}"
|
|
251
|
+
;;
|
|
252
|
+
esac
|
|
253
|
+
v="${v%"${v##*[![:space:]]}"}"
|
|
167
254
|
v="${v%\"}"; v="${v#\"}"; v="${v%\'}"; v="${v#\'}"
|
|
168
|
-
echo "$indent- \"$(rewrite_source_value "$v")\""
|
|
255
|
+
echo "$indent- \"$(rewrite_source_value "$v")\"$trail"
|
|
169
256
|
continue
|
|
170
257
|
;;
|
|
171
258
|
esac
|
|
172
259
|
fi
|
|
173
260
|
echo "$line"
|
|
174
261
|
done < "$config" > "$manifest_stage"
|
|
175
|
-
|
|
262
|
+
stamp_schema_version "$manifest_stage" "$eng"
|
|
176
263
|
|
|
177
264
|
# packages: entries (pin preserved — no invented ranges).
|
|
178
265
|
while IFS="$LOCK_SEP" read -r pack name url ref mirror; do
|
|
@@ -201,14 +288,24 @@ for section in rules agents skills; do
|
|
|
201
288
|
esac
|
|
202
289
|
done < <(read_yaml_list "$manifest_stage" "$section")
|
|
203
290
|
done
|
|
204
|
-
|
|
291
|
+
target_keys() {
|
|
292
|
+
awk '
|
|
293
|
+
/^targets:[[:space:]]*$/ { in_targets=1; next }
|
|
294
|
+
in_targets && /^[^[:space:]#]/ { exit }
|
|
295
|
+
in_targets && /^ [A-Za-z_][A-Za-z0-9_]*:/ {
|
|
296
|
+
line=$0; sub(/^ /, "", line); sub(/:.*/, "", line); print line
|
|
297
|
+
}
|
|
298
|
+
' "$1"
|
|
299
|
+
}
|
|
300
|
+
while IFS= read -r adapter; do
|
|
301
|
+
[ -n "$adapter" ] || continue
|
|
205
302
|
old_e="$(is_target_enabled "$config" "$adapter")"
|
|
206
303
|
new_e="$(is_target_enabled "$manifest_stage" "$adapter")"
|
|
207
304
|
[ "$old_e" = "$new_e" ] || { echo " TARGET DRIFT: $adapter enabled '$old_e' -> '$new_e'"; fail=1; }
|
|
208
305
|
old_o="$(get_target_output "$config" "$adapter")"
|
|
209
306
|
new_o="$(get_target_output "$manifest_stage" "$adapter")"
|
|
210
307
|
[ "$old_o" = "$new_o" ] || { echo " TARGET DRIFT: $adapter output '$old_o' -> '$new_o'"; fail=1; }
|
|
211
|
-
done
|
|
308
|
+
done < <({ target_keys "$config"; target_keys "$manifest_stage"; } | sort -u)
|
|
212
309
|
[ "$fail" -eq 0 ] || die "staged state failed verification — nothing changed"
|
|
213
310
|
|
|
214
311
|
if [ "$dry_run" -eq 1 ]; then
|
|
@@ -230,45 +327,66 @@ if [ -f "$root/.gitignore" ]; then
|
|
|
230
327
|
gitignore_existed=1
|
|
231
328
|
cp "$root/.gitignore" "$stage/gitignore.orig"
|
|
232
329
|
fi
|
|
233
|
-
|
|
234
|
-
{
|
|
235
|
-
[ -f "$root/.gitignore" ] && [ -n "$(tail -c 1 "$root/.gitignore" 2>/dev/null)" ] && echo ""
|
|
236
|
-
echo "# intelligence CLI package store (restored by 'intelligence install')"
|
|
237
|
-
echo ".intelligence/"
|
|
238
|
-
} >> "$root/.gitignore"
|
|
239
|
-
fi
|
|
330
|
+
commit_active=1
|
|
240
331
|
rollback() {
|
|
332
|
+
[ "$commit_active" -eq 1 ] || return 0
|
|
333
|
+
commit_active=0
|
|
334
|
+
set +e
|
|
241
335
|
rm -rf "$root/.intelligence" "$root/intelligence.yaml" "$root/intelligence.lock"
|
|
242
336
|
if [ "$gitignore_existed" -eq 1 ]; then
|
|
243
337
|
cp "$stage/gitignore.orig" "$root/.gitignore"
|
|
244
338
|
else
|
|
245
339
|
rm -f "$root/.gitignore"
|
|
246
340
|
fi
|
|
341
|
+
rm -rf "$stage"
|
|
247
342
|
echo "rolled back — the vendored setup is untouched" >&2
|
|
248
343
|
}
|
|
344
|
+
rollback_on_exit() {
|
|
345
|
+
local rc=$?
|
|
346
|
+
rollback
|
|
347
|
+
exit "$rc"
|
|
348
|
+
}
|
|
349
|
+
# Every live write through verified sync is covered by rollback, including a
|
|
350
|
+
# plain set -e/EXIT failure rather than only an explicit sync refusal.
|
|
351
|
+
trap rollback_on_exit EXIT
|
|
352
|
+
trap 'rollback; exit 130' INT TERM
|
|
353
|
+
if [ ! -f "$root/.gitignore" ] || ! grep -q '^\.intelligence/' "$root/.gitignore"; then
|
|
354
|
+
{
|
|
355
|
+
[ -f "$root/.gitignore" ] && [ -n "$(tail -c 1 "$root/.gitignore" 2>/dev/null)" ] && echo ""
|
|
356
|
+
echo "# intelligence CLI package store (restored automatically by 'intelligence sync')"
|
|
357
|
+
echo ".intelligence/"
|
|
358
|
+
} >> "$root/.gitignore"
|
|
359
|
+
fi
|
|
249
360
|
mv "$stage/.intelligence" "$root/.intelligence"
|
|
250
361
|
cp "$manifest_stage" "$root/intelligence.yaml"
|
|
251
362
|
lock_write_from_tsv "$root/intelligence.lock" "$lock_rows"
|
|
252
363
|
|
|
253
364
|
sync_out="$(cd "$root" && IS_SUPPRESS_CLI_NOTE=1 bash "$CLI_DIR/commands/sync.sh" 2>&1)" || {
|
|
254
365
|
echo "$sync_out"
|
|
255
|
-
|
|
256
|
-
die "sync of the migrated state failed — rolled back"
|
|
366
|
+
die "sync of the migrated state failed"
|
|
257
367
|
}
|
|
258
368
|
echo "$sync_out" | grep -q '^IS_STATUS=ok' || {
|
|
259
369
|
echo "$sync_out"
|
|
260
|
-
|
|
261
|
-
die "sync of the migrated state did not report ok — rolled back"
|
|
370
|
+
die "sync of the migrated state did not report ok"
|
|
262
371
|
}
|
|
372
|
+
# Point of no return: the new state is verified. From here an interrupt must
|
|
373
|
+
# NOT roll back (the old setup is being removed) — only clean the stage.
|
|
374
|
+
commit_active=0
|
|
375
|
+
trap cleanup_stage EXIT INT TERM
|
|
263
376
|
|
|
264
377
|
# Only now is anything of the old setup removed — and config.yaml is kept as
|
|
265
378
|
# a reviewable backup inside the store.
|
|
266
379
|
mkdir -p "$root/.intelligence/backup"
|
|
267
380
|
cp "$config" "$root/.intelligence/backup/config.yaml"
|
|
268
381
|
while IFS="$LOCK_SEP" read -r pack name url ref mirror; do
|
|
269
|
-
|
|
382
|
+
# Containment before deletion: a mirror value is config input, and rm -rf
|
|
383
|
+
# must never follow it out of the repository.
|
|
384
|
+
case "$mirror" in
|
|
385
|
+
""|/*|*..*) [ -n "$mirror" ] && echo " WARN: mirror '$mirror' not repo-contained — left in place" >&2; continue ;;
|
|
386
|
+
esac
|
|
387
|
+
if [ -d "$root/$mirror" ]; then
|
|
270
388
|
rm -rf "${root:?}/$mirror"
|
|
271
|
-
# An emptied mirror parent (
|
|
389
|
+
# An emptied v1 mirror parent (for example intelligence/external/) goes with it.
|
|
272
390
|
rmdir "$(dirname "$root/$mirror")" 2>/dev/null || true
|
|
273
391
|
fi
|
|
274
392
|
done < "$pack_rows"
|
|
@@ -280,4 +398,5 @@ rmdir "$umbrella" 2>/dev/null || true
|
|
|
280
398
|
|
|
281
399
|
echo ""
|
|
282
400
|
echo "migrated. Review the diff, then commit. The old config.yaml is kept at .intelligence/backup/config.yaml."
|
|
283
|
-
echo "From now on: intelligence sync |
|
|
401
|
+
echo "From now on: intelligence sync | package | update | status."
|
|
402
|
+
echo "Next: ask your agent to run /intelligence-learn-from-repository to review the migrated project context."
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
#
|
|
2
|
+
# Internal package add operation.
|
|
3
3
|
#
|
|
4
4
|
# Specs:
|
|
5
|
-
# @scope/name[@range] resolved
|
|
6
|
-
# github:org/repo[#path] direct from GitHub, no registry involved
|
|
7
|
-
# git+<url>[@ref][#path] direct from any git host
|
|
5
|
+
# @scope/name[@range] resolved ONLY through the manifest's trusted registries
|
|
6
|
+
# github:org/repo[#path] explicit source: direct from GitHub, no registry involved
|
|
7
|
+
# git+<url>[@ref][#path] explicit source: direct from any git host
|
|
8
8
|
set -euo pipefail
|
|
9
9
|
source "$CLI_DIR/lib/cli-common.sh"
|
|
10
10
|
|
|
11
11
|
spec="${1:-}"
|
|
12
|
-
[ -n "$spec" ] || die "usage: intelligence add <@scope/name[@range] | github:org/repo[#path] | git+<url>[@ref][#path]>"
|
|
12
|
+
[ -n "$spec" ] || die "usage: intelligence package add <@scope/name[@range] | github:org/repo[#path] | git+<url>[@ref][#path]>"
|
|
13
13
|
shift || true
|
|
14
14
|
|
|
15
15
|
no_sync=0
|
|
@@ -43,14 +43,15 @@ case "$spec" in
|
|
|
43
43
|
rest="${spec#git+}"
|
|
44
44
|
case "$rest" in *#*) path="${rest#*#}"; rest="${rest%%#*}" ;; esac
|
|
45
45
|
# ref = the part after the last `@` in the post-scheme URL, unless it
|
|
46
|
-
# contains `/`
|
|
47
|
-
# git
|
|
46
|
+
# contains `/` or `:` — either marks a user@host part (scp-like
|
|
47
|
+
# `git@host:repo.git` included), not a ref. A branch name containing
|
|
48
|
+
# `/` cannot be expressed here; pin it via `ref:` in the manifest.
|
|
48
49
|
base="${rest#*://}"
|
|
49
50
|
case "$base" in
|
|
50
51
|
*@*)
|
|
51
52
|
tail="${base##*@}"
|
|
52
53
|
case "$tail" in
|
|
53
|
-
|
|
54
|
+
*/*|*:*) ;;
|
|
54
55
|
*) ref="$tail"; rest="${rest%@"$tail"}" ;;
|
|
55
56
|
esac
|
|
56
57
|
;;
|
|
@@ -84,7 +85,7 @@ if [ "$mode" = "registry" ]; then
|
|
|
84
85
|
echo " ('intelligence registry list' shows them). Either add its registry:" >&2
|
|
85
86
|
echo " intelligence registry add <registry-repo-url>" >&2
|
|
86
87
|
echo " or install from an explicit source you name yourself:" >&2
|
|
87
|
-
echo " intelligence add github:org/repo | intelligence add git+<url>[@ref][#path]" >&2
|
|
88
|
+
echo " intelligence package add github:org/repo | intelligence package add git+<url>[@ref][#path]" >&2
|
|
88
89
|
suggest_similar "$manifest" "$name"
|
|
89
90
|
exit 1
|
|
90
91
|
fi
|
|
@@ -93,7 +94,7 @@ fi
|
|
|
93
94
|
|
|
94
95
|
# The source must exist before anything else is attempted — never die
|
|
95
96
|
# mid-pipeline with no message.
|
|
96
|
-
if ! GIT_TERMINAL_PROMPT=0 git ls-remote "$url" >/dev/null 2>&1; then
|
|
97
|
+
if ! GIT_TERMINAL_PROMPT=0 git ls-remote -- "$url" >/dev/null 2>&1; then
|
|
97
98
|
echo "ERROR: no reachable repository at $url" >&2
|
|
98
99
|
echo " Declared by ${RES_VIA:-the spec} — check the URL and your git access." >&2
|
|
99
100
|
exit 1
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
#
|
|
2
|
+
# Internal package list operation.
|
|
3
3
|
set -euo pipefail
|
|
4
4
|
source "$CLI_DIR/lib/cli-common.sh"
|
|
5
5
|
|
|
@@ -14,8 +14,13 @@ while IFS= read -r name; do
|
|
|
14
14
|
range="$(qmap_field "$manifest" "packages" "$name" "version")"
|
|
15
15
|
ref="$(qmap_field "$manifest" "packages" "$name" "ref")"
|
|
16
16
|
resolved="$(qmap_field "$lock" "packages" "$name" "resolved")"
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
if [ ! -f "$lock" ]; then
|
|
18
|
+
state="lock missing — restore committed intelligence.lock"
|
|
19
|
+
elif [ -d "$IP_ROOT/.intelligence/packages/$name" ]; then
|
|
20
|
+
state="installed"
|
|
21
|
+
else
|
|
22
|
+
state="store missing — run 'intelligence sync'"
|
|
23
|
+
fi
|
|
19
24
|
printf '%s %s locked:%s %s\n' \
|
|
20
25
|
"$name" \
|
|
21
26
|
"${ref:+ref:$ref}${ref:-${range:-*}}" \
|
|
@@ -24,5 +29,5 @@ while IFS= read -r name; do
|
|
|
24
29
|
done < <(qmap_keys "$manifest" "packages")
|
|
25
30
|
|
|
26
31
|
if [ "$count" -eq 0 ]; then
|
|
27
|
-
echo "No packages. Add one: intelligence add @ainova-systems/core"
|
|
32
|
+
echo "No packages. Add one: intelligence package add @ainova-systems/core"
|
|
28
33
|
fi
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
#
|
|
2
|
+
# Internal package remove operation.
|
|
3
3
|
set -euo pipefail
|
|
4
4
|
source "$CLI_DIR/lib/cli-common.sh"
|
|
5
5
|
|
|
6
6
|
name="${1:-}"
|
|
7
|
-
[ -n "$name" ] || die "usage: intelligence remove <@scope/name>"
|
|
7
|
+
[ -n "$name" ] || die "usage: intelligence package remove <@scope/name>"
|
|
8
8
|
shift || true
|
|
9
9
|
no_sync=0 force=0
|
|
10
10
|
for a in "$@"; do
|
|
@@ -24,7 +24,7 @@ if [ "$name" = "$SYNC_PKG_NAME" ] && [ "$force" -eq 0 ]; then
|
|
|
24
24
|
echo "ERROR: $SYNC_PKG_NAME is the engine's own content — removing it drops every" >&2
|
|
25
25
|
echo " intelligence-* meta-skill, the authoring rule and the engine agents from" >&2
|
|
26
26
|
echo " the outputs. The sync engine itself keeps working (it ships with the CLI)." >&2
|
|
27
|
-
echo " If that is what you want: intelligence remove $SYNC_PKG_NAME --force" >&2
|
|
27
|
+
echo " If that is what you want: intelligence package remove $SYNC_PKG_NAME --force" >&2
|
|
28
28
|
exit 1
|
|
29
29
|
fi
|
|
30
30
|
assert_valid_pkg_name "$name"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
#
|
|
2
|
+
# Internal package search operation: what the registries offer and what this project
|
|
3
3
|
# already has. The catalogue view `list` deliberately is not: `list` answers
|
|
4
4
|
# "what does this project depend on", `search` answers "what could it".
|
|
5
5
|
#
|
|
@@ -27,7 +27,7 @@ pkg_state() {
|
|
|
27
27
|
elif [ -d "$IP_ROOT/.intelligence/packages/$name" ]; then
|
|
28
28
|
printf 'installed %s' "$(qmap_field "$IP_ROOT/intelligence.lock" "packages" "$name" "resolved")"
|
|
29
29
|
else
|
|
30
|
-
printf 'declared (run
|
|
30
|
+
printf 'declared (run sync)'
|
|
31
31
|
fi
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -75,9 +75,9 @@ if [ "$rows" -eq 0 ]; then
|
|
|
75
75
|
else
|
|
76
76
|
echo "No trusted registries (or they offer nothing) — intelligence registry add <repo-url>."
|
|
77
77
|
fi
|
|
78
|
-
echo "Explicit installs need no registry: intelligence add github:org/repo"
|
|
78
|
+
echo "Explicit installs need no registry: intelligence package add github:org/repo"
|
|
79
79
|
exit 0
|
|
80
80
|
fi
|
|
81
81
|
|
|
82
82
|
echo ""
|
|
83
|
-
echo "add one: intelligence add <name> | private registry: intelligence registry add <repo-url>"
|
|
83
|
+
echo "add one: intelligence package add <name> | private registry: intelligence registry add <repo-url>"
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Internal package range update operation.
|
|
3
|
+
#
|
|
4
|
+
# Re-resolve version ranges against the remotes and rewrite the lock. Pinned
|
|
5
|
+
# packages (`ref:`) never move here — repin by editing the manifest or
|
|
6
|
+
# re-adding. `update <name>` limits the pass to one package.
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
source "$CLI_DIR/lib/cli-common.sh"
|
|
9
|
+
|
|
10
|
+
only="" no_sync=0 preview=0
|
|
11
|
+
while [ $# -gt 0 ]; do
|
|
12
|
+
case "$1" in
|
|
13
|
+
--no-sync) no_sync=1 ;;
|
|
14
|
+
--preview) preview=1 ;;
|
|
15
|
+
@*) only="$1" ;;
|
|
16
|
+
*) die "unknown argument '$1'" ;;
|
|
17
|
+
esac
|
|
18
|
+
shift || true
|
|
19
|
+
done
|
|
20
|
+
|
|
21
|
+
require_v2
|
|
22
|
+
manifest="$IP_ROOT/intelligence.yaml"
|
|
23
|
+
lock="$IP_ROOT/intelligence.lock"
|
|
24
|
+
|
|
25
|
+
moved=0 found=0
|
|
26
|
+
while IFS= read -r name; do
|
|
27
|
+
[ -n "$name" ] || continue
|
|
28
|
+
# Manifest keys are untrusted input on their way into store paths.
|
|
29
|
+
assert_valid_pkg_name "$name"
|
|
30
|
+
[ -n "$only" ] && [ "$name" != "$only" ] && continue
|
|
31
|
+
# A named package that exists is "found" whatever happens next — a skip
|
|
32
|
+
# message followed by "not in the manifest" would contradict itself.
|
|
33
|
+
found=1
|
|
34
|
+
if [ "$name" = "$SYNC_PKG_NAME" ]; then
|
|
35
|
+
echo " $name: engine content follows the installed CLI — skipped"
|
|
36
|
+
continue
|
|
37
|
+
fi
|
|
38
|
+
ref="$(qmap_field "$manifest" "packages" "$name" "ref")"
|
|
39
|
+
[ -n "$ref" ] && { echo " $name: pinned to ref '$ref' — skipped"; continue; }
|
|
40
|
+
range="$(qmap_field "$manifest" "packages" "$name" "version")"
|
|
41
|
+
# An exact version is a pin, same as ref: — update never moves pins.
|
|
42
|
+
if [ -n "$range" ] && semver_is_stable "$range"; then
|
|
43
|
+
echo " $name: pinned to $range — edit the manifest to move it"
|
|
44
|
+
continue
|
|
45
|
+
fi
|
|
46
|
+
url="$(qmap_field "$manifest" "packages" "$name" "url")"
|
|
47
|
+
path="$(qmap_field "$manifest" "packages" "$name" "path")"
|
|
48
|
+
if [ -z "$url" ]; then
|
|
49
|
+
resolve_package_source "$manifest" "$name"
|
|
50
|
+
url="$RES_URL"; path="$RES_PATH"
|
|
51
|
+
fi
|
|
52
|
+
picked="$(list_remote_versions "$url" | semver_pick_highest "$range")"
|
|
53
|
+
[ -n "$picked" ] || { echo " $name: nothing satisfies '${range:-*}' at $url" >&2; continue; }
|
|
54
|
+
read -r tag _ <<< "$(remote_tag_for_version "$url" "$picked")"
|
|
55
|
+
current="$(qmap_field "$lock" "packages" "$name" "resolved")"
|
|
56
|
+
cur_url="$(qmap_field "$lock" "packages" "$name" "url")"
|
|
57
|
+
cur_path="$(qmap_field "$lock" "packages" "$name" "path")"
|
|
58
|
+
# Same tag is not enough: a registry may re-point a name's source while
|
|
59
|
+
# keeping the version — the move doctor warns about must be actionable
|
|
60
|
+
# here, so url/path changes refetch too.
|
|
61
|
+
if [ "$tag" = "$current" ] && [ "$url" = "$cur_url" ] && [ "$path" = "$cur_path" ]; then
|
|
62
|
+
echo " $name: ${current:-<none>} (up to date)"
|
|
63
|
+
continue
|
|
64
|
+
fi
|
|
65
|
+
if [ "$preview" -eq 1 ]; then
|
|
66
|
+
echo " $name: ${current:-<none>} -> $tag"
|
|
67
|
+
moved=$((moved + 1))
|
|
68
|
+
continue
|
|
69
|
+
fi
|
|
70
|
+
rel=".intelligence/packages/$name"
|
|
71
|
+
# Fetch into staging FIRST: a failed clone must leave the current install
|
|
72
|
+
# fully wired and intact. Only after success is the old shape unwired
|
|
73
|
+
# (the new version may have dropped a section dir), the store swapped,
|
|
74
|
+
# and the new shape wired.
|
|
75
|
+
staging="$IP_ROOT/.intelligence/.staging-$$"
|
|
76
|
+
rm -rf "$staging"
|
|
77
|
+
sha="$(fetch_package "$url" "$tag" "$path" "$staging")"
|
|
78
|
+
unwire_package_sources "$manifest" "$rel"
|
|
79
|
+
rm -rf "${IP_ROOT:?}/$rel"
|
|
80
|
+
mkdir -p "$(dirname "$IP_ROOT/$rel")"
|
|
81
|
+
mv "$staging" "$IP_ROOT/$rel"
|
|
82
|
+
wire_package_sources "$manifest" "$name" "$rel" "$IP_ROOT"
|
|
83
|
+
lock_upsert "$lock" "$name" "$range" "$url" "$path" "$tag" "$sha"
|
|
84
|
+
echo " $name: ${current:-<none>} -> $tag"
|
|
85
|
+
moved=$((moved + 1))
|
|
86
|
+
done < <(qmap_keys "$manifest" "packages")
|
|
87
|
+
|
|
88
|
+
[ -n "$only" ] && [ "$found" -eq 0 ] && die "package '$only' is not in the manifest"
|
|
89
|
+
if [ "$preview" -eq 1 ]; then
|
|
90
|
+
echo "updates available: $moved package(s)"
|
|
91
|
+
else
|
|
92
|
+
echo "updated: $moved package(s)"
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
if [ "$preview" -eq 0 ] && [ "$moved" -gt 0 ] && [ "$no_sync" -eq 0 ]; then
|
|
96
|
+
exec bash "$CLI_DIR/commands/sync.sh"
|
|
97
|
+
fi
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
#
|
|
2
|
+
# Internal locked-store restore operation.
|
|
3
3
|
#
|
|
4
4
|
# Restore .intelligence/ from intelligence.lock — the fresh-clone command.
|
|
5
5
|
# --frozen additionally refuses any manifest/lock divergence (CI mode) and
|
|
@@ -30,7 +30,7 @@ if [ "$frozen" -eq 1 ]; then
|
|
|
30
30
|
while IFS= read -r name; do
|
|
31
31
|
[ -n "$name" ] || continue
|
|
32
32
|
[ -n "$(qmap_field "$lock" "packages" "$name" "url")" ] \
|
|
33
|
-
|| die "
|
|
33
|
+
|| die "locked restore: $name is in the manifest but not in intelligence.lock"
|
|
34
34
|
m_ver="$(qmap_field "$manifest" "packages" "$name" "version")"
|
|
35
35
|
m_ref="$(qmap_field "$manifest" "packages" "$name" "ref")"
|
|
36
36
|
m_url="$(qmap_field "$manifest" "packages" "$name" "url")"
|
|
@@ -40,7 +40,7 @@ if [ "$frozen" -eq 1 ]; then
|
|
|
40
40
|
l_url="$(qmap_field "$lock" "packages" "$name" "url")"
|
|
41
41
|
l_path="$(qmap_field "$lock" "packages" "$name" "path")"
|
|
42
42
|
[ -z "$m_ver" ] || [ "$m_ver" = "$l_req" ] \
|
|
43
|
-
|| die "
|
|
43
|
+
|| die "locked restore: $name requests '$m_ver' but the lock recorded '$l_req' — reconcile the manifest and lock"
|
|
44
44
|
[ -z "$m_ref" ] || [ "$m_ref" = "$l_res" ] \
|
|
45
45
|
|| die "--frozen: $name pins ref '$m_ref' but the lock resolved '$l_res'"
|
|
46
46
|
[ -z "$m_url" ] || [ "$m_url" = "$l_url" ] \
|
|
@@ -63,6 +63,7 @@ fi
|
|
|
63
63
|
missing=""
|
|
64
64
|
while IFS= read -r name; do
|
|
65
65
|
[ -n "$name" ] || continue
|
|
66
|
+
assert_valid_pkg_name "$name"
|
|
66
67
|
if [ ! -f "$lock" ] || [ -z "$(qmap_field "$lock" "packages" "$name" "url")" ]; then
|
|
67
68
|
missing="$missing $name"
|
|
68
69
|
fi
|
|
@@ -101,8 +102,14 @@ fi
|
|
|
101
102
|
if [ -f "$lock" ]; then
|
|
102
103
|
while IFS="$LOCK_SEP" read -r name requested url path resolved sha; do
|
|
103
104
|
[ -n "$name" ] || continue
|
|
105
|
+
# The lock arrives with a cloned repo — its keys are untrusted input
|
|
106
|
+
# on their way into rm -rf paths, and its urls into git argv.
|
|
107
|
+
assert_valid_pkg_name "$name"
|
|
104
108
|
rel=".intelligence/packages/$name"
|
|
105
|
-
|
|
109
|
+
# --frozen never trusts what is already on disk: a pre-existing
|
|
110
|
+
# (possibly substituted) store dir would otherwise be synced with no
|
|
111
|
+
# sha verification at all.
|
|
112
|
+
if [ -d "$IP_ROOT/$rel" ] && [ "$force" -eq 0 ] && [ "$frozen" -eq 0 ]; then
|
|
106
113
|
continue
|
|
107
114
|
fi
|
|
108
115
|
# Fetch into staging and commit to the store only after the integrity
|