@erclx/canon 5.25.0 → 5.26.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 +2 -1
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/canvas/SKILL.md +1 -1
- package/claude/skills/context-fold/REQUIREMENT.md +6 -2
- package/claude/skills/context-fold/SKILL.md +8 -6
- package/claude/skills/context-fold/references/anchor-sweep.md +2 -2
- package/claude/skills/design-taste/REQUIREMENT.md +1 -1
- package/claude/skills/design-taste/SKILL.md +4 -2
- package/claude/skills/design-taste/references/adopted.md +40 -0
- package/claude/skills/design-taste/references/kinds.md +1 -1
- package/claude/skills/design-taste/references/systems.md +1 -7
- package/claude/skills/design-taste/references/tells.md +2 -24
- package/claude/skills/docs-sync/REQUIREMENT.md +2 -1
- package/claude/skills/document-health/REQUIREMENT.md +2 -0
- package/claude/skills/document-health/SKILL.md +21 -0
- package/claude/skills/document-health/references/axes.md +12 -0
- package/claude/skills/draft-and-pick/SKILL.md +1 -1
- package/claude/skills/draft-identity/SKILL.md +1 -1
- package/claude/skills/draft-screencast/SKILL.md +9 -7
- package/claude/skills/draft-slides/REQUIREMENT.md +17 -12
- package/claude/skills/draft-slides/SKILL.md +31 -20
- package/claude/skills/git-issue/references/issue.md +10 -0
- package/claude/skills/git-pr/references/evidence.md +8 -0
- package/claude/skills/git-pr/references/labels.md +12 -0
- package/claude/skills/git-split/SKILL.md +19 -8
- package/claude/skills/markdown-propose/references/format.md +9 -0
- package/claude/skills/memory-review/references/later-phases.md +7 -0
- package/claude/skills/review-ui/references/post.md +4 -2
- package/claude/skills/role-orchestrator/references/orchestrator-cloud-launch.md +8 -0
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +13 -0
- package/claude/skills/search-craft/REQUIREMENT.md +1 -1
- package/claude/skills/search-craft/SKILL.md +3 -1
- package/claude/skills/search-craft/references/adopted.md +30 -0
- package/claude/skills/target-setup/references/detect.md +8 -0
- package/claude/skills/target-setup/references/indexes.md +13 -0
- package/claude/skills/target-setup/references/verify.md +8 -0
- package/claude/skills/test-first/SKILL.md +2 -0
- package/claude/skills/test-first/references/adopted.md +36 -0
- package/claude/skills/write-human/SKILL.md +1 -1
- package/claude/skills/write-human/references/machine-tells.md +13 -0
- package/docs/agents/canvas.md +3 -1
- package/docs/agents/capture.md +1 -1
- package/docs/agents/commands.md +3 -3
- package/docs/agents/index.md +2 -2
- package/docs/agents/records-stale.md +22 -9
- package/docs/agents/records.md +1 -1
- package/docs/agents/sandbox.md +1 -1
- package/docs/agents/skills-audit.md +10 -3
- package/docs/agents/slides.md +15 -3
- package/docs/workflow/ai-workflow.md +1 -1
- package/docs/workflow/index.md +1 -0
- package/docs/workflow/parallel-features.md +1 -1
- package/docs/workflow/skill-map-on-request.md +42 -0
- package/docs/workflow/skill-map.md +1 -34
- package/package.json +1 -1
- package/scripts/lib/sandbox-dispatch.sh +1 -1
- package/scripts/lib/sandbox-git.sh +1 -9
- package/scripts/lib/sandbox-path.sh +1 -1
- package/scripts/sandbox-hook.sh +99 -0
- package/src/canvas/capture.ts +1 -9
- package/src/canvas/client/inspector/color-field.tsx +368 -0
- package/src/canvas/client/inspector/fields.css +163 -22
- package/src/canvas/client/inspector/values.ts +123 -0
- package/src/canvas/client/inspector.tsx +46 -69
- package/src/capture/render.ts +45 -7
- package/src/capture/sources.ts +43 -0
- package/src/census/count.ts +1 -1
- package/src/claude/cases/workflow.ts +5 -0
- package/src/claude/seeds.ts +1 -1
- package/src/claude/skills-audit.ts +68 -14
- package/src/client-commands.ts +1 -1
- package/src/commands/claude.ts +27 -2
- package/src/commands/feedback.ts +3 -3
- package/src/commands/records.ts +88 -6
- package/src/commands/sandbox.ts +50 -10
- package/src/commands/slides.ts +31 -77
- package/src/context/audit.ts +1 -1
- package/src/design/tokens.ts +2 -18
- package/src/docs/read.ts +1 -1
- package/src/{github.ts → feedback/github.ts} +1 -1
- package/src/gate/measures.ts +6 -4
- package/src/gov/citations.ts +1 -1
- package/src/gov/payload.ts +1 -1
- package/src/gov/superseded.ts +2 -2
- package/src/markdown/ceiling.ts +1 -1
- package/src/record-root.ts +1 -0
- package/src/records/backup.ts +1 -0
- package/src/records/push-guard.ts +1 -1
- package/src/records/stale-canonical.ts +146 -0
- package/src/records/stale.ts +1 -1
- package/src/sandbox/hooks.ts +128 -0
- package/src/sandbox/provision.ts +596 -0
- package/src/sandbox/tree.ts +103 -4
- package/src/secrets/marker.ts +2 -2
- package/src/secrets/scan.ts +1 -1
- package/src/shipped/references.ts +2 -2
- package/src/slides/convert/export.ts +2 -16
- package/src/slides/decks.ts +110 -0
- package/src/standards/read.ts +1 -1
- package/src/sync/seeds-report.ts +1 -1
- package/src/tasks/answers.ts +1 -1
- package/src/tasks/archive.ts +1 -1
- package/src/teach/nav.ts +1 -1
- package/src/tooling/inject.ts +1 -1
- package/src/web/readme-citations.ts +1 -1
- package/standards/architecture.md +1 -0
- package/standards/canvas.md +1 -1
- package/standards/requirements.md +2 -0
- package/standards/skill-practice.md +2 -1
- package/standards/skill.md +1 -0
- package/scripts/manage-sandbox.sh +0 -472
- package/src/slides/layouts.ts +0 -478
- package/src/slides/parse.ts +0 -84
- package/src/slides/render.ts +0 -110
- package/src/slides/styles.ts +0 -96
- /package/claude/skills/write-human/references/{source-material.md → adopted.md} +0 -0
- /package/src/{commands/feedback-format.ts → feedback/body.ts} +0 -0
- /package/src/{github-format.ts → feedback/github-format.ts} +0 -0
- /package/src/{frontmatter.ts → markdown/frontmatter.ts} +0 -0
- /package/src/{binary.ts → sweep/binary.ts} +0 -0
- /package/src/{exempt-marker.ts → sweep/exempt-marker.ts} +0 -0
- /package/src/{paths.ts → tasks/paths.ts} +0 -0
- /package/src/{seed-marker.ts → tooling/seed-marker.ts} +0 -0
package/src/commands/records.ts
CHANGED
|
@@ -30,6 +30,11 @@ import {
|
|
|
30
30
|
type StaleReport,
|
|
31
31
|
staleMemory,
|
|
32
32
|
} from '@/records/stale'
|
|
33
|
+
import {
|
|
34
|
+
type CanonicalDoc,
|
|
35
|
+
type CanonicalReport,
|
|
36
|
+
staleCanonical,
|
|
37
|
+
} from '@/records/stale-canonical'
|
|
33
38
|
import {
|
|
34
39
|
type Finding,
|
|
35
40
|
type FindingRemedy,
|
|
@@ -85,8 +90,11 @@ interface StaleCommandOptions extends ValidateCommandOptions {
|
|
|
85
90
|
readonly days?: string
|
|
86
91
|
}
|
|
87
92
|
|
|
88
|
-
/**
|
|
89
|
-
|
|
93
|
+
/**
|
|
94
|
+
* The kinds `stale` reads. A memory entry ages by days in the shared pen, and
|
|
95
|
+
* a canonical doc ages by the releases shipped since its review.
|
|
96
|
+
*/
|
|
97
|
+
const STALE_KINDS = ['memory', 'canonical'] as const
|
|
90
98
|
|
|
91
99
|
interface OrdinalCommandOptions extends ValidateCommandOptions {
|
|
92
100
|
readonly claim?: boolean
|
|
@@ -270,14 +278,17 @@ export function register(program: Command): void {
|
|
|
270
278
|
.option('--json', 'Add a machine-readable record on stdout')
|
|
271
279
|
.option(
|
|
272
280
|
'--days <n>',
|
|
273
|
-
`Days after a review before
|
|
281
|
+
`Days after a review before a memory entry is due again (default ${DEFAULT_REVIEW_DAYS})`,
|
|
282
|
+
)
|
|
283
|
+
.option(
|
|
284
|
+
'--root <path>',
|
|
285
|
+
'Project root, defaulting to the main worktree except on canonical',
|
|
274
286
|
)
|
|
275
|
-
.option('--root <path>', 'Project root, defaulting to the main worktree')
|
|
276
287
|
.addHelpText(
|
|
277
288
|
'after',
|
|
278
289
|
[
|
|
279
290
|
'',
|
|
280
|
-
'
|
|
291
|
+
'memory: reads the top-level entries of the memory folder, never review/, archive/,',
|
|
281
292
|
'or index.md. An entry is due when it carries no reviewed date, a reviewed',
|
|
282
293
|
'value that is not a date, or one older than --days. A path is a backticked',
|
|
283
294
|
'token holding a / and a file extension, resolved against the project root',
|
|
@@ -288,17 +299,24 @@ export function register(program: Command): void {
|
|
|
288
299
|
'Entries are ordered due first, then those citing a missing path, then the',
|
|
289
300
|
'longest unreviewed, then by name, so a review takes the first N as a batch.',
|
|
290
301
|
'',
|
|
302
|
+
'canonical: reads REQUIREMENTS.md and ARCHITECTURE.md at whichever surface',
|
|
303
|
+
'root carries each, in the current worktree since both are tracked. Each',
|
|
304
|
+
'reports its reviewed date and the release tags merged into HEAD after that',
|
|
305
|
+
'day, or never when it carries no date. --days does not apply.',
|
|
306
|
+
'',
|
|
291
307
|
'Exit codes:',
|
|
292
308
|
' 0 the reading completed, whatever it found',
|
|
293
309
|
' 1 refused, with the reason on stderr or in the JSON record',
|
|
294
310
|
'',
|
|
295
311
|
'It gates nothing and writes nothing. A due entry is a queue position rather',
|
|
296
|
-
'than a failure, and a missing path is a prompt for the
|
|
312
|
+
'than a failure, and a missing path or a release count is a prompt for the',
|
|
313
|
+
'review to judge.',
|
|
297
314
|
'',
|
|
298
315
|
'Examples:',
|
|
299
316
|
' canon records stale memory',
|
|
300
317
|
' canon records stale memory --json',
|
|
301
318
|
' canon records stale memory --days 60 --json',
|
|
319
|
+
' canon records stale canonical --json',
|
|
302
320
|
'',
|
|
303
321
|
].join('\n'),
|
|
304
322
|
)
|
|
@@ -560,6 +578,8 @@ async function runStale(
|
|
|
560
578
|
)
|
|
561
579
|
}
|
|
562
580
|
|
|
581
|
+
if (kind === 'canonical') return runStaleCanonical(opts, emitJson)
|
|
582
|
+
|
|
563
583
|
const days = Number(opts.days ?? DEFAULT_REVIEW_DAYS)
|
|
564
584
|
|
|
565
585
|
if (!Number.isInteger(days) || days <= 0) {
|
|
@@ -588,6 +608,68 @@ async function runStale(
|
|
|
588
608
|
return 0
|
|
589
609
|
}
|
|
590
610
|
|
|
611
|
+
/**
|
|
612
|
+
* Both docs are tracked, so the root is the checkout the caller stands in. The
|
|
613
|
+
* main worktree would answer from its own copy and miss a stamp this branch
|
|
614
|
+
* just wrote.
|
|
615
|
+
*/
|
|
616
|
+
async function runStaleCanonical(
|
|
617
|
+
opts: StaleCommandOptions,
|
|
618
|
+
emitJson: boolean,
|
|
619
|
+
): Promise<number> {
|
|
620
|
+
if (opts.days !== undefined) {
|
|
621
|
+
return reportRefusal(
|
|
622
|
+
'canon records stale',
|
|
623
|
+
{
|
|
624
|
+
reason: 'bad-days',
|
|
625
|
+
message:
|
|
626
|
+
'--days does not apply to canonical, which counts releases rather than days.',
|
|
627
|
+
},
|
|
628
|
+
emitJson,
|
|
629
|
+
)
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const root = opts.root ?? (await currentWorktreeRoot())
|
|
633
|
+
const outcome = await staleCanonical(root)
|
|
634
|
+
|
|
635
|
+
if (!outcome.ok)
|
|
636
|
+
return reportRefusal('canon records stale', outcome, emitJson)
|
|
637
|
+
|
|
638
|
+
if (emitJson) {
|
|
639
|
+
process.stdout.write(`${JSON.stringify(outcome)}\n`)
|
|
640
|
+
return 0
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
intro('canon records stale')
|
|
644
|
+
logStep('Canonical')
|
|
645
|
+
|
|
646
|
+
if (outcome.docs.length === 0) {
|
|
647
|
+
logInfo('neither REQUIREMENTS.md nor ARCHITECTURE.md exists')
|
|
648
|
+
} else {
|
|
649
|
+
for (const doc of outcome.docs) logInfo(describeCanonical(doc, outcome))
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
if (!outcome.tagged) logInfo('no release tags merged into HEAD')
|
|
653
|
+
outro()
|
|
654
|
+
return 0
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
function describeCanonical(
|
|
658
|
+
doc: CanonicalDoc,
|
|
659
|
+
outcome: CanonicalReport,
|
|
660
|
+
): string {
|
|
661
|
+
if (doc.reviewed === null) {
|
|
662
|
+
return doc.invalidReviewed === undefined
|
|
663
|
+
? `${doc.path}: never reviewed`
|
|
664
|
+
: `${doc.path}: reviewed value is not a date: ${doc.invalidReviewed}`
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
const since = outcome.tagged
|
|
668
|
+
? `${plural(doc.releasesSince ?? 0, 'release')} since`
|
|
669
|
+
: 'no releases to count'
|
|
670
|
+
return `${doc.path}: reviewed ${doc.reviewed}, ${since}`
|
|
671
|
+
}
|
|
672
|
+
|
|
591
673
|
function staleRow(entry: StaleEntry): string[] {
|
|
592
674
|
return [
|
|
593
675
|
entry.name,
|
package/src/commands/sandbox.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync } from 'node:fs'
|
|
2
2
|
import { join } from 'node:path'
|
|
3
3
|
import type { Command } from 'commander'
|
|
4
|
-
import { execScript } from '@/exec'
|
|
5
4
|
import { checkoutMismatchWarning, PROJECT_ROOT } from '@/project-root'
|
|
6
5
|
import {
|
|
7
6
|
assertedPercent,
|
|
@@ -26,6 +25,7 @@ import {
|
|
|
26
25
|
type RunEnvelope,
|
|
27
26
|
type Verdict,
|
|
28
27
|
} from '@/sandbox/expect'
|
|
28
|
+
import { runSandbox } from '@/sandbox/provision'
|
|
29
29
|
import { sandboxTree } from '@/sandbox/tree'
|
|
30
30
|
import {
|
|
31
31
|
frameError,
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
logStep,
|
|
37
37
|
logWarn,
|
|
38
38
|
outro,
|
|
39
|
+
palette,
|
|
39
40
|
select,
|
|
40
41
|
} from '@/ui'
|
|
41
42
|
|
|
@@ -69,11 +70,39 @@ function reportAbsentScenarioTree(): boolean {
|
|
|
69
70
|
return true
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
/** Holds fixture content for scenarios rather than scenarios of its own. */
|
|
74
|
+
const FIXTURES_DIR = 'fixtures'
|
|
75
|
+
|
|
72
76
|
/**
|
|
73
|
-
*
|
|
74
|
-
*
|
|
77
|
+
* The provision verb takes positional words rather than options, since `reset`
|
|
78
|
+
* and `clean` share its slot with a target, so its usage is written out here
|
|
79
|
+
* rather than generated by commander.
|
|
75
80
|
*/
|
|
76
|
-
|
|
81
|
+
function showProvisionHelp(): void {
|
|
82
|
+
const { GREY, NC, WHITE } = palette(process.stdout)
|
|
83
|
+
const lines = [
|
|
84
|
+
`${GREY}┌${NC}`,
|
|
85
|
+
`${GREY}├${NC} ${WHITE}Usage:${NC} canon sandbox [cat:cmd]`,
|
|
86
|
+
`${GREY}│${NC}`,
|
|
87
|
+
`${GREY}│${NC} ${WHITE}Arguments:${NC}`,
|
|
88
|
+
`${GREY}│${NC} cat:cmd ${GREY}# Scenario to provision (e.g. git:commit)${NC}`,
|
|
89
|
+
`${GREY}│${NC}`,
|
|
90
|
+
`${GREY}│${NC} ${WHITE}Commands:${NC}`,
|
|
91
|
+
`${GREY}│${NC} reset ${GREY}# Reset sandbox to baseline${NC}`,
|
|
92
|
+
`${GREY}│${NC} clean ${GREY}# Wipe the sandbox${NC}`,
|
|
93
|
+
`${GREY}│${NC}`,
|
|
94
|
+
`${GREY}│${NC} ${WHITE}Options:${NC}`,
|
|
95
|
+
`${GREY}│${NC} -h, --help ${GREY}# Show this help message${NC}`,
|
|
96
|
+
`${GREY}│${NC}`,
|
|
97
|
+
`${GREY}│${NC} ${WHITE}Examples:${NC}`,
|
|
98
|
+
`${GREY}│${NC} canon sandbox`,
|
|
99
|
+
`${GREY}│${NC} canon sandbox git:commit`,
|
|
100
|
+
`${GREY}│${NC} canon sandbox reset`,
|
|
101
|
+
`${GREY}│${NC} canon sandbox clean`,
|
|
102
|
+
`${GREY}└${NC}`,
|
|
103
|
+
]
|
|
104
|
+
process.stdout.write(`${lines.join('\n')}\n`)
|
|
105
|
+
}
|
|
77
106
|
|
|
78
107
|
/** A run that reported nothing counts as clean, so only real signals fail. */
|
|
79
108
|
const CLEAN_ENVELOPE: RunEnvelope = { isError: false, turns: 0, denials: 0 }
|
|
@@ -477,14 +506,25 @@ export function register(program: Command): void {
|
|
|
477
506
|
.action(async (_opts: unknown, cmd: Command) => {
|
|
478
507
|
if (reportAbsentScenarioTree()) return
|
|
479
508
|
|
|
480
|
-
const
|
|
509
|
+
const mismatch = checkoutMismatchWarning(process.cwd())
|
|
510
|
+
if (mismatch !== undefined) logWarn(mismatch)
|
|
511
|
+
|
|
512
|
+
// `--no-header` is the frame opt-out for a caller that already opened
|
|
513
|
+
// one, which `sandbox/run.sh` and the picker below both do.
|
|
514
|
+
const isHeader = cmd.args[0] !== '--no-header'
|
|
515
|
+
const args = isHeader ? cmd.args : cmd.args.slice(1)
|
|
481
516
|
|
|
482
|
-
if (args
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
} else {
|
|
486
|
-
await execScript('manage-sandbox.sh', args)
|
|
517
|
+
if (args[0] === '-h' || args[0] === '--help') {
|
|
518
|
+
showProvisionHelp()
|
|
519
|
+
return
|
|
487
520
|
}
|
|
521
|
+
|
|
522
|
+
const target = args[0] ?? (await interactivePicker())
|
|
523
|
+
process.exitCode = await runSandbox(PROJECT_ROOT, {
|
|
524
|
+
isHeader: isHeader && args[0] !== undefined,
|
|
525
|
+
target,
|
|
526
|
+
arm: args[1] === '' ? undefined : args[1],
|
|
527
|
+
})
|
|
488
528
|
})
|
|
489
529
|
|
|
490
530
|
sandbox
|
package/src/commands/slides.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import { existsSync, statSync } from 'node:fs'
|
|
2
1
|
import { resolve } from 'node:path'
|
|
3
2
|
import type { Command } from 'commander'
|
|
4
3
|
import { INSTALL_BROWSER } from '@/browser/engine'
|
|
5
4
|
import { creationRel, SCRATCH } from '@/record-root'
|
|
6
|
-
import {
|
|
5
|
+
import { listDecks, resolveDeck, SLIDES_FOLDER } from '@/slides/decks'
|
|
7
6
|
import { openDeck } from '@/slides/open'
|
|
8
|
-
import { renderSlidesDoc } from '@/slides/render'
|
|
9
|
-
import type { Variant } from '@/slides/styles'
|
|
10
7
|
import { intro, outro, palette } from '@/ui'
|
|
11
8
|
|
|
9
|
+
const SLIDES_AT = creationRel(process.cwd(), SLIDES_FOLDER)
|
|
10
|
+
|
|
12
11
|
export function register(program: Command): void {
|
|
13
12
|
const slides = program
|
|
14
13
|
.command('slides')
|
|
@@ -19,77 +18,34 @@ export function register(program: Command): void {
|
|
|
19
18
|
.command('render')
|
|
20
19
|
.helpOption('-h, --help', 'Show this help message')
|
|
21
20
|
.description(
|
|
22
|
-
|
|
21
|
+
`Render a deck under ${SLIDES_AT}/, or a folder of HTML slides, into a PowerPoint deck`,
|
|
23
22
|
)
|
|
24
|
-
.
|
|
25
|
-
'
|
|
26
|
-
'
|
|
27
|
-
'.claude/SLIDES.md',
|
|
23
|
+
.argument(
|
|
24
|
+
'[deck]',
|
|
25
|
+
'Deck name or folder, defaulting to the only deck when there is one',
|
|
28
26
|
)
|
|
29
27
|
.option(
|
|
30
28
|
'-o, --out <path>',
|
|
31
29
|
'Output directory',
|
|
32
30
|
creationRel(process.cwd(), SCRATCH, 'render', 'slides'),
|
|
33
31
|
)
|
|
34
|
-
.option('-v, --variant <variant>', 'Override variant (light or dark)')
|
|
35
32
|
.option(
|
|
36
33
|
'-m, --mirror <path>',
|
|
37
34
|
'Copy the deck to this directory after writing',
|
|
38
35
|
)
|
|
39
36
|
.option('--open', 'Open the deck after writing')
|
|
40
37
|
.action(
|
|
41
|
-
async (
|
|
42
|
-
|
|
43
|
-
out: string
|
|
44
|
-
|
|
45
|
-
mirror?: string
|
|
46
|
-
open?: boolean
|
|
47
|
-
}) => {
|
|
38
|
+
async (
|
|
39
|
+
deck: string | undefined,
|
|
40
|
+
opts: { out: string; mirror?: string; open?: boolean },
|
|
41
|
+
) => {
|
|
48
42
|
try {
|
|
49
|
-
const
|
|
50
|
-
const
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
}
|
|
43
|
+
const root = process.cwd()
|
|
44
|
+
const resolved = resolveDeck(root, deck)
|
|
45
|
+
if (resolved.status === 'refused') fail(resolved.message)
|
|
46
|
+
const outDir = resolve(root, opts.out)
|
|
54
47
|
const mirror = resolveMirror(opts.mirror)
|
|
55
|
-
|
|
56
|
-
if (opts.variant !== undefined) {
|
|
57
|
-
fail('--variant applies to a SLIDES.md source, not a folder')
|
|
58
|
-
}
|
|
59
|
-
await renderHtmlFolder(sourcePath, outDir, mirror, opts.open)
|
|
60
|
-
return
|
|
61
|
-
}
|
|
62
|
-
const variant = parseVariant(opts.variant)
|
|
63
|
-
const { GREEN, GREY, NC, RED } = palette(process.stderr)
|
|
64
|
-
intro('Render slides')
|
|
65
|
-
const result = await renderSlidesDoc(sourcePath, outDir, {
|
|
66
|
-
variant,
|
|
67
|
-
mirror,
|
|
68
|
-
})
|
|
69
|
-
process.stderr.write(
|
|
70
|
-
`${GREY}│${NC} ${GREEN}✓${NC} ${result.slideCount} slides\n${GREY}│${NC} ${GREEN}✓${NC} ${result.pptxPath}\n`,
|
|
71
|
-
)
|
|
72
|
-
const validNames = LAYOUTS.map((layout) => layout.name).join(', ')
|
|
73
|
-
for (const { value, slideNumbers } of result.unrecognizedLayouts) {
|
|
74
|
-
const noun = slideNumbers.length === 1 ? 'slide' : 'slides'
|
|
75
|
-
process.stderr.write(
|
|
76
|
-
`${GREY}│${NC} ${RED}✗${NC} unrecognized layout "${value}" on ${noun} ${slideNumbers.join(', ')}. Valid layouts: ${validNames}\n`,
|
|
77
|
-
)
|
|
78
|
-
}
|
|
79
|
-
if (result.mirrorPath) {
|
|
80
|
-
process.stderr.write(
|
|
81
|
-
`${GREY}│${NC} ${GREEN}✓${NC} mirrored to ${result.mirrorPath}\n`,
|
|
82
|
-
)
|
|
83
|
-
}
|
|
84
|
-
if (opts.open) {
|
|
85
|
-
const target = result.mirrorPath ?? result.pptxPath
|
|
86
|
-
const opened = await openDeck(target)
|
|
87
|
-
const mark = opened ? `${GREEN}✓${NC}` : `${RED}✗${NC}`
|
|
88
|
-
process.stderr.write(
|
|
89
|
-
`${GREY}│${NC} ${mark} ${opened ? 'opened' : 'could not open'} ${target}\n`,
|
|
90
|
-
)
|
|
91
|
-
}
|
|
92
|
-
outro()
|
|
48
|
+
await renderHtmlFolder(resolved.dir, outDir, mirror, opts.open)
|
|
93
49
|
} catch (error) {
|
|
94
50
|
reportFailure(error)
|
|
95
51
|
}
|
|
@@ -99,18 +55,23 @@ export function register(program: Command): void {
|
|
|
99
55
|
slides
|
|
100
56
|
.command('list')
|
|
101
57
|
.helpOption('-h, --help', 'Show this help message')
|
|
102
|
-
.description(
|
|
103
|
-
.option('--json', 'Output the
|
|
58
|
+
.description(`List the decks under ${SLIDES_AT}/`)
|
|
59
|
+
.option('--json', 'Output the decks as JSON')
|
|
104
60
|
.action((opts: { json?: boolean }) => {
|
|
61
|
+
const decks = listDecks(process.cwd())
|
|
105
62
|
if (opts.json) {
|
|
106
|
-
process.stdout.write(`${JSON.stringify(
|
|
63
|
+
process.stdout.write(`${JSON.stringify(decks)}\n`)
|
|
107
64
|
return
|
|
108
65
|
}
|
|
109
66
|
const { GREEN, GREY, NC } = palette(process.stderr)
|
|
110
|
-
intro('Slide
|
|
111
|
-
|
|
67
|
+
intro('Slide decks')
|
|
68
|
+
if (decks.length === 0) {
|
|
69
|
+
process.stderr.write(`${GREY}│${NC} No deck under ${SLIDES_AT}/\n`)
|
|
70
|
+
}
|
|
71
|
+
for (const { name, title, slides: count, path } of decks) {
|
|
72
|
+
const noun = count === 1 ? 'slide' : 'slides'
|
|
112
73
|
process.stderr.write(
|
|
113
|
-
`${GREY}│${NC} ${GREEN}✓${NC} ${
|
|
74
|
+
`${GREY}│${NC} ${GREEN}✓${NC} ${name} ${GREY}${title}, ${count} ${noun}, ${path}${NC}\n`,
|
|
114
75
|
)
|
|
115
76
|
}
|
|
116
77
|
outro()
|
|
@@ -121,8 +82,7 @@ export function register(program: Command): void {
|
|
|
121
82
|
* The converter loads on demand, so no other command resolves pptxgenjs,
|
|
122
83
|
* jszip, or the token module at startup. A fallback, a refused link, chart, or
|
|
123
84
|
* effect, or a token the master fell back on reports one line each and keeps
|
|
124
|
-
* the exit at 0,
|
|
125
|
-
* deck is written either way. A malformed `deck.json` refuses before any slide is laid
|
|
85
|
+
* the exit at 0, since the deck is written either way. A malformed `deck.json` refuses before any slide is laid
|
|
126
86
|
* out and exits 1.
|
|
127
87
|
*/
|
|
128
88
|
async function renderHtmlFolder(
|
|
@@ -192,12 +152,6 @@ async function renderHtmlFolder(
|
|
|
192
152
|
outro()
|
|
193
153
|
}
|
|
194
154
|
|
|
195
|
-
function parseVariant(value: string | undefined): Variant | undefined {
|
|
196
|
-
if (value === undefined) return undefined
|
|
197
|
-
if (value === 'light' || value === 'dark') return value
|
|
198
|
-
fail(`Invalid variant "${value}". Use light or dark.`)
|
|
199
|
-
}
|
|
200
|
-
|
|
201
155
|
function resolveMirror(value: string | undefined): string | undefined {
|
|
202
156
|
const mirror = value ?? process.env.CANON_SLIDES_MIRROR
|
|
203
157
|
return mirror ? resolve(process.cwd(), mirror) : undefined
|
|
@@ -205,9 +159,9 @@ function resolveMirror(value: string | undefined): string | undefined {
|
|
|
205
159
|
|
|
206
160
|
/**
|
|
207
161
|
* Carries a fail-fast message from a validation helper to the action that
|
|
208
|
-
* called it. `fail`
|
|
209
|
-
*
|
|
210
|
-
*
|
|
162
|
+
* called it. `fail` keeps its `never` return so the compiler narrows past it,
|
|
163
|
+
* and a helper deep in the call stack cannot set `process.exitCode` and unwind
|
|
164
|
+
* on its own.
|
|
211
165
|
*/
|
|
212
166
|
class SlidesError extends Error {}
|
|
213
167
|
|
package/src/context/audit.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { BARE_NAME, IGNORE_MARKER } from '@/context/citations'
|
|
|
4
4
|
import type { AuditedFolder } from '@/context/folders'
|
|
5
5
|
import { type BodyLine, bodyLines, maskDisplayed } from '@/markdown/scan'
|
|
6
6
|
import { documentHeight } from '@/markdown/structure'
|
|
7
|
-
import { isStubSeed } from '@/seed-marker'
|
|
7
|
+
import { isStubSeed } from '@/tooling/seed-marker'
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Checkpoint quoted from the standard stating it. It is not a cap.
|
package/src/design/tokens.ts
CHANGED
|
@@ -2,11 +2,8 @@
|
|
|
2
2
|
* The design system's one source of values.
|
|
3
3
|
*
|
|
4
4
|
* `canon/DESIGN.md` is rendered from this module rather than read by it, so
|
|
5
|
-
* the document a person opens is a view and this file is the fact.
|
|
6
|
-
*
|
|
7
|
-
* takes custom properties through `@/design/css`, and the slide renderer takes
|
|
8
|
-
* bare hex through `bareHex` below, because PowerPoint has no concept of a
|
|
9
|
-
* custom property.
|
|
5
|
+
* the document a person opens is a view and this file is the fact. A CSS
|
|
6
|
+
* surface reads it as custom properties through `@/design/css`.
|
|
10
7
|
*
|
|
11
8
|
* The two artifacts can disagree, which is what the `design` gate stage exists
|
|
12
9
|
* to catch. Nothing else compares them.
|
|
@@ -371,16 +368,3 @@ export function colorValue(
|
|
|
371
368
|
): string | undefined {
|
|
372
369
|
return tokens.color.find((token) => token.role === role)?.value
|
|
373
370
|
}
|
|
374
|
-
|
|
375
|
-
/**
|
|
376
|
-
* The adapter the slide renderer takes. `PptxGenJS` receives color as
|
|
377
|
-
* `{ color: theme.background }` and wants six hex digits with no leading `#`,
|
|
378
|
-
* so the shared thing is the value and this is the per-consumer form.
|
|
379
|
-
*
|
|
380
|
-
* The hex is raised to upper case because that is the spelling
|
|
381
|
-
* `src/slides/styles.ts` has always written, and the only one a diff of a
|
|
382
|
-
* rendered deck reads cleanly against.
|
|
383
|
-
*/
|
|
384
|
-
export function bareHex(value: string): string {
|
|
385
|
-
return value.replace(/^#/, '').toUpperCase()
|
|
386
|
-
}
|
package/src/docs/read.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync, readdirSync } from 'node:fs'
|
|
2
2
|
import { basename, join } from 'node:path'
|
|
3
|
-
import { stripFrontmatter } from '@/frontmatter'
|
|
3
|
+
import { stripFrontmatter } from '@/markdown/frontmatter'
|
|
4
4
|
import { SURFACE_ROOTS } from '@/surface-root'
|
|
5
5
|
|
|
6
6
|
const INDEX_TOPIC = 'index'
|
package/src/gate/measures.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
import { isOverWordCap, measureRequirements } from '@/context/requirements'
|
|
19
19
|
import {
|
|
20
20
|
auditSkills,
|
|
21
|
+
LEDGER_SKILLS,
|
|
21
22
|
PRACTICE_LEDGER,
|
|
22
23
|
PRACTICE_SKILLS,
|
|
23
24
|
SHIPPED_CORPUS,
|
|
@@ -411,8 +412,9 @@ export const skillProvenance: Measure = async (ctx) => {
|
|
|
411
412
|
}
|
|
412
413
|
|
|
413
414
|
/**
|
|
414
|
-
* Every listed practice skill against the closing sections and
|
|
415
|
-
*
|
|
415
|
+
* Every listed practice skill against the closing sections, and every skill on
|
|
416
|
+
* the wider ledger list against the ledger, both of which
|
|
417
|
+
* `standards/skill-practice.md` requires.
|
|
416
418
|
*
|
|
417
419
|
* Read in-process for the reason `skillProvenance` is, so the verb keeps its
|
|
418
420
|
* missing-requirement-only exit. The list names shipped folders alone, so a
|
|
@@ -434,14 +436,14 @@ export const skillPracticeShape: Measure = async (ctx) => {
|
|
|
434
436
|
.join(', ')
|
|
435
437
|
return {
|
|
436
438
|
emissions: [],
|
|
437
|
-
failure: `${plural(found.length, 'practice shape finding')}: ${where}. Add the closing H2 exactly as named, or the ledger at ${PRACTICE_LEDGER}.`,
|
|
439
|
+
failure: `${plural(found.length, 'practice shape finding')}: ${where}. Add the closing H2 exactly as named to a practice skill, or the ledger at ${PRACTICE_LEDGER} to any skill on the ledger list.`,
|
|
438
440
|
}
|
|
439
441
|
}
|
|
440
442
|
|
|
441
443
|
return {
|
|
442
444
|
emissions: [
|
|
443
445
|
info(
|
|
444
|
-
`${plural(PRACTICE_SKILLS.length, 'practice skill')} carry every closing section and a ledger`,
|
|
446
|
+
`${plural(PRACTICE_SKILLS.length, 'practice skill')} carry every closing section, and ${plural(LEDGER_SKILLS.length, 'listed skill')} carry a ledger`,
|
|
445
447
|
),
|
|
446
448
|
],
|
|
447
449
|
}
|
package/src/gov/citations.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync } from 'node:fs'
|
|
|
2
2
|
import { readFile } from 'node:fs/promises'
|
|
3
3
|
import { dirname, join, resolve } from 'node:path'
|
|
4
4
|
import { $ } from 'bun'
|
|
5
|
-
import { isMarked } from '@/exempt-marker'
|
|
5
|
+
import { isMarked } from '@/sweep/exempt-marker'
|
|
6
6
|
import { gitEnv } from '@/git-env'
|
|
7
7
|
import { listRuleFiles } from '@/gov/payload'
|
|
8
8
|
import { parseFrontmatter } from '@/indexes/frontmatter'
|
package/src/gov/payload.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs'
|
|
2
2
|
import { basename, resolve } from 'node:path'
|
|
3
|
-
import { stripFrontmatter } from '@/frontmatter'
|
|
3
|
+
import { stripFrontmatter } from '@/markdown/frontmatter'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Lists installed rules in the order the payload concatenates them. Sorting by
|
package/src/gov/superseded.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises'
|
|
2
2
|
import { join } from 'node:path'
|
|
3
|
-
import { isBinary } from '@/binary'
|
|
4
|
-
import { isMarked } from '@/exempt-marker'
|
|
3
|
+
import { isBinary } from '@/sweep/binary'
|
|
4
|
+
import { isMarked } from '@/sweep/exempt-marker'
|
|
5
5
|
import { listRepositoryFiles } from '@/git-files'
|
|
6
6
|
|
|
7
7
|
/**
|
package/src/markdown/ceiling.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { CHECKPOINTS, documentHeight } from '@/markdown/structure'
|
|
|
8
8
|
*
|
|
9
9
|
* The whole line rather than anywhere on it, so a standard quoting the marker
|
|
10
10
|
* inside a code span to document it does not exempt itself. A marker with no
|
|
11
|
-
* reason does not count, for the reason `isMarked` in `src/exempt-marker.ts`
|
|
11
|
+
* reason does not count, for the reason `isMarked` in `src/sweep/exempt-marker.ts`
|
|
12
12
|
* gives: a bare token reads as a line that meant to say something and did not.
|
|
13
13
|
*/
|
|
14
14
|
const EXEMPT_MARKER = /^<!--\s*canon-length-exempt:[ \t]*(\S.*?)\s*-->$/
|
package/src/record-root.ts
CHANGED
package/src/records/backup.ts
CHANGED