@erclx/aitk 3.10.0 → 3.11.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/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/claude-address-review/SKILL.md +10 -0
- package/claude/skills/claude-markdown-propose/REQUIREMENT.md +48 -0
- package/claude/skills/claude-markdown-propose/SKILL.md +118 -0
- package/claude/skills/claude-markdown-propose/references/format.md +107 -0
- package/claude/skills/claude-orchestrate/references/orchestrator-poll.md +4 -1
- package/claude/skills/claude-orchestrate/scripts/poll.sh +59 -13
- package/claude/skills/claude-pr-review/SKILL.md +4 -2
- package/claude/skills/claude-teach/SKILL.md +1 -1
- package/claude/skills/claude-worktree/SKILL.md +1 -1
- package/claude/skills/create-snippet/SKILL.md +1 -1
- package/claude/skills/git-branch/SKILL.md +1 -1
- package/claude/skills/git-commit/SKILL.md +1 -1
- package/claude/skills/git-issue/SKILL.md +2 -2
- package/claude/skills/git-pr/SKILL.md +5 -5
- package/claude/skills/git-split/SKILL.md +2 -2
- package/claude/skills/git-stage/SKILL.md +1 -1
- package/docs/agents/audits.md +2 -2
- package/docs/agents/records.md +1 -1
- package/docs/agents/teach.md +1 -1
- package/docs/ai-workflow.md +7 -6
- package/package.json +1 -1
- package/scripts/core/check-skill-paths.sh +1 -2
- package/scripts/core/verify.sh +0 -5
- package/src/audits/catalog.ts +20 -0
- package/src/claude/skills-audit.ts +7 -0
- package/src/commands/claude.ts +20 -6
- package/src/commands/comments.ts +6 -0
- package/src/commands/context.ts +32 -7
- package/src/commands/gov.ts +2 -2
- package/src/commands/markdown.ts +18 -2
- package/src/context/audit.ts +16 -0
- package/src/gov/test-order.ts +31 -7
- package/src/markdown/files.ts +10 -0
- package/src/records/backup.ts +1 -0
- package/src/records/validate.ts +1 -3
- package/{claude/skills/git-split/references → standards}/branch.md +0 -1
- package/{claude/skills/git-commit/references → standards}/commit.md +0 -1
- package/{claude/skills/claude-teach/references → standards}/glossary.md +0 -1
- package/standards/index.md +6 -0
- package/standards/{bundled/issue.md → issue.md} +0 -1
- package/standards/{bundled/pr.md → pr.md} +0 -1
- package/standards/{bundled/snippets.md → snippets.md} +0 -1
- package/claude/skills/claude-worktree/references/branch.md +0 -60
- package/claude/skills/create-snippet/references/snippets.md +0 -78
- package/claude/skills/git-branch/references/branch.md +0 -60
- package/claude/skills/git-issue/references/issue.md +0 -95
- package/claude/skills/git-pr/references/branch.md +0 -60
- package/claude/skills/git-pr/references/pr.md +0 -139
- package/claude/skills/git-split/references/pr.md +0 -139
- package/claude/skills/git-stage/references/commit.md +0 -73
- package/scripts/core/regen-skill-references.sh +0 -27
- package/standards/bundled/branch.md +0 -60
- package/standards/bundled/commit.md +0 -73
- package/standards/bundled/glossary.md +0 -76
package/docs/agents/audits.md
CHANGED
|
@@ -57,11 +57,11 @@ The verdict is published as a field as well as an exit code. A reader branching
|
|
|
57
57
|
|
|
58
58
|
A per-machine folder that is not on this disk reports as `absent` and moves neither the verdict nor the exit code. Every one of those folders is gitignored, so a fresh clone and every CI run carry none of them, and counting six expected absences as failures pins the verdict at `incomplete` forever. A signal that never changes is one nobody reads after the second time they see it.
|
|
59
59
|
|
|
60
|
-
Which refusals count as an absence is declared per audit rather than inferred from the corpus alone. A per-machine corpus counts a missing folder. An upstream corpus counts every reason the advisory verb publishes, since each of the three means there is nothing resolved to measure.
|
|
60
|
+
Which refusals count as an absence is declared per audit rather than inferred from the corpus alone. A per-machine corpus counts a missing folder. An upstream corpus counts every reason the advisory verb publishes, since each of the three means there is nothing resolved to measure. Several tracked audits override that default too, wherever the corpus not being there is the ordinary state of a target rather than a broken checkout: the secret scan for a project that publishes nothing, the skill corpora and the citation reach check for a project holding no skills tree, and the context audit for a project that adopted none of the folders it measures.
|
|
61
61
|
|
|
62
62
|
Everything else stays `unmeasured`. That covers a tree git cannot list, which is a broken checkout, and a package declaring no `files` field, where a publish would pack the whole tree and the scan read none of it. Both are a corpus that exists and went unread, so softening either would report a pass over a shipped tree nobody measured.
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
Each override exists because the corpus answers the wrong question for that audit. The secret scan earns its from `private: true`, the one declaration that a package is never published, so a project carrying it reports an absent corpus rather than a broken one. Without that the aggregate would report `incomplete` in every such project on every run, which is the permanent signal this section opens by rejecting.
|
|
65
65
|
|
|
66
66
|
`upstream` is the third corpus value, carried by the advisory check alone. Its count moves when someone publishes rather than when someone edits here, so it is kept out of the retained baseline for the mirror image of the reason gitignored scratch is, and an offline run reports it absent rather than pinning the verdict at `incomplete` on every machine without a network.
|
|
67
67
|
|
package/docs/agents/records.md
CHANGED
|
@@ -23,7 +23,7 @@ aitk records validate intake --json
|
|
|
23
23
|
|
|
24
24
|
It reads and never writes, and the reason splits by kind. A session record is per-machine scratch with no history behind it, so a repair that guessed wrong could not be undone. A standard installs into every target and is cited by bare filename, so a rename the verb performed would reach further than the file it moved, which is why a finding naming one says so.
|
|
25
25
|
|
|
26
|
-
`standards` reads the authoring root at `standards/` where it exists and the installed copy at `.claude/standards/` otherwise. The authoring root wins because the installed tree is generated from it in the toolkit, where a fix written to the copy is discarded by the next regen. A project that consumed the corpus holds only the second, so one precedence serves both. The walk stays flat, matching install and the catalog
|
|
26
|
+
`standards` reads the authoring root at `standards/` where it exists and the installed copy at `.claude/standards/` otherwise. The authoring root wins because the installed tree is generated from it in the toolkit, where a fix written to the copy is discarded by the next regen. A project that consumed the corpus holds only the second, so one precedence serves both. The walk stays flat, matching install and the catalog.
|
|
27
27
|
|
|
28
28
|
Nothing fires it automatically. The five record folders are gitignored, so the standards-audit hook exits early on them and any check reading changed files from git never lists one. The corpus is tracked and still unreached, since the markdown audit reads content across the files git lists and rules on no filename. The verb runs at the moment a session claims the record is finished, which is the same placement `aitk tasks validate` takes over the board.
|
|
29
29
|
|
package/docs/agents/teach.md
CHANGED
|
@@ -5,7 +5,7 @@ description: Listing learning workspaces and the ordinal a new one takes, openin
|
|
|
5
5
|
|
|
6
6
|
# Teach
|
|
7
7
|
|
|
8
|
-
Learning workspaces sit under `.claude/teach/<nn>-<topic>/`, and `standards/teach.md` fixes their layout, naming, and file formats, apart from the glossary, whose shape `standards/
|
|
8
|
+
Learning workspaces sit under `.claude/teach/<nn>-<topic>/`, and `standards/teach.md` fixes their layout, naming, and file formats, apart from the glossary, whose shape `standards/glossary.md` fixes, cited from the `claude-teach` skill, so it travels with the file wherever a promotion lands it. Every verb here resolves that folder against the main worktree root rather than against the working directory, so a session standing in a linked worktree reaches the one workspace the learner has rather than opening a second.
|
|
9
9
|
|
|
10
10
|
That root resolution is also why the writing verbs exist at all. The file-editing tools refuse a main-root path from a linked worktree and offer a worktree copy instead, and a caller naming only the destination reports a success that did not happen. A whole-file create still goes out as a shell heredoc. Changing a line inside a file that already exists has no shell route, because the stream editors are banned, so `resource` and `glossary` are the route for the two files a running workspace edits.
|
|
11
11
|
|
package/docs/ai-workflow.md
CHANGED
|
@@ -214,12 +214,13 @@ This section is the corpus the coverage claim is measured against: every name `a
|
|
|
214
214
|
|
|
215
215
|
### Check the work before it leaves the branch
|
|
216
216
|
|
|
217
|
-
| Skill
|
|
218
|
-
|
|
|
219
|
-
| `aitk:claude-review`
|
|
220
|
-
| `aitk:claude-standards-audit`
|
|
221
|
-
| `aitk:claude-
|
|
222
|
-
| `aitk:claude-ux-
|
|
217
|
+
| Skill | When to use |
|
|
218
|
+
| ------------------------------ | --------------------------------------------------------------------------------------- |
|
|
219
|
+
| `aitk:claude-review` | On the local branch diff, before anything is pushed |
|
|
220
|
+
| `aitk:claude-standards-audit` | When changed markdown has to answer to the authoring standards |
|
|
221
|
+
| `aitk:claude-markdown-propose` | When a markdown claim needs rewriting and the change should wait for an answer per file |
|
|
222
|
+
| `aitk:claude-ux-audit` | To read UI source for missing states, edge cases, and inconsistencies |
|
|
223
|
+
| `aitk:claude-ux-measure` | To start the interface and measure paint, processor, and layout cost |
|
|
223
224
|
|
|
224
225
|
### Ship it
|
|
225
226
|
|
package/package.json
CHANGED
|
@@ -20,6 +20,5 @@ matches=$(grep -rnE "$BANNED_PATH_PATTERN" "$SKILLS_DIR" || true)
|
|
|
20
20
|
echo "Shipped skills reference a repo-local path that does not exist in a target project:"
|
|
21
21
|
echo "$matches" | sed "s|^$PROJECT_ROOT/||"
|
|
22
22
|
echo
|
|
23
|
-
echo "Reach supporting prose through an aitk docs command, a
|
|
24
|
-
echo "A match inside a references/ folder is a generated copy. Fix the source under standards/bundled/."
|
|
23
|
+
echo "Reach supporting prose through an aitk docs command, a standard cited at the flat root, or inlined text."
|
|
25
24
|
exit 1
|
package/scripts/core/verify.sh
CHANGED
|
@@ -267,11 +267,6 @@ main() {
|
|
|
267
267
|
run_check "assert_hero_stamp" "The hero set disagrees with the stamp written when the image was captured. Run aitk capture assets/hero.html and commit all three files together."
|
|
268
268
|
log_info "Hero clean"
|
|
269
269
|
|
|
270
|
-
log_step "Skill references"
|
|
271
|
-
run_check "bash $PROJECT_ROOT/scripts/core/regen-skill-references.sh" "Skill-reference regen failed"
|
|
272
|
-
assert_no_drift "claude/skills/*/references" "Skill references drifted. Run bun run check and commit the updated reference files."
|
|
273
|
-
log_info "Skill references clean"
|
|
274
|
-
|
|
275
270
|
log_step "Tooling paths"
|
|
276
271
|
run_check "bash $PROJECT_ROOT/scripts/core/regen-tooling-paths.sh" "Tooling-path regen failed"
|
|
277
272
|
assert_no_drift "claude/skills/toolkit-cli/SKILL.md" "The overwrite contract drifted from what the stacks hold. Run bun run check and commit claude/skills/toolkit-cli/SKILL.md."
|
package/src/audits/catalog.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import type { SkillsAuditRefusal } from '@/claude/skills-audit'
|
|
1
2
|
import type { ReachRefusal } from '@/claude/skills-reach'
|
|
3
|
+
import type { ContextAuditRefusal } from '@/context/audit'
|
|
2
4
|
import type { AuditRefusal } from '@/deps/audit'
|
|
3
5
|
import type { RestatedRefusal } from '@/gov/restated'
|
|
4
6
|
import type { LabelAuditRefusal } from '@/labels/audit'
|
|
@@ -465,6 +467,12 @@ export const AUDITS: readonly AuditSpec[] = [
|
|
|
465
467
|
argv: ['context', 'audit', '--json'],
|
|
466
468
|
gatingExits: [EXIT_FINDINGS],
|
|
467
469
|
corpus: 'tracked',
|
|
470
|
+
// The one reason this verb refuses for that is an absence rather than a
|
|
471
|
+
// break, on the same test the reach check and the skill audit take: no
|
|
472
|
+
// target adopts `.claude/context/`, `.claude/diagrams/`, and
|
|
473
|
+
// `.claude/wireframes/` all at once, so without the allowance every such
|
|
474
|
+
// project reports the verb unmeasured on every run and never changes.
|
|
475
|
+
absentReasons: ['no-folders'] satisfies ContextAuditRefusal[],
|
|
468
476
|
counts: contextCounts,
|
|
469
477
|
},
|
|
470
478
|
{
|
|
@@ -484,6 +492,10 @@ export const AUDITS: readonly AuditSpec[] = [
|
|
|
484
492
|
argv: ['claude', 'skills', 'audit', '--json'],
|
|
485
493
|
gatingExits: [EXIT_FINDINGS],
|
|
486
494
|
corpus: 'tracked',
|
|
495
|
+
// The one reason this verb refuses for, and it is an absence for the same
|
|
496
|
+
// reason the reach check's is: a project carrying neither `claude/skills/`
|
|
497
|
+
// nor `.claude/skills/` has adopted no skill convention this audit reads.
|
|
498
|
+
absentReasons: ['no-corpus'] satisfies SkillsAuditRefusal[],
|
|
487
499
|
counts: skillCounts,
|
|
488
500
|
},
|
|
489
501
|
{
|
|
@@ -520,6 +532,14 @@ export const AUDITS: readonly AuditSpec[] = [
|
|
|
520
532
|
argv: ['records', 'validate', kind, '--json'],
|
|
521
533
|
gatingExits: [],
|
|
522
534
|
corpus,
|
|
535
|
+
// `standards` is the one tracked kind here, so it takes none of the
|
|
536
|
+
// per-machine default the other five inherit from their corpus. A target
|
|
537
|
+
// reads standards through `aitk standards` rather than a copy in its own
|
|
538
|
+
// tree, so carrying neither `standards/` nor `.claude/standards/` is the
|
|
539
|
+
// ordinary state of every project but this repository.
|
|
540
|
+
...(kind === 'standards' && {
|
|
541
|
+
absentReasons: ['no-folder'] satisfies RecordRefusal[],
|
|
542
|
+
}),
|
|
523
543
|
counts: findingsOnly,
|
|
524
544
|
})),
|
|
525
545
|
{
|
|
@@ -25,6 +25,13 @@ export const CORPORA: readonly string[] = [
|
|
|
25
25
|
join('.claude', 'skills'),
|
|
26
26
|
]
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* The one reason this audit refuses. A project carrying neither corpus is the
|
|
30
|
+
* ordinary state of a target that has not adopted either skills convention,
|
|
31
|
+
* the same absence `no-skills` reads for the shipped citation reach check.
|
|
32
|
+
*/
|
|
33
|
+
export type SkillsAuditRefusal = 'no-corpus'
|
|
34
|
+
|
|
28
35
|
/** Kebab-case, which the standard states as no spaces, capitals, or underscores. */
|
|
29
36
|
const KEBAB_CASE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/
|
|
30
37
|
|
package/src/commands/claude.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
REQUIREMENT_SECTIONS,
|
|
21
21
|
type SkillFinding,
|
|
22
22
|
type SkillsAudit,
|
|
23
|
+
type SkillsAuditRefusal,
|
|
23
24
|
} from '@/claude/skills-audit'
|
|
24
25
|
import {
|
|
25
26
|
type RoutingRefusal,
|
|
@@ -862,8 +863,11 @@ async function runSkillsAudit(
|
|
|
862
863
|
|
|
863
864
|
if (report.corpora.length === 0) {
|
|
864
865
|
return refuseAudit(
|
|
866
|
+
'no-corpus',
|
|
865
867
|
`No skill corpus under ${root}. Looked for ${CORPORA.join(' and ')}.`,
|
|
866
868
|
gateOnly,
|
|
869
|
+
root,
|
|
870
|
+
opts.json ?? false,
|
|
867
871
|
)
|
|
868
872
|
}
|
|
869
873
|
|
|
@@ -910,16 +914,26 @@ async function runSkillsAudit(
|
|
|
910
914
|
return auditExitCode(report)
|
|
911
915
|
}
|
|
912
916
|
|
|
913
|
-
function refuseAudit(
|
|
917
|
+
function refuseAudit(
|
|
918
|
+
reason: SkillsAuditRefusal,
|
|
919
|
+
message: string,
|
|
920
|
+
gateOnly: boolean,
|
|
921
|
+
root: string,
|
|
922
|
+
emitJson: boolean,
|
|
923
|
+
): number {
|
|
914
924
|
if (gateOnly) {
|
|
915
925
|
frameError(message)
|
|
916
|
-
|
|
926
|
+
} else {
|
|
927
|
+
intro('aitk claude skills audit')
|
|
928
|
+
logStep('Refused')
|
|
929
|
+
logWarn(message)
|
|
930
|
+
outro()
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
if (emitJson) {
|
|
934
|
+
process.stdout.write(`${JSON.stringify({ root, reason, message })}\n`)
|
|
917
935
|
}
|
|
918
936
|
|
|
919
|
-
intro('aitk claude skills audit')
|
|
920
|
-
logStep('Refused')
|
|
921
|
-
logWarn(message)
|
|
922
|
-
outro()
|
|
923
937
|
return 1
|
|
924
938
|
}
|
|
925
939
|
|
package/src/commands/comments.ts
CHANGED
package/src/commands/context.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolve } from 'node:path'
|
|
2
2
|
import type { Command } from 'commander'
|
|
3
3
|
import {
|
|
4
|
+
type ContextAuditRefusal,
|
|
4
5
|
type EntryReport,
|
|
5
6
|
governsContent,
|
|
6
7
|
type LengthCause,
|
|
@@ -157,12 +158,17 @@ async function runAudit(
|
|
|
157
158
|
// short a required section, which is the pass a gate exists to prevent.
|
|
158
159
|
if (gateOnly && widened) {
|
|
159
160
|
return refuse(
|
|
161
|
+
'conflicting-options',
|
|
160
162
|
'--citations-only runs the citation check alone, so --gate would widen the gate to findings the run never measures. Pass one.',
|
|
161
163
|
gateOnly,
|
|
164
|
+
root,
|
|
165
|
+
opts.json ?? false,
|
|
162
166
|
)
|
|
163
167
|
}
|
|
164
168
|
|
|
165
|
-
if (typeof names === 'string')
|
|
169
|
+
if (typeof names === 'string') {
|
|
170
|
+
return refuse('bad-folder-list', names, gateOnly, root, opts.json ?? false)
|
|
171
|
+
}
|
|
166
172
|
|
|
167
173
|
// The root base is opt-in. A target carrying a root `wireframes/` would
|
|
168
174
|
// otherwise be audited against a standard it never adopted, on a bare run
|
|
@@ -173,8 +179,11 @@ async function runAudit(
|
|
|
173
179
|
})
|
|
174
180
|
if (folders.length === 0) {
|
|
175
181
|
return refuse(
|
|
182
|
+
'no-folders',
|
|
176
183
|
`No audited folder found ${named ? 'under .claude/ or the project root' : 'under .claude/'}. Looked for: ${names.join(', ')}.`,
|
|
177
184
|
gateOnly,
|
|
185
|
+
root,
|
|
186
|
+
opts.json ?? false,
|
|
178
187
|
)
|
|
179
188
|
}
|
|
180
189
|
|
|
@@ -190,16 +199,22 @@ async function runAudit(
|
|
|
190
199
|
const cited = presentNames(folders)
|
|
191
200
|
if (gateOnly && cited.length === 0) {
|
|
192
201
|
return refuse(
|
|
202
|
+
'no-citation-scope',
|
|
193
203
|
`The citation check spells the .claude/ prefix and no audited folder resolved there. Looked for: ${names.join(', ')}.`,
|
|
194
204
|
gateOnly,
|
|
205
|
+
root,
|
|
206
|
+
opts.json ?? false,
|
|
195
207
|
)
|
|
196
208
|
}
|
|
197
209
|
|
|
198
210
|
const citations = await auditCitations(root, cited)
|
|
199
211
|
if (citations.kind === 'unavailable') {
|
|
200
212
|
return refuse(
|
|
213
|
+
'no-git',
|
|
201
214
|
'git could not list the tree, so no citation was checked. Run inside a git repository.',
|
|
202
215
|
gateOnly,
|
|
216
|
+
root,
|
|
217
|
+
opts.json ?? false,
|
|
203
218
|
)
|
|
204
219
|
}
|
|
205
220
|
|
|
@@ -308,16 +323,26 @@ async function runAudit(
|
|
|
308
323
|
return gating ? EXIT_GATE : 0
|
|
309
324
|
}
|
|
310
325
|
|
|
311
|
-
function refuse(
|
|
326
|
+
function refuse(
|
|
327
|
+
reason: ContextAuditRefusal,
|
|
328
|
+
message: string,
|
|
329
|
+
gateOnly: boolean,
|
|
330
|
+
root: string,
|
|
331
|
+
emitJson: boolean,
|
|
332
|
+
): number {
|
|
312
333
|
if (gateOnly) {
|
|
313
334
|
frameError(message)
|
|
314
|
-
|
|
335
|
+
} else {
|
|
336
|
+
intro('aitk context audit')
|
|
337
|
+
logStep('Refused')
|
|
338
|
+
logWarn(message)
|
|
339
|
+
outro()
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
if (emitJson) {
|
|
343
|
+
process.stdout.write(`${JSON.stringify({ root, reason, message })}\n`)
|
|
315
344
|
}
|
|
316
345
|
|
|
317
|
-
intro('aitk context audit')
|
|
318
|
-
logStep('Refused')
|
|
319
|
-
logWarn(message)
|
|
320
|
-
outro()
|
|
321
346
|
return 1
|
|
322
347
|
}
|
|
323
348
|
|
package/src/commands/gov.ts
CHANGED
|
@@ -506,12 +506,12 @@ function runTestOrder(opts: TestOrderOptions): number {
|
|
|
506
506
|
if (report.kind === 'unreadable') {
|
|
507
507
|
intro('aitk gov test-order')
|
|
508
508
|
logStep('Refused')
|
|
509
|
-
logError(report.
|
|
509
|
+
logError(report.message)
|
|
510
510
|
outro()
|
|
511
511
|
|
|
512
512
|
if (emitJson) {
|
|
513
513
|
process.stdout.write(
|
|
514
|
-
`${JSON.stringify({
|
|
514
|
+
`${JSON.stringify({ root, reason: report.reason, message: report.message })}\n`,
|
|
515
515
|
)
|
|
516
516
|
}
|
|
517
517
|
|
package/src/commands/markdown.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { readFile } from 'node:fs/promises'
|
|
|
2
2
|
import { resolve } from 'node:path'
|
|
3
3
|
import type { Command } from 'commander'
|
|
4
4
|
import { BAN_SETS, emptyBanSets } from '@/markdown/bans'
|
|
5
|
-
import { resolveMarkdown } from '@/markdown/files'
|
|
5
|
+
import { type MarkdownAuditRefusal, resolveMarkdown } from '@/markdown/files'
|
|
6
6
|
import { isGating } from '@/markdown/gate'
|
|
7
7
|
import {
|
|
8
8
|
type BanFinding,
|
|
@@ -119,15 +119,21 @@ async function runAudit(
|
|
|
119
119
|
|
|
120
120
|
if (scope.kind === 'unavailable') {
|
|
121
121
|
return refuse(
|
|
122
|
+
'no-git',
|
|
122
123
|
'git could not list the tree, so no corpus was built. Run inside a git repository.',
|
|
124
|
+
root,
|
|
125
|
+
opts.json ?? false,
|
|
123
126
|
)
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
if (scope.files.length === 0) {
|
|
127
130
|
return refuse(
|
|
131
|
+
paths.length === 0 ? 'no-markdown' : 'no-match',
|
|
128
132
|
paths.length === 0
|
|
129
133
|
? 'No markdown file in the tree.'
|
|
130
134
|
: `No markdown file matched: ${scope.unmatched.join(', ')}`,
|
|
135
|
+
root,
|
|
136
|
+
opts.json ?? false,
|
|
131
137
|
)
|
|
132
138
|
}
|
|
133
139
|
|
|
@@ -203,11 +209,21 @@ async function runAudit(
|
|
|
203
209
|
return gating ? EXIT_GATE : 0
|
|
204
210
|
}
|
|
205
211
|
|
|
206
|
-
function refuse(
|
|
212
|
+
function refuse(
|
|
213
|
+
reason: MarkdownAuditRefusal,
|
|
214
|
+
message: string,
|
|
215
|
+
root: string,
|
|
216
|
+
emitJson: boolean,
|
|
217
|
+
): number {
|
|
207
218
|
intro('aitk markdown audit')
|
|
208
219
|
logStep('Refused')
|
|
209
220
|
logWarn(message)
|
|
210
221
|
outro()
|
|
222
|
+
|
|
223
|
+
if (emitJson) {
|
|
224
|
+
process.stdout.write(`${JSON.stringify({ root, reason, message })}\n`)
|
|
225
|
+
}
|
|
226
|
+
|
|
211
227
|
return EXIT_REFUSED
|
|
212
228
|
}
|
|
213
229
|
|
package/src/context/audit.ts
CHANGED
|
@@ -15,6 +15,22 @@ import { isStubSeed } from '@/seed-marker'
|
|
|
15
15
|
* any markdown file, so they are stated at the attribute tier and measured by
|
|
16
16
|
* `aitk markdown audit` rather than here.
|
|
17
17
|
*/
|
|
18
|
+
/**
|
|
19
|
+
* Every reason `aitk context audit` refuses for.
|
|
20
|
+
*
|
|
21
|
+
* `no-folders` is the one ordinary absence: a project that never adopted
|
|
22
|
+
* `.claude/context/`, `.claude/diagrams/`, or `.claude/wireframes/` names no
|
|
23
|
+
* corpus this audit can measure, the same state `no-skills` reads for the
|
|
24
|
+
* skill corpora. The other four are a malformed invocation or a checkout git
|
|
25
|
+
* cannot read, which stay a break rather than an absence.
|
|
26
|
+
*/
|
|
27
|
+
export type ContextAuditRefusal =
|
|
28
|
+
| 'conflicting-options'
|
|
29
|
+
| 'bad-folder-list'
|
|
30
|
+
| 'no-folders'
|
|
31
|
+
| 'no-citation-scope'
|
|
32
|
+
| 'no-git'
|
|
33
|
+
|
|
18
34
|
export const LENGTH_CHECKPOINT = 150
|
|
19
35
|
|
|
20
36
|
/**
|
package/src/gov/test-order.ts
CHANGED
|
@@ -44,6 +44,21 @@ export interface Scope {
|
|
|
44
44
|
readonly testSuffixes: readonly string[]
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Every reason `aitk gov test-order` refuses for.
|
|
49
|
+
*
|
|
50
|
+
* None is an ordinary absence. A depth-1 checkout falls back to the root
|
|
51
|
+
* commit and reports zero rather than reaching any of these, so what remains
|
|
52
|
+
* is a repository with no commit at all or a git operation that failed
|
|
53
|
+
* outright, both a broken checkout rather than a target's ordinary state.
|
|
54
|
+
*/
|
|
55
|
+
export type TestOrderRefusal =
|
|
56
|
+
| 'no-history'
|
|
57
|
+
| 'no-log'
|
|
58
|
+
| 'no-tree'
|
|
59
|
+
| 'bad-base'
|
|
60
|
+
| 'no-base'
|
|
61
|
+
|
|
47
62
|
export type TestOrderReport =
|
|
48
63
|
| {
|
|
49
64
|
readonly kind: 'measured'
|
|
@@ -56,7 +71,11 @@ export type TestOrderReport =
|
|
|
56
71
|
/** Changed paths outside the pairing's reach, named rather than counted. */
|
|
57
72
|
readonly ignored: readonly string[]
|
|
58
73
|
}
|
|
59
|
-
| {
|
|
74
|
+
| {
|
|
75
|
+
readonly kind: 'unreadable'
|
|
76
|
+
readonly reason: TestOrderRefusal
|
|
77
|
+
readonly message: string
|
|
78
|
+
}
|
|
60
79
|
|
|
61
80
|
export interface TestOrderOptions {
|
|
62
81
|
/** The far side of the range, defaulting to the merge base against the trunk. */
|
|
@@ -258,7 +277,8 @@ export function readTestOrder(
|
|
|
258
277
|
if (head === undefined) {
|
|
259
278
|
return {
|
|
260
279
|
kind: 'unreadable',
|
|
261
|
-
reason:
|
|
280
|
+
reason: 'no-history',
|
|
281
|
+
message: `No git history under ${root}. History is the only surface carrying the ordering, so there is nothing to read.`,
|
|
262
282
|
}
|
|
263
283
|
}
|
|
264
284
|
|
|
@@ -277,7 +297,8 @@ export function readTestOrder(
|
|
|
277
297
|
if (log === undefined) {
|
|
278
298
|
return {
|
|
279
299
|
kind: 'unreadable',
|
|
280
|
-
reason:
|
|
300
|
+
reason: 'no-log',
|
|
301
|
+
message: `Reading history between ${base} and HEAD failed under ${root}.`,
|
|
281
302
|
}
|
|
282
303
|
}
|
|
283
304
|
|
|
@@ -285,7 +306,8 @@ export function readTestOrder(
|
|
|
285
306
|
if (tree === undefined) {
|
|
286
307
|
return {
|
|
287
308
|
kind: 'unreadable',
|
|
288
|
-
reason:
|
|
309
|
+
reason: 'no-tree',
|
|
310
|
+
message: `Reading the tree at ${base} failed under ${root}. Without it a test written before the range reads as absent.`,
|
|
289
311
|
}
|
|
290
312
|
}
|
|
291
313
|
|
|
@@ -323,13 +345,14 @@ function resolveBase(
|
|
|
323
345
|
root: string,
|
|
324
346
|
ref: string | undefined,
|
|
325
347
|
head: string,
|
|
326
|
-
): string | { kind: 'unreadable'; reason: string } {
|
|
348
|
+
): string | { kind: 'unreadable'; reason: TestOrderRefusal; message: string } {
|
|
327
349
|
if (ref !== undefined) {
|
|
328
350
|
const resolved = revParse(root, ref)
|
|
329
351
|
if (resolved === undefined) {
|
|
330
352
|
return {
|
|
331
353
|
kind: 'unreadable',
|
|
332
|
-
reason:
|
|
354
|
+
reason: 'bad-base',
|
|
355
|
+
message: `Ref ${ref} resolves to no commit in ${root}. Pass a commit this tree carries.`,
|
|
333
356
|
}
|
|
334
357
|
}
|
|
335
358
|
return resolved
|
|
@@ -345,7 +368,8 @@ function resolveBase(
|
|
|
345
368
|
if (rootCommits === undefined || rootCommits === '') {
|
|
346
369
|
return {
|
|
347
370
|
kind: 'unreadable',
|
|
348
|
-
reason:
|
|
371
|
+
reason: 'no-base',
|
|
372
|
+
message: `No base resolves against ${root}. Fetch origin or pass --base.`,
|
|
349
373
|
}
|
|
350
374
|
}
|
|
351
375
|
|
package/src/markdown/files.ts
CHANGED
|
@@ -19,6 +19,16 @@ export type FileScope =
|
|
|
19
19
|
}
|
|
20
20
|
| { readonly kind: 'unavailable' }
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Every reason `aitk markdown audit` refuses for.
|
|
24
|
+
*
|
|
25
|
+
* None is an ordinary absence. `no-git` is a broken checkout the way it is
|
|
26
|
+
* for the secret scan, and a tree tracked by git carrying no markdown file at
|
|
27
|
+
* all, or an argument matching none, is a corpus this run could not build
|
|
28
|
+
* rather than a target that adopted none of the convention this check reads.
|
|
29
|
+
*/
|
|
30
|
+
export type MarkdownAuditRefusal = 'no-git' | 'no-markdown' | 'no-match'
|
|
31
|
+
|
|
22
32
|
function isDirectory(path: string): boolean {
|
|
23
33
|
try {
|
|
24
34
|
return statSync(path).isDirectory()
|
package/src/records/backup.ts
CHANGED
package/src/records/validate.ts
CHANGED
|
@@ -1115,9 +1115,7 @@ export async function validateRecords(
|
|
|
1115
1115
|
)
|
|
1116
1116
|
}
|
|
1117
1117
|
|
|
1118
|
-
// The walk stays flat, matching install and the catalog.
|
|
1119
|
-
// is a subfolder whose members are named for the skill that reads them rather
|
|
1120
|
-
// than for a path they govern, so the derivation below reports every one.
|
|
1118
|
+
// The walk stays flat, matching install and the catalog.
|
|
1121
1119
|
if (kind === 'standards') {
|
|
1122
1120
|
return validateFiles(
|
|
1123
1121
|
dir,
|
package/standards/index.md
CHANGED
|
@@ -8,14 +8,19 @@ subtitle: Reference docs for consistent authoring across the toolkit and target
|
|
|
8
8
|
Reference docs for consistent authoring across the toolkit and target projects.
|
|
9
9
|
|
|
10
10
|
- [Architecture reference](architecture.md): Shape and content rules for .claude/ARCHITECTURE.md
|
|
11
|
+
- [Branch reference](branch.md): Branch naming format and type conventions
|
|
12
|
+
- [Commit reference](commit.md): Commit message format and type conventions
|
|
11
13
|
- [Context entry reference](context.md): Shape and content rules for .claude/context/<domain>.md entries
|
|
12
14
|
- [Design reference](design.md): Shape and content rules for .claude/DESIGN.md
|
|
13
15
|
- [Diagram reference](diagrams.md): Shape and content rules for .claude/diagrams/<kind>.md files
|
|
16
|
+
- [Glossary reference](glossary.md): Frontmatter, entry shape, ordering, and the rules deciding which terms a glossary carries
|
|
14
17
|
- [Groundwork reference](groundwork.md): Folder layout, reserved numbering, frontmatter and dating, required file contents, and conventions for a measurement track
|
|
15
18
|
- [Intake reference](intake.md): Folder layout, reserved index number, frontmatter and dating, the item template, the answer contract, and retrieval
|
|
19
|
+
- [Issue reference](issue.md): GitHub issue title, labels, and body conventions
|
|
16
20
|
- [Markdown reference](markdown.md): Headings, paragraph and list structure, code spans, the date form, punctuation, emphasis, file references, banned words, and frontmatter wording
|
|
17
21
|
- [Memory reference](memory.md): Filename and type prefix, frontmatter, the body shape per type, links between entries, and the lifecycle from write to retire
|
|
18
22
|
- [Plan reference](plan.md): Filename and slug, required sections, the suggested-and-answer contract, and the lifecycle from the live folder to the archive
|
|
23
|
+
- [Pull request reference](pr.md): Pull request title and body conventions
|
|
19
24
|
- [Publish reference](publish.md): Scan an author runs against finished text, the cross-reference form each destination takes, and the response to an unreadable source
|
|
20
25
|
- [Readme reference](readme.md): Readme voice, structure, and content conventions
|
|
21
26
|
- [Requirements reference](requirements.md): Shape and content rules for .claude/REQUIREMENTS.md
|
|
@@ -23,6 +28,7 @@ Reference docs for consistent authoring across the toolkit and target projects.
|
|
|
23
28
|
- [Session map reference](session.md): Filename and location, the sections a handoff carries, the write and read procedures, and how a role extends it
|
|
24
29
|
- [Claude skill reference](skill.md): Claude skill structure and authoring rules
|
|
25
30
|
- [Slug reference](slug.md): Transform from a git branch name to a slug, and the three responses to an empty result
|
|
31
|
+
- [Snippet reference](snippets.md): Snippet reference and authoring conventions
|
|
26
32
|
- [Standard reference](standard.md): Shape and content rules for authoring a standard
|
|
27
33
|
- [Tasks reference](tasks.md): Folder layout, filename convention, readiness groups, and content rules for .claude/tasks/
|
|
28
34
|
- [Teach reference](teach.md): Workspace layout, ordinal naming, frontmatter, and the mission and learning-record formats for a learning workspace
|