@ainova-systems/intelligence 0.24.0 → 0.25.1

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.
@@ -8,6 +8,74 @@ warn() { echo " ✗ $*"; problems=$((problems + 1)); }
8
8
  note() { echo " ! $*"; }
9
9
  ok() { echo " ✓ $*"; }
10
10
 
11
+ # claude_md_excluded <path> <settings>... - whether an entry of
12
+ # claudeMdExcludes in one of the settings files matches <path>. Claude Code
13
+ # matches those globs against absolute paths; this reads `**`, `*` and `?` and
14
+ # takes every other character literally. A Windows path is tried in both the
15
+ # Git Bash spelling and the drive spelling (`/d/x`, `D:/x`).
16
+ claude_md_excluded() {
17
+ local f alt=""
18
+ if command -v cygpath >/dev/null 2>&1; then alt="$(cygpath -m "$1")"; fi
19
+ for f in "${@:2}"; do
20
+ [ -f "$f" ] || continue
21
+ CMX_PATH="$1" CMX_ALT="$alt" awk '
22
+ function glob_re(g, i, c, out) {
23
+ out = "^"
24
+ for (i = 1; i <= length(g); i++) {
25
+ c = substr(g, i, 1)
26
+ if (c == "*" && substr(g, i + 1, 1) == "*") {
27
+ i++
28
+ if (substr(g, i + 1, 1) == "/") { i++; out = out "(.*/)?" }
29
+ else out = out ".*"
30
+ }
31
+ else if (c == "*") out = out "[^/]*"
32
+ else if (c == "?") out = out "[^/]"
33
+ else if (index("\\^$.|+()[]{}", c)) out = out "\\" c
34
+ else out = out c
35
+ }
36
+ return out "$"
37
+ }
38
+ { buf = buf $0 "\n" }
39
+ END {
40
+ p = index(buf, "\"claudeMdExcludes\"")
41
+ if (!p) exit 1
42
+ s = substr(buf, p + 18)
43
+ if (!sub(/^[ \t\r\n]*:[ \t\r\n]*\[/, "", s)) exit 1
44
+ while (1) {
45
+ sub(/^[ \t\r\n,]*/, "", s)
46
+ if (substr(s, 1, 1) != "\"") exit 1
47
+ v = ""
48
+ for (i = 2; i <= length(s); i++) {
49
+ c = substr(s, i, 1)
50
+ if (c == "\\") { i++; v = v substr(s, i, 1); continue }
51
+ if (c == "\"") break
52
+ v = v c
53
+ }
54
+ s = substr(s, i + 1)
55
+ re = glob_re(v)
56
+ if (ENVIRON["CMX_PATH"] ~ re) exit 0
57
+ if (ENVIRON["CMX_ALT"] != "" && ENVIRON["CMX_ALT"] ~ re) exit 0
58
+ }
59
+ }
60
+ ' "$f" && return 0
61
+ done
62
+ return 1
63
+ }
64
+
65
+ # claude_md_above <dir> - whether <dir> or a directory above it holds a
66
+ # CLAUDE.md, .claude/CLAUDE.md or CLAUDE.local.md, any of which keeps Claude
67
+ # Code from reading AGENTS.md. The user's own ~/.claude/CLAUDE.md does not.
68
+ claude_md_above() {
69
+ local d="$1" up
70
+ while :; do
71
+ [ ! -e "$d/CLAUDE.md" ] && [ ! -e "$d/CLAUDE.local.md" ] || return 0
72
+ if [ -e "$d/.claude/CLAUDE.md" ] && ! [ "$d" -ef "${HOME:-}" ]; then return 0; fi
73
+ up="${d%/*}"
74
+ [ -n "$d" ] && [ "$up" != "$d" ] || return 1
75
+ d="$up"
76
+ done
77
+ }
78
+
11
79
  echo "environment:"
12
80
  command -v git >/dev/null 2>&1 && ok "git $(git --version | awk '{print $3}')" || warn "git not found"
13
81
  command -v awk >/dev/null 2>&1 && ok "awk present" || warn "awk not found"
@@ -144,6 +212,7 @@ content_dir="$(manifest_intelligence_dir "$manifest")"
144
212
  # Ownership across enabled adapters: two adapters claiming one owned path prune
145
213
  # each other silently, and sync reports success for both.
146
214
  adapter_claims_reset
215
+ claude_out="" agents_out="" disabled_owned=""
147
216
  while IFS= read -r target; do
148
217
  [ -n "$target" ] || continue
149
218
  output="$(get_target_output "$manifest" "$target")"
@@ -161,6 +230,14 @@ while IFS= read -r target; do
161
230
  if [ "$(is_target_enabled "$manifest" "$target")" = "1" ]; then
162
231
  adapter_claims_add_records "$target" "$records" \
163
232
  || warn "$IS_ADAPTER_CLAIM_CONFLICT"
233
+ case "$target" in
234
+ claude) claude_out="$output" ;;
235
+ agents) agents_out="$output" ;;
236
+ esac
237
+ else
238
+ while IFS=$'\t' read -r kind value; do
239
+ [ "$kind" != owned ] || disabled_owned+="$target"$'\t'"$value"$'\n'
240
+ done <<< "$records"
164
241
  fi
165
242
  while IFS=$'\t' read -r kind value; do
166
243
  if [ "$kind" = "requires" ] \
@@ -204,6 +281,52 @@ while IFS= read -r target; do
204
281
  ok "adapter $target contract v1"
205
282
  done < <(target_names "$manifest")
206
283
 
284
+ # Disabling an adapter keeps its output for a cleanup the owner approves. An
285
+ # owned path an enabled adapter also claims is that adapter's output now. The
286
+ # empty line the here-string ends with reports the last adapter.
287
+ left_target="" left_paths=""
288
+ while IFS=$'\t' read -r target value; do
289
+ if [ "$target" != "$left_target" ] && [ -n "$left_paths" ]; then
290
+ note "adapter '$left_target' is disabled, and its output is still here: ${left_paths#, }. The intelligence-manage-adapters skill reviews it and removes what you approve"
291
+ left_paths=""
292
+ fi
293
+ left_target="$target"
294
+ [ -n "$target" ] || continue
295
+ [ -e "$IP_ROOT/$value" ] || [ -L "$IP_ROOT/$value" ] || continue
296
+ adapter_contract_rel_path "$value"
297
+ claimed=0
298
+ while IFS=$'\t' read -r _ _ path; do
299
+ [ -n "$path" ] || continue
300
+ case "$IS_ADAPTER_REL_PATH/" in "$path"/*) claimed=1 ;; esac
301
+ case "$path/" in "$IS_ADAPTER_REL_PATH"/*) claimed=1 ;; esac
302
+ done <<< "$IS_ADAPTER_CLAIMS"
303
+ [ "$claimed" = 1 ] || left_paths="$left_paths, $IS_ADAPTER_REL_PATH"
304
+ done <<< "$disabled_owned"
305
+
306
+ # Claude Code 2.1.277 and later reads AGENTS.md as project instructions when no
307
+ # CLAUDE.md, .claude/CLAUDE.md or CLAUDE.local.md exists in the project or above
308
+ # it, and keeps loading .claude/rules/ beside it (code.claude.com/docs/en/memory).
309
+ # With both targets enabled every always-on rule then loads twice. The contract
310
+ # preserves settings.json, so the exclude is the owner's to add.
311
+ if [ -n "$claude_out" ] && [ -n "$agents_out" ]; then
312
+ agents_output_path_var "$agents_out"
313
+ adapter_contract_rel_path "$IS_AGENTS_OUTPUT_PATH"
314
+ agents_md="$IS_ADAPTER_REL_PATH"
315
+ adapter_contract_rel_path "$claude_out"
316
+ claude_out="$IS_ADAPTER_REL_PATH"
317
+ case "/$agents_md" in
318
+ */.agents/*) ;;
319
+ */AGENTS.md)
320
+ if ! claude_md_above "$IP_ROOT" && [ ! -e "$IP_ROOT/$claude_out/CLAUDE.md" ] \
321
+ && ! claude_md_excluded "$IP_ROOT/$agents_md" \
322
+ "$IP_ROOT/$claude_out/settings.json" "$IP_ROOT/$claude_out/settings.local.json"; then
323
+ note "Claude Code 2.1.277 and later reads $agents_md when there is no CLAUDE.md, $claude_out/CLAUDE.md or CLAUDE.local.md, so the always-on rules load twice: from $claude_out/rules/ and from $agents_md. Sync never edits settings; put this key in $claude_out/settings.json:"
324
+ printf ' { "claudeMdExcludes": ["**/%s"] }\n' "$agents_md"
325
+ fi
326
+ ;;
327
+ esac
328
+ fi
329
+
207
330
  # A local rule, agent or skill is one these patterns name (decision 0021): the
208
331
  # engine keeps it out of every committed output, and without the pattern Git
209
332
  # would still offer to commit the file itself.
package/engine/ENGINE_SHA CHANGED
@@ -1 +1 @@
1
- 6878d4ce559755d55f90ae23296678333e95f775
1
+ 6dcb00c874ff7a12df5b838d3391f2dd96b06df0
package/engine/VERSION CHANGED
@@ -1 +1 @@
1
- 0.24.0
1
+ 0.25.1
@@ -34,6 +34,17 @@ agents_md_module_rel() {
34
34
  printf '%s' "${IS_MODULE_REL:-.intelligence/packages/@ainova-systems/sync}"
35
35
  }
36
36
 
