@erclx/aitk 0.65.1 → 0.66.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.
package/README.md CHANGED
@@ -63,7 +63,7 @@ Each domain has a canonical source in this repo and a thin install or sync CLI o
63
63
 
64
64
  - [Claude Code plugin](.claude/context/claude-plugin/index.md): skills for planning, review, docs sync, and the git ship chain
65
65
  - [Governance rules](.claude/context/governance.md): Cursor rules and stacks, installable per project
66
- - [Standards](.claude/context/standards.md): shared authoring conventions, synced to projects
66
+ - [Standards](.claude/context/standards/index.md): shared authoring conventions, synced to projects
67
67
  - [Snippets](.claude/context/snippets.md): reusable prompts for Claude and Gemini chat
68
68
  - [Tooling stacks](.claude/context/tooling.md): golden configs, seeds, and references per framework
69
69
  - [Design system](.claude/context/design.md): `DESIGN.md` token shape, extract skill and its two paths, render command
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "aitk",
3
3
  "description": "Automated governance, versioning, and discovery tools for Claude Code.",
4
- "version": "0.65.1",
4
+ "version": "0.66.1",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -7,7 +7,7 @@ description: What the standards audit is for, the gaps it closes, and the fixing
7
7
 
8
8
  ## Gap
9
9
 
10
- Without this skill, markdown ships unchecked against the authoring standards it was written under. A session that just wrote prose does not reopen the standard it was meant to follow, banned punctuation survives because it reads fine, and the mapping from a file to the standards governing it gets re-derived by guess on every pass.
10
+ Without this skill, markdown ships unchecked against the authoring standards it was written under. A session fresh from writing prose does not reopen the standard it was meant to follow, banned punctuation survives because it reads fine, and the mapping from a file to the standards governing it gets re-derived by guess on every pass.
11
11
 
12
12
  ## Must
13
13
 
@@ -28,5 +28,5 @@ Read a standard from `${CLAUDE_SKILL_DIR}/../../standards/` instead when the pro
28
28
 
29
29
  Emit the full path on its own line.
30
30
 
31
- - Root surface: this is the toolkit's authoring source. Remind the user to run `bun run check` to regenerate the consumed copy under `.claude/`. That pass also regenerates the `standards/index.md` entry, and the user adds a row to the standards table in `.claude/context/standards.md`.
31
+ - Root surface: this is the toolkit's authoring source. Remind the user to run `bun run check` to regenerate the consumed copy under `.claude/`. That pass also regenerates the `standards/index.md` entry, and the user adds a row to the standards table in `.claude/context/standards/overview.md`.
32
32
  - `.claude/` surface: the file is project-local. `aitk standards sync` leaves it alone, since sync only updates filenames it recognizes from the toolkit. Remind the user to copy it to the toolkit repo, under `standards/<slug>.md`, if it should ship to every project.
@@ -56,7 +56,7 @@ Run these commands in parallel to gather git context:
56
56
 
57
57
  **All <total> commits accounted for.**
58
58
 
59
- - For the primary concern (the current branch), show `<current_branch> → <new_name>` only when renaming. Show just `<current_branch>` if the existing name already matches the concern.
59
+ - For the primary concern (the current branch), show `<current_branch> → <new_name>` only when renaming. Show `<current_branch>` alone if the existing name already matches the concern.
60
60
  - For stacked mode, list rows in merge order: branches based on `main` first, then each layer above. For independent mode, list the primary concern first.
61
61
 
62
62
  If Mode is Stacked, append this line to the preview:
@@ -51,7 +51,7 @@ When this happens, stop fixing and ask the user whether the underlying pattern s
51
51
  ## Red flags that mean "return to phase 1"
52
52
 
53
53
  - "Quick fix for now, investigate later"
54
- - "Just try X and see if it works"
54
+ - "Try X and see if it works"
55
55
  - "It's probably Y, let me change that"
56
56
  - Proposing a fix before tracing data flow
57
57
  - Adding multiple changes and running tests to see what sticks
