@ainova-systems/intelligence 0.17.2 → 0.17.3
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/cli/internal/check.sh +14 -1
- package/cli/internal/convert-legacy.sh +5 -0
- package/cli/internal/package-add.sh +5 -0
- package/cli/internal/package-remove.sh +1 -0
- package/cli/internal/package-update.sh +2 -0
- package/cli/internal/restore.sh +10 -5
- package/cli/lib/cli-common.sh +72 -0
- package/engine/ENGINE_SHA +1 -1
- package/engine/VERSION +1 -1
- package/package.json +1 -1
- package/packages/sync/references/conventions.md +2 -2
package/cli/internal/check.sh
CHANGED
|
@@ -102,7 +102,20 @@ while IFS= read -r name; do
|
|
|
102
102
|
warn "$name is locked but not installed — run 'intelligence sync'"
|
|
103
103
|
continue
|
|
104
104
|
fi
|
|
105
|
-
|
|
105
|
+
# Report what the store holds, never what the lock pins: they differ
|
|
106
|
+
# after a pull that moved the lock and left the ignored store behind.
|
|
107
|
+
l_url="$(qmap_field "$lock" "packages" "$name" "url")"
|
|
108
|
+
l_path="$(qmap_field "$lock" "packages" "$name" "path")"
|
|
109
|
+
l_sha="$(qmap_field "$lock" "packages" "$name" "sha")"
|
|
110
|
+
if ! store_record_get "$IP_ROOT" "$name"; then
|
|
111
|
+
warn "$name is installed, but the store does not record which commit it holds; the lock pins $(pin_label "$m_ref" "$l_res" "$l_sha") — run 'intelligence sync'"
|
|
112
|
+
elif [ "$IS_REC_URL" != "$l_url" ] || [ "$IS_REC_PATH" != "$l_path" ]; then
|
|
113
|
+
warn "$name was installed from ${IS_REC_URL}${IS_REC_PATH:+ (path $IS_REC_PATH)} but the lock names ${l_url}${l_path:+ (path $l_path)} — run 'intelligence sync'"
|
|
114
|
+
elif [ "$IS_REC_RESOLVED" != "$l_res" ] || [ "$IS_REC_SHA" != "$l_sha" ]; then
|
|
115
|
+
warn "$name holds $IS_REC_RESOLVED@$(short_sha "$IS_REC_SHA") but the lock pins $l_res@$(short_sha "$l_sha") — run 'intelligence sync'"
|
|
116
|
+
else
|
|
117
|
+
ok "$name @ $(pin_label "$m_ref" "$IS_REC_RESOLVED" "$IS_REC_SHA")"
|
|
118
|
+
fi
|
|
106
119
|
fi
|
|
107
120
|
done < <(qmap_keys "$manifest" "packages")
|
|
108
121
|
|
|
@@ -406,6 +406,11 @@ trap 'rollback; exit 130' INT TERM
|
|
|
406
406
|
mv "$stage/.intelligence" "$root/.intelligence"
|
|
407
407
|
cp "$manifest_stage" "$root/intelligence.yaml"
|
|
408
408
|
lock_write_from_tsv "$root/intelligence.lock" "$lock_rows"
|
|
409
|
+
# The staged store holds exactly these rows; record them so sync trusts it.
|
|
410
|
+
while IFS="$LOCK_SEP" read -r rec_name _ rec_url rec_path rec_resolved rec_sha; do
|
|
411
|
+
[ -n "$rec_name" ] || continue
|
|
412
|
+
store_record_set "$root" "$rec_name" "$rec_url" "$rec_path" "$rec_resolved" "$rec_sha"
|
|
413
|
+
done < "$lock_rows"
|
|
409
414
|
ensure_manifest_gitignore "$root" "$root/intelligence.yaml"
|
|
410
415
|
ensure_manifest_publisher_ignores "$root" "$root/intelligence.yaml"
|
|
411
416
|
|
|
@@ -123,6 +123,10 @@ if [ -z "$ref" ]; then
|
|
|
123
123
|
fi
|
|
124
124
|
|
|
125
125
|
rel=".intelligence/packages/$name"
|
|
126
|
+
# Forget what the directory held before replacing it: an interruption then
|
|
127
|
+
# leaves the package unrecorded, which the next sync repairs, never a stale
|
|
128
|
+
# record vouching for new bytes.
|
|
129
|
+
store_record_remove "$IP_ROOT" "$name"
|
|
126
130
|
sha="$(fetch_package "$url" "${ref:-$resolved_tag}" "$path" "$IP_ROOT/$rel")"
|
|
127
131
|
|
|
128
132
|
wire_package_sources "$manifest" "$name" "$rel" "$IP_ROOT"
|
|
@@ -140,6 +144,7 @@ else
|
|
|
140
144
|
fi
|
|
141
145
|
|
|
142
146
|
lock_upsert "$lock" "$name" "$requested" "$url" "$path" "${ref:-$resolved_tag}" "$sha"
|
|
147
|
+
store_record_set "$IP_ROOT" "$name" "$url" "$path" "${ref:-$resolved_tag}" "$sha"
|
|
143
148
|
|
|
144
149
|
sections=""
|
|
145
150
|
for s in rules agents skills; do
|
|
@@ -43,6 +43,7 @@ unwire_package_sources "$manifest" "$rel"
|
|
|
43
43
|
qmap_delete_key "$manifest" "packages" "$name"
|
|
44
44
|
lock_remove "$lock" "$name"
|
|
45
45
|
rm -rf "${IP_ROOT:?}/$rel"
|
|
46
|
+
store_record_remove "$IP_ROOT" "$name"
|
|
46
47
|
# Drop the now-empty scope dir so the store stays tidy.
|
|
47
48
|
rmdir "$IP_ROOT/.intelligence/packages/${name%%/*}" 2>/dev/null || true
|
|
48
49
|
echo "- $name"
|
|
@@ -174,11 +174,13 @@ while IFS= read -r name; do
|
|
|
174
174
|
rm -rf "$staging"
|
|
175
175
|
sha="$(fetch_package "$url" "$tag" "$path" "$staging")"
|
|
176
176
|
unwire_package_sources "$manifest" "$rel"
|
|
177
|
+
store_record_remove "$IP_ROOT" "$name"
|
|
177
178
|
rm -rf "${IP_ROOT:?}/$rel"
|
|
178
179
|
mkdir -p "$(dirname "$IP_ROOT/$rel")"
|
|
179
180
|
mv "$staging" "$IP_ROOT/$rel"
|
|
180
181
|
wire_package_sources "$manifest" "$name" "$rel" "$IP_ROOT"
|
|
181
182
|
lock_upsert "$lock" "$name" "$requested" "$url" "$path" "$tag" "$sha"
|
|
183
|
+
store_record_set "$IP_ROOT" "$name" "$url" "$path" "$tag" "$sha"
|
|
182
184
|
# The widened intent is recorded only once its content is installed and
|
|
183
185
|
# wired: a manifest saying ^0.6.1 over a failed fetch would describe a
|
|
184
186
|
# state the project never reached.
|
package/cli/internal/restore.sh
CHANGED
|
@@ -82,14 +82,16 @@ if [ -n "$missing" ]; then
|
|
|
82
82
|
read -r resolved_tag _ <<< "$(remote_tag_for_version "$url" "$picked")"
|
|
83
83
|
fi
|
|
84
84
|
rel=".intelligence/packages/$name"
|
|
85
|
+
store_record_remove "$IP_ROOT" "$name"
|
|
85
86
|
sha="$(fetch_package "$url" "${ref:-$resolved_tag}" "$path" "$IP_ROOT/$rel")"
|
|
86
87
|
wire_package_sources "$manifest" "$name" "$rel" "$IP_ROOT"
|
|
87
88
|
lock_upsert "$lock" "$name" "$range" "$url" "$path" "${ref:-$resolved_tag}" "$sha"
|
|
89
|
+
store_record_set "$IP_ROOT" "$name" "$url" "$path" "${ref:-$resolved_tag}" "$sha"
|
|
88
90
|
echo "+ $name@${resolved_tag:-${ref:-HEAD}} (resolved)"
|
|
89
91
|
done
|
|
90
92
|
fi
|
|
91
93
|
|
|
92
|
-
# Restore every locked package
|
|
94
|
+
# Restore every locked package the store does not hold at its locked commit.
|
|
93
95
|
if [ -f "$lock" ]; then
|
|
94
96
|
while IFS="$LOCK_SEP" read -r name _requested url path resolved sha; do
|
|
95
97
|
[ -n "$name" ] || continue
|
|
@@ -97,10 +99,11 @@ if [ -f "$lock" ]; then
|
|
|
97
99
|
# on their way into rm -rf paths, and its urls into git argv.
|
|
98
100
|
assert_valid_pkg_name "$name"
|
|
99
101
|
rel=".intelligence/packages/$name"
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
|
|
102
|
+
# A directory counts only with a record of the lock row it satisfies:
|
|
103
|
+
# one present without it may hold any commit. --frozen never trusts
|
|
104
|
+
# what is already on disk: a pre-existing (possibly substituted) store
|
|
105
|
+
# dir would otherwise be synced with no sha verification at all.
|
|
106
|
+
if [ -d "$IP_ROOT/$rel" ] && [ "$force" -eq 0 ] && [ "$frozen" -eq 0 ] && store_record_matches "$IP_ROOT" "$name" "$url" "$path" "$resolved" "$sha"; then
|
|
104
107
|
continue
|
|
105
108
|
fi
|
|
106
109
|
# Fetch into staging and commit to the store only after the integrity
|
|
@@ -109,9 +112,11 @@ if [ -f "$lock" ]; then
|
|
|
109
112
|
staging="$IP_ROOT/.intelligence/.staging-$$"
|
|
110
113
|
rm -rf "$staging"
|
|
111
114
|
fetch_package "$url" "$resolved" "$path" "$staging" "$sha" >/dev/null
|
|
115
|
+
store_record_remove "$IP_ROOT" "$name"
|
|
112
116
|
rm -rf "${IP_ROOT:?}/$rel"
|
|
113
117
|
mkdir -p "$(dirname "$IP_ROOT/$rel")"
|
|
114
118
|
mv "$staging" "$IP_ROOT/$rel"
|
|
119
|
+
store_record_set "$IP_ROOT" "$name" "$url" "$path" "$resolved" "$sha"
|
|
115
120
|
wire_package_sources "$manifest" "$name" "$rel" "$IP_ROOT"
|
|
116
121
|
echo "= $name@${resolved:-HEAD}"
|
|
117
122
|
done < <(lock_to_tsv "$lock")
|
package/cli/lib/cli-common.sh
CHANGED
|
@@ -303,9 +303,11 @@ sync_pkg_entry() {
|
|
|
303
303
|
sync_pkg_install() {
|
|
304
304
|
local root="$1" ver sha
|
|
305
305
|
ver="$(bundled_engine_version)"
|
|
306
|
+
store_record_remove "$root" "$SYNC_PKG_NAME"
|
|
306
307
|
sha="$(fetch_package "$SYNC_PKG_URL" "v$ver" "$SYNC_PKG_PATH" "$root/$SYNC_PKG_STORE")"
|
|
307
308
|
wire_package_sources "$root/intelligence.yaml" "$SYNC_PKG_NAME" "$SYNC_PKG_STORE" "$root"
|
|
308
309
|
lock_upsert "$root/intelligence.lock" "$SYNC_PKG_NAME" "$ver" "$SYNC_PKG_URL" "$SYNC_PKG_PATH" "v$ver" "$sha"
|
|
310
|
+
store_record_set "$root" "$SYNC_PKG_NAME" "$SYNC_PKG_URL" "$SYNC_PKG_PATH" "v$ver" "$sha"
|
|
309
311
|
echo " engine content installed: $SYNC_PKG_STORE (v$ver)"
|
|
310
312
|
}
|
|
311
313
|
|
|
@@ -477,6 +479,65 @@ check_project_lock() {
|
|
|
477
479
|
validate_project_lock "$1" "$mode"
|
|
478
480
|
}
|
|
479
481
|
|
|
482
|
+
# --- What the store holds (decision 0012) ---------------------------------
|
|
483
|
+
# The store keeps plain files only, so which commit a package directory holds is
|
|
484
|
+
# recorded beside the packages, never inside one (the engine would render it):
|
|
485
|
+
# .intelligence/packages/.installed, one row per package — name, url, path,
|
|
486
|
+
# resolved, sha, separated like the lock's rows — written whenever a package is
|
|
487
|
+
# installed from the lock row it now satisfies. A directory without a row, or
|
|
488
|
+
# with one that differs from the lock, is not what the lock pins.
|
|
489
|
+
STORE_RECORD=".intelligence/packages/.installed"
|
|
490
|
+
|
|
491
|
+
# store_record_get <root> <name> — set IS_REC_URL / _PATH / _RESOLVED / _SHA;
|
|
492
|
+
# returns 1 when the store records nothing for <name>.
|
|
493
|
+
store_record_get() {
|
|
494
|
+
local file="$1/$STORE_RECORD" name url path resolved sha
|
|
495
|
+
IS_REC_URL="" IS_REC_PATH="" IS_REC_RESOLVED="" IS_REC_SHA=""
|
|
496
|
+
[ -f "$file" ] || return 1
|
|
497
|
+
while IFS="$LOCK_SEP" read -r name url path resolved sha; do
|
|
498
|
+
[ "$name" = "$2" ] || continue
|
|
499
|
+
IS_REC_URL="$url" IS_REC_PATH="$path" IS_REC_RESOLVED="$resolved" IS_REC_SHA="$sha"
|
|
500
|
+
return 0
|
|
501
|
+
done < "$file"
|
|
502
|
+
return 1
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
# store_record_matches <root> <name> <url> <path> <resolved> <sha>
|
|
506
|
+
store_record_matches() {
|
|
507
|
+
store_record_get "$1" "$2" || return 1
|
|
508
|
+
[ "$IS_REC_URL" = "$3" ] && [ "$IS_REC_PATH" = "$4" ] \
|
|
509
|
+
&& [ "$IS_REC_RESOLVED" = "$5" ] && [ "$IS_REC_SHA" = "$6" ]
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
# store_record_set <root> <name> <url> <path> <resolved> <sha> — record what
|
|
513
|
+
# <name> now holds; store_record_remove <root> <name> forgets it. Both rewrite
|
|
514
|
+
# the file through a temp file, so a reader never sees half a row.
|
|
515
|
+
store_record_set() {
|
|
516
|
+
_store_record_write "$@"
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
store_record_remove() {
|
|
520
|
+
_store_record_write "$1" "$2"
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
_store_record_write() {
|
|
524
|
+
local root="$1" target="$2" file="$1/$STORE_RECORD" tmp name rest
|
|
525
|
+
mkdir -p "$root/.intelligence/packages" || die "cannot create $root/.intelligence/packages"
|
|
526
|
+
tmp="$file.tmp.$$"
|
|
527
|
+
{
|
|
528
|
+
if [ -f "$file" ]; then
|
|
529
|
+
while IFS="$LOCK_SEP" read -r name rest; do
|
|
530
|
+
[ -n "$name" ] && [ "$name" != "$target" ] || continue
|
|
531
|
+
printf '%s%s%s\n' "$name" "$LOCK_SEP" "$rest"
|
|
532
|
+
done < "$file"
|
|
533
|
+
fi
|
|
534
|
+
[ "$#" -lt 6 ] || printf '%s%s%s%s%s%s%s%s%s\n' "$target" "$LOCK_SEP" "$3" "$LOCK_SEP" "$4" "$LOCK_SEP" "$5" "$LOCK_SEP" "$6"
|
|
535
|
+
} > "$tmp" || { rm -f "$tmp"; die "cannot write $file"; }
|
|
536
|
+
mv -f "$tmp" "$file" || { rm -f "$tmp"; die "cannot write $file"; }
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
# project_store_missing <root> — a package the manifest or lock names is absent
|
|
540
|
+
# from the store, or holds something other than its lock row pins.
|
|
480
541
|
project_store_missing() {
|
|
481
542
|
local root="$1" manifest="$1/intelligence.yaml" name src
|
|
482
543
|
while IFS= read -r name; do
|
|
@@ -493,6 +554,17 @@ project_store_missing() {
|
|
|
493
554
|
esac
|
|
494
555
|
done < <(read_yaml_list "$manifest" "$section")
|
|
495
556
|
done
|
|
557
|
+
# Present is not enough: a `git pull` moves the lock and leaves the ignored
|
|
558
|
+
# store on the commit it held before.
|
|
559
|
+
local rows _requested url path resolved sha
|
|
560
|
+
[ -f "$root/intelligence.lock" ] || return 1
|
|
561
|
+
rows="$(lock_to_tsv "$root/intelligence.lock")"
|
|
562
|
+
while IFS="$LOCK_SEP" read -r name _requested url path resolved sha; do
|
|
563
|
+
[ -n "$name" ] || continue
|
|
564
|
+
assert_valid_pkg_name "$name"
|
|
565
|
+
[ -d "$root/.intelligence/packages/$name" ] || return 0
|
|
566
|
+
store_record_matches "$root" "$name" "$url" "$path" "$resolved" "$sha" || return 0
|
|
567
|
+
done <<< "$rows"
|
|
496
568
|
return 1
|
|
497
569
|
}
|
|
498
570
|
|
package/engine/ENGINE_SHA
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
c7851685b484be141067e86ff826a1f16ffe9db6
|
package/engine/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.17.
|
|
1
|
+
0.17.3
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ainova-systems/intelligence",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.3",
|
|
4
4
|
"description": "Build, version and distribute AI agent intelligence across your organization — one CLI, versioned Intelligence Packages, and a sync engine for Claude Code, Cursor, Copilot, Codex, Pi and OpenCode.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"intelligence": "bin/intelligence.js"
|
|
@@ -121,7 +121,7 @@ Practice:
|
|
|
121
121
|
- **`latest` and `*` are for a package you own and release in lockstep.** Anywhere else they hand an upstream author write access to your agents' behavior between two syncs.
|
|
122
122
|
- **Never pin a range to dodge a broken release.** Pin the exact version (`1.4.2`), record why, and remove the pin when the fix ships — a narrowed range hides the reason and outlives the incident.
|
|
123
123
|
|
|
124
|
-
Commit `intelligence.lock`. It records requested versions, source URLs and paths, resolved refs and commit SHAs. After cloning, `intelligence sync` restores
|
|
124
|
+
Commit `intelligence.lock`. It records requested versions, source URLs and paths, resolved refs and commit SHAs. After cloning, or after a pull that moved the lock, `intelligence sync` restores every package the store lacks or holds at another commit, strictly from that lock, before rendering; manifest/lock or SHA drift is refused. Re-run `package add` when deliberately changing a source.
|
|
125
125
|
|
|
126
126
|
`@ainova-systems/sync` is ordinary package content exact-pinned to the bundled engine version. `intelligence init` installs it unless `--bare` is used. Lifecycle preflight keeps that pin and `schema_version` aligned with the installed CLI; package-range updates never move it independently.
|
|
127
127
|
|
|
@@ -416,7 +416,7 @@ The permanent applied-schema key is the top-level scalar `schema_version` in `in
|
|
|
416
416
|
The public lifecycle is deliberately compact:
|
|
417
417
|
|
|
418
418
|
- `intelligence init [--preview|--apply]` is universal: it creates a new setup, aligns an existing Intelligence project, or plans/applies conversion of an eligible legacy Intelligence Sync project.
|
|
419
|
-
- `intelligence sync [adapter] [--compact] [--force]` first aligns an existing Intelligence project with the installed CLI and restores
|
|
419
|
+
- `intelligence sync [adapter] [--compact] [--force]` first aligns an existing Intelligence project with the installed CLI and restores every package the store lacks or holds at another commit, strictly from `intelligence.lock`. It skips rendering when local inputs and outputs match a previous successful run. Changes or missing outputs trigger rendering; `--force` always renders and refreshes the search for unconfigured source directories. An unchanged run replays the last full run's useful diagnostics. Compact mode shows context sizes, actionable warnings and final status on success, and all diagnostics on failure. In CI it refuses an alignment that would change tracked files and points to a local `intelligence init --apply` plus review/commit.
|
|
420
420
|
- `intelligence update [@scope/name] [--preview|--apply]` is the only update surface. It prints the CLI/project/package plan; default mode prompts, `--preview` never writes, and `--apply` does not prompt. It never moves `ref:` pins.
|
|
421
421
|
- `intelligence upgrade [--next] [--preview|--apply]` replaces the installed CLI with the newest version on its npm channel (`next` for a prerelease or with `--next`, otherwise `latest`) with the same modes. It touches no project, never downgrades, and refuses an installation that npm did not make.
|
|
422
422
|
- `intelligence package add|remove|list|search` owns package inventory.
|