@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
package/cli/lib/qmap.awk
CHANGED
|
@@ -1,16 +1,58 @@
|
|
|
1
1
|
# Shared tokenizer for the CLI-owned quoted-key map and scalar document shape.
|
|
2
2
|
# Reader modes ignore unrelated YAML. Lock mode validates the whole document
|
|
3
3
|
# before publishing rows, so a bad later entry never yields a partial result.
|
|
4
|
+
#
|
|
5
|
+
# One read per process: QMAP_MODE and friends describe it, the document
|
|
6
|
+
# arrives on stdin, and a strict failure exits 1 with its reason on stderr.
|
|
7
|
+
# Several reads per process: QMAP_JOBS holds one job per line (mode, block,
|
|
8
|
+
# key, field and expected lock version, unit-separated) and each job names its
|
|
9
|
+
# document as the matching file operand, so a command that needs the manifest,
|
|
10
|
+
# the lock and the CLI's package descriptor parses them in a single awk start.
|
|
11
|
+
# Every job then prints a header, "\036<status> <line count>", followed by that
|
|
12
|
+
# many lines, each behind a ">" so none is empty: its rows, or its error. Empty
|
|
13
|
+
# documents cannot be job operands (they yield no first line to start the job);
|
|
14
|
+
# the caller reads those alone.
|
|
4
15
|
BEGIN {
|
|
5
|
-
mode = ENVIRON["QMAP_MODE"]
|
|
6
|
-
block = ENVIRON["QMAP_BLOCK"]
|
|
7
|
-
wanted_key = ENVIRON["QMAP_KEY"]
|
|
8
|
-
wanted_field = ENVIRON["QMAP_FIELD"]
|
|
9
|
-
expected_version = ENVIRON["QMAP_EXPECTED_VERSION"]
|
|
10
|
-
source_file = ENVIRON["QMAP_FILE"]
|
|
11
|
-
strict = (mode == "lock" || mode == "validate")
|
|
12
16
|
sep = sprintf("%c", 31)
|
|
13
17
|
single_quote = sprintf("%c", 39)
|
|
18
|
+
multi = (ENVIRON["QMAP_JOBS"] != "")
|
|
19
|
+
if (multi) job_count = split(ENVIRON["QMAP_JOBS"], job_spec, "\n")
|
|
20
|
+
else configure(ENVIRON["QMAP_MODE"], ENVIRON["QMAP_BLOCK"], ENVIRON["QMAP_KEY"], ENVIRON["QMAP_FIELD"], ENVIRON["QMAP_EXPECTED_VERSION"], ENVIRON["QMAP_FILE"])
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function configure(m, b, k, f, v, file) {
|
|
24
|
+
mode = m
|
|
25
|
+
block = b
|
|
26
|
+
wanted_key = k
|
|
27
|
+
wanted_field = f
|
|
28
|
+
expected_version = v
|
|
29
|
+
source_file = file
|
|
30
|
+
strict = (mode == "lock" || mode == "validate")
|
|
31
|
+
problem = ""; problem_line = 0
|
|
32
|
+
in_block = 0; package = ""
|
|
33
|
+
row_count = 0; found = 0; result = ""; block_seen = 0
|
|
34
|
+
split("", row_name); split("", row_line)
|
|
35
|
+
split("", package_seen); split("", field_seen); split("", field_value)
|
|
36
|
+
split("", top_seen); split("", top_value)
|
|
37
|
+
out_count = 0; failed = 0
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# emit/fail keep the single-document output stream and exit status unchanged;
|
|
41
|
+
# a job buffers both so its header can state them first.
|
|
42
|
+
function emit(line) {
|
|
43
|
+
if (multi) out[++out_count] = line
|
|
44
|
+
else print line
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function fail(message) {
|
|
48
|
+
if (multi) {
|
|
49
|
+
failed = 1
|
|
50
|
+
out_count = 0
|
|
51
|
+
out[++out_count] = message
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
print message > "/dev/stderr"
|
|
55
|
+
exit 1
|
|
14
56
|
}
|
|
15
57
|
|
|
16
58
|
function error_at(reason, line) {
|
|
@@ -104,6 +146,14 @@ function token(line, text, colon, tail) {
|
|
|
104
146
|
return 1
|
|
105
147
|
}
|
|
106
148
|
|
|
149
|
+
# A job starts at its document's first line and ends where the next begins.
|
|
150
|
+
multi && FNR == 1 {
|
|
151
|
+
if (job) finish()
|
|
152
|
+
job++
|
|
153
|
+
split(job_spec[job], spec, sep)
|
|
154
|
+
configure(spec[1], spec[2], spec[3], spec[4], spec[5], FILENAME)
|
|
155
|
+
}
|
|
156
|
+
|
|
107
157
|
{
|
|
108
158
|
sub(/\r$/, "")
|
|
109
159
|
if ($0 ~ /^[ \t]*(#.*)?$/) next
|
|
@@ -114,7 +164,7 @@ function token(line, text, colon, tail) {
|
|
|
114
164
|
if (kind == "field" && package != "") context = "package " package ", field " key ": "
|
|
115
165
|
else if (kind == "top") context = "field " key ": "
|
|
116
166
|
}
|
|
117
|
-
error_at(context "malformed lock structure or unsupported scalar",
|
|
167
|
+
error_at(context "malformed lock structure or unsupported scalar", FNR)
|
|
118
168
|
}
|
|
119
169
|
if ($0 ~ /^[^ #\t]/) { in_block = 0; package = "" }
|
|
120
170
|
next
|
|
@@ -122,14 +172,14 @@ function token(line, text, colon, tail) {
|
|
|
122
172
|
if (kind == "top") {
|
|
123
173
|
in_block = (key == block && empty)
|
|
124
174
|
package = ""
|
|
125
|
-
if (strict && top_seen[key]++) error_at("duplicate top-level field " key,
|
|
175
|
+
if (strict && top_seen[key]++) error_at("duplicate top-level field " key, FNR)
|
|
126
176
|
if (!(key in top_value)) top_value[key] = value
|
|
127
177
|
if (mode == "top" && key == wanted_key && !found) { result = value; found = 1 }
|
|
128
178
|
if (strict) {
|
|
129
179
|
if (key == block) {
|
|
130
180
|
block_seen = 1
|
|
131
|
-
if (!empty) error_at(block " must be a block map",
|
|
132
|
-
} else if (empty) error_at("expected scalar for " key,
|
|
181
|
+
if (!empty) error_at(block " must be a block map", FNR)
|
|
182
|
+
} else if (empty) error_at("expected scalar for " key, FNR)
|
|
133
183
|
}
|
|
134
184
|
next
|
|
135
185
|
}
|
|
@@ -137,46 +187,55 @@ function token(line, text, colon, tail) {
|
|
|
137
187
|
package = key
|
|
138
188
|
row_count++
|
|
139
189
|
row_name[row_count] = key
|
|
140
|
-
row_line[row_count] =
|
|
190
|
+
row_line[row_count] = FNR
|
|
141
191
|
if (strict) {
|
|
142
|
-
if (key == "") error_at("empty package key",
|
|
143
|
-
if (package_seen[key]++) error_at("duplicate package " key,
|
|
144
|
-
if (!empty) error_at("expected field map for " key,
|
|
192
|
+
if (key == "") error_at("empty package key", FNR)
|
|
193
|
+
if (package_seen[key]++) error_at("duplicate package " key, FNR)
|
|
194
|
+
if (!empty) error_at("expected field map for " key, FNR)
|
|
145
195
|
}
|
|
146
196
|
if (mode == "value" && key == wanted_key && !found) { result = value; found = 1 }
|
|
147
197
|
next
|
|
148
198
|
}
|
|
149
199
|
if (kind == "field" && in_block && package != "") {
|
|
150
200
|
identity = package SUBSEP key
|
|
151
|
-
if (strict && field_seen[identity]++) error_at("duplicate field " package "." key,
|
|
201
|
+
if (strict && field_seen[identity]++) error_at("duplicate field " package "." key, FNR)
|
|
152
202
|
if (!(identity in field_value)) field_value[identity] = value
|
|
153
|
-
if (strict && empty) error_at("expected scalar for " package "." key,
|
|
203
|
+
if (strict && empty) error_at("expected scalar for " package "." key, FNR)
|
|
154
204
|
if (mode == "field" && package == wanted_key && key == wanted_field && !found) {
|
|
155
205
|
result = value
|
|
156
206
|
found = 1
|
|
157
207
|
}
|
|
158
208
|
next
|
|
159
209
|
}
|
|
160
|
-
if (strict) error_at("malformed lock structure: expected top-level scalar or quoted package field",
|
|
210
|
+
if (strict) error_at("malformed lock structure: expected top-level scalar or quoted package field", FNR)
|
|
161
211
|
}
|
|
162
212
|
|
|
163
|
-
|
|
213
|
+
# The end of one document: its rows or its error, through emit/fail.
|
|
214
|
+
function finish( i) {
|
|
215
|
+
finish_rows()
|
|
216
|
+
if (multi) {
|
|
217
|
+
printf "%c%d %d\n", 30, failed, out_count
|
|
218
|
+
for (i = 1; i <= out_count; i++) print ">" out[i]
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function finish_rows( version, count, fields, tops, i, f, name, row) {
|
|
164
223
|
# Read the format header before interpreting a future body's shape. Errors
|
|
165
224
|
# were retained during the single pass so header placement does not matter.
|
|
166
225
|
if (mode == "lock" && top_value["lockfile_version"] != expected_version) {
|
|
167
226
|
version = top_value["lockfile_version"]
|
|
168
|
-
|
|
169
|
-
|
|
227
|
+
fail(source_file " has unsupported or missing lockfile_version '" (version == "" ? "<missing>" : version) "' — expected " expected_version)
|
|
228
|
+
return
|
|
170
229
|
}
|
|
171
230
|
if (strict && problem != "") {
|
|
172
|
-
|
|
173
|
-
|
|
231
|
+
fail(source_file ":" problem_line ": " problem)
|
|
232
|
+
return
|
|
174
233
|
}
|
|
175
234
|
if (strict && !block_seen) {
|
|
176
|
-
|
|
177
|
-
|
|
235
|
+
fail(source_file ": missing " block " block")
|
|
236
|
+
return
|
|
178
237
|
}
|
|
179
|
-
if (mode == "lock")
|
|
238
|
+
if (mode == "lock") emit("V" sep top_value["lockfile_version"] sep top_value["engine_version"])
|
|
180
239
|
if (mode == "fieldrows") {
|
|
181
240
|
# One row per package key, as `keys` lists them: the name, then each
|
|
182
241
|
# field QMAP_FIELD names (space separated), as qmap_field decodes it.
|
|
@@ -185,22 +244,28 @@ END {
|
|
|
185
244
|
name = row_name[i]
|
|
186
245
|
row = name
|
|
187
246
|
for (f = 1; f <= count; f++) row = row sep field_value[name SUBSEP fields[f]]
|
|
188
|
-
|
|
247
|
+
emit(row)
|
|
189
248
|
}
|
|
190
249
|
} else if (mode == "rows" || mode == "lock" || mode == "keys") {
|
|
191
250
|
for (i = 1; i <= row_count; i++) {
|
|
192
251
|
name = row_name[i]
|
|
193
|
-
if (mode == "keys")
|
|
252
|
+
if (mode == "keys") emit(name)
|
|
194
253
|
else {
|
|
195
254
|
row = name sep field_value[name SUBSEP "requested"] sep field_value[name SUBSEP "url"] sep field_value[name SUBSEP "path"] sep field_value[name SUBSEP "resolved"] sep field_value[name SUBSEP "sha"]
|
|
196
|
-
if (mode == "lock")
|
|
197
|
-
else
|
|
255
|
+
if (mode == "lock") emit("R" sep row sep row_line[i])
|
|
256
|
+
else emit(row)
|
|
198
257
|
}
|
|
199
258
|
}
|
|
200
259
|
} else if (mode == "tops") {
|
|
201
260
|
# Several top-level scalars in one pass: QMAP_KEY lists them, space
|
|
202
261
|
# separated; each present one prints as key<US>value.
|
|
203
262
|
count = split(wanted_key, tops, " ")
|
|
204
|
-
for (i = 1; i <= count; i++) if (tops[i] in top_value)
|
|
205
|
-
} else if (found)
|
|
263
|
+
for (i = 1; i <= count; i++) if (tops[i] in top_value) emit(tops[i] sep top_value[tops[i]])
|
|
264
|
+
} else if (found) emit(result)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
END {
|
|
268
|
+
if (multi) {
|
|
269
|
+
if (job) finish()
|
|
270
|
+
} else finish()
|
|
206
271
|
}
|
package/cli/lib/registry.sh
CHANGED
|
@@ -61,7 +61,7 @@ resolve_package_source() {
|
|
|
61
61
|
[ -n "$reg_url" ] || continue
|
|
62
62
|
index="$(_fetch_index "${reg_url#git+}")"
|
|
63
63
|
if [ -z "$index" ]; then
|
|
64
|
-
echo "
|
|
64
|
+
echo "WARNING: registry unreachable or missing index.yaml, skipped: $reg_url" >&2
|
|
65
65
|
continue
|
|
66
66
|
fi
|
|
67
67
|
RES_URL="$(qmap_field "$index" "packages" "$name" "url")"
|
|
@@ -200,25 +200,30 @@ fetch_package() {
|
|
|
200
200
|
printf '%s' "$sha"
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
-
# wire_package_sources <manifest> <@scope/name> <store-rel-dir>
|
|
203
|
+
# wire_package_sources <manifest> <@scope/name> <store-rel-dir> <root>
|
|
204
204
|
# The provides convention: whichever of rules/agents/skills the installed
|
|
205
|
-
# package has becomes a sources entry — inserted FIRST in its
|
|
206
|
-
# project-owned entries come later and win on same-named artifacts.
|
|
205
|
+
# package has becomes a sources entry — its store path, inserted FIRST in its
|
|
206
|
+
# section, so project-owned entries come later and win on same-named artifacts.
|
|
207
|
+
# A section that already names that directory, in any spelling, keeps the entry
|
|
208
|
+
# where it stands: the person who wrote `@scope/name/<dir>` or `<alias>:<dir>`
|
|
209
|
+
# chose both the spelling and the position. Call it once `packages:` declares
|
|
210
|
+
# the package and its alias, which is what resolves those spellings.
|
|
207
211
|
wire_package_sources() {
|
|
208
|
-
local manifest="$1"
|
|
212
|
+
local manifest="$1" rel="$3" root="$4"
|
|
209
213
|
local section
|
|
210
214
|
for section in rules agents skills; do
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
215
|
+
[ -d "$root/$rel/$section" ] || continue
|
|
216
|
+
package_source_listed "$manifest" "$section" "$rel/$section" && continue
|
|
217
|
+
sources_add_entry_first "$manifest" "$section" "$rel/$section"
|
|
214
218
|
done
|
|
215
219
|
}
|
|
216
220
|
|
|
217
|
-
# unwire_package_sources <manifest>
|
|
221
|
+
# unwire_package_sources <manifest> <@scope/name> [section...]
|
|
222
|
+
# Drop the package's sources entries in every spelling (sources_remove_package):
|
|
223
|
+
# every one without sections, as `package remove` does; only the named
|
|
224
|
+
# sections' own directories with them, as `update` does for the directories a
|
|
225
|
+
# new version no longer has. Call it while `packages:` still declares the
|
|
226
|
+
# package and its alias.
|
|
218
227
|
unwire_package_sources() {
|
|
219
|
-
|
|
220
|
-
local section
|
|
221
|
-
for section in rules agents skills; do
|
|
222
|
-
sources_remove_entry "$manifest" "$section" "$rel/$section"
|
|
223
|
-
done
|
|
228
|
+
sources_remove_package "$@"
|
|
224
229
|
}
|