@@ -86,7 +86,11 @@ Exit codes are `0` for a completed run and `1` for a refusal. Every finding repo
86
86
 
87
87
  A banned character is a fact rather than a judgment, which is the test that would ordinarily make it gate. What holds it back is that gating on day one against a corpus never checked mechanically fails loudly on work nobody has had a chance to fix. The order is to land the verb reporting, measure the corpus once, fix what it finds, and turn the gate on as its own change. Bullet, paragraph, and depth weight are judgments and stay advisory under any later gate.
88
88
 
89
- Measured across 444 files with the paragraph checkpoint at 600: 8 word hits, no character or spelling hits, 119 heavy bullets, 221 heavy paragraphs across 86 files, and 41 files carrying a run past the depth checkpoint. Of the paragraphs, 88 fire on weight alone. This is the baseline the corpus sweep tracks its work against. The ban count is what a gate would have to hold at zero, and it is the only one of the five a gate should ever read.
89
+ Measured at `4b7b13a2` across 444 files with the paragraph checkpoint at 600: 8 word hits, no character or spelling hits, 119 heavy bullets, 221 heavy paragraphs across 86 files, and 41 files carrying a run past the depth checkpoint. Of the paragraphs, 88 fire on weight alone. This is the baseline the corpus sweep tracks its work against. The ban count is what a gate would have to hold at zero, and it is the only one of the five a gate should ever read.
90
+
91
+ The ban half of that baseline now reports no character, word, or spelling hit, which is the precondition the gate was waiting on. The other four moved with the corpus rather than with any decision, so read them from a run rather than from this paragraph.
92
+
93
+ ### Why a recorded count goes stale
90
94
 
91
95
  A count written into prose goes stale against the corpus it describes, and nothing compares the two. The paragraph figure recorded when the masking fix shipped was already wrong by twelve one release later, which is why the standard states the rule and this page carries the numbers.
92
96
 
@@ -94,6 +98,20 @@ A standard sits inside the corpus this verb measures, so rewriting a rule can br
94
98
 
95
99
  Masking took 7 of the weight-only paragraphs the checkpoint reported at 400 and 4 of the 44 files under their checkpoints, and no bullet at all. The first corpus triage put those at 31 paragraphs and 2 bullets, and neither reproduces: a code span is walked around, so a backticked path holding an angle-bracket placeholder keeps the width the page gives it, and both bullets the triage counted were that shape.
96
100
 
101
+ ### How the ban count reached zero
102
+
103
+ Eight word hits stood between the baseline and a gate, and only three carried the sense `prose.md` bans. `leverage` sat in the requirements worldview, `allows` in the claude stack reference, and one `just` was the vague qualifier in a skill body. Those three lost the qualifier rather than the word.
104
+
105
+ The other five were correct prose the closed set cannot separate from a violation. Four were the temporal `just`, meaning a moment ago, in phrases like the implementation `just` completed and the field the user `just` edited. The fifth quoted an anti-pattern a skill exists to forbid. `prose.md` bans vague qualifiers and lists the tokens those qualifiers happen to spell, so the rule as written reaches none of the five while the scan reaches all of them.
106
+
107
+ ### Why they were rewritten rather than exempted
108
+
109
+ Rewriting all five is what settled them, over building an exemption path. An exemption has three consumers, `src/markdown/scan.ts` for the patterns, `src/markdown/bans.ts` for the sets, and `.claude/hooks/standards-audit.sh`, which greps its own copy of the word bans in awk. A mechanism landing in the verb and not the hook leaves an exempted line still failing on edit, which is the surface an author actually meets. Five sentences lost a small amount of naturalness and the count now means what it says.
110
+
111
+ A code span was the first answer for the quoted anti-pattern and it was the wrong one. The ban scan walks around a code span, so backticking a quotation clears the report, and `## Code and identifiers` reserves the span for commands, API names, file paths, and identifiers, which a quoted utterance is none of. Spending one rule to satisfy another leaves the corpus no cleaner than dropping the qualifier does.
112
+
113
+ The collision is structural rather than a property of five legacy sentences. Writing the task and the plan behind this change each reproduced it, because a fresh file discussing the ban quotes the tokens it discusses. A later author writing about vague qualifiers meets the same thing, and the answer is to name the token in a code span where it is genuinely an identifier being discussed, and to rewrite the sentence where it is not.
114
+
97
115
  ## What it does not cover
