@erclx/aitk 0.72.0 → 0.74.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -62,7 +62,7 @@ Two limits worth knowing before you install. Claude Code is the only agent runti
62
62
  Each domain has a canonical source in this repo and a thin install or sync CLI on your side. The links run to internal narrative, written for someone maintaining the toolkit rather than installing it, so skip them on a first pass.
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
- - [Governance rules](.claude/context/governance.md): Cursor rules and stacks, installable per project
65
+ - [Governance rules](.claude/context/governance/index.md): Claude rules and stacks, installable per project
66
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
@@ -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.72.0",
4
+ "version": "0.74.0",
5
5
  "author": {
6
6
  "name": "Eric Le",
7
7
  "url": "https://github.com/erclx"
@@ -21,6 +21,8 @@ The last failure is writing without re-checking. A domain sync brought a stricte
21
21
  - Map the stated intent to one lifecycle phase, then either run the simplest command that satisfies it or hand off
22
22
  - Resolve every stack, rule, snippet, and standard name from a catalog at runtime
23
23
  - Run the CLI non-interactively and report the command run, what changed, and the full path of anything written
24
+ - Read the reverse walk beside the forward sections, so a folder the toolkit stopped shipping and a migration with a live case both reach the user
25
+ - Hand off a named migration to the skill the report names, the way an unmigrated domain reaches `migration-standards`
24
26
  - Offer every audit whose surface the target carries, and offer none whose surface it lacks
25
27
  - Re-check the target after any operation that wrote, and report what those writes changed against the state read before acting
26
28
 
@@ -29,6 +31,7 @@ The last failure is writing without re-checking. A domain sync brought a stricte
29
31
  - Edit a managed file by hand instead of running the CLI that owns it
30
32
  - Reimplement a flow another skill owns
31
33
  - Hardcode a catalog name
34
+ - Move, delete, or name a command against a folder in `unclaimed`. The toolkit stopped claiming the path and only the user knows whether the project owns what sits there
32
35
  - Run an audit the user did not pick, which turns a front door into a full sweep
33
36
  - Refuse to finish an operation over an audit finding, since every audit reports judgments beside facts and a router that stops on one is a router a target works around
34
37
  - Auto-trigger. It is a door the user opens, and a router that fires on its own routes requests nobody made.
@@ -38,6 +41,7 @@ The last failure is writing without re-checking. A domain sync brought a stricte
38
41
  - An intent matching a deep flow hands off rather than running a shallow version of it, since a partial scaffold is harder to recover from than none
39
42
  - An ignore-only fix on an unmeasured tooling report stops, names which cause applies, and names the command answering that cause. Unmeasured has several causes and they do not share a remedy, so one suggested command is wrong for at least one of them. No cause asks the user to supply a stack name, and none reads the zero counts under an unmeasured report as a clean target.
40
43
  - A re-check with no earlier report to compare against says the write ran without a baseline, rather than describing the target from scratch
44
+ - A report missing a field this skill reads names the CLI version as the cause and reports that section as unread. Neither an absent key nor a hand-rolled substitute for it may be reported as a finding, since the first reads as a clean target and the second reaches a verdict only the field's attribution can make
41
45
 
42
46
  ## Out of scope
43
47
 
@@ -22,23 +22,28 @@ Load a domain doc with `aitk docs <topic>` only when the intent touches that dom
22
22
 
23
23
  Run `aitk sync --check . --json` before routing. It reports what a target is behind on across every surface, so the intent comes from the project state rather than from the user having to know it already. Skip only when the user named a single operation to run.
24
24
 
25
- Read six fields off the report and carry each to `## Route`:
25
+ Read seven fields off the report and carry each to `## Route`:
26
26
 
27
27
  - `unmigrated`: a domain sitting at the root layout with nothing under `.claude/`. The most urgent finding, because that domain reports no drift of its own while being entirely behind
28
28
  - `superseded`: a file a newer seed folder replaced. Report it and stop. No command moves it, since the content is the project's own
29
29
  - `seeds`: entries are `matching`, `stale`, `drifted`, or `missing`. Anything but `matching` needs the seed handoff
30
30
  - `domains[].entries`: per-file `stale`, `customized`, `stranded`, and `orphaned` as before
31
- - `historyUnavailable` on a domain or on `seeds`: attribution failed, so treat every difference as unverified and say so rather than reporting a file as untouched
31
+ - `reverse`: what the target holds that no live catalog claims. `unclaimed` lists folders at roots the toolkit stopped shipping, each with an `attribution` of `dropped`, `unattributed`, or `project`. Act on the first two and leave `project` alone, which history proved the project owns. `migrations` names a proposal-only skill with a live case here and the `reason` it was measured from
32
+ - `historyUnavailable` on a domain, on `seeds`, or on `reverse`: nothing could be dated, so treat every difference as unverified and say so rather than reporting a file as untouched. It is set on `reverse` when the toolkit itself ships without history, which is the registry-install case, so the walk found nothing rather than finding a clean target
32
33
  - `tooling`: read `measured` first. Every count under it is zero when it is false, which is an absence of measurement rather than a measured zero. Past that, `chain` names the stacks the install resolved, nearest first, and `counts.gitignore` counts the managed ignore entries the target is missing.
33
34
 
34
35
  State what the report found in one line per finding before acting on any of it.
35
36
 
37
+ A `reverse` key the report does not carry at all is a CLI predating the field rather than a target with nothing unclaimed. Say the walk did not run and name the CLI version as the cause. Reading the absent key as an empty answer reports a clean target to a project nobody has looked at, and answering it from a filesystem walk of your own is worse, since that reaches a verdict the attribution the field carries is the only thing entitled to make.
38
+
36
39
  ## Route
37
40
 
38
41
  Map the stated intent, or what `## Diagnose` found, to one lifecycle phase, then act:
39
42
 
40
43
  - A domain in `unmigrated`: hand off to `migration-standards`
44
+ - An entry in `migrations`: hand off to the skill its `skill` field names, spelled as the report spells it. Both of them propose without writing, so the handoff is where this stops
41
45
  - Anything in `superseded`: tell the user which files and what replaced them. Do not move or delete them
46
+ - A folder in `unclaimed`: name it and the attribution it carries. No command moves it and the content may be the project's own, so the decision is the user's
42
47
 
43
48
  - First-time scaffold of a fresh project: hand off to `setup-init`
44
49
  - Governance rules for the project stack: hand off to `setup-gov`
@@ -210,6 +210,12 @@ code change. Scoping to those roots is what keeps the walk useful: walking the
210
210
  whole tree reports every project folder as unclaimed, which is true and says
211
211
  nothing.
212
212
 
213
+ The managed gate above applies here too, and it is the one place it surprises.
214
+ A directory holding a dropped folder and nothing else reports an empty `reverse`
215
+ rather than the folder, because it carries none of the three markers. Read an
216
+ empty section on an unmanaged target as a walk that never ran rather than as a
217
+ clean result.
218
+
213
219
  Each entry carries `rel`, a file count, and an `attribution` of `dropped`,
214
220
  `project`, or `unattributed`. A dropped folder and one the project wrote are the
215
221
  same bytes at the same path, so the verdict is traced from history rather than
@@ -55,14 +55,16 @@ Every weight and depth measure counts the text a reader is shown. A link reduces
55
55
 
56
56
  A code span is walked around rather than through, so a path quoting link or angle-bracket syntax keeps the width the page gives it. Masking inside one takes back the decision to count it, and the placeholders this toolkit writes are where that shows.
57
57
 
58
- The paragraph check measures both halves of one rule. `markdown.md` caps a paragraph at four sentences, and a sentence cap on its own is satisfied by writing fewer and longer ones: 88 paragraphs in this corpus sit inside four sentences and past the weight checkpoint, and the heaviest of those runs 1121 characters. The standard therefore states a weight beside the sentence cap, and the verb reads it as its own checkpoint.
58
+ The paragraph check measures both halves of one rule. `markdown.md` caps a paragraph at four sentences, and a sentence cap on its own is satisfied by writing fewer and longer ones: 36 paragraphs in this corpus sit inside four sentences and past the weight checkpoint, and the heaviest of those runs 1121 characters. The standard therefore states a weight beside the sentence cap, and the verb reads it as its own checkpoint.
59
59
 
60
- The paragraph weight sits at 600 and the bullet weight at 400. The two shapes measure one population, sharing a median near 170 characters with no gap behind either candidate, so the paragraph number was borrowed from the bullet rule when both checks shipped. They are separate checkpoints in the standard and separate patterns in the parser, and the sample below moved one and left the other untouched.
60
+ The paragraph weight sits at 700 and the bullet weight at 400. Both shipped at 400, because the paragraph number was borrowed from the bullet rule when the two checks landed together, and each has since been read against a sample of its own. They are separate checkpoints in the standard and separate patterns in the parser, so a read that moves one leaves the other where it is.
61
61
 
62
62
  #### The sample behind the paragraph number
63
63
 
64
64
  The checkpoint shipped at 400 as a borrowed number and was decided against a read of the prose it reports. Thirty-six findings were sampled, six from each of six weight bands, drawn at even spacing through each band ordered by path and line, and each was classed as prose a reader wants split or prose the checkpoint should not have reported.
65
65
 
66
+ Every band below was measured before the scan stopped counting link syntax as prose, so a paragraph sitting in one of these bands is heavier than a paragraph reported at the same number today. The re-sample in the section below re-reads the same range against the corrected measure and reaches the opposite verdict on it, which is the measure moving rather than the reader.
67
+
66
68
  | Band | Wants the split | Reads as written |
67
69
  | --------- | --------------- | ---------------- |
68
70
  | 400 - 425 | 1 | 5 |
@@ -78,6 +80,10 @@ Nothing inside the 500 to 600 band separated the two classes by length, which is
78
80
 
79
81
  The sample is thirty-six paragraphs against a reported population in the hundreds, and one reader classed all of them. Treat a band's rate as the order of magnitude it is rather than as a measured precision, and re-sample before moving the number again.
80
82
 
83
+ #### The re-sample that moved the number to 700
84
+
85
+ That re-sample ran once the scan stopped counting link syntax as prose. Findings at 604, 633, and 677 characters each read as an ordinary four-sentence paragraph on one topic, density arrived around 760 and was plain by 860, and the move cut the weight half of the report roughly in half while leaving the sentence cap untouched.
86
+
81
87
  A bullet, a heading, a table row, a blockquote, a blank line, and a fence each end a paragraph, so a heavy bullet is reported by the bullet check alone and never counted twice.
82
88
 
83
89
  ## Exit codes
@@ -193,5 +193,5 @@ Sync also refuses a target whose working tree is dirty, so commit or stash befor
193
193
 
194
194
  - [agents](agents/index.md): CLI flags, exit codes, and JSON output shapes
195
195
  - [AI workflow](ai-workflow.md): feature-development loop inside a toolkit-managed project
196
- - [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
196
+ - [tooling](../.claude/context/tooling.md), [governance](../.claude/context/governance/index.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
197
197
  - [sandbox](../.claude/context/sandbox/index.md): scenario catalog for verifying domain flows
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@erclx/aitk",
3
3
  "type": "module",
4
- "version": "0.72.0",
4
+ "version": "0.74.0",
5
5
  "description": "Infrastructure and quality tooling for developer workflows",
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -99,6 +99,67 @@ stage_toolkit_markdown() {
99
99
  done < <(find "$src" -maxdepth 1 -type f -name "*.md" ! -name "index.md" | sort | head -n "$count")
100
100
  }
101
101
 
102
+ # The newest top-level path history records a deletion under that the toolkit no
103
+ # longer ships. Read from history rather than hardcoded, so an arm stages a root
104
+ # the walk will actually recognize instead of a name that has since come back.
105
+ # `prompts` is preferred because it is the case measured in a real target, and
106
+ # any other dropped root exercises the same walk.
107
+ pick_dropped_root() {
108
+ local preferred="prompts"
109
+ local first=""
110
+ local candidate
111
+
112
+ while IFS= read -r candidate; do
113
+ [ -e "$PROJECT_ROOT/$candidate" ] && continue
114
+ [ "$candidate" = "$preferred" ] && {
115
+ echo "$preferred"
116
+ return 0
117
+ }
118
+ [ -n "$first" ] || first="$candidate"
119
+ done < <(git -C "$PROJECT_ROOT" log --all --diff-filter=D --name-only --format= |
120
+ awk -F/ 'NF > 1 { print $1 }' | sort -u)
121
+
122
+ echo "$first"
123
+ }
124
+
125
+ # Restores one file's exact published bytes from the commit before it was
126
+ # deleted. Content is what the attribution matches on, so a file written by hand
127
+ # would report unattributed and an arm would assert the wrong verdict.
128
+ #
129
+ # Both reads take the whole listing through a process substitution rather than a
130
+ # pipeline ending in an early exit. `set -o pipefail` is on, and a `grep -m 1`
131
+ # that matches the first line closes the pipe while git is still writing, so the
132
+ # substitution returns git's SIGPIPE status and the arm fails on a listing it
133
+ # actually read.
134
+ restore_dropped_file() {
135
+ local root="$1"
136
+ local rel="" commit="" line
137
+
138
+ while IFS= read -r line; do
139
+ case "$line" in
140
+ "$root"/*)
141
+ rel="$line"
142
+ break
143
+ ;;
144
+ esac
145
+ done < <(git -C "$PROJECT_ROOT" log --all --diff-filter=D --name-only \
146
+ --format= -- "$root/")
147
+
148
+ [ -n "$rel" ] || return 1
149
+
150
+ while IFS= read -r line; do
151
+ commit="$line"
152
+ break
153
+ done < <(git -C "$PROJECT_ROOT" log --all --diff-filter=D --format=%H -- "$rel")
154
+
155
+ [ -n "$commit" ] || return 1
156
+
157
+ mkdir -p "$(dirname "$rel")"
158
+ git -C "$PROJECT_ROOT" show "$commit^:$rel" >"$rel" || return 1
159
+
160
+ echo "$rel"
161
+ }
162
+
102
163
  # Stages one step of a scenario arm into the sandbox working directory.
103
164
  # Scenarios call this once per step so their own git operations stay between
104
165
  # the steps, where they are visible.
@@ -45,7 +45,7 @@ export const DEFAULT_CHECKPOINTS = {
45
45
  run: 40,
46
46
  peerBullet: 130,
47
47
  bullet: 400,
48
- paragraph: 600,
48
+ paragraph: 700,
49
49
  sentences: 4,
50
50
  renderWidth: 80,
51
51
  } as const
@@ -34,7 +34,7 @@ Does not govern:
34
34
 
35
35
  - Use prose by default. Reserve bullets for discrete, unrelated items.
36
36
  - Keep paragraphs to four sentences or fewer. Split longer blocks at the next logical boundary.
37
- - Past roughly 600 characters in one paragraph, folding in the lines that wrap it, split at the next logical boundary as well. A paragraph written as two long sentences satisfies the sentence cap above and still asks the reader to hold too much at once. This number sits above the bullet checkpoint because a paragraph is read straight through and a bullet is scanned.
37
+ - Past roughly 700 characters in one paragraph, folding in the lines that wrap it, split at the next logical boundary as well. A paragraph written as two long sentences satisfies the sentence cap above and still asks the reader to hold too much at once. This number sits well above the bullet checkpoint because a paragraph is read straight through and a bullet is scanned.
38
38
  - Keep bullets tight. Past roughly 400 characters in one top-level bullet, counting the lines that continue it and excluding any bullet nested under it, the overflow belongs in prose. The number is a checkpoint rather than a cap, and a bullet reading well past it means the number is wrong rather than the rule.
39
39
  - Collapse a stack of bullets narrating one subsystem into a single `###` subsection carrying one narrative. Splitting a heavy bullet into three light ones satisfies the checkpoint above and leaves the reader no better off, and subdividing a block does not lighten the bullets inside it, so the two rules answer different defects.
40
40
  - Use dashes (`-`) not asterisks (`*`) for bulleted lists