@erclx/canon 5.43.0 → 5.44.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/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/backlog-triage/references/apply.md +6 -6
- package/claude/skills/canon-cli/SKILL.md +4 -4
- package/claude/skills/canvas-board/REQUIREMENT.md +1 -1
- package/claude/skills/canvas-board/SKILL.md +1 -1
- package/claude/skills/context-fold/references/context.md +1 -1
- package/claude/skills/create-skill/REQUIREMENT.md +1 -1
- package/claude/skills/create-skill/SKILL.md +2 -2
- package/claude/skills/create-skill/references/skill-requirement.md +68 -9
- package/claude/skills/create-skill/references/skill.md +1 -1
- package/claude/skills/design-identity/REQUIREMENT.md +40 -0
- package/claude/skills/design-identity/SKILL.md +138 -0
- package/claude/skills/{draft-identity → design-identity}/references/card-route.md +2 -2
- package/claude/skills/design-taste/REQUIREMENT.md +4 -4
- package/claude/skills/design-taste/SKILL.md +2 -2
- package/claude/skills/design-taste/references/kinds.md +1 -1
- package/claude/skills/design-taste/references/preflight.md +1 -1
- package/claude/skills/draft-and-pick/REQUIREMENT.md +8 -69
- package/claude/skills/draft-and-pick/SKILL.md +3 -153
- package/claude/skills/draft-doc/REQUIREMENT.md +9 -30
- package/claude/skills/draft-doc/SKILL.md +4 -76
- package/claude/skills/draft-figure/REQUIREMENT.md +8 -42
- package/claude/skills/draft-figure/SKILL.md +3 -77
- package/claude/skills/draft-identity/REQUIREMENT.md +8 -23
- package/claude/skills/draft-identity/SKILL.md +3 -130
- package/claude/skills/draft-ready/REQUIREMENT.md +8 -37
- package/claude/skills/draft-ready/SKILL.md +3 -128
- package/claude/skills/draft-screencast/REQUIREMENT.md +8 -23
- package/claude/skills/draft-screencast/SKILL.md +3 -62
- package/claude/skills/draft-slides/REQUIREMENT.md +8 -27
- package/claude/skills/draft-slides/SKILL.md +3 -72
- package/claude/skills/draw-figure/REQUIREMENT.md +59 -0
- package/claude/skills/draw-figure/SKILL.md +85 -0
- package/claude/skills/{draft-figure → draw-figure}/references/architecture-set.md +2 -2
- package/claude/skills/{draft-figure → draw-figure}/references/hand-drawn.md +2 -2
- package/claude/skills/{draft-figure → draw-figure}/references/mermaid-path.md +2 -2
- package/claude/skills/{draft-figure → draw-figure}/references/mermaid.md +1 -1
- package/claude/skills/make-deck/REQUIREMENT.md +44 -0
- package/claude/skills/make-deck/SKILL.md +80 -0
- package/claude/skills/markdown-craft/REQUIREMENT.md +1 -1
- package/claude/skills/markdown-craft/SKILL.md +2 -2
- package/claude/skills/pick-by-eye/REQUIREMENT.md +86 -0
- package/claude/skills/pick-by-eye/SKILL.md +161 -0
- package/claude/skills/plan-feature/SKILL.md +2 -1
- package/claude/skills/plan-feature/references/plan.md +24 -10
- package/claude/skills/plan-groundwork/SKILL.md +1 -1
- package/claude/skills/plan-intake/SKILL.md +1 -1
- package/claude/skills/plan-ready/REQUIREMENT.md +54 -0
- package/claude/skills/plan-ready/SKILL.md +136 -0
- package/claude/skills/{draft-ready → plan-ready}/references/assembly.md +1 -1
- package/claude/skills/{draft-ready → plan-ready}/references/ready.md +1 -1
- package/claude/skills/record-screencast/REQUIREMENT.md +3 -3
- package/claude/skills/record-screencast/SKILL.md +2 -2
- package/claude/skills/role-orchestrator/REQUIREMENT.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +5 -2
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-refill.md +18 -13
- package/claude/skills/role-planner/SKILL.md +2 -1
- package/claude/skills/role-worker/SKILL.md +9 -0
- package/claude/skills/script-screencast/REQUIREMENT.md +40 -0
- package/claude/skills/script-screencast/SKILL.md +70 -0
- package/claude/skills/{draft-screencast → script-screencast}/references/beats-template.md +2 -2
- package/claude/skills/search-craft/REQUIREMENT.md +4 -4
- package/claude/skills/search-craft/SKILL.md +1 -1
- package/claude/skills/target-setup/SKILL.md +1 -0
- package/claude/skills/task-board/SKILL.md +11 -9
- package/claude/skills/task-board/references/board.md +15 -12
- package/claude/skills/teach-workspace/SKILL.md +1 -1
- package/claude/skills/teach-workspace/references/lesson-craft.md +2 -2
- package/claude/skills/ui-checklist/SKILL.md +2 -0
- package/claude/skills/write-doc/REQUIREMENT.md +46 -0
- package/claude/skills/write-doc/SKILL.md +83 -0
- package/claude/skills/{draft-doc → write-doc}/references/context.md +2 -2
- package/claude/skills/{draft-doc → write-doc}/references/docs.md +2 -2
- package/claude/skills/{draft-doc → write-doc}/references/readme.md +2 -2
- package/claude/skills/{draft-doc → write-doc}/references/wiki-kind.md +3 -3
- package/docs/agents/markdown-audit.md +4 -2
- package/docs/agents/migrate.md +2 -2
- package/docs/target/migrations.md +17 -4
- package/docs/target/projects.md +1 -1
- package/docs/workflow/parallel-features.md +3 -2
- package/docs/workflow/skill-map-on-request.md +18 -12
- package/docs/workflow/skill-map.md +13 -12
- package/docs/workflow/visual-design-workflow.md +2 -2
- package/governance/ledger/framework/astro.md +73 -0
- package/governance/ledger/framework/fastapi.md +76 -0
- package/governance/ledger/framework/nextjs.md +86 -0
- package/governance/ledger/framework/react.md +83 -0
- package/governance/ledger/framework/shadcn.md +60 -0
- package/governance/ledger/framework/tailwind.md +55 -0
- package/governance/rules/framework/astro.md +12 -14
- package/governance/rules/framework/fastapi.md +20 -24
- package/governance/rules/framework/nextjs.md +19 -27
- package/governance/rules/framework/react.md +22 -33
- package/governance/rules/framework/shadcn.md +11 -15
- package/governance/rules/framework/tailwind.md +12 -14
- package/governance/rules/writing/markdown.md +1 -1
- package/package.json +1 -1
- package/src/claude/cases/authoring.ts +15 -3
- package/src/claude/cases/misc.ts +12 -0
- package/src/claude/cases/workflow.ts +19 -1
- package/src/claude/skills-audit.ts +113 -1
- package/src/commands/claude.ts +34 -11
- package/src/commands/demo.ts +1 -1
- package/src/commands/markdown.ts +1 -1
- package/src/commands/migrate.ts +4 -0
- package/src/commands/pr.ts +30 -6
- package/src/commands/tasks.ts +383 -2
- package/src/context/audit.ts +1 -1
- package/src/demo/beats.ts +1 -1
- package/src/gov/audit.ts +3 -2
- package/src/markdown/scope.ts +45 -35
- package/src/migrate/record-layout.ts +1 -1
- package/src/migrate/skill-names.ts +68 -21
- package/src/pr/frames.ts +14 -3
- package/src/records/validate.ts +31 -0
- package/src/tasks/board.ts +854 -0
- package/src/tasks/record.ts +20 -1
- package/src/tasks/validate.ts +206 -9
- package/tooling/astro/configs/card-src/pages/og-card.astro +1 -1
- package/tooling/astro/reference.md +2 -2
- package/tooling/claude/seeds/.claude/hooks/board-validate.sh +65 -0
- package/tooling/claude/seeds/.claude/settings.json +4 -0
- /package/claude/skills/{draft-figure → draw-figure}/references/figures.md +0 -0
- /package/claude/skills/{draft-slides → make-deck}/references/deck.md +0 -0
- /package/claude/skills/{draft-and-pick → pick-by-eye}/references/live-arms.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/agent-facing.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/application.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/cli.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/library.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/readme/plugin.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/requirements.md +0 -0
- /package/claude/skills/{draft-doc → write-doc}/references/wiki.md +0 -0
|
@@ -2,7 +2,7 @@ import { existsSync } from 'node:fs'
|
|
|
2
2
|
import { dirname, join } from 'node:path'
|
|
3
3
|
import { isFamilyKey } from '@/claude/skills-families'
|
|
4
4
|
import { parseFrontmatter, readField } from '@/indexes/frontmatter'
|
|
5
|
-
import { bodyLines, maskDisplayed } from '@/markdown/scan'
|
|
5
|
+
import { type BodyLine, bodyLines, maskDisplayed } from '@/markdown/scan'
|
|
6
6
|
|
|
7
7
|
/** Returned when a skill folder carries no `REQUIREMENT.md`, the gating check. */
|
|
8
8
|
export const EXIT_MISSING_REQUIREMENT = 2
|
|
@@ -13,6 +13,19 @@ export const DESCRIPTION_LIMIT = 1024
|
|
|
13
13
|
/** The two headings the requirement template declares, matched at any level. */
|
|
14
14
|
export const REQUIREMENT_SECTIONS: readonly string[] = ['Gap', 'Must']
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* The caps `claude/skills/create-skill/references/skill-requirement.md` states.
|
|
18
|
+
* Words count body tokens carrying a letter or digit, so a heading or bullet
|
|
19
|
+
* marker costs nothing and frontmatter is left out.
|
|
20
|
+
*/
|
|
21
|
+
export const REQUIREMENT_LIMITS = { bullets: 5, words: 400 } as const
|
|
22
|
+
|
|
23
|
+
/** The section the standard retired into Must not, matched at any level. */
|
|
24
|
+
export const RETIRED_REQUIREMENT_SECTION = 'Guards'
|
|
25
|
+
|
|
26
|
+
/** The section that states today's failures, where history is reported. */
|
|
27
|
+
const GAP_SECTION = 'Gap'
|
|
28
|
+
|
|
16
29
|
/** The one tree that ships to a target, and the only one the practice list names. */
|
|
17
30
|
export const SHIPPED_CORPUS = join('claude', 'skills')
|
|
18
31
|
|
|
@@ -100,6 +113,13 @@ const SECTION = /^##\s+(.+?)\s*$/
|
|
|
100
113
|
*/
|
|
101
114
|
const ISO_DATE = /\b\d{4}-\d{2}-\d{2}\b/g
|
|
102
115
|
|
|
116
|
+
/** An issue or pull request number, which a heading's `#` never matches. */
|
|
117
|
+
const ISSUE_NUMBER = /(?<![\w&/#])#\d+\b/g
|
|
118
|
+
|
|
119
|
+
const TOP_LEVEL_BULLET = /^(?:[-*+]|\d+[.)])\s/
|
|
120
|
+
|
|
121
|
+
const WORD = /\S*[\p{L}\p{N}]\S*/gu
|
|
122
|
+
|
|
103
123
|
export interface SkillFinding {
|
|
104
124
|
readonly rel: string
|
|
105
125
|
readonly detail: string
|
|
@@ -126,6 +146,7 @@ export interface SkillsAudit {
|
|
|
126
146
|
readonly nameMismatch: readonly SkillFinding[]
|
|
127
147
|
readonly longDescription: readonly SkillFinding[]
|
|
128
148
|
readonly requirementSections: readonly SkillFinding[]
|
|
149
|
+
readonly requirementLimits: readonly SkillFinding[]
|
|
129
150
|
readonly datedProvenance: readonly SkillFinding[]
|
|
130
151
|
readonly practiceShape: readonly SkillFinding[]
|
|
131
152
|
readonly referenceContents: readonly SkillFinding[]
|
|
@@ -142,6 +163,7 @@ interface SkillSource {
|
|
|
142
163
|
readonly family: string | undefined
|
|
143
164
|
/** Undefined when the folder carries no `REQUIREMENT.md` at all. */
|
|
144
165
|
readonly requirementHeadings: readonly string[] | undefined
|
|
166
|
+
readonly requirement: string | undefined
|
|
145
167
|
/** Every H2 in `SKILL.md` outside a fence, for the practice shape. */
|
|
146
168
|
readonly sections: readonly string[]
|
|
147
169
|
readonly hasLedger: boolean
|
|
@@ -198,6 +220,7 @@ export async function auditSkills(root: string): Promise<SkillsAudit> {
|
|
|
198
220
|
nameMismatch: sources.flatMap(nameFindings),
|
|
199
221
|
longDescription: sources.flatMap(lengthFindings),
|
|
200
222
|
requirementSections: sources.flatMap(sectionFindings),
|
|
223
|
+
requirementLimits: sources.flatMap(limitFindings),
|
|
201
224
|
datedProvenance: sources.flatMap((source) =>
|
|
202
225
|
source.bodies.flatMap(dateFindings),
|
|
203
226
|
),
|
|
@@ -269,6 +292,7 @@ async function readSkill(
|
|
|
269
292
|
family: declaredFamily(fields?.fields.metadata),
|
|
270
293
|
requirementHeadings:
|
|
271
294
|
requirement === undefined ? undefined : headings(requirement),
|
|
295
|
+
requirement,
|
|
272
296
|
sections: sectionsOf(body),
|
|
273
297
|
hasLedger: existsSync(join(skillDir, PRACTICE_LEDGER)),
|
|
274
298
|
bodies: [
|
|
@@ -349,6 +373,94 @@ function sectionFindings(source: SkillSource): SkillFinding[] {
|
|
|
349
373
|
]
|
|
350
374
|
}
|
|
351
375
|
|
|
376
|
+
/**
|
|
377
|
+
* Reports every cap the standard states, each as its own finding so a file
|
|
378
|
+
* breaching two reads as two edits. Silent on an absent requirement for the
|
|
379
|
+
* reason `sectionFindings` is.
|
|
380
|
+
*/
|
|
381
|
+
function limitFindings(source: SkillSource): SkillFinding[] {
|
|
382
|
+
const { requirement, requirementHeadings } = source
|
|
383
|
+
if (requirement === undefined || requirementHeadings === undefined) return []
|
|
384
|
+
|
|
385
|
+
const rel = join(source.rel, 'REQUIREMENT.md')
|
|
386
|
+
const lines = bodyLines(requirement)
|
|
387
|
+
const details = [
|
|
388
|
+
...bulletDetails(lines),
|
|
389
|
+
...wordDetails(lines),
|
|
390
|
+
...gapHistoryDetails(lines),
|
|
391
|
+
...(requirementHeadings.includes(RETIRED_REQUIREMENT_SECTION)
|
|
392
|
+
? [`## ${RETIRED_REQUIREMENT_SECTION}: retired, fold it into Must not`]
|
|
393
|
+
: []),
|
|
394
|
+
]
|
|
395
|
+
|
|
396
|
+
return details.map((detail) => ({ rel, detail }))
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Counts top-level bullets per H2, since the template holds one list per
|
|
401
|
+
* section. A nested bullet elaborates its parent rather than adding to it.
|
|
402
|
+
*/
|
|
403
|
+
function bulletDetails(lines: readonly BodyLine[]): string[] {
|
|
404
|
+
const counts = new Map<string, number>()
|
|
405
|
+
let section: string | undefined
|
|
406
|
+
|
|
407
|
+
for (const line of lines) {
|
|
408
|
+
if (line.fenced) continue
|
|
409
|
+
const heading = SECTION.exec(line.text)?.[1]
|
|
410
|
+
if (heading !== undefined) {
|
|
411
|
+
section = heading
|
|
412
|
+
continue
|
|
413
|
+
}
|
|
414
|
+
if (section === undefined || !TOP_LEVEL_BULLET.test(line.text)) continue
|
|
415
|
+
counts.set(section, (counts.get(section) ?? 0) + 1)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return [...counts]
|
|
419
|
+
.filter(([, count]) => count > REQUIREMENT_LIMITS.bullets)
|
|
420
|
+
.map(
|
|
421
|
+
([name, count]) =>
|
|
422
|
+
`## ${name}: ${count} bullets, cap ${REQUIREMENT_LIMITS.bullets}`,
|
|
423
|
+
)
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function wordDetails(lines: readonly BodyLine[]): string[] {
|
|
427
|
+
const words = lines.reduce(
|
|
428
|
+
(total, line) => total + (line.text.match(WORD)?.length ?? 0),
|
|
429
|
+
0,
|
|
430
|
+
)
|
|
431
|
+
if (words <= REQUIREMENT_LIMITS.words) return []
|
|
432
|
+
return [`${words} words, cap ${REQUIREMENT_LIMITS.words}`]
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* Gap states what fails today, so a date or a number naming the incident that
|
|
437
|
+
* earned a line is history git already holds. A code span is quoted data, such
|
|
438
|
+
* as a sample filename, and is masked before matching.
|
|
439
|
+
*/
|
|
440
|
+
function gapHistoryDetails(lines: readonly BodyLine[]): string[] {
|
|
441
|
+
const details: string[] = []
|
|
442
|
+
let inGap = false
|
|
443
|
+
|
|
444
|
+
for (const line of lines) {
|
|
445
|
+
if (line.fenced) continue
|
|
446
|
+
const heading = HEADING.exec(line.text)?.[1]
|
|
447
|
+
if (heading !== undefined) {
|
|
448
|
+
inGap = heading === GAP_SECTION
|
|
449
|
+
continue
|
|
450
|
+
}
|
|
451
|
+
if (!inGap) continue
|
|
452
|
+
const masked = maskDisplayed(line.text)
|
|
453
|
+
for (const match of [
|
|
454
|
+
...masked.matchAll(ISO_DATE),
|
|
455
|
+
...masked.matchAll(ISSUE_NUMBER),
|
|
456
|
+
]) {
|
|
457
|
+
details.push(`line ${line.number}: ${match[0]} in ${GAP_SECTION}`)
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
return details
|
|
462
|
+
}
|
|
463
|
+
|
|
352
464
|
/**
|
|
353
465
|
* A listed folder the shipped corpus does not hold, such as a renamed skill or
|
|
354
466
|
* a misspelled append, would otherwise read as a pass. Silent where the
|
package/src/commands/claude.ts
CHANGED
|
@@ -32,7 +32,9 @@ import {
|
|
|
32
32
|
PRACTICE_LEDGER,
|
|
33
33
|
PRACTICE_SECTIONS,
|
|
34
34
|
PRACTICE_SKILLS,
|
|
35
|
+
REQUIREMENT_LIMITS,
|
|
35
36
|
REQUIREMENT_SECTIONS,
|
|
37
|
+
RETIRED_REQUIREMENT_SECTION,
|
|
36
38
|
SHIPPED_CORPUS,
|
|
37
39
|
type SkillFinding,
|
|
38
40
|
type SkillsAudit,
|
|
@@ -441,7 +443,7 @@ export function register(program: Command): void {
|
|
|
441
443
|
'Refuses when neither claude/skills/ nor .claude/skills/ resolves.',
|
|
442
444
|
'',
|
|
443
445
|
'Only a missing REQUIREMENT.md sets a failing exit code. Name, description,',
|
|
444
|
-
'folder, and requirement
|
|
446
|
+
'folder, and requirement shape and limit findings are advisory.',
|
|
445
447
|
'',
|
|
446
448
|
'Examples:',
|
|
447
449
|
' canon claude skills audit',
|
|
@@ -1503,6 +1505,7 @@ async function runSkillsAudit(
|
|
|
1503
1505
|
readme: report.readme,
|
|
1504
1506
|
folderName: report.folderName,
|
|
1505
1507
|
requirementSections: report.requirementSections,
|
|
1508
|
+
requirementLimits: report.requirementLimits,
|
|
1506
1509
|
datedProvenance: report.datedProvenance,
|
|
1507
1510
|
practiceShape: report.practiceShape,
|
|
1508
1511
|
referenceContents: report.referenceContents,
|
|
@@ -1511,6 +1514,7 @@ async function runSkillsAudit(
|
|
|
1511
1514
|
checkpoints: {
|
|
1512
1515
|
descriptionLimit: DESCRIPTION_LIMIT,
|
|
1513
1516
|
requirementSections: REQUIREMENT_SECTIONS,
|
|
1517
|
+
requirementLimits: REQUIREMENT_LIMITS,
|
|
1514
1518
|
corpora: CORPORA,
|
|
1515
1519
|
practiceSkills: PRACTICE_SKILLS,
|
|
1516
1520
|
ledgerSkills: LEDGER_SKILLS,
|
|
@@ -1671,21 +1675,40 @@ function reportRequirementShape(report: SkillsAudit): void {
|
|
|
1671
1675
|
'A folder carrying no requirement is reported above rather than counted twice here.',
|
|
1672
1676
|
)
|
|
1673
1677
|
|
|
1674
|
-
|
|
1675
|
-
|
|
1678
|
+
logInfo(
|
|
1679
|
+
`Each list holds at most ${REQUIREMENT_LIMITS.bullets} top-level bullets, the body at most ${REQUIREMENT_LIMITS.words} words, and Gap no date or issue number outside a code span.`,
|
|
1680
|
+
)
|
|
1681
|
+
logInfo(
|
|
1682
|
+
`A ## ${RETIRED_REQUIREMENT_SECTION} section is retired into Must not. These limits report and never fail the run.`,
|
|
1683
|
+
)
|
|
1684
|
+
|
|
1685
|
+
if (
|
|
1686
|
+
report.requirementSections.length === 0 &&
|
|
1687
|
+
report.requirementLimits.length === 0
|
|
1688
|
+
) {
|
|
1689
|
+
logInfo('Every requirement declares both and holds to the limits.')
|
|
1676
1690
|
return
|
|
1677
1691
|
}
|
|
1678
1692
|
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1693
|
+
if (report.requirementSections.length > 0) {
|
|
1694
|
+
logWarn(
|
|
1695
|
+
`${plural(report.requirementSections.length, 'requirement')} short a declared section`,
|
|
1696
|
+
)
|
|
1697
|
+
reportFindings(report.requirementSections)
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
if (report.requirementLimits.length > 0) {
|
|
1701
|
+
logWarn(
|
|
1702
|
+
`${plural(report.requirementLimits.length, 'finding')} past a requirement limit`,
|
|
1703
|
+
)
|
|
1704
|
+
reportFindings(report.requirementLimits)
|
|
1705
|
+
}
|
|
1683
1706
|
}
|
|
1684
1707
|
|
|
1685
1708
|
/**
|
|
1686
|
-
* Names where the date belongs, since
|
|
1687
|
-
*
|
|
1688
|
-
*
|
|
1709
|
+
* Names where the date belongs, since a reader deleting it should know git
|
|
1710
|
+
* still holds it, and reports without failing the run for the same reason the
|
|
1711
|
+
* frontmatter measures do.
|
|
1689
1712
|
*/
|
|
1690
1713
|
function reportProvenance(report: SkillsAudit): void {
|
|
1691
1714
|
logStep('Provenance')
|
|
@@ -1693,7 +1716,7 @@ function reportProvenance(report: SkillsAudit): void {
|
|
|
1693
1716
|
'A SKILL.md and its references state the current rule, with no ISO date outside a fence or code span.',
|
|
1694
1717
|
)
|
|
1695
1718
|
logInfo(
|
|
1696
|
-
'The incident and date that earned a rule
|
|
1719
|
+
'The incident and date that earned a rule stay in git, where the log of the file finds them.',
|
|
1697
1720
|
)
|
|
1698
1721
|
|
|
1699
1722
|
if (report.datedProvenance.length === 0) {
|
package/src/commands/demo.ts
CHANGED
|
@@ -62,7 +62,7 @@ export function register(program: Command): void {
|
|
|
62
62
|
demo
|
|
63
63
|
.command('compile')
|
|
64
64
|
.description('Turn a screencast draft into a plan a run can drive')
|
|
65
|
-
.argument('<draft>', 'Screencast draft written by
|
|
65
|
+
.argument('<draft>', 'Screencast draft written by script-screencast')
|
|
66
66
|
.helpOption('-h, --help', 'Show this help message')
|
|
67
67
|
.option('-o, --out <dir>', 'Directory the plan is written to', DEFAULT_OUT)
|
|
68
68
|
.option('-s, --slug <slug>', 'Plan name, defaulting to the draft filename')
|
package/src/commands/markdown.ts
CHANGED
|
@@ -486,7 +486,7 @@ function reportDensity(reports: readonly FileReport[], gates: boolean): void {
|
|
|
486
486
|
`Paragraphs past ${CHECKPOINTS.paragraph} characters, more than 2 paragraphs in a row, bullets past ${CHECKPOINTS.bullet} characters, more than 5 plain bullets in a row, a heading past H3, and an H3 opening with neither a paragraph nor a table.`,
|
|
487
487
|
)
|
|
488
488
|
logInfo(
|
|
489
|
-
'Measured on context entries, docs, skill bodies and
|
|
489
|
+
'Measured on context entries, docs, skill bodies, references, and requirements, tooling seeds and stack references, the wiki, README.md, and CONTRIBUTING.md. Every other path takes no density finding.',
|
|
490
490
|
)
|
|
491
491
|
logInfo(
|
|
492
492
|
gates
|
package/src/commands/migrate.ts
CHANGED
|
@@ -1335,6 +1335,10 @@ export function register(program: Command): void {
|
|
|
1335
1335
|
'',
|
|
1336
1336
|
`Rewrites ${Object.keys(SKILL_NAME_MAP).length} skill names and moves the folders that carry them.`,
|
|
1337
1337
|
`It also carries ${Object.keys(SKILL_CITATION_MAP).length} namespaced-only rows for a skill renamed from a single word.`,
|
|
1338
|
+
'The seven draft- skills move onto verb-first names, such as',
|
|
1339
|
+
// The old spellings below are the rename itself. canon-keep-retired
|
|
1340
|
+
'draft-doc to write-doc and draft-and-pick to pick-by-eye, and a',
|
|
1341
|
+
'pointer left under each old name is never moved.',
|
|
1338
1342
|
'It takes no scope. The skill folders it moves are authored in the',
|
|
1339
1343
|
'toolkit and no target holds a copy of that catalog, so in a target it',
|
|
1340
1344
|
'rewrites citations of a renamed skill and moves nothing.',
|
package/src/commands/pr.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs'
|
|
1
|
+
import { existsSync, statSync } from 'node:fs'
|
|
2
2
|
import { readFile, realpath } from 'node:fs/promises'
|
|
3
3
|
import { basename, join, resolve } from 'node:path'
|
|
4
4
|
import { $ } from 'bun'
|
|
@@ -57,6 +57,7 @@ import {
|
|
|
57
57
|
pruneFramesWithLegacy,
|
|
58
58
|
pushFrame,
|
|
59
59
|
readFrame,
|
|
60
|
+
isDesignReference,
|
|
60
61
|
referenceSlug,
|
|
61
62
|
type TreeEntry,
|
|
62
63
|
} from '@/pr/frames'
|
|
@@ -222,9 +223,9 @@ const FRAMES_REFUSALS: Record<FramesCommandRefusal, string> = {
|
|
|
222
223
|
'no-number':
|
|
223
224
|
'--add and --drop write for one pull request, so name its number.',
|
|
224
225
|
'bad-box':
|
|
225
|
-
'--add takes --box <n>, a positive whole number naming the checklist box, or --reference <page>/<frame
|
|
226
|
+
'--add takes --box <n>, a positive whole number naming the checklist box, or --reference, as <page>/<frame> or a tracked canon/design/<surface>/<name> path, never both.',
|
|
226
227
|
'bad-reference':
|
|
227
|
-
'--reference takes <page>/<frame
|
|
228
|
+
'--reference takes <page>/<frame> or a tracked canon/design/<surface>/<name> path, each half opening on a letter or digit and holding only letters, digits, dots, underscores, and dashes, with no `--` in either, so the capture files under a name that reads back as the frame.',
|
|
228
229
|
'bad-head':
|
|
229
230
|
'--head takes a commit sha, 7 to 40 lowercase hex characters. A branch name or a path would file the frame under a folder that names no commit.',
|
|
230
231
|
'bad-pass':
|
|
@@ -1060,7 +1061,7 @@ export function register(program: Command): void {
|
|
|
1060
1061
|
.option('--box <n>', 'The checklist box the frame shows, with --add')
|
|
1061
1062
|
.option(
|
|
1062
1063
|
'--reference <address>',
|
|
1063
|
-
'The design reference the capture shows, as <page>/<frame
|
|
1064
|
+
'The design reference the capture shows, as <page>/<frame> or a tracked canon/design/<surface>/<name> path, with --add in place of --box',
|
|
1064
1065
|
)
|
|
1065
1066
|
.option(
|
|
1066
1067
|
'--head <sha>',
|
|
@@ -1087,7 +1088,9 @@ export function register(program: Command): void {
|
|
|
1087
1088
|
'so nothing is checked out and no other branch is touched. A frame lands',
|
|
1088
1089
|
'at pr-<number>/<short-head>/<pass>/box-<n>.png, or box-<n>-<theme>.png',
|
|
1089
1090
|
'with --theme, or ref-<page>--<frame>.png for a design reference capture',
|
|
1090
|
-
'passed as --reference <page>/<frame> in place of --box,
|
|
1091
|
+
'passed as --reference <page>/<frame> in place of --box, or',
|
|
1092
|
+
'ref-design--<surface>--<name>.png for a tracked canon/design/<surface>/<name>',
|
|
1093
|
+
'reference, whose --add must name that same file, and `link` names that',
|
|
1091
1094
|
`path on ${FRAMES_BRANCH}, so it survives a rewrite that drops other pull`,
|
|
1092
1095
|
'requests and resolves until its own pull request is dropped. The pass',
|
|
1093
1096
|
'segment keeps a second pass at one head and box from replacing an image',
|
|
@@ -1109,7 +1112,7 @@ export function register(program: Command): void {
|
|
|
1109
1112
|
' bad-mode not exactly one of --add, --drop, --prune',
|
|
1110
1113
|
' no-number --add or --drop named no pull request',
|
|
1111
1114
|
' bad-box --add with both --box and --reference, or neither',
|
|
1112
|
-
' bad-reference --reference is not <page>/<frame> in the safe name form',
|
|
1115
|
+
' bad-reference --reference is not <page>/<frame> or canon/design/<surface>/<name> in the safe name form, or the tracked path is no file',
|
|
1113
1116
|
' bad-head --head is not 7 to 40 lowercase hex characters',
|
|
1114
1117
|
' bad-pass --add without a compact UTC --pass stamp',
|
|
1115
1118
|
' bad-theme --theme is not dark or light, or no --add, or rides a --reference',
|
|
@@ -3192,6 +3195,27 @@ async function runFrames(
|
|
|
3192
3195
|
const invalid = framesArgumentRefusal(number, opts)
|
|
3193
3196
|
if (invalid !== undefined) return refuseFrames(invalid)
|
|
3194
3197
|
|
|
3198
|
+
if (opts.reference !== undefined && isDesignReference(opts.reference)) {
|
|
3199
|
+
const tracked = resolve(
|
|
3200
|
+
root,
|
|
3201
|
+
opts.reference.endsWith('.png')
|
|
3202
|
+
? opts.reference
|
|
3203
|
+
: `${opts.reference}.png`,
|
|
3204
|
+
)
|
|
3205
|
+
const refuseTracked = (message: string) =>
|
|
3206
|
+
refuseWith('bad-reference', message, emitJson, root)
|
|
3207
|
+
if (!statSync(tracked, { throwIfNoEntry: false })?.isFile()) {
|
|
3208
|
+
return refuseTracked(
|
|
3209
|
+
`${opts.reference} is not a file under the repository, so no tracked reference can be filed from it.`,
|
|
3210
|
+
)
|
|
3211
|
+
}
|
|
3212
|
+
if (opts.add === undefined || resolve(opts.add) !== tracked) {
|
|
3213
|
+
return refuseTracked(
|
|
3214
|
+
`--add must name ${tracked}, since a tracked reference files its own image and a different one would disagree with its label.`,
|
|
3215
|
+
)
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
|
|
3195
3219
|
const bytes =
|
|
3196
3220
|
opts.add === undefined ? undefined : await readFrame(resolve(opts.add))
|
|
3197
3221
|
if (opts.add !== undefined && bytes === undefined) {
|