98
116
 
99
117
  The verb reads the two attribute standards and nothing else. The five standards declaring `appliesTo: ["*"]` also include `publish.md`, `slug.md`, and `versioning.md`, none of which this implements.
@@ -185,5 +185,5 @@ Sync also refuses a target whose working tree is dirty, so commit or stash befor
185
185
 
186
186
  - [agents](agents/index.md): CLI flags, exit codes, and JSON output shapes
187
187
  - [AI workflow](ai-workflow.md): feature-development loop inside a toolkit-managed project
188
- - [tooling](../.claude/context/tooling.md), [governance](../.claude/context/governance.md), [claude plugin](../.claude/context/claude-plugin/index.md), [indexes](../.claude/context/indexes.md), [snippets](../.claude/context/snippets.md), [standards](../.claude/context/standards.md): per-domain mechanics
188
+ - [tooling](../.claude/context/tooling.md), [governance](../.claude/context/governance.md), [claude plugin](../.claude/context/claude-plugin/index.md), [indexes](../.claude/context/indexes.md), [snippets](../.claude/context/snippets.md), [standards](../.claude/context/standards/index.md): per-domain mechanics
189
189
  - [sandbox](../.claude/context/sandbox/index.md): scenario catalog for verifying domain flows
@@ -22,7 +22,7 @@ paths:
22
22
  ## Error placement
23
23
 
24
24
  - Show the error directly under the field that caused it, not under an unrelated field.
25
- - When a change to field A causes a conflict involving field B, show the error under field A (the field the user just edited), not under field B.
25
+ - When a change to field A causes a conflict involving field B, show the error under field A (the field the user edited), not under field B.
26
26
 
27
27
  ## Input sanitization
28
28
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/aitk",
3
3
  "type": "module",
4
- "version": "0.65.1",
4
+ "version": "0.66.1",
5
5
  "description": "Infrastructure and quality tooling for developer workflows",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env bash
2
+ set -e
3
+ set -o pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ PROJECT_ROOT="${PROJECT_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
7
+
8
+ source "$PROJECT_ROOT/scripts/lib/tooling.sh"
9
+
10
+ TOOLKIT_TOKEN="aitk"
11
+ TOOLING_ROOT="$PROJECT_ROOT/tooling"
12
+
13
+ # `find` writes to stderr and returns non-zero for a missing root, inside a
14
+ # process substitution whose status nothing reads. Without this the walk covers
15
+ # nothing and the check reports the seeds independent having never read one.
16
+ if [ ! -d "$TOOLING_ROOT" ]; then
17
+ echo "No tooling root at ${TOOLING_ROOT#"$PROJECT_ROOT/"}, seed independence unverifiable." >&2
18
+ exit 1
19
+ fi
20
+
21
+ # An empty discovery is a real state rather than a broken walk, and the Seed
22
+ # standards stage already reports it as a skip. Failing here would have the two
23
+ # stages disagree about what the same condition means.
24
+ seed_roots=$(collect_seed_roots)
25
+ if [ -z "$seed_roots" ]; then
26
+ echo "No seed root carries .claude/, nothing to check." >&2
27
+ exit 0
28
+ fi
29
+
30
+ # Markdown alone. The seed tree also ships hooks that call the toolkit CLI on
31
+ # purpose, each reporting a named stale-index warning when the binary is absent,
32
+ # and scoping by extension leaves them outside this walk without an exemption
33
+ # list that would have to be maintained against them.
34
+ cited=""
35
+ measured=0
36
+ while IFS= read -r seed_root; do
37
+ while IFS= read -r file; do
38
+ measured=$((measured + 1))
39
+ while IFS= read -r hit; do
40
+ cited="$cited ${file#"$PROJECT_ROOT/"}:$hit"$'\n'
41
+ done < <(grep -n "$TOOLKIT_TOKEN" "$file" || true)
42
+ done < <(find "$PROJECT_ROOT/$seed_root" -type f -name '*.md')
43
+ done <<<"$seed_roots"
44
+
45
+ # Roots resolved and no markdown under any of them is a walk that covered
46
+ # nothing, which is the verdict-without-a-measurement the guards above refuse for
47
+ # a missing tree. Reporting a pass here would say the seeds cite no CLI on the
48
+ # strength of having read no prose.
49
+ if [ "$measured" -eq 0 ]; then
50
+ echo "Seed roots resolved but carry no markdown, seed independence unverifiable." >&2
51
+ exit 1
52
+ fi
53
+
54
+ if [ -n "$cited" ]; then
55
+ echo "Seed prose cites the toolkit CLI:" >&2
56
+ printf '%s' "$cited" >&2
57
+ echo "A scaffolded project may not have $TOOLKIT_TOKEN installed. State the capability the line needs rather than the binary that supplies it." >&2
58
+ exit 1
59
+ fi
@@ -7,6 +7,7 @@ PROJECT_ROOT="${PROJECT_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
7
7
 
