@ainova-systems/intelligence 0.13.0 → 0.14.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/README.md +1 -1
- package/cli/commands/update.sh +9 -2
- package/cli/intelligence +1 -0
- package/cli/internal/package-update.sh +57 -9
- package/engine/ENGINE_SHA +1 -1
- package/engine/VERSION +1 -1
- package/engine/adapters/agents.sh +7 -12
- package/engine/lib/common.sh +57 -25
- package/package.json +1 -1
- package/packages/sync/references/conventions.md +23 -0
package/README.md
CHANGED
|
@@ -34,7 +34,7 @@ intelligence sync
|
|
|
34
34
|
|---|---|
|
|
35
35
|
| `intelligence init [--preview\|--apply]` | Create, convert, restore or align a project |
|
|
36
36
|
| `intelligence sync [adapter]` | Restore locked content if needed, then render all or one enabled adapter |
|
|
37
|
-
| `intelligence update [@scope/name] [--preview\|--apply]` | Plan or apply project and ranged-package updates |
|
|
37
|
+
| `intelligence update [@scope/name] [--latest] [--preview\|--apply]` | Plan or apply project and ranged-package updates; `--latest` crosses one package's range |
|
|
38
38
|
| `intelligence upgrade [--next] [--preview\|--apply]` | Replace the installed CLI with the newest version on its npm channel |
|
|
39
39
|
| `intelligence package add\|remove\|list\|search` | Manage versioned Intelligence Packages |
|
|
40
40
|
| `intelligence adapter list\|create\|enable\|disable\|remove` | Manage render adapters |
|
package/cli/commands/update.sh
CHANGED
|
@@ -8,17 +8,22 @@
|
|
|
8
8
|
set -euo pipefail
|
|
9
9
|
source "$CLI_DIR/lib/cli-common.sh"
|
|
10
10
|
|
|
11
|
-
only="" mode="ask" preview_seen=0 apply_seen=0
|
|
11
|
+
only="" mode="ask" preview_seen=0 apply_seen=0 latest=0
|
|
12
12
|
while [ $# -gt 0 ]; do
|
|
13
13
|
case "$1" in
|
|
14
14
|
--preview) preview_seen=1; mode="preview" ;;
|
|
15
15
|
--apply) apply_seen=1; mode="apply" ;;
|
|
16
|
+
--latest) latest=1 ;;
|
|
16
17
|
@*) [ -z "$only" ] || die "only one package may be selected"; only="$1" ;;
|
|
17
|
-
*) die "usage: intelligence update [@scope/name] [--preview|--apply]" ;;
|
|
18
|
+
*) die "usage: intelligence update [@scope/name] [--latest] [--preview|--apply]" ;;
|
|
18
19
|
esac
|
|
19
20
|
shift
|
|
20
21
|
done
|
|
21
22
|
[ "$preview_seen" -eq 0 ] || [ "$apply_seen" -eq 0 ] || die "choose either --preview or --apply"
|
|
23
|
+
# Crossing a range is one package's decision, and SemVer marks the boundary as
|
|
24
|
+
# the place a changelog has to be read.
|
|
25
|
+
[ "$latest" -eq 0 ] || [ -n "$only" ] \
|
|
26
|
+
|| die "--latest needs the package to move: intelligence update @scope/name --latest"
|
|
22
27
|
|
|
23
28
|
require_cli_project
|
|
24
29
|
manifest="$IP_ROOT/intelligence.yaml"
|
|
@@ -74,6 +79,7 @@ fi
|
|
|
74
79
|
|
|
75
80
|
pkg_args=(--preview)
|
|
76
81
|
[ -z "$only" ] || pkg_args+=("$only")
|
|
82
|
+
[ "$latest" -eq 0 ] || pkg_args+=(--latest)
|
|
77
83
|
pkg_plan="$(bash "$CLI_DIR/internal/package-update.sh" "${pkg_args[@]}")"
|
|
78
84
|
echo ""
|
|
79
85
|
echo "Packages:"
|
|
@@ -102,5 +108,6 @@ fi
|
|
|
102
108
|
ensure_project_current "$IP_ROOT"
|
|
103
109
|
apply_args=(--no-sync)
|
|
104
110
|
[ -z "$only" ] || apply_args+=("$only")
|
|
111
|
+
[ "$latest" -eq 0 ] || apply_args+=(--latest)
|
|
105
112
|
bash "$CLI_DIR/internal/package-update.sh" "${apply_args[@]}"
|
|
106
113
|
exec bash "$CLI_DIR/commands/sync.sh"
|
package/cli/intelligence
CHANGED
|
@@ -51,6 +51,7 @@ Usage: intelligence <command> [args]
|
|
|
51
51
|
update [@scope/name] Show an update plan; ask before applying it
|
|
52
52
|
--preview Show the plan without asking or writing
|
|
53
53
|
--apply Apply the plan without asking
|
|
54
|
+
--latest Move one named package past its range to the newest version
|
|
54
55
|
upgrade [--next] [--preview|--apply] Replace the installed CLI with the newest npm version
|
|
55
56
|
--next Follow the prerelease line instead of the stable one
|
|
56
57
|
package <command> add | remove | list | search
|
|
@@ -7,16 +7,22 @@
|
|
|
7
7
|
set -euo pipefail
|
|
8
8
|
source "$CLI_DIR/lib/cli-common.sh"
|
|
9
9
|
|
|
10
|
-
only="" no_sync=0 preview=0
|
|
10
|
+
only="" no_sync=0 preview=0 latest=0
|
|
11
11
|
while [ $# -gt 0 ]; do
|
|
12
12
|
case "$1" in
|
|
13
13
|
--no-sync) no_sync=1 ;;
|
|
14
14
|
--preview) preview=1 ;;
|
|
15
|
+
--latest) latest=1 ;;
|
|
15
16
|
@*) only="$1" ;;
|
|
16
17
|
*) die "unknown argument '$1'" ;;
|
|
17
18
|
esac
|
|
18
19
|
shift || true
|
|
19
20
|
done
|
|
21
|
+
# Crossing a range is per package on purpose: each boundary SemVer marks is a
|
|
22
|
+
# changelog to read, so a blanket sweep would be one confirmation for several
|
|
23
|
+
# unrelated decisions.
|
|
24
|
+
[ "$latest" -eq 0 ] || [ -n "$only" ] \
|
|
25
|
+
|| die "--latest needs the package to move: intelligence update @scope/name --latest"
|
|
20
26
|
|
|
21
27
|
require_cli_project
|
|
22
28
|
manifest="$IP_ROOT/intelligence.yaml"
|
|
@@ -36,7 +42,7 @@ move_label() {
|
|
|
36
42
|
fi
|
|
37
43
|
}
|
|
38
44
|
|
|
39
|
-
moved=0 found=0
|
|
45
|
+
moved=0 found=0 outside=0
|
|
40
46
|
while IFS= read -r name; do
|
|
41
47
|
[ -n "$name" ] || continue
|
|
42
48
|
# Manifest keys are untrusted input on their way into store paths.
|
|
@@ -46,6 +52,8 @@ while IFS= read -r name; do
|
|
|
46
52
|
# message followed by "not in the manifest" would contradict itself.
|
|
47
53
|
found=1
|
|
48
54
|
if [ "$name" = "$SYNC_PKG_NAME" ]; then
|
|
55
|
+
[ "$latest" -eq 0 ] \
|
|
56
|
+
|| die "$name is the engine content package — its pin follows the installed CLI; use 'intelligence upgrade'"
|
|
49
57
|
echo " $name: engine content follows the installed CLI — skipped"
|
|
50
58
|
continue
|
|
51
59
|
fi
|
|
@@ -59,7 +67,9 @@ while IFS= read -r name; do
|
|
|
59
67
|
range="$(qmap_field "$manifest" "packages" "$name" "version")"
|
|
60
68
|
[ -n "$ref" ] || [ -n "$range" ] || die "$name has neither version nor ref in the manifest"
|
|
61
69
|
|
|
62
|
-
ref_moved=0 remote_sha=""
|
|
70
|
+
ref_moved=0 remote_sha="" beyond="" range_move=""
|
|
71
|
+
[ "$latest" -eq 0 ] || [ -z "$ref" ] \
|
|
72
|
+
|| die "$name is pinned to ref '$ref', not a version range — a ref pin is frozen by intent; re-add the package to change it"
|
|
63
73
|
if [ -n "$ref" ]; then
|
|
64
74
|
tag="$ref"
|
|
65
75
|
requested=""
|
|
@@ -101,27 +111,56 @@ while IFS= read -r name; do
|
|
|
101
111
|
fi
|
|
102
112
|
[ -z "$remote_sha" ] || [ "$remote_sha" = "$locked_sha" ] || ref_moved=1
|
|
103
113
|
else
|
|
104
|
-
|
|
114
|
+
# One remote read answers both questions: what the range selects, and
|
|
115
|
+
# what it excludes.
|
|
116
|
+
versions="$(list_remote_versions "$url")"
|
|
117
|
+
picked="$(printf '%s\n' "$versions" | semver_pick_highest "$range")"
|
|
105
118
|
[ -n "$picked" ] || { echo " $name: nothing satisfies '$range' at $url" >&2; continue; }
|
|
106
119
|
read -r tag _ <<< "$(remote_tag_for_version "$url" "$picked")"
|
|
107
120
|
requested="$range"
|
|
121
|
+
# A range is a ceiling as much as a floor, and on a 0.x package the
|
|
122
|
+
# caret stops at the minor: a project sits on 0.4.x while 0.6.1 ships
|
|
123
|
+
# and every plan still reads "up to date". Name what the range leaves
|
|
124
|
+
# out. Crossing it edits requested intent, which is a manifest change
|
|
125
|
+
# and never this command's to make.
|
|
126
|
+
newest="$(printf '%s\n' "$versions" | semver_pick_highest "latest")"
|
|
127
|
+
if [ -n "$newest" ] && [ "$(semver_cmp "$newest" "$picked")" = "1" ]; then
|
|
128
|
+
if [ "$latest" -eq 1 ]; then
|
|
129
|
+
# Asked for by name: take the newest and widen the recorded
|
|
130
|
+
# intent to match, keeping the caret so the next boundary is
|
|
131
|
+
# still a decision. The manifest keeps saying what the project
|
|
132
|
+
# asked for — that is what makes the move reviewable.
|
|
133
|
+
picked="$newest"
|
|
134
|
+
read -r tag _ <<< "$(remote_tag_for_version "$url" "$picked")"
|
|
135
|
+
requested="^$newest"
|
|
136
|
+
range_move=" (range $range -> $requested)"
|
|
137
|
+
else
|
|
138
|
+
beyond=" — $newest available outside '$range'"
|
|
139
|
+
beyond="$beyond
|
|
140
|
+
follow it: intelligence update $name --latest"
|
|
141
|
+
outside=$((outside + 1))
|
|
142
|
+
fi
|
|
143
|
+
fi
|
|
144
|
+
# When the newest version is already inside the range there is nothing
|
|
145
|
+
# to cross: `--latest` falls through to the ordinary comparison, which
|
|
146
|
+
# still installs a move the lock is behind on and still widens nothing.
|
|
108
147
|
fi
|
|
109
148
|
if [ "$ref_moved" -eq 0 ] && [ "$tag" = "$current" ] && [ "$requested" != "$locked_requested" ]; then
|
|
110
149
|
if [ "$preview" -eq 1 ]; then
|
|
111
|
-
echo " $name: request ${locked_requested:-<none>} -> ${requested:-<ref>} (keeps $current)"
|
|
150
|
+
echo " $name: request ${locked_requested:-<none>} -> ${requested:-<ref>} (keeps $current)$beyond"
|
|
112
151
|
else
|
|
113
152
|
lock_upsert "$lock" "$name" "$requested" "$url" "$path" "$current" "$locked_sha"
|
|
114
|
-
echo " $name: request ${locked_requested:-<none>} -> ${requested:-<ref>} (kept $current)"
|
|
153
|
+
echo " $name: request ${locked_requested:-<none>} -> ${requested:-<ref>} (kept $current)$beyond"
|
|
115
154
|
fi
|
|
116
155
|
moved=$((moved + 1))
|
|
117
156
|
continue
|
|
118
157
|
fi
|
|
119
158
|
if [ "$ref_moved" -eq 0 ] && [ "$tag" = "$current" ]; then
|
|
120
|
-
echo " $name: $(pin_label "$ref" "$current" "$locked_sha") (up to date)"
|
|
159
|
+
echo " $name: $(pin_label "$ref" "$current" "$locked_sha") (up to date)$beyond"
|
|
121
160
|
continue
|
|
122
161
|
fi
|
|
123
162
|
if [ "$preview" -eq 1 ]; then
|
|
124
|
-
echo " $name: $(move_label "$ref" "$current" "$tag" "$locked_sha" "$remote_sha")"
|
|
163
|
+
echo " $name: $(move_label "$ref" "$current" "$tag" "$locked_sha" "$remote_sha")$range_move$beyond"
|
|
125
164
|
moved=$((moved + 1))
|
|
126
165
|
continue
|
|
127
166
|
fi
|
|
@@ -139,7 +178,11 @@ while IFS= read -r name; do
|
|
|
139
178
|
mv "$staging" "$IP_ROOT/$rel"
|
|
140
179
|
wire_package_sources "$manifest" "$name" "$rel" "$IP_ROOT"
|
|
141
180
|
lock_upsert "$lock" "$name" "$requested" "$url" "$path" "$tag" "$sha"
|
|
142
|
-
|
|
181
|
+
# The widened intent is recorded only once its content is installed and
|
|
182
|
+
# wired: a manifest saying ^0.6.1 over a failed fetch would describe a
|
|
183
|
+
# state the project never reached.
|
|
184
|
+
[ -z "$range_move" ] || qmap_set "$manifest" "packages" "$name" "version" "$requested"
|
|
185
|
+
echo " $name: $(move_label "$ref" "$current" "$tag" "$locked_sha" "$sha")$range_move$beyond"
|
|
143
186
|
moved=$((moved + 1))
|
|
144
187
|
done < <(qmap_keys "$manifest" "packages")
|
|
145
188
|
|
|
@@ -149,6 +192,11 @@ if [ "$preview" -eq 1 ]; then
|
|
|
149
192
|
else
|
|
150
193
|
echo "updated: $moved package(s)"
|
|
151
194
|
fi
|
|
195
|
+
# Counted apart from the movable ones: no mode of this command installs these,
|
|
196
|
+
# so folding them into "updates available" would promise work --apply skips.
|
|
197
|
+
if [ "$outside" -gt 0 ]; then
|
|
198
|
+
echo "outside the requested range: $outside package(s) — read the changelog, then run the 'follow it' command above"
|
|
199
|
+
fi
|
|
152
200
|
|
|
153
201
|
if [ "$preview" -eq 0 ] && [ "$moved" -gt 0 ] && [ "$no_sync" -eq 0 ]; then
|
|
154
202
|
exec bash "$CLI_DIR/commands/sync.sh"
|
package/engine/ENGINE_SHA
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
a9cf448bc3f2944c988ce57581822df081195ca2
|
package/engine/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.
|
|
1
|
+
0.14.0
|
|
@@ -58,11 +58,9 @@ agents_md_append_agents_table() {
|
|
|
58
58
|
local rows=""
|
|
59
59
|
local count=0
|
|
60
60
|
|
|
61
|
-
local f
|
|
62
61
|
local -a files=()
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
done < <(source_artifact_files "$repo_root" "$config_file" "agents")
|
|
62
|
+
read_source_artifact_files "$repo_root" "$config_file" "agents"
|
|
63
|
+
[ "${#IS_SOURCE_FILES[@]}" -eq 0 ] || files=("${IS_SOURCE_FILES[@]}")
|
|
66
64
|
|
|
67
65
|
if [ "${#files[@]}" -gt 0 ]; then
|
|
68
66
|
local path tier access desc name
|
|
@@ -101,11 +99,10 @@ agents_md_append_skills_table() {
|
|
|
101
99
|
local rows=""
|
|
102
100
|
local count=0
|
|
103
101
|
|
|
104
|
-
local
|
|
102
|
+
local dirname
|
|
105
103
|
local -a skill_files=()
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
done < <(source_artifact_files "$repo_root" "$config_file" "skills")
|
|
104
|
+
read_source_artifact_files "$repo_root" "$config_file" "skills"
|
|
105
|
+
[ "${#IS_SOURCE_FILES[@]}" -eq 0 ] || skill_files=("${IS_SOURCE_FILES[@]}")
|
|
109
106
|
|
|
110
107
|
if [ "${#skill_files[@]}" -gt 0 ]; then
|
|
111
108
|
local path desc
|
|
@@ -145,11 +142,9 @@ agents_md_append_rules_list() {
|
|
|
145
142
|
local count=0
|
|
146
143
|
local global_rule_files=()
|
|
147
144
|
|
|
148
|
-
local f
|
|
149
145
|
local -a files=()
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
done < <(source_artifact_files "$repo_root" "$config_file" "rules")
|
|
146
|
+
read_source_artifact_files "$repo_root" "$config_file" "rules"
|
|
147
|
+
[ "${#IS_SOURCE_FILES[@]}" -eq 0 ] || files=("${IS_SOURCE_FILES[@]}")
|
|
153
148
|
|
|
154
149
|
if [ "${#files[@]}" -gt 0 ]; then
|
|
155
150
|
local path hp name scope
|
package/engine/lib/common.sh
CHANGED
|
@@ -643,14 +643,20 @@ sync_open_skill_dirs() {
|
|
|
643
643
|
|
|
644
644
|
local count=0 log="" d skill_file skill_name
|
|
645
645
|
local -a skill_dirs=()
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
646
|
+
read_source_artifact_files "$repo_root" "$config_file" "skills"
|
|
647
|
+
# Guard on the count, then expand quoted: Bash 3.2 (macOS) errors on an
|
|
648
|
+
# empty array under `set -u`, and quoting keeps a path with a space or a
|
|
649
|
+
# glob character intact.
|
|
650
|
+
if [ "${#IS_SOURCE_FILES[@]}" -gt 0 ]; then
|
|
651
|
+
for skill_file in "${IS_SOURCE_FILES[@]}"; do
|
|
652
|
+
[ -n "$skill_file" ] || continue
|
|
653
|
+
d="${skill_file%/SKILL.md}"
|
|
654
|
+
skill_name="${d##*/}"
|
|
655
|
+
skill_dirs+=("$d/")
|
|
656
|
+
count=$((count + 1))
|
|
657
|
+
log+=" skill: $skill_name"$'\n'
|
|
658
|
+
done
|
|
659
|
+
fi
|
|
654
660
|
# copy_skill_bundle_dirs owns the frontmatter-quoting pass, so every
|
|
655
661
|
# target gets it — not just this open-standard dir.
|
|
656
662
|
if [ "$count" -gt 0 ]; then
|
|
@@ -1335,29 +1341,58 @@ warn_unsynced() {
|
|
|
1335
1341
|
|
|
1336
1342
|
# Enumerate one manifest source kind using the same depth and ordering
|
|
1337
1343
|
# everywhere. Source-list order is significant; entries inside each directory
|
|
1338
|
-
# use byte-order sorting for cross-platform determinism.
|
|
1339
|
-
|
|
1344
|
+
# use byte-order sorting for cross-platform determinism. The result lands in
|
|
1345
|
+
# IS_SOURCE_FILES; callers read that array.
|
|
1346
|
+
#
|
|
1347
|
+
# It is deliberately not streamed out of a subshell. This list decides what
|
|
1348
|
+
# every adapter renders, and a truncated one is indistinguishable from a
|
|
1349
|
+
# smaller project: a short list renders an incomplete file and the run still
|
|
1350
|
+
# reports IS_STATUS=ok. Streaming had two ways to truncate silently — a pipe
|
|
1351
|
+
# write cut short (bash's printf surfaces EINTR as `write error: Interrupted
|
|
1352
|
+
# system call` and drops the line) and a process substitution, which discards
|
|
1353
|
+
# its writer's exit status entirely. Assembling in the caller's shell leaves no
|
|
1354
|
+
# writer whose failure can be lost, and the remaining pipeline is checked. An
|
|
1355
|
+
# enumeration that cannot answer must stop the run, never shorten the answer.
|
|
1356
|
+
read_source_artifact_files() {
|
|
1340
1357
|
local repo_root="$1"
|
|
1341
1358
|
local config_file="$2"
|
|
1342
1359
|
local section="$3"
|
|
1343
|
-
local src f dir
|
|
1360
|
+
local src f dir listing
|
|
1344
1361
|
|
|
1362
|
+
IS_SOURCE_FILES=()
|
|
1345
1363
|
load_yaml_list "$config_file" "$section"
|
|
1346
1364
|
while IFS= read -r src; do
|
|
1347
1365
|
[ -n "$src" ] || continue
|
|
1348
1366
|
dir="$repo_root/$src"
|
|
1349
1367
|
[ -d "$dir" ] || continue
|
|
1368
|
+
# Command substitution, so the pipeline's status is the assignment's:
|
|
1369
|
+
# `find` or `sort` failing aborts here instead of yielding a short list.
|
|
1350
1370
|
case "$section" in
|
|
1351
1371
|
rules|agents)
|
|
1352
|
-
find "$dir" -maxdepth 1 -type f -name '*.md' -print | LC_ALL=C sort
|
|
1372
|
+
listing="$(find "$dir" -maxdepth 1 -type f -name '*.md' -print | LC_ALL=C sort)" || {
|
|
1373
|
+
echo "ERROR: cannot enumerate $section under '$dir' — refusing to render a partial list." >&2
|
|
1374
|
+
exit 1
|
|
1375
|
+
}
|
|
1353
1376
|
;;
|
|
1354
1377
|
skills)
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1378
|
+
listing="$(find "$dir" -mindepth 1 -maxdepth 1 -type d -print | LC_ALL=C sort)" || {
|
|
1379
|
+
echo "ERROR: cannot enumerate $section under '$dir' — refusing to render a partial list." >&2
|
|
1380
|
+
exit 1
|
|
1381
|
+
}
|
|
1359
1382
|
;;
|
|
1383
|
+
*) continue ;;
|
|
1360
1384
|
esac
|
|
1385
|
+
[ -n "$listing" ] || continue
|
|
1386
|
+
# A here-string redirect keeps this loop in the current shell, so the
|
|
1387
|
+
# array it fills survives and any failure inside it is this shell's.
|
|
1388
|
+
while IFS= read -r f; do
|
|
1389
|
+
[ -n "$f" ] || continue
|
|
1390
|
+
if [ "$section" = "skills" ]; then
|
|
1391
|
+
[ -f "$f/SKILL.md" ] || continue
|
|
1392
|
+
f="$f/SKILL.md"
|
|
1393
|
+
fi
|
|
1394
|
+
IS_SOURCE_FILES+=("$f")
|
|
1395
|
+
done <<< "$listing"
|
|
1361
1396
|
done <<< "$IS_YAML_LIST"
|
|
1362
1397
|
}
|
|
1363
1398
|
|
|
@@ -1391,9 +1426,8 @@ report_context_source_sizes() {
|
|
|
1391
1426
|
local f path has_paths
|
|
1392
1427
|
local -a rule_files=() always_on_rules=() scoped_rules=() agent_files=() skill_files=()
|
|
1393
1428
|
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
done < <(source_artifact_files "$repo_root" "$config_file" "rules")
|
|
1429
|
+
read_source_artifact_files "$repo_root" "$config_file" "rules"
|
|
1430
|
+
[ "${#IS_SOURCE_FILES[@]}" -eq 0 ] || rule_files=("${IS_SOURCE_FILES[@]}")
|
|
1397
1431
|
if [ "${#rule_files[@]}" -gt 0 ]; then
|
|
1398
1432
|
while IFS=$'\x1f' read -r path has_paths; do
|
|
1399
1433
|
[ -n "$path" ] || continue
|
|
@@ -1405,13 +1439,11 @@ report_context_source_sizes() {
|
|
|
1405
1439
|
done < <(frontmatter_index "paths#" "${rule_files[@]}")
|
|
1406
1440
|
fi
|
|
1407
1441
|
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
done < <(source_artifact_files "$repo_root" "$config_file" "agents")
|
|
1442
|
+
read_source_artifact_files "$repo_root" "$config_file" "agents"
|
|
1443
|
+
[ "${#IS_SOURCE_FILES[@]}" -eq 0 ] || agent_files=("${IS_SOURCE_FILES[@]}")
|
|
1411
1444
|
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
done < <(source_artifact_files "$repo_root" "$config_file" "skills")
|
|
1445
|
+
read_source_artifact_files "$repo_root" "$config_file" "skills"
|
|
1446
|
+
[ "${#IS_SOURCE_FILES[@]}" -eq 0 ] || skill_files=("${IS_SOURCE_FILES[@]}")
|
|
1415
1447
|
|
|
1416
1448
|
local always_bytes custom_bytes agents_output agents_bytes=0 agents_status="disabled"
|
|
1417
1449
|
always_bytes="$(context_files_bytes "${always_on_rules[@]+"${always_on_rules[@]}"}")"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ainova-systems/intelligence",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
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"
|
|
@@ -96,6 +96,29 @@ Registries are an ordered trust list and the only resolver for a package name. T
|
|
|
96
96
|
|
|
97
97
|
Stable Git tags provide package versions. Semver ranges select the highest matching stable tag; a `ref:` pin names a branch or commit and does not move during `intelligence update`. One package name has one version per project.
|
|
98
98
|
|
|
99
|
+
### Choosing a range
|
|
100
|
+
|
|
101
|
+
`package add` without an explicit range writes `^<the version it installed>`, npm's caret. The caret holds the **leftmost non-zero** component, and that is the whole story:
|
|
102
|
+
|
|
103
|
+
| Requested | Follows automatically | Stops at |
|
|
104
|
+
|---|---|---|
|
|
105
|
+
| `^1.4.0` | every later `1.x` | `2.0.0` |
|
|
106
|
+
| `^0.4.0` | every later `0.4.x` | `0.5.0` |
|
|
107
|
+
| `~1.4.0` | every later `1.4.x` | `1.5.0` |
|
|
108
|
+
| `1.4.0` | nothing | it is a pin |
|
|
109
|
+
| `latest` | every stable tag | nothing |
|
|
110
|
+
|
|
111
|
+
**A pre-1.0 package therefore follows patches only.** SemVer treats a `0.x` minor as a major, so `^0.4.0` is a ceiling at `0.5.0` and the project stays on `0.4.x` while `0.5`, `0.6` and later ship. That is the correct reading of the range, not a defect — but it is the single most common surprise, because the same spelling behaves differently once the package reaches `1.0.0`.
|
|
112
|
+
|
|
113
|
+
Practice:
|
|
114
|
+
|
|
115
|
+
- **Keep the caret.** It is the right default at both stages: automatic patches, and a deliberate decision at every boundary that SemVer says may break.
|
|
116
|
+
- **Read `intelligence update --preview` as two answers.** The move is what the range allows; the `available outside '<range>'` note is what it excludes, and it prints the command that follows it. That note never joins the counter, because no ordinary mode of `update` installs it.
|
|
117
|
+
- **Cross a boundary deliberately, one at a time.** Read the package's changelog for every version crossed, then `intelligence update @scope/name --latest`: it takes the newest stable version and rewrites the requested range to `^<that version>`, so the manifest still records what the project asked for and the next boundary is still a decision. The flag needs the package named — one confirmation must not cover several unrelated changelogs — and it refuses a `ref:` pin, which is frozen by intent. Without it `update` never widens a range, because a command that granted itself permission to install would make the range meaningless.
|
|
118
|
+
- **Review the generated diff, not just the lock.** A package minor may rename or drop an artifact. A renamed skill breaks every `/skill-name` that invoked it, and a renamed artifact silently ends the override relationship a same-named project artifact had, so the project's version stops winning and the package's content appears instead.
|
|
119
|
+
- **`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.
|
|
120
|
+
- **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.
|
|
121
|
+
|
|
99
122
|
Commit `intelligence.lock`. It records requested versions, source URLs and paths, resolved refs and commit SHAs. After cloning, `intelligence sync` restores a missing store strictly from that lock before rendering; manifest/lock or SHA drift is refused. Re-run `package add` when deliberately changing a source.
|
|
100
123
|
|
|
101
124
|
`@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.
|