@ainova-systems/intelligence 0.19.0 → 0.21.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 +2 -1
- package/cli/commands/registry.sh +1 -1
- package/cli/commands/source.sh +1 -1
- package/cli/commands/sync.sh +78 -6
- package/cli/intelligence +15 -2
- package/cli/internal/check.sh +39 -0
- package/cli/internal/convert-legacy.sh +1 -1
- package/cli/internal/package-search.sh +1 -1
- package/cli/internal/package-update.sh +2 -2
- package/cli/internal/restore.sh +3 -2
- package/cli/lib/cli-common.sh +145 -33
- package/cli/lib/gitignore.sh +19 -2
- package/cli/lib/lockfile.sh +14 -4
- package/cli/lib/manifest.sh +147 -1
- package/cli/lib/qmap.awk +97 -32
- package/cli/lib/registry.sh +1 -1
- 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 +32 -8
- package/engine/adapters/agents.sh +47 -52
- package/engine/adapters/antigravity.sh +71 -53
- package/engine/adapters/claude.sh +42 -57
- package/engine/adapters/codex.sh +77 -16
- package/engine/adapters/copilot.sh +54 -65
- package/engine/adapters/cursor.sh +88 -78
- package/engine/adapters/opencode.sh +23 -32
- package/engine/adapters/pi.sh +140 -42
- package/engine/lib/adapter-contract.sh +36 -4
- package/engine/lib/common.sh +1388 -165
- package/engine/lib/contract.sh +5 -0
- package/engine/sync.sh +145 -38
- package/package.json +1 -1
- package/packages/sync/references/adapters.md +48 -38
- package/packages/sync/references/conventions.md +61 -12
- package/packages/sync/references/onboarding-migration.md +4 -4
- package/packages/sync/rules/intelligence-authoring.md +2 -2
- package/packages/sync/skills/intelligence-learn-from-repository/SKILL.md +7 -5
- package/packages/sync/skills/intelligence-review-context/references/audit-checks.md +1 -1
- package/packages/sync/skills/intelligence-update-context/references/rules.md +5 -0
package/engine/lib/contract.sh
CHANGED
|
@@ -84,6 +84,11 @@ IS_RC_AMBIGUOUS=3 # conflicting state; agent/human-only — bash never
|
|
|
84
84
|
IS_RC_AHEAD=4 # project stamped newer than this engine understands
|
|
85
85
|
IS_RC_ABORTED_INCOMPLETE=5 # staged state incomplete; the project was left untouched
|
|
86
86
|
IS_RC_NEEDS_UPDATE=6 # pending schema changes (stamp < engine) — run `intelligence init --apply`
|
|
87
|
+
# `intelligence sync --check` only: generated files differ from what sync would
|
|
88
|
+
# leave, or alignment is pending (IS_STATUS=out-of-date). It shares 2 with
|
|
89
|
+
# config-missing, which a check never reports: the CLI found the manifest, and
|
|
90
|
+
# the check turns any other failure that ends in 2 into 1.
|
|
91
|
+
IS_RC_OUT_OF_DATE=2
|
|
87
92
|
|
|
88
93
|
# is_status <code-name> [detail] — emit one parseable line for the skill.
|
|
89
94
|
is_status() {
|
package/engine/sync.sh
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
# IS_CONTENT_REL the project's content dir, repo-relative
|
|
11
11
|
# IS_MODULE_REL the installed sync package, repo-relative
|
|
12
12
|
# IS_PROTECTED_DIRS dirs an adapter output may never overlap
|
|
13
|
+
# IS_SYNC_CHECK optional: render, compare and restore, then write
|
|
14
|
+
# `same` or `differs` to this file (`sync --check`)
|
|
13
15
|
#
|
|
14
16
|
# Usage: intelligence sync [target]
|
|
15
17
|
|
|
@@ -111,45 +113,19 @@ while IFS=$'\037' read -r section src state _dir alias holders; do
|
|
|
111
113
|
echo "WARNING: sources.$section '$src' $IS_SOURCE_PROBLEM — skipped; 'intelligence status --check' reports it" >&2
|
|
112
114
|
done <<< "${IS_YL_UNRESOLVED:-}"
|
|
113
115
|
|
|
114
|
-
# Lint frontmatter across all source files (rules, agents, skills).
|
|
115
|
-
# Catches issues like unquoted colons that strict YAML consumers reject.
|
|
116
|
-
LINT_FILES=()
|
|
117
|
-
LINT_SKILL_DIRS=()
|
|
118
|
-
for section in rules agents skills; do
|
|
119
|
-
load_yaml_list "$CONFIG_FILE" "$section"
|
|
120
|
-
while IFS= read -r src; do
|
|
121
|
-
[ -z "$src" ] && continue
|
|
122
|
-
src_dir="$REPO_ROOT/$src"
|
|
123
|
-
[ -d "$src_dir" ] || continue
|
|
124
|
-
if [ "$section" = "skills" ]; then
|
|
125
|
-
LINT_SKILL_DIRS+=("$src_dir")
|
|
126
|
-
else
|
|
127
|
-
for f in "$src_dir"/*.md; do
|
|
128
|
-
[ -f "$f" ] && LINT_FILES+=("$f")
|
|
129
|
-
done
|
|
130
|
-
fi
|
|
131
|
-
done <<< "$IS_YAML_LIST"
|
|
132
|
-
done
|
|
133
|
-
# One find for every skills source: it walks its start points in order.
|
|
134
|
-
if [ "${#LINT_SKILL_DIRS[@]}" -gt 0 ]; then
|
|
135
|
-
while IFS= read -r f; do
|
|
136
|
-
[ -n "$f" ] && LINT_FILES+=("$f")
|
|
137
|
-
done < <(find "${LINT_SKILL_DIRS[@]}" -mindepth 2 -maxdepth 2 -name 'SKILL.md' 2>/dev/null)
|
|
138
|
-
fi
|
|
139
|
-
if [ "${#LINT_FILES[@]}" -gt 0 ]; then
|
|
140
|
-
lint_frontmatter_files "${LINT_FILES[@]}"
|
|
141
|
-
fi
|
|
142
116
|
|
|
143
117
|
# Sources stay read-only for the whole run (validate_output_path refuses an
|
|
144
|
-
# output inside one), so
|
|
145
|
-
#
|
|
146
|
-
#
|
|
147
|
-
#
|
|
118
|
+
# output inside one), so the source catalog is built once, in this shell, and
|
|
119
|
+
# every renderer replays it: AGENTS.md, every adapter, the shared skill
|
|
120
|
+
# directory and the context report read sources through source_artifacts_var
|
|
121
|
+
# (decision 0021). Building it names every artifact, settles which of a name
|
|
122
|
+
# renders, refuses ambiguous names before any output is touched, settles every
|
|
123
|
+
# symlink in a skills source (decision 0017), and lints the frontmatter of every
|
|
124
|
+
# source — skills as the inventory renders them: SKILL.md behind a link inside
|
|
125
|
+
# its source is linted, one that leaves its source is never read.
|
|
148
126
|
# shellcheck disable=SC2034 # read by read_source_artifact_files in lib/common.sh
|
|
149
127
|
IS_SOURCE_FILES_MEMO=1
|
|
150
|
-
|
|
151
|
-
read_source_artifact_files "$REPO_ROOT" "$CONFIG_FILE" "$section"
|
|
152
|
-
done
|
|
128
|
+
source_catalog_build "$REPO_ROOT" "$CONFIG_FILE" lint
|
|
153
129
|
|
|
154
130
|
# Adapters come from two places, discovered by filename (minus `.sh`,
|
|
155
131
|
# `_template` excluded):
|
|
@@ -190,6 +166,11 @@ for adapters_dir in "$SCRIPT_DIR/adapters" "$INTELLIGENCE_DIR/adapters"; do
|
|
|
190
166
|
done
|
|
191
167
|
done
|
|
192
168
|
|
|
169
|
+
# A `targets:` or `exclude_targets:` id must name an adapter this project has,
|
|
170
|
+
# enabled or not, or the group `committed` (decision 0021). Refused here,
|
|
171
|
+
# before anything is snapshotted or written.
|
|
172
|
+
source_catalog_check_ids "$REPO_ROOT" "${ADAPTERS[*]}" || exit 1
|
|
173
|
+
|
|
193
174
|
# Validate every selected adapter contract before any output is touched, then
|
|
194
175
|
# snapshot the declared write-set. If a later adapter fails, the EXIT handler
|
|
195
176
|
# restores all earlier adapter outputs so sync is atomic from the repository's
|
|
@@ -211,6 +192,17 @@ SYNC_PARALLEL=1
|
|
|
211
192
|
SYNC_BG_PIDS=()
|
|
212
193
|
SYNC_COPY_INDEXES=()
|
|
213
194
|
|
|
195
|
+
# An accepted output-root directory link is a write path, unlike a nested
|
|
196
|
+
# generated asset link. Checks must retain the bytes behind that root too.
|
|
197
|
+
snapshot_sync_entry() {
|
|
198
|
+
local src="$1" index="$2"
|
|
199
|
+
cp -a "$src" "$SYNC_TX_DIR/data/$index" || return $?
|
|
200
|
+
if [ -n "${IS_SYNC_CHECK:-}" ] && [ -L "$src" ] && [ -d "$src" ]; then
|
|
201
|
+
mkdir -p "$SYNC_TX_DIR/targets/$index" || return $?
|
|
202
|
+
cp -a "$src/." "$SYNC_TX_DIR/targets/$index" || return $?
|
|
203
|
+
fi
|
|
204
|
+
}
|
|
205
|
+
|
|
214
206
|
snapshot_sync_path() {
|
|
215
207
|
local adapter_name="$1" rel="$2" src index present=0
|
|
216
208
|
case "$SYNC_TX_SEEN_LIST" in
|
|
@@ -224,11 +216,11 @@ snapshot_sync_path() {
|
|
|
224
216
|
# Copies of distinct paths into distinct slots: they can overlap, and
|
|
225
217
|
# The snapshot wait before the render collects every status.
|
|
226
218
|
if [ "$SYNC_PARALLEL" = 1 ]; then
|
|
227
|
-
|
|
219
|
+
snapshot_sync_entry "$src" "$index" 2> "$SYNC_TX_DIR/copy.$index.err" &
|
|
228
220
|
SYNC_BG_PIDS+=("$!")
|
|
229
221
|
SYNC_COPY_INDEXES+=("$index")
|
|
230
222
|
else
|
|
231
|
-
|
|
223
|
+
snapshot_sync_entry "$src" "$index"
|
|
232
224
|
fi
|
|
233
225
|
present=1
|
|
234
226
|
fi
|
|
@@ -274,6 +266,68 @@ wait_sync_jobs() {
|
|
|
274
266
|
return "$first"
|
|
275
267
|
}
|
|
276
268
|
|
|
269
|
+
# Compare each entry without following links: copied relative and dangling
|
|
270
|
+
# links describe their targets, not bytes at the snapshot's new location.
|
|
271
|
+
compare_sync_entries() {
|
|
272
|
+
local snap="$1" live="$2" rc=0 target before
|
|
273
|
+
if [ -L "$snap" ]; then
|
|
274
|
+
[ -L "$live" ] || return 1
|
|
275
|
+
before="$(readlink "$snap")" || return 2
|
|
276
|
+
target="$(readlink "$live")" || return 2
|
|
277
|
+
# Source links inside the private copy refer to its root. Compare the
|
|
278
|
+
# generated target in the original project's coordinate system.
|
|
279
|
+
if [ -n "${IS_SYNC_CHECK_ROOT:-}" ] && [ -n "${IS_SYNC_CHECK:-}" ]; then
|
|
280
|
+
case "$before" in "$REPO_ROOT") before="$IS_SYNC_CHECK_ROOT" ;; "$REPO_ROOT"/*) before="$IS_SYNC_CHECK_ROOT/${before#"$REPO_ROOT/"}" ;; esac
|
|
281
|
+
case "$target" in "$REPO_ROOT") target="$IS_SYNC_CHECK_ROOT" ;; "$REPO_ROOT"/*) target="$IS_SYNC_CHECK_ROOT/${target#"$REPO_ROOT/"}" ;; esac
|
|
282
|
+
fi
|
|
283
|
+
[ "$before" = "$target" ] || return 1
|
|
284
|
+
elif [ -L "$live" ]; then
|
|
285
|
+
return 1
|
|
286
|
+
elif [ -f "$snap" ] && [ -f "$live" ]; then
|
|
287
|
+
cmp -s "$snap" "$live" || rc=$?
|
|
288
|
+
case "$rc" in 0) ;; 1) return 1 ;; *) return 2 ;; esac
|
|
289
|
+
if [ -x "$snap" ]; then [ -x "$live" ] || return 1; else [ ! -x "$live" ] || return 1; fi
|
|
290
|
+
elif [ -d "$snap" ] && [ -d "$live" ]; then
|
|
291
|
+
return 0
|
|
292
|
+
else
|
|
293
|
+
return 1
|
|
294
|
+
fi
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
compare_sync_snapshot() {
|
|
298
|
+
local index rel present live snap entry sub
|
|
299
|
+
while IFS=$'\t' read -r index rel present; do
|
|
300
|
+
[ -n "$rel" ] || continue
|
|
301
|
+
live="$REPO_ROOT/$rel"
|
|
302
|
+
snap="$SYNC_TX_DIR/data/$index"
|
|
303
|
+
if [ "$present" != 1 ]; then
|
|
304
|
+
if [ -e "$live" ] || [ -L "$live" ]; then return 1; fi
|
|
305
|
+
continue
|
|
306
|
+
fi
|
|
307
|
+
compare_sync_entries "$snap" "$live" || return $?
|
|
308
|
+
if [ -d "$SYNC_TX_DIR/targets/$index" ]; then
|
|
309
|
+
# validate_output_path permitted this physical directory inside
|
|
310
|
+
# the private project. Only its root is followed; descendants
|
|
311
|
+
# still use the entry-kind and literal-target comparison.
|
|
312
|
+
snap="$SYNC_TX_DIR/targets/$index"
|
|
313
|
+
live="$(cd "$live" && pwd -P)" || return 2
|
|
314
|
+
fi
|
|
315
|
+
[ ! -L "$snap" ] && [ -d "$snap" ] || continue
|
|
316
|
+
# NUL-delimited paths also preserve whitespace and newlines. find's
|
|
317
|
+
# default walk does not dereference links on GNU or BSD userland.
|
|
318
|
+
find "$snap" -print0 > "$SYNC_TX_DIR/snapshot.list" || return 2
|
|
319
|
+
find "$live" -print0 > "$SYNC_TX_DIR/live.list" || return 2
|
|
320
|
+
while IFS= read -r -d '' entry; do
|
|
321
|
+
sub="${entry#"$snap"}"
|
|
322
|
+
compare_sync_entries "$entry" "$live$sub" || return $?
|
|
323
|
+
done < "$SYNC_TX_DIR/snapshot.list"
|
|
324
|
+
while IFS= read -r -d '' entry; do
|
|
325
|
+
sub="${entry#"$live"}"
|
|
326
|
+
[ -e "$snap$sub" ] || [ -L "$snap$sub" ] || return 1
|
|
327
|
+
done < "$SYNC_TX_DIR/live.list"
|
|
328
|
+
done < "$SYNC_TX_INDEX"
|
|
329
|
+
}
|
|
330
|
+
|
|
277
331
|
finish_sync_transaction() {
|
|
278
332
|
local rc=$?
|
|
279
333
|
trap - EXIT INT TERM
|
|
@@ -305,6 +359,20 @@ note_agents_dependent() {
|
|
|
305
359
|
}
|
|
306
360
|
SELECTED_RECORDS=()
|
|
307
361
|
RENDER_PARALLEL="$SYNC_PARALLEL"
|
|
362
|
+
# What a source may render into depends on Git policy only when the catalog
|
|
363
|
+
# holds a local artifact: then every contract is read with the manifest, which
|
|
364
|
+
# may choose that policy (targets.copilot.commit_output), and its ignore and
|
|
365
|
+
# include records become IS_COMMIT_POLICY. Every other sync reads ownership
|
|
366
|
+
# alone, as decision 0020 keeps it (decision 0021).
|
|
367
|
+
policy_manifest=""
|
|
368
|
+
[ "$IS_CAT_NEED_COMMITTED" != 1 ] || policy_manifest="$CONFIG_FILE"
|
|
369
|
+
IS_COMMIT_POLICY=""
|
|
370
|
+
# The tools that read .agents/skills/ and AGENTS.md, by their contracts: a
|
|
371
|
+
# skill lands in .agents/skills/ when it is for any of them, and a shared path
|
|
372
|
+
# that delivers an artifact to a tool outside its targets is named.
|
|
373
|
+
IS_OPEN_SKILLS_READERS=""
|
|
374
|
+
IS_AGENTS_READERS=""
|
|
375
|
+
IS_ENABLED_ADAPTERS=""
|
|
308
376
|
preflight_idx=0
|
|
309
377
|
while [ "$preflight_idx" -lt "${#ADAPTERS[@]}" ]; do
|
|
310
378
|
adapter="${ADAPTERS[$preflight_idx]}"
|
|
@@ -315,13 +383,26 @@ while [ "$preflight_idx" -lt "${#ADAPTERS[@]}" ]; do
|
|
|
315
383
|
target_output_var "$CONFIG_FILE" "$adapter"
|
|
316
384
|
output="$IS_TGT_OUTPUT"
|
|
317
385
|
[ -n "$output" ] || output=".$adapter"
|
|
318
|
-
records="$(adapter_contract_records "$adapter" "$adapter_file" "$output")" || exit 1
|
|
386
|
+
records="$(adapter_contract_records "$adapter" "$adapter_file" "$output" "$policy_manifest")" || exit 1
|
|
319
387
|
|
|
320
388
|
if ! adapter_claims_add_records "$adapter" "$records"; then
|
|
321
389
|
echo "ERROR: $IS_ADAPTER_CLAIM_CONFLICT." >&2
|
|
322
390
|
echo " Give each adapter its own output: intelligence adapter list" >&2
|
|
323
391
|
exit 1
|
|
324
392
|
fi
|
|
393
|
+
IS_ENABLED_ADAPTERS="$IS_ENABLED_ADAPTERS${IS_ENABLED_ADAPTERS:+ }$adapter"
|
|
394
|
+
while IFS=$'\t' read -r kind value; do
|
|
395
|
+
case "$kind" in
|
|
396
|
+
requires) [ "$value" != agents ] || IS_AGENTS_READERS="$IS_AGENTS_READERS${IS_AGENTS_READERS:+ }$adapter" ;;
|
|
397
|
+
ignore|include) IS_COMMIT_POLICY="$IS_COMMIT_POLICY$adapter"$'\t'"$kind"$'\t'"$value"$'\n' ;;
|
|
398
|
+
owned|managed)
|
|
399
|
+
adapter_contract_rel_path "$value"
|
|
400
|
+
if [ "$IS_ADAPTER_REL_PATH" = ".agents/skills" ]; then
|
|
401
|
+
IS_OPEN_SKILLS_READERS="$IS_OPEN_SKILLS_READERS${IS_OPEN_SKILLS_READERS:+ }$adapter"
|
|
402
|
+
fi
|
|
403
|
+
;;
|
|
404
|
+
esac
|
|
405
|
+
done <<< "$records"
|
|
325
406
|
|
|
326
407
|
if [ -n "$TARGET_FILTER" ] && [ "$adapter" != "$TARGET_FILTER" ]; then
|
|
327
408
|
# Not selected: its claims are registered, its output is not touched.
|
|
@@ -356,6 +437,13 @@ while [ "$preflight_idx" -lt "${#ADAPTERS[@]}" ]; do
|
|
|
356
437
|
esac
|
|
357
438
|
done <<< "$records"
|
|
358
439
|
done
|
|
440
|
+
# shellcheck disable=SC2034 # read by output_committed in lib/common.sh
|
|
441
|
+
[ -z "$policy_manifest" ] || IS_COMMIT_POLICY_SET=1
|
|
442
|
+
# A local artifact that names a committed output in its targets is refused
|
|
443
|
+
# before any adapter writes; then every shared path that delivers an artifact
|
|
444
|
+
# beyond its targets, and every target no channel reaches, is named once.
|
|
445
|
+
source_catalog_check_local "$REPO_ROOT" || exit 1
|
|
446
|
+
source_catalog_warn "$REPO_ROOT"
|
|
359
447
|
|
|
360
448
|
# Every adapter that requires `agents` skips always-on rules because AGENTS.md
|
|
361
449
|
# carries them — and each of those tools reads AGENTS.md at the workspace root
|
|
@@ -625,6 +713,25 @@ if [ $synced -eq 0 ]; then
|
|
|
625
713
|
exit 1
|
|
626
714
|
fi
|
|
627
715
|
|
|
716
|
+
# A check renders exactly like a sync, then compares every snapshotted path
|
|
717
|
+
# with what the render left and restores the snapshot whatever the answer: it
|
|
718
|
+
# never changes a file. The transaction stays active until the restore is
|
|
719
|
+
# complete, so an interrupted restore is finished by the EXIT handler.
|
|
720
|
+
if [ -n "${IS_SYNC_CHECK:-}" ]; then
|
|
721
|
+
check_rc=0
|
|
722
|
+
compare_sync_snapshot || check_rc=$?
|
|
723
|
+
case "$check_rc" in
|
|
724
|
+
0) check_verdict=same ;;
|
|
725
|
+
1) check_verdict=differs ;;
|
|
726
|
+
*)
|
|
727
|
+
echo "ERROR: sync --check could not compare the rendered output with its snapshot." >&2
|
|
728
|
+
exit 1
|
|
729
|
+
;;
|
|
730
|
+
esac
|
|
731
|
+
restore_sync_snapshot
|
|
732
|
+
printf '%s\n' "$check_verdict" > "$IS_SYNC_CHECK"
|
|
733
|
+
fi
|
|
734
|
+
|
|
628
735
|
SYNC_TX_ACTIVE=0
|
|
629
736
|
|
|
630
737
|
# After rendering, three read-only reports, in this order:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ainova-systems/intelligence",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.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"
|
|
@@ -141,22 +141,26 @@ The engine has already loaded `engine/lib/common.sh` before it sources the adapt
|
|
|
141
141
|
|
|
142
142
|
`sources.rules`, `sources.agents` and `sources.skills` contain repo-relative directory paths. Packages have already been resolved, fetched and pinned by the CLI, so package content appears as an ordinary path under `.intelligence/packages/`. The manifest may name a package's directory by the package's full name or alias instead; `read_yaml_list` and `load_yaml_list` hand an adapter the store path either way, and leave out a reference that names nothing, so an adapter reads every source the same way. Adapters never perform network access or parse `packages:`.
|
|
143
143
|
|
|
144
|
-
|
|
144
|
+
Read a section through the source catalog, never with a glob of your own. `AGENTS.md`, every built-in adapter and the template read it the same way (decision 0021):
|
|
145
145
|
|
|
146
146
|
```bash
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
[
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
# transform file
|
|
155
|
-
done
|
|
156
|
-
done < <(read_yaml_list "$config_file" "rules")
|
|
147
|
+
output_rel_var "$repo_root" "$output_dir"
|
|
148
|
+
source_artifacts_var "$repo_root" "$config_file" rules mytool "$IS_OUTPUT_REL/rules/{}.md"
|
|
149
|
+
for ((i = 0; i < ${#IS_SOURCE_FILES[@]}; i++)); do
|
|
150
|
+
# IS_SOURCE_FILES[i] is the source, IS_SOURCE_NAMES[i] its rendered name,
|
|
151
|
+
# IS_SOURCE_SCOPED[i] is 1 for a rule with paths:
|
|
152
|
+
:
|
|
153
|
+
done
|
|
157
154
|
```
|
|
158
155
|
|
|
159
|
-
|
|
156
|
+
The last argument is the repository-relative path an artifact lands at, `{}` standing for its name. The catalog lists sources in manifest order and each directory's entries in byte order, and settles four things before any adapter runs:
|
|
157
|
+
|
|
158
|
+
- **Names.** An artifact is named by its file name without a leading `.`, `.md` and a trailing `.local`, a skill by its directory name without the leading `.` and `.local`: `.draft.md` renders as `draft`, `deploy.local/` as `deploy/`. Write each output under `IS_SOURCE_NAMES`, never under the source file name; `finalize_copy_named` and `copy_skill_bundle_dirs_named` take the `IS_SOURCE_NAME_MAP` the call leaves.
|
|
159
|
+
- **Overrides.** Of two artifacts with one name, only the later source's is listed. Package sources are wired before project sources, so project content overrides a package artifact by name. Two of one name in one directory, or a local and a non-local one anywhere, refuse the sync before any adapter runs.
|
|
160
|
+
- **Local artifacts.** A `*.local.md` rule or agent and a `*.local/` skill are listed only for an output path no `ignore` record of your contract covers — what Git tracks never holds one. That is why the path must be the real one: an adapter that globs sources itself, or names a path it does not write, can put a teammate's local file into a committed output.
|
|
161
|
+
- **Targets.** An artifact whose `targets:` leaves your adapter out, or whose `exclude_targets:` names it, is not listed for it; `committed` in either field is resolved against the path you pass ([Selecting adapters](conventions.md#selecting-adapters)). Your adapter's id is a valid target as soon as its file exists, and sync has removed both fields from every copy the shared helpers write; strip them from any frontmatter you copy yourself.
|
|
162
|
+
|
|
163
|
+
`IS_SOURCE_IN_AGENTS` is 1 when `AGENTS.md` carries the artifact too. An adapter whose tool reads `AGENTS.md` renders an always-on rule only where it is 0: that is a local rule, or one whose targets leave `agents` out, and the tool's own always-on channel is the only way it arrives.
|
|
160
164
|
|
|
161
165
|
## Shared functions
|
|
162
166
|
|
|
@@ -164,6 +168,10 @@ Use the engine library instead of copying parsers or file-handling logic.
|
|
|
164
168
|
|
|
165
169
|
| Function | Purpose |
|
|
166
170
|
|---|---|
|
|
171
|
+
| `source_artifacts_var(root, config, section, adapters, output)` | The artifacts of a section that render for one of `adapters` at `output` (`{}` is the name): `IS_SOURCE_FILES`, `IS_SOURCE_NAMES`, `IS_SOURCE_SCOPED`, `IS_SOURCE_IN_AGENTS`, `IS_SOURCE_NAME_MAP`. |
|
|
172
|
+
| `output_rel_var(root, output_dir)` | The output's repository-relative path in `IS_OUTPUT_REL`, as `source_artifacts_var` takes it. |
|
|
173
|
+
| `finalize_copy_named(dest, map, src...)` | Copy-and-finalize every source into `dest` under its rendered name, in one process. |
|
|
174
|
+
| `copy_skill_bundle_dirs_named(tool, dest_root, map, src...)` | `copy_skill_bundle_dirs_for` with each skill copied under its rendered name. |
|
|
167
175
|
| `resolve_source_dir(repo_root, source)` | Resolve a manifest source to its local directory. |
|
|
168
176
|
| `read_yaml_list(config, section)` | Stream entries from `sources.<section>`, package references as their store paths. |
|
|
169
177
|
| `load_yaml_list(config, section)` | Same list into `IS_YAML_LIST`, cached — no subprocess on repeat reads. |
|
|
@@ -195,23 +203,27 @@ Prefer the batched forms inside per-file loops: a process spawn costs tens of mi
|
|
|
195
203
|
|
|
196
204
|
Rules without `paths:` are always-on. Rules with `paths:` are scoped.
|
|
197
205
|
|
|
198
|
-
Intelligence routes always-on rules once through `AGENTS.md` for tools that consume it. An adapter for such a tool should not copy those rules again. If the tool has a native scoped-rule channel, transform `paths:` to the tool's equivalent.
|
|
206
|
+
Intelligence routes always-on rules once through `AGENTS.md` for tools that consume it. An adapter for such a tool should not copy those rules again — except one `AGENTS.md` does not carry (`IS_SOURCE_IN_AGENTS` is 0), which reaches the tool only through its own always-on channel. If the tool has a native scoped-rule channel, transform `paths:` to the tool's equivalent.
|
|
199
207
|
|
|
200
|
-
| Built-in | Always-on rules | Scoped rules |
|
|
201
|
-
|
|
202
|
-
| `agents` | Inline in `AGENTS.md` | List with links |
|
|
203
|
-
| `antigravity` | Omit; reads `AGENTS.md` | `.agents/rules/*.md` with `trigger: glob` and `globs:` |
|
|
204
|
-
| `claude` | Copy | Copy with `paths:` preserved |
|
|
205
|
-
| `cursor` | Omit; reads `AGENTS.md` | `.mdc` with `globs:` |
|
|
206
|
-
| `copilot` | Omit; reads `AGENTS.md` | `.instructions.md` with `applyTo:` |
|
|
207
|
-
| `codex` | Omit; reads `AGENTS.md` | No native channel |
|
|
208
|
-
| `pi` | Omit; reads `AGENTS.md` | Generated on-demand files and extension |
|
|
209
|
-
| `opencode` | Omit; reads `AGENTS.md` | No native channel |
|
|
208
|
+
| Built-in | Always-on rules | Always-on, not in `AGENTS.md` | Scoped rules |
|
|
209
|
+
|---|---|---|---|
|
|
210
|
+
| `agents` | Inline in `AGENTS.md` | — | List with links |
|
|
211
|
+
| `antigravity` | Omit; reads `AGENTS.md` | `.agents/rules/*.md` with `trigger: always_on` | `.agents/rules/*.md` with `trigger: glob` and `globs:` |
|
|
212
|
+
| `claude` | Copy | Copy | Copy with `paths:` preserved |
|
|
213
|
+
| `cursor` | Omit; reads `AGENTS.md` | `.mdc` with `alwaysApply: true` | `.mdc` with `globs:` |
|
|
214
|
+
| `copilot` | Omit; reads `AGENTS.md` | `.instructions.md` with `applyTo: "**"` | `.instructions.md` with `applyTo:` |
|
|
215
|
+
| `codex` | Omit; reads `AGENTS.md` | `AGENTS.override.md`: all of `AGENTS.md` plus these rules | No native channel |
|
|
216
|
+
| `pi` | Omit; reads `AGENTS.md` | Added to the system prompt by the generated extension | Generated on-demand files and extension |
|
|
217
|
+
| `opencode` | Omit; reads `AGENTS.md` | No channel; sync warns | No native channel |
|
|
218
|
+
|
|
219
|
+
Codex reads `AGENTS.override.md` instead of `AGENTS.md` in the same directory, so the Codex adapter writes it — the whole rendered `AGENTS.md`, then the rules only Codex receives — while such a rule exists, and removes it when none does. Its contract manages and ignores the path; a file there that Intelligence did not generate is never touched, and a render that needs the path refuses until it is moved.
|
|
210
220
|
|
|
211
221
|
If a new adapter relies on `AGENTS.md` for always-on rules, its target must require `agents`. Add that invariant to `engine/sync.sh` when contributing the adapter upstream.
|
|
212
222
|
|
|
213
223
|
Every successful sync prints an adapter-agnostic `CONTEXT:` summary with source byte totals and file counts. It separates always-on rules from custom context (scoped rules, agent prompts and skill entry points), then reports a numeric `agents-md` byte count and `generated`, `not-generated`, or `disabled` status; supporting skill assets are excluded until explicitly read. Adapter-specific hard limits and suppression controls stay inside the adapter that owns them.
|
|
214
224
|
|
|
225
|
+
Print an adapter warning on stderr as one unindented line starting `WARNING:`. `sync --compact`, which `intelligence init` runs, keeps exactly those lines and drops indented progress, so a warning in any other form never reaches the reader.
|
|
226
|
+
|
|
215
227
|
The Codex adapter checks its `project_doc_max_bytes` default (32 KiB). When generated `AGENTS.md` exceeds it, sync prints the byte count and a sufficient Codex setting. The warning says "may truncate" because a developer can already have a larger override. `targets.codex.warn_project_doc_limit` accepts `true` or omission for the default threshold, a positive byte count to match another effective limit, or `false` to disable the Intelligence warning. Both inline and block target forms work, and the field does not change Codex configuration. The warning remains visible in `sync --compact`.
|
|
216
228
|
|
|
217
229
|
The Antigravity adapter checks the 12,000-character limit its documentation
|
|
@@ -237,7 +249,9 @@ Skills follow the [Agent Skills standard](https://agentskills.io). Copy each ski
|
|
|
237
249
|
copy_skill_bundle "$source_skill_dir" "$output_dir/skills/$skill_name"
|
|
238
250
|
```
|
|
239
251
|
|
|
240
|
-
Do not use plain `cp` for skill bundles. `copy_skill_bundle` preserves non-Markdown assets,
|
|
252
|
+
Do not use plain `cp` for skill bundles. `copy_skill_bundle` preserves non-Markdown assets, normalizes Markdown, expands layout tokens and quotes `description` and `argument-hint` where strict YAML readers require strings. It renders `effort:` as the shared tree does; a tree only one tool reads uses `copy_skill_bundle_dirs_for <tool>`, so the copy carries that tool's level or no effort at all. It never writes a symlink; how a link in a skills source renders is defined in [conventions](conventions.md#skill-body-and-resources).
|
|
253
|
+
|
|
254
|
+
`source_artifacts_var ... skills` lists only the skills sync renders: one whose directory or `SKILL.md` is a link leaving its source is left out of every output, and sync has already warned about it once. Copy them with `copy_skill_bundle_dirs_named` and the call's `IS_SOURCE_NAME_MAP`, so `deploy.local/` lands as `deploy/`.
|
|
241
255
|
|
|
242
256
|
Antigravity, Codex, Pi and OpenCode share `.agents/skills/`. Any adapter writing that open-standard directory must call `sync_open_skill_dirs`; it is the single lifecycle owner for immediate skill subdirectories.
|
|
243
257
|
|
|
@@ -303,21 +317,16 @@ This example copies rules without frontmatter. A real adapter must decide how th
|
|
|
303
317
|
#!/bin/bash
|
|
304
318
|
|
|
305
319
|
sync_mytool_rules() {
|
|
306
|
-
local repo_root="$1" config_file="$2" output_dir="$3"
|
|
320
|
+
local repo_root="$1" config_file="$2" output_dir="$3" i out
|
|
307
321
|
mkdir -p "$output_dir/rules"
|
|
308
322
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
[
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
[ -f "$file" ] || continue
|
|
317
|
-
strip_frontmatter "$file" > "$output_dir/rules/$(basename "$file")"
|
|
318
|
-
finalize_output_file "$output_dir/rules/$(basename "$file")"
|
|
319
|
-
done
|
|
320
|
-
done < <(read_yaml_list "$config_file" "rules")
|
|
323
|
+
output_rel_var "$repo_root" "$output_dir"
|
|
324
|
+
source_artifacts_var "$repo_root" "$config_file" rules mytool "$IS_OUTPUT_REL/rules/{}.md"
|
|
325
|
+
for ((i = 0; i < ${#IS_SOURCE_FILES[@]}; i++)); do
|
|
326
|
+
out="$output_dir/rules/${IS_SOURCE_NAMES[i]}.md"
|
|
327
|
+
strip_frontmatter "${IS_SOURCE_FILES[i]}" > "$out"
|
|
328
|
+
finalize_output_file "$out"
|
|
329
|
+
done
|
|
321
330
|
}
|
|
322
331
|
|
|
323
332
|
adapter_contract_mytool() {
|
|
@@ -347,6 +356,7 @@ Verify:
|
|
|
347
356
|
|
|
348
357
|
- native file names, frontmatter and model/permission mappings;
|
|
349
358
|
- scoped and always-on routing without duplicated context;
|
|
359
|
+
- a dotfile or `*.local.md` source renders under its name, and a local one only into outputs the contract ignores;
|
|
350
360
|
- skill resources are present;
|
|
351
361
|
- no literal layout tokens remain;
|
|
352
362
|
- hand-authored siblings under the tool root survive;
|
|
@@ -366,6 +376,6 @@ For a built-in adapter, add the same assertions to the repository's smoke or lif
|
|
|
366
376
|
| `claude` | `.claude/rules`, `.claude/agents`, `.claude/skills` |
|
|
367
377
|
| `cursor` | `.cursor/rules`, `.cursor/agents`, `.cursor/skills` |
|
|
368
378
|
| `copilot` | `.github/instructions`, `.github/agents`, `.github/skills` |
|
|
369
|
-
| `codex` | `.codex/agents`, `.agents/skills` |
|
|
379
|
+
| `codex` | `.codex/agents`, `.agents/skills`, marker-owned `AGENTS.override.md` |
|
|
370
380
|
| `pi` | `.pi/intelligence-sync`, `.pi/extensions`, `.pi/prompts`, `.agents/skills` |
|
|
371
381
|
| `opencode` | `.opencode/agents`, marker-owned `.opencode/commands`, `.agents/skills` |
|
|
@@ -73,7 +73,7 @@ sources:
|
|
|
73
73
|
|
|
74
74
|
A package's directory may also be named by the package instead of its store path: `@ainova-systems/sync/rules` names folder `rules` of the package `packages:` declares as `@ainova-systems/sync`, and `sync:rules` names it through the alias that package declares (`intelligence package alias @ainova-systems/sync sync`). Both render exactly as the store path, and both are installed package content like it: edit that content in its source repository. The CLI writes store paths and never rewrites a reference a person wrote; an `@scope/name/...` entry for a package `packages:` does not declare is an ordinary project path.
|
|
75
75
|
|
|
76
|
-
Missing project-owned source directories are skipped, so a package-only project need not create empty `rules/`, `agents/` or `skills/` directories. Source order matters:
|
|
76
|
+
Missing project-owned source directories are skipped, so a package-only project need not create empty `rules/`, `agents/` or `skills/` directories. Source order matters: of two artifacts with one name, the later source's renders everywhere, `AGENTS.md` included, and the earlier one renders nowhere (see [Artifact names](#artifact-names-and-local-artifacts)). Package sources are wired before project sources so the project can override a package artifact deliberately.
|
|
77
77
|
|
|
78
78
|
Project-owned entries are managed with `intelligence source add|remove|list` rather than by hand — it validates the path against the way the engine resolves it and prints the resulting order. `add` appends by default; `--before <entry>` / `--after <entry>` place content that should behave like a package ahead of the project's own directories.
|
|
79
79
|
|
|
@@ -164,6 +164,41 @@ Common skill verbs:
|
|
|
164
164
|
|
|
165
165
|
The verb describes the outcome, not whether the skill implements the work or delegates to another command or skill.
|
|
166
166
|
|
|
167
|
+
## Artifact names and local artifacts
|
|
168
|
+
|
|
169
|
+
An artifact's name is its file name without a leading `.`, `.md` and a trailing `.local`; a skill's is its directory name without the leading `.` and the trailing `.local`. `backend.md`, `.backend.md` and `backend.local.md` are all the rule `backend`, `deploy.local/` is the skill `deploy`, and every generated copy carries the name: `.claude/rules/backend.md`, `.cursor/rules/backend.mdc`, `.claude/skills/deploy/`. A leading dot means nothing — a dotfile source renders like any other.
|
|
170
|
+
|
|
171
|
+
Names stay unique. Across sources the later source's artifact renders and the earlier one does not, in `AGENTS.md` as in every tool's output; a skill a later source overrides is replaced whole, resources included. Sync refuses two artifacts with one name in one source directory (`backend.md` beside `.backend.md`), and a local and a non-local artifact with one name anywhere in the sources: a local file that overrode or yielded to a committed one would change what committed output holds.
|
|
172
|
+
|
|
173
|
+
A **local** artifact is personal or machine-specific guidance — the OS, the shell, a local service, a personal habit — that only this clone loads: a rule or agent named `*.local.md`, or a skill directory named `*.local/`. `intelligence init` writes the matching patterns, for the project's content directory, into its managed `.gitignore` block, so Git and sync agree on which files are local:
|
|
174
|
+
|
|
175
|
+
```gitignore
|
|
176
|
+
intelligence/rules/*.local.md
|
|
177
|
+
intelligence/agents/*.local.md
|
|
178
|
+
intelligence/skills/*.local/
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Sync renders a local artifact only into outputs Git ignores, and never into one it tracks: not `AGENTS.md`, not OpenCode's slash commands, not Copilot's output under `commit_output: true`. A clone without the file therefore renders the same committed files, and `intelligence sync --check` passes in CI. A local always-on rule reaches each tool through that tool's own always-on channel (see [Routing](#routing)), since `AGENTS.md` cannot carry it; OpenCode has none, and sync says so. `intelligence status --check` reports a missing pattern, and `intelligence init` restores it.
|
|
182
|
+
|
|
183
|
+
A pattern never applies to a file Git already tracks, and a source outside the content directory has no pattern at all. `intelligence status --check` therefore names every file with a local name that Git tracks in a project-owned source: rename it to share it, or `git rm --cached` it to keep it on this machine.
|
|
184
|
+
|
|
185
|
+
## Selecting adapters
|
|
186
|
+
|
|
187
|
+
Every rule, agent and skill renders to every enabled adapter. Two frontmatter fields narrow that, for guidance that matters to one tool or should cost no other tool context:
|
|
188
|
+
|
|
189
|
+
```yaml
|
|
190
|
+
---
|
|
191
|
+
targets: [claude, cursor] # only these adapters; without it, every enabled one
|
|
192
|
+
exclude_targets: [committed] # remove these from that set
|
|
193
|
+
---
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Both take adapter ids — built-in or the project's own — and the group `committed`: every output path Git tracks, resolved per path from each adapter's contract. `AGENTS.md` always belongs to it, and so do OpenCode's slash commands and, under `commit_output: true`, Copilot's output; OpenCode's agents and the shared `.agents/skills/` do not. Write them in the list syntax `paths:` takes — a flow list `[a, b]` or a block list — and in `SKILL.md` for a skill. `targets: []` renders the artifact nowhere. Sync removes both fields from every generated copy.
|
|
197
|
+
|
|
198
|
+
An id that names no adapter refuses the sync. An adapter that is not enabled is simply no target: enablement stays the manifest's decision. A local artifact excludes `committed` already, and one whose `targets` names a committed output — `agents`, `committed`, or an adapter all of whose output Git tracks — refuses the sync, because every other clone and CI render that output without it.
|
|
199
|
+
|
|
200
|
+
Two paths are shared, not per tool, and a filter can withhold an artifact from a shared path but never from one of its readers. An artifact whose set contains `agents` goes into `AGENTS.md` and reaches every tool that reads it. A skill whose set contains any reader of `.agents/skills/` — Antigravity, Codex, Pi, OpenCode — lands there for all of them. Sync prints a `WARNING:` line when a shared path delivers an artifact to an enabled adapter outside its set, and when an adapter in its set can receive it through no channel. An artifact with neither field nor a `.local` name keeps the routing, and the warnings, it always had.
|
|
201
|
+
|
|
167
202
|
## Agent conventions
|
|
168
203
|
|
|
169
204
|
```yaml
|
|
@@ -246,9 +281,12 @@ Rule content in Markdown.
|
|
|
246
281
|
|---|---|---|---|---|---|
|
|
247
282
|
| Scoped | copied with `paths:` | `.mdc` with `globs:` | `.md` with `trigger: glob` and `globs:` | `.instructions.md` with `applyTo:` | listed in `AGENTS.md`; Pi also gets on-demand files and an extension |
|
|
248
283
|
| Always-on | copied | omitted | omitted | omitted | inlined once into `AGENTS.md` |
|
|
284
|
+
| Always-on, not in `AGENTS.md` | copied | `.mdc` with `alwaysApply: true` | `.md` with `trigger: always_on` | `.instructions.md` with `applyTo: "**"` | Codex: `AGENTS.override.md`; Pi: added to the system prompt by its extension; OpenCode: none |
|
|
249
285
|
|
|
250
286
|
Cursor, Copilot, Codex, Pi, OpenCode and Antigravity consume `AGENTS.md`, so always-on rules are not duplicated in their tool-specific channels. Claude does not consume `AGENTS.md`, so it receives the full rule set. OpenCode and Codex have no generated path-scoped rule channel; OpenCode users may configure `instructions:` globs themselves.
|
|
251
287
|
|
|
288
|
+
An always-on rule `AGENTS.md` does not carry — a local one, or one whose set leaves `agents` out (see [Selecting adapters](#selecting-adapters)) — reaches each tool in its set through that tool's own channel instead, the last row. Codex reads `AGENTS.override.md` instead of `AGENTS.md` in the same directory, so the file holds the whole `AGENTS.md` plus the rules only Codex receives; it is ignored, exists only while such a rule does, and one Intelligence did not generate is never touched. Antigravity's `trigger: always_on` belongs to the same undocumented key family as its `trigger: glob`. OpenCode reads instructions only from `AGENTS.md` or its committed `opencode.json`, so it can receive neither a local nor a tool-targeted always-on rule.
|
|
289
|
+
|
|
252
290
|
Keep always-on rules small. Put narrow framework or component guidance behind `paths:` so unrelated tasks do not pay its context cost.
|
|
253
291
|
|
|
254
292
|
## Skill conventions
|
|
@@ -270,7 +308,7 @@ argument-hint: "<route-name>"
|
|
|
270
308
|
4. Report the changed route and verification.
|
|
271
309
|
```
|
|
272
310
|
|
|
273
|
-
Standard optional fields (`license`, `compatibility`, `metadata`, `allowed-tools`) and tool extensions pass through unchanged. A tool ignores fields it does not understand. `effort:` is the exception: sync renders it per tool, as [Effort mappings](#effort-mappings) describes.
|
|
311
|
+
Standard optional fields (`license`, `compatibility`, `metadata`, `allowed-tools`) and tool extensions pass through unchanged. A tool ignores fields it does not understand. `effort:` is the exception: sync renders it per tool, as [Effort mappings](#effort-mappings) describes. `targets:` and `exclude_targets:` select the adapters that receive the skill and are removed from every copy ([Selecting adapters](#selecting-adapters)).
|
|
274
312
|
|
|
275
313
|
`disable-model-invocation: true` makes a skill one only the owner starts, as a slash command: use it for a procedure that changes versions, the lock or generated outputs, where an agent selecting it unasked is the failure. Claude Code, Cursor and Copilot read the field itself; Codex does not, so sync writes `agents/openai.yaml` with `allow_implicit_invocation: false` beside the skill in `.agents/skills/`. Keep the source to the field. A skill that ships its own `agents/openai.yaml` keeps everything else in it: sync adds the policy when the file sets none, and refuses one that sets it otherwise or not as a plain `allow_implicit_invocation: false` directly under `policy:`.
|
|
276
314
|
|
|
@@ -311,6 +349,8 @@ skill-name/
|
|
|
311
349
|
|
|
312
350
|
The engine copies the complete skill directory. Promote a helper outside the skill only when multiple skills share it.
|
|
313
351
|
|
|
352
|
+
A symlink inside a skills source renders as the file or directory it points at when that stays inside the same source, so one source can alias a skill or share a resource between skills. A link that leaves its source, dangles or points at a directory enclosing it is left out with a `WARNING:` line; a skill whose directory or `SKILL.md` is such a link is left out entirely. Add a skill kept elsewhere as its own source or package instead of linking it in. Generated skill directories never contain symlinks.
|
|
353
|
+
|
|
314
354
|
Size limits are backstops, not quotas:
|
|
315
355
|
|
|
316
356
|
| Artifact | Hard cap | Response |
|
|
@@ -336,12 +376,12 @@ Every line enters a finite context budget. Prefer subtraction, consolidation and
|
|
|
336
376
|
| Target | Rules | Skills | Agents |
|
|
337
377
|
|---|---|---|---|
|
|
338
378
|
| `agents` | Always-on inlined; scoped listed in `AGENTS.md` | Listed | Listed |
|
|
339
|
-
| Antigravity | `AGENTS.md` plus
|
|
379
|
+
| Antigravity | `AGENTS.md` plus `.agents/rules/*.md` | `.agents/skills/` | `.agents/agents/*.md` |
|
|
340
380
|
| Claude | `.claude/rules/` | `.claude/skills/` | `.claude/agents/` |
|
|
341
|
-
| Cursor |
|
|
342
|
-
| Copilot |
|
|
343
|
-
| Codex | `AGENTS.md`
|
|
344
|
-
| Pi | `AGENTS.md` plus scoped `.pi/intelligence-sync/rules/` | `.agents/skills/` | `.pi/prompts/intelligence-agent-*.md` |
|
|
381
|
+
| Cursor | `AGENTS.md` plus `.cursor/rules/*.mdc` | `.cursor/skills/` | `.cursor/agents/` |
|
|
382
|
+
| Copilot | `AGENTS.md` plus `.github/instructions/*.instructions.md` | `.github/skills/` | `.github/agents/` |
|
|
383
|
+
| Codex | `AGENTS.md`, or `AGENTS.override.md` while a rule needs it | `.agents/skills/` | `.codex/agents/*.toml` |
|
|
384
|
+
| Pi | `AGENTS.md` plus scoped `.pi/intelligence-sync/rules/` and its extension | `.agents/skills/` | `.pi/prompts/intelligence-agent-*.md` |
|
|
345
385
|
| OpenCode | `AGENTS.md` only | `.agents/skills/` plus slash commands | `.opencode/agents/*.md` |
|
|
346
386
|
|
|
347
387
|
`AGENTS.md` is regenerated by the `agents` adapter. Its optional static header is `targets.agents.header` in `intelligence.yaml`; generated rule, agent and skill sections follow it. Commit `AGENTS.md` when it is the project's shared canonical context.
|
|
@@ -352,7 +392,12 @@ By default, commit the manifest, lock, project-owned content and `AGENTS.md`. Ig
|
|
|
352
392
|
# CLI-managed package store
|
|
353
393
|
.intelligence/
|
|
354
394
|
|
|
355
|
-
# Local
|
|
395
|
+
# Local rules, agents and skills of the content directory stay on this machine
|
|
396
|
+
intelligence/rules/*.local.md
|
|
397
|
+
intelligence/agents/*.local.md
|
|
398
|
+
intelligence/skills/*.local/
|
|
399
|
+
|
|
400
|
+
# Root tool instructions migrate into project rules and stay ignored
|
|
356
401
|
CLAUDE.md
|
|
357
402
|
.cursorrules
|
|
358
403
|
GEMINI.md
|
|
@@ -372,6 +417,7 @@ GEMINI.md
|
|
|
372
417
|
.agents/rules/
|
|
373
418
|
.agents/agents/
|
|
374
419
|
.codex/agents/
|
|
420
|
+
AGENTS.override.md
|
|
375
421
|
|
|
376
422
|
# Generated Pi content
|
|
377
423
|
.pi/intelligence-sync/
|
|
@@ -428,9 +474,10 @@ than inferring contents from Git status.
|
|
|
428
474
|
move useful repository guidance from legacy root files such as `.cursorrules`
|
|
429
475
|
and instruction-bearing `CLAUDE.md` out of their quarantined backup copies and
|
|
430
476
|
into project-owned rules, then verify the generated tool output. Do not restore
|
|
431
|
-
the original root monolith.
|
|
432
|
-
|
|
433
|
-
|
|
477
|
+
the original root monolith. Genuinely machine-local guidance becomes a local
|
|
478
|
+
rule, agent or skill (see
|
|
479
|
+
[Artifact names and local artifacts](#artifact-names-and-local-artifacts)), not
|
|
480
|
+
a new root tool-specific file.
|
|
434
481
|
|
|
435
482
|
Before the first render, `intelligence init` preserves existing AI prompt paths
|
|
436
483
|
under `<content-dir>/_backup/`. Its `manifest.tsv` labels the snapshot
|
|
@@ -458,7 +505,7 @@ The permanent applied-schema key is the top-level scalar `schema_version` in `in
|
|
|
458
505
|
The public lifecycle is deliberately compact:
|
|
459
506
|
|
|
460
507
|
- `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.
|
|
461
|
-
- `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.
|
|
508
|
+
- `intelligence sync [adapter] [--compact] [--force] [--check]` 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. `--check` changes no generated file: it prints one `IS_STATUS` line and exits 0 when generated files are up to date and 2 (`out-of-date`) when sync would change them or alignment is pending; it never applies alignment.
|
|
462
509
|
- `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.
|
|
463
510
|
- `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.
|
|
464
511
|
- `intelligence package add|remove|list|search` owns package inventory.
|
|
@@ -479,6 +526,7 @@ The engine emits one machine-readable line, `IS_STATUS=<code> [IS_DETAIL=...]`,
|
|
|
479
526
|
| `migrated` | 0 | Initialization converted an older layout |
|
|
480
527
|
| `error` | 1 | Generic failure |
|
|
481
528
|
| `config-missing` | 2 | Required manifest is absent |
|
|
529
|
+
| `out-of-date` | 2 | `sync --check` only: sync would change generated files, or alignment is pending. A check never reports `config-missing`, and turns any other failure that would end in 2 into 1 |
|
|
482
530
|
| `ambiguous` | 3 | Conflicting state requiring agent/human judgment; reserved |
|
|
483
531
|
| `ahead-of-engine` | 4 | Manifest schema is a newer major than the engine (a newer minor or patch warns and continues) |
|
|
484
532
|
| `aborted-incomplete` | 5 | Staged replacement was incomplete; prior state remains |
|
|
@@ -493,6 +541,7 @@ Callers capture the real code with `command || rc=$?`. Do not use `if ! command;
|
|
|
493
541
|
| `intelligence.yaml` | Manifest and schema contract | Tracked |
|
|
494
542
|
| `intelligence.lock` | Resolved package state | Tracked |
|
|
495
543
|
| `<content-dir>/{rules,agents,skills,adapters}/` | Project source of truth | Tracked |
|
|
544
|
+
| `<content-dir>/{rules,agents}/*.local.md`, `<content-dir>/skills/*.local/` | Local artifacts, this clone only | Ignored |
|
|
496
545
|
| `.intelligence/` | Restorable package store | Ignored |
|
|
497
546
|
| `AGENTS.md` | Generated canonical project context | Normally tracked |
|
|
498
547
|
| Tool output directories | Generated native content | Built-in adapter-owned paths ignored; shared settings tracked |
|
|
@@ -47,7 +47,7 @@ Migrate meaning, not tool syntax:
|
|
|
47
47
|
|
|
48
48
|
| Existing format | Project-owned destination |
|
|
49
49
|
|---|---|
|
|
50
|
-
| Root `AGENTS.md`, `CLAUDE.md`, `.cursorrules`, Copilot root instructions | Split verified guidance by topic into rules;
|
|
50
|
+
| Root `AGENTS.md`, `CLAUDE.md`, `.cursorrules`, Copilot root instructions | Split verified guidance by topic into rules; move machine-local preferences into local rules (`<content-dir>/rules/<name>.local.md`), which Git ignores and sync keeps out of committed output |
|
|
51
51
|
| `.claude/rules/*.md` | Rule; preserve valid `paths:` |
|
|
52
52
|
| `.cursor/rules/*.mdc` | Rule; rename `globs:` to `paths:` and remove `alwaysApply:` |
|
|
53
53
|
| Claude/Cursor/Copilot agents, Codex `.codex/agents/*.toml` | Agent; map native model/readonly/tool fields back to `tier:` and `access:`, and a native effort level (`effort:`, `model_reasoning_effort`) back to `effort:` |
|
|
@@ -64,9 +64,9 @@ Obtain approval per `CREATE`, `UPDATE`, `REMOVE`, or `KEEP` proposal. Apply
|
|
|
64
64
|
project-owned source changes first, then run `intelligence sync` and
|
|
65
65
|
`intelligence status --check`. Inspect the enabled targets to prove the
|
|
66
66
|
migrated guidance arrived and verify quarantined old root instructions remain
|
|
67
|
-
absent.
|
|
68
|
-
|
|
69
|
-
original instruction monolith.
|
|
67
|
+
absent. A separately approved, genuinely machine-local subset becomes a local
|
|
68
|
+
rule, `<content-dir>/rules/<name>.local.md`, not a new root tool file; never
|
|
69
|
+
restore the original instruction monolith.
|
|
70
70
|
|
|
71
71
|
For every removed or renamed path, search all tracked files with `git ls-files`
|
|
72
72
|
and report remaining references with file and line number. Apply an unambiguous
|