@erclx/canon 5.54.0 → 5.55.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/CHANGELOG.md +8 -0
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/create-skill/SKILL.md +1 -1
- package/claude/skills/document-health/SKILL.md +6 -6
- package/claude/skills/document-repair/SKILL.md +5 -5
- package/claude/skills/target-check/SKILL.md +6 -8
- package/claude/skills/target-check/references/domains.md +1 -4
- package/claude/skills/target-setup/SKILL.md +8 -8
- package/claude/skills/target-update/SKILL.md +5 -5
- package/docs/agents/install-and-sync.md +2 -2
- package/docs/agents/output-shape.md +11 -10
- package/docs/target/sync.md +1 -1
- package/package.json +1 -1
- package/scripts/core/install-check.sh +1 -1
- package/scripts/lib/ui.sh +12 -65
- package/src/audits/run.ts +1 -1
- package/src/claude/routing.ts +1 -1
- package/src/cli/run.ts +5 -6
- package/src/cli/ui.ts +87 -28
- package/src/cli.ts +6 -0
- package/src/commands/claude.ts +2 -3
- package/src/commands/design.ts +8 -13
- package/src/commands/gate.ts +5 -3
- package/src/commands/gov.ts +11 -21
- package/src/commands/labels.ts +1 -1
- package/src/commands/pr.ts +1 -1
- package/src/commands/sync.ts +1 -1
- package/src/commands/tooling.ts +5 -8
- package/src/commands/transcripts.ts +6 -11
- package/src/gate/sequencer.ts +1 -1
- package/src/gate/stages.ts +0 -10
- package/src/hooks/post-merge.ts +2 -5
- package/src/process/harness.ts +3 -4
- package/src/sandbox/equivalence.ts +0 -1
- package/src/sandbox/provision.ts +8 -13
- package/src/sandbox/scenario.ts +6 -12
- package/src/sync/engine.ts +3 -8
- package/src/sync/workflow.ts +3 -5
- package/src/tooling/verify-stack.ts +1 -1
- package/tooling/base/configs/.husky/post-merge +6 -10
- package/scripts/core/check-color-source.sh +0 -51
package/src/sandbox/provision.ts
CHANGED
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
logStep,
|
|
28
28
|
logWarn,
|
|
29
29
|
outro,
|
|
30
|
-
|
|
30
|
+
outroSuccess,
|
|
31
31
|
select,
|
|
32
32
|
} from '@/cli/ui'
|
|
33
33
|
|
|
@@ -99,9 +99,7 @@ function readGit(cwd: string, args: string[]): string | undefined {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
function finish(message: string): void {
|
|
102
|
-
|
|
103
|
-
outro()
|
|
104
|
-
process.stderr.write(`\n${GREEN}${message}${NC}\n`)
|
|
102
|
+
outroSuccess(message)
|
|
105
103
|
}
|
|
106
104
|
|
|
107
105
|
function stageAnchorTree(
|
|
@@ -210,7 +208,7 @@ function install(
|
|
|
210
208
|
env: NodeJS.ProcessEnv,
|
|
211
209
|
): void {
|
|
212
210
|
const result = spawnSync('bun', [join(root, 'src', 'cli.ts'), ...args], {
|
|
213
|
-
env
|
|
211
|
+
env,
|
|
214
212
|
encoding: 'utf8',
|
|
215
213
|
stdio: ['ignore', 'ignore', 'pipe'],
|
|
216
214
|
})
|
|
@@ -432,10 +430,7 @@ async function provisionScenario(
|
|
|
432
430
|
baseEnv: NodeJS.ProcessEnv,
|
|
433
431
|
): Promise<void> {
|
|
434
432
|
const env: NodeJS.ProcessEnv = { ...baseEnv }
|
|
435
|
-
if (scenario.arm !== undefined)
|
|
436
|
-
env.SANDBOX_SCENARIO = scenario.arm
|
|
437
|
-
env.CANON_NON_INTERACTIVE = '1'
|
|
438
|
-
}
|
|
433
|
+
if (scenario.arm !== undefined) env.SANDBOX_SCENARIO = scenario.arm
|
|
439
434
|
|
|
440
435
|
const sandboxDir = join(root, 'sandbox')
|
|
441
436
|
const declared = declareScenario(root, scenario, env)
|
|
@@ -475,14 +470,14 @@ async function provisionScenario(
|
|
|
475
470
|
must(commitScenarioChanges(sandbox, isSkipAutoCommit, env))
|
|
476
471
|
tagBaseline(sandbox, env)
|
|
477
472
|
|
|
478
|
-
finish('
|
|
473
|
+
finish('Sandbox Ready')
|
|
479
474
|
}
|
|
480
475
|
|
|
481
476
|
function cleanSandbox(sandbox: string): void {
|
|
482
477
|
logStep('Removing sandbox')
|
|
483
478
|
rmSync(sandbox, { recursive: true, force: true })
|
|
484
479
|
logRemove(sandbox)
|
|
485
|
-
finish('
|
|
480
|
+
finish('Sandbox clean')
|
|
486
481
|
}
|
|
487
482
|
|
|
488
483
|
function isAtBaseline(sandbox: string): boolean {
|
|
@@ -520,7 +515,7 @@ async function resetSandbox(sandbox: string): Promise<void> {
|
|
|
520
515
|
|
|
521
516
|
if (isAtBaseline(sandbox)) {
|
|
522
517
|
logInfo('At baseline')
|
|
523
|
-
finish('
|
|
518
|
+
finish('Sandbox at baseline')
|
|
524
519
|
return
|
|
525
520
|
}
|
|
526
521
|
|
|
@@ -556,7 +551,7 @@ async function resetSandbox(sandbox: string): Promise<void> {
|
|
|
556
551
|
must(run('git', ['checkout-index', '-a', '-f'], quiet))
|
|
557
552
|
}
|
|
558
553
|
logInfo('Sandbox reset to baseline')
|
|
559
|
-
finish('
|
|
554
|
+
finish('Sandbox reset complete')
|
|
560
555
|
}
|
|
561
556
|
|
|
562
557
|
/**
|
package/src/sandbox/scenario.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { createRequire } from 'node:module'
|
|
14
14
|
import { dirname, join, relative } from 'node:path'
|
|
15
15
|
import {
|
|
16
|
+
isNonInteractive,
|
|
16
17
|
logError,
|
|
17
18
|
logInfo,
|
|
18
19
|
logStep,
|
|
@@ -580,8 +581,9 @@ export function createStageContext(options: ContextOptions): StageContext {
|
|
|
580
581
|
}
|
|
581
582
|
|
|
582
583
|
/**
|
|
583
|
-
* Picks the arm: a routed name first,
|
|
584
|
-
*
|
|
584
|
+
* Picks the arm: a routed name first, echoed on the frame, then a prompt. A
|
|
585
|
+
* headless caller naming no arm is refused rather than handed the first one,
|
|
586
|
+
* since a verdict on an arm nobody chose reads as a verdict on the scenario.
|
|
585
587
|
*/
|
|
586
588
|
async function routeArm(
|
|
587
589
|
names: readonly string[],
|
|
@@ -600,17 +602,9 @@ async function routeArm(
|
|
|
600
602
|
const routed = env.SANDBOX_SCENARIO ?? ''
|
|
601
603
|
if (routed !== '') return echo(routed)
|
|
602
604
|
|
|
603
|
-
|
|
604
|
-
// `is_non_interactive` read the shell it ran in.
|
|
605
|
-
const isHeadless =
|
|
606
|
-
env.CANON_NON_INTERACTIVE === '1' ||
|
|
607
|
-
// canon-keep-retired
|
|
608
|
-
env.AITK_NON_INTERACTIVE === '1'
|
|
609
|
-
if (isHeadless) return echo(names[0] ?? DEFAULT_ARM_NAME)
|
|
610
|
-
|
|
611
|
-
if (!process.stdin.isTTY) {
|
|
605
|
+
if (isNonInteractive()) {
|
|
612
606
|
logError(
|
|
613
|
-
`${prompt} requires a TTY. Pass
|
|
607
|
+
`${prompt} requires a TTY. Pass the arm as the scenario argument, one of: ${names.join(', ')}.`,
|
|
614
608
|
)
|
|
615
609
|
throw new ScenarioStop(1, 'exited')
|
|
616
610
|
}
|
package/src/sync/engine.ts
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
logStep,
|
|
27
27
|
logWarn,
|
|
28
28
|
outro,
|
|
29
|
-
|
|
29
|
+
outroSuccess,
|
|
30
30
|
select,
|
|
31
31
|
} from '@/cli/ui'
|
|
32
32
|
import { compareVersions, parseVersion } from '@/version/compare'
|
|
@@ -436,7 +436,6 @@ export async function runDomainSync(
|
|
|
436
436
|
}
|
|
437
437
|
|
|
438
438
|
const plan = planSync(adapter, resolved)
|
|
439
|
-
const { GREEN, GREY, NC } = palette(process.stderr)
|
|
440
439
|
|
|
441
440
|
if (
|
|
442
441
|
!existsSync(adapter.installedRoot(resolved)) &&
|
|
@@ -453,8 +452,7 @@ export async function runDomainSync(
|
|
|
453
452
|
if (count === 0) {
|
|
454
453
|
await adapter.onComplete?.(resolved)
|
|
455
454
|
await recordStamp(adapter, resolved, new Date())
|
|
456
|
-
|
|
457
|
-
process.stderr.write(`${GREEN}✓ Everything up to date${NC}\n`)
|
|
455
|
+
outroSuccess('Everything up to date')
|
|
458
456
|
return 0
|
|
459
457
|
}
|
|
460
458
|
|
|
@@ -489,10 +487,7 @@ export async function runDomainSync(
|
|
|
489
487
|
await adapter.onComplete?.(resolved)
|
|
490
488
|
await recordStamp(adapter, resolved, new Date())
|
|
491
489
|
|
|
492
|
-
|
|
493
|
-
process.stderr.write(
|
|
494
|
-
`${GREEN}✓ Sync complete${NC} ${GREY}(${count} ${adapter.unit})${NC}\n`,
|
|
495
|
-
)
|
|
490
|
+
outroSuccess('Sync complete', `(${count} ${adapter.unit})`)
|
|
496
491
|
return 0
|
|
497
492
|
}
|
|
498
493
|
|
package/src/sync/workflow.ts
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
logStep,
|
|
20
20
|
logWarn,
|
|
21
21
|
outro,
|
|
22
|
-
|
|
22
|
+
outroSuccess,
|
|
23
23
|
pipeOutput,
|
|
24
24
|
select,
|
|
25
25
|
} from '@/cli/ui'
|
|
@@ -75,7 +75,7 @@ export async function collectChanges(git: GitRunner): Promise<DomainChange[]> {
|
|
|
75
75
|
*
|
|
76
76
|
* This is the only toolkit code that writes to a git remote, which is why a
|
|
77
77
|
* headless run refuses it outright. `select_option` resolved to its first
|
|
78
|
-
* option
|
|
78
|
+
* option with no terminal to answer it, and the first option here was
|
|
79
79
|
* `Commit and open PR` whenever `gh` was installed, so an agent following the
|
|
80
80
|
* documented non-interactive path pushed a branch and opened a pull request on
|
|
81
81
|
* someone's repository without being asked.
|
|
@@ -193,8 +193,6 @@ function promptChoice(canOpenPullRequest: boolean): Promise<WorkflowChoice> {
|
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
function succeed(message: string): number {
|
|
196
|
-
|
|
197
|
-
outro()
|
|
198
|
-
process.stderr.write(`\n${GREEN}✓ ${message}${NC}\n`)
|
|
196
|
+
outroSuccess(message)
|
|
199
197
|
return 0
|
|
200
198
|
}
|
|
@@ -27,20 +27,16 @@ if ! command -v canon >/dev/null 2>&1; then
|
|
|
27
27
|
exit 0
|
|
28
28
|
fi
|
|
29
29
|
|
|
30
|
-
# A global binary older than the verb cannot run it, and
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
30
|
+
# A global binary older than the verb cannot run it, and its owner upgrades it
|
|
31
|
+
# by hand. The probe reads the verb's own usage line rather than the exit,
|
|
32
|
+
# since an older binary answers an unknown command's `--help` with its root
|
|
33
|
+
# help and exits zero.
|
|
34
34
|
has_verb() {
|
|
35
35
|
canon hooks post-merge --help 2>/dev/null | grep -q '^Usage: canon hooks post-merge'
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
if ! has_verb; then
|
|
39
|
-
if [ -z "$CANON_SKIP_UPGRADE" ]; then
|
|
40
|
-
CANON_NON_INTERACTIVE=1 canon upgrade >/dev/null 2>&1 || true
|
|
41
|
-
fi
|
|
42
|
-
fi
|
|
43
|
-
|
|
44
38
|
if has_verb; then
|
|
45
39
|
canon hooks post-merge --root "$root" || true
|
|
40
|
+
else
|
|
41
|
+
echo 'canon is too old to run the post-merge hook. Run `canon upgrade`.' >&2
|
|
46
42
|
fi
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
set -e
|
|
3
|
-
set -o pipefail
|
|
4
|
-
|
|
5
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
6
|
-
PROJECT_ROOT="${PROJECT_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}"
|
|
7
|
-
|
|
8
|
-
SCRIPTS_ROOT="$PROJECT_ROOT/scripts"
|
|
9
|
-
SANDBOX_ROOT="$PROJECT_ROOT/sandbox"
|
|
10
|
-
PALETTE_SOURCE="$SCRIPTS_ROOT/lib/ui.sh"
|
|
11
|
-
|
|
12
|
-
# `find` writes to stderr and returns non-zero for a missing root, inside a
|
|
13
|
-
# process substitution whose status nothing reads. Without this the walk covers
|
|
14
|
-
# nothing and the check reports the tree clean having never measured it.
|
|
15
|
-
if [ ! -f "$PALETTE_SOURCE" ]; then
|
|
16
|
-
echo "No palette source at ${PALETTE_SOURCE#"$PROJECT_ROOT/"}, color sourcing unverifiable." >&2
|
|
17
|
-
exit 1
|
|
18
|
-
fi
|
|
19
|
-
|
|
20
|
-
# The same silence follows a root that moved, so a walked root that is gone
|
|
21
|
-
# fails here rather than shrinking the corpus with no message.
|
|
22
|
-
for root in "$SCRIPTS_ROOT" "$SANDBOX_ROOT"; do
|
|
23
|
-
if [ ! -d "$root" ]; then
|
|
24
|
-
echo "No shell root at ${root#"$PROJECT_ROOT/"}, color sourcing unverifiable." >&2
|
|
25
|
-
exit 1
|
|
26
|
-
fi
|
|
27
|
-
done
|
|
28
|
-
|
|
29
|
-
# Any SGR sequence in either spelling. The cursor and erase sequences an
|
|
30
|
-
# interactive prompt writes end in a letter instead, and they run only where a
|
|
31
|
-
# terminal already exists, so they are not what this measures.
|
|
32
|
-
SGR='\\(033|e|x1b)\[[0-9;]*m'
|
|
33
|
-
|
|
34
|
-
# Sixteen copies of one decision is what made color impossible to turn off on
|
|
35
|
-
# the TypeScript side. A rule nothing enforces rebuilds them the next time a
|
|
36
|
-
# script wants grey, so the escape lives in the shared library and every other
|
|
37
|
-
# script reads the palette that library derives for its stream.
|
|
38
|
-
offenders=""
|
|
39
|
-
while IFS= read -r file; do
|
|
40
|
-
[ "$file" = "$PALETTE_SOURCE" ] && continue
|
|
41
|
-
if grep -qE "$SGR" "$file"; then
|
|
42
|
-
offenders="$offenders ${file#"$PROJECT_ROOT/"}"$'\n'
|
|
43
|
-
fi
|
|
44
|
-
done < <(find "$SCRIPTS_ROOT" "$SANDBOX_ROOT" -type f -name '*.sh')
|
|
45
|
-
|
|
46
|
-
if [ -n "$offenders" ]; then
|
|
47
|
-
echo "A color escape is defined outside the shared library:" >&2
|
|
48
|
-
printf '%s' "$offenders" >&2
|
|
49
|
-
echo "Source scripts/lib/ui.sh and read its palette, so one answer covers every writer." >&2
|
|
50
|
-
exit 1
|
|
51
|
-
fi
|