8
8
  source "$PROJECT_ROOT/scripts/lib/ui.sh"
9
9
  source "$PROJECT_ROOT/scripts/lib/worktree.sh"
10
+ source "$PROJECT_ROOT/scripts/lib/tooling.sh"
10
11
 
11
12
  NESTED="${VERIFY_NESTED:-false}"
12
13
  WRITE="${VERIFY_WRITE:-true}"
@@ -127,16 +128,6 @@ assert_hero_pair() {
127
128
  [ "$html_commit" = "$png_commit" ]
128
129
  }
129
130
 
130
- # Whatever stacks the repo currently carries, so a new one is covered without an
131
- # edit here. A seed root holding no `.claude/` seeds nothing a standard governs.
132
- collect_seed_roots() {
133
- local dir
134
- for dir in "$PROJECT_ROOT"/tooling/*/seeds; do
135
- [ -d "$dir/.claude" ] || continue
136
- printf '%s\n' "${dir#"$PROJECT_ROOT"/}"
137
- done
138
- }
139
-
140
131
  # Entries the audit actually measured, summed across the folders it resolved.
141
132
  # `--json` carries one `"entries":<n>` per folder object, and the top-level key
142
133
  # of that name holds an array, so the numeric match reaches folders alone.
@@ -214,6 +205,15 @@ main() {
214
205
  run_check "bash $PROJECT_ROOT/scripts/core/check-plugin-boundary.sh" "Plugin ships toolkit-internal content."
215
206
  log_info "Plugin boundary clean"
216
207
 
208
+ # Seed prose is installed into every scaffolded project and read there as
209
+ # instruction about that project, so a line naming this repository's CLI hands
210
+ # a target a verb it may not be able to run. This gates for the reason the
211
+ # Seed standards stage below gates: a defect authored once propagates into
212
+ # every project scaffolded after it.
213
+ log_step "Seed independence"
214
+ run_check "bash $PROJECT_ROOT/scripts/core/check-seed-independence.sh" "Seed prose cites the toolkit CLI."
215
+ log_info "Seed prose cites no toolkit CLI"
216
+
217
217
  # A stack entry naming a rule folder takes every rule in it, which is what
218
218
  # stops a new rule from needing a second edit to reach a target. The failure
219
219
  # it leaves open is a rule authored into a folder no stack names, which
@@ -22,3 +22,14 @@ list_tooling_stacks() {
22
22
  echo "$name"
23
23
  done < <(find "$tooling_dir" -mindepth 1 -maxdepth 1 -type d | sort)
24
24
  }
25
+
26
+ # Seed roots that carry a `.claude/`, emitted relative to `PROJECT_ROOT`. Every
27
+ # stage measuring seed content discovers through this rather than naming a stack,
28
+ # so a stack seeding `.claude/` later arrives covered with no edit to any caller.
29
+ collect_seed_roots() {
30
+ local dir
31
+ for dir in "$PROJECT_ROOT"/tooling/*/seeds; do
32
+ [ -d "$dir/.claude" ] || continue
33
+ printf '%s\n' "${dir#"$PROJECT_ROOT"/}"
34
+ done
35
+ }
@@ -1,4 +1,4 @@
1
- Recap the implementation just completed. Read the plan file referenced in the session (usually `.claude/plans/feature-<slug>.md`) and produce a verification block.
1
+ Recap the implementation this session completed. Read the plan file referenced in the session (usually `.claude/plans/feature-<slug>.md`) and produce a verification block.
2
2
 
