@event4u/agent-config 8.8.0 → 8.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +139 -263
- package/README.md +5 -5
- package/dist/agent-src/commands/agent-handoff.md +39 -2
- package/dist/agent-src/commands/cost/report.md +13 -3
- package/dist/agent-src/commands/research/deep.md +8 -0
- package/dist/agent-src/commands/research/report.md +7 -0
- package/dist/agent-src/commands/roadmap/create.md +12 -0
- package/dist/agent-src/commands/roadmap/process-full.md +31 -5
- package/dist/agent-src/commands/tests/create.md +28 -6
- package/dist/agent-src/commands/work.md +33 -0
- package/dist/agent-src/commands/worktree/cleanup.md +78 -0
- package/dist/agent-src/commands/worktree/create.md +62 -0
- package/dist/agent-src/commands/worktree/status.md +63 -0
- package/dist/agent-src/commands/worktree/verify.md +70 -0
- package/dist/agent-src/commands/worktree.md +85 -0
- package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +30 -1
- package/dist/agent-src/contexts/execution/orchestration-telemetry.md +60 -31
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +37 -0
- package/dist/agent-src/contexts/execution/subagent-topologies.md +34 -0
- package/dist/agent-src/contexts/execution/verification-mechanics.md +9 -9
- package/dist/agent-src/rules/agent-authority.md +1 -1
- package/dist/agent-src/rules/communication-through-line.md +1 -1
- package/dist/agent-src/rules/delegation-policy.md +5 -0
- package/dist/agent-src/rules/design-fidelity.md +40 -0
- package/dist/agent-src/rules/direct-answers.md +1 -1
- package/dist/agent-src/rules/downstream-changes.md +1 -0
- package/dist/agent-src/rules/external-code-graph-interop.md +65 -0
- package/dist/agent-src/rules/git-history-discipline.md +18 -0
- package/dist/agent-src/rules/lethal-trifecta-guard.md +6 -1
- package/dist/agent-src/rules/minimal-safe-diff.md +58 -10
- package/dist/agent-src/rules/non-destructive-by-default.md +3 -0
- package/dist/agent-src/rules/question-not-instruction.md +76 -0
- package/dist/agent-src/rules/senior-engineering-discipline.md +1 -1
- package/dist/agent-src/rules/spreadsheet-source-quality.md +61 -0
- package/dist/agent-src/rules/think-before-action.md +1 -0
- package/dist/agent-src/rules/tool-safety.md +8 -0
- package/dist/agent-src/rules/untrusted-input-defense.md +87 -22
- package/dist/agent-src/scripts/update_roadmap_progress.ts +2 -1
- package/dist/agent-src/skills/accessibility-auditor/SKILL.md +19 -0
- package/dist/agent-src/skills/accessibility-auditor/evals/evals.json +15 -0
- package/dist/agent-src/skills/ai-code-blindspots/SKILL.md +10 -3
- package/dist/agent-src/skills/analysis-skill-router/evals/evals.json +13 -0
- package/dist/agent-src/skills/api-testing/SKILL.md +15 -1
- package/dist/agent-src/skills/character-consistency/evals/evals.json +16 -0
- package/dist/agent-src/skills/code-review/SKILL.md +2 -0
- package/dist/agent-src/skills/command-routing/evals/evals.json +14 -0
- package/dist/agent-src/skills/command-writing/SKILL.md +53 -40
- package/dist/agent-src/skills/dashboard-design/evals/evals.json +16 -0
- package/dist/agent-src/skills/dcf-modeling/evals/domain-truth.json +29 -0
- package/dist/agent-src/skills/dcf-modeling/evals/evals.json +20 -0
- package/dist/agent-src/skills/decision-record/evals/evals.json +20 -0
- package/dist/agent-src/skills/deep-reading-analyst/SKILL.md +1 -0
- package/dist/agent-src/skills/design-intelligence/SKILL.md +49 -12
- package/dist/agent-src/skills/design-intelligence/evals/evals.json +14 -0
- package/dist/agent-src/skills/design-review/SKILL.md +35 -15
- package/dist/agent-src/skills/design-system-capture/SKILL.md +84 -24
- package/dist/agent-src/skills/design-system-capture/evals/evals.json +15 -0
- package/dist/agent-src/skills/developer-like-execution/evals/evals.json +16 -0
- package/dist/agent-src/skills/doc-coauthoring/SKILL.md +27 -0
- package/dist/agent-src/skills/doc-coauthoring/evals/evals.json +16 -0
- package/dist/agent-src/skills/editorial-calendar/evals/domain-truth.json +16 -0
- package/dist/agent-src/skills/editorial-calendar/evals/evals.json +16 -0
- package/dist/agent-src/skills/estimate-ticket/evals/evals.json +16 -0
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +30 -0
- package/dist/agent-src/skills/existing-ui-audit/evals/triggers.json +47 -0
- package/dist/agent-src/skills/fe-design/SKILL.md +36 -5
- package/dist/agent-src/skills/fe-design/evals/triggers.json +47 -0
- package/dist/agent-src/skills/forecasting/evals/domain-truth.json +29 -0
- package/dist/agent-src/skills/forecasting/evals/evals.json +20 -0
- package/dist/agent-src/skills/frontend-render-security/SKILL.md +2 -2
- package/dist/agent-src/skills/fundraising-narrative/evals/domain-truth.json +16 -0
- package/dist/agent-src/skills/fundraising-narrative/evals/evals.json +16 -0
- package/dist/agent-src/skills/html-deck/SKILL.md +27 -0
- package/dist/agent-src/skills/iconography/SKILL.md +24 -0
- package/dist/agent-src/skills/incident-commander/evals/domain-truth.json +16 -0
- package/dist/agent-src/skills/incident-commander/evals/evals.json +20 -0
- package/dist/agent-src/skills/launch-readiness/evals/evals.json +21 -0
- package/dist/agent-src/skills/logging-monitoring/evals/evals.json +16 -0
- package/dist/agent-src/skills/markitdown/SKILL.md +1 -0
- package/dist/agent-src/skills/mcp/SKILL.md +24 -0
- package/dist/agent-src/skills/mcp-builder/SKILL.md +9 -1
- package/dist/agent-src/skills/memory-consolidation/SKILL.md +55 -4
- package/dist/agent-src/skills/messaging-architecture/evals/domain-truth.json +16 -0
- package/dist/agent-src/skills/messaging-architecture/evals/evals.json +16 -0
- package/dist/agent-src/skills/perf-feedback-craft/evals/evals.json +16 -0
- package/dist/agent-src/skills/pest-testing/SKILL.md +1 -1
- package/dist/agent-src/skills/playwright-testing/SKILL.md +16 -0
- package/dist/agent-src/skills/prompt-engineering-patterns/evals/evals.json +16 -0
- package/dist/agent-src/skills/refine-prompt/evals/evals.json +16 -0
- package/dist/agent-src/skills/refine-ticket/evals/evals.json +16 -0
- package/dist/agent-src/skills/release-comms/evals/evals.json +16 -0
- package/dist/agent-src/skills/rice-prioritization/evals/evals.json +20 -0
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +39 -5
- package/dist/agent-src/skills/runway-cognition/evals/domain-truth.json +29 -0
- package/dist/agent-src/skills/runway-cognition/evals/evals.json +20 -0
- package/dist/agent-src/skills/scenario-modeling/evals/domain-truth.json +29 -0
- package/dist/agent-src/skills/scenario-modeling/evals/evals.json +20 -0
- package/dist/agent-src/skills/secrets-management/SKILL.md +7 -0
- package/dist/agent-src/skills/skill-writing/SKILL.md +12 -3
- package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +98 -0
- package/dist/agent-src/skills/spreadsheet-authoring/evals/triggers.json +47 -0
- package/dist/agent-src/skills/subagent-orchestration/SKILL.md +27 -33
- package/dist/agent-src/skills/supply-chain-intake/SKILL.md +12 -0
- package/dist/agent-src/skills/systematic-debugging/evals/evals.json +20 -0
- package/dist/agent-src/skills/test-case-discovery/SKILL.md +118 -0
- package/dist/agent-src/skills/test-case-discovery/evals/triggers.json +52 -0
- package/dist/agent-src/skills/test-driven-development/SKILL.md +32 -6
- package/dist/agent-src/skills/test-driven-development/evals/evals.json +16 -0
- package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +34 -4
- package/dist/agent-src/skills/threat-modeling/evals/evals.json +21 -0
- package/dist/agent-src/skills/typography-system/evals/evals.json +14 -0
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +9 -0
- package/dist/agent-src/skills/unit-economics-modeling/evals/domain-truth.json +40 -0
- package/dist/agent-src/skills/unit-economics-modeling/evals/evals.json +24 -0
- package/dist/agent-src/skills/verify-completion-evidence/evals/evals.json +16 -0
- package/dist/agent-src/skills/vision-articulation/evals/evals.json +16 -0
- package/dist/agent-src/skills/voice-and-tone-design/evals/evals.json +16 -0
- package/dist/agent-src/skills/worktree-lifecycle/SKILL.md +180 -0
- package/dist/agent-src/skills/worktree-lifecycle/evals/triggers.json +52 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +36 -0
- package/dist/cli/registry.js +3 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +326 -67
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +6 -6
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +25 -14
- package/dist/discovery/trust-report.md +4 -4
- package/dist/discovery/workspaces.json +25 -12
- package/dist/install/install.mjs +136 -1
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/claude_builtin_names.js +145 -0
- package/dist/scripts/_lib/claude_builtin_names.js.map +1 -0
- package/dist/scripts/condense.js +14 -0
- package/dist/scripts/condense.js.map +1 -1
- package/docs/CLAIMS.md +36 -8
- package/docs/architecture.md +4 -4
- package/docs/archive/CHANGELOG-pre-8.9.0.md +306 -0
- package/docs/benchmark.md +113 -0
- package/docs/catalog.md +32 -8
- package/docs/command-flows.md +6 -1
- package/docs/contracts/CHANGELOG-conventions.md +6 -0
- package/docs/contracts/command-clusters.md +18 -0
- package/docs/contracts/design-artifact-lifecycle.md +93 -0
- package/docs/contracts/design-artifact-verification.md +153 -0
- package/docs/contracts/injected-block-authenticity-model.md +66 -0
- package/docs/contracts/mcp-tool-inventory.md +28 -28
- package/docs/contracts/release-pr-gating.md +26 -0
- package/docs/contracts/release-sizing.md +85 -0
- package/docs/contracts/surface-agent-contracts.md +153 -0
- package/docs/decisions/ADR-061-corpus-grounding-layer.md +13 -0
- package/docs/decisions/ADR-115-command-cluster-consolidation-phase-4.md +1 -1
- package/docs/decisions/ADR-116-memory-tripwire-activation-path.md +9 -0
- package/docs/distribution/consumer-matrix.md +96 -0
- package/docs/featured-skills.md +2 -2
- package/docs/getting-started-by-role.md +1 -1
- package/docs/getting-started.md +2 -2
- package/docs/governance-advantage.md +1 -1
- package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +27 -0
- package/docs/guidelines/agent-infra/emphasis-budget.md +45 -0
- package/docs/guidelines/agent-infra/simplicity-and-goal-demos.md +151 -0
- package/docs/guidelines/agent-infra/tool-description-as-policy.md +35 -0
- package/docs/guidelines/design-canon.md +1 -0
- package/docs/proof.md +38 -23
- package/docs/release-runbook.md +3 -0
- package/docs/second-brain-scope.md +69 -26
- package/docs/self-review-gate.md +51 -0
- package/package.json +1 -1
- package/src/agent-src/scripts/update_roadmap_progress.ts +2 -1
- package/src/scripts/_dispatch.bash +27 -0
- package/src/scripts/_lib/claude_builtin_names.ts +148 -0
- package/src/scripts/_lib/file_slicer.ts +117 -0
- package/src/scripts/_lib/lexical_index.ts +155 -0
- package/src/scripts/_lib/orchestration_record.ts +183 -0
- package/src/scripts/_lib/orchestration_savings.ts +114 -9
- package/src/scripts/_lib/retrieval_sanitize.ts +71 -0
- package/src/scripts/_lib/security_lint.ts +1 -0
- package/src/scripts/_lib/stat_index.ts +98 -0
- package/src/scripts/benchmark.ts +122 -0
- package/src/scripts/build_proof.ts +30 -17
- package/src/scripts/check_memory.ts +2 -0
- package/src/scripts/check_quality_regression.ts +51 -0
- package/src/scripts/check_release_adjacent_health.ts +121 -0
- package/src/scripts/check_roadmap_trackable.ts +3 -2
- package/src/scripts/check_rule_invariants.ts +275 -0
- package/src/scripts/condense.ts +14 -0
- package/src/scripts/consumer_matrix.ts +353 -0
- package/src/scripts/discovery_graph.ts +0 -0
- package/src/scripts/external_sources_denylist.json +4 -1
- package/src/scripts/generate_index.ts +6 -2
- package/src/scripts/install-hooks.sh +25 -0
- package/src/scripts/install.ts +35 -2
- package/src/scripts/learning_sidecar.ts +0 -0
- package/src/scripts/lint_agent_skill_names.ts +29 -0
- package/src/scripts/lint_changelog_rollback.ts +293 -0
- package/src/scripts/lint_knowledge_scale.ts +4 -3
- package/src/scripts/lint_roadmap_complexity.ts +124 -0
- package/src/scripts/lint_versioned_cache.ts +195 -0
- package/src/scripts/mcp_server/consumer_tool_catalog.json +2 -1
- package/src/scripts/mcp_server/tools.ts +24 -1
- package/src/scripts/measure_lexical_ranking.ts +173 -0
- package/src/scripts/memory_lookup.ts +0 -0
- package/src/scripts/orchestration_record.ts +156 -0
- package/src/scripts/orchestration_savings_report.ts +65 -8
- package/src/scripts/schemas/skill.schema.json +4 -0
- package/src/scripts/score_domain_truth.ts +192 -0
- package/src/scripts/second_brain_retrieval.ts +320 -0
- package/src/scripts/second_brain_run.ts +341 -0
- package/src/scripts/self_review_gate.test.ts +65 -0
- package/src/scripts/self_review_gate.ts +272 -0
- package/src/scripts/trigger_eval_grandfather.json +223 -224
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Consumer-proof release matrix — road-to-proof-under-real-conditions Phase 1.
|
|
4
|
+
*
|
|
5
|
+
* Exercises the PUBLISHED TARBALL the way a consumer does: pack → fresh
|
|
6
|
+
* global install (isolated prefix) → `init` into a fresh project → doctor →
|
|
7
|
+
* conformance → MCP stdio handshake → hook health → projection presence →
|
|
8
|
+
* uninstall. Optional upgrade leg: install the last published minor from the
|
|
9
|
+
* registry, then upgrade to the packed tarball and assert doctor stays green.
|
|
10
|
+
*
|
|
11
|
+
* Why this exists: the release-PR gate skips source-level matrices (soundly —
|
|
12
|
+
* a version-bump diff cannot regress source behaviour), but nothing between
|
|
13
|
+
* merge and tag exercised the tarball shape. Two published minors shipped
|
|
14
|
+
* without `src/install/` while published code imported from it; `tsx` was
|
|
15
|
+
* missing from the published package; each manifested only at publish time.
|
|
16
|
+
* Every leg here asserts exit code + an expected artifact, no LLM calls.
|
|
17
|
+
*
|
|
18
|
+
* Usage:
|
|
19
|
+
* npx tsx src/scripts/consumer_matrix.ts [--tarball <path>] [--only leg1,leg2]
|
|
20
|
+
* [--skip-registry] [--keep-tmp] [--json]
|
|
21
|
+
*
|
|
22
|
+
* Without --tarball the script runs `npm pack` itself (requires a built
|
|
23
|
+
* tree — `npm ci && npm run build` first; CI does this, local carve-out
|
|
24
|
+
* runs do it once).
|
|
25
|
+
*
|
|
26
|
+
* Exit codes: 0 all requested legs green · 1 leg failure · 2 usage/env.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { spawnSync, spawn } from 'node:child_process';
|
|
30
|
+
import * as fs from 'node:fs';
|
|
31
|
+
import * as os from 'node:os';
|
|
32
|
+
import * as path from 'node:path';
|
|
33
|
+
import { fileURLToPath } from 'node:url';
|
|
34
|
+
|
|
35
|
+
const _HERE = fileURLToPath(import.meta.url);
|
|
36
|
+
const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
37
|
+
const PKG_NAME = '@event4u/agent-config';
|
|
38
|
+
|
|
39
|
+
interface Ctx {
|
|
40
|
+
tmpRoot: string;
|
|
41
|
+
prefix: string; // isolated npm global prefix
|
|
42
|
+
projectDir: string; // fresh consumer project
|
|
43
|
+
tarball: string;
|
|
44
|
+
bin: string; // installed agent-config binary
|
|
45
|
+
keepTmp: boolean;
|
|
46
|
+
skipRegistry: boolean;
|
|
47
|
+
results: LegResult[];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface LegResult {
|
|
51
|
+
leg: string;
|
|
52
|
+
ok: boolean;
|
|
53
|
+
detail: string;
|
|
54
|
+
durationMs: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function log(msg: string): void {
|
|
58
|
+
process.stderr.write(`${msg}\n`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function run(
|
|
62
|
+
cmd: string,
|
|
63
|
+
args: string[],
|
|
64
|
+
opts: { cwd?: string; env?: NodeJS.ProcessEnv; timeoutMs?: number } = {},
|
|
65
|
+
): { status: number; stdout: string; stderr: string } {
|
|
66
|
+
const r = spawnSync(cmd, args, {
|
|
67
|
+
cwd: opts.cwd ?? REPO_ROOT,
|
|
68
|
+
env: { ...process.env, ...opts.env },
|
|
69
|
+
encoding: 'utf8',
|
|
70
|
+
timeout: opts.timeoutMs ?? 600_000,
|
|
71
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
72
|
+
});
|
|
73
|
+
return { status: r.status ?? 1, stdout: r.stdout ?? '', stderr: r.stderr ?? '' };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function fail(msg: string): never {
|
|
77
|
+
log(`❌ consumer-matrix: ${msg}`);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Env for running the installed binary against the isolated prefix.
|
|
83
|
+
* HOME is redirected into the tmp root so the simulation is a FRESH
|
|
84
|
+
* MACHINE even on a maintainer host: without this, an existing global
|
|
85
|
+
* install (~/.event4u, ~/.claude, …) trips the cross-scope drift guard
|
|
86
|
+
* inside `init` and the leg fails on developer machines while passing
|
|
87
|
+
* on clean CI runners — the opposite of what a consumer gate is for.
|
|
88
|
+
*/
|
|
89
|
+
function binEnv(ctx: Ctx): NodeJS.ProcessEnv {
|
|
90
|
+
const home = path.join(ctx.tmpRoot, 'home');
|
|
91
|
+
fs.mkdirSync(home, { recursive: true });
|
|
92
|
+
return {
|
|
93
|
+
PATH: `${path.join(ctx.prefix, 'bin')}${path.delimiter}${process.env.PATH ?? ''}`,
|
|
94
|
+
npm_config_prefix: ctx.prefix,
|
|
95
|
+
npm_config_cache: path.join(ctx.tmpRoot, 'npm-cache'),
|
|
96
|
+
HOME: home,
|
|
97
|
+
// Never let a maintainer checkout leak into the consumer simulation.
|
|
98
|
+
AGENT_CONFIG_DEV_MODE: '',
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ── legs ────────────────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
function legPack(ctx: Ctx): string {
|
|
105
|
+
if (ctx.tarball) {
|
|
106
|
+
if (!fs.existsSync(ctx.tarball)) fail(`--tarball not found: ${ctx.tarball}`);
|
|
107
|
+
return `reused ${path.basename(ctx.tarball)}`;
|
|
108
|
+
}
|
|
109
|
+
const r = run('npm', ['pack', '--ignore-scripts', '--silent', '--pack-destination', ctx.tmpRoot]);
|
|
110
|
+
if (r.status !== 0) throw new Error(`npm pack failed: ${r.stderr.slice(-400)}`);
|
|
111
|
+
const name = r.stdout.trim().split('\n').pop() ?? '';
|
|
112
|
+
ctx.tarball = path.join(ctx.tmpRoot, name);
|
|
113
|
+
if (!fs.existsSync(ctx.tarball)) throw new Error(`packed tarball missing: ${ctx.tarball}`);
|
|
114
|
+
return path.basename(ctx.tarball);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function legFreshInstall(ctx: Ctx): string {
|
|
118
|
+
const r = run('npm', ['install', '-g', '--prefix', ctx.prefix, ctx.tarball], { timeoutMs: 900_000 });
|
|
119
|
+
if (r.status !== 0) throw new Error(`global install failed: ${r.stderr.slice(-600)}`);
|
|
120
|
+
ctx.bin = path.join(ctx.prefix, 'bin', 'agent-config');
|
|
121
|
+
if (!fs.existsSync(ctx.bin)) throw new Error(`installed binary missing at ${ctx.bin}`);
|
|
122
|
+
const v = run(ctx.bin, ['--version'], { env: binEnv(ctx) });
|
|
123
|
+
if (v.status !== 0) throw new Error(`--version failed (missing runtime dep in tarball?): ${v.stderr.slice(-400)}`);
|
|
124
|
+
return `bin ok, version ${v.stdout.trim()}`;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function legInit(ctx: Ctx): string {
|
|
128
|
+
fs.mkdirSync(ctx.projectDir, { recursive: true });
|
|
129
|
+
run('git', ['init', '-q', '.'], { cwd: ctx.projectDir });
|
|
130
|
+
// --scope=auto is the working headless consumer invocation. The bare
|
|
131
|
+
// default ('project default for backward compatibility') hard-fails on
|
|
132
|
+
// a fresh machine with '--scope=project is reserved for maintainers'
|
|
133
|
+
// (ADR-020) — a real trap this matrix documents; see
|
|
134
|
+
// docs/distribution/consumer-matrix.md § Headless default-scope trap.
|
|
135
|
+
const r = run(ctx.bin, ['init', '--tools=claude-code,cursor,windsurf', '--scope=auto', '--yes'], {
|
|
136
|
+
cwd: ctx.projectDir,
|
|
137
|
+
env: binEnv(ctx),
|
|
138
|
+
timeoutMs: 600_000,
|
|
139
|
+
});
|
|
140
|
+
if (r.status !== 0) throw new Error(`init failed: ${(r.stderr + r.stdout).slice(-600)}`);
|
|
141
|
+
// ADR-020: consumer installs are GLOBAL-only — assert the global trees.
|
|
142
|
+
const home = path.join(ctx.tmpRoot, 'home');
|
|
143
|
+
for (const p of ['.claude', '.cursor', '.codeium/windsurf', '.event4u']) {
|
|
144
|
+
if (!fs.existsSync(path.join(home, p))) throw new Error(`init did not write ~/${p}`);
|
|
145
|
+
}
|
|
146
|
+
return 'init --scope=auto wrote global ~/.claude, ~/.cursor, ~/.codeium/windsurf, ~/.event4u';
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function legConformance(ctx: Ctx): string {
|
|
150
|
+
const r = run(ctx.bin, ['conformance'], { cwd: ctx.projectDir, env: binEnv(ctx) });
|
|
151
|
+
if (r.status !== 0) throw new Error(`conformance exit ${r.status}: ${(r.stdout + r.stderr).slice(-600)}`);
|
|
152
|
+
return 'conformance green (embeds doctor --ci + installed-and-firing checks)';
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function legMcpHandshake(ctx: Ctx): Promise<string> {
|
|
156
|
+
// Newline-delimited JSON-RPC per src/cli/mcp/stdio.ts.
|
|
157
|
+
return await new Promise<string>((resolve, reject) => {
|
|
158
|
+
const child = spawn(ctx.bin, ['mcp-server'], {
|
|
159
|
+
cwd: ctx.projectDir,
|
|
160
|
+
env: { ...process.env, ...binEnv(ctx) },
|
|
161
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
162
|
+
});
|
|
163
|
+
let out = '';
|
|
164
|
+
const timer = setTimeout(() => {
|
|
165
|
+
child.kill();
|
|
166
|
+
reject(new Error(`no initialize response within 20s; stdout so far: ${out.slice(0, 300)}`));
|
|
167
|
+
}, 20_000);
|
|
168
|
+
child.stdout.on('data', (d: Buffer) => {
|
|
169
|
+
out += d.toString();
|
|
170
|
+
const line = out.split('\n').find((l) => l.trim().startsWith('{'));
|
|
171
|
+
if (line) {
|
|
172
|
+
try {
|
|
173
|
+
const msg = JSON.parse(line) as { id?: number; result?: unknown; error?: unknown };
|
|
174
|
+
clearTimeout(timer);
|
|
175
|
+
child.kill();
|
|
176
|
+
if (msg.id === 1 && msg.result && !msg.error) {
|
|
177
|
+
resolve('initialize handshake ok');
|
|
178
|
+
} else {
|
|
179
|
+
reject(new Error(`unexpected first message: ${line.slice(0, 300)}`));
|
|
180
|
+
}
|
|
181
|
+
} catch {
|
|
182
|
+
/* partial line — keep buffering */
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
child.on('error', (e) => {
|
|
187
|
+
clearTimeout(timer);
|
|
188
|
+
reject(e);
|
|
189
|
+
});
|
|
190
|
+
child.on('exit', (code) => {
|
|
191
|
+
if (out === '') {
|
|
192
|
+
clearTimeout(timer);
|
|
193
|
+
reject(new Error(`mcp-server exited (${code}) before responding`));
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
child.stdin.write(
|
|
197
|
+
`${JSON.stringify({
|
|
198
|
+
jsonrpc: '2.0',
|
|
199
|
+
id: 1,
|
|
200
|
+
method: 'initialize',
|
|
201
|
+
params: {
|
|
202
|
+
protocolVersion: '2025-03-26',
|
|
203
|
+
capabilities: {},
|
|
204
|
+
clientInfo: { name: 'consumer-matrix', version: '0' },
|
|
205
|
+
},
|
|
206
|
+
})}\n`,
|
|
207
|
+
);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function legHooks(ctx: Ctx): string {
|
|
212
|
+
const r = run(ctx.bin, ['hooks:doctor'], { cwd: ctx.projectDir, env: binEnv(ctx) });
|
|
213
|
+
if (r.status !== 0) throw new Error(`hooks:doctor exit ${r.status}: ${(r.stdout + r.stderr).slice(-600)}`);
|
|
214
|
+
return 'hooks:doctor green';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function legProjections(ctx: Ctx): string {
|
|
218
|
+
const home = path.join(ctx.tmpRoot, 'home');
|
|
219
|
+
const missing = ['.cursor/rules', '.codeium/windsurf', '.claude/skills']
|
|
220
|
+
.filter((p) => !fs.existsSync(path.join(home, p)))
|
|
221
|
+
.join(', ');
|
|
222
|
+
if (missing) throw new Error(`global projection trees missing: ${missing}`);
|
|
223
|
+
return 'cursor + windsurf + claude projection trees present in global scope';
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function legUninstall(ctx: Ctx): string {
|
|
227
|
+
const r = run(ctx.bin, ['uninstall', '--global', '--tools=claude-code,cursor,windsurf', '--force'], {
|
|
228
|
+
cwd: ctx.projectDir,
|
|
229
|
+
env: binEnv(ctx),
|
|
230
|
+
});
|
|
231
|
+
if (r.status !== 0) throw new Error(`uninstall exit ${r.status}: ${(r.stdout + r.stderr).slice(-600)}`);
|
|
232
|
+
const lock = path.join(ctx.tmpRoot, 'home', '.event4u', 'agent-config', 'installed.lock');
|
|
233
|
+
if (fs.existsSync(lock)) throw new Error(`global lockfile still present after uninstall: ${lock}`);
|
|
234
|
+
return 'uninstall --global green, lockfile removed';
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function legUpgrade(ctx: Ctx): string {
|
|
238
|
+
if (ctx.skipRegistry) return 'skipped (--skip-registry)';
|
|
239
|
+
// Last published version from the registry (network).
|
|
240
|
+
const view = run('npm', ['view', PKG_NAME, 'version'], { timeoutMs: 120_000 });
|
|
241
|
+
if (view.status !== 0) throw new Error(`npm view failed: ${view.stderr.slice(-300)}`);
|
|
242
|
+
const published = view.stdout.trim();
|
|
243
|
+
const upPrefix = path.join(ctx.tmpRoot, 'prefix-upgrade');
|
|
244
|
+
const upProject = path.join(ctx.tmpRoot, 'project-upgrade');
|
|
245
|
+
fs.mkdirSync(upProject, { recursive: true });
|
|
246
|
+
const upHome = path.join(ctx.tmpRoot, 'home-upgrade');
|
|
247
|
+
fs.mkdirSync(upHome, { recursive: true });
|
|
248
|
+
const env = {
|
|
249
|
+
PATH: `${path.join(upPrefix, 'bin')}${path.delimiter}${process.env.PATH ?? ''}`,
|
|
250
|
+
npm_config_prefix: upPrefix,
|
|
251
|
+
npm_config_cache: path.join(ctx.tmpRoot, 'npm-cache'),
|
|
252
|
+
HOME: upHome,
|
|
253
|
+
AGENT_CONFIG_DEV_MODE: '',
|
|
254
|
+
};
|
|
255
|
+
let r = run('npm', ['install', '-g', '--prefix', upPrefix, `${PKG_NAME}@${published}`], { timeoutMs: 900_000 });
|
|
256
|
+
if (r.status !== 0) throw new Error(`registry install of ${published} failed: ${r.stderr.slice(-400)}`);
|
|
257
|
+
const bin = path.join(upPrefix, 'bin', 'agent-config');
|
|
258
|
+
r = run(bin, ['init', '--tools=claude-code', '--scope=auto', '--yes'], { cwd: upProject, env, timeoutMs: 600_000 });
|
|
259
|
+
if (r.status !== 0) throw new Error(`init on ${published} failed: ${(r.stderr + r.stdout).slice(-400)}`);
|
|
260
|
+
// Upgrade: overwrite the global install with the packed tarball.
|
|
261
|
+
r = run('npm', ['install', '-g', '--prefix', upPrefix, ctx.tarball], { timeoutMs: 900_000 });
|
|
262
|
+
if (r.status !== 0) throw new Error(`tarball upgrade failed: ${r.stderr.slice(-400)}`);
|
|
263
|
+
r = run(bin, ['conformance'], { cwd: upProject, env });
|
|
264
|
+
if (r.status !== 0) {
|
|
265
|
+
throw new Error(`conformance red after ${published} → tarball upgrade: ${(r.stdout + r.stderr).slice(-600)}`);
|
|
266
|
+
}
|
|
267
|
+
return `upgrade ${published} → packed tarball, conformance green`;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ── main ────────────────────────────────────────────────────────────
|
|
271
|
+
|
|
272
|
+
const LEGS: Array<{ name: string; fn: (ctx: Ctx) => string | Promise<string> }> = [
|
|
273
|
+
{ name: 'pack', fn: legPack },
|
|
274
|
+
{ name: 'fresh-install', fn: legFreshInstall },
|
|
275
|
+
{ name: 'init', fn: legInit },
|
|
276
|
+
{ name: 'conformance', fn: legConformance },
|
|
277
|
+
{ name: 'mcp-handshake', fn: legMcpHandshake },
|
|
278
|
+
{ name: 'hooks', fn: legHooks },
|
|
279
|
+
{ name: 'projections', fn: legProjections },
|
|
280
|
+
{ name: 'uninstall', fn: legUninstall },
|
|
281
|
+
{ name: 'upgrade', fn: legUpgrade },
|
|
282
|
+
];
|
|
283
|
+
|
|
284
|
+
async function main(): Promise<number> {
|
|
285
|
+
const argv = process.argv.slice(2);
|
|
286
|
+
const getOpt = (flag: string): string | undefined => {
|
|
287
|
+
const i = argv.indexOf(flag);
|
|
288
|
+
return i >= 0 ? argv[i + 1] : undefined;
|
|
289
|
+
};
|
|
290
|
+
const only = getOpt('--only')?.split(',').map((s) => s.trim());
|
|
291
|
+
const unknownLeg = only?.find((l) => !LEGS.some((x) => x.name === l));
|
|
292
|
+
if (unknownLeg) {
|
|
293
|
+
log(`❌ unknown leg '${unknownLeg}'. Legs: ${LEGS.map((l) => l.name).join(', ')}`);
|
|
294
|
+
return 2;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'consumer-matrix-'));
|
|
298
|
+
const ctx: Ctx = {
|
|
299
|
+
tmpRoot,
|
|
300
|
+
prefix: path.join(tmpRoot, 'prefix'),
|
|
301
|
+
projectDir: path.join(tmpRoot, 'project'),
|
|
302
|
+
tarball: getOpt('--tarball') ?? '',
|
|
303
|
+
bin: '',
|
|
304
|
+
keepTmp: argv.includes('--keep-tmp'),
|
|
305
|
+
skipRegistry: argv.includes('--skip-registry'),
|
|
306
|
+
results: [],
|
|
307
|
+
};
|
|
308
|
+
fs.mkdirSync(ctx.prefix, { recursive: true });
|
|
309
|
+
log(`consumer-matrix · tmp ${tmpRoot} · node ${process.version}`);
|
|
310
|
+
|
|
311
|
+
let failed = false;
|
|
312
|
+
for (const leg of LEGS) {
|
|
313
|
+
if (only && !only.includes(leg.name)) continue;
|
|
314
|
+
// Legs after a hard dependency failure cannot run meaningfully.
|
|
315
|
+
if (failed) {
|
|
316
|
+
ctx.results.push({ leg: leg.name, ok: false, detail: 'skipped (earlier leg failed)', durationMs: 0 });
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
const t0 = Date.now();
|
|
320
|
+
try {
|
|
321
|
+
const detail = await leg.fn(ctx);
|
|
322
|
+
ctx.results.push({ leg: leg.name, ok: true, detail, durationMs: Date.now() - t0 });
|
|
323
|
+
log(` ✅ ${leg.name} — ${detail}`);
|
|
324
|
+
} catch (err) {
|
|
325
|
+
ctx.results.push({
|
|
326
|
+
leg: leg.name,
|
|
327
|
+
ok: false,
|
|
328
|
+
detail: (err as Error).message,
|
|
329
|
+
durationMs: Date.now() - t0,
|
|
330
|
+
});
|
|
331
|
+
log(` ❌ ${leg.name} — ${(err as Error).message}`);
|
|
332
|
+
failed = true;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (argv.includes('--json')) {
|
|
337
|
+
process.stdout.write(`${JSON.stringify({ ok: !failed, results: ctx.results }, null, 2)}\n`);
|
|
338
|
+
}
|
|
339
|
+
if (!ctx.keepTmp) {
|
|
340
|
+
fs.rmSync(tmpRoot, { recursive: true, force: true });
|
|
341
|
+
} else {
|
|
342
|
+
log(`kept tmp: ${tmpRoot}`);
|
|
343
|
+
}
|
|
344
|
+
return failed ? 1 : 0;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
main().then(
|
|
348
|
+
(code) => process.exit(code),
|
|
349
|
+
(err) => {
|
|
350
|
+
log(`❌ consumer-matrix crashed: ${(err as Error).stack ?? err}`);
|
|
351
|
+
process.exit(2);
|
|
352
|
+
},
|
|
353
|
+
);
|
|
Binary file
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
iter_commands,
|
|
25
25
|
strip_source_prefix,
|
|
26
26
|
} from './_lib/agent_src.js';
|
|
27
|
+
import { computeStatus as domainSoundnessStatus } from './domain_soundness_status.js';
|
|
27
28
|
|
|
28
29
|
const _HERE = fileURLToPath(import.meta.url);
|
|
29
30
|
|
|
@@ -398,6 +399,7 @@ export function _render_catalog(
|
|
|
398
399
|
const publicRules = rules.filter((r) => !INTERNAL_RULES.has(r.name));
|
|
399
400
|
const publicCommands = commands.filter((c) => c.kind === 'command');
|
|
400
401
|
const total = skills.length + publicRules.length + publicCommands.length + guidelines.length;
|
|
402
|
+
const ds = domainSoundnessStatus();
|
|
401
403
|
const parts = [
|
|
402
404
|
'# agent-config — Public Catalog',
|
|
403
405
|
'',
|
|
@@ -419,8 +421,10 @@ export function _render_catalog(
|
|
|
419
421
|
'',
|
|
420
422
|
'> **Non-coding domain correctness is scoped, not proven.** The',
|
|
421
423
|
'> `finance` / `founder` / `ops` / `content` skills are forged on TS/PHP',
|
|
422
|
-
|
|
423
|
-
'>
|
|
424
|
+
`> codebases; ${ds.validated} of ${ds.total} default-surface domain skills carry a sourced`,
|
|
425
|
+
'> `evals/domain-truth.json` fixture (deterministic targets with keys from',
|
|
426
|
+
'> cited formulas, or rubric targets matching a named external practice);',
|
|
427
|
+
'> the rest are labeled `unvalidated` until they pass one',
|
|
424
428
|
'> (`./scripts-run src/scripts/domain_soundness_status`). A disclaimer',
|
|
425
429
|
'> floor bounds liability, not correctness — see [the proof page](proof.md).',
|
|
426
430
|
'',
|
|
@@ -42,6 +42,31 @@ if ! ./scripts-run src/scripts/check_no_conflict_markers --quiet; then
|
|
|
42
42
|
fail=1
|
|
43
43
|
fi
|
|
44
44
|
|
|
45
|
+
# Changed-files static pass (typecheck + lint) — the deterministic backstop to
|
|
46
|
+
# the behavioural verify-before-complete rule (#818). Catches the compile/lint
|
|
47
|
+
# errors that reach remote CI "Static Checks" (e.g. TS18048). Runs only when
|
|
48
|
+
# the push touches .ts, so docs-only pushes stay fast. Skip a genuine WIP push
|
|
49
|
+
# with AGENT_CONFIG_SKIP_PREPUSH_STATIC=1 (the agent cannot use --no-verify).
|
|
50
|
+
echo "🔍 Static check on changed TypeScript (typecheck + lint)..."
|
|
51
|
+
if [ "${AGENT_CONFIG_SKIP_PREPUSH_STATIC:-}" = "1" ]; then
|
|
52
|
+
echo "⏭️ skipped via AGENT_CONFIG_SKIP_PREPUSH_STATIC=1"
|
|
53
|
+
else
|
|
54
|
+
base="$(git merge-base HEAD origin/main 2>/dev/null || echo HEAD~1)"
|
|
55
|
+
changed_ts="$(git diff --name-only --diff-filter=d "$base"...HEAD -- '*.ts' 2>/dev/null || true)"
|
|
56
|
+
if [ -n "$changed_ts" ]; then
|
|
57
|
+
if ! npx eslint $changed_ts; then
|
|
58
|
+
echo "❌ ESLint failed on changed TypeScript. Fix before pushing (or AGENT_CONFIG_SKIP_PREPUSH_STATIC=1 for a WIP push)."
|
|
59
|
+
fail=1
|
|
60
|
+
fi
|
|
61
|
+
if ! npm run --silent typecheck; then
|
|
62
|
+
echo "❌ Typecheck (tsc) failed — this is the class of error remote CI 'Static Checks' catches (#818). Fix before pushing."
|
|
63
|
+
fail=1
|
|
64
|
+
fi
|
|
65
|
+
else
|
|
66
|
+
echo "⏭️ no changed .ts vs origin/main — skipping."
|
|
67
|
+
fi
|
|
68
|
+
fi
|
|
69
|
+
|
|
45
70
|
if [ $fail -ne 0 ]; then
|
|
46
71
|
echo ""
|
|
47
72
|
echo " Push blocked — fix the failures above and re-push."
|
package/src/scripts/install.ts
CHANGED
|
@@ -72,6 +72,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
72
72
|
import type * as YamlModule from 'yaml';
|
|
73
73
|
|
|
74
74
|
import { build_merge_entries } from './_lib/json_pointers.js';
|
|
75
|
+
import { is_claude_builtin_name } from './_lib/claude_builtin_names.js';
|
|
75
76
|
import * as installed_lock from './_lib/installed_lock.js';
|
|
76
77
|
import * as surface_tiers from './_lib/surface_tiers.js';
|
|
77
78
|
import * as global_deploy_inventory from './_lib/global_deploy_inventory.js';
|
|
@@ -2847,15 +2848,40 @@ function _deploy_claude_desktop(
|
|
|
2847
2848
|
* discovery (revert = one package release, no per-machine gating).
|
|
2848
2849
|
* Tier-2/internal flat commands stay command files. A command whose name
|
|
2849
2850
|
* collides with a real skill is skipped (the skill already owns `/name`).
|
|
2851
|
+
*
|
|
2852
|
+
* Reserved-name floor: a flat `/name` that equals a Claude Code BUILT-IN
|
|
2853
|
+
* command or bundled skill (see `_lib/claude_builtin_names.ts`) would shadow
|
|
2854
|
+
* the built-in — e.g. a `review` wrapper hides Claude Code's own `/review`.
|
|
2855
|
+
* Such slugs get NEITHER a skill wrapper NOR a flat command file on this
|
|
2856
|
+
* anchor (their nested `/cluster:sub` commands are untouched); the flat file
|
|
2857
|
+
* is removed so a future upstream flat-discovery fix cannot resurrect the
|
|
2858
|
+
* shadowing. The suite complements the host — it never overlays built-ins.
|
|
2850
2859
|
*/
|
|
2851
2860
|
const _CLAUDE_FLAT_WRAPPER_EXTRA = new Set(['commit']); // essential; absent from the manifest
|
|
2852
2861
|
export function _apply_claude_flat_command_wrappers(
|
|
2853
2862
|
anchor: string,
|
|
2854
2863
|
package_root: string,
|
|
2855
2864
|
current_files: Set<string>,
|
|
2856
|
-
): { wrapped: string[]; collisions: string[] } {
|
|
2865
|
+
): { wrapped: string[]; collisions: string[]; reserved: string[] } {
|
|
2857
2866
|
const wrapped: string[] = [];
|
|
2858
2867
|
const collisions: string[] = [];
|
|
2868
|
+
const reserved: string[] = [];
|
|
2869
|
+
// Reserved-name sweep over ALL flat command files (any tier): a name
|
|
2870
|
+
// shadowing a Claude Code built-in never ships on the claude-code anchor.
|
|
2871
|
+
const commands_dir = path.join(anchor, 'commands');
|
|
2872
|
+
let flat_entries: string[] = [];
|
|
2873
|
+
try {
|
|
2874
|
+
flat_entries = fs.readdirSync(commands_dir).filter((f) => f.endsWith('.md'));
|
|
2875
|
+
} catch {
|
|
2876
|
+
// No commands dir → nothing to sweep.
|
|
2877
|
+
}
|
|
2878
|
+
for (const fname of flat_entries.sort()) {
|
|
2879
|
+
const slug = fname.slice(0, -'.md'.length);
|
|
2880
|
+
if (!is_claude_builtin_name(slug)) continue;
|
|
2881
|
+
fs.rmSync(path.join(commands_dir, fname), { force: true });
|
|
2882
|
+
current_files.delete(`commands/${fname}`);
|
|
2883
|
+
reserved.push(slug);
|
|
2884
|
+
}
|
|
2859
2885
|
// Tier-0/1 visible command slugs from the locked discovery manifest.
|
|
2860
2886
|
const eligible = new Set<string>(_CLAUDE_FLAT_WRAPPER_EXTRA);
|
|
2861
2887
|
try {
|
|
@@ -2892,7 +2918,7 @@ export function _apply_claude_flat_command_wrappers(
|
|
|
2892
2918
|
current_files.add(`skills/${slug}/SKILL.md`);
|
|
2893
2919
|
wrapped.push(slug);
|
|
2894
2920
|
}
|
|
2895
|
-
return { wrapped, collisions };
|
|
2921
|
+
return { wrapped, collisions, reserved };
|
|
2896
2922
|
}
|
|
2897
2923
|
|
|
2898
2924
|
function _deploy_global_content(
|
|
@@ -2949,6 +2975,13 @@ function _deploy_global_content(
|
|
|
2949
2975
|
'skill wrappers (Claude Code flat-command discovery workaround)',
|
|
2950
2976
|
);
|
|
2951
2977
|
}
|
|
2978
|
+
if (res.reserved.length > 0 && !state.QUIET) {
|
|
2979
|
+
info(
|
|
2980
|
+
` claude-code: ${res.reserved.length} flat command(s) withheld — name is a ` +
|
|
2981
|
+
`Claude Code built-in (${res.reserved.join(', ')}); nested /cluster:sub ` +
|
|
2982
|
+
'commands remain available',
|
|
2983
|
+
);
|
|
2984
|
+
}
|
|
2952
2985
|
}
|
|
2953
2986
|
|
|
2954
2987
|
const missing_targets = _verify_deploy_targets(anchor, plan);
|
|
Binary file
|
|
@@ -14,6 +14,7 @@ import * as path from 'node:path';
|
|
|
14
14
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
15
15
|
|
|
16
16
|
import { command_slug } from './_lib/agent_src.js';
|
|
17
|
+
import { is_claude_builtin_name } from './_lib/claude_builtin_names.js';
|
|
17
18
|
|
|
18
19
|
const SCRIPTS_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
19
20
|
const ROOT = path.dirname(path.dirname(SCRIPTS_DIR));
|
|
@@ -97,6 +98,18 @@ function frontmatterNameFromFile(p: string): string | null {
|
|
|
97
98
|
return _frontmatter_name(fs.readFileSync(p, 'utf-8'));
|
|
98
99
|
}
|
|
99
100
|
|
|
101
|
+
const USER_INVOCABLE_FALSE_RE = /^user-invocable:\s*false\s*$/m;
|
|
102
|
+
|
|
103
|
+
/** True when the SKILL.md opts out of slash registration (`user-invocable: false`). */
|
|
104
|
+
export function _opts_out_of_slash(text: string): boolean {
|
|
105
|
+
if (!text.startsWith('---')) {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
const end = text.indexOf('\n---', 3);
|
|
109
|
+
const block = end !== -1 ? text.slice(3, end) : text;
|
|
110
|
+
return USER_INVOCABLE_FALSE_RE.test(block);
|
|
111
|
+
}
|
|
112
|
+
|
|
100
113
|
export function _spec_violation(name: string): string | null {
|
|
101
114
|
if (name.length > MAX_NAME_LEN) {
|
|
102
115
|
return `longer than ${MAX_NAME_LEN} chars`;
|
|
@@ -178,6 +191,22 @@ export function check_skills(): string[] {
|
|
|
178
191
|
'requires the folder name to match the `name:` field',
|
|
179
192
|
);
|
|
180
193
|
}
|
|
194
|
+
// Reserved-name floor: a skill named after a Claude Code built-in
|
|
195
|
+
// command / bundled skill shadows the built-in when projected as
|
|
196
|
+
// `/name` (e.g. a `mcp` skill hides Claude Code's own /mcp dialog).
|
|
197
|
+
// Opting out of slash registration (`user-invocable: false`) keeps
|
|
198
|
+
// the skill model-loadable while the host retains the /name.
|
|
199
|
+
// Reserved set: src/scripts/_lib/claude_builtin_names.ts.
|
|
200
|
+
if (
|
|
201
|
+
is_claude_builtin_name(dirname) &&
|
|
202
|
+
!_opts_out_of_slash(fs.readFileSync(md, 'utf-8'))
|
|
203
|
+
) {
|
|
204
|
+
violations.push(
|
|
205
|
+
`${rel}: \`${dirname}\` is a Claude Code built-in command name — ` +
|
|
206
|
+
'add `user-invocable: false` (model-only invocation) or rename ' +
|
|
207
|
+
'the skill; the suite must never shadow a host built-in',
|
|
208
|
+
);
|
|
209
|
+
}
|
|
181
210
|
}
|
|
182
211
|
return violations;
|
|
183
212
|
}
|