@ainova-systems/intelligence 0.11.0-rc.1 → 0.11.0-rc.10
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 +19 -16
- package/cli/commands/adapter.sh +153 -0
- package/cli/commands/init.sh +171 -34
- package/cli/commands/package.sh +30 -0
- package/cli/commands/registry.sh +65 -30
- package/cli/commands/status.sh +20 -11
- package/cli/commands/sync.sh +9 -4
- package/cli/commands/update.sh +80 -46
- package/cli/engine-package.yaml +14 -0
- package/cli/intelligence +37 -26
- package/cli/internal/align-project.sh +134 -0
- package/cli/internal/check.sh +128 -0
- package/cli/internal/convert-legacy.sh +400 -0
- package/cli/{commands/add.sh → internal/package-add.sh} +37 -19
- package/cli/{commands/list.sh → internal/package-list.sh} +10 -5
- package/cli/{commands/remove.sh → internal/package-remove.sh} +16 -4
- package/cli/internal/package-search.sh +83 -0
- package/cli/internal/package-update.sh +103 -0
- package/cli/internal/restore.sh +130 -0
- package/cli/internal/target-state.sh +57 -0
- package/cli/lib/cli-common.sh +213 -63
- package/cli/lib/lockfile.sh +15 -9
- package/cli/lib/manifest.sh +257 -4
- package/cli/lib/registry.sh +96 -47
- package/cli/lib/semver.sh +12 -4
- 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 +30 -538
- package/engine/lib/contract.sh +120 -0
- package/engine/sync.sh +238 -0
- package/package.json +5 -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 +7 -7
- package/{engine → packages/sync}/skills/intelligence-add-agent/SKILL.md +7 -7
- package/{engine → packages/sync}/skills/intelligence-add-rule/SKILL.md +5 -5
- package/{engine → packages/sync}/skills/intelligence-add-skill/SKILL.md +5 -5
- 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 +7 -7
- 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/cli/commands/doctor.sh +0 -100
- package/cli/commands/install.sh +0 -84
- package/cli/commands/migrate.sh +0 -251
- package/cli/commands/upgrade.sh +0 -27
- package/engine/INIT.md +0 -498
- package/engine/docs/ADAPTERS.md +0 -212
- package/engine/docs/CLI.md +0 -91
- package/engine/docs/CONVENTIONS.md +0 -440
- 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 -18
- package/engine/skills/intelligence-uninstall-adapter/SKILL.md +0 -42
- package/engine/skills/intelligence-update/SKILL.md +0 -159
- package/registry/index.yaml +0 -15
- /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
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Internal conversion from legacy Intelligence Sync to Intelligence CLI.
|
|
3
|
+
# to an Intelligence project: root intelligence.yaml, .intelligence/ store,
|
|
4
|
+
# intelligence.lock, no vendored engine.
|
|
5
|
+
#
|
|
6
|
+
# Transactional: STAGE everything next to the
|
|
7
|
+
# project, VERIFY the staged state, then COMMIT in an order where every
|
|
8
|
+
# destructive step happens only after a real sync of the new state reported
|
|
9
|
+
# IS_STATUS=ok. Any earlier failure leaves the project untouched.
|
|
10
|
+
set -euo pipefail
|
|
11
|
+
source "$CLI_DIR/lib/cli-common.sh"
|
|
12
|
+
|
|
13
|
+
dry_run=0 force=0
|
|
14
|
+
while [ $# -gt 0 ]; do
|
|
15
|
+
case "$1" in
|
|
16
|
+
--dry-run) dry_run=1 ;;
|
|
17
|
+
--force) force=1 ;;
|
|
18
|
+
*) die "unknown option '$1'" ;;
|
|
19
|
+
esac
|
|
20
|
+
shift || true
|
|
21
|
+
done
|
|
22
|
+
|
|
23
|
+
# ---- Preconditions (fail closed, nothing written) -------------------------
|
|
24
|
+
detect_project
|
|
25
|
+
case "$IP_MODE" in
|
|
26
|
+
legacy) ;;
|
|
27
|
+
cli) die "already an Intelligence project ($IP_ROOT/intelligence.yaml)" ;;
|
|
28
|
+
*) die "no legacy Intelligence Sync project found here — 'intelligence init' starts a fresh project" ;;
|
|
29
|
+
esac
|
|
30
|
+
root="$IP_ROOT"
|
|
31
|
+
umbrella="$IP_UMBRELLA"
|
|
32
|
+
module_dir="$IP_MODULE_DIR"
|
|
33
|
+
config="$umbrella/config.yaml"
|
|
34
|
+
module_rel="${module_dir#"$root"/}"
|
|
35
|
+
umbrella_rel="${umbrella#"$root"/}"
|
|
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 Intelligence CLI state; move it aside or restore the project before converting"
|
|
40
|
+
fi
|
|
41
|
+
done
|
|
42
|
+
if [ "$dry_run" -eq 0 ] && [ "$force" -eq 0 ]; then
|
|
43
|
+
if [ -n "$(git -C "$root" status --porcelain 2>/dev/null)" ]; then
|
|
44
|
+
die "working tree is not clean — commit or stash first (or --force); conversion produces a one-commit diff you can review and revert"
|
|
45
|
+
fi
|
|
46
|
+
fi
|
|
47
|
+
|
|
48
|
+
# `sync_version` belongs to the legacy Intelligence Sync format. Intelligence uses
|
|
49
|
+
# `schema_version`, so conversion reads the legacy scalar explicitly.
|
|
50
|
+
stamp="$(top_scalar "$config" "sync_version")"
|
|
51
|
+
eng="$(bundled_engine_version)"
|
|
52
|
+
[ -n "$stamp" ] && _ver_gt "$stamp" "$eng" && die "project schema $stamp is newer than this CLI's engine $eng — update the CLI first"
|
|
53
|
+
|
|
54
|
+
# The legacy `packs:` block is the one Intelligence Sync shape conversion still reads, 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 legacy 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 legacy 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
|
+
|
|
108
|
+
# ---- Stage ----------------------------------------------------------------
|
|
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
|
|
114
|
+
cleanup_stage() { rm -rf "$stage"; }
|
|
115
|
+
trap cleanup_stage EXIT INT TERM
|
|
116
|
+
mkdir -p "$stage/.intelligence/packages"
|
|
117
|
+
|
|
118
|
+
# Intelligence carries no legacy migration chain: the archived Intelligence Sync
|
|
119
|
+
# engine knows how to walk an old project forward. Conversion accepts only a
|
|
120
|
+
# project already at the final legacy 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
|
+
LEGACY_FINAL_SCHEMA="0.10.0"
|
|
124
|
+
if [ -z "$stamp" ] || _ver_gt "$LEGACY_FINAL_SCHEMA" "$stamp"; then
|
|
125
|
+
echo "ERROR: project schema is ${stamp:-absent (pre-0.3.1)}, older than the final legacy Intelligence Sync schema $LEGACY_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
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
echo "== staging =="
|
|
133
|
+
# Engine content arrives as the @ainova-systems/sync package, seeded from the
|
|
134
|
+
# bundle (fetch_package's guard — no network).
|
|
135
|
+
sync_pkg_sha="$(fetch_package "$SYNC_PKG_URL" "v$eng" "$SYNC_PKG_PATH" "$stage/$SYNC_PKG_STORE")"
|
|
136
|
+
|
|
137
|
+
# Legacy pack name -> Intelligence package name (@org/repo derived from the url), plus
|
|
138
|
+
# per-pack source facts. Recorded as rows for the config rewrite and the lock.
|
|
139
|
+
pack_rows="$stage/packs.rows"
|
|
140
|
+
: > "$pack_rows"
|
|
141
|
+
seen_names=" "
|
|
142
|
+
seen_mirrors=" "
|
|
143
|
+
while IFS= read -r pack; do
|
|
144
|
+
[ -n "$pack" ] || continue
|
|
145
|
+
url="$(get_pack_field "$config" "$pack" "url")"
|
|
146
|
+
ref="$(get_pack_field "$config" "$pack" "ref")"
|
|
147
|
+
mirror="$(get_pack_field "$config" "$pack" "mirror")"
|
|
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
|
|
157
|
+
base="${url%.git}"; base="${base%/}"
|
|
158
|
+
repo="$(basename "$base")"
|
|
159
|
+
org="$(basename "$(dirname "$base")")"
|
|
160
|
+
case "$org" in ""|*[!A-Za-z0-9._-]*) org="local" ;; esac
|
|
161
|
+
case "$repo" in ""|*[!A-Za-z0-9._-]*) repo="$(printf '%s' "$repo" | tr -c 'A-Za-z0-9._-' '-')" ;; esac
|
|
162
|
+
name="@$org/$repo"
|
|
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 "
|
|
170
|
+
printf '%s\n' "$pack$LOCK_SEP$name$LOCK_SEP$url$LOCK_SEP$ref$LOCK_SEP$mirror"
|
|
171
|
+
done < <(read_yaml_keys "$config" "packs") >> "$pack_rows"
|
|
172
|
+
|
|
173
|
+
# Stage the store: mirrored packs are COPIED from their mirrors (content the
|
|
174
|
+
# repo already holds — the network is not consulted, and the mirror is not
|
|
175
|
+
# touched); transient packs are fetched.
|
|
176
|
+
lock_rows="$stage/lock.rows"
|
|
177
|
+
: > "$lock_rows"
|
|
178
|
+
while IFS="$LOCK_SEP" read -r pack name url ref mirror; do
|
|
179
|
+
[ -n "$pack" ] || continue
|
|
180
|
+
dest="$stage/.intelligence/packages/$name"
|
|
181
|
+
sha=""
|
|
182
|
+
if [ -n "$mirror" ] && [ -d "$root/$mirror" ]; then
|
|
183
|
+
mkdir -p "$dest"
|
|
184
|
+
cp -R "$root/$mirror/." "$dest/"
|
|
185
|
+
if [ -f "$dest/.pack" ]; then
|
|
186
|
+
sha="$(awk -F= '$1 == "sha" {print $2; exit}' "$dest/.pack")"
|
|
187
|
+
rm -f "$dest/.pack"
|
|
188
|
+
fi
|
|
189
|
+
echo " pack '$pack' -> $name (from mirror $mirror)"
|
|
190
|
+
else
|
|
191
|
+
sha="$(fetch_package "$url" "$ref" "" "$dest")"
|
|
192
|
+
echo " pack '$pack' -> $name (fetched)"
|
|
193
|
+
fi
|
|
194
|
+
if [ -z "$sha" ] || [ "$sha" = "unknown" ]; then
|
|
195
|
+
sha="$(GIT_TERMINAL_PROMPT=0 git ls-remote -- "$url" "$ref" 2>/dev/null | awk '{print $1; exit}')"
|
|
196
|
+
fi
|
|
197
|
+
[ -n "$ref" ] || ref="HEAD"
|
|
198
|
+
printf '%s\037%s\037%s\037%s\037%s\037%s\n' \
|
|
199
|
+
"$name" "" "$url" "" "$ref" "$sha" >> "$lock_rows"
|
|
200
|
+
done < "$pack_rows"
|
|
201
|
+
|
|
202
|
+
# The manifest: config.yaml transformed line by line — comments and unknown
|
|
203
|
+
# blocks survive verbatim; `sync_version` and `packs:` are dropped (restamped
|
|
204
|
+
# / converted); sources entries are rewritten onto the store.
|
|
205
|
+
manifest_stage="$stage/intelligence.yaml"
|
|
206
|
+
rewrite_source_value() {
|
|
207
|
+
local v="$1" pack name rest
|
|
208
|
+
case "$v" in
|
|
209
|
+
"$module_rel"/rules|"$module_rel"/agents|"$module_rel"/skills)
|
|
210
|
+
printf '%s/%s' "$SYNC_PKG_STORE" "${v##*/}"
|
|
211
|
+
return 0 ;;
|
|
212
|
+
@*)
|
|
213
|
+
rest="${v#@}"
|
|
214
|
+
pack="${rest%%/*}"
|
|
215
|
+
while IFS="$LOCK_SEP" read -r p name _ _ _; do
|
|
216
|
+
if [ "$p" = "$pack" ]; then
|
|
217
|
+
if [ "$rest" = "$pack" ]; then
|
|
218
|
+
printf '.intelligence/packages/%s' "$name"
|
|
219
|
+
else
|
|
220
|
+
printf '.intelligence/packages/%s/%s' "$name" "${rest#*/}"
|
|
221
|
+
fi
|
|
222
|
+
return 0
|
|
223
|
+
fi
|
|
224
|
+
done < "$pack_rows"
|
|
225
|
+
;;
|
|
226
|
+
esac
|
|
227
|
+
printf '%s' "$v"
|
|
228
|
+
}
|
|
229
|
+
in_packs=0 in_sources=0
|
|
230
|
+
while IFS= read -r line || [ -n "$line" ]; do
|
|
231
|
+
line="${line%$'\r'}"
|
|
232
|
+
case "$line" in
|
|
233
|
+
sync_version:*) continue ;;
|
|
234
|
+
packs:*) in_packs=1; continue ;;
|
|
235
|
+
sources:*) in_sources=1; in_packs=0; echo "$line"; continue ;;
|
|
236
|
+
[!\ \#]*) in_packs=0; in_sources=0 ;;
|
|
237
|
+
esac
|
|
238
|
+
[ "$in_packs" -eq 1 ] && continue
|
|
239
|
+
if [ "$in_sources" -eq 1 ]; then
|
|
240
|
+
case "$line" in
|
|
241
|
+
*"- "*)
|
|
242
|
+
indent="${line%%-*}"
|
|
243
|
+
v="${line#*- }"
|
|
244
|
+
# A trailing inline comment survives the rewrite — split it
|
|
245
|
+
# off before touching quotes, or it would be mangled into the
|
|
246
|
+
# value.
|
|
247
|
+
trail=""
|
|
248
|
+
case "$v" in
|
|
249
|
+
*[[:space:]]"#"*)
|
|
250
|
+
trail=" #${v#*[[:space:]]#}"
|
|
251
|
+
v="${v%%[[:space:]]#*}"
|
|
252
|
+
;;
|
|
253
|
+
esac
|
|
254
|
+
v="${v%"${v##*[![:space:]]}"}"
|
|
255
|
+
v="${v%\"}"; v="${v#\"}"; v="${v%\'}"; v="${v#\'}"
|
|
256
|
+
echo "$indent- \"$(rewrite_source_value "$v")\"$trail"
|
|
257
|
+
continue
|
|
258
|
+
;;
|
|
259
|
+
esac
|
|
260
|
+
fi
|
|
261
|
+
echo "$line"
|
|
262
|
+
done < "$config" > "$manifest_stage"
|
|
263
|
+
stamp_schema_version "$manifest_stage" "$eng"
|
|
264
|
+
|
|
265
|
+
# packages: requested pins only; resolved source details live in the lock.
|
|
266
|
+
while IFS="$LOCK_SEP" read -r pack name url ref mirror; do
|
|
267
|
+
[ -n "$pack" ] || continue
|
|
268
|
+
qmap_set "$manifest_stage" "packages" "$name" "ref" "${ref:-HEAD}"
|
|
269
|
+
done < "$pack_rows"
|
|
270
|
+
|
|
271
|
+
# The engine's own content rides along as a package, pinned to the engine.
|
|
272
|
+
qmap_set "$manifest_stage" "packages" "$SYNC_PKG_NAME" "version" "$eng"
|
|
273
|
+
printf '%s\037%s\037%s\037%s\037%s\037%s\n' \
|
|
274
|
+
"$SYNC_PKG_NAME" "$eng" "$SYNC_PKG_URL" "$SYNC_PKG_PATH" "v$eng" "$sync_pkg_sha" >> "$lock_rows"
|
|
275
|
+
|
|
276
|
+
# ---- Verify ---------------------------------------------------------------
|
|
277
|
+
echo "== verifying staged state =="
|
|
278
|
+
fail=0
|
|
279
|
+
for section in rules agents skills; do
|
|
280
|
+
while IFS= read -r src; do
|
|
281
|
+
[ -n "$src" ] || continue
|
|
282
|
+
case "$src" in
|
|
283
|
+
.intelligence/*) [ -d "$stage/$src" ] || { echo " MISSING (staged): $src"; fail=1; } ;;
|
|
284
|
+
git+*|@*) echo " UNCONVERTED source token: $src"; fail=1 ;;
|
|
285
|
+
*) [ -d "$root/$src" ] || echo " NOTE: local source '$src' does not exist (kept — same as before)" ;;
|
|
286
|
+
esac
|
|
287
|
+
done < <(read_yaml_list "$manifest_stage" "$section")
|
|
288
|
+
done
|
|
289
|
+
target_keys() {
|
|
290
|
+
awk '
|
|
291
|
+
/^targets:[[:space:]]*$/ { in_targets=1; next }
|
|
292
|
+
in_targets && /^[^[:space:]#]/ { exit }
|
|
293
|
+
in_targets && /^ [A-Za-z_][A-Za-z0-9_]*:/ {
|
|
294
|
+
line=$0; sub(/^ /, "", line); sub(/:.*/, "", line); print line
|
|
295
|
+
}
|
|
296
|
+
' "$1"
|
|
297
|
+
}
|
|
298
|
+
while IFS= read -r adapter; do
|
|
299
|
+
[ -n "$adapter" ] || continue
|
|
300
|
+
old_e="$(is_target_enabled "$config" "$adapter")"
|
|
301
|
+
new_e="$(is_target_enabled "$manifest_stage" "$adapter")"
|
|
302
|
+
[ "$old_e" = "$new_e" ] || { echo " TARGET DRIFT: $adapter enabled '$old_e' -> '$new_e'"; fail=1; }
|
|
303
|
+
old_o="$(get_target_output "$config" "$adapter")"
|
|
304
|
+
new_o="$(get_target_output "$manifest_stage" "$adapter")"
|
|
305
|
+
[ "$old_o" = "$new_o" ] || { echo " TARGET DRIFT: $adapter output '$old_o' -> '$new_o'"; fail=1; }
|
|
306
|
+
done < <({ target_keys "$config"; target_keys "$manifest_stage"; } | sort -u)
|
|
307
|
+
[ "$fail" -eq 0 ] || die "staged state failed verification — nothing changed"
|
|
308
|
+
|
|
309
|
+
if [ "$dry_run" -eq 1 ]; then
|
|
310
|
+
echo ""
|
|
311
|
+
echo "== dry run — the project is untouched. Staged manifest: =="
|
|
312
|
+
cat "$manifest_stage"
|
|
313
|
+
echo ""
|
|
314
|
+
echo "Would: write intelligence.yaml + intelligence.lock, move the store in,"
|
|
315
|
+
echo "sync, then remove $module_rel/, config.yaml and the pack mirrors."
|
|
316
|
+
exit 0
|
|
317
|
+
fi
|
|
318
|
+
|
|
319
|
+
# ---- Commit ---------------------------------------------------------------
|
|
320
|
+
echo "== committing =="
|
|
321
|
+
# .gitignore is part of the transaction: rollback must leave it exactly as
|
|
322
|
+
# found, absent included.
|
|
323
|
+
gitignore_existed=0
|
|
324
|
+
if [ -f "$root/.gitignore" ]; then
|
|
325
|
+
gitignore_existed=1
|
|
326
|
+
cp "$root/.gitignore" "$stage/gitignore.orig"
|
|
327
|
+
fi
|
|
328
|
+
commit_active=1
|
|
329
|
+
rollback() {
|
|
330
|
+
[ "$commit_active" -eq 1 ] || return 0
|
|
331
|
+
commit_active=0
|
|
332
|
+
set +e
|
|
333
|
+
rm -rf "$root/.intelligence" "$root/intelligence.yaml" "$root/intelligence.lock"
|
|
334
|
+
if [ "$gitignore_existed" -eq 1 ]; then
|
|
335
|
+
cp "$stage/gitignore.orig" "$root/.gitignore"
|
|
336
|
+
else
|
|
337
|
+
rm -f "$root/.gitignore"
|
|
338
|
+
fi
|
|
339
|
+
rm -rf "$stage"
|
|
340
|
+
echo "rolled back — the vendored setup is untouched" >&2
|
|
341
|
+
}
|
|
342
|
+
rollback_on_exit() {
|
|
343
|
+
local rc=$?
|
|
344
|
+
rollback
|
|
345
|
+
exit "$rc"
|
|
346
|
+
}
|
|
347
|
+
# Every live write through verified sync is covered by rollback, including a
|
|
348
|
+
# plain set -e/EXIT failure rather than only an explicit sync refusal.
|
|
349
|
+
trap rollback_on_exit EXIT
|
|
350
|
+
trap 'rollback; exit 130' INT TERM
|
|
351
|
+
if [ ! -f "$root/.gitignore" ] || ! grep -q '^\.intelligence/' "$root/.gitignore"; then
|
|
352
|
+
{
|
|
353
|
+
[ -f "$root/.gitignore" ] && [ -n "$(tail -c 1 "$root/.gitignore" 2>/dev/null)" ] && echo ""
|
|
354
|
+
echo "# intelligence CLI package store (restored automatically by 'intelligence sync')"
|
|
355
|
+
echo ".intelligence/"
|
|
356
|
+
} >> "$root/.gitignore"
|
|
357
|
+
fi
|
|
358
|
+
mv "$stage/.intelligence" "$root/.intelligence"
|
|
359
|
+
cp "$manifest_stage" "$root/intelligence.yaml"
|
|
360
|
+
lock_write_from_tsv "$root/intelligence.lock" "$lock_rows"
|
|
361
|
+
|
|
362
|
+
sync_out="$(cd "$root" && IS_SUPPRESS_CLI_NOTE=1 bash "$CLI_DIR/commands/sync.sh" 2>&1)" || {
|
|
363
|
+
echo "$sync_out"
|
|
364
|
+
die "sync of the converted state failed"
|
|
365
|
+
}
|
|
366
|
+
echo "$sync_out" | grep -q '^IS_STATUS=ok' || {
|
|
367
|
+
echo "$sync_out"
|
|
368
|
+
die "sync of the converted state did not report ok"
|
|
369
|
+
}
|
|
370
|
+
# Point of no return: the new state is verified. From here an interrupt must
|
|
371
|
+
# NOT roll back (the old setup is being removed) — only clean the stage.
|
|
372
|
+
commit_active=0
|
|
373
|
+
trap cleanup_stage EXIT INT TERM
|
|
374
|
+
|
|
375
|
+
# Only now is anything of the old setup removed — and config.yaml is kept as
|
|
376
|
+
# a reviewable backup inside the store.
|
|
377
|
+
mkdir -p "$root/.intelligence/backup"
|
|
378
|
+
cp "$config" "$root/.intelligence/backup/config.yaml"
|
|
379
|
+
while IFS="$LOCK_SEP" read -r pack name url ref mirror; do
|
|
380
|
+
# Containment before deletion: a mirror value is config input, and rm -rf
|
|
381
|
+
# must never follow it out of the repository.
|
|
382
|
+
case "$mirror" in
|
|
383
|
+
""|/*|*..*) [ -n "$mirror" ] && echo " WARN: mirror '$mirror' not repo-contained — left in place" >&2; continue ;;
|
|
384
|
+
esac
|
|
385
|
+
if [ -d "$root/$mirror" ]; then
|
|
386
|
+
rm -rf "${root:?}/$mirror"
|
|
387
|
+
# An emptied legacy mirror parent (for example intelligence/external/) goes with it.
|
|
388
|
+
rmdir "$(dirname "$root/$mirror")" 2>/dev/null || true
|
|
389
|
+
fi
|
|
390
|
+
done < "$pack_rows"
|
|
391
|
+
rm -rf "$module_dir"
|
|
392
|
+
rm -f "$config"
|
|
393
|
+
# An emptied umbrella dir (config-only setups) is dropped; one with project
|
|
394
|
+
# content stays.
|
|
395
|
+
rmdir "$umbrella" 2>/dev/null || true
|
|
396
|
+
|
|
397
|
+
echo ""
|
|
398
|
+
echo "converted. Review the diff, then commit. The old config.yaml is kept at .intelligence/backup/config.yaml."
|
|
399
|
+
echo "From now on: intelligence sync | package | update | status."
|
|
400
|
+
echo "Next: ask your agent to run /intelligence-learn-from-repository to review the converted 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
|
|
@@ -23,7 +23,7 @@ while [ $# -gt 0 ]; do
|
|
|
23
23
|
shift || true
|
|
24
24
|
done
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
require_cli_project
|
|
27
27
|
manifest="$IP_ROOT/intelligence.yaml"
|
|
28
28
|
lock="$IP_ROOT/intelligence.lock"
|
|
29
29
|
|
|
@@ -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
|
;;
|
|
@@ -78,9 +79,27 @@ assert_valid_pkg_name "$name"
|
|
|
78
79
|
if [ "$mode" = "registry" ]; then
|
|
79
80
|
resolve_package_source "$manifest" "$name"
|
|
80
81
|
url="$RES_URL"; path="$RES_PATH"
|
|
82
|
+
if [ -z "$url" ]; then
|
|
83
|
+
echo "ERROR: no trusted registry declares '$name'." >&2
|
|
84
|
+
echo " A name installs only through a registry this project added explicitly" >&2
|
|
85
|
+
echo " ('intelligence registry list' shows them). Either add its registry:" >&2
|
|
86
|
+
echo " intelligence registry add <registry-repo-url>" >&2
|
|
87
|
+
echo " or install from an explicit source you name yourself:" >&2
|
|
88
|
+
echo " intelligence package add github:org/repo | intelligence package add git+<url>[@ref][#path]" >&2
|
|
89
|
+
suggest_similar "$manifest" "$name"
|
|
90
|
+
exit 1
|
|
91
|
+
fi
|
|
81
92
|
echo " resolving $name via $RES_VIA -> $url${path:+ ($path)}"
|
|
82
93
|
fi
|
|
83
94
|
|
|
95
|
+
# The source must exist before anything else is attempted — never die
|
|
96
|
+
# mid-pipeline with no message.
|
|
97
|
+
if ! GIT_TERMINAL_PROMPT=0 git ls-remote -- "$url" >/dev/null 2>&1; then
|
|
98
|
+
echo "ERROR: no reachable repository at $url" >&2
|
|
99
|
+
echo " Declared by ${RES_VIA:-the spec} — check the URL and your git access." >&2
|
|
100
|
+
exit 1
|
|
101
|
+
fi
|
|
102
|
+
|
|
84
103
|
# Pick the version: an explicit ref pins; otherwise the highest stable tag
|
|
85
104
|
# matching the range (npm's behaviour, `^highest` becoming the requested range
|
|
86
105
|
# when none was given).
|
|
@@ -98,6 +117,7 @@ if [ -z "$ref" ]; then
|
|
|
98
117
|
die "'$name' has no stable version tags at $url — a registry package must be versioned; use github:/git+ with @<ref> to pin a branch or commit"
|
|
99
118
|
fi
|
|
100
119
|
echo " NOTE: no version tags at $url — pinning the default branch head" >&2
|
|
120
|
+
ref="HEAD"
|
|
101
121
|
fi
|
|
102
122
|
fi
|
|
103
123
|
|
|
@@ -106,18 +126,16 @@ sha="$(fetch_package "$url" "${ref:-$resolved_tag}" "$path" "$IP_ROOT/$rel")"
|
|
|
106
126
|
|
|
107
127
|
wire_package_sources "$manifest" "$name" "$rel" "$IP_ROOT"
|
|
108
128
|
|
|
109
|
-
#
|
|
110
|
-
#
|
|
111
|
-
|
|
129
|
+
# The manifest records requested intent only. The resolved source URL/path,
|
|
130
|
+
# tag/ref and SHA live in the required lock. Re-adding a package is the
|
|
131
|
+
# explicit operation for changing its trusted source.
|
|
132
|
+
qmap_delete_key "$manifest" "packages" "$name"
|
|
133
|
+
if [ -n "$ref" ]; then
|
|
134
|
+
qmap_set "$manifest" "packages" "$name" "ref" "$ref"
|
|
135
|
+
elif [ -n "$requested" ]; then
|
|
112
136
|
qmap_set "$manifest" "packages" "$name" "version" "$requested"
|
|
113
137
|
else
|
|
114
|
-
|
|
115
|
-
[ -n "$path" ] && qmap_set "$manifest" "packages" "$name" "path" "$path"
|
|
116
|
-
if [ -n "$ref" ]; then
|
|
117
|
-
qmap_set "$manifest" "packages" "$name" "ref" "$ref"
|
|
118
|
-
elif [ -n "$requested" ]; then
|
|
119
|
-
qmap_set "$manifest" "packages" "$name" "version" "$requested"
|
|
120
|
-
fi
|
|
138
|
+
die "internal: package '$name' has neither a requested version nor ref"
|
|
121
139
|
fi
|
|
122
140
|
|
|
123
141
|
lock_upsert "$lock" "$name" "$requested" "$url" "$path" "${ref:-$resolved_tag}" "$sha"
|
|
@@ -1,9 +1,9 @@
|
|
|
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
|
|
|
6
|
-
|
|
6
|
+
require_cli_project
|
|
7
7
|
manifest="$IP_ROOT/intelligence.yaml"
|
|
8
8
|
lock="$IP_ROOT/intelligence.lock"
|
|
9
9
|
|
|
@@ -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,20 +1,32 @@
|
|
|
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
|
-
no_sync=0
|
|
9
|
+
no_sync=0 force=0
|
|
10
10
|
for a in "$@"; do
|
|
11
11
|
case "$a" in
|
|
12
12
|
--no-sync) no_sync=1 ;;
|
|
13
|
+
--force) force=1 ;;
|
|
13
14
|
*) die "unknown option '$a'" ;;
|
|
14
15
|
esac
|
|
15
16
|
done
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
require_cli_project
|
|
19
|
+
|
|
20
|
+
# Removing the engine-content package guts the outputs (meta-skills, the
|
|
21
|
+
# authoring rule, both engine agents disappear) while everything still
|
|
22
|
+
# reports ok — legal, but never by accident.
|
|
23
|
+
if [ "$name" = "$SYNC_PKG_NAME" ] && [ "$force" -eq 0 ]; then
|
|
24
|
+
echo "ERROR: $SYNC_PKG_NAME is the engine's own content — removing it drops every" >&2
|
|
25
|
+
echo " intelligence-* meta-skill, the authoring rule and the engine agents from" >&2
|
|
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 package remove $SYNC_PKG_NAME --force" >&2
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
18
30
|
assert_valid_pkg_name "$name"
|
|
19
31
|
manifest="$IP_ROOT/intelligence.yaml"
|
|
20
32
|
lock="$IP_ROOT/intelligence.lock"
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Internal package search operation: what the registries offer and what this project
|
|
3
|
+
# already has. The catalogue view `list` deliberately is not: `list` answers
|
|
4
|
+
# "what does this project depend on", `search` answers "what could it".
|
|
5
|
+
#
|
|
6
|
+
# Sources, in resolution order (a name found earlier wins, exactly as `add`
|
|
7
|
+
# resolves it): the manifest's trusted registries, in order.
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
source "$CLI_DIR/lib/cli-common.sh"
|
|
10
|
+
|
|
11
|
+
term="${1:-}"
|
|
12
|
+
|
|
13
|
+
detect_project
|
|
14
|
+
manifest=""
|
|
15
|
+
[ "$IP_MODE" = "cli" ] && manifest="$IP_ROOT/intelligence.yaml"
|
|
16
|
+
|
|
17
|
+
# State of one package name in this project: installed / declared / available.
|
|
18
|
+
pkg_state() {
|
|
19
|
+
local name="$1"
|
|
20
|
+
[ -n "$manifest" ] || { printf 'available'; return 0; }
|
|
21
|
+
local declared=0 k
|
|
22
|
+
while IFS= read -r k; do
|
|
23
|
+
[ "$k" = "$name" ] && declared=1
|
|
24
|
+
done < <(qmap_keys "$manifest" "packages")
|
|
25
|
+
if [ "$declared" -eq 0 ]; then
|
|
26
|
+
printf 'available'
|
|
27
|
+
elif [ -d "$IP_ROOT/.intelligence/packages/$name" ]; then
|
|
28
|
+
printf 'installed %s' "$(qmap_field "$IP_ROOT/intelligence.lock" "packages" "$name" "resolved")"
|
|
29
|
+
else
|
|
30
|
+
printf 'declared (run sync)'
|
|
31
|
+
fi
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
seen=" "
|
|
35
|
+
rows=0
|
|
36
|
+
# emit_index <index-file> <origin-label>
|
|
37
|
+
# Registries are consulted in trust order; the first to declare a name wins,
|
|
38
|
+
# so a name already seen is not repeated (that IS the resolution order).
|
|
39
|
+
emit_index() {
|
|
40
|
+
local index="$1" origin="$2" name desc state
|
|
41
|
+
[ -n "$index" ] && [ -f "$index" ] || return 0
|
|
42
|
+
while IFS= read -r name; do
|
|
43
|
+
[ -n "$name" ] || continue
|
|
44
|
+
case "$seen" in *" $name "*) continue ;; esac
|
|
45
|
+
if [ -n "$term" ]; then
|
|
46
|
+
desc="$(qmap_field "$index" "packages" "$name" "description")"
|
|
47
|
+
case "$name$desc" in
|
|
48
|
+
*"$term"*) ;;
|
|
49
|
+
*) continue ;;
|
|
50
|
+
esac
|
|
51
|
+
fi
|
|
52
|
+
seen="$seen$name "
|
|
53
|
+
desc="$(qmap_field "$index" "packages" "$name" "description")"
|
|
54
|
+
state="$(pkg_state "$name")"
|
|
55
|
+
printf '%-34s %-22s %s\n' "$name" "$state" "${desc:-$(qmap_field "$index" "packages" "$name" "url")}"
|
|
56
|
+
[ "$origin" = "bundled" ] || printf '%-34s %s\n' "" " via $origin"
|
|
57
|
+
rows=$((rows + 1))
|
|
58
|
+
done < <(qmap_keys "$index" "packages")
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if [ -n "$manifest" ]; then
|
|
62
|
+
while IFS= read -r url; do
|
|
63
|
+
[ -n "$url" ] || continue
|
|
64
|
+
index="$(_fetch_index "${url#git+}")"
|
|
65
|
+
if [ -z "$index" ]; then
|
|
66
|
+
echo " WARN: registry unreachable or missing index.yaml: $url" >&2
|
|
67
|
+
continue
|
|
68
|
+
fi
|
|
69
|
+
emit_index "$index" "$url"
|
|
70
|
+
done < <(registries_list "$manifest")
|
|
71
|
+
fi
|
|
72
|
+
if [ "$rows" -eq 0 ]; then
|
|
73
|
+
if [ -n "$term" ]; then
|
|
74
|
+
echo "Nothing matching '$term' in this project's trusted registries."
|
|
75
|
+
else
|
|
76
|
+
echo "No trusted registries (or they offer nothing) — intelligence registry add <repo-url>."
|
|
77
|
+
fi
|
|
78
|
+
echo "Explicit installs need no registry: intelligence package add github:org/repo"
|
|
79
|
+
exit 0
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
echo ""
|
|
83
|
+
echo "add one: intelligence package add <name> | private registry: intelligence registry add <repo-url>"
|