@ainova-systems/intelligence 0.18.1 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/commands/init.sh +6 -1
- package/cli/commands/package.sh +8 -3
- package/cli/commands/registry.sh +1 -1
- package/cli/commands/source.sh +56 -17
- package/cli/commands/sync.sh +78 -6
- package/cli/intelligence +16 -3
- package/cli/internal/check.sh +64 -13
- package/cli/internal/convert-legacy.sh +1 -1
- package/cli/internal/package-add.sh +25 -4
- package/cli/internal/package-alias.sh +51 -0
- package/cli/internal/package-list.sh +16 -2
- package/cli/internal/package-remove.sh +3 -1
- package/cli/internal/package-search.sh +1 -1
- package/cli/internal/package-update.sh +14 -6
- package/cli/internal/restore.sh +3 -2
- package/cli/lib/adapter-lifecycle.sh +5 -2
- package/cli/lib/cli-common.sh +149 -34
- package/cli/lib/gitignore.sh +128 -34
- package/cli/lib/lockfile.sh +14 -4
- package/cli/lib/manifest.sh +315 -21
- package/cli/lib/qmap.awk +97 -32
- package/cli/lib/registry.sh +19 -14
- package/cli/lib/sync-cache.sh +596 -142
- package/cli/lib/sync-lock.sh +19 -2
- package/engine/ENGINE_SHA +1 -1
- package/engine/VERSION +1 -1
- package/engine/adapters/_template.sh +6 -2
- package/engine/adapters/agents.sh +8 -2
- package/engine/adapters/antigravity.sh +3 -1
- package/engine/adapters/claude.sh +29 -12
- package/engine/adapters/codex.sh +11 -13
- package/engine/adapters/copilot.sh +54 -4
- package/engine/adapters/cursor.sh +13 -2
- package/engine/adapters/opencode.sh +7 -1
- package/engine/adapters/pi.sh +3 -1
- package/engine/lib/adapter-contract.sh +51 -8
- package/engine/lib/common.sh +1072 -160
- package/engine/lib/contract.sh +5 -0
- package/engine/sync.sh +128 -28
- package/package.json +1 -1
- package/packages/sync/references/adapters.md +44 -9
- package/packages/sync/references/conventions.md +48 -8
- package/packages/sync/references/onboarding-migration.md +3 -3
- package/packages/sync/skills/intelligence-learn-from-repository/SKILL.md +3 -2
- package/packages/sync/skills/intelligence-review-context/SKILL.md +2 -1
- package/packages/sync/skills/intelligence-update-context/SKILL.md +4 -2
- package/packages/sync/skills/intelligence-update-context/references/agents.md +7 -3
- package/packages/sync/skills/intelligence-update-context/references/skills.md +3 -1
|
@@ -9,6 +9,10 @@ lock="$IP_ROOT/intelligence.lock"
|
|
|
9
9
|
lock_valid=1
|
|
10
10
|
if ! check_project_lock "$IP_ROOT"; then lock_valid=0; fi
|
|
11
11
|
|
|
12
|
+
# Aliases come from the parser the engine resolves them with, in one pass; an
|
|
13
|
+
# alias it would not resolve is shown as such, never echoed.
|
|
14
|
+
aliases="$(read_package_aliases "$manifest")"
|
|
15
|
+
|
|
12
16
|
count=0
|
|
13
17
|
while IFS= read -r name; do
|
|
14
18
|
[ -n "$name" ] || continue
|
|
@@ -29,11 +33,21 @@ while IFS= read -r name; do
|
|
|
29
33
|
else
|
|
30
34
|
state="store missing — run 'intelligence sync'"
|
|
31
35
|
fi
|
|
32
|
-
|
|
36
|
+
alias_note=""
|
|
37
|
+
while IFS=$'\037' read -r a_name a_alias a_verdict; do
|
|
38
|
+
[ "$a_name" = "$name" ] || continue
|
|
39
|
+
case "$a_verdict" in
|
|
40
|
+
ok) alias_note=" alias:$a_alias" ;;
|
|
41
|
+
ambiguous) alias_note=" alias:$a_alias (another package declares it too)" ;;
|
|
42
|
+
*) alias_note=" alias:invalid — intelligence status --check" ;;
|
|
43
|
+
esac
|
|
44
|
+
done <<< "$aliases"
|
|
45
|
+
printf '%s %s locked:%s %s%s\n' \
|
|
33
46
|
"$name" \
|
|
34
47
|
"${ref:+ref:$ref}${ref:-${range:-*}}" \
|
|
35
48
|
"$(pin_label "$ref" "$resolved" "$sha")" \
|
|
36
|
-
"$state"
|
|
49
|
+
"$state" \
|
|
50
|
+
"$alias_note"
|
|
37
51
|
done < <(qmap_keys "$manifest" "packages")
|
|
38
52
|
|
|
39
53
|
if [ "$count" -eq 0 ]; then
|
|
@@ -39,7 +39,9 @@ while IFS= read -r k; do
|
|
|
39
39
|
done < <(qmap_keys "$manifest" "packages")
|
|
40
40
|
[ "$known" -eq 1 ] || die "package '$name' is not in the manifest"
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
# Every entry naming the package, in every spelling, goes while `packages:`
|
|
43
|
+
# still declares it and its alias: that is what resolves the references.
|
|
44
|
+
unwire_package_sources "$manifest" "$name"
|
|
43
45
|
qmap_delete_key "$manifest" "packages" "$name"
|
|
44
46
|
lock_remove "$lock" "$name"
|
|
45
47
|
rm -rf "${IP_ROOT:?}/$rel"
|
|
@@ -63,7 +63,7 @@ if [ -n "$manifest" ]; then
|
|
|
63
63
|
[ -n "$url" ] || continue
|
|
64
64
|
index="$(_fetch_index "${url#git+}")"
|
|
65
65
|
if [ -z "$index" ]; then
|
|
66
|
-
echo "
|
|
66
|
+
echo "WARNING: registry unreachable or missing index.yaml: $url" >&2
|
|
67
67
|
continue
|
|
68
68
|
fi
|
|
69
69
|
emit_index "$index" "$url"
|
|
@@ -85,7 +85,7 @@ while IFS= read -r name; do
|
|
|
85
85
|
if [ "$probe_rc" -ne 0 ]; then
|
|
86
86
|
# No answer is not "no change": reporting up to date here would
|
|
87
87
|
# freeze the pin exactly as the ref-name comparison used to.
|
|
88
|
-
echo "
|
|
88
|
+
echo "WARNING: $name: cannot reach $url — ref '$ref' was not checked" >&2
|
|
89
89
|
echo " $name: $ref (not checked — remote unreachable)"
|
|
90
90
|
continue
|
|
91
91
|
fi
|
|
@@ -104,7 +104,7 @@ while IFS= read -r name; do
|
|
|
104
104
|
echo " $name: $(short_sha "$ref") (pinned commit)"
|
|
105
105
|
;;
|
|
106
106
|
*)
|
|
107
|
-
echo "
|
|
107
|
+
echo "WARNING: $name pins ref '$ref', which $url no longer advertises" >&2
|
|
108
108
|
echo " $name: $ref (unresolvable — gone upstream)"
|
|
109
109
|
;;
|
|
110
110
|
esac
|
|
@@ -167,13 +167,21 @@ while IFS= read -r name; do
|
|
|
167
167
|
fi
|
|
168
168
|
rel=".intelligence/packages/$name"
|
|
169
169
|
# Fetch into staging FIRST: a failed clone must leave the current install
|
|
170
|
-
# fully wired and intact. Only after success
|
|
171
|
-
#
|
|
172
|
-
#
|
|
170
|
+
# fully wired and intact. Only after success are the sections the new
|
|
171
|
+
# version dropped unwired, in every spelling, the store swapped, and the
|
|
172
|
+
# sections it gained wired. A section it keeps stays where it stands in
|
|
173
|
+
# the list: position is the override order between packages, and an
|
|
174
|
+
# update changes content, not which package wins.
|
|
173
175
|
staging="$IP_ROOT/.intelligence/.staging-$$"
|
|
174
176
|
rm -rf "$staging"
|
|
175
177
|
sha="$(fetch_package "$url" "$tag" "$path" "$staging")"
|
|
176
|
-
|
|
178
|
+
dropped=()
|
|
179
|
+
for section in rules agents skills; do
|
|
180
|
+
[ -d "$staging/$section" ] || dropped+=("$section")
|
|
181
|
+
done
|
|
182
|
+
if [ "${#dropped[@]}" -gt 0 ]; then
|
|
183
|
+
unwire_package_sources "$manifest" "$name" "${dropped[@]}"
|
|
184
|
+
fi
|
|
177
185
|
store_record_remove "$IP_ROOT" "$name"
|
|
178
186
|
rm -rf "${IP_ROOT:?}/$rel"
|
|
179
187
|
mkdir -p "$(dirname "$IP_ROOT/$rel")"
|
package/cli/internal/restore.sh
CHANGED
|
@@ -7,12 +7,13 @@
|
|
|
7
7
|
set -euo pipefail
|
|
8
8
|
source "$CLI_DIR/lib/cli-common.sh"
|
|
9
9
|
|
|
10
|
-
frozen=0 force=0 no_sync=0
|
|
10
|
+
frozen=0 force=0 no_sync=0 keep_sources=0
|
|
11
11
|
while [ $# -gt 0 ]; do
|
|
12
12
|
case "$1" in
|
|
13
13
|
--frozen) frozen=1 ;;
|
|
14
14
|
--force) force=1 ;;
|
|
15
15
|
--no-sync) no_sync=1 ;;
|
|
16
|
+
--keep-sources) keep_sources=1 ;;
|
|
16
17
|
*) die "unknown option '$1'" ;;
|
|
17
18
|
esac
|
|
18
19
|
shift || true
|
|
@@ -117,7 +118,7 @@ if [ -f "$lock" ]; then
|
|
|
117
118
|
mkdir -p "$(dirname "$IP_ROOT/$rel")"
|
|
118
119
|
mv "$staging" "$IP_ROOT/$rel"
|
|
119
120
|
store_record_set "$IP_ROOT" "$name" "$url" "$path" "$resolved" "$sha"
|
|
120
|
-
wire_package_sources "$manifest" "$name" "$rel" "$IP_ROOT"
|
|
121
|
+
[ "$keep_sources" = 1 ] || wire_package_sources "$manifest" "$name" "$rel" "$IP_ROOT"
|
|
121
122
|
echo "= $name@${resolved:-HEAD}"
|
|
122
123
|
done < <(lock_to_tsv "$lock")
|
|
123
124
|
fi
|
|
@@ -12,13 +12,16 @@ adapter_file_for() {
|
|
|
12
12
|
fi
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
# adapter_records_for <root> <content-dir> <name> <output> [manifest] — pass the
|
|
16
|
+
# manifest whenever the caller applies or checks Git policy; ownership callers
|
|
17
|
+
# may omit it (engine/lib/adapter-contract.sh).
|
|
15
18
|
adapter_records_for() {
|
|
16
|
-
local root="$1" content_dir="$2" name="$3" output="$4" file
|
|
19
|
+
local root="$1" content_dir="$2" name="$3" output="$4" manifest="${5:-}" file
|
|
17
20
|
file="$(adapter_file_for "$root" "$content_dir" "$name")" || {
|
|
18
21
|
echo "ERROR: adapter '$name' not found" >&2
|
|
19
22
|
return 1
|
|
20
23
|
}
|
|
21
|
-
adapter_contract_records "$name" "$file" "$output"
|
|
24
|
+
adapter_contract_records "$name" "$file" "$output" "$manifest"
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
validate_adapter_contract_for() {
|
package/cli/lib/cli-common.sh
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
|
-
# Shared plumbing for every CLI command.
|
|
3
|
-
# dispatcher execs
|
|
2
|
+
# Shared plumbing for every CLI command. Each command is its own script (the
|
|
3
|
+
# dispatcher execs it, or sources `sync`), so each one sources this file first.
|
|
4
4
|
#
|
|
5
5
|
# All YAML reading goes through the engine's lib/common.sh — the CLI never
|
|
6
6
|
# grows a parallel parser for shapes the engine can already read. The only
|
|
7
7
|
# CLI-owned parsing lives in lib/manifest.sh (quoted-key `packages:` /
|
|
8
|
-
# `registries:` blocks, which the engine
|
|
8
|
+
# `registries:` blocks, which the engine never reads beyond the package names
|
|
9
|
+
# and aliases its list parser resolves `sources:` references against).
|
|
9
10
|
|
|
10
11
|
# Engine libraries (readers, is_status, IS_RC_*, engine_version). CLI_DIR /
|
|
11
12
|
# IS_ENGINE_DIR come exported from the dispatcher.
|
|
@@ -98,23 +99,32 @@ source "$CLI_DIR/lib/sync-lock.sh"
|
|
|
98
99
|
# Its identity is DATA shipped with the distribution (cli/engine-package.yaml),
|
|
99
100
|
# never a name compiled into cli code — a fork edits the file.
|
|
100
101
|
_EPKG="$CLI_DIR/engine-package.yaml"
|
|
101
|
-
|
|
102
|
+
_EPKG_FIELDS='name url path default_registry'
|
|
103
|
+
SYNC_PKG_NAME="" SYNC_PKG_URL="" SYNC_PKG_PATH="" SYNC_PKG_STORE=""
|
|
102
104
|
# Read by commands (init seeds it) — per-file shellcheck cannot see that.
|
|
103
105
|
# shellcheck disable=SC2034
|
|
104
106
|
DEFAULT_REGISTRY_URL=""
|
|
105
|
-
|
|
107
|
+
|
|
108
|
+
# load_sync_package_identity — read the four fields in one pass. Every command
|
|
109
|
+
# loads this file first and gets them at once; `sync` sets CLI_DEFER_SYNC_PKG
|
|
110
|
+
# before sourcing and reads them in the same awk pass as the project's manifest
|
|
111
|
+
# and lock (project_reads_preload), once it holds the project lock.
|
|
106
112
|
# shellcheck disable=SC2034 # DEFAULT_REGISTRY_URL is read by commands, as above
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
113
|
+
load_sync_package_identity() {
|
|
114
|
+
local key value
|
|
115
|
+
_qmap_read_var tops "$_EPKG" '' "$_EPKG_FIELDS" || true
|
|
116
|
+
while IFS="$LOCK_SEP" read -r key value; do
|
|
117
|
+
case "$key" in
|
|
118
|
+
name) SYNC_PKG_NAME="$value" ;;
|
|
119
|
+
url) SYNC_PKG_URL="$value" ;;
|
|
120
|
+
path) SYNC_PKG_PATH="$value" ;;
|
|
121
|
+
default_registry) DEFAULT_REGISTRY_URL="$value" ;;
|
|
122
|
+
esac
|
|
123
|
+
done <<< "$IS_QMAP_OUT"
|
|
124
|
+
[ -n "$SYNC_PKG_NAME" ] || die "corrupt CLI installation: $_EPKG is missing or has no 'name'"
|
|
125
|
+
SYNC_PKG_STORE=".intelligence/packages/$SYNC_PKG_NAME"
|
|
126
|
+
}
|
|
127
|
+
[ "${CLI_DEFER_SYNC_PKG:-0}" = 1 ] || load_sync_package_identity
|
|
118
128
|
|
|
119
129
|
# --- Project detection ---------------------------------------------------
|
|
120
130
|
# Sets: IP_MODE (cli|legacy|none), IP_ROOT, IP_UMBRELLA, IP_MODULE_DIR.
|
|
@@ -256,9 +266,14 @@ source_entry_problem() {
|
|
|
256
266
|
}
|
|
257
267
|
|
|
258
268
|
manifest_intelligence_dir() {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
269
|
+
manifest_intelligence_dir_var "$1"
|
|
270
|
+
printf '%s' "$IS_CONTENT_DIR"
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
# manifest_intelligence_dir_var <manifest> — the same answer in IS_CONTENT_DIR.
|
|
274
|
+
manifest_intelligence_dir_var() {
|
|
275
|
+
get_yaml_field_var "$1" "project" "intelligence_dir"
|
|
276
|
+
IS_CONTENT_DIR="${IS_YAML_FIELD:-intelligence}"
|
|
262
277
|
}
|
|
263
278
|
|
|
264
279
|
default_target_output() {
|
|
@@ -316,7 +331,8 @@ sync_pkg_install() {
|
|
|
316
331
|
export_engine_env() {
|
|
317
332
|
local root="$1"
|
|
318
333
|
local content_rel
|
|
319
|
-
|
|
334
|
+
manifest_intelligence_dir_var "$root/intelligence.yaml"
|
|
335
|
+
content_rel="$IS_CONTENT_DIR"
|
|
320
336
|
export IS_CLI=1
|
|
321
337
|
export CONFIG_FILE="$root/intelligence.yaml"
|
|
322
338
|
export REPO_ROOT="$root"
|
|
@@ -381,6 +397,7 @@ project_stamped_ahead() {
|
|
|
381
397
|
|
|
382
398
|
project_needs_upgrade() {
|
|
383
399
|
local root="$1" manifest="$1/intelligence.yaml" stamp eng pinned locked name url path rows
|
|
400
|
+
local lname _requested _url _path resolved _sha
|
|
384
401
|
[ -f "$manifest" ] || return 1
|
|
385
402
|
read_schema_version_var "$manifest"
|
|
386
403
|
stamp="$IS_SCHEMA_VERSION"
|
|
@@ -391,12 +408,14 @@ project_needs_upgrade() {
|
|
|
391
408
|
# restamp the schema and re-pin the engine content DOWNWARD, and the next
|
|
392
409
|
# teammate on the current CLI would move both back: leave it as found.
|
|
393
410
|
project_stamped_ahead "$root" && return 1
|
|
394
|
-
|
|
411
|
+
_qmap_read_var top "$manifest" '' sync_version || true
|
|
412
|
+
[ -z "$IS_QMAP_OUT" ] || return 0
|
|
395
413
|
_ver_gt "$eng" "$stamp" && return 0
|
|
396
414
|
[ -d "$root/.intelligence/engine" ] && return 0
|
|
397
415
|
|
|
398
416
|
# One manifest pass for every package's fields, not a reader per field.
|
|
399
|
-
|
|
417
|
+
_qmap_read_var fieldrows "$manifest" packages '' 'url path version' || true
|
|
418
|
+
rows="$IS_QMAP_OUT"
|
|
400
419
|
while IFS="$LOCK_SEP" read -r name url path pinned; do
|
|
401
420
|
[ -n "$name" ] || continue
|
|
402
421
|
# Early RC manifests mixed requested intent with resolved source
|
|
@@ -404,7 +423,14 @@ project_needs_upgrade() {
|
|
|
404
423
|
[ -n "$url" ] && return 0
|
|
405
424
|
[ -n "$path" ] && return 0
|
|
406
425
|
if [ "$name" = "$SYNC_PKG_NAME" ]; then
|
|
407
|
-
|
|
426
|
+
# The first lock row for the name: what qmap_field would decode.
|
|
427
|
+
locked=""
|
|
428
|
+
_qmap_read_var rows "$root/intelligence.lock" packages || true
|
|
429
|
+
while IFS="$LOCK_SEP" read -r lname _requested _url _path resolved _sha; do
|
|
430
|
+
[ "$lname" = "$name" ] || continue
|
|
431
|
+
locked="$resolved"
|
|
432
|
+
break
|
|
433
|
+
done <<< "$IS_QMAP_OUT"
|
|
408
434
|
[ "$pinned" = "$eng" ] || return 0
|
|
409
435
|
if [ -f "$root/intelligence.lock" ]; then
|
|
410
436
|
[ -n "$locked" ] || return 0
|
|
@@ -419,21 +445,30 @@ project_needs_upgrade() {
|
|
|
419
445
|
# which lifecycle alignment can proceed. Never manufacture a partial lock.
|
|
420
446
|
project_has_packages() {
|
|
421
447
|
local root="$1" name found=1
|
|
448
|
+
_qmap_read_var keys "$root/intelligence.yaml" "packages" || true
|
|
422
449
|
while IFS= read -r name; do
|
|
423
450
|
[ -n "$name" ] || continue
|
|
424
451
|
assert_valid_pkg_name "$name"
|
|
425
452
|
found=0
|
|
426
|
-
done
|
|
453
|
+
done <<< "$IS_QMAP_OUT"
|
|
427
454
|
return "$found"
|
|
428
455
|
}
|
|
429
456
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
457
|
+
# project_preflight <root> — what every project-aware command checks before it
|
|
458
|
+
# may align or write: schema compatibility (a newer major refuses with 4), a
|
|
459
|
+
# valid lock, and a lock for whatever packages the manifest declares.
|
|
460
|
+
project_preflight() {
|
|
461
|
+
local root="$1"
|
|
462
|
+
check_version_compat "$root/intelligence.yaml" || return $?
|
|
433
463
|
validate_project_lock "$root" || return $?
|
|
434
464
|
if project_has_packages "$root" && [ ! -f "$root/intelligence.lock" ]; then
|
|
435
465
|
die "manifest declares packages but intelligence.lock is absent — restore the committed lock before running project lifecycle commands"
|
|
436
466
|
fi
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
ensure_project_current() {
|
|
470
|
+
local root="$1" explicit="${2:-}" manifest="$1/intelligence.yaml" stamp eng
|
|
471
|
+
project_preflight "$root" || return $?
|
|
437
472
|
project_needs_upgrade "$root" || return 0
|
|
438
473
|
read_schema_version_var "$manifest"
|
|
439
474
|
stamp="$IS_SCHEMA_VERSION"
|
|
@@ -443,7 +478,8 @@ ensure_project_current() {
|
|
|
443
478
|
die "project lifecycle requires alignment (stamp ${stamp:-unstamped}, engine $eng) — run 'intelligence init --apply' locally, review and commit the diff"
|
|
444
479
|
fi
|
|
445
480
|
echo " project alignment: stamp ${stamp:-unstamped}, engine $eng"
|
|
446
|
-
bash "$CLI_DIR/internal/align-project.sh" --no-sync
|
|
481
|
+
bash "$CLI_DIR/internal/align-project.sh" --no-sync || return $?
|
|
482
|
+
project_reads_refresh "$root"
|
|
447
483
|
}
|
|
448
484
|
|
|
449
485
|
# A present lock is validated even when there is no missing store to restore.
|
|
@@ -451,7 +487,8 @@ ensure_project_current() {
|
|
|
451
487
|
validate_project_lock() {
|
|
452
488
|
local lock="$1/intelligence.lock" output line rc=0
|
|
453
489
|
if [ -e "$lock" ] || [ -L "$lock" ]; then
|
|
454
|
-
|
|
490
|
+
# The capture is the subshell that contains the guards' `die`.
|
|
491
|
+
output="$(lock_validate_checks "$lock" "${2---metadata}" "$1/intelligence.yaml" 2>&1)" || rc=$?
|
|
455
492
|
while IFS= read -r line; do
|
|
456
493
|
[ -n "$line" ] || continue
|
|
457
494
|
case "$line" in
|
|
@@ -471,6 +508,74 @@ validate_project_lock() {
|
|
|
471
508
|
fi
|
|
472
509
|
}
|
|
473
510
|
|
|
511
|
+
# --- Reads shared by one command's preflight -----------------------------
|
|
512
|
+
# A sync reads the CLI's package descriptor, the manifest and the lock several
|
|
513
|
+
# times on its way to the engine. Once it holds the project lock it answers all
|
|
514
|
+
# of those reads with one qmap pass and one pass of the engine's manifest
|
|
515
|
+
# readers; a child that may rewrite either document (alignment, restore) is
|
|
516
|
+
# followed by project_reads_refresh. Without a preload every reader takes its
|
|
517
|
+
# ordinary path.
|
|
518
|
+
IS_PROJECT_READS_ROOT=""
|
|
519
|
+
|
|
520
|
+
project_reads_preload() {
|
|
521
|
+
local root="$1" manifest="$1/intelligence.yaml" lock="$1/intelligence.lock" s=$'\037'
|
|
522
|
+
local qmap=0 view=0 out qmap_pid="" view_pid=""
|
|
523
|
+
local -a view_args=("$manifest" project intelligence_dir rules agents skills)
|
|
524
|
+
project_reads_reset
|
|
525
|
+
qmap_preload_plan \
|
|
526
|
+
"tops$s$_EPKG$s$s$_EPKG_FIELDS$s$s" \
|
|
527
|
+
"keys$s$manifest${s}packages$s$s$s" \
|
|
528
|
+
"fieldrows$s$manifest${s}packages$s${s}url path version$s" \
|
|
529
|
+
"top$s$manifest$s${s}sync_version$s$s" \
|
|
530
|
+
"lock$s$lock${s}packages$s$s$s$LOCKFILE_VERSION" \
|
|
531
|
+
"rows$s$lock${s}packages$s$s$s" && qmap=1
|
|
532
|
+
[ ! -f "$manifest" ] || [ ! -r "$manifest" ] || view=1
|
|
533
|
+
if project_lock_scratch "$root"; then
|
|
534
|
+
# Both passes at once: starting their processes is most of their cost.
|
|
535
|
+
if [ "$qmap" = 1 ]; then
|
|
536
|
+
qmap_preload_run > "$IS_LOCK_SCRATCH.qmap" &
|
|
537
|
+
qmap_pid=$!
|
|
538
|
+
fi
|
|
539
|
+
if [ "$view" = 1 ]; then
|
|
540
|
+
manifest_view_run "${view_args[@]}" > "$IS_LOCK_SCRATCH.view" &
|
|
541
|
+
view_pid=$!
|
|
542
|
+
fi
|
|
543
|
+
if [ "$qmap" = 1 ] && wait "$qmap_pid"; then
|
|
544
|
+
qmap_preload_store "$(< "$IS_LOCK_SCRATCH.qmap")"
|
|
545
|
+
fi
|
|
546
|
+
if [ "$view" = 1 ]; then
|
|
547
|
+
if wait "$view_pid"; then
|
|
548
|
+
manifest_view_store "$(< "$IS_LOCK_SCRATCH.view")" "${view_args[@]}"
|
|
549
|
+
else
|
|
550
|
+
manifest_view_reset
|
|
551
|
+
fi
|
|
552
|
+
fi
|
|
553
|
+
else
|
|
554
|
+
if [ "$qmap" = 1 ] && out="$(qmap_preload_run)"; then
|
|
555
|
+
qmap_preload_store "$out"
|
|
556
|
+
fi
|
|
557
|
+
[ "$view" = 0 ] || load_manifest_view "${view_args[@]}" || manifest_view_reset
|
|
558
|
+
fi
|
|
559
|
+
IS_PROJECT_READS_ROOT="$root"
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
project_reads_reset() {
|
|
563
|
+
qmap_memo_reset
|
|
564
|
+
manifest_view_reset
|
|
565
|
+
IS_PROJECT_READS_ROOT=""
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
# project_reads_refresh <root> — after a child may have rewritten the manifest
|
|
569
|
+
# or the lock: read them again if this process preloaded them.
|
|
570
|
+
project_reads_refresh() {
|
|
571
|
+
[ "$IS_PROJECT_READS_ROOT" = "$1" ] || return 0
|
|
572
|
+
project_reads_preload "$1"
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
# CLI and engine share one list-cache format. Writers invalidate both the
|
|
576
|
+
# qmap answers and the engine view through qmap_memo_reset.
|
|
577
|
+
_yaml_list_var() { load_yaml_list "$1" "$2"; }
|
|
578
|
+
|
|
474
579
|
# Read-only diagnosis must distinguish usable installed metadata from metadata
|
|
475
580
|
# which could actually restore a missing store.
|
|
476
581
|
check_project_lock() {
|
|
@@ -539,26 +644,31 @@ _store_record_write() {
|
|
|
539
644
|
# project_store_missing <root> — a package the manifest or lock names is absent
|
|
540
645
|
# from the store, or holds something other than its lock row pins.
|
|
541
646
|
project_store_missing() {
|
|
542
|
-
local root="$1" manifest="$1/intelligence.yaml" name src
|
|
647
|
+
local root="$1" manifest="$1/intelligence.yaml" name src section
|
|
648
|
+
_qmap_read_var keys "$manifest" "packages" || true
|
|
543
649
|
while IFS= read -r name; do
|
|
544
650
|
[ -n "$name" ] || continue
|
|
545
651
|
assert_valid_pkg_name "$name"
|
|
546
652
|
[ -d "$root/.intelligence/packages/$name" ] || return 0
|
|
547
|
-
done
|
|
653
|
+
done <<< "$IS_QMAP_OUT"
|
|
654
|
+
# The parser hands a package reference over as the store directory it
|
|
655
|
+
# names, so every spelling is checked here as that directory.
|
|
548
656
|
for section in rules agents skills; do
|
|
657
|
+
_yaml_list_var "$manifest" "$section"
|
|
549
658
|
while IFS= read -r src; do
|
|
550
659
|
case "$src" in
|
|
551
660
|
.intelligence/packages/*)
|
|
552
661
|
[ -d "$root/$src" ] || return 0
|
|
553
662
|
;;
|
|
554
663
|
esac
|
|
555
|
-
done
|
|
664
|
+
done <<< "$IS_YAML_LIST"
|
|
556
665
|
done
|
|
557
666
|
# Present is not enough: a `git pull` moves the lock and leaves the ignored
|
|
558
667
|
# store on the commit it held before.
|
|
559
668
|
local rows _requested url path resolved sha
|
|
560
669
|
[ -f "$root/intelligence.lock" ] || return 1
|
|
561
|
-
rows
|
|
670
|
+
_qmap_read_var rows "$root/intelligence.lock" packages || true
|
|
671
|
+
rows="$IS_QMAP_OUT"
|
|
562
672
|
while IFS="$LOCK_SEP" read -r name _requested url path resolved sha; do
|
|
563
673
|
[ -n "$name" ] || continue
|
|
564
674
|
assert_valid_pkg_name "$name"
|
|
@@ -573,5 +683,10 @@ restore_project_store_if_missing() {
|
|
|
573
683
|
project_store_missing "$root" || return 0
|
|
574
684
|
[ -f "$root/intelligence.lock" ] || die "package store is missing and intelligence.lock is absent — run 'intelligence init'"
|
|
575
685
|
echo " restoring package store from intelligence.lock"
|
|
576
|
-
|
|
686
|
+
if [ "${2:-}" = keep-sources ]; then
|
|
687
|
+
bash "$CLI_DIR/internal/restore.sh" --frozen --no-sync --keep-sources || return $?
|
|
688
|
+
else
|
|
689
|
+
bash "$CLI_DIR/internal/restore.sh" --frozen --no-sync || return $?
|
|
690
|
+
fi
|
|
691
|
+
project_reads_refresh "$root"
|
|
577
692
|
}
|