@erclx/canon 5.31.0 → 5.32.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 +1 -1
- package/claude/.claude-plugin/plugin.json +1 -1
- package/claude/skills/api-design/references/adopted.md +1 -1
- package/claude/skills/auto-ship/REQUIREMENT.md +4 -16
- package/claude/skills/auto-ship/SKILL.md +13 -58
- package/claude/skills/auto-ship/references/draft-mark.md +2 -2
- package/claude/skills/auto-ship/references/failure-recovery.md +1 -5
- package/claude/skills/auto-ship/references/tier-failures.md +1 -1
- package/claude/skills/auto-ship/references/ui-checklist.md +2 -2
- package/claude/skills/auto-ship/references/verb-absent.md +3 -27
- package/claude/skills/backlog-triage/SKILL.md +1 -1
- package/claude/skills/canon-cli/REQUIREMENT.md +4 -6
- package/claude/skills/canon-cli/SKILL.md +2 -2
- package/claude/skills/canon-feedback/REQUIREMENT.md +1 -1
- package/claude/skills/canon-feedback/SKILL.md +10 -13
- package/claude/skills/canon-feedback-triage/REQUIREMENT.md +13 -14
- package/claude/skills/canon-feedback-triage/SKILL.md +49 -30
- package/claude/skills/canvas/REQUIREMENT.md +3 -4
- package/claude/skills/canvas/SKILL.md +5 -5
- package/{standards → claude/skills/canvas/references}/canvas.md +12 -1
- package/claude/skills/ci-workflow/REQUIREMENT.md +0 -1
- package/claude/skills/context-fold/REQUIREMENT.md +8 -20
- package/claude/skills/context-fold/SKILL.md +20 -44
- package/claude/skills/context-fold/references/classify.md +7 -8
- package/claude/skills/context-fold/references/completion-report.md +2 -6
- package/claude/skills/context-fold/references/context-refresh.md +5 -15
- package/claude/skills/context-fold/references/promoted-pages.md +3 -3
- package/claude/skills/context-fold/references/receipt-sweep.md +6 -17
- package/claude/skills/context-fold/references/unusable-baseline.md +4 -6
- package/claude/skills/create-rule/REQUIREMENT.md +0 -1
- package/claude/skills/create-skill/REQUIREMENT.md +6 -4
- package/claude/skills/create-skill/SKILL.md +8 -7
- package/{standards → claude/skills/create-skill/references}/skill-paths.md +1 -0
- package/claude/skills/decision-escalate/REQUIREMENT.md +1 -1
- package/claude/skills/decision-escalate/SKILL.md +1 -1
- package/claude/skills/design-taste/REQUIREMENT.md +0 -2
- package/claude/skills/design-taste/SKILL.md +1 -3
- package/claude/skills/design-taste/references/vocabulary.md +1 -1
- package/claude/skills/docs-sync/REQUIREMENT.md +3 -3
- package/claude/skills/document-health/REQUIREMENT.md +2 -2
- package/claude/skills/document-health/SKILL.md +4 -5
- package/claude/skills/document-health/references/axes.md +4 -6
- package/claude/skills/draft-and-pick/REQUIREMENT.md +16 -14
- package/claude/skills/draft-and-pick/SKILL.md +40 -32
- package/claude/skills/draft-and-pick/references/live-arms.md +9 -9
- package/claude/skills/draft-doc/REQUIREMENT.md +5 -5
- package/claude/skills/draft-doc/SKILL.md +9 -10
- package/claude/skills/draft-doc/references/requirements.md +61 -0
- package/claude/skills/draft-doc/references/wiki-kind.md +53 -0
- package/claude/skills/draft-doc/references/wiki.md +50 -30
- package/claude/skills/draft-figure/REQUIREMENT.md +2 -2
- package/claude/skills/draft-figure/SKILL.md +4 -4
- package/claude/skills/draft-figure/references/architecture-set.md +4 -4
- package/claude/skills/draft-figure/references/hand-drawn.md +1 -1
- package/claude/skills/draft-figure/references/mermaid-path.md +55 -0
- package/claude/skills/draft-figure/references/mermaid.md +43 -34
- package/claude/skills/draft-identity/REQUIREMENT.md +1 -1
- package/claude/skills/draft-identity/SKILL.md +9 -7
- package/claude/skills/draft-ready/REQUIREMENT.md +1 -1
- package/claude/skills/draft-ready/SKILL.md +6 -6
- package/claude/skills/draft-ready/references/assembly.md +1 -1
- package/{standards → claude/skills/draft-ready/references}/ready.md +13 -5
- package/claude/skills/git-issue/references/issue.md +3 -3
- package/claude/skills/git-ship/REQUIREMENT.md +0 -2
- package/claude/skills/git-ship/SKILL.md +16 -24
- package/claude/skills/plan-feature/SKILL.md +2 -6
- package/{standards → claude/skills/plan-feature/references}/plan.md +9 -10
- package/claude/skills/plan-groundwork/SKILL.md +5 -6
- package/{standards → claude/skills/plan-groundwork/references}/groundwork.md +17 -4
- package/claude/skills/plan-intake/SKILL.md +2 -3
- package/{standards → claude/skills/plan-intake/references}/intake.md +17 -2
- package/claude/skills/plan-intake-answer/REQUIREMENT.md +1 -1
- package/claude/skills/plan-intake-answer/SKILL.md +1 -1
- package/claude/skills/review-address/SKILL.md +1 -1
- package/claude/skills/review-craft/REQUIREMENT.md +3 -3
- package/claude/skills/review-craft/SKILL.md +2 -2
- package/claude/skills/review-craft/references/ui.md +4 -4
- package/claude/skills/review-pr/REQUIREMENT.md +1 -1
- package/claude/skills/review-pr/SKILL.md +5 -7
- package/claude/skills/review-ui/REQUIREMENT.md +0 -1
- package/claude/skills/review-ui/SKILL.md +1 -1
- package/claude/skills/role-orchestrator/REQUIREMENT.md +0 -3
- package/claude/skills/role-orchestrator/SKILL.md +8 -8
- package/claude/skills/role-orchestrator/references/orchestrator-dispatch.md +3 -3
- package/claude/skills/role-orchestrator/references/orchestrator-handoff.md +7 -10
- package/claude/skills/role-orchestrator/references/orchestrator-parked.md +2 -2
- package/claude/skills/role-orchestrator/references/orchestrator-refill.md +0 -3
- package/claude/skills/role-orchestrator/references/orchestrator-resume.md +1 -1
- package/claude/skills/role-planner/SKILL.md +2 -2
- package/claude/skills/role-reviewer/SKILL.md +1 -1
- package/claude/skills/role-worker/SKILL.md +2 -2
- package/claude/skills/session-compact/REQUIREMENT.md +1 -4
- package/claude/skills/session-compact/SKILL.md +3 -13
- package/claude/skills/session-compact/references/handoff-note.md +2 -2
- package/claude/skills/session-map/REQUIREMENT.md +1 -9
- package/claude/skills/session-map/SKILL.md +8 -16
- package/{standards → claude/skills/session-map/references}/session.md +17 -11
- package/claude/skills/session-relay/REQUIREMENT.md +1 -1
- package/claude/skills/session-resume/REQUIREMENT.md +4 -6
- package/claude/skills/session-resume/SKILL.md +3 -9
- package/claude/skills/session-worktree/SKILL.md +1 -1
- package/claude/skills/session-worktree/references/main-root-writes.md +2 -2
- package/claude/skills/systematic-debugging/REQUIREMENT.md +0 -1
- package/claude/skills/target-check/REQUIREMENT.md +3 -3
- package/claude/skills/target-check/SKILL.md +1 -1
- package/claude/skills/target-check/references/domains.md +1 -1
- package/claude/skills/target-setup/REQUIREMENT.md +4 -4
- package/claude/skills/target-setup/SKILL.md +3 -3
- package/claude/skills/target-setup/references/detect.md +1 -1
- package/claude/skills/task-board/SKILL.md +4 -4
- package/claude/skills/task-board/references/archive.md +1 -1
- package/{standards → claude/skills/task-board/references}/board.md +9 -0
- package/claude/skills/task-board/references/decline.md +1 -1
- package/{standards → claude/skills/task-board/references}/tasks.md +13 -8
- package/claude/skills/teach-workspace/SKILL.md +1 -1
- package/claude/skills/teach-workspace/references/revisit.md +1 -1
- package/{standards → claude/skills/teach-workspace/references}/teach.md +17 -5
- package/claude/skills/ux-audit/REQUIREMENT.md +2 -2
- package/claude/skills/ux-audit/SKILL.md +2 -3
- package/claude/skills/write-human/REQUIREMENT.md +0 -1
- package/claude/skills/write-human/SKILL.md +1 -1
- package/docs/agents/audits.md +1 -1
- package/docs/agents/canvas.md +1 -1
- package/docs/agents/commands.md +1 -6
- package/docs/agents/context-audit-checks.md +9 -23
- package/docs/agents/context-audit.md +7 -15
- package/docs/agents/context-classify.md +5 -5
- package/docs/agents/counts.md +5 -5
- package/docs/agents/drift-surfaces.md +2 -2
- package/docs/agents/index.md +3 -5
- package/docs/agents/install-and-sync.md +6 -6
- package/docs/agents/intake.md +1 -1
- package/docs/agents/records-stale.md +5 -5
- package/docs/agents/records-validate.md +1 -1
- package/docs/agents/records.md +2 -2
- package/docs/agents/restated.md +1 -1
- package/docs/agents/rule-citations.md +1 -1
- package/docs/agents/skills-audit.md +2 -2
- package/docs/agents/state-scoped-risk.md +2 -2
- package/docs/agents/superseded.md +2 -2
- package/docs/agents/tasks-plans.md +1 -1
- package/docs/agents/tasks-validate.md +1 -1
- package/docs/agents/tasks.md +1 -1
- package/docs/agents/teach.md +1 -1
- package/docs/agents/upstream.md +26 -2
- package/docs/target/migrations.md +24 -0
- package/docs/target/projects.md +5 -7
- package/docs/target/scenarios.md +2 -2
- package/docs/target/sync.md +3 -3
- package/docs/workflow/ai-workflow.md +7 -38
- package/docs/workflow/index.md +1 -1
- package/docs/workflow/operating-model.md +8 -8
- package/docs/workflow/parallel-features.md +7 -7
- package/docs/workflow/skill-map-on-request.md +12 -15
- package/docs/workflow/skill-map.md +20 -32
- package/docs/workflow/visual-design-workflow.md +11 -14
- package/governance/renames.toml +0 -19
- package/governance/rules/canon/605-worktrees.md +1 -1
- package/governance/rules/{standards → canon}/plan.md +4 -5
- package/governance/rules/canon/tasks.md +18 -0
- package/governance/stacks/base.toml +1 -1
- package/package.json +1 -1
- package/scripts/core/install-check.sh +1 -1
- package/scripts/core/regen-web-previews.ts +1 -1
- package/src/audits/catalog.ts +5 -97
- package/src/claude/cases/authoring.ts +0 -18
- package/src/claude/cases/misc.ts +0 -13
- package/src/claude/cases/setup.ts +3 -20
- package/src/claude/cases/workflow.ts +0 -33
- package/src/claude/seeds.ts +1 -7
- package/src/claude/skills-audit.ts +1 -1
- package/src/cli/help.ts +1 -4
- package/src/cli.ts +0 -4
- package/src/commands/claude.ts +8 -14
- package/src/commands/context.ts +18 -280
- package/src/commands/feedback.ts +22 -91
- package/src/commands/records.ts +5 -5
- package/src/commands/sync.ts +5 -3
- package/src/commands/upstream.ts +135 -0
- package/src/context/audit.ts +5 -14
- package/src/context/classify/extract.ts +6 -13
- package/src/context/classify/patterns.ts +2 -41
- package/src/context/classify/prompts.ts +2 -6
- package/src/context/classify/run.ts +3 -3
- package/src/context/folders.ts +7 -12
- package/src/context/gate.ts +11 -91
- package/src/context/requirements.ts +13 -4
- package/src/counts/scan.ts +3 -3
- package/src/design/components.ts +2 -2
- package/src/feedback/body.ts +0 -12
- package/src/gate/measures.ts +6 -57
- package/src/gov/citations.ts +9 -3
- package/src/gov/restated.ts +3 -3
- package/src/markdown/structure.ts +2 -2
- package/src/migrate/record-layout.ts +3 -3
- package/src/records/ordinal.ts +1 -1
- package/src/records/prune.ts +2 -1
- package/src/records/stale-canonical.ts +2 -2
- package/src/roots/record.ts +1 -1
- package/src/sandbox/scenario.ts +115 -11
- package/src/shipped/references.ts +3 -3
- package/src/standards/read.ts +31 -0
- package/src/sync/seeds-report.ts +1 -1
- package/src/tasks/reach.ts +2 -2
- package/src/tasks/validate.ts +1 -1
- package/src/teach/workspace.ts +1 -1
- package/src/upstream/cursor.ts +1 -1
- package/src/upstream/due.ts +128 -0
- package/src/upstream/releases.ts +2 -1
- package/standards/context.md +3 -5
- package/standards/design.md +2 -3
- package/standards/index.md +0 -22
- package/standards/publish.md +1 -1
- package/standards/skill.md +1 -1
- package/tooling/base/reference.md +1 -1
- package/tooling/claude/reference.md +6 -20
- package/tooling/claude/seeds/.claude/hooks/records-index.sh +8 -20
- package/tooling/claude/seeds/.claude/hooks/standards-audit.sh +3 -3
- package/tooling/claude/seeds/CLAUDE.md +1 -3
- package/tooling/claude/seeds/canon/REQUIREMENTS.md +2 -9
- package/tooling/web/configs/e2e/screenshot.ts +0 -1
- package/claude/skills/auto-ship/references/review-findings.md +0 -20
- package/claude/skills/bash-cli-script/REQUIREMENT.md +0 -43
- package/claude/skills/bash-cli-script/SKILL.md +0 -53
- package/claude/skills/bash-cli-script/references/template.md +0 -19
- package/claude/skills/canon-operator/REQUIREMENT.md +0 -62
- package/claude/skills/canon-operator/SKILL.md +0 -113
- package/claude/skills/canon-rollout/REQUIREMENT.md +0 -59
- package/claude/skills/canon-rollout/SKILL.md +0 -140
- package/claude/skills/canon-rollout/references/failure-recovery.md +0 -20
- package/claude/skills/context-fold/references/anchor-sweep.md +0 -58
- package/claude/skills/context-fold/references/wireframe-sweep.md +0 -45
- package/claude/skills/create-standard/REQUIREMENT.md +0 -33
- package/claude/skills/create-standard/SKILL.md +0 -31
- package/claude/skills/design-extract/REQUIREMENT.md +0 -42
- package/claude/skills/design-extract/SKILL.md +0 -108
- package/claude/skills/draft-and-pick/references/frame.html +0 -118
- package/claude/skills/draft-doc/references/wireframes.md +0 -48
- package/claude/skills/markdown-propose/REQUIREMENT.md +0 -47
- package/claude/skills/markdown-propose/SKILL.md +0 -122
- package/claude/skills/markdown-propose/references/format.md +0 -118
- package/claude/skills/memory-capture/REQUIREMENT.md +0 -50
- package/claude/skills/memory-capture/SKILL.md +0 -117
- package/claude/skills/memory-review/REQUIREMENT.md +0 -52
- package/claude/skills/memory-review/SKILL.md +0 -129
- package/claude/skills/memory-review/references/later-phases.md +0 -118
- package/claude/skills/memory-review/references/receipt-format.md +0 -64
- package/claude/skills/project-commands/REQUIREMENT.md +0 -42
- package/claude/skills/project-commands/SKILL.md +0 -87
- package/claude/skills/repo-metadata/REQUIREMENT.md +0 -37
- package/claude/skills/repo-metadata/SKILL.md +0 -54
- package/claude/skills/restate-plainly/REQUIREMENT.md +0 -41
- package/claude/skills/restate-plainly/SKILL.md +0 -41
- package/claude/skills/review-branch/REQUIREMENT.md +0 -40
- package/claude/skills/review-branch/SKILL.md +0 -137
- package/claude/skills/seed-sync/REQUIREMENT.md +0 -45
- package/claude/skills/seed-sync/SKILL.md +0 -158
- package/claude/skills/sketch-design/REQUIREMENT.md +0 -38
- package/claude/skills/sketch-design/SKILL.md +0 -94
- package/claude/skills/ux-walkthrough/REQUIREMENT.md +0 -56
- package/claude/skills/ux-walkthrough/SKILL.md +0 -53
- package/claude/skills/ux-walkthrough/references/builds.md +0 -15
- package/claude/skills/ux-walkthrough/references/candidate-pages.md +0 -11
- package/claude/skills/ux-walkthrough/references/measuring.md +0 -22
- package/claude/skills/ux-walkthrough/references/record.md +0 -36
- package/claude/skills/ux-walkthrough/references/relay.md +0 -25
- package/docs/agents/architecture-record.md +0 -36
- package/docs/agents/review-classification.md +0 -76
- package/governance/rules/canon/603-memory.md +0 -11
- package/governance/rules/lang/120-bash.md +0 -18
- package/governance/rules/standards/architecture.md +0 -13
- package/governance/rules/standards/groundwork.md +0 -13
- package/governance/rules/standards/intake.md +0 -12
- package/governance/rules/standards/memory.md +0 -12
- package/governance/rules/standards/ready.md +0 -13
- package/governance/rules/standards/requirements.md +0 -12
- package/governance/rules/standards/session.md +0 -13
- package/governance/rules/standards/tasks.md +0 -15
- package/governance/rules/standards/teach.md +0 -14
- package/governance/rules/standards/wiki.md +0 -13
- package/governance/rules/standards/wireframes.md +0 -13
- package/src/autoship/classify.ts +0 -75
- package/src/autoship/paths.ts +0 -50
- package/src/commands/autoship.ts +0 -129
- package/src/commands/repo.ts +0 -393
- package/src/context/architecture.ts +0 -654
- package/src/context/wireframe-states.ts +0 -238
- package/src/repo/metadata.ts +0 -206
- package/standards/architecture.md +0 -127
- package/standards/memory.md +0 -158
- package/standards/mermaid.md +0 -64
- package/standards/requirements.md +0 -79
- package/standards/wiki.md +0 -77
- package/standards/wireframes.md +0 -143
- package/tooling/claude/seeds/.claude/memory/index.md +0 -8
- package/tooling/claude/seeds/canon/ARCHITECTURE.md +0 -23
- package/tooling/claude/seeds/canon/wireframes/index.md +0 -8
- /package/{standards → claude/skills/create-skill/references}/skill-practice.md +0 -0
- /package/{standards → claude/skills/create-skill/references}/skill-requirement.md +0 -0
- /package/{standards → claude/skills/plan-feature/references}/plan-lifecycle.md +0 -0
- /package/{standards → claude/skills/plan-groundwork/references}/groundwork-spikes.md +0 -0
- /package/{standards → claude/skills/task-board/references}/tasks-decline.md +0 -0
- /package/{standards → claude/skills/teach-workspace/references}/glossary.md +0 -0
package/src/commands/repo.ts
DELETED
|
@@ -1,393 +0,0 @@
|
|
|
1
|
-
import { resolve } from 'node:path'
|
|
2
|
-
import type { Command } from 'commander'
|
|
3
|
-
import { execa } from 'execa'
|
|
4
|
-
import { gitEnv } from '@/git/env'
|
|
5
|
-
import {
|
|
6
|
-
compareMetadata,
|
|
7
|
-
type CurrentMetadata,
|
|
8
|
-
isValidTopic,
|
|
9
|
-
type MetadataDiff,
|
|
10
|
-
proposeMetadata,
|
|
11
|
-
} from '@/repo/metadata'
|
|
12
|
-
import {
|
|
13
|
-
intro,
|
|
14
|
-
logAdd,
|
|
15
|
-
logInfo,
|
|
16
|
-
logRemove,
|
|
17
|
-
logStep,
|
|
18
|
-
logWarn,
|
|
19
|
-
outro,
|
|
20
|
-
} from '@/cli/ui'
|
|
21
|
-
|
|
22
|
-
const GH_TIMEOUT_MS = 30_000
|
|
23
|
-
|
|
24
|
-
type SourceRefusal = 'gh-missing' | 'gh-failed'
|
|
25
|
-
type ApplyRefusal =
|
|
26
|
-
| SourceRefusal
|
|
27
|
-
| 'no-changes'
|
|
28
|
-
| 'empty-topics'
|
|
29
|
-
| 'invalid-topics'
|
|
30
|
-
| 'wrong-repo'
|
|
31
|
-
|
|
32
|
-
const REFUSALS: Record<ApplyRefusal, string> = {
|
|
33
|
-
'gh-missing': 'gh is not on the path, so no remote could be read.',
|
|
34
|
-
'gh-failed':
|
|
35
|
-
'gh could not read this repository. Check the remote and gh auth status.',
|
|
36
|
-
'no-changes':
|
|
37
|
-
'Nothing to apply. Pass --description, --homepage, or --topics with the answered value.',
|
|
38
|
-
'empty-topics':
|
|
39
|
-
'--topics carried no usable topic, and this command never reads that as a request to remove every topic the remote carries. Pass the full desired set, or omit the flag to leave topics untouched.',
|
|
40
|
-
'invalid-topics':
|
|
41
|
-
'--topics carried an entry GitHub does not accept as a topic (lowercase letters, digits, and internal hyphens only). This command never narrows the desired set silently, since dropping it changes what the remaining diff removes.',
|
|
42
|
-
'wrong-repo':
|
|
43
|
-
'The remote --root resolved to is not the one --repo named. This command never writes to a repository the invocation did not explicitly confirm.',
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
interface ProposeOptions {
|
|
47
|
-
readonly root?: string
|
|
48
|
-
readonly json?: boolean
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
interface ApplyOptions {
|
|
52
|
-
readonly description?: string
|
|
53
|
-
readonly homepage?: string
|
|
54
|
-
readonly topics?: string
|
|
55
|
-
readonly repo: string
|
|
56
|
-
readonly root?: string
|
|
57
|
-
readonly json?: boolean
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
type CurrentRead =
|
|
61
|
-
| {
|
|
62
|
-
readonly kind: 'read'
|
|
63
|
-
readonly current: CurrentMetadata
|
|
64
|
-
readonly nameWithOwner: string
|
|
65
|
-
}
|
|
66
|
-
| { readonly kind: 'refused'; readonly reason: SourceRefusal }
|
|
67
|
-
|
|
68
|
-
/** Reads what the remote already carries. The one network call this domain makes to read. */
|
|
69
|
-
async function readCurrent(cwd: string): Promise<CurrentRead> {
|
|
70
|
-
if (Bun.which('gh') === null) return { kind: 'refused', reason: 'gh-missing' }
|
|
71
|
-
|
|
72
|
-
try {
|
|
73
|
-
const result = await execa(
|
|
74
|
-
'gh',
|
|
75
|
-
[
|
|
76
|
-
'repo',
|
|
77
|
-
'view',
|
|
78
|
-
'--json',
|
|
79
|
-
'description,homepageUrl,repositoryTopics,nameWithOwner',
|
|
80
|
-
],
|
|
81
|
-
{ cwd, timeout: GH_TIMEOUT_MS, env: gitEnv(), extendEnv: false },
|
|
82
|
-
)
|
|
83
|
-
const row = JSON.parse(result.stdout) as {
|
|
84
|
-
description?: string
|
|
85
|
-
homepageUrl?: string
|
|
86
|
-
repositoryTopics?: readonly { name: string }[]
|
|
87
|
-
nameWithOwner?: string
|
|
88
|
-
}
|
|
89
|
-
return {
|
|
90
|
-
kind: 'read',
|
|
91
|
-
current: {
|
|
92
|
-
description: row.description ?? '',
|
|
93
|
-
homepage: row.homepageUrl ?? '',
|
|
94
|
-
topics: (row.repositoryTopics ?? []).map((topic) => topic.name),
|
|
95
|
-
},
|
|
96
|
-
nameWithOwner: row.nameWithOwner ?? '',
|
|
97
|
-
}
|
|
98
|
-
} catch {
|
|
99
|
-
return { kind: 'refused', reason: 'gh-failed' }
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export function register(program: Command): void {
|
|
104
|
-
const repo = program
|
|
105
|
-
.command('repo')
|
|
106
|
-
.description('Read and write this repository’s own remote metadata')
|
|
107
|
-
.helpOption('-h, --help', 'Show this help message')
|
|
108
|
-
|
|
109
|
-
const metadata = repo
|
|
110
|
-
.command('metadata')
|
|
111
|
-
.description('The About description, homepage, and topics GitHub shows')
|
|
112
|
-
.helpOption('-h, --help', 'Show this help message')
|
|
113
|
-
|
|
114
|
-
metadata
|
|
115
|
-
.command('propose')
|
|
116
|
-
.description(
|
|
117
|
-
'Compare a locally computed description, homepage, and topic set against the remote',
|
|
118
|
-
)
|
|
119
|
-
.helpOption('-h, --help', 'Show this help message')
|
|
120
|
-
.option('--root <path>', 'Repository to read, defaulting to the cwd')
|
|
121
|
-
.option('--json', 'Add a machine-readable record on stdout')
|
|
122
|
-
.addHelpText(
|
|
123
|
-
'after',
|
|
124
|
-
[
|
|
125
|
-
'',
|
|
126
|
-
"Computes from the tree alone: a description from the README's opening",
|
|
127
|
-
'line past its title and badges, and a homepage and topics from',
|
|
128
|
-
"package.json's own homepage and keywords fields. A field neither file",
|
|
129
|
-
'declares is never diffed, so an undeclared topic set never reads as a',
|
|
130
|
-
'proposal to clear what the remote already carries.',
|
|
131
|
-
'',
|
|
132
|
-
'This is the read half. It never writes. Run `canon repo metadata apply`',
|
|
133
|
-
'with the answered fields once a proposal is worth taking.',
|
|
134
|
-
'',
|
|
135
|
-
'Exit codes:',
|
|
136
|
-
' 0 the remote already carries what this run computed',
|
|
137
|
-
' 1 refused, with the reason on stderr or in the JSON record',
|
|
138
|
-
' 2 a difference was found',
|
|
139
|
-
'',
|
|
140
|
-
'Examples:',
|
|
141
|
-
' canon repo metadata propose',
|
|
142
|
-
' canon repo metadata propose --json',
|
|
143
|
-
'',
|
|
144
|
-
].join('\n'),
|
|
145
|
-
)
|
|
146
|
-
.action(async (opts: ProposeOptions) => {
|
|
147
|
-
process.exitCode = await runPropose(opts)
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
metadata
|
|
151
|
-
.command('apply')
|
|
152
|
-
.description(
|
|
153
|
-
'Write an explicitly supplied description, homepage, or topic set to the remote',
|
|
154
|
-
)
|
|
155
|
-
.helpOption('-h, --help', 'Show this help message')
|
|
156
|
-
.requiredOption(
|
|
157
|
-
'--repo <owner/name>',
|
|
158
|
-
'Repository this run is allowed to write to, checked against what gh resolves',
|
|
159
|
-
)
|
|
160
|
-
.option('--description <text>', 'About text to write')
|
|
161
|
-
.option('--homepage <url>', 'Homepage URL to write')
|
|
162
|
-
.option(
|
|
163
|
-
'--topics <list>',
|
|
164
|
-
'Comma-separated topic set to write, replacing what the remote carries',
|
|
165
|
-
)
|
|
166
|
-
.option('--root <path>', 'Repository to read, defaulting to the cwd')
|
|
167
|
-
.option('--json', 'Add a machine-readable record on stdout')
|
|
168
|
-
.addHelpText(
|
|
169
|
-
'after',
|
|
170
|
-
[
|
|
171
|
-
'',
|
|
172
|
-
'Takes the answered value for each field directly rather than',
|
|
173
|
-
're-running propose, so a change this writes is always one a person or',
|
|
174
|
-
'a second invocation already read and confirmed. --topics is the full',
|
|
175
|
-
'desired set. gh only takes an add and a remove list, so this reads the',
|
|
176
|
-
'current set once to compute both. An entry shaped like something other',
|
|
177
|
-
'than a GitHub topic refuses the whole run rather than being dropped.',
|
|
178
|
-
'',
|
|
179
|
-
'--repo is required and takes no default, since --root silently',
|
|
180
|
-
'resolving to the caller’s own cwd is what turned a verification run',
|
|
181
|
-
'against this exact command into a live write against a public remote.',
|
|
182
|
-
'The value is checked against what gh resolves for --root, so the',
|
|
183
|
-
'write refuses on any repository the invocation did not name aloud.',
|
|
184
|
-
'',
|
|
185
|
-
'Exit codes:',
|
|
186
|
-
' 0 the write succeeded, or the remote already matched',
|
|
187
|
-
' 1 refused, with the reason on stderr or in the JSON record',
|
|
188
|
-
'',
|
|
189
|
-
'Examples:',
|
|
190
|
-
' canon repo metadata apply --repo erclx/canon --description "One source for conventions."',
|
|
191
|
-
' canon repo metadata apply --repo erclx/canon --topics cli-tool,governance,standards',
|
|
192
|
-
'',
|
|
193
|
-
].join('\n'),
|
|
194
|
-
)
|
|
195
|
-
.action(async (opts: ApplyOptions) => {
|
|
196
|
-
process.exitCode = await runApply(opts)
|
|
197
|
-
})
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function refuse(
|
|
201
|
-
reason: ApplyRefusal,
|
|
202
|
-
emitJson: boolean,
|
|
203
|
-
root: string,
|
|
204
|
-
detail?: string,
|
|
205
|
-
): number {
|
|
206
|
-
const message =
|
|
207
|
-
detail === undefined ? REFUSALS[reason] : `${REFUSALS[reason]} ${detail}`
|
|
208
|
-
|
|
209
|
-
logStep('Refused')
|
|
210
|
-
logWarn(message)
|
|
211
|
-
outro()
|
|
212
|
-
|
|
213
|
-
if (emitJson) {
|
|
214
|
-
process.stdout.write(`${JSON.stringify({ root, reason, message })}\n`)
|
|
215
|
-
}
|
|
216
|
-
return 1
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
async function runPropose(opts: ProposeOptions): Promise<number> {
|
|
220
|
-
const root = resolve(opts.root ?? process.cwd())
|
|
221
|
-
const emitJson = opts.json ?? false
|
|
222
|
-
|
|
223
|
-
intro('canon repo metadata propose')
|
|
224
|
-
|
|
225
|
-
const [proposal, currentRead] = await Promise.all([
|
|
226
|
-
proposeMetadata(root),
|
|
227
|
-
readCurrent(root),
|
|
228
|
-
])
|
|
229
|
-
|
|
230
|
-
if (currentRead.kind === 'refused') {
|
|
231
|
-
return refuse(currentRead.reason, emitJson, root)
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const diff: MetadataDiff = compareMetadata(currentRead.current, proposal)
|
|
235
|
-
const changed = Object.keys(diff).length > 0
|
|
236
|
-
|
|
237
|
-
logStep('Repository')
|
|
238
|
-
logInfo(currentRead.nameWithOwner)
|
|
239
|
-
|
|
240
|
-
logStep('Computed')
|
|
241
|
-
if (
|
|
242
|
-
proposal.description === undefined &&
|
|
243
|
-
proposal.homepage === undefined &&
|
|
244
|
-
proposal.topics === undefined
|
|
245
|
-
) {
|
|
246
|
-
logInfo('nothing local resolved a description, a homepage, or topics')
|
|
247
|
-
} else {
|
|
248
|
-
if (proposal.description !== undefined) {
|
|
249
|
-
logInfo(`description: ${proposal.description}`)
|
|
250
|
-
}
|
|
251
|
-
if (proposal.homepage !== undefined)
|
|
252
|
-
logInfo(`homepage: ${proposal.homepage}`)
|
|
253
|
-
if (proposal.topics !== undefined) {
|
|
254
|
-
logInfo(`topics: ${proposal.topics.join(', ')}`)
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
logStep(changed ? 'Difference' : 'No difference')
|
|
259
|
-
if (!changed) {
|
|
260
|
-
logInfo('the remote already carries what this run computed')
|
|
261
|
-
} else {
|
|
262
|
-
if (diff.description !== undefined) {
|
|
263
|
-
logWarn(
|
|
264
|
-
`description: "${diff.description.current}" → "${diff.description.proposed}"`,
|
|
265
|
-
)
|
|
266
|
-
}
|
|
267
|
-
if (diff.homepage !== undefined) {
|
|
268
|
-
logWarn(
|
|
269
|
-
`homepage: "${diff.homepage.current}" → "${diff.homepage.proposed}"`,
|
|
270
|
-
)
|
|
271
|
-
}
|
|
272
|
-
if (diff.topics !== undefined) {
|
|
273
|
-
for (const topic of diff.topics.added) logAdd(`topic: ${topic}`)
|
|
274
|
-
for (const topic of diff.topics.removed) logRemove(`topic: ${topic}`)
|
|
275
|
-
}
|
|
276
|
-
logInfo(
|
|
277
|
-
'Run `canon repo metadata apply` with the answered fields. This run writes nothing.',
|
|
278
|
-
)
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
outro()
|
|
282
|
-
|
|
283
|
-
if (emitJson) {
|
|
284
|
-
process.stdout.write(
|
|
285
|
-
`${JSON.stringify({
|
|
286
|
-
root,
|
|
287
|
-
repo: currentRead.nameWithOwner,
|
|
288
|
-
current: currentRead.current,
|
|
289
|
-
proposal,
|
|
290
|
-
diff,
|
|
291
|
-
})}\n`,
|
|
292
|
-
)
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
return changed ? 2 : 0
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
async function runApply(opts: ApplyOptions): Promise<number> {
|
|
299
|
-
const root = resolve(opts.root ?? process.cwd())
|
|
300
|
-
const emitJson = opts.json ?? false
|
|
301
|
-
|
|
302
|
-
intro('canon repo metadata apply')
|
|
303
|
-
|
|
304
|
-
if (
|
|
305
|
-
opts.description === undefined &&
|
|
306
|
-
opts.homepage === undefined &&
|
|
307
|
-
opts.topics === undefined
|
|
308
|
-
) {
|
|
309
|
-
return refuse('no-changes', emitJson, root)
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
let desired: ReadonlySet<string> | undefined
|
|
313
|
-
if (opts.topics !== undefined) {
|
|
314
|
-
const entries = opts.topics
|
|
315
|
-
.split(',')
|
|
316
|
-
.map((topic) => topic.trim().toLowerCase())
|
|
317
|
-
.filter((topic) => topic !== '')
|
|
318
|
-
if (entries.length === 0) return refuse('empty-topics', emitJson, root)
|
|
319
|
-
|
|
320
|
-
const invalid = entries.filter((topic) => !isValidTopic(topic))
|
|
321
|
-
if (invalid.length > 0) {
|
|
322
|
-
return refuse(
|
|
323
|
-
'invalid-topics',
|
|
324
|
-
emitJson,
|
|
325
|
-
root,
|
|
326
|
-
`Invalid: ${invalid.join(', ')}`,
|
|
327
|
-
)
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
desired = new Set(entries)
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// Read ahead of every write, never only for --topics, since this is the
|
|
334
|
-
// one call that confirms --repo names the remote --root actually resolved
|
|
335
|
-
// to. A write skipping it on a description-only or homepage-only run would
|
|
336
|
-
// reopen the gap --repo exists to close.
|
|
337
|
-
const currentRead = await readCurrent(root)
|
|
338
|
-
if (currentRead.kind === 'refused')
|
|
339
|
-
return refuse(currentRead.reason, emitJson, root)
|
|
340
|
-
if (currentRead.nameWithOwner !== opts.repo) {
|
|
341
|
-
return refuse('wrong-repo', emitJson, root)
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const args = ['repo', 'edit']
|
|
345
|
-
if (opts.description !== undefined)
|
|
346
|
-
args.push('--description', opts.description)
|
|
347
|
-
if (opts.homepage !== undefined) args.push('--homepage', opts.homepage)
|
|
348
|
-
|
|
349
|
-
if (desired !== undefined) {
|
|
350
|
-
const currentSet = new Set(currentRead.current.topics)
|
|
351
|
-
const toAdd = [...desired].filter((topic) => !currentSet.has(topic))
|
|
352
|
-
const toRemove = currentRead.current.topics.filter(
|
|
353
|
-
(topic) => !desired.has(topic),
|
|
354
|
-
)
|
|
355
|
-
|
|
356
|
-
if (toAdd.length > 0) args.push('--add-topic', toAdd.join(','))
|
|
357
|
-
if (toRemove.length > 0) args.push('--remove-topic', toRemove.join(','))
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
if (args.length === 2) {
|
|
361
|
-
logStep('Applied')
|
|
362
|
-
logInfo('the remote already matches every field supplied. Nothing written.')
|
|
363
|
-
outro()
|
|
364
|
-
|
|
365
|
-
if (emitJson) {
|
|
366
|
-
process.stdout.write(`${JSON.stringify({ root, written: false })}\n`)
|
|
367
|
-
}
|
|
368
|
-
return 0
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
if (Bun.which('gh') === null) return refuse('gh-missing', emitJson, root)
|
|
372
|
-
|
|
373
|
-
try {
|
|
374
|
-
await execa('gh', args, {
|
|
375
|
-
cwd: root,
|
|
376
|
-
timeout: GH_TIMEOUT_MS,
|
|
377
|
-
env: gitEnv(),
|
|
378
|
-
extendEnv: false,
|
|
379
|
-
})
|
|
380
|
-
} catch {
|
|
381
|
-
return refuse('gh-failed', emitJson, root)
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
logStep('Applied')
|
|
385
|
-
logInfo('gh repo edit wrote the supplied fields to the remote.')
|
|
386
|
-
outro()
|
|
387
|
-
|
|
388
|
-
if (emitJson) {
|
|
389
|
-
process.stdout.write(`${JSON.stringify({ root, written: true })}\n`)
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
return 0
|
|
393
|
-
}
|