@erclx/canon 5.26.0 → 5.27.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 -2
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/api-design/SKILL.md +2 -0
- package/claude/skills/auto-ship/SKILL.md +3 -11
- package/claude/skills/auto-ship/references/review-findings.md +20 -0
- package/claude/skills/backlog-triage/SKILL.md +2 -0
- package/claude/skills/bash-cli-script/SKILL.md +2 -0
- package/claude/skills/build-in-slices/SKILL.md +2 -0
- package/claude/skills/canon-cli/SKILL.md +2 -0
- package/claude/skills/canon-feedback/SKILL.md +2 -0
- package/claude/skills/canon-feedback-triage/SKILL.md +2 -0
- package/claude/skills/canon-operator/SKILL.md +2 -0
- package/claude/skills/canon-rollout/SKILL.md +3 -11
- package/claude/skills/canon-rollout/references/failure-recovery.md +20 -0
- package/claude/skills/canvas/SKILL.md +13 -0
- package/claude/skills/ci-workflow/SKILL.md +2 -0
- package/claude/skills/code-craft/SKILL.md +2 -0
- package/claude/skills/codebase-layout/SKILL.md +2 -0
- package/claude/skills/context-fold/SKILL.md +3 -17
- package/claude/skills/context-fold/references/completion-report.md +26 -0
- package/claude/skills/create-rule/SKILL.md +2 -0
- package/claude/skills/create-skill/SKILL.md +2 -0
- package/claude/skills/create-standard/SKILL.md +2 -0
- package/claude/skills/decision-escalate/SKILL.md +2 -0
- package/claude/skills/deploy-app/SKILL.md +2 -0
- package/claude/skills/deprecation-migration/SKILL.md +2 -0
- package/claude/skills/design-extract/SKILL.md +2 -0
- package/claude/skills/design-taste/SKILL.md +2 -0
- package/claude/skills/docs-sync/SKILL.md +2 -0
- package/claude/skills/document-health/SKILL.md +2 -0
- package/claude/skills/draft-and-pick/SKILL.md +2 -0
- package/claude/skills/draft-doc/SKILL.md +2 -0
- package/claude/skills/draft-doc/references/wiki.md +1 -1
- package/claude/skills/draft-figure/SKILL.md +2 -0
- package/claude/skills/draft-identity/SKILL.md +2 -0
- package/claude/skills/draft-ready/SKILL.md +2 -0
- package/claude/skills/draft-screencast/SKILL.md +2 -0
- package/claude/skills/draft-slides/SKILL.md +2 -0
- package/claude/skills/git-branch/SKILL.md +2 -0
- package/claude/skills/git-commit/SKILL.md +2 -0
- package/claude/skills/git-followup/SKILL.md +2 -0
- package/claude/skills/git-issue/SKILL.md +2 -0
- package/claude/skills/git-pr/SKILL.md +2 -0
- package/claude/skills/git-pr/references/evidence.md +6 -6
- package/claude/skills/git-ship/SKILL.md +2 -0
- package/claude/skills/git-split/SKILL.md +2 -0
- package/claude/skills/git-stage/SKILL.md +2 -0
- package/claude/skills/git-worktree/SKILL.md +2 -0
- package/claude/skills/index-lookup/SKILL.md +2 -0
- package/claude/skills/markdown-propose/SKILL.md +2 -0
- package/claude/skills/memory-capture/SKILL.md +2 -0
- package/claude/skills/memory-review/SKILL.md +2 -0
- package/claude/skills/plan-feature/SKILL.md +2 -0
- package/claude/skills/plan-groundwork/SKILL.md +2 -0
- package/claude/skills/plan-intake/SKILL.md +2 -0
- package/claude/skills/plan-intake-answer/SKILL.md +2 -0
- package/claude/skills/project-commands/SKILL.md +2 -0
- package/claude/skills/read-frames/SKILL.md +2 -0
- package/claude/skills/record-screencast/SKILL.md +2 -0
- package/claude/skills/repo-metadata/SKILL.md +2 -0
- package/claude/skills/restate-plainly/SKILL.md +2 -0
- package/claude/skills/review-address/SKILL.md +3 -7
- package/claude/skills/review-address/references/post-review-findings.md +16 -0
- package/claude/skills/review-branch/SKILL.md +2 -0
- package/claude/skills/review-craft/SKILL.md +2 -0
- package/claude/skills/review-craft/references/ui.md +2 -2
- package/claude/skills/review-pr/SKILL.md +4 -8
- package/claude/skills/review-pr/references/body-variants.md +52 -0
- package/claude/skills/review-pr/references/key-changes.md +16 -0
- package/claude/skills/review-pr/references/post.md +15 -43
- package/claude/skills/review-ui/SKILL.md +3 -1
- package/claude/skills/role-orchestrator/SKILL.md +5 -14
- package/claude/skills/role-orchestrator/references/orchestrator-handback.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-inbound.md +23 -0
- package/claude/skills/role-orchestrator/references/orchestrator-launch.md +5 -33
- package/claude/skills/role-orchestrator/references/orchestrator-poll.md +1 -21
- package/claude/skills/role-orchestrator/references/orchestrator-review-dispatch.md +1 -1
- package/claude/skills/role-orchestrator/references/orchestrator-reviewer-launch.md +38 -0
- package/claude/skills/role-orchestrator/references/orchestrator-watch.md +26 -0
- package/claude/skills/role-planner/SKILL.md +2 -0
- package/claude/skills/role-reviewer/REQUIREMENT.md +1 -1
- package/claude/skills/role-reviewer/SKILL.md +2 -0
- package/claude/skills/role-worker/SKILL.md +2 -0
- package/claude/skills/search-craft/SKILL.md +2 -0
- package/claude/skills/seed-sync/SKILL.md +2 -0
- package/claude/skills/session-compact/SKILL.md +2 -0
- package/claude/skills/session-map/SKILL.md +2 -0
- package/claude/skills/session-relay/SKILL.md +2 -0
- package/claude/skills/session-resume/SKILL.md +2 -0
- package/claude/skills/session-worktree/SKILL.md +3 -11
- package/claude/skills/session-worktree/references/install-command.md +20 -0
- package/claude/skills/sketch-design/SKILL.md +2 -0
- package/claude/skills/systematic-debugging/SKILL.md +2 -0
- package/claude/skills/target-check/SKILL.md +2 -0
- package/claude/skills/target-setup/SKILL.md +2 -0
- package/claude/skills/task-board/SKILL.md +2 -0
- package/claude/skills/teach-workspace/SKILL.md +5 -15
- package/claude/skills/teach-workspace/references/lesson-build.md +75 -0
- package/claude/skills/teach-workspace/references/lesson-craft.md +13 -69
- package/claude/skills/teach-workspace/references/revisit.md +22 -0
- package/claude/skills/test-craft/SKILL.md +2 -0
- package/claude/skills/test-first/SKILL.md +2 -0
- package/claude/skills/ui-checklist/SKILL.md +2 -0
- package/claude/skills/ux-audit/SKILL.md +2 -0
- package/claude/skills/ux-measure/SKILL.md +2 -0
- package/claude/skills/ux-walkthrough/SKILL.md +2 -0
- package/claude/skills/video-craft/SKILL.md +2 -0
- package/claude/skills/write-human/SKILL.md +2 -0
- package/claude/skills/youtube-transcripts/SKILL.md +2 -0
- package/docs/agents/canvas.md +23 -7
- package/docs/agents/capture.md +5 -5
- package/docs/agents/commands.md +2 -2
- package/docs/agents/index.md +3 -3
- package/docs/agents/install-and-sync.md +7 -0
- package/docs/agents/key-changes.md +1 -1
- package/docs/agents/pr-evidence.md +38 -15
- package/docs/agents/pr-local.md +19 -13
- package/docs/agents/pr-preview.md +44 -21
- package/docs/agents/sandbox.md +28 -4
- package/docs/agents/scripting.md +8 -1
- package/docs/agents/skills-audit.md +2 -1
- package/docs/workflow/operating-model.md +5 -6
- package/docs/workflow/parallel-features.md +22 -1
- package/docs/workflow/skill-map.md +3 -1
- package/docs/workflow/zshrc-aliases.md +1 -1
- package/package.json +1 -1
- package/scripts/core/regen-hero.ts +1 -1
- package/scripts/lib/sandbox-path.sh +6 -162
- package/src/audits/run.ts +1 -1
- package/src/canvas/address.ts +12 -3
- package/src/canvas/client/inspector/align-grid.tsx +162 -0
- package/src/canvas/client/inspector/color-field.tsx +201 -171
- package/src/canvas/client/inspector/color-inputs.tsx +139 -0
- package/src/canvas/client/inspector/color-picker.tsx +668 -0
- package/src/canvas/client/inspector/color-space.ts +240 -0
- package/src/canvas/client/inspector/field.tsx +5 -1
- package/src/canvas/client/inspector/fields.css +395 -6
- package/src/canvas/client/inspector/size-field.tsx +69 -0
- package/src/canvas/client/inspector/values.ts +43 -1
- package/src/canvas/client/inspector.tsx +190 -4
- package/src/canvas/client/selection.tsx +197 -0
- package/src/canvas/client/shell.css +104 -12
- package/src/canvas/client/state.ts +69 -9
- package/src/canvas/client/surface.tsx +147 -22
- package/src/canvas/content.ts +50 -1
- package/src/canvas/edit.ts +37 -4
- package/src/canvas/editing.ts +203 -0
- package/src/canvas/global.d.ts +15 -0
- package/src/canvas/server.ts +149 -12
- package/src/canvas/tokens.ts +59 -17
- package/src/census/count.ts +1 -1
- package/src/claude/seeds.ts +1 -1
- package/src/claude/skills-audit.ts +28 -0
- package/src/claude/skills-drift.ts +1 -1
- package/src/claude/skills-families.ts +36 -0
- package/src/claude/skills-list.ts +76 -15
- package/src/commands/audits.ts +2 -2
- package/src/commands/canvas.ts +179 -2
- package/src/commands/claude.ts +67 -9
- package/src/commands/design.ts +142 -2
- package/src/commands/gov.ts +1 -1
- package/src/commands/hooks.ts +1 -1
- package/src/commands/indexes.ts +1 -1
- package/src/commands/init.ts +1 -1
- package/src/commands/intake.ts +1 -1
- package/src/commands/migrate.ts +1 -1
- package/src/commands/pr.ts +105 -58
- package/src/commands/records.ts +1 -1
- package/src/commands/repo.ts +1 -1
- package/src/commands/sandbox.ts +54 -2
- package/src/commands/sessions.ts +1 -1
- package/src/commands/sync.ts +1 -1
- package/src/commands/tasks.ts +2 -2
- package/src/commands/teach.ts +1 -1
- package/src/commands/transcripts.ts +1 -1
- package/src/commands/worktrees.ts +1 -1
- package/src/comments/scan.ts +1 -1
- package/src/comments/trend.ts +1 -1
- package/src/context/citations.ts +1 -1
- package/src/context/classify/extract.ts +2 -2
- package/src/design/project-fonts.ts +392 -0
- package/src/feedback/github.ts +1 -1
- package/src/gate/measures.ts +47 -9
- package/src/gate/sequencer.ts +1 -1
- package/src/gate/stages.ts +9 -0
- package/src/{git-files.ts → git/files.ts} +2 -2
- package/src/{git-ignore.ts → git/ignore.ts} +1 -1
- package/src/{worktree.ts → git/worktree.ts} +1 -1
- package/src/gov/citations.ts +1 -1
- package/src/gov/install.ts +1 -1
- package/src/gov/superseded.ts +1 -1
- package/src/gov/test-order.ts +2 -2
- package/src/help.ts +1 -1
- package/src/hooks/post-merge.ts +1 -1
- package/src/indexes/walk.ts +1 -1
- package/src/labels/audit.ts +1 -1
- package/src/markdown/files.ts +1 -1
- package/src/migrate/apply.ts +1 -1
- package/src/pr/bijection.ts +1 -1
- package/src/pr/evidence.ts +96 -43
- package/src/pr/local.ts +16 -10
- package/src/pr/preview.ts +78 -2
- package/src/records/backup.ts +1 -1
- package/src/records/stale-canonical.ts +1 -1
- package/src/sandbox/headless/dispatch.ts +264 -0
- package/src/sandbox/headless/run.ts +653 -0
- package/src/sandbox/headless/watch.ts +145 -0
- package/src/sandbox/provision.ts +12 -1
- package/src/sandbox/tree.ts +4 -8
- package/src/secrets/scan.ts +1 -1
- package/src/sessions/claim.ts +1 -1
- package/src/sessions/resolve.ts +1 -1
- package/src/sessions/transfer/bundle.ts +1 -1
- package/src/slides/package/fonts.ts +6 -2
- package/src/standards/audit.ts +2 -2
- package/src/sync/check.ts +2 -2
- package/src/sync/engine.ts +2 -2
- package/src/sync/git.ts +1 -1
- package/src/sync/history.ts +1 -1
- package/src/sync/reverse.ts +1 -1
- package/src/sync/target.ts +1 -1
- package/src/sync/workflow.ts +1 -1
- package/src/targets/pulls.ts +2 -2
- package/src/targets/sweep.ts +2 -2
- package/src/tasks/reach.ts +1 -1
- package/src/tasks/trunk.ts +1 -1
- package/src/tooling/inject.ts +1 -1
- package/src/tooling/subfolder.ts +1 -1
- package/src/web/readme-citations.ts +1 -1
- package/src/worktrees/bare-flag.ts +1 -1
- package/src/worktrees/reclaim.ts +2 -2
- package/src/worktrees/remove.ts +1 -1
- package/standards/canvas.md +2 -1
- package/standards/skill.md +1 -0
- package/standards/wiki.md +12 -3
- package/tooling/base/configs/.github/workflows/pr-local-preview.yml +2 -2
- package/scripts/lib/sandbox-dispatch.sh +0 -190
- /package/src/{client-commands.ts → gate/client-commands.ts} +0 -0
- /package/src/{git-base.ts → git/base.ts} +0 -0
- /package/src/{git-env.ts → git/env.ts} +0 -0
- /package/src/{copy.ts → sync/copy.ts} +0 -0
- /package/src/{target.ts → targets/validate.ts} +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs'
|
|
2
2
|
import { dirname, join } from 'node:path'
|
|
3
3
|
import { CORPORA } from '@/claude/skills-audit'
|
|
4
|
+
import { isFamilyKey, SKILL_FAMILIES } from '@/claude/skills-families'
|
|
4
5
|
|
|
5
6
|
const FRONTMATTER = /^---\n([\s\S]*?)\n---/
|
|
6
7
|
|
|
@@ -8,6 +9,11 @@ export interface SkillListing {
|
|
|
8
9
|
readonly name: string
|
|
9
10
|
readonly description: string
|
|
10
11
|
readonly requirement: boolean
|
|
12
|
+
/**
|
|
13
|
+
* The `metadata.family` the frontmatter declares, read as written. Whether it
|
|
14
|
+
* names a vocabulary key is the audit's finding, not the listing's.
|
|
15
|
+
*/
|
|
16
|
+
readonly family: string | null
|
|
11
17
|
}
|
|
12
18
|
|
|
13
19
|
export interface SkillsCorpus {
|
|
@@ -72,28 +78,83 @@ export function listSkillsAt(skillsRoot: string): SkillListing[] {
|
|
|
72
78
|
}),
|
|
73
79
|
].sort()
|
|
74
80
|
|
|
75
|
-
return paths.map((path) =>
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
return paths.map((path) => {
|
|
82
|
+
const { description, family } = readListedFields(join(skillsRoot, path))
|
|
83
|
+
return {
|
|
84
|
+
name: dirname(path),
|
|
85
|
+
description,
|
|
86
|
+
requirement: existsSync(
|
|
87
|
+
join(skillsRoot, dirname(path), 'REQUIREMENT.md'),
|
|
88
|
+
),
|
|
89
|
+
family,
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface FamilyGroup {
|
|
95
|
+
readonly key: string
|
|
96
|
+
readonly group: string
|
|
97
|
+
readonly skills: readonly string[]
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface FamilyGrouping {
|
|
101
|
+
/** Vocabulary order, holding only the families some listing declares. */
|
|
102
|
+
readonly groups: readonly FamilyGroup[]
|
|
103
|
+
/** Listings whose family is missing or names no vocabulary key. */
|
|
104
|
+
readonly unassigned: readonly string[]
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function groupByFamily(
|
|
108
|
+
listings: readonly SkillListing[],
|
|
109
|
+
): FamilyGrouping {
|
|
110
|
+
const groups = SKILL_FAMILIES.map((family) => ({
|
|
111
|
+
...family,
|
|
112
|
+
skills: listings
|
|
113
|
+
.filter((listing) => listing.family === family.key)
|
|
114
|
+
.map((listing) => listing.name),
|
|
115
|
+
})).filter((group) => group.skills.length > 0)
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
groups,
|
|
119
|
+
unassigned: listings
|
|
120
|
+
.filter(
|
|
121
|
+
(listing) => listing.family === null || !isFamilyKey(listing.family),
|
|
122
|
+
)
|
|
123
|
+
.map((listing) => listing.name),
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
interface ListedFields {
|
|
128
|
+
readonly description: string
|
|
129
|
+
readonly family: string | null
|
|
80
130
|
}
|
|
81
131
|
|
|
82
132
|
/**
|
|
83
|
-
* Returns an empty description rather than throwing on a
|
|
84
|
-
* frontmatter is missing or unparseable, so one malformed file
|
|
85
|
-
* the rest of the catalog from a caller counting it.
|
|
133
|
+
* Returns an empty description and a null family rather than throwing on a
|
|
134
|
+
* skill whose frontmatter is missing or unparseable, so one malformed file
|
|
135
|
+
* does not hide the rest of the catalog from a caller counting it.
|
|
86
136
|
*/
|
|
87
|
-
function
|
|
137
|
+
function readListedFields(path: string): ListedFields {
|
|
138
|
+
const empty = { description: '', family: null }
|
|
88
139
|
const match = FRONTMATTER.exec(readFileSync(path, 'utf8'))
|
|
89
|
-
if (!match) return
|
|
140
|
+
if (!match) return empty
|
|
90
141
|
|
|
142
|
+
let parsed: unknown
|
|
91
143
|
try {
|
|
92
|
-
|
|
93
|
-
if (typeof parsed !== 'object' || parsed === null) return ''
|
|
94
|
-
const { description } = parsed as Record<string, unknown>
|
|
95
|
-
return typeof description === 'string' ? description : ''
|
|
144
|
+
parsed = Bun.YAML.parse(match[1])
|
|
96
145
|
} catch {
|
|
97
|
-
return
|
|
146
|
+
return empty
|
|
98
147
|
}
|
|
148
|
+
if (!isRecord(parsed)) return empty
|
|
149
|
+
|
|
150
|
+
const { description, metadata } = parsed
|
|
151
|
+
const family = isRecord(metadata) ? metadata.family : undefined
|
|
152
|
+
return {
|
|
153
|
+
description: typeof description === 'string' ? description : '',
|
|
154
|
+
family: typeof family === 'string' ? family : null,
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
159
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
99
160
|
}
|
package/src/commands/audits.ts
CHANGED
|
@@ -19,9 +19,9 @@ import {
|
|
|
19
19
|
type Summary,
|
|
20
20
|
summarize,
|
|
21
21
|
} from '@/audits/run'
|
|
22
|
-
import { gitEnv } from '@/git
|
|
22
|
+
import { gitEnv } from '@/git/env'
|
|
23
23
|
import { intro, logError, logInfo, logStep, logWarn, outro, plural } from '@/ui'
|
|
24
|
-
import { currentWorktreeRoot } from '@/worktree'
|
|
24
|
+
import { currentWorktreeRoot } from '@/git/worktree'
|
|
25
25
|
|
|
26
26
|
interface RunCommandOptions {
|
|
27
27
|
readonly json?: boolean
|
package/src/commands/canvas.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
readPage,
|
|
13
13
|
readSelection,
|
|
14
14
|
renamePage,
|
|
15
|
+
resizeFrame,
|
|
15
16
|
} from '@/canvas/content'
|
|
16
17
|
import { captureCanvas, captureComposite } from '@/canvas/capture'
|
|
17
18
|
import {
|
|
@@ -19,10 +20,22 @@ import {
|
|
|
19
20
|
editFrameAtIndex,
|
|
20
21
|
STYLE_PROPERTIES,
|
|
21
22
|
} from '@/canvas/edit'
|
|
23
|
+
import {
|
|
24
|
+
clearEditing,
|
|
25
|
+
EDITING_TTL_MS,
|
|
26
|
+
markEditing,
|
|
27
|
+
readEditing,
|
|
28
|
+
} from '@/canvas/editing'
|
|
22
29
|
import { CANVAS_PORT, startCanvas } from '@/canvas/server'
|
|
23
30
|
import { PROJECT_ROOT } from '@/project-root'
|
|
24
31
|
import { resolveFrameTokens } from '@/canvas/tokens'
|
|
25
32
|
import { displayPath, parsePort, waitForInterrupt } from '@/serve/report'
|
|
33
|
+
import {
|
|
34
|
+
callerIdentity,
|
|
35
|
+
type Located,
|
|
36
|
+
resolveSessions,
|
|
37
|
+
selfOf,
|
|
38
|
+
} from '@/sessions/resolve'
|
|
26
39
|
import {
|
|
27
40
|
intro,
|
|
28
41
|
logAdd,
|
|
@@ -33,7 +46,7 @@ import {
|
|
|
33
46
|
outro,
|
|
34
47
|
plural,
|
|
35
48
|
} from '@/ui'
|
|
36
|
-
import { mainWorktreeRoot } from '@/worktree'
|
|
49
|
+
import { mainWorktreeRoot } from '@/git/worktree'
|
|
37
50
|
|
|
38
51
|
const BANNER = 'canon canvas'
|
|
39
52
|
|
|
@@ -135,7 +148,7 @@ export function register(program: Command): void {
|
|
|
135
148
|
.command('canvas')
|
|
136
149
|
.helpOption('-h, --help', 'Show this help message')
|
|
137
150
|
.description(
|
|
138
|
-
'Local canvas of pages and HTML frames (serve, list, page, frame, selection, edit, capture)',
|
|
151
|
+
'Local canvas of pages and HTML frames (serve, list, page, frame, selection, edit, editing, capture)',
|
|
139
152
|
)
|
|
140
153
|
.addHelpText(
|
|
141
154
|
'after',
|
|
@@ -152,6 +165,7 @@ export function register(program: Command): void {
|
|
|
152
165
|
' canon canvas frame move drafts hero --x 200 --y 120',
|
|
153
166
|
' canon canvas selection --json',
|
|
154
167
|
" canon canvas edit drafts/hero --element 4 --set 'color=var(--color-accent)'",
|
|
168
|
+
' canon canvas editing drafts/hero',
|
|
155
169
|
' canon canvas capture drafts/hero',
|
|
156
170
|
' canon canvas list --json',
|
|
157
171
|
'',
|
|
@@ -341,6 +355,56 @@ export function register(program: Command): void {
|
|
|
341
355
|
},
|
|
342
356
|
)
|
|
343
357
|
|
|
358
|
+
withRoot(
|
|
359
|
+
frame
|
|
360
|
+
.command('resize')
|
|
361
|
+
.description('Resize a frame, and move it when given a position')
|
|
362
|
+
.argument('<page>', 'Page the frame is on')
|
|
363
|
+
.argument('<name>', 'Frame name')
|
|
364
|
+
.option('--width <px>', 'New width, defaulting to the frame width')
|
|
365
|
+
.option('--height <px>', 'New height, defaulting to the frame height')
|
|
366
|
+
.option('--x <px>', 'New x, defaulting to where the frame is')
|
|
367
|
+
.option('--y <px>', 'New y, defaulting to where the frame is'),
|
|
368
|
+
).action(
|
|
369
|
+
async (
|
|
370
|
+
pageName: string,
|
|
371
|
+
name: string,
|
|
372
|
+
opts: RootOptions & {
|
|
373
|
+
width?: string
|
|
374
|
+
height?: string
|
|
375
|
+
x?: string
|
|
376
|
+
y?: string
|
|
377
|
+
},
|
|
378
|
+
) => {
|
|
379
|
+
const resolved = await resolveRoot(opts)
|
|
380
|
+
if (!resolved.ok) {
|
|
381
|
+
process.exitCode = refuse(resolved, opts.json ?? false)
|
|
382
|
+
return
|
|
383
|
+
}
|
|
384
|
+
const { root } = resolved
|
|
385
|
+
const current = readPage(root, pageName)?.frames.find(
|
|
386
|
+
(candidate) => candidate.name === name,
|
|
387
|
+
)
|
|
388
|
+
const outcome = resizeFrame(root, pageName, name, {
|
|
389
|
+
x: parsePosition(opts.x, current?.x ?? 0),
|
|
390
|
+
y: parsePosition(opts.y, current?.y ?? 0),
|
|
391
|
+
width: parseSize(opts.width, current?.width ?? DEFAULT_FRAME.width),
|
|
392
|
+
height: parseSize(opts.height, current?.height ?? DEFAULT_FRAME.height),
|
|
393
|
+
})
|
|
394
|
+
if (!outcome.ok) {
|
|
395
|
+
process.exitCode = refuse(outcome, opts.json ?? false)
|
|
396
|
+
return
|
|
397
|
+
}
|
|
398
|
+
if (opts.json) writeJson(outcome)
|
|
399
|
+
intro(BANNER)
|
|
400
|
+
logAdd(`${outcome.page}/${outcome.file}`)
|
|
401
|
+
logInfo(
|
|
402
|
+
`${outcome.box.width} × ${outcome.box.height} at ${outcome.box.x}, ${outcome.box.y}`,
|
|
403
|
+
)
|
|
404
|
+
outro()
|
|
405
|
+
},
|
|
406
|
+
)
|
|
407
|
+
|
|
344
408
|
withRoot(
|
|
345
409
|
canvas
|
|
346
410
|
.command('selection')
|
|
@@ -487,6 +551,91 @@ export function register(program: Command): void {
|
|
|
487
551
|
},
|
|
488
552
|
)
|
|
489
553
|
|
|
554
|
+
withRoot(
|
|
555
|
+
canvas
|
|
556
|
+
.command('editing')
|
|
557
|
+
.description('Mark a frame as being edited, clear it, or list the marks')
|
|
558
|
+
.argument('[target]', '<page>/<frame>, or none to list the marks')
|
|
559
|
+
.option('--done', 'Clear the mark once the edits are written')
|
|
560
|
+
.option('--by <name>', 'The label the mark shows, over the session name'),
|
|
561
|
+
)
|
|
562
|
+
.addHelpText(
|
|
563
|
+
'after',
|
|
564
|
+
[
|
|
565
|
+
'',
|
|
566
|
+
'Mark a frame before writing it and clear it with --done after, so an',
|
|
567
|
+
`open canvas shows who is editing it. A mark lapses after ${EDITING_TTL_MS / 60_000} minutes`,
|
|
568
|
+
'unless marked again, so re-mark during a long edit. A mark gates no',
|
|
569
|
+
'write, and --done on a frame holding no mark succeeds.',
|
|
570
|
+
'',
|
|
571
|
+
].join('\n'),
|
|
572
|
+
)
|
|
573
|
+
.action(
|
|
574
|
+
async (
|
|
575
|
+
target: string | undefined,
|
|
576
|
+
opts: RootOptions & { done?: boolean; by?: string },
|
|
577
|
+
) => {
|
|
578
|
+
const emitJson = opts.json ?? false
|
|
579
|
+
const resolved = await resolveRoot(opts)
|
|
580
|
+
if (!resolved.ok) {
|
|
581
|
+
process.exitCode = refuse(resolved, emitJson)
|
|
582
|
+
return
|
|
583
|
+
}
|
|
584
|
+
const { root } = resolved
|
|
585
|
+
const now = new Date()
|
|
586
|
+
|
|
587
|
+
if (target !== undefined) {
|
|
588
|
+
const [pageName, frameName, ...rest] = target.split('/')
|
|
589
|
+
if (!pageName || !frameName || rest.length > 0) {
|
|
590
|
+
process.exitCode = refuse(
|
|
591
|
+
{
|
|
592
|
+
ok: false,
|
|
593
|
+
reason: 'invalid-name',
|
|
594
|
+
detail: `${target} is not <page>/<frame>`,
|
|
595
|
+
},
|
|
596
|
+
emitJson,
|
|
597
|
+
)
|
|
598
|
+
return
|
|
599
|
+
}
|
|
600
|
+
const outcome = opts.done
|
|
601
|
+
? clearEditing(root, pageName, frameName)
|
|
602
|
+
: markEditing(
|
|
603
|
+
root,
|
|
604
|
+
pageName,
|
|
605
|
+
frameName,
|
|
606
|
+
opts.by ?? (await sessionLabel()),
|
|
607
|
+
now,
|
|
608
|
+
)
|
|
609
|
+
if (!outcome.ok) {
|
|
610
|
+
process.exitCode = refuse(outcome, emitJson)
|
|
611
|
+
return
|
|
612
|
+
}
|
|
613
|
+
} else if (opts.done) {
|
|
614
|
+
process.exitCode = refuse(
|
|
615
|
+
{
|
|
616
|
+
ok: false,
|
|
617
|
+
reason: 'invalid-name',
|
|
618
|
+
detail: '--done needs the <page>/<frame> to clear',
|
|
619
|
+
},
|
|
620
|
+
emitJson,
|
|
621
|
+
)
|
|
622
|
+
return
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
const marks = readEditing(root, now)
|
|
626
|
+
if (emitJson) writeJson({ ok: true, editing: marks })
|
|
627
|
+
intro(BANNER)
|
|
628
|
+
if (target !== undefined) {
|
|
629
|
+
logStep(`${target} ${opts.done ? 'cleared' : 'marked'}`)
|
|
630
|
+
}
|
|
631
|
+
if (marks.length === 0) logInfo('No frame is marked as being edited')
|
|
632
|
+
for (const mark of marks) {
|
|
633
|
+
logInfo(`${mark.page}/${mark.frame} ${mark.by} until ${mark.until}`)
|
|
634
|
+
}
|
|
635
|
+
outro()
|
|
636
|
+
},
|
|
637
|
+
)
|
|
638
|
+
|
|
490
639
|
withRoot(
|
|
491
640
|
canvas
|
|
492
641
|
.command('capture')
|
|
@@ -644,6 +793,29 @@ async function runComposite(
|
|
|
644
793
|
return 0
|
|
645
794
|
}
|
|
646
795
|
|
|
796
|
+
async function unlocated(): Promise<Located> {
|
|
797
|
+
return {
|
|
798
|
+
repository: null,
|
|
799
|
+
worktree: null,
|
|
800
|
+
branch: null,
|
|
801
|
+
unresolved: 'git-unavailable',
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* The caller's roster name, or a generic label when the environment names no
|
|
807
|
+
* session or the roster holds no row for it, as in a sandbox or a CI run.
|
|
808
|
+
*/
|
|
809
|
+
async function sessionLabel(): Promise<string> {
|
|
810
|
+
const identity = callerIdentity()
|
|
811
|
+
if (identity.sessionId === null && identity.pid === null) return 'a session'
|
|
812
|
+
/* The label needs the name alone, so skip the git probe each row's branch costs. */
|
|
813
|
+
const report = await resolveSessions({ locate: unlocated })
|
|
814
|
+
if (report.kind === 'absent') return 'a session'
|
|
815
|
+
const self = selfOf(report.sessions, identity)
|
|
816
|
+
return self.kind === 'self' ? self.session.name : 'a session'
|
|
817
|
+
}
|
|
818
|
+
|
|
647
819
|
/** NaN for a value that is not a number, which the content module refuses. */
|
|
648
820
|
function parsePosition(raw: string | undefined, fallback: number): number {
|
|
649
821
|
if (raw === undefined) return fallback
|
|
@@ -698,6 +870,11 @@ async function runServe(
|
|
|
698
870
|
if (tokens.source === 'none') logWarn(tokens.notice)
|
|
699
871
|
else if (tokens.source === 'installed') {
|
|
700
872
|
for (const file of tokens.files) logInfo(file)
|
|
873
|
+
for (const face of tokens.dropped ?? []) {
|
|
874
|
+
logWarn(
|
|
875
|
+
`${face.sheet}: dropped the face at ${face.file} (${face.reason})`,
|
|
876
|
+
)
|
|
877
|
+
}
|
|
701
878
|
} else logInfo('this toolkit’s own token module')
|
|
702
879
|
logStep('Stop')
|
|
703
880
|
logInfo('Ctrl-C')
|
package/src/commands/claude.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { join, resolve } from 'node:path'
|
|
|
5
5
|
import type { Command } from 'commander'
|
|
6
6
|
import { execa } from 'execa'
|
|
7
7
|
import { singleLine } from '@/commands/upgrade'
|
|
8
|
-
import { gitEnv } from '@/git
|
|
8
|
+
import { gitEnv } from '@/git/env'
|
|
9
9
|
import { claudeChain, pendingEntries, planGitignore } from '@/claude/gitignore'
|
|
10
10
|
import {
|
|
11
11
|
matchInstall,
|
|
@@ -44,7 +44,12 @@ import {
|
|
|
44
44
|
scanRouting,
|
|
45
45
|
} from '@/claude/routing'
|
|
46
46
|
import { type DriftReport, readDrift } from '@/claude/skills-drift'
|
|
47
|
-
import {
|
|
47
|
+
import { familyKeys, SKILL_FAMILIES } from '@/claude/skills-families'
|
|
48
|
+
import {
|
|
49
|
+
type FamilyGrouping,
|
|
50
|
+
groupByFamily,
|
|
51
|
+
listSkills,
|
|
52
|
+
} from '@/claude/skills-list'
|
|
48
53
|
import {
|
|
49
54
|
type ReachRefusal,
|
|
50
55
|
type ReachReport,
|
|
@@ -64,12 +69,12 @@ import {
|
|
|
64
69
|
serializeSettings,
|
|
65
70
|
writeSettings,
|
|
66
71
|
} from '@/claude/settings'
|
|
67
|
-
import { copyPreservingMode } from '@/copy'
|
|
72
|
+
import { copyPreservingMode } from '@/sync/copy'
|
|
68
73
|
import { execScript } from '@/exec'
|
|
69
74
|
import { checkoutMismatchWarning, PROJECT_ROOT } from '@/project-root'
|
|
70
75
|
import { recordDir } from '@/record-root'
|
|
71
76
|
import { SURFACE_ENTRIES, surfaceDir } from '@/surface-root'
|
|
72
|
-
import { isDirectory, resolveTarget } from '@/
|
|
77
|
+
import { isDirectory, resolveTarget } from '@/targets/validate'
|
|
73
78
|
import { injectGitignore, pruneGitignore } from '@/tooling/inject'
|
|
74
79
|
import {
|
|
75
80
|
frameError,
|
|
@@ -96,6 +101,7 @@ interface SeedsListOptions {
|
|
|
96
101
|
interface SkillsListOptions {
|
|
97
102
|
readonly json?: boolean
|
|
98
103
|
readonly names?: boolean
|
|
104
|
+
readonly byFamily?: boolean
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
interface SkillsAuditOptions {
|
|
@@ -359,8 +365,12 @@ export function register(program: Command): void {
|
|
|
359
365
|
.command('list')
|
|
360
366
|
.description('List the plugin skills shipped under claude/skills/')
|
|
361
367
|
.helpOption('-h, --help', 'Show this help message')
|
|
362
|
-
.option(
|
|
368
|
+
.option(
|
|
369
|
+
'--json',
|
|
370
|
+
'Emit JSON with each skill and the ordered family vocabulary',
|
|
371
|
+
)
|
|
363
372
|
.option('--names', 'Only list skill names, one per line')
|
|
373
|
+
.option('--by-family', 'Group the listing under each family')
|
|
364
374
|
.addHelpText(
|
|
365
375
|
'after',
|
|
366
376
|
[
|
|
@@ -368,6 +378,8 @@ export function register(program: Command): void {
|
|
|
368
378
|
'Notes:',
|
|
369
379
|
' Internal skills under .claude/skills/ are excluded, since they',
|
|
370
380
|
' never install into a target project.',
|
|
381
|
+
' Each skill carries the family its frontmatter declares under',
|
|
382
|
+
' metadata.family, and --json lists the vocabulary as families.',
|
|
371
383
|
'',
|
|
372
384
|
].join('\n'),
|
|
373
385
|
)
|
|
@@ -770,7 +782,9 @@ function runSkillsList(opts: SkillsListOptions): number {
|
|
|
770
782
|
|
|
771
783
|
if (opts.json) {
|
|
772
784
|
if (mismatch !== undefined) logWarn(mismatch)
|
|
773
|
-
process.stdout.write(
|
|
785
|
+
process.stdout.write(
|
|
786
|
+
`${JSON.stringify({ skills: listings, families: SKILL_FAMILIES })}\n`,
|
|
787
|
+
)
|
|
774
788
|
return 0
|
|
775
789
|
}
|
|
776
790
|
|
|
@@ -784,14 +798,34 @@ function runSkillsList(opts: SkillsListOptions): number {
|
|
|
784
798
|
|
|
785
799
|
intro('canon claude')
|
|
786
800
|
if (mismatch !== undefined) logWarn(mismatch)
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
801
|
+
if (opts.byFamily) {
|
|
802
|
+
reportByFamily(groupByFamily(listings))
|
|
803
|
+
} else {
|
|
804
|
+
logStep('Plugin skills')
|
|
805
|
+
for (const listing of listings) {
|
|
806
|
+
logInfo(listing.name)
|
|
807
|
+
}
|
|
790
808
|
}
|
|
791
809
|
outro()
|
|
792
810
|
return 0
|
|
793
811
|
}
|
|
794
812
|
|
|
813
|
+
/**
|
|
814
|
+
* One step per family in vocabulary order. A skill with a missing or unknown
|
|
815
|
+
* family takes a trailing step rather than vanishing, so the view still
|
|
816
|
+
* accounts for every listed skill.
|
|
817
|
+
*/
|
|
818
|
+
function reportByFamily(grouping: FamilyGrouping): void {
|
|
819
|
+
for (const group of grouping.groups) {
|
|
820
|
+
logStep(`${group.group} (${group.key})`)
|
|
821
|
+
for (const name of group.skills) logInfo(name)
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
if (grouping.unassigned.length === 0) return
|
|
825
|
+
logStep('No known family')
|
|
826
|
+
for (const name of grouping.unassigned) logWarn(name)
|
|
827
|
+
}
|
|
828
|
+
|
|
795
829
|
/**
|
|
796
830
|
* Measures the cwd for the same reason the audit does, and takes the ref as a
|
|
797
831
|
* required argument with no default. `HEAD` would be the only defensible one and
|
|
@@ -1402,6 +1436,7 @@ async function runSkillsAudit(
|
|
|
1402
1436
|
reportProvenance(report)
|
|
1403
1437
|
reportPracticeShape(report)
|
|
1404
1438
|
reportReferenceContents(report)
|
|
1439
|
+
reportFamily(report)
|
|
1405
1440
|
reportUnmeasured()
|
|
1406
1441
|
outro()
|
|
1407
1442
|
}
|
|
@@ -1426,6 +1461,7 @@ async function runSkillsAudit(
|
|
|
1426
1461
|
datedProvenance: report.datedProvenance,
|
|
1427
1462
|
practiceShape: report.practiceShape,
|
|
1428
1463
|
referenceContents: report.referenceContents,
|
|
1464
|
+
family: report.family,
|
|
1429
1465
|
},
|
|
1430
1466
|
checkpoints: {
|
|
1431
1467
|
descriptionLimit: DESCRIPTION_LIMIT,
|
|
@@ -1435,6 +1471,7 @@ async function runSkillsAudit(
|
|
|
1435
1471
|
ledgerSkills: LEDGER_SKILLS,
|
|
1436
1472
|
practiceSections: PRACTICE_SECTIONS,
|
|
1437
1473
|
contentsThreshold: CONTENTS_THRESHOLD,
|
|
1474
|
+
families: familyKeys(),
|
|
1438
1475
|
},
|
|
1439
1476
|
})}\n`,
|
|
1440
1477
|
)
|
|
@@ -1668,6 +1705,27 @@ function reportReferenceContents(report: SkillsAudit): void {
|
|
|
1668
1705
|
reportFindings(report.referenceContents)
|
|
1669
1706
|
}
|
|
1670
1707
|
|
|
1708
|
+
/**
|
|
1709
|
+
* Reports without failing the verb, the split the practice shape takes. The
|
|
1710
|
+
* Skill family stage of `canon gate run` is what fails a push on it.
|
|
1711
|
+
*/
|
|
1712
|
+
function reportFamily(report: SkillsAudit): void {
|
|
1713
|
+
logStep('Family')
|
|
1714
|
+
logInfo(
|
|
1715
|
+
`A skill under ${SHIPPED_CORPUS} declares metadata.family, one of ${familyKeys().join(', ')}.`,
|
|
1716
|
+
)
|
|
1717
|
+
|
|
1718
|
+
if (report.family.length === 0) {
|
|
1719
|
+
logInfo('Every shipped skill declares a known family.')
|
|
1720
|
+
return
|
|
1721
|
+
}
|
|
1722
|
+
|
|
1723
|
+
logWarn(
|
|
1724
|
+
`${plural(report.family.length, 'skill')} with a missing or unknown family`,
|
|
1725
|
+
)
|
|
1726
|
+
reportFindings(report.family)
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1671
1729
|
/**
|
|
1672
1730
|
* Stated on every run, including the run where everything above passed. A
|
|
1673
1731
|
* report that lists only what it measured reads as a verdict on the standard
|