3
3
  Output this shape, nothing else:
4
4
 
@@ -53,11 +53,11 @@ A project installed before the diagram surface became a folder still holds `.cla
53
53
 
54
54
  ## CLI
55
55
 
56
- | Command | What it does |
57
- | ------------------- | --------------------------------------------------------------------------------------------------------------------------- |
58
- | `aitk claude init` | Seeds `.claude/` workflow docs, updates `.gitignore` |
59
- | `aitk claude sync` | Reconciles `.gitignore` against the manifest. Reports seeded file status. |
60
- | `aitk claude setup` | Installs user-level config to `~/.claude/`: statusline, attribution, and permission allows or denies. Run once per machine. |
56
+ | Command | What it does |
57
+ | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
58
+ | `aitk claude init` | Seeds `.claude/` workflow docs, updates `.gitignore` |
59
+ | `aitk claude sync` | Reconciles `.gitignore` against the manifest. Reports seeded file status. |
60
+ | `aitk claude setup` | Installs user-level config to `~/.claude/`: statusline, attribution, and the `permissions.allow` and `permissions.deny` lists. Run once per machine. |
61
61
 
62
62
  ## CLAUDE.md
63
63
 
@@ -74,7 +74,7 @@
74
74
  - Write all memory files to `.claude/memory/`, not `~/.claude/projects/`
75
75
  - A fact about a domain goes to that domain's `.claude/context/` entry, not to memory. `claude-memory-capture` routes it there and `claude-docs` folds it in. Memory keeps only what no context entry owns.
76
76
  - Never delete a memory entry. Retire one by moving it to `.claude/.tmp/memory-archive/`. A bulk retire runs through the shell, where no file edit fires a path-scoped rule, and the folder is gitignored with nothing to recover from.
77
- - Follow `.claude/standards/memory.md` for the filename and type prefix, the frontmatter, the body shape each type carries, and the lifecycle. Run `aitk records validate memory` to check the pen against it.
77
+ - Follow `.claude/standards/memory.md` for the filename and type prefix, the frontmatter, the body shape each type carries, and the lifecycle. Check every entry in the pen against that standard and fix what breaks it, since nothing keeps the folder conforming on its own.
78
78
 
79
79
  ## Scratch
80
80
 
@@ -87,4 +87,4 @@
87
87
  - From a linked worktree, every `Edit` or `Write` to a tracked file (source, docs) must use a path starting with `pwd`.
88
88
  - From a linked worktree, `Edit` and `Write` are refused for every main-root path, session scratch included. The refusal names session isolation and points at the worktree copy, which is a second gitignored file no later session reads, so never take that redirect.
89
89
  - `Read` resolves against the main root normally from a linked worktree. A main-root write reaches it only through `Bash`, as one plain command rather than a compound one, which is refused for complexity.
90
- - Route a main-root write by what it does to the file. Creating a whole file goes out as one plain `Bash` command carrying a heredoc. Changing a line inside a file that already exists goes through an `aitk` verb, which resolves the main root in-process, because the shell route for that case is the stream editor this file bans.
90
+ - Route a main-root write by what it does to the file. Creating a whole file goes out as one plain `Bash` command carrying a heredoc. Changing a line inside a file that already exists goes through a command that resolves the main root in-process, because the shell route for that case is the stream editor this file bans.