@ainova-systems/intelligence 0.20.0 → 0.22.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.
Files changed (31) hide show
  1. package/cli/commands/init.sh +2 -1
  2. package/cli/commands/sync.sh +9 -0
  3. package/cli/internal/check.sh +39 -0
  4. package/cli/lib/gitignore.sh +19 -2
  5. package/cli/lib/sync-lock.sh +103 -21
  6. package/engine/ENGINE_SHA +1 -1
  7. package/engine/VERSION +1 -1
  8. package/engine/adapters/_template.sh +32 -8
  9. package/engine/adapters/agents.sh +42 -50
  10. package/engine/adapters/antigravity.sh +68 -52
  11. package/engine/adapters/claude-plugin/.claude-plugin/plugin.json +6 -0
  12. package/engine/adapters/claude-plugin/README.md +14 -0
  13. package/engine/adapters/claude-plugin/hooks/hooks.json +3 -0
  14. package/engine/adapters/claude-plugin/hooks/register.js +324 -0
  15. package/engine/adapters/claude.sh +129 -59
  16. package/engine/adapters/codex.sh +74 -15
  17. package/engine/adapters/copilot.sh +56 -79
  18. package/engine/adapters/cursor.sh +85 -81
  19. package/engine/adapters/opencode.sh +20 -35
  20. package/engine/adapters/pi.sh +137 -41
  21. package/engine/lib/common.sh +753 -56
  22. package/engine/lib/contract.sh +15 -6
  23. package/engine/sync.sh +49 -33
  24. package/package.json +1 -1
  25. package/packages/sync/references/adapters.md +56 -40
  26. package/packages/sync/references/conventions.md +57 -11
  27. package/packages/sync/references/onboarding-migration.md +4 -4
  28. package/packages/sync/rules/intelligence-authoring.md +2 -2
  29. package/packages/sync/skills/intelligence-learn-from-repository/SKILL.md +7 -5
  30. package/packages/sync/skills/intelligence-review-context/references/audit-checks.md +1 -1
  31. package/packages/sync/skills/intelligence-update-context/references/rules.md +5 -0
@@ -100,18 +100,27 @@ is_status() {
100
100
  fi
101
101
  }
102
102
 
103
+ # engine_root_var — set IS_ENGINE_ROOT to the engine directory, one level above
104
+ # this lib. A project adapter that overrides a built-in finds the engine's own
105
+ # files through it, never beside itself.
106
+ engine_root_var() {
107
+ local lib="${BASH_SOURCE[0]}"
108
+ case "$lib" in
109
+ */*) lib="${lib%/*}" ;;
110
+ *) lib="." ;;
111
+ esac
112
+ IS_ENGINE_ROOT="$lib/.."
113
+ }
114
+
103
115
  # Engine version = VERSION one level above this lib (BASH_SOURCE works when
104
116
  # sourced). Empty if unreadable — callers treat empty as "no guard".
105
117
  # engine_version_var sets IS_ENGINE_VERSION, and IS_ENGINE_VERSION_FOUND=1 when
106
118
  # the file exists; it reads the file once per process.
107
119
  engine_version_var() {
108
120
  [ -z "${IS_ENGINE_VERSION_FOUND:-}" ] || return 0
109
- local lib="${BASH_SOURCE[0]}" vf content=""
110
- case "$lib" in
111
- */*) lib="${lib%/*}" ;;
112
- *) lib="." ;;
113
- esac
114
- vf="$lib/../VERSION"
121
+ local vf content=""
122
+ engine_root_var
123
+ vf="$IS_ENGINE_ROOT/VERSION"
115
124
  IS_ENGINE_VERSION=""
116
125
  IS_ENGINE_VERSION_FOUND=0
117
126
  [ -f "$vf" ] || return 0
package/engine/sync.sh CHANGED
@@ -115,40 +115,17 @@ done <<< "${IS_YL_UNRESOLVED:-}"
115
115
 
116
116
 
117
117
  # Sources stay read-only for the whole run (validate_output_path refuses an
