@ainova-systems/intelligence 0.11.0-rc.7 → 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 +4 -2
- package/cli/commands/status.sh +12 -4
- package/cli/commands/sync.sh +7 -20
- package/cli/commands/update.sh +76 -70
- package/cli/engine-package.yaml +2 -2
- package/cli/intelligence +10 -12
- package/cli/{commands/doctor.sh → internal/check.sh} +32 -14
- package/cli/{commands/migrate.sh → internal/migrate-v1.sh} +114 -25
- package/cli/{commands/add.sh → internal/package-add.sh} +3 -3
- 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} +3 -3
- 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 +133 -7
- package/cli/lib/lockfile.sh +1 -1
- package/cli/lib/manifest.sh +86 -0
- package/cli/lib/registry.sh +4 -4
- package/engine/ENGINE_SHA +1 -1
- package/engine/adapters/_template.sh +10 -9
- package/engine/adapters/agents.sh +11 -11
- package/engine/adapters/opencode.sh +1 -1
- package/engine/lib/common.sh +14 -22
- package/engine/lib/contract.sh +14 -14
- package/engine/sync.sh +25 -20
- package/package.json +1 -1
- package/packages/sync/agents/intelligence-architect.md +5 -3
- package/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/packages/sync/rules/intelligence-authoring.md +6 -6
- package/packages/sync/skills/intelligence-add-agent/SKILL.md +5 -5
- package/packages/sync/skills/intelligence-add-rule/SKILL.md +3 -3
- package/packages/sync/skills/intelligence-add-skill/SKILL.md +3 -3
- package/packages/sync/skills/intelligence-extract-skill/SKILL.md +2 -2
- package/packages/sync/skills/intelligence-install-adapter/SKILL.md +29 -22
- package/packages/sync/skills/intelligence-learn-from-context/SKILL.md +3 -3
- package/packages/sync/skills/intelligence-learn-from-repository/SKILL.md +55 -0
- package/packages/sync/skills/intelligence-review-skills/SKILL.md +6 -6
- package/packages/sync/skills/intelligence-sync/SKILL.md +13 -9
- package/packages/sync/skills/intelligence-uninstall-adapter/SKILL.md +19 -37
- package/packages/sync/skills/intelligence-update/SKILL.md +34 -156
- package/packages/sync/docs/ADAPTERS.md +0 -214
- package/packages/sync/docs/CONVENTIONS.md +0 -456
package/cli/commands/update.sh
CHANGED
|
@@ -1,87 +1,93 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# intelligence update [name] [--
|
|
2
|
+
# intelligence update [@scope/name] [--preview|--apply]
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
4
|
+
# One update surface: show the installed-CLI/project/package plan, then either
|
|
5
|
+
# stop, ask interactively, or apply without a prompt. Updating the global npm
|
|
6
|
+
# installation remains an explicit package-manager operation.
|
|
7
7
|
set -euo pipefail
|
|
8
8
|
source "$CLI_DIR/lib/cli-common.sh"
|
|
9
9
|
|
|
10
|
-
only=""
|
|
10
|
+
only="" mode="ask" preview_seen=0 apply_seen=0
|
|
11
11
|
while [ $# -gt 0 ]; do
|
|
12
12
|
case "$1" in
|
|
13
|
-
--
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
--preview) preview_seen=1; mode="preview" ;;
|
|
14
|
+
--apply) apply_seen=1; mode="apply" ;;
|
|
15
|
+
@*) [ -z "$only" ] || die "only one package may be selected"; only="$1" ;;
|
|
16
|
+
*) die "usage: intelligence update [@scope/name] [--preview|--apply]" ;;
|
|
16
17
|
esac
|
|
17
|
-
shift
|
|
18
|
+
shift
|
|
18
19
|
done
|
|
20
|
+
[ "$preview_seen" -eq 0 ] || [ "$apply_seen" -eq 0 ] || die "choose either --preview or --apply"
|
|
19
21
|
|
|
20
22
|
require_v2
|
|
21
23
|
manifest="$IP_ROOT/intelligence.yaml"
|
|
22
|
-
|
|
24
|
+
check_version_compat "$manifest"
|
|
25
|
+
eng="$(bundled_engine_version)"
|
|
26
|
+
stamp="$(read_schema_version "$manifest")"
|
|
27
|
+
project_change=0
|
|
23
28
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
echo " $
|
|
35
|
-
|
|
29
|
+
echo "Update plan"
|
|
30
|
+
echo ""
|
|
31
|
+
echo "CLI:"
|
|
32
|
+
if [ -n "${INTELLIGENCE_NPM_VERSION:-}" ] && [ "${IS_SKIP_NPM_CHECK:-0}" != "1" ] && command -v npm >/dev/null 2>&1; then
|
|
33
|
+
channel="latest"
|
|
34
|
+
case "$INTELLIGENCE_NPM_VERSION" in *-*) channel="next" ;; esac
|
|
35
|
+
available="$(npm view "@ainova-systems/intelligence" "dist-tags.$channel" 2>/dev/null || true)"
|
|
36
|
+
if [ -z "$available" ]; then
|
|
37
|
+
echo " installed: $INTELLIGENCE_NPM_VERSION; registry check unavailable"
|
|
38
|
+
elif [ "$available" = "$INTELLIGENCE_NPM_VERSION" ]; then
|
|
39
|
+
echo " $INTELLIGENCE_NPM_VERSION (up to date on npm $channel)"
|
|
40
|
+
else
|
|
41
|
+
echo " $INTELLIGENCE_NPM_VERSION -> $available (npm $channel)"
|
|
42
|
+
echo " run: npm install -g @ainova-systems/intelligence@$channel"
|
|
43
|
+
echo " then rerun: intelligence update --apply"
|
|
36
44
|
fi
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
url="$RES_URL"; path="$RES_PATH"
|
|
50
|
-
fi
|
|
51
|
-
picked="$(list_remote_versions "$url" | semver_pick_highest "$range")"
|
|
52
|
-
[ -n "$picked" ] || { echo " $name: nothing satisfies '${range:-*}' at $url" >&2; continue; }
|
|
53
|
-
read -r tag _ <<< "$(remote_tag_for_version "$url" "$picked")"
|
|
54
|
-
current="$(qmap_field "$lock" "packages" "$name" "resolved")"
|
|
55
|
-
cur_url="$(qmap_field "$lock" "packages" "$name" "url")"
|
|
56
|
-
cur_path="$(qmap_field "$lock" "packages" "$name" "path")"
|
|
57
|
-
# Same tag is not enough: a registry may re-point a name's source while
|
|
58
|
-
# keeping the version — the move doctor warns about must be actionable
|
|
59
|
-
# here, so url/path changes refetch too.
|
|
60
|
-
if [ "$tag" = "$current" ] && [ "$url" = "$cur_url" ] && [ "$path" = "$cur_path" ]; then
|
|
61
|
-
echo " $name: ${current:-<none>} (up to date)"
|
|
62
|
-
continue
|
|
63
|
-
fi
|
|
64
|
-
rel=".intelligence/packages/$name"
|
|
65
|
-
# Fetch into staging FIRST: a failed clone must leave the current install
|
|
66
|
-
# fully wired and intact. Only after success is the old shape unwired
|
|
67
|
-
# (the new version may have dropped a section dir), the store swapped,
|
|
68
|
-
# and the new shape wired.
|
|
69
|
-
staging="$IP_ROOT/.intelligence/.staging-$$"
|
|
70
|
-
rm -rf "$staging"
|
|
71
|
-
sha="$(fetch_package "$url" "$tag" "$path" "$staging")"
|
|
72
|
-
unwire_package_sources "$manifest" "$rel"
|
|
73
|
-
rm -rf "${IP_ROOT:?}/$rel"
|
|
74
|
-
mkdir -p "$(dirname "$IP_ROOT/$rel")"
|
|
75
|
-
mv "$staging" "$IP_ROOT/$rel"
|
|
76
|
-
wire_package_sources "$manifest" "$name" "$rel" "$IP_ROOT"
|
|
77
|
-
lock_upsert "$lock" "$name" "$range" "$url" "$path" "$tag" "$sha"
|
|
78
|
-
echo " $name: ${current:-<none>} -> $tag"
|
|
79
|
-
moved=$((moved + 1))
|
|
80
|
-
done < <(qmap_keys "$manifest" "packages")
|
|
45
|
+
else
|
|
46
|
+
echo " source checkout/registry check skipped; engine $eng"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
echo ""
|
|
50
|
+
echo "Project:"
|
|
51
|
+
if project_needs_upgrade "$IP_ROOT"; then
|
|
52
|
+
echo " lifecycle alignment required (stamp ${stamp:-unstamped}, engine $eng)"
|
|
53
|
+
project_change=1
|
|
54
|
+
else
|
|
55
|
+
echo " schema/content $eng (up to date)"
|
|
56
|
+
fi
|
|
81
57
|
|
|
82
|
-
|
|
83
|
-
|
|
58
|
+
if project_has_packages "$IP_ROOT" && [ ! -f "$IP_ROOT/intelligence.lock" ]; then
|
|
59
|
+
die "manifest declares packages but intelligence.lock is absent — restore the committed lock before planning or applying updates"
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
pkg_args=(--preview)
|
|
63
|
+
[ -z "$only" ] || pkg_args+=("$only")
|
|
64
|
+
pkg_plan="$(bash "$CLI_DIR/internal/package-update.sh" "${pkg_args[@]}")"
|
|
65
|
+
echo ""
|
|
66
|
+
echo "Packages:"
|
|
67
|
+
printf '%s\n' "$pkg_plan"
|
|
68
|
+
package_changes="$(printf '%s\n' "$pkg_plan" | awk '/^updates available:/ {print $3; exit}')"
|
|
69
|
+
package_changes="${package_changes:-0}"
|
|
84
70
|
|
|
85
|
-
if [ "$
|
|
86
|
-
|
|
71
|
+
if [ "$mode" = "preview" ]; then
|
|
72
|
+
exit 0
|
|
73
|
+
fi
|
|
74
|
+
if [ "$project_change" -eq 0 ] && [ "$package_changes" -eq 0 ]; then
|
|
75
|
+
echo ""
|
|
76
|
+
echo "Nothing to apply with the currently installed CLI."
|
|
77
|
+
exit 0
|
|
87
78
|
fi
|
|
79
|
+
if [ "$mode" = "ask" ]; then
|
|
80
|
+
[ -t 0 ] || die "update requires confirmation — rerun with --preview or --apply"
|
|
81
|
+
printf '\nApply this plan? [Y/n] '
|
|
82
|
+
read -r answer
|
|
83
|
+
case "$answer" in
|
|
84
|
+
""|y|Y|yes|YES) ;;
|
|
85
|
+
*) echo "update cancelled"; exit 0 ;;
|
|
86
|
+
esac
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
ensure_project_current "$IP_ROOT"
|
|
90
|
+
apply_args=(--no-sync)
|
|
91
|
+
[ -z "$only" ] || apply_args+=("$only")
|
|
92
|
+
bash "$CLI_DIR/internal/package-update.sh" "${apply_args[@]}"
|
|
93
|
+
exec bash "$CLI_DIR/commands/sync.sh"
|
package/cli/engine-package.yaml
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
# The engine-content package is the one package whose content documents the
|
|
7
7
|
# engine scripts bundled with the CLI: init writes this entry into the
|
|
8
8
|
# manifest (visible, removable), its pin always equals the engine VERSION,
|
|
9
|
-
#
|
|
10
|
-
# bundle without network.
|
|
9
|
+
# lifecycle preflight aligns it with the installed CLI, and at the matching
|
|
10
|
+
# version it materializes from the bundle without network.
|
|
11
11
|
name: "@ainova-systems/sync"
|
|
12
12
|
url: "https://github.com/ainova-systems/intelligence.git"
|
|
13
13
|
path: "packages/sync"
|
package/cli/intelligence
CHANGED
|
@@ -43,19 +43,17 @@ intelligence — build, version and distribute AI agent intelligence
|
|
|
43
43
|
|
|
44
44
|
Usage: intelligence <command> [args]
|
|
45
45
|
|
|
46
|
-
init
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
update [name]
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
46
|
+
init [--preview|--apply] Initialize, migrate, restore or align this project
|
|
47
|
+
--targets a,b --dir name --bare --no-sync New-project options
|
|
48
|
+
--force Accept a dirty worktree during v1 conversion
|
|
49
|
+
sync [adapter] Restore locked content if needed, then render enabled adapters
|
|
50
|
+
update [@scope/name] Show an update plan; ask before applying it
|
|
51
|
+
--preview Show the plan without asking or writing
|
|
52
|
+
--apply Apply the plan without asking
|
|
53
|
+
package <command> add | remove | list | search
|
|
54
|
+
adapter <command> list | create | enable | disable | remove
|
|
55
|
+
status [--check] Project state; --check runs deep consistency checks
|
|
55
56
|
registry <list|add|remove> Manage the trusted registry list (add <repo-url>)
|
|
56
|
-
migrate [--dry-run] Convert a vendored (v1) setup to the CLI setup
|
|
57
|
-
doctor Check manifest, lockfile, store and engine consistency
|
|
58
|
-
status Project state at a glance
|
|
59
57
|
version Print CLI and engine versions
|
|
60
58
|
EOF
|
|
61
59
|
exit 0
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
#
|
|
2
|
+
# Internal deep consistency check. No writes; exits 1 on problems.
|
|
3
3
|
set -euo pipefail
|
|
4
4
|
source "$CLI_DIR/lib/cli-common.sh"
|
|
5
5
|
|
|
@@ -22,7 +22,7 @@ case "$IP_MODE" in
|
|
|
22
22
|
legacy)
|
|
23
23
|
echo "project: vendored (v1) at $IP_ROOT"
|
|
24
24
|
ok "vendored engine $(tr -d ' \t\r\n' < "$IP_MODULE_DIR/scripts/VERSION")"
|
|
25
|
-
echo " → 'intelligence
|
|
25
|
+
echo " → 'intelligence init' converts it to the CLI setup"
|
|
26
26
|
exit 0
|
|
27
27
|
;;
|
|
28
28
|
esac
|
|
@@ -31,16 +31,16 @@ echo "project: $IP_ROOT"
|
|
|
31
31
|
manifest="$IP_ROOT/intelligence.yaml"
|
|
32
32
|
lock="$IP_ROOT/intelligence.lock"
|
|
33
33
|
|
|
34
|
-
stamp="$(
|
|
34
|
+
stamp="$(read_schema_version "$manifest")"
|
|
35
35
|
eng="$(bundled_engine_version)"
|
|
36
36
|
if [ -z "$stamp" ]; then
|
|
37
|
-
warn "manifest has no
|
|
37
|
+
warn "manifest has no schema_version — run 'intelligence init'"
|
|
38
38
|
elif _ver_gt "$stamp" "$eng"; then
|
|
39
39
|
warn "manifest schema $stamp is newer than this CLI's engine $eng — update the CLI: npm i -g @ainova-systems/intelligence@latest"
|
|
40
40
|
elif _ver_gt "$eng" "$stamp"; then
|
|
41
|
-
warn "manifest schema $stamp behind engine $eng — run 'intelligence
|
|
41
|
+
warn "manifest schema $stamp behind engine $eng — run 'intelligence init'"
|
|
42
42
|
else
|
|
43
|
-
ok "
|
|
43
|
+
ok "schema_version $stamp matches the engine"
|
|
44
44
|
fi
|
|
45
45
|
|
|
46
46
|
# The engine-content package must sit at the bundled engine version — the
|
|
@@ -50,11 +50,11 @@ if [ -n "$sync_locked" ]; then
|
|
|
50
50
|
if [ "${sync_locked#v}" = "$eng" ]; then
|
|
51
51
|
ok "$SYNC_PKG_NAME at $sync_locked (matches the engine)"
|
|
52
52
|
else
|
|
53
|
-
warn "$SYNC_PKG_NAME locked at $sync_locked but the engine is $eng — run 'intelligence
|
|
53
|
+
warn "$SYNC_PKG_NAME locked at $sync_locked but the engine is $eng — run 'intelligence init'"
|
|
54
54
|
fi
|
|
55
55
|
fi
|
|
56
56
|
if [ -d "$IP_ROOT/.intelligence/engine" ]; then
|
|
57
|
-
warn "stale .intelligence/engine directory (pre-package layout) — run 'intelligence
|
|
57
|
+
warn "stale .intelligence/engine directory (pre-package layout) — run 'intelligence init'"
|
|
58
58
|
fi
|
|
59
59
|
|
|
60
60
|
# Manifest packages: names valid, locked, installed — and the lock's source
|
|
@@ -64,16 +64,34 @@ while IFS= read -r name; do
|
|
|
64
64
|
[ -n "$name" ] || continue
|
|
65
65
|
assert_valid_pkg_name "$name"
|
|
66
66
|
if [ -z "$(qmap_field "$lock" "packages" "$name" "url")" ]; then
|
|
67
|
-
warn "$name is in the manifest but not in intelligence.lock — run 'intelligence
|
|
68
|
-
elif [ ! -d "$IP_ROOT/.intelligence/packages/$name" ]; then
|
|
69
|
-
warn "$name is locked but not installed — run 'intelligence install'"
|
|
67
|
+
warn "$name is in the manifest but not in intelligence.lock — run 'intelligence init'"
|
|
70
68
|
else
|
|
69
|
+
m_ver="$(qmap_field "$manifest" "packages" "$name" "version")"
|
|
70
|
+
m_ref="$(qmap_field "$manifest" "packages" "$name" "ref")"
|
|
71
|
+
m_url="$(qmap_field "$manifest" "packages" "$name" "url")"
|
|
72
|
+
m_path="$(qmap_field "$manifest" "packages" "$name" "path")"
|
|
73
|
+
l_req="$(qmap_field "$lock" "packages" "$name" "requested")"
|
|
74
|
+
l_res="$(qmap_field "$lock" "packages" "$name" "resolved")"
|
|
75
|
+
l_url="$(qmap_field "$lock" "packages" "$name" "url")"
|
|
76
|
+
l_path="$(qmap_field "$lock" "packages" "$name" "path")"
|
|
77
|
+
[ -z "$m_ver" ] || [ "$m_ver" = "$l_req" ] \
|
|
78
|
+
|| warn "$name requests '$m_ver' but the lock recorded '$l_req' — run 'intelligence update --preview'"
|
|
79
|
+
[ -z "$m_ref" ] || [ "$m_ref" = "$l_res" ] \
|
|
80
|
+
|| warn "$name pins ref '$m_ref' but the lock resolved '$l_res'"
|
|
81
|
+
[ -z "$m_url" ] || [ "$m_url" = "$l_url" ] \
|
|
82
|
+
|| warn "$name declares url '$m_url' but the lock recorded '$l_url'"
|
|
83
|
+
[ -z "$m_path" ] || [ "$m_path" = "$l_path" ] \
|
|
84
|
+
|| warn "$name declares path '$m_path' but the lock recorded '$l_path'"
|
|
85
|
+
if [ ! -d "$IP_ROOT/.intelligence/packages/$name" ]; then
|
|
86
|
+
warn "$name is locked but not installed — run 'intelligence sync'"
|
|
87
|
+
continue
|
|
88
|
+
fi
|
|
71
89
|
ok "$name @ $(qmap_field "$lock" "packages" "$name" "resolved")"
|
|
72
90
|
if [ -z "$(qmap_field "$manifest" "packages" "$name" "url")" ]; then
|
|
73
91
|
locked_url="$(qmap_field "$lock" "packages" "$name" "url")"
|
|
74
92
|
resolve_package_source "$manifest" "$name" 2>/dev/null || true
|
|
75
93
|
if [ -n "${RES_URL:-}" ] && [ -n "$locked_url" ] && [ "$RES_URL" != "$locked_url" ]; then
|
|
76
|
-
warn "$name now resolves to $RES_URL but the lock pins $locked_url — a registry re-pointed the name; verify
|
|
94
|
+
warn "$name now resolves to $RES_URL but the lock pins $locked_url — a registry re-pointed the name; verify with 'intelligence update --preview'"
|
|
77
95
|
fi
|
|
78
96
|
fi
|
|
79
97
|
fi
|
|
@@ -87,7 +105,7 @@ if [ -f "$lock" ]; then
|
|
|
87
105
|
while IFS= read -r m; do
|
|
88
106
|
[ "$m" = "$name" ] && found=1
|
|
89
107
|
done < <(qmap_keys "$manifest" "packages")
|
|
90
|
-
[ "$found" -eq 1 ] || warn "$name is locked but absent from the manifest — 'intelligence remove $name' cleans it up"
|
|
108
|
+
[ "$found" -eq 1 ] || warn "$name is locked but absent from the manifest — 'intelligence package remove $name' cleans it up"
|
|
91
109
|
done < <(qmap_keys "$lock" "packages")
|
|
92
110
|
fi
|
|
93
111
|
|
|
@@ -102,7 +120,7 @@ for section in rules agents skills; do
|
|
|
102
120
|
if [ ! -d "$IP_ROOT/$src" ]; then
|
|
103
121
|
case "$src" in
|
|
104
122
|
# Store content is restorable state: absent means un-installed.
|
|
105
|
-
.intelligence/*) warn "sources.$section '$src' missing — run 'intelligence
|
|
123
|
+
.intelligence/*) warn "sources.$section '$src' missing — run 'intelligence sync'" ;;
|
|
106
124
|
# A project-owned dir that does not exist yet is a legitimate
|
|
107
125
|
# state — a project consuming only packages authors nothing of
|
|
108
126
|
# its own — and the engine simply skips it. Declared up front so
|
|
@@ -1,5 +1,5 @@
|
|
|
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
|
#
|
|
@@ -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,15 +32,22 @@ 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
|
|
|
@@ -50,9 +57,60 @@ get_pack_field() {
|
|
|
50
57
|
get_nested_yaml_value "$1" "packs" "$2" "$3"
|
|
51
58
|
}
|
|
52
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
|
+
|
|
53
108
|
# ---- Stage ----------------------------------------------------------------
|
|
54
|
-
|
|
55
|
-
|
|
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
|
|
56
114
|
cleanup_stage() { rm -rf "$stage"; }
|
|
57
115
|
trap cleanup_stage EXIT INT TERM
|
|
58
116
|
mkdir -p "$stage/.intelligence/packages"
|
|
@@ -67,7 +125,7 @@ if [ -z "$stamp" ] || _ver_gt "$V1_FINAL_SCHEMA" "$stamp"; then
|
|
|
67
125
|
echo "ERROR: project schema is ${stamp:-absent (pre-0.3.1)}, older than the final v1 schema $V1_FINAL_SCHEMA." >&2
|
|
68
126
|
echo " Bring it forward with its own engine first — that still works:" >&2
|
|
69
127
|
echo " bash $module_rel/scripts/update.sh --yes" >&2
|
|
70
|
-
echo " then re-run: intelligence
|
|
128
|
+
echo " then re-run: intelligence init" >&2
|
|
71
129
|
exit 1
|
|
72
130
|
fi
|
|
73
131
|
|
|
@@ -81,12 +139,21 @@ sync_pkg_sha="$(fetch_package "$SYNC_PKG_URL" "v$eng" "$SYNC_PKG_PATH" "$stage/$
|
|
|
81
139
|
pack_rows="$stage/packs.rows"
|
|
82
140
|
: > "$pack_rows"
|
|
83
141
|
seen_names=" "
|
|
142
|
+
seen_mirrors=" "
|
|
84
143
|
while IFS= read -r pack; do
|
|
85
144
|
[ -n "$pack" ] || continue
|
|
86
145
|
url="$(get_pack_field "$config" "$pack" "url")"
|
|
87
146
|
ref="$(get_pack_field "$config" "$pack" "ref")"
|
|
88
147
|
mirror="$(get_pack_field "$config" "$pack" "mirror")"
|
|
89
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
|
|
90
157
|
base="${url%.git}"; base="${base%/}"
|
|
91
158
|
repo="$(basename "$base")"
|
|
92
159
|
org="$(basename "$(dirname "$base")")"
|
|
@@ -125,7 +192,7 @@ while IFS="$LOCK_SEP" read -r pack name url ref mirror; do
|
|
|
125
192
|
echo " pack '$pack' -> $name (fetched)"
|
|
126
193
|
fi
|
|
127
194
|
if [ -z "$sha" ] || [ "$sha" = "unknown" ]; then
|
|
128
|
-
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}')"
|
|
129
196
|
fi
|
|
130
197
|
printf '%s\037%s\037%s\037%s\037%s\037%s\n' \
|
|
131
198
|
"$name" "" "$url" "" "$ref" "$sha" >> "$lock_rows"
|
|
@@ -192,7 +259,7 @@ while IFS= read -r line || [ -n "$line" ]; do
|
|
|
192
259
|
fi
|
|
193
260
|
echo "$line"
|
|
194
261
|
done < "$config" > "$manifest_stage"
|
|
195
|
-
|
|
262
|
+
stamp_schema_version "$manifest_stage" "$eng"
|
|
196
263
|
|
|
197
264
|
# packages: entries (pin preserved — no invented ranges).
|
|
198
265
|
while IFS="$LOCK_SEP" read -r pack name url ref mirror; do
|
|
@@ -221,14 +288,24 @@ for section in rules agents skills; do
|
|
|
221
288
|
esac
|
|
222
289
|
done < <(read_yaml_list "$manifest_stage" "$section")
|
|
223
290
|
done
|
|
224
|
-
|
|
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
|
|
225
302
|
old_e="$(is_target_enabled "$config" "$adapter")"
|
|
226
303
|
new_e="$(is_target_enabled "$manifest_stage" "$adapter")"
|
|
227
304
|
[ "$old_e" = "$new_e" ] || { echo " TARGET DRIFT: $adapter enabled '$old_e' -> '$new_e'"; fail=1; }
|
|
228
305
|
old_o="$(get_target_output "$config" "$adapter")"
|
|
229
306
|
new_o="$(get_target_output "$manifest_stage" "$adapter")"
|
|
230
307
|
[ "$old_o" = "$new_o" ] || { echo " TARGET DRIFT: $adapter output '$old_o' -> '$new_o'"; fail=1; }
|
|
231
|
-
done
|
|
308
|
+
done < <({ target_keys "$config"; target_keys "$manifest_stage"; } | sort -u)
|
|
232
309
|
[ "$fail" -eq 0 ] || die "staged state failed verification — nothing changed"
|
|
233
310
|
|
|
234
311
|
if [ "$dry_run" -eq 1 ]; then
|
|
@@ -250,40 +327,51 @@ if [ -f "$root/.gitignore" ]; then
|
|
|
250
327
|
gitignore_existed=1
|
|
251
328
|
cp "$root/.gitignore" "$stage/gitignore.orig"
|
|
252
329
|
fi
|
|
253
|
-
|
|
254
|
-
{
|
|
255
|
-
[ -f "$root/.gitignore" ] && [ -n "$(tail -c 1 "$root/.gitignore" 2>/dev/null)" ] && echo ""
|
|
256
|
-
echo "# intelligence CLI package store (restored by 'intelligence install')"
|
|
257
|
-
echo ".intelligence/"
|
|
258
|
-
} >> "$root/.gitignore"
|
|
259
|
-
fi
|
|
330
|
+
commit_active=1
|
|
260
331
|
rollback() {
|
|
332
|
+
[ "$commit_active" -eq 1 ] || return 0
|
|
333
|
+
commit_active=0
|
|
334
|
+
set +e
|
|
261
335
|
rm -rf "$root/.intelligence" "$root/intelligence.yaml" "$root/intelligence.lock"
|
|
262
336
|
if [ "$gitignore_existed" -eq 1 ]; then
|
|
263
337
|
cp "$stage/gitignore.orig" "$root/.gitignore"
|
|
264
338
|
else
|
|
265
339
|
rm -f "$root/.gitignore"
|
|
266
340
|
fi
|
|
341
|
+
rm -rf "$stage"
|
|
267
342
|
echo "rolled back — the vendored setup is untouched" >&2
|
|
268
343
|
}
|
|
269
|
-
|
|
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
|
|
270
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
|
|
271
360
|
mv "$stage/.intelligence" "$root/.intelligence"
|
|
272
361
|
cp "$manifest_stage" "$root/intelligence.yaml"
|
|
273
362
|
lock_write_from_tsv "$root/intelligence.lock" "$lock_rows"
|
|
274
363
|
|
|
275
364
|
sync_out="$(cd "$root" && IS_SUPPRESS_CLI_NOTE=1 bash "$CLI_DIR/commands/sync.sh" 2>&1)" || {
|
|
276
365
|
echo "$sync_out"
|
|
277
|
-
|
|
278
|
-
die "sync of the migrated state failed — rolled back"
|
|
366
|
+
die "sync of the migrated state failed"
|
|
279
367
|
}
|
|
280
368
|
echo "$sync_out" | grep -q '^IS_STATUS=ok' || {
|
|
281
369
|
echo "$sync_out"
|
|
282
|
-
|
|
283
|
-
die "sync of the migrated state did not report ok — rolled back"
|
|
370
|
+
die "sync of the migrated state did not report ok"
|
|
284
371
|
}
|
|
285
372
|
# Point of no return: the new state is verified. From here an interrupt must
|
|
286
373
|
# NOT roll back (the old setup is being removed) — only clean the stage.
|
|
374
|
+
commit_active=0
|
|
287
375
|
trap cleanup_stage EXIT INT TERM
|
|
288
376
|
|
|
289
377
|
# Only now is anything of the old setup removed — and config.yaml is kept as
|
|
@@ -298,7 +386,7 @@ while IFS="$LOCK_SEP" read -r pack name url ref mirror; do
|
|
|
298
386
|
esac
|
|
299
387
|
if [ -d "$root/$mirror" ]; then
|
|
300
388
|
rm -rf "${root:?}/$mirror"
|
|
301
|
-
# An emptied mirror parent (
|
|
389
|
+
# An emptied v1 mirror parent (for example intelligence/external/) goes with it.
|
|
302
390
|
rmdir "$(dirname "$root/$mirror")" 2>/dev/null || true
|
|
303
391
|
fi
|
|
304
392
|
done < "$pack_rows"
|
|
@@ -310,4 +398,5 @@ rmdir "$umbrella" 2>/dev/null || true
|
|
|
310
398
|
|
|
311
399
|
echo ""
|
|
312
400
|
echo "migrated. Review the diff, then commit. The old config.yaml is kept at .intelligence/backup/config.yaml."
|
|
313
|
-
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,5 +1,5 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
#
|
|
2
|
+
# Internal package add operation.
|
|
3
3
|
#
|
|
4
4
|
# Specs:
|
|
5
5
|
# @scope/name[@range] resolved ONLY through the manifest's trusted registries
|
|
@@ -9,7 +9,7 @@ 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
|
|
@@ -85,7 +85,7 @@ if [ "$mode" = "registry" ]; then
|
|
|
85
85
|
echo " ('intelligence registry list' shows them). Either add its registry:" >&2
|
|
86
86
|
echo " intelligence registry add <registry-repo-url>" >&2
|
|
87
87
|
echo " or install from an explicit source you name yourself:" >&2
|
|
88
|
-
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
|
|
89
89
|
suggest_similar "$manifest" "$name"
|
|
90
90
|
exit 1
|
|
91
91
|
fi
|
|
@@ -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
|