37
+ # agents_md_link_var <repo_root> <path>: the link target AGENTS.md gives an
38
+ # artifact in IS_REPO_REL, "" for its bare name. The package store is ignored,
39
+ # so a link into .intelligence/ is dead in every clone before a sync and on the
40
+ # forge: store content is listed by name, like content outside the root.
41
+ agents_md_link_var() {
42
+ repo_rel_link_var "$1" "$2"
43
+ case "$IS_REPO_REL" in
44
+ .intelligence/*) IS_REPO_REL="" ;;
45
+ esac
46
+ }
47
+
37
48
  # Append the static header block from config.yaml (or a fallback).
38
49
  agents_md_append_header() {
39
50
  local output="$1"
@@ -73,7 +84,7 @@ agents_md_append_agents_table() {
73
84
  while IFS=$'\x1f' read -r path tier access desc; do
74
85
  name="${names[i]}"
75
86
  i=$((i + 1))
76
- repo_rel_link_var "$repo_root" "$path"
87
+ agents_md_link_var "$repo_root" "$path"
77
88
  if [ -n "$IS_REPO_REL" ]; then
78
89
  rows+="| [$name]($IS_REPO_REL) | ${tier:--} | ${access:--} | ${desc:--} |"$'\n'
79
90
  else
@@ -118,7 +129,7 @@ agents_md_append_skills_table() {
118
129
  while IFS=$'\x1f' read -r path desc; do
119
130
  name="${names[i]}"
120
131
  i=$((i + 1))
121
- repo_rel_link_var "$repo_root" "$path"
132
+ agents_md_link_var "$repo_root" "$path"
122
133
  if [ -n "$IS_REPO_REL" ]; then
123
134
  rows+="| [$name]($IS_REPO_REL) | ${desc:--} |"$'\n'
124
135
  else
@@ -162,7 +173,7 @@ agents_md_append_rules_list() {
162
173
  else
163
174
  global_rule_files+=("$path")
164
175
  fi
165
- repo_rel_link_var "$repo_root" "$path"
176
+ agents_md_link_var "$repo_root" "$path"
166
177
  if [ -n "$IS_REPO_REL" ]; then
167
178
  lines+="- [$name]($IS_REPO_REL) ($scope)"$'\n'
168
179
  else
@@ -88,12 +88,10 @@ sync_codex_override() {
88
88
  i=$((i + 1))
89
89
  done <<< "$rows"
90
90
  fi
91
+ # Only the Codex section is finalized: AGENTS.md already is, and a second
92
+ # pass would expand the tokens its rules escaped.
93
+ local section=""
91
94
  {
92
- if [ -f "$agents_file" ]; then
93
- cat "$agents_file"
94
- else
95
- printf '%s\n\n' "$CODEX_OVERRIDE_MARKER"
96
- fi
97
95
  echo "---"
98
96
  echo ""
99
97
  echo "## Codex Project Context"
@@ -108,6 +106,15 @@ sync_codex_override() {
108
106
  [ "${#files[@]}" -eq 0 ] || emit_rule_bodies "${files[@]}"
109
107
  } > "$override"
110
108
  finalize_output_file "$override"
109
+ IFS= read -r -d '' section < "$override" || true
110
+ {
111
+ if [ -f "$agents_file" ]; then
112
+ cat "$agents_file"
113
+ else
114
+ printf '%s\n\n' "$CODEX_OVERRIDE_MARKER"
115
+ fi
116
+ printf '%s' "$section"
117
+ } > "$override"
111
118
  for ((i = 0; i < ${#names[@]}; i++)); do
112
119
  echo " rule: ${names[i]} (AGENTS.override.md)"
113
120
  done
@@ -39,8 +39,9 @@ adapter_contract_pi() {
39
39
 
40
40
  # Fork-free TS string escape: sets IS_PI_TS_ESCAPED.
41
41
  pi_ts_escape_var() {
42
- local s="$1"
43
- s="${s//\\/\\\\}"
42
+ local s
43
+ token_keep_var "$1"
44
+ s="${IS_TOKEN_KEPT//\\/\\\\}"
44
45
  s="${s//\"/\\\"}"
45
46
  s="${s//$'\r'/}"
46
47
  s="${s//$'\n'/ }"
@@ -109,7 +110,7 @@ pi_rule_body_rows() {
109
110
  /^---$/ && !past_fm { in_fm = !in_fm; if (!in_fm) past_fm = 1; next }
110
111
  !past_fm { next }
111
112
  {
112
- line = is_repl($0, "\\", "\\\\")
113
+ line = is_repl(fin_keep($0), "\\", "\\\\")
113
114
  line = is_repl(line, "\"", "\\\"")
114
115
  line = is_repl(line, "\t", "\\t")
115
116
  B[++n] = line
@@ -18,16 +18,34 @@ normalize_file_to_lf() {
18
18
  #
19
19
  # The content directory is named by the project (`intelligence/`,
20
20
  # `Intelligence/`, a codename), so package-shipped artifacts cannot hardcode
21
- # it. Layout-sensitive paths and commands use tokens expanded at output time:
21
+ # it. Layout-sensitive paths and commands use tokens expanded at output time,
22
+ # in EVERY generated file, frontmatter and body alike, so a scoped rule reaches
23
+ # Claude's `paths:`, Cursor's `globs:` and Copilot's `applyTo:` already
24
+ # carrying the project's real folder name.
22
25
  #
23
- # <content-dir> -> the repo-relative project content dir (e.g. `Intelligence`)
24
- # <module> -> the repo-relative sync package store path
25
- #
26
- # Values come from IS_CONTENT_REL / IS_MODULE_REL, which the CLI derives from
27
- # the detected layout (never hardcoded) and exports before any adapter runs.
28
- # Expansion happens in EVERY generated file, frontmatter and body alike, so a
29
- # scoped rule reaches Claude's `paths:`, Cursor's `globs:` and Copilot's
30
- # `applyTo:` already carrying the project's real folder name.
26
+ # IS_LAYOUT_TOKENS is the one declaration of them, a `<name> <variable>
27
+ # <default>` row per token: `<name>` renders as the value of <variable>, which
28
+ # the CLI derives from the detected layout (never hardcoded) and exports
29
+ # before any adapter runs, or as <default>, the rest of the row, while that
30
+ # variable is unset or empty. Expansion (fin_line), the backslash that keeps a
31
+ # token as written (fin_keep, token_keep_var) and the source lint that warns
32
+ # about a token reading as ordinary text (_IS_AWK_LINT) all derive from these
33
+ # rows, so a new token is one row here plus its entry in the reference tables
34
+ # (packages/sync/references/conventions.md and adapters.md). A name is
35
+ # lowercase letters, digits and `-`: it is spliced into awk text and a Bash
36
+ # pattern.
37
+ IS_LAYOUT_TOKENS=(
38
+ 'content-dir IS_CONTENT_REL intelligence'
39
+ 'module IS_MODULE_REL .intelligence/packages/@ainova-systems/sync'
40
+ 'manifest IS_MANIFEST_NAME intelligence.yaml'
41
+ 'sync-cmd IS_SYNC_CMD intelligence sync'
42
+ )
43
+ IS_LAYOUT_NAMES=()
44
+ _is_names=""
45
+ for _is_row in "${IS_LAYOUT_TOKENS[@]}"; do
46
+ IS_LAYOUT_NAMES+=("${_is_row%% *}")
47
+ _is_names="$_is_names${_is_names:+ }${_is_row%% *}"
48
+ done
31
49
 
32
50
  # Process spawns dominate sync time on Git Bash for Windows: one fork costs
33
51
  # tens of milliseconds against ~1ms on Linux, so a helper that runs awk per
@@ -39,8 +57,10 @@ normalize_file_to_lf() {
39
57
  #
40
58
  # fin_line() uses literal (index-based) substitution, not gsub: a regex
41
59
  # replacement would give `&` in a path its special meaning, and POSIX awk has
42
- # no way to pass a replacement string verbatim. Token values arrive via the
43
- # -v args produced by is_fin_awk_vars.
60
+ # no way to pass a replacement string verbatim. The token names are part of
61
+ # the program text, so fin_keep works in every program that includes this
62
+ # library; the values are read in BEGIN from the environment variables that
63
+ # is_fin_awk_vars fills and names in IS_FIN_V.
44
64
  IS_AWK_LIB='
45
65
  function is_repl(s, from, to, out, i) {
46
66
  out = ""
@@ -50,11 +70,36 @@ IS_AWK_LIB='
50
70
  }
51
71
  return out s
52
72
  }
53
- function fin_line(s) {
54
- s = is_repl(s, "<sync-cmd>", FIN_SC)
55
- s = is_repl(s, "<manifest>", FIN_MF)
56
- s = is_repl(s, "<module>", FIN_MOD)
57
- s = is_repl(s, "<content-dir>", FIN_CONTENT)
73
+ # FIN_TOK[k] is the k-th token of IS_LAYOUT_TOKENS, FIN_VAL[k] its value,
74
+ # FIN_ESC[k] the token escaped and FIN_HOLD[k] what fin_keep turns that into.
75
+ BEGIN {
76
+ FIN_N = split("'"$_is_names"'", is_tn, " ")
77
+ for (is_tk = 1; is_tk <= FIN_N; is_tk++) {
78
+ FIN_TOK[is_tk] = "<" is_tn[is_tk] ">"
79
+ FIN_ESC[is_tk] = "\\" FIN_TOK[is_tk]
80
+ FIN_HOLD[is_tk] = "\034" is_tn[is_tk] ">"
81
+ if (FIN_ENV != "") FIN_VAL[is_tk] = ENVIRON[FIN_ENV is_tk]
82
+ }
83
+ }
84
+ # A backslash keeps a token as written: `\<module>` renders as `<module>`.
85
+ # The escaped token waits on a control character while the others expand.
86
+ # A writer that doubles backslashes for YAML or TOML calls fin_keep (or
87
+ # token_keep_var) first: once escaped, `\\<module>` no longer reads as one.
88
+ function fin_keep(s, k) {
89
+ if (!index(s, "\\<")) return s
90
+ for (k = 1; k <= FIN_N; k++) s = is_repl(s, FIN_ESC[k], FIN_HOLD[k])
91
+ return s
92
+ }
93
+ # Every line of every rendered file passes here, and most hold no `<`, so
94
+ # those skip the token loop. Tokens expand from the last declared to the
95
+ # first, the order the engine has always used: a value that itself holds a
96
+ # token renders as it always did.
97
+ function fin_line(s, k) {
98
+ if (index(s, "<")) {
99
+ s = fin_keep(s)
100
+ for (k = FIN_N; k > 0; k--) s = is_repl(s, FIN_TOK[k], FIN_VAL[k])
101
+ }
102
+ if (index(s, "\034")) s = is_repl(s, "\034", "<")
58
103
  return s
59
104
  }
60
105
  function fm_value_strip(val, n, first, last) {
@@ -112,17 +157,28 @@ IS_AWK_LIB='
112
157
  return 1
113
158
  }
114
159
  '
115
-
116
- # is_fin_awk_vars — fill the global IS_FIN_V array with the -v bindings the
117
- # IS_AWK_LIB fin_line() function needs. Rebuilt on every call: the IS_* env
118
- # contract is exported after this file is sourced.
160
+ unset _is_row _is_names
161
+
162
+ # is_fin_awk_vars — export each token's value as IS_LAYOUT_VALUE_<k>, <k> its
163
+ # row in IS_LAYOUT_TOKENS, and set the global IS_FIN_V to the -v binding that
164
+ # points IS_AWK_LIB at those variables; a program run without IS_FIN_V
165
+ # expands every token to nothing, as it always did. The values travel through
166
+ # the environment, which awk reads verbatim: a -v value has its backslashes
167
+ # read as escapes, and BSD awk refuses one that holds a newline. One variable
168
+ # per token, so no byte a value holds can split it into two. Rebuilt on every
169
+ # call: the IS_* env contract is exported after this file is sourced, and a
170
+ # caller may set one variable for a single call.
119
171
  is_fin_awk_vars() {
120
- IS_FIN_V=(
121
- -v "FIN_SC=${IS_SYNC_CMD:-intelligence sync}"
122
- -v "FIN_MF=${IS_MANIFEST_NAME:-intelligence.yaml}"
123
- -v "FIN_MOD=${IS_MODULE_REL:-.intelligence/packages/@ainova-systems/sync}"
124
- -v "FIN_CONTENT=${IS_CONTENT_REL:-intelligence}"
125
- )
172
+ local row rest var def k=0
173
+ for row in "${IS_LAYOUT_TOKENS[@]}"; do
174
+ rest="${row#* }"
175
+ var="${rest%% *}"
176
+ def=""
177
+ case "$rest" in *' '*) def="${rest#* }" ;; esac
178
+ k=$((k + 1))
179
+ export "IS_LAYOUT_VALUE_$k=${!var:-$def}"
180
+ done
181
+ IS_FIN_V=(-v FIN_ENV=IS_LAYOUT_VALUE_)
126
182
  }
127
183
 
128
184
  # finalize_output_files <file>...
@@ -288,12 +344,28 @@ frontmatter_index() {
288
344
  ' "$@"
289
345
  }
290
346
 
347
+ # token_keep_var <s> — fin_keep for the shell escapers below, whose output
348
+ # fin_line finalizes later: an escaped layout token gives up its backslash
349
+ # before they double backslashes. Sets IS_TOKEN_KEPT.
350
+ token_keep_var() {
351
+ local s="$1" fs=$'\034' t
352
+ case "$s" in
353
+ *'\<'*)
354
+ for t in "${IS_LAYOUT_NAMES[@]}"; do
355
+ s="${s//\\<$t>/$fs$t>}"
356
+ done
357
+ ;;
358
+ esac
359
+ IS_TOKEN_KEPT="$s"
360
+ }
361
+
291
362
  # Escape a string for safe interpolation into a TOML basic string ("..").
292
363
  # Backslash and double-quote are escaped; control chars stripped.
293
364
  # Fork-free form: sets IS_TOML_ESCAPED; the printing form wraps it.
294
365
  toml_escape_var() {
295
- local s="$1"
296
- s="${s//\\/\\\\}"
366
+ local s
367
+ token_keep_var "$1"
368
+ s="${IS_TOKEN_KEPT//\\/\\\\}"
297
369
  s="${s//\"/\\\"}"
298
370
  # Strip any literal newline / carriage return — TOML basic strings
299
371
  # do not allow them; multi-line content belongs in `"""..."""`.
@@ -310,8 +382,9 @@ toml_escape() {
310
382
  # Escape a string for safe interpolation into a YAML double-quoted scalar.
311
383
  # Fork-free form: sets IS_YAML_ESCAPED; the printing form wraps it.
312
384
  yaml_dq_escape_var() {
313
- local s="$1"
314
- s="${s//\\/\\\\}"
385
+ local s
386
+ token_keep_var "$1"
387
+ s="${IS_TOKEN_KEPT//\\/\\\\}"
315
388
  s="${s//\"/\\\"}"
316
389
  s="${s//$'\n'/ }"
317
390
  s="${s//$'\r'/}"
@@ -469,7 +542,7 @@ emit_wrapped_bodies() {
469
542
  else if (!past_fm) next
470
543
  line = $0
471
544
  if (ESC[cur] == "toml") {
472
- line = is_repl(line, "\\", "\\\\")
545
+ line = is_repl(fin_keep(line), "\\", "\\\\")
473
546
  line = is_repl(line, "\"\"\"", "\"\"\\\"")
474
547
  }
475
548
  body[++body_n] = line
@@ -514,13 +587,15 @@ emit_rule_bodies() {
514
587
  # When wrapping an unquoted value, literal `\` and `"` inside it are escaped
515
588
  # (`\\`, `\"`) so an inner quote — e.g. `Use as a quick "what do we have" view`
516
589
  # — cannot prematurely terminate the generated double-quoted scalar. Values the
517
- # author already wrapped (in `"` or `'`) pass through untouched.
590
+ # author already wrapped (in `"` or `'`) pass through untouched. An escaped
591
+ # layout token is resolved first (fin_keep), so <dst> still needs
592
+ # finalize_output_file, as every emitted file does.
518
593
  #
519
594
  # Usage: copy_md_with_quoted_frontmatter "src.md" "dst.md"
520
595
  copy_md_with_quoted_frontmatter() {
521
596
  local src="$1"
522
597
  local dst="$2"
523
- awk '
598
+ awk "$IS_AWK_LIB"'
524
599
  function yamlq(s, out, i, c) {
525
600
  out = ""
526
601
  for (i = 1; i <= length(s); i++) {
@@ -550,7 +625,7 @@ copy_md_with_quoted_frontmatter() {
550
625
  first = substr(val, 1, 1)
551
626
  last = substr(val, length(val), 1)
552
627
  if ((first == "\"" && last == "\"") || (first == "\047" && last == "\047")) { print; next }
553
- print key ": \"" yamlq(val) "\""
628
+ print key ": \"" yamlq(fin_keep(val)) "\""
554
629
  next
555
630
  }
556
631
  state == "after" { print }
@@ -832,7 +907,7 @@ _skill_bundles_flush() {
832
907
  first = substr(val, 1, 1)
833
908
  last = substr(val, length(val), 1)
834
909
  if (!((first == "\"" && last == "\"") || (first == "\047" && last == "\047"))) {
835
- $0 = key ": \"" yamlq(val) "\""
910
+ $0 = key ": \"" yamlq(fin_keep(val)) "\""
836
911
  }
837
912
  }
838
913
  }
@@ -1072,6 +1147,9 @@ enforce_authored_invocation_policies() {
1072
1147
  # warning names the source exactly once however many tools render it. Unlike
1073
1148
  # the lint notes it is an unindented `WARNING:` line, because `sync --compact`
1074
1149
  # (and so `init`) keeps only those (decision 0015).
1150
+ # This form takes no repository root, so its warnings name each file as the
1151
+ # caller passed it; the engine's pass, source_frontmatter_scan, names it
1152
+ # relative to the root.
1075
1153
  # Usage: lint_frontmatter_files "a.md" "b.md" ...
1076
1154
  lint_frontmatter_files() {
1077
1155
  [ "$#" -gt 0 ] || return 0
@@ -1080,7 +1158,10 @@ lint_frontmatter_files() {
1080
1158
 
1081
1159
  # The lint rules, shared with source_frontmatter_scan, which runs them in the
1082
1160
  # same pass that reads routing fields. Every rule may end with `next`, so a
1083
- # program that joins them puts its own rules first.
1161
+ # program that joins them puts its own rules first. Every warning names the
1162
+ # source as lint_src: relative to ENVIRON["IS_LINT_ROOT"] when the file lies
1163
+ # under it, so a warning reads the same from any checkout, from the private
1164
+ # copy `sync --check` renders in, and in the replay of an unchanged sync.
1084
1165
  _IS_AWK_LINT='
1085
1166
  BEGIN {
1086
1167
  nl = split(levels, LV, " ")
@@ -1089,9 +1170,40 @@ _IS_AWK_LINT='
1089
1170
  LEVEL[LV[li]] = 1
1090
1171
  allowed = allowed (li > 1 ? ", " : "") LV[li]
1091
1172
  }
1173
+ lint_root = ENVIRON["IS_LINT_ROOT"]
1174
+ }
1175
+ # A layout token that cannot mean what it renders as: joined to a word
1176
+ # or a path around it, or an XML element closed on the same line. A
1177
+ # module-name placeholder `<module>_update_N` rendered as the package
1178
+ # path. Packages use tokens by design, so only project sources (under
1179
+ # the root, outside its package store) are read, and an escaped token
1180
+ # (`\<module>`) is literal. The tokens are FIN_TOK from IS_AWK_LIB.
1181
+ function tok_check(l, k, t, n, at, p, b, a) {
1182
+ for (k = 1; k <= FIN_N; k++) {
1183
+ t = FIN_TOK[k]; n = length(t); at = 0
1184
+ while ((p = index(substr(l, at + 1), t)) > 0) {
1185
+ at += p
1186
+ b = (at > 1) ? substr(l, at - 1, 1) : ""
1187
+ a = substr(l, at + n, 2)
1188
+ at += n - 1
1189
+ if (b == "\\") continue
1190
+ if (b ~ /[[:alnum:]_]/ || b == "/" || a ~ /^([[:alnum:]_-]|[.][[:alnum:]_])/ \
1191
+ || index(l, "</" substr(t, 2))) {
1192
+ printf "WARNING: %s:%d %s is part of the text around it, but sync renders this layout token as %s; write \\%s to keep it as written\n", \
1193
+ lint_src, FNR, t, FIN_VAL[k], t > "/dev/stderr"
1194
+ break
1195
+ }
1196
+ }
1197
+ }
1198
+ }
1199
+ FNR == 1 {
1200
+ in_fm = 0; done = 0; effort_seen = 0
1201
+ lint_under = lint_root != "" && index(FILENAME, lint_root "/") == 1
1202
+ lint_src = lint_under ? substr(FILENAME, length(lint_root) + 2) : FILENAME
1203
+ tok_own = lint_under && index(FILENAME, lint_root "/.intelligence/") != 1
1092
1204
  }
1093
- FNR == 1 { in_fm = 0; done = 0; effort_seen = 0 }
1094
1205
  { sub(/\r$/, "") }
1206
+ tok_own && index($0, ">") { tok_check($0) }
1095
1207
  done { next }
1096
1208
  FNR == 1 && $0 != "---" { done = 1; next }
1097
1209
  FNR == 1 { in_fm = 1; next }
@@ -1099,11 +1211,11 @@ _IS_AWK_LINT='
1099
1211
  in_fm && substr($0, 1, 7) == "effort:" && !effort_seen++ {
1100
1212
  effort = fm_value_strip(substr($0, 8))
1101
1213
  if (effort != "" && !(effort in LEVEL)) {
1102
- printf "WARNING: %s:%d effort \"%s\" is not one of %s — ignored, so no tool receives an effort from this file\n", FILENAME, FNR, effort, allowed > "/dev/stderr"
1214
+ printf "WARNING: %s:%d effort \"%s\" is not one of %s — ignored, so no tool receives an effort from this file\n", lint_src, FNR, effort, allowed > "/dev/stderr"
1103
1215
  }
1104
1216
  }
1105
1217
  in_fm && /^\t/ {
1106
- printf "WARNING: %s:%d leading tab in frontmatter (use spaces)\n", FILENAME, FNR > "/dev/stderr"
1218
+ printf "WARNING: %s:%d leading tab in frontmatter (use spaces)\n", lint_src, FNR > "/dev/stderr"
1107
1219
  }
1108
1220
  in_fm && /^[a-zA-Z0-9_-]+:[[:space:]]+[^"\047|>[{]/ {
1109
1221
  value_start = index($0, ":") + 1
@@ -1111,10 +1223,10 @@ _IS_AWK_LINT='
1111
1223
  sub(/^[[:space:]]+/, "", value)
1112
1224
  if (value ~ /:[[:space:]]/ || value ~ /:$/) {
1113
1225
  col = index(value, ":") + value_start
1114
- printf "WARNING: %s:%d unquoted colon in value at column %d — wrap value in quotes\n", FILENAME, FNR, col > "/dev/stderr"
1226
+ printf "WARNING: %s:%d unquoted colon in value at column %d — wrap value in quotes\n", lint_src, FNR, col > "/dev/stderr"
1115
1227
  }
1116
1228
  if (value ~ /"/) {
1117
- printf "WARNING: %s:%d literal double quote in unquoted value — wrap value in single quotes or escape as \\\" so strict-YAML targets accept it\n", FILENAME, FNR > "/dev/stderr"
1229
+ printf "WARNING: %s:%d literal double quote in unquoted value — wrap value in single quotes or escape as \\\" so strict-YAML targets accept it\n", lint_src, FNR > "/dev/stderr"
1118
1230
  }
1119
1231
  }
1120
1232
  # Field-length limits. Both Claude Code and the Agent Skills standard
@@ -1133,7 +1245,7 @@ _IS_AWK_LINT='
1133
1245
  }
1134
1246
  limit = (key == "name") ? 64 : 1024
1135
1247
  if (length(val) > limit) {
1136
- printf "WARNING: %s:%d %s is %d chars — over the %d-char limit; the skill/agent will be REJECTED at load time\n", FILENAME, FNR, key, length(val), limit > "/dev/stderr"
1248
+ printf "WARNING: %s:%d %s is %d chars — over the %d-char limit; the skill/agent will be REJECTED at load time\n", lint_src, FNR, key, length(val), limit > "/dev/stderr"
1137
1249
  }
1138
1250
  }
1139
1251
  '
@@ -2442,7 +2554,7 @@ source_catalog_build() {
2442
2554
  # One pass reads what routing needs from every source's frontmatter
2443
2555
  # and, for the engine, lints it. Captured, not streamed: a short answer
2444
2556
  # must stop the run, not route the files it lost as always-on.
2445
- rows="$(source_frontmatter_scan "$lint" "${files[@]}")" || {
2557
+ rows="$(source_frontmatter_scan "${lint:+$repo_root}" "${files[@]}")" || {
2446
2558
  echo "ERROR: cannot read the frontmatter of the sources — refusing to render a partial list." >&2
2447
2559
  exit 1
2448
2560
  }
@@ -2466,20 +2578,23 @@ source_catalog_build() {
2466
2578
  [ "${IS_SOURCE_FILES_MEMO:-0}" != 1 ] || IS_CAT_KEY="$key"
2467
2579
  }
2468
2580
 
2469
- # source_frontmatter_scan <lint> <file>... — one row per file in argument
2581
+ # source_frontmatter_scan <lint-root> <file>... - one row per file in argument
2470
2582
  # order, \x1f-separated: the path; its `paths:` line count, read with
2471
2583
  # frontmatter_index's `paths#` semantics; then for `targets:` and for
2472
2584
  # `exclude_targets:` whether it is set and its ids, space-separated. Both take
2473
2585
  # the list syntax `paths:` takes — a block list or a flow list `[a, b]`, items
2474
2586
  # optionally quoted — and a bare value is a one-item list. The first
2475
2587
  # occurrence of a key wins, and a key with no value at all is not set
2476
- # (`targets: []` is: it is the empty list). With <lint> = lint the same pass
2477
- # runs lint_frontmatter_files over every file, its warnings on stderr.
2588
+ # (`targets: []` is: it is the empty list). With <lint-root>, the repository
2589
+ # root, the same pass runs lint_frontmatter_files over every file and the
2590
+ # layout-token check over the project's own; warnings go to stderr and name
2591
+ # each source relative to that root.
2478
2592
  source_frontmatter_scan() {
2479
- local do_lint=0
2480
- [ "$1" != lint ] || do_lint=1
2593
+ local do_lint=0 root="$1"
2594
+ [ -z "$root" ] || do_lint=1
2481
2595
  shift
2482
- awk -v levels="$IS_EFFORT_LEVELS" -v do_lint="$do_lint" "$IS_AWK_LIB"'
2596
+ is_fin_awk_vars
2597
+ IS_LINT_ROOT="$root" awk -v levels="$IS_EFFORT_LEVELS" -v do_lint="$do_lint" "${IS_FIN_V[@]}" "$IS_AWK_LIB"'
2483
2598
  BEGIN { SX_US = sprintf("%c", 31) }
2484
2599
  function sx_items(s, k, parts, m, j, v, q) {
2485
2600
  m = split(s, parts, ",")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainova-systems/intelligence",
3
- "version": "0.24.0",
3
+ "version": "0.25.1",
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"
@@ -42,7 +42,7 @@ To stop syncing a target:
42
42
  intelligence adapter disable mytool
43
43
  ```
44
44
 
45
- Disabling changes only target state. Generated output is deliberately kept so disabling is reversible; the adapter contract identifies its paths for review. A disabled project adapter can be deleted with `intelligence adapter remove mytool`; removal prompts by default, accepts `--apply` for explicit non-interactive use, and also keeps generated output. Built-in adapter source cannot be removed. Use `intelligence adapter list` to inspect source, state and output.
45
+ Disabling changes only target state. Generated output is deliberately kept so disabling is reversible; the adapter contract identifies its paths for review, and `intelligence status --check` names the owned paths still on disk and the `intelligence-manage-adapters` skill that removes them. A disabled project adapter can be deleted with `intelligence adapter remove mytool`; removal prompts by default, accepts `--apply` for explicit non-interactive use, and also keeps generated output. Built-in adapter source cannot be removed. Use `intelligence adapter list` to inspect source, state and output.
46
46
 
47
47
  ## Required interface
48
48
 
@@ -218,7 +218,7 @@ Intelligence routes always-on rules once through `AGENTS.md` for tools that cons
218
218
 
219
219
  | Built-in | Always-on rules | Always-on, not in `AGENTS.md` | Scoped rules |
220
220
  |---|---|---|---|
221
- | `agents` | Inline in `AGENTS.md` | — | List with links |
221
+ | `agents` | Inline in `AGENTS.md` | None | List with links; package and out-of-tree content by name |
222
222
  | `antigravity` | Omit; reads `AGENTS.md` | `.agents/rules/*.md` with `trigger: always_on` | `.agents/rules/*.md` with `trigger: glob` and `globs:` |
223
223
  | `claude` | Copy | Copy | Copy with `paths:` preserved |
224
224
  | `cursor` | Omit; reads `AGENTS.md` | `.mdc` with `alwaysApply: true` | `.mdc` with `globs:` |
@@ -305,7 +305,7 @@ Content shipped in `@ainova-systems/sync` cannot assume the project's content-di
305
305
  | `<manifest>` | `intelligence.yaml` |
306
306
  | `<sync-cmd>` | `intelligence sync` |
307
307
 
308
- Call `finalize_output_file` on every text file after transformation. It expands the tokens literally and converts CRLF to LF. A generated file containing an unexpanded token is an adapter bug.
308
+ Call `finalize_output_file` on every text file after transformation. It expands the tokens literally, keeps a token escaped with a backslash as written ([Layout tokens](conventions.md#layout-tokens)), and converts CRLF to LF. A generated file containing an unexpanded token is an adapter bug.
309
309
 
310
310
  `copy_skill_bundle` already finalizes Markdown within a skill; do not run a second custom token pass.
311
311
 
@@ -142,6 +142,8 @@ Expansion applies to frontmatter and bodies. Thus `paths: ["<content-dir>/**"]`
142
142
 
143
143
  Project-authored artifacts normally use their known project paths directly. Tokens are useful only when the same artifact must work under different content-directory or package-store locations.
144
144
 
145
+ The four tokens are reserved in every source, so a placeholder that means something else, such as a module name, needs a backslash: `\<module>` renders as `<module>`. Sync prints a `WARNING:` line with the file and line when a project source holds a token joined to the text around it (`<module>_update_N`, `modules/<module>.md`) or used as an XML element closed on the same line (`<manifest></manifest>`).
146
+
145
147
  ## Naming
146
148
 
147
149
  Rule filenames, agent names and skill names share a domain prefix such as `backend-`, `frontend-`, `devops-`, `core-`, `tests-`, a project codename or a monorepo component. Pick the domain from repository structure and reuse it.
@@ -251,7 +253,7 @@ Skills take the same field. `.claude/skills/` receives Claude's level, the share
251
253
 
252
254
  ### Access mappings
253
255
 
254
- `access: full` inherits ordinary tool permissions. `access: readonly` is transformed into the target's native restriction: for example Claude receives read/search/bash tools with writes disallowed, Cursor receives `readonly: true`, and Codex receives a read-only sandbox.
256
+ `access: full` inherits ordinary tool permissions. `access: readonly` is transformed into the target's native restriction, and the targets differ on commands. Claude keeps `Bash` beside its read and search tools, with `Write` and `Edit` disallowed. Cursor receives `readonly: true`, which its documentation says blocks edits and state-changing shell commands. Copilot, OpenCode and Antigravity receive read and search tools only, and Pi receives the restriction as an instruction. Codex receives `sandbox_mode = "read-only"`, which its [subagent documentation](https://learn.chatgpt.com/docs/agent-configuration/subagents) says a custom agent file cannot set, so a Codex agent runs in the parent session's sandbox.
255
257
 
256
258
  Use only `full` or `readonly` in source agents. Tool-specific permission syntax belongs in adapters.
257
259
 
@@ -283,7 +285,7 @@ Rule content in Markdown.
283
285
  | Always-on | copied | omitted | omitted | omitted | inlined once into `AGENTS.md` |
284
286
  | 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 |
285
287
 
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. Codex's override lists scoped patterns and source links with an instruction to read matching rules on demand; this depends on the model following the instruction, not native glob-triggered loading. OpenCode users may configure `instructions:` globs themselves. See [adapter routing](adapters.md#rules) for delivery details.
288
+ Cursor, Copilot, Codex, Pi, OpenCode and Antigravity consume `AGENTS.md`, so always-on rules are not duplicated in their tool-specific channels. Claude receives the full rule set in `.claude/rules/`. Claude Code 2.1.277 and later also reads `AGENTS.md` when no `CLAUDE.md`, `.claude/CLAUDE.md` or `CLAUDE.local.md` exists in the working directory or above it ([memory docs](https://code.claude.com/docs/en/memory#agents-md)), so the always-on rules load twice until `.claude/settings.json` carries `"claudeMdExcludes": ["**/AGENTS.md"]`. Sync never edits that file; `intelligence status --check` prints the entry when it is missing. Codex's override lists scoped patterns and source links with an instruction to read matching rules on demand; this depends on the model following the instruction, not native glob-triggered loading. OpenCode users may configure `instructions:` globs themselves. See [adapter routing](adapters.md#rules) for delivery details.
287
289
 
288
290
  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 scoped routing and additional always-on rules; it is ignored, exists while either needs it, 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
291
 
@@ -308,9 +310,15 @@ argument-hint: "<route-name>"
308
310
  4. Report the changed route and verification.
309
311
  ```
310
312
 
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)).
313
+ Standard optional fields (`license`, `compatibility`, `metadata`, `allowed-tools`) and tool extensions pass through unchanged. `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)).
314
+
315
+ `disable-model-invocation: true` makes a skill one only a person starts, as a slash command. The flag hides the procedure, not the act: Claude Code keeps the skill's description out of the model's context, so a request in words such as "commit this" never reaches the skill and is carried out without its checks. Keep the flag for a skill that starts a named person's decision, such as approving a plan, moving versions or changing adapters; the person typing the command after seeing what it acts on is the yes, so the skill does not ask again. A skill with side effects stays model-invocable, and its description names the requests that should reach it.
316
+
317
+ Claude Code, Cursor, Copilot, OpenCode and Pi 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:`.
312
318
 
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:`.
319
+ `context: fork`, `agent`, `background` and `model` are Claude Code fields, and sync copies them unchanged. A forked skill runs as a subagent of the `agent` type that receives the skill as its prompt, sees none of the conversation and has no `AskUserQuestion` tool. In an interactive session it runs in the background by default, so the caller's turn goes on before the result arrives, and a skill whose result its caller needs in the same turn sets `background: false` ([skills](https://code.claude.com/docs/en/skills#run-skills-in-a-subagent), [subagents](https://code.claude.com/docs/en/sub-agents#available-tools)). Without `context: fork`, `agent` selects nothing, and `model` switches the session's model for the rest of the turn, which then re-reads the whole conversation without the prompt cache ([prompt caching](https://code.claude.com/docs/en/prompt-caching#switching-models)). Of the other targets, only Copilot in VS Code documents `context: fork`, as experimental, and none documents `agent`, `background` or `model` for a skill (checked 2026-11-07).
320
+
321
+ Fork a skill whose inputs are files and arguments, that asks nothing on the way, and whose work is heavy reading, auditing or judging: the caller's context stays small and the work gets an independent reader. Keep a skill inline, with no `agent`, when its input is the conversation or its job is a dialogue or an approval with the person. A forked skill never asks. When it reaches a decision that belongs to the person, it stops before the write and ends its result with the decision, the options and its recommendation, so the caller asks.
314
322
 
315
323
  These limits reject a skill instead of degrading it:
316
324
 
@@ -351,14 +359,15 @@ The engine copies the complete skill directory. Promote a helper outside the ski
351
359
 
352
360
  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
361
 
354
- Size limits are backstops, not quotas:
362
+ Size limits are backstops, not quotas. They count words, because a paragraph written as one long line passes any line cap:
355
363
 
356
- | Artifact | Hard cap | Response |
364
+ | Artifact | Budget | Response |
357
365
  |---|---|---|
358
- | `SKILL.md` body | 1000 lines | Move detail to `references/` |
359
- | Reference file | 500 lines | Add a contents list past 300; split if still oversized |
360
- | Rule | 500 lines | Split by scope or move examples behind a skill reference |
361
- | Agent | 200 lines | Move procedures and constraints into skills/rules |
366
+ | `SKILL.md` | 1,500 words | Move detail to `references/` |
367
+ | Reference file | 3,000 words | Add a contents list past 1,500; split if still oversized |
368
+ | Rule | 2,000 words | Split by scope or move examples behind a skill reference |
369
+ | Agent | 500 words | Move procedures and constraints into skills/rules |
370
+ | Paragraph, list item or table row | 150 words | Split it into the thoughts it holds |
362
371
 
363
372
  ## Writing discipline
364
373
 
@@ -448,6 +457,8 @@ targets:
448
457
 
449
458
  `AGENTS.md` stays committed by default: it is the tool-neutral fallback every tool reads, and a fresh clone has it only when it is committed, before anyone has synced.
450
459
 
460
+ Claude Code loads project skills from the checkout a session starts in, and from 2.1.277 a Git worktree without its own `.claude/skills`, such as one not yet synced, gets the main checkout's ([skills docs](https://code.claude.com/docs/en/skills#load-skills-in-monorepos-and-subdirectories)), so a task in a worktree runs in a session started there after `intelligence sync` there.
461
+
451
462
  Git tracking and release packaging are separate policies. When a project
452
463
  already has `.vscodeignore`, `.npmignore`, or `.dockerignore`, the CLI appends a
453
464
  small idempotent block excluding the package store, `intelligence.yaml`,
@@ -21,7 +21,7 @@ Three ways to shorten, in order of what they are worth:
21
21
  2. **Delete what the code already says.** A rule restating what a reader can see in the file is noise, and noise trains people to skim the lines that are not.
22
22
  3. **Cut the words, keep the reason.** Prose can shrink; the *why* cannot — an instruction without its reason does not survive contact with a judgement call.
23
23
 
24
- **Never solve a problem by adding an artifact when moving one, or removing one, would do.** A new rule is the most expensive answer available, and it is the one that feels cheapest to write.
24
+ **Never solve a problem by adding an artifact when moving one, or removing one, would do.** A new rule is the most expensive answer available, and it is the one that feels cheapest to write. The same holds inside an artifact: a fix restates the outcome once, and a clause added for each edge case or review finding is itself the defect.
25
25
 
26
26
  ## Source of truth
27
27
 
@@ -60,7 +60,7 @@ The mistakes that actually happen, in order of frequency:
60
60
 
61
61
  ### Invariants
62
62
 
63
- - **Never state behaviour of a tool or engine you have not verified in its documentation or source.** An unverified claim about tooling is worse than a gap: nothing in the repository contradicts it, so it silently reshapes every decision downstream. Verified means a page you can cite - for how rules load, the Claude Code docs *Memory → Organize rules with `.claude/rules/`* and *Subagents → What loads at startup*.
63
+ - **Never state behaviour of a tool or engine you have not verified in its documentation or source.** An unverified claim about tooling is worse than a gap: nothing in the repository contradicts it, so it silently reshapes every decision downstream. Verified means a page you can cite - for how rules load, the Claude Code [memory](https://code.claude.com/docs/en/memory#organize-rules-with-claude/rules/) and [subagent startup](https://code.claude.com/docs/en/sub-agents#what-loads-at-startup) docs.
64
64
  - **Never write a current defect into a rule as if it were the design.** Known breakage belongs in one place that says so. Every other rule describes the project *as it is meant to work* — a workaround documented as procedure becomes permanent.
65
65
  - **Never link from one always-on rule to another.** Always-on rules are inlined verbatim into `AGENTS.md`, and the path-scoped channels carry only scoped rules, so a relative link is dead in at least one output. Name the rule instead; it loads on its own.
66
66
 
@@ -68,7 +68,7 @@ The mistakes that actually happen, in order of frequency:
68
68
 
69
69
  An agent is **thin**: who it is, where it stops, how it verifies. Nothing else.
70
70
 
71
- - **Do not list rules for an agent to read.** They load on their own — Claude Code loads `.claude/rules/` into every custom subagent's startup context, and Cursor, Copilot, Codex, Pi, OpenCode and Antigravity receive always-on rules inlined in `AGENTS.md`. Naming a rule is fine; copying it is duplication that drifts.
71
+ - **Do not list rules for an agent to read.** They load on their own: a Claude Code subagent starts with the project's instruction files, `.claude/rules/` included, unless it is the built-in Explore or Plan agent or its definition sets `omitClaudeMd`, and Cursor, Copilot, Codex, Pi, OpenCode and Antigravity receive always-on rules inlined in `AGENTS.md`. A step handed to an agent that starts without them names the rules it depends on in the delegation message. Naming a rule is fine; copying it is duplication that drifts.
72
72
  - **Point at a rule, do not restate it.** Wanting to copy a rule into an agent means the rule is in the wrong place — move it.
73
73
  - **Do carry** what is genuinely the agent's own: its boundaries ("if the app will not start, stop — do not hand-write the output"), its verification commands, its definition of done.
74
74
 
@@ -95,15 +95,16 @@ Two verbs are told apart by what already exists. **`add-` puts one new member in
95
95
 
96
96
  ## Size — the backstop, not the goal
97
97
 
98
- The goal is subtraction, above. These are only the line past which something is definitely wrong.
98
+ The goal is subtraction, above. These are only the line past which something is definitely wrong. They count words, because a line cap never sees a paragraph written as one long line.
99
99
 
100
- | Type | Hard cap |
100
+ | Type | Budget |
101
101
  |---|---|
102
- | Rule | 500 lines |
103
- | Agent | 200 lines |
104
- | `SKILL.md` | 1000 lines |
102
+ | Rule | 2,000 words |
103
+ | Agent | 500 words |
104
+ | `SKILL.md` | 1,500 words |
105
+ | Paragraph, list item or table row | 150 words |
105
106
 
106
- **Ceilings, not quotas.** An artifact that says everything it needs to is finished, not underweight — nothing here is a reason to pad. Over the cap means the artifact is doing two jobs, or the detail belongs in `references/<topic>.md` with a pointer from the body.
107
+ **Ceilings, not quotas.** An artifact that says everything it needs to is finished, not underweight; nothing here is a reason to pad. Over the budget means the artifact is doing two jobs, or the detail belongs in `references/<topic>.md` with a pointer from the body, and a paragraph over it holds several thoughts to split.
107
108
 
108
109
  `description` fields compete for one shared budget across the whole registry: a long one pushes another artifact out of reach. Four to eight words when the name is unambiguous; longer only when a sibling does something similar and needs distinguishing. (The tools reject a `description` over 1024 characters outright — but that is a wall to stay far away from, not a target.)
109
110
 
@@ -104,10 +104,13 @@ explains itself well.
104
104
  back. Completion requires
105
105
  `IS_STATUS=ok`, a clean final check, and no `onboarding is pending` header
106
106
  after accepted migration.
107
- 11. Report what was created, updated, removed, or deliberately kept. Remind the
108
- user to commit source, manifest, lock and `AGENTS.md` changes, plus Copilot's
109
- `.github/` output only when `targets.copilot.commit_output` is `true`. Keep or
110
- remove the initial backup only by separate user approval.
107
+ 11. Report what was created, updated, removed, or deliberately kept. A
108
+ `status --check` note that names a tool settings change is the owner's step:
109
+ show it with the exact JSON it prints and leave the settings file to the
110
+ owner. Remind the user to commit source, manifest, lock and `AGENTS.md`
111
+ changes, plus Copilot's `.github/` output only when
112
+ `targets.copilot.commit_output` is `true`. Keep or remove the initial backup
113
+ only by separate user approval.
111
114
 
112
115
  ## Later learning
113
116
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: intelligence-learn-from-session
3
3
  description: "Capture session lessons and workflows in project context"
4
- agent: intelligence-architect
4
+ argument-hint: "[lesson, or a folder of lesson records]"
5
5
  ---
6
6
 
7
7
  # Learn from a session
@@ -9,6 +9,8 @@ agent: intelligence-architect
9
9
  Capture a durable preference, working pattern, recurring friction, or successful
10
10
  workflow from a session in an established Intelligence project. This skill owns
11
11
  identifying and generalizing the lesson; `intelligence-update-context` owns writing it.
12
+ A lesson is saved only through that skill, as a source every clone and tool reads,
13
+ never in a tool's own memory.
12
14
 
13
15
  ## Verify readiness
14
16
 
@@ -23,7 +25,10 @@ identifying and generalizing the lesson; `intelligence-update-context` owns writ
23
25
 
24
26
  2. Identify the lesson from the conversation or explicit user input. For a
25
27
  workflow, list the actual steps performed, user decisions, branches, failure
26
- recovery, and verification. Retain the sequence that worked.
28
+ recovery, and verification. Retain the sequence that worked. Given a folder
29
+ of lesson records, read every record against the current sources: report a
30
+ record the sources already satisfy as applied and drop it, and carry the rest
31
+ forward as lessons.
27
32
  3. Generalize the evidence by removing instance-specific filenames, dates, and
28
33
  phrasing. Keep the reason needed to apply the lesson to the next task. Prefer
29
34
  a positive instruction such as "Default to one recommendation" for "Stop
@@ -33,18 +38,26 @@ identifying and generalizing the lesson; `intelligence-update-context` owns writ
33
38
  4. Inspect configured sources for an existing owner. A preference or constraint
34
39
  belongs in a rule, path-specific context in a scoped rule, an observed
35
40
  repeatable procedure in a skill, and a persona or expertise boundary in an
36
- agent. Prefer extending an existing artifact over adding a sibling.
37
- 5. Present each proposal with its action (`CREATE`, `UPDATE`, or `ARCHIVE`),
38
- source path, concrete draft, and one-line reason. This phase is read-only;
39
- only user-accepted lessons become persistent instructions. Reuse approval
40
- already given for the exact proposal.
41
+ agent. Prefer extending an existing artifact over adding a sibling. A lesson
42
+ about installed package content or the CLI itself is fixed upstream, never in
43
+ the installed copy: draft an issue for the package's source repository, which
44
+ `intelligence.lock` records, or for Intelligence. The draft holds only safe
45
+ facts: versions from `intelligence version`, what was done, the expected and
46
+ the actual behavior, and nothing confidential or specific to the project.
47
+ 5. Present each proposal with its action (`CREATE`, `UPDATE`, `ARCHIVE`, or
48
+ `UPSTREAM` for an issue draft), source path or repository, concrete draft, and
49
+ one-line reason. This phase is read-only; only user-accepted lessons become
50
+ persistent instructions. Reuse approval already given for the exact proposal.
41
51
 
42
52
  ## Apply and verify
43
53
 
44
54
  6. Pass accepted proposals and their session evidence to
45
55
  `/intelligence-update-context`. It handles artifact-specific authoring,
46
- references, a single batch sync, and the final status check.
56
+ references, a single batch sync, and the final status check. An accepted
57
+ `UPSTREAM` draft goes into the report; filing it is a separate request the
58
+ person makes.
47
59
  7. Compare the resulting source changes with the accepted lesson. For a workflow,
48
60
  verify that its decisions, working steps, recovery, and proof of completion
49
61
  remain executable without this conversation. Report the saved lesson, its
50
- owner, and verification; a session-specific transcript is not completion.
62
+ owner, and verification, the records dropped as applied, and each upstream
63
+ draft with its state; a session-specific transcript is not completion.
@@ -2,7 +2,6 @@
2
2
  name: intelligence-manage-adapters
3
3
  description: "Enable, disable, or remove adapters and assess generated-output cleanup"
4
4
  argument-hint: "<enable|disable|remove> <adapter-name>"
5
- agent: intelligence-operator
6
5
  disable-model-invocation: true
7
6
  ---
8
7
 
@@ -42,5 +41,6 @@ scaffolding, and sync. Adapter implementation follows the public adapter guide.
42
41
  5. Verify with `intelligence adapter list` and `intelligence status --check`:
43
42
  requested target state is correct, retained files are intact, and only approved
44
43
  adapter-owned output was deleted. Report state, output paths, and any remaining
45
- implementation or cleanup work. Preserve CLI refusals instead of recreating
46
- lifecycle mechanics manually.
44
+ implementation or cleanup work, with each settings change a check note names
45
+ as the owner's step and its exact JSON. Preserve CLI refusals instead of
46
+ recreating lifecycle mechanics manually.
@@ -3,33 +3,37 @@ name: intelligence-review-context
3
3
  description: "Audit rules, agents, and skills and propose behavior-preserving reductions"
4
4
  argument-hint: "[rules|agents|skills|all] [compact]"
5
5
  agent: intelligence-architect
6
+ context: fork
7
+ background: false
6
8
  ---
7
9
 
8
10
  # Review project context
9
11
 
10
- Audit the intelligence layer and propose improvements. Review and compaction
11
- analysis are read-only: do not sync or edit while inspecting. Accepted changes
12
- go through `intelligence-update-context`; this skill owns their review criteria.
12
+ Audit the intelligence layer and return proposals. Work from the arguments and
13
+ the files alone and ask nothing: in Claude Code this skill runs as a subagent that
14
+ sees none of the conversation. Review and compaction analysis are read-only, so
15
+ never sync or edit. Accepted changes go through `intelligence-update-context`;
16
+ this skill owns their review criteria.
13
17
 
14
18
  ## Audit
15
19
 
16
20
  1. Read `<manifest>` and enumerate its configured rule, agent, and skill sources.
17
21
  Read the `intelligence-authoring` rule and
18
22
  `<module>/references/conventions.md`. Review project-owned sources; an installed
19
- package finding — an entry naming a package's directory, however spelled —
23
+ package finding, an entry naming a package's directory however spelled,
20
24
  belongs upstream. In a package's own repository, review its
21
25
  authoritative source tree. Do not audit generated output prose. Its byte count
22
26
  is the metadata-only exception.
23
27
  2. Record source line and byte counts, and git history when available: first
24
28
  addition, last edit, and edit count. Find incoming references before proposing
25
29
  an archive. Resolve the shared agents output from the manifest and measure its
26
- bytes, or reuse a fresh `CONTEXT:` summary. Mark missing or stale measurements;
27
- do not regenerate output during analysis.
30
+ bytes. Mark missing or stale measurements; do not regenerate output during
31
+ analysis.
28
32
  3. Read and apply [references/audit-checks.md](references/audit-checks.md). Reuse
29
33
  these generic checks in any project-specific audit. Check subtraction before
30
34
  proposing a split or rewrite: remove an unnecessary artifact, merge overlapping
31
35
  owners, or replace prose with an existing deterministic gate where possible.
32
- 4. When the user asks to compact context, or the audit proposes a merge, move,
36
+ 4. When the arguments ask to compact context, or the audit proposes a merge, move,
33
37
  deletion, scoping change, or size reduction, read
34
38
  [references/compaction.md](references/compaction.md) and its principles. Build
35
39
  the behavior ledger and draft the structural reduction before wording changes.
@@ -39,17 +43,17 @@ go through `intelligence-update-context`; this skill owns their review criteria.
39
43
  3: description or naming polish). Compaction items also show the authoritative
40
44
  owner, behavior preserved, estimated byte savings, and any change to meaning,
41
45
  scope, or loading. Surface unverified reasons without silently rewriting them.
46
+ A finding about what an artifact says is a wrong or missing outcome, a
47
+ contradiction, or a stale reference; an edge case its outcome already covers
48
+ is no finding.
42
49
 
43
- ## Accepted changes
50
+ ## Hand the decision back
44
51
 
45
- 6. The user accepts items individually; a batch acceptance may cover named items.
46
- Pass accepted proposals to `/intelligence-update-context`, with the behavior
47
- ledger and additional checks. Reuse existing approval for those exact changes.
48
- If fresh rendered measurements are needed, capture them only after apply is
49
- authorized and before editing; record `<sync-cmd> --compact` output and require
50
- `IS_STATUS=ok`. The authoring skill performs the final batch sync and status check.
51
- 7. Verify each accepted finding against the resulting sources. For reductions,
52
- complete the ledger comparison, measurements, and behavioral evaluation in
53
- the compaction reference. Report each artifact as pass, fixed (what), or
54
- flagged (for whom), plus unresolved proposals. A read-only review ends with
55
- the evidenced punch-list; an apply run ends only after its checks complete.
52
+ 6. End the result with the decision that belongs to the person: which numbered
53
+ items to accept, one by one or as a named batch, with the items you recommend
54
+ and the reason for each. Accepted items go to `/intelligence-update-context`
55
+ with their drafts. For compaction items, include what that skill needs to
56
+ verify them: the behavior ledger, the baseline counts with any missing
57
+ rendered measurement marked, and the steps under *Verify the accepted result*
58
+ in the compaction reference, copied into the result. The review is complete
59
+ when this evidenced punch-list is returned.
@@ -6,7 +6,8 @@ Apply these checks to the source groups resolved by the review skill.
6
6
  |---|---|---|
7
7
  | **Duplicate content** | Two artifacts cover overlapping scope, or their descriptions share trigger phrases | `MERGE` — present both, propose one |
8
8
  | **Misplaced content** | A checklist or procedure in an agent body; a convention in an agent; a workflow in a rule; expertise in a skill (the *Pick the right artifact* table in `intelligence-authoring`) | `MOVE` — a move, not a rewrite: both files change together |
9
- | **Over the cap** | `SKILL.md` over 1000 lines, rule over 500, agent over 200 | `SPLIT` — two artifacts, or move detail into `references/<topic>.md` |
9
+ | **Over the budget** | `SKILL.md` over 1,500 words, rule over 2,000, agent over 500, skill reference over 3,000, or a paragraph, list item or table row over 150 (`R7`) | `SPLIT` - two artifacts, detail into `references/<topic>.md`, or a long paragraph into the thoughts it holds |
10
+ | **Rule repeats a gate** | A rule sentence restating what a script, test or sync already checks or refuses | `DELETE` - the gate holds the decision; a second copy in prose drifts from it and costs context in every session |
10
11
  | **Shared instruction budget** | The measured shared agents output, or `agents-md` in sync's `CONTEXT:` summary, is over 32 KiB (32,768 bytes) | `COMPACT` — this is Intelligence's recommended maximum, not an adapter rejection threshold; use the compaction procedure in `intelligence-review-context` to reduce the owning sources without teaching terse output |
11
12
  | **Rule links to a rule** | A markdown link from one rule to another (`R1`) | `UNLINK` — name the rule instead: always-on rules are inlined into `AGENTS.md` and the scoped channels carry only scoped rules, so the link is dead in at least one output |
12
13
  | **Machine facts in a rule** | OS, shell, editor or a local absolute path (`R2`) | `MOVE` — these belong in a local rule, `<content-dir>/rules/<name>.local.md`, which Git ignores and sync keeps out of committed output; a rule is committed and read by everyone, including whoever is on another platform |
@@ -14,7 +15,7 @@ Apply these checks to the source groups resolved by the review skill.
14
15
  | **Skill with no verification** | Nothing at the end proves the procedure worked (`R4`) | `FLAG` — a procedure that proves nothing is a note, or just the work: give it a verification, or delete it |
15
16
  | **Pressure marker** | A heading or section named CRITICAL / MANDATORY / HARD RULE / READ FIRST, or a density of `MUST` / `NEVER` / `ALWAYS` with no reason beside it (`R5`) | `REWRITE` - plain heading, one reason per constraint; when several instructions are each marked critical the marker stops carrying information, so keep emphasis for the one instruction demonstrably under-weighted without it |
16
17
  | **History narrative** | A PR number, incident id, commit SHA, date or "this session" inside a rule body (`R6`) | `REWRITE` - keep the causal sentence, drop the archaeology; a rule's authority is the behaviour it prescribes, and git history keeps the date |
17
- | **Reserved prefix** | A project artifact named `intelligence-*` | `RENAME` — the prefix belongs to the sync package and collides in generated output |
18
+ | **Name collision** | A project artifact named `intelligence-*` or like an installed package artifact it does not mean to override, or a skill named like a built-in command or bundled skill of an enabled tool ([Claude Code commands](https://code.claude.com/docs/en/commands)) | `RENAME` - the project artifact renders in the package's place, and a Claude Code skill replaces the command of its name but not that command's aliases, so one request runs different procedures depending on the name typed |
18
19
  | **Naming** | A skill that is not `<domain>-<verb>-<noun>`, or a domain invented rather than reused | `RENAME` — or introduce the new domain deliberately |
19
20
  | **Stale** | No edits in 6+ months and nothing cross-references it | `ARCHIVE` — move to `<content-dir>/_archive/` |
20
21
  | **Negative-framed judgement call** | "Never do X" where a positive default fits, outside safety / security / output-format | `REWRITE` — state the default; reserve NEVER for true must-nots |
@@ -23,6 +24,7 @@ Apply these checks to the source groups resolved by the review skill.
23
24
  | **Weak / duplicate description** | Identical to a sibling, or too vague to choose between them | `DIFFERENTIATE` — add the distinguishing trigger |
24
25
  | **Description over budget** | Over ~250 characters (the shared registry budget); over **1024** the tools reject the artifact outright | `TRIM` — keep the distinguishing trigger, drop the rest |
25
26
  | **Missing frontmatter field** | `name` or `description` absent | `PATCH` — add it |
27
+ | **Dead skill field** | `agent:` in a `SKILL.md` without `context: fork` (`R8`) | `PATCH` - delete `agent:`, which selects nothing without the fork, or fork the skill when the authoring conventions say it should |
26
28
  | **Orphan rule** | Nothing points at it and nothing loads it | `FLAG` — intentional, or dead? |
27
29
 
28
30
  Detection commands, portable on purpose — no `\b` and no `-P`, so the same command works in Git Bash on Windows, on macOS (BSD grep) and on Linux. Run each over the source directories resolved in step 2, never over generated output:
@@ -51,6 +53,28 @@ grep -rcE '(MUST|NEVER|ALWAYS)' <rule-dirs> <agent-dirs> <skill-dirs> | grep -vE
51
53
  # R6 - history narrative in a rule body: PR numbers, incident ids, dates, "this session", commit SHAs
52
54
  grep -rniE -e '(#|PR )[0-9]{3,}' -e '[0-9]{4}-[0-9]{2}-[0-9]{2}' -e 'this session|origin session' \
53
55
  -e '`[0-9a-f]{7,40}`' <rule-dirs>
56
+
57
+ # R7 - words per file, then the longest paragraph; a list item or table row is its own paragraph
58
+ find <rule-dirs> <agent-dirs> <skill-dirs> -name '*.md' | while IFS= read -r f; do
59
+ awk -v f="$f" 'BEGIN { fence = sprintf("%c%c%c", 96, 96, 96) }
60
+ NR == 1 && /^---[ \t\r]*$/ { fm = 1; next }
61
+ fm { if (/^---[ \t\r]*$/) fm = 0; next }
62
+ { w += NF }
63
+ index($1, fence) == 1 { code = !code; p = 0; next }
64
+ code || NF == 0 { p = 0; next }
65
+ /^[ \t]*(#|[-*] |[0-9]+\. |\|)/ { p = 0 }
66
+ { p += NF; if (p > max) max = p }
67
+ END { printf "%6d %4d %s\n", w, max, f }' "$f"
68
+ done | sort -rn
69
+
70
+ # R8 - a skill's agent: without context: fork, read from the frontmatter only
71
+ find <skill-dirs> -name SKILL.md | while IFS= read -r f; do
72
+ awk -v f="$f" 'NR == 1 && /^---[ \t\r]*$/ { fm = 1; next }
73
+ fm && /^---[ \t\r]*$/ { exit }
74
+ fm && /^agent:/ { a = 1 }
75
+ fm && /^context:[ \t]*"?fork/ { c = 1 }
76
+ END { if (a && !c) print f }' "$f"
77
+ done
54
78
  ```
55
79
 
56
80
  `R4` is a coarse net, not a verdict: a skill that merely *mentions* a verification command anywhere passes it. Read the final step of every skill regardless — the question is whether something at the end **proves the work landed**, not whether the word appears. So are `R5` and `R6`: a `NEVER` that carries its reason and a date inside a frontmatter template are both legitimate hits, and the row's action applies only where the marker or the id is doing no work.
@@ -35,12 +35,14 @@ product requirement.
35
35
 
36
36
  Proposals use `DELETE`, `MERGE`, `SCOPE`, `MOVE`, `REFERENCE`, or `REWRITE` and name
37
37
  the owner, semantic contract, estimated savings, and any changed behavior or load
38
- timing. Keep unapproved passages byte-for-byte; the review skill routes only
39
- accepted proposals to the shared authoring procedure.
38
+ timing. Keep unapproved passages byte-for-byte; only accepted proposals reach
39
+ the shared authoring procedure, which runs the steps below.
40
40
 
41
41
  ## Verify the accepted result
42
42
 
43
- 1. Compare the ledger with the diff. Each original behavior exists once in its
43
+ 1. Before editing, when the baseline marks a rendered measurement missing, record
44
+ `<sync-cmd> --compact` output and require `IS_STATUS=ok`. After editing,
45
+ compare the ledger with the diff. Each original behavior exists once in its
44
46
  authoritative owner, was deliberately removed with approval, or is enforced by
45
47
  the named deterministic mechanism. Check for dead links, unconditional reference
46
48
  loads, conflicting instructions, and unintended scope expansion.
@@ -54,4 +56,4 @@ accepted proposals to the shared authoring procedure.
54
56
  and an ordinary explanation that reveals clipped language. Report explicitly
55
57
  when this evaluation is unavailable: fewer bytes prove size, not quality.
56
58
  4. Report artifacts changed, behavior checks, measurements, and remaining owner
57
- decisions. Return these results to the review skill's final report.
59
+ decisions in the authoring skill's final report.
@@ -3,6 +3,7 @@ name: intelligence-sync
3
3
  description: "Sync intelligence to enabled adapters"
4
4
  agent: intelligence-operator
5
5
  context: fork
6
+ background: false
6
7
  ---
7
8
 
8
9
  # Sync intelligence
@@ -19,13 +20,12 @@ context: fork
19
20
  `IS_STATUS=needs-update` (exit 6) means the project needs a migration only
20
21
  `intelligence update` applies; report that, and never edit the manifest or
21
22
  lock by hand to get past it. A frozen restore refusal means the
22
- manifest and lock disagree or the locked commit cannot be verified/restored—report it without
23
- hand-copying package content.
24
- 4. On a probable Intelligence defect, first ask whether to collect a sanitized
25
- issue draft; collect nothing before approval and never use telemetry. Include
26
- only approved CLI/version, expected/actual behavior, and safe error details—
27
- no confidential or project-specific data. Show the draft and obtain separate
28
- approval before filing. If declined, ask whether the opt-out is for this
29
- session, a user/gitignored dev-project profile, or a shared team policy; save
30
- it only with separate approval. Put team policy in a project-owned rule, and
31
- never invent a profile path or infer the intended scope.
23
+ manifest and lock disagree or the locked commit cannot be verified or restored;
24
+ report it without hand-copying package content.
25
+ 4. On a probable Intelligence defect, end the result with a block headed
26
+ `Probable Intelligence defect` that holds only safe facts: the CLI and engine
27
+ versions from `intelligence version`, the command, the expected and the actual
28
+ behavior, and error text with nothing confidential or specific to the project.
29
+ Follow it with an issue draft built from those facts alone. The result is the
30
+ only place they go: send and file nothing, and use no telemetry. Filing the
31
+ draft is a separate request the person makes.
@@ -2,7 +2,6 @@
2
2
  name: intelligence-update-context
3
3
  description: "Create, revise, or remove project rules, agents, and skills"
4
4
  argument-hint: "[rule|agent|skill] [request or accepted proposals]"
5
- agent: intelligence-architect
6
5
  ---
7
6
 
8
7
  # Update project context
@@ -74,3 +73,6 @@ the same procedure. Updating the layer can create a new artifact.
74
73
  9. Report the created, updated, removed, or archived artifacts and their checks.
75
74
  Return control to the originating workflow for its additional semantic,
76
75
  compaction, migration, or packaging verification; those checks remain required.
76
+ Review proposals carry their verification steps instead, because the review
77
+ has already returned: run each at the point it names, before or after editing,
78
+ and report the results here.
@@ -22,15 +22,16 @@
22
22
  name: <domain>-<verb>-<noun>
23
23
  description: "What the skill does and when to use it"
24
24
  argument-hint: "Expected arguments"
25
- agent: <existing-agent>
26
25
  ---
27
26
  ```
28
27
 
29
28
  Omit optional fields that do not apply. Add `effort:` only when the procedure
30
29
  needs a reasoning effort other than the session's, using the scale in the
31
- authoring conventions' effort mappings. Add the skill to its matching agent's
30
+ authoring conventions' effort mappings. Add `context: fork` with `agent:` only
31
+ for a skill the authoring conventions say to fork, and `background: false` when
32
+ its caller needs the result in the same turn. Add the skill to its matching agent's
32
33
  `skills:` list when that agent is project-owned; propose an upstream change for
33
34
  a package-owned agent instead of editing the installed copy. Verify bindings,
34
35
  relative resource links, executable steps, and final success criteria. Apply the
35
- 1000-line skill limit and other budgets from the authoring conventions, then
36
- return to the shared sync and verification steps.
36
+ word budgets from the authoring conventions, then return to the shared sync and
37
+ verification steps.
@@ -2,7 +2,6 @@
2
2
  name: intelligence-upgrade
3
3
  description: "Upgrade Intelligence and installed packages with migration checks"
4
4
  argument-hint: "[@scope/name]"
5
- agent: intelligence-operator
6
5
  disable-model-invocation: true
7
6
  ---
8
7