118
- # output inside one), so read_source_artifact_files enumerates each section
119
- # once, in this shell, and its later callers — the agents adapter, the shared
120
- # skill directory, the context report — replay it. Adapters that list sources
121
- # with in-shell globs spawn nothing and keep their own, locale-ordered listing.
122
- # The skills pass also settles every symlink in a skills source and warns once
123
- # about each it leaves out (decision 0017).
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.
124
126
  # shellcheck disable=SC2034 # read by read_source_artifact_files in lib/common.sh
125
127
  IS_SOURCE_FILES_MEMO=1
126
- for section in rules agents skills; do
127
- read_source_artifact_files "$REPO_ROOT" "$CONFIG_FILE" "$section"
128
- done
129
-
130
- # Lint frontmatter across all source files (rules, agents, skills).
131
- # Catches issues like unquoted colons that strict YAML consumers reject.
132
- # Skills are the rendered ones the pass above settled: SKILL.md behind a link
133
- # inside its source is linted, one that leaves its source is never read.
134
- LINT_FILES=()
135
- LINT_SKILLS=()
136
- [ "${#IS_SOURCE_FILES[@]}" -eq 0 ] || LINT_SKILLS=("${IS_SOURCE_FILES[@]}")
137
- for section in rules agents; do
138
- load_yaml_list "$CONFIG_FILE" "$section"
139
- while IFS= read -r src; do
140
- [ -z "$src" ] && continue
141
- src_dir="$REPO_ROOT/$src"
142
- [ -d "$src_dir" ] || continue
143
- for f in "$src_dir"/*.md; do
144
- [ -f "$f" ] && LINT_FILES+=("$f")
145
- done
146
- done <<< "$IS_YAML_LIST"
147
- done
148
- [ "${#LINT_SKILLS[@]}" -eq 0 ] || LINT_FILES+=("${LINT_SKILLS[@]}")
149
- if [ "${#LINT_FILES[@]}" -gt 0 ]; then
150
- lint_frontmatter_files "${LINT_FILES[@]}"
151
- fi
128
+ source_catalog_build "$REPO_ROOT" "$CONFIG_FILE" lint
152
129
 
153
130
  # Adapters come from two places, discovered by filename (minus `.sh`,
154
131
  # `_template` excluded):
@@ -189,6 +166,11 @@ for adapters_dir in "$SCRIPT_DIR/adapters" "$INTELLIGENCE_DIR/adapters"; do
189
166
  done
190
167
  done
191
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
+
192
174
  # Validate every selected adapter contract before any output is touched, then
193
175
  # snapshot the declared write-set. If a later adapter fails, the EXIT handler
194
176
  # restores all earlier adapter outputs so sync is atomic from the repository's
@@ -377,6 +359,20 @@ note_agents_dependent() {
377
359
  }
378
360
  SELECTED_RECORDS=()
379
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=""
380
376
  preflight_idx=0
381
377
  while [ "$preflight_idx" -lt "${#ADAPTERS[@]}" ]; do
382
378
  adapter="${ADAPTERS[$preflight_idx]}"
@@ -387,13 +383,26 @@ while [ "$preflight_idx" -lt "${#ADAPTERS[@]}" ]; do
387
383
  target_output_var "$CONFIG_FILE" "$adapter"
388
384
  output="$IS_TGT_OUTPUT"
389
385
  [ -n "$output" ] || output=".$adapter"
390
- records="$(adapter_contract_records "$adapter" "$adapter_file" "$output")" || exit 1
386
+ records="$(adapter_contract_records "$adapter" "$adapter_file" "$output" "$policy_manifest")" || exit 1
391
387
 
392
388
  if ! adapter_claims_add_records "$adapter" "$records"; then
393
389
  echo "ERROR: $IS_ADAPTER_CLAIM_CONFLICT." >&2
394
390
  echo " Give each adapter its own output: intelligence adapter list" >&2
395
391
  exit 1
396
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"
397
406
 
398
407
  if [ -n "$TARGET_FILTER" ] && [ "$adapter" != "$TARGET_FILTER" ]; then
399
408
  # Not selected: its claims are registered, its output is not touched.
@@ -428,6 +437,13 @@ while [ "$preflight_idx" -lt "${#ADAPTERS[@]}" ]; do
428
437
  esac
429
438
  done <<< "$records"
430
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"
431
447
 
432
448
  # Every adapter that requires `agents` skips always-on rules because AGENTS.md
433
449
  # carries them — and each of those tools reads AGENTS.md at the workspace root
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainova-systems/intelligence",
3
- "version": "0.20.0",
3
+ "version": "0.22.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"
@@ -129,6 +129,17 @@ Code ignore theirs, and never `.github/` itself. `targets.copilot.commit_output:
129
129
  true` keeps them tracked for Copilot on github.com; the setting is defined in
130
130
  [Generated output and version control](conventions.md#generated-output-and-version-control).
131
131
 
132
+ The Claude adapter renders a Claude Code plugin into `<output>/skills/intelligence/`
133
+ when `targets.claude.plugin` is `true` (decision 0022): a mod that checks
134
+ `intelligence sync --check` at session start and every five minutes, shows the
135
+ verdict in a line above the prompt and runs sync from a button there or
136
+ `/intelligence sync`.
137
+ The template is `engine/adapters/claude-plugin/`, copied with the engine version
138
+ stamped into `plugin.json`. It lives inside the owned `skills` path, so it needs
139
+ no contract record of its own. The field's values, the name collision it refuses
140
+ and the plugin's limits are defined in the
141
+ [CLI reference](https://github.com/ainova-systems/intelligence/blob/main/docs/cli.md#intelligence-sync).
142
+
132
143
  The engine calls:
133
144
 
134
145
  ```text
@@ -141,22 +152,26 @@ The engine has already loaded `engine/lib/common.sh` before it sources the adapt
141
152
 
142
153
  `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
154
 
144
- Iterate a source section in manifest order:
155
+ 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
156
 
146
157
  ```bash
147
- while IFS= read -r src; do
148
- [ -n "$src" ] || continue
149
- dir="$(resolve_source_dir "$repo_root" "$src")"
150
- [ -d "$dir" ] || continue
151
-
152
- for file in "$dir"/*.md; do
153
- [ -f "$file" ] || continue
154
- # transform file
155
- done
156
- done < <(read_yaml_list "$config_file" "rules")
158
+ output_rel_var "$repo_root" "$output_dir"
159
+ source_artifacts_var "$repo_root" "$config_file" rules mytool "$IS_OUTPUT_REL/rules/{}.md"
160
+ for ((i = 0; i < ${#IS_SOURCE_FILES[@]}; i++)); do
161
+ # IS_SOURCE_FILES[i] is the source, IS_SOURCE_NAMES[i] its rendered name,
162
+ # IS_SOURCE_SCOPED[i] is 1 for a rule with paths:
163
+ :
164
+ done
157
165
  ```
158
166
 
159
- Later sources intentionally overwrite same-named artifacts from earlier sources. Package sources are wired before project sources, so project content can override a package artifact by name.
167
+ 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:
168
+
169
+ - **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.
170
+ - **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.
171
+ - **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.
172
+ - **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.
173
+
174
+ `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
175
 
161
176
  ## Shared functions
162
177
 
@@ -164,6 +179,10 @@ Use the engine library instead of copying parsers or file-handling logic.
164
179
 
165
180
  | Function | Purpose |
166
181
  |---|---|
182
+ | `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`. |
183
+ | `output_rel_var(root, output_dir)` | The output's repository-relative path in `IS_OUTPUT_REL`, as `source_artifacts_var` takes it. |
184
+ | `finalize_copy_named(dest, map, src...)` | Copy-and-finalize every source into `dest` under its rendered name, in one process. |
185
+ | `copy_skill_bundle_dirs_named(tool, dest_root, map, src...)` | `copy_skill_bundle_dirs_for` with each skill copied under its rendered name. |
167
186
  | `resolve_source_dir(repo_root, source)` | Resolve a manifest source to its local directory. |
168
187
  | `read_yaml_list(config, section)` | Stream entries from `sources.<section>`, package references as their store paths. |
169
188
  | `load_yaml_list(config, section)` | Same list into `IS_YAML_LIST`, cached — no subprocess on repeat reads. |
@@ -178,7 +197,6 @@ Use the engine library instead of copying parsers or file-handling logic.
178
197
  | `copy_skill_bundle(src, dest)` | Copy `SKILL.md` and all resources safely, normalize Markdown, quote free-text frontmatter, and keep a valid `effort:` as written, as the shared skills tree does. |
179
198
  | `copy_skill_bundle_dirs(dest_root, src...)` | Batch form: copy every skill directory into `dest_root/<name>` with one copy and one finalize pass. |
180
199
  | `copy_skill_bundle_dirs_for(tool, dest_root, src...)` | The batch form for a tree one tool reads: `SKILL.md`'s `effort:` becomes that tool's level, or is removed when it has none. |
181
- | `skill_source_rendered(skill_dir)` | Whether sync renders a skill directory an adapter listed with its own glob; a skill left out for its symlinks fails it. |
182
200
  | `sync_open_skill_dirs(root, config, dest)` | Own and populate a shared Agent Skills directory such as `.agents/skills/`, deriving Codex's `agents/openai.yaml` for a skill with `disable-model-invocation: true`. |
183
201
  | `finalize_output_file(file)` | Expand layout tokens and normalize line endings; required for every emitted text file. |
184
202
  | `finalize_output_files(file...)` / `finalize_copy_files(dest, src...)` | Batch forms: finalize in place, or copy-and-finalize into a directory, in one process. |
@@ -196,18 +214,20 @@ Prefer the batched forms inside per-file loops: a process spawn costs tens of mi
196
214
 
197
215
  Rules without `paths:` are always-on. Rules with `paths:` are scoped.
198
216
 
199
- 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.
217
+ 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.
200
218
 
201
- | Built-in | Always-on rules | Scoped rules |
202
- |---|---|---|
203
- | `agents` | Inline in `AGENTS.md` | List with links |
204
- | `antigravity` | Omit; reads `AGENTS.md` | `.agents/rules/*.md` with `trigger: glob` and `globs:` |
205
- | `claude` | Copy | Copy with `paths:` preserved |
206
- | `cursor` | Omit; reads `AGENTS.md` | `.mdc` with `globs:` |
207
- | `copilot` | Omit; reads `AGENTS.md` | `.instructions.md` with `applyTo:` |
208
- | `codex` | Omit; reads `AGENTS.md` | No native channel |
209
- | `pi` | Omit; reads `AGENTS.md` | Generated on-demand files and extension |
210
- | `opencode` | Omit; reads `AGENTS.md` | No native channel |
219
+ | Built-in | Always-on rules | Always-on, not in `AGENTS.md` | Scoped rules |
220
+ |---|---|---|---|
221
+ | `agents` | Inline in `AGENTS.md` | — | List with links |
222
+ | `antigravity` | Omit; reads `AGENTS.md` | `.agents/rules/*.md` with `trigger: always_on` | `.agents/rules/*.md` with `trigger: glob` and `globs:` |
223
+ | `claude` | Copy | Copy | Copy with `paths:` preserved |
224
+ | `cursor` | Omit; reads `AGENTS.md` | `.mdc` with `alwaysApply: true` | `.mdc` with `globs:` |
225
+ | `copilot` | Omit; reads `AGENTS.md` | `.instructions.md` with `applyTo: "**"` | `.instructions.md` with `applyTo:` |
226
+ | `codex` | Omit; reads `AGENTS.md` | `AGENTS.override.md`: all of `AGENTS.md` plus these rules | No native channel |
227
+ | `pi` | Omit; reads `AGENTS.md` | Added to the system prompt by the generated extension | Generated on-demand files and extension |
228
+ | `opencode` | Omit; reads `AGENTS.md` | No channel; sync warns | No native channel |
229
+
230
+ 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.
211
231
 
212
232
  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.
213
233
 
@@ -242,7 +262,7 @@ copy_skill_bundle "$source_skill_dir" "$output_dir/skills/$skill_name"
242
262
 
243
263
  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).
244
264
 
245
- An adapter that lists a skills source with its own glob checks each directory with `skill_source_rendered`. A skill 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.
265
+ `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/`.
246
266
 
247
267
  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.
248
268
 
@@ -308,21 +328,16 @@ This example copies rules without frontmatter. A real adapter must decide how th
308
328
  #!/bin/bash
309
329
 
310
330
  sync_mytool_rules() {
311
- local repo_root="$1" config_file="$2" output_dir="$3"
331
+ local repo_root="$1" config_file="$2" output_dir="$3" i out
312
332
  mkdir -p "$output_dir/rules"
313
333
 
314
- while IFS= read -r src; do
315
- [ -n "$src" ] || continue
316
- local dir
317
- dir="$(resolve_source_dir "$repo_root" "$src")"
318
- [ -d "$dir" ] || continue
319
-
320
- for file in "$dir"/*.md; do
321
- [ -f "$file" ] || continue
322
- strip_frontmatter "$file" > "$output_dir/rules/$(basename "$file")"
323
- finalize_output_file "$output_dir/rules/$(basename "$file")"
324
- done
325
- done < <(read_yaml_list "$config_file" "rules")
334
+ output_rel_var "$repo_root" "$output_dir"
335
+ source_artifacts_var "$repo_root" "$config_file" rules mytool "$IS_OUTPUT_REL/rules/{}.md"
336
+ for ((i = 0; i < ${#IS_SOURCE_FILES[@]}; i++)); do
337
+ out="$output_dir/rules/${IS_SOURCE_NAMES[i]}.md"
338
+ strip_frontmatter "${IS_SOURCE_FILES[i]}" > "$out"
339
+ finalize_output_file "$out"
340
+ done
326
341
  }
327
342
 
328
343
  adapter_contract_mytool() {
@@ -352,6 +367,7 @@ Verify:
352
367
 
353
368
  - native file names, frontmatter and model/permission mappings;
354
369
  - scoped and always-on routing without duplicated context;
370
+ - a dotfile or `*.local.md` source renders under its name, and a local one only into outputs the contract ignores;
355
371
  - skill resources are present;
356
372
  - no literal layout tokens remain;
357
373
  - hand-authored siblings under the tool root survive;
@@ -368,9 +384,9 @@ For a built-in adapter, add the same assertions to the repository's smoke or lif
368
384
  |---|---|
369
385
  | `agents` | `AGENTS.md` |
370
386
  | `antigravity` | `.agents/rules`, `.agents/agents`, `.agents/skills` |
371
- | `claude` | `.claude/rules`, `.claude/agents`, `.claude/skills` |
387
+ | `claude` | `.claude/rules`, `.claude/agents`, `.claude/skills` (with `plugin: true`, `.claude/skills/intelligence` is the Claude Code plugin) |
372
388
  | `cursor` | `.cursor/rules`, `.cursor/agents`, `.cursor/skills` |
373
389
  | `copilot` | `.github/instructions`, `.github/agents`, `.github/skills` |
374
- | `codex` | `.codex/agents`, `.agents/skills` |
390
+ | `codex` | `.codex/agents`, `.agents/skills`, marker-owned `AGENTS.override.md` |
375
391
  | `pi` | `.pi/intelligence-sync`, `.pi/extensions`, `.pi/prompts`, `.agents/skills` |
376
392
  | `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: later files with the same artifact name overwrite earlier ones. Package sources are wired before project sources so the project can override a package artifact deliberately.
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
 
@@ -338,12 +376,12 @@ Every line enters a finite context budget. Prefer subtraction, consolidation and
338
376
  | Target | Rules | Skills | Agents |
339
377
  |---|---|---|---|
340
378
  | `agents` | Always-on inlined; scoped listed in `AGENTS.md` | Listed | Listed |
341
- | Antigravity | `AGENTS.md` plus scoped `.agents/rules/*.md` | `.agents/skills/` | `.agents/agents/*.md` |
379
+ | Antigravity | `AGENTS.md` plus `.agents/rules/*.md` | `.agents/skills/` | `.agents/agents/*.md` |
342
380
  | Claude | `.claude/rules/` | `.claude/skills/` | `.claude/agents/` |
343
- | Cursor | scoped `.cursor/rules/*.mdc` | `.cursor/skills/` | `.cursor/agents/` |
344
- | Copilot | scoped `.github/instructions/*.instructions.md` | `.github/skills/` | `.github/agents/` |
345
- | Codex | `AGENTS.md` only | `.agents/skills/` | `.codex/agents/*.toml` |
346
- | 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` |
347
385
  | OpenCode | `AGENTS.md` only | `.agents/skills/` plus slash commands | `.opencode/agents/*.md` |
348
386
 
349
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.
@@ -354,7 +392,12 @@ By default, commit the manifest, lock, project-owned content and `AGENTS.md`. Ig
354
392
  # CLI-managed package store
355
393
  .intelligence/
356
394
 
357
- # Local root instructions migrate into project rules; local preferences stay ignored
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
358
401
  CLAUDE.md
359
402
  .cursorrules
360
403
  GEMINI.md
@@ -374,6 +417,7 @@ GEMINI.md
374
417
  .agents/rules/
375
418
  .agents/agents/
376
419
  .codex/agents/
420
+ AGENTS.override.md
377
421
 
378
422
  # Generated Pi content
379
423
  .pi/intelligence-sync/
@@ -430,9 +474,10 @@ than inferring contents from Git status.
430
474
  move useful repository guidance from legacy root files such as `.cursorrules`
431
475
  and instruction-bearing `CLAUDE.md` out of their quarantined backup copies and
432
476
  into project-owned rules, then verify the generated tool output. Do not restore
433
- the original root monolith. Create a new root tool-specific file only when it
434
- contains genuinely local configuration that an adapter cannot represent; keep
435
- that exception gitignored rather than maintaining a second committed source.
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.
436
481
 
437
482
  Before the first render, `intelligence init` preserves existing AI prompt paths
438
483
  under `<content-dir>/_backup/`. Its `manifest.tsv` labels the snapshot
@@ -496,6 +541,7 @@ Callers capture the real code with `command || rc=$?`. Do not use `if ! command;
496
541
  | `intelligence.yaml` | Manifest and schema contract | Tracked |
497
542
  | `intelligence.lock` | Resolved package state | Tracked |
498
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 |
499
545
  | `.intelligence/` | Restorable package store | Ignored |
500
546
  | `AGENTS.md` | Generated canonical project context | Normally tracked |
501
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; keep local machine preferences in a gitignored root file only when no adapter representation exists |
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. Create a new root tool file only from a separately approved, genuinely
68
- machine-local subset that has no adapter representation; never restore the
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
@@ -48,13 +48,13 @@ The mistakes that actually happen, in order of frequency:
48
48
 
49
49
  ## Rules
50
50
 
51
- **Scope with `paths:`.** A rule without `paths:` is loaded into *every* session and inlined into `AGENTS.md` — the most expensive context real estate there is. Earn it. A rule that only matters when someone touches the frontend belongs scoped to the frontend.
51
+ **Scope with `paths:`.** A rule without `paths:` is loaded into *every* session and inlined into `AGENTS.md` — the most expensive context real estate there is. Earn it. A rule that only matters when someone touches the frontend belongs scoped to the frontend. Guidance about one tool belongs to that tool: `targets: [claude]` keeps it out of every other tool's context. A rule whose targets leave `agents` out also leaves `AGENTS.md`, and sync names any tool that can then no longer receive it.
52
52
 
53
53
  **Lead with the positive default, then the invariant.** The model acts on the first instruction it reads, and it follows whatever is named — negation ("never do X") draws attention to X. Reserve absolute language (NEVER, MUST) for true must-nots: safety, security, output format. A judgement call written as a NEVER only teaches the model that NEVERs are negotiable.
54
54
 
55
55
  **Derive from the code, not from prose that already exists.** Every REQUIRED and every invariant must be backed by something observed in the repository. Documentation is a claim, not evidence — read the file.
56
56
 
57
- **Describe the repository, not a machine.** The OS, the shell, the editor, a local dev stack, personal tooling — these are environment facts. They belong in a personal, gitignored `CLAUDE.md`. A rule is committed and read by everyone, so a shell-specific command or an absolute local path in it is simply wrong for whoever is on another platform.
57
+ **Describe the repository, not a machine.** The OS, the shell, the editor, a local dev stack, personal tooling — these are environment facts. They belong in a local rule, `<content-dir>/rules/<name>.local.md`: Git ignores it, and sync renders it only into outputs Git ignores. A rule is committed and read by everyone, so a shell-specific command or an absolute local path in it is simply wrong for whoever is on another platform.
58
58
 
59
59
  **Explain the why — but only a why you can back.** A reason the reader can check is what makes an instruction survive a judgement call. An invented reason is worse than none: it sounds like evidence.
60
60
 
@@ -59,9 +59,10 @@ mechanics; this skill supplies repository judgement.
59
59
  Treat backed-up legacy root instructions such as `.cursorrules` and an
60
60
  instruction-bearing `CLAUDE.md` as migration sources, not permanent parallel
61
61
  entry points. Move still-valid shared guidance into project-owned rules; do
62
- not restore the original monolith. Only when approved machine-local guidance
63
- remains, create a small new gitignored `CLAUDE.md` from that subset after
64
- migration. `AGENTS.md` remains the shared root instruction entry point.
62
+ not restore the original monolith. Approved machine-local guidance becomes a
63
+ local rule, `<content-dir>/rules/<name>.local.md`, which Git ignores and sync
64
+ keeps out of committed output. `AGENTS.md` remains the shared root
65
+ instruction entry point.
65
66
 
66
67
  Review `.gitignore` and every existing `.vscodeignore`, `.npmignore`, and
67
68
  `.dockerignore` against the CLI-managed policy. Detect tracked files which
@@ -95,8 +96,9 @@ explains itself well.
95
96
  relevant generated `AGENTS.md`, Cursor rules, Claude rules, and any
96
97
  packaging/build file list affected by ignore policy. Verify quarantined
97
98
  legacy root entry points remain absent. If the user approved a genuinely
98
- machine-local exception, create a concise new gitignored root file from only
99
- that content; never copy the backed-up monolith back. Completion requires
99
+ machine-local exception, write only that content to a local rule
100
+ (`<content-dir>/rules/<name>.local.md`); never copy the backed-up monolith
101
+ back. Completion requires
100
102
  `IS_STATUS=ok`, a clean final check, and no `onboarding is pending` header
101
103
  after accepted migration.
102
104
  11. Report what was created, updated, removed, or deliberately kept. Remind the
@@ -9,7 +9,7 @@ Apply these checks to the source groups resolved by the review skill.
9
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` |
10
10
  | **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
11
  | **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
- | **Machine facts in a rule** | OS, shell, editor or a local absolute path (`R2`) | `MOVE` — these belong in a personal, gitignored `CLAUDE.md`; a rule is committed and read by everyone, including whoever is on another platform |
12
+ | **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 |
13
13
  | **Literal path or command in a skill** | A path *outside the skill's own folder* baked into a procedure (`R3`) | `PARAMETERIZE` — a skill is *executed*, so a literal path breaks the moment the layout moves; resolve it from a rule or from `<manifest>`. **Exempt:** the skill's own bundle (`references/`, `scripts/`, `assets/` — content is co-located with its skill by default); rules and agents (describing the repository is their job); an example inside an output-format block; the resolution step itself |
14
14
  | **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
15
  | **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 |
@@ -13,6 +13,11 @@
13
13
  Architecture, Build and test, Examples, and Patterns to recognize and replace.
14
14
  Keep reasons that guide judgment and reference real examples. An accepted
15
15
  user preference is identified as a preference, not a claim about existing code.
16
+ 5. Guidance that belongs to one machine or one person — the OS, the shell, a
17
+ local service, a personal habit — goes in a local rule,
18
+ `<content-dir>/rules/<name>.local.md`. Git ignores it, and sync renders it
19
+ only into outputs Git ignores, so it never reaches `AGENTS.md` or a teammate.
20
+ A local rule cannot share a name with a committed one.
16
21
 
17
22
  For a scoped rule:
18
23