@cyanheads/mcp-ts-core 0.9.14 → 0.9.15
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/AGENTS.md +559 -0
- package/CLAUDE.md +3 -2
- package/README.md +4 -4
- package/changelog/0.9.x/0.9.15.md +52 -0
- package/changelog/template.md +1 -1
- package/dist/core/context.d.ts +18 -2
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +4 -0
- package/dist/core/context.js.map +1 -1
- package/dist/linter/rules/enrichment-rules.d.ts +13 -4
- package/dist/linter/rules/enrichment-rules.d.ts.map +1 -1
- package/dist/linter/rules/enrichment-rules.js +92 -4
- package/dist/linter/rules/enrichment-rules.js.map +1 -1
- package/dist/linter/rules/schema-rules.d.ts +4 -0
- package/dist/linter/rules/schema-rules.d.ts.map +1 -1
- package/dist/linter/rules/schema-rules.js +2 -2
- package/dist/linter/rules/schema-rules.js.map +1 -1
- package/dist/linter/rules/tool-rules.d.ts.map +1 -1
- package/dist/linter/rules/tool-rules.js.map +1 -1
- package/dist/mcp-server/tools/utils/toolDefinition.d.ts +40 -0
- package/dist/mcp-server/tools/utils/toolDefinition.d.ts.map +1 -1
- package/dist/mcp-server/tools/utils/toolDefinition.js.map +1 -1
- package/dist/mcp-server/tools/utils/toolHandlerFactory.d.ts.map +1 -1
- package/dist/mcp-server/tools/utils/toolHandlerFactory.js +35 -7
- package/dist/mcp-server/tools/utils/toolHandlerFactory.js.map +1 -1
- package/package.json +5 -3
- package/scripts/build-changelog.ts +3 -1
- package/scripts/check-skills-sync.ts +42 -8
- package/skills/add-app-tool/SKILL.md +2 -2
- package/skills/add-export/SKILL.md +2 -2
- package/skills/add-service/SKILL.md +2 -2
- package/skills/add-tool/SKILL.md +30 -2
- package/skills/api-context/SKILL.md +4 -2
- package/skills/api-linter/SKILL.md +27 -3
- package/skills/design-mcp-server/SKILL.md +2 -2
- package/skills/git-wrapup/SKILL.md +22 -15
- package/skills/maintenance/SKILL.md +8 -7
- package/skills/orchestrations/workflows/maintenance-release.md +1 -1
- package/skills/polish-docs-meta/SKILL.md +1 -1
- package/skills/polish-docs-meta/references/agent-protocol.md +2 -2
- package/skills/polish-docs-meta/references/readme.md +3 -3
- package/skills/setup/SKILL.md +5 -10
- package/templates/AGENTS.md +2 -1
- package/templates/CLAUDE.md +2 -1
- package/templates/_.mcpbignore +2 -0
- package/templates/changelog/template.md +1 -1
- package/templates/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolHandlerFactory.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/utils/toolHandlerFactory.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAC;AACxF,OAAO,KAAK,EACV,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACd,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAY,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,MAAM,KAAK,CAAC;AAGjE,OAAO,KAAK,EAAE,OAAO,EAAiC,MAAM,mBAAmB,CAAC;AAGhF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,KAAK,gBAAgB,EAAY,MAAM,0BAA0B,CAAC;AAE3E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAIzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAM7D,KAAK,QAAQ,GAAG,mBAAmB,CAAC,aAAa,EAAE,kBAAkB,CAAC,CAAC;AAOvE,qEAAqE;AACrE,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,cAAc,CAAC;CACzB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,uBAAuB,CAAC,EAAE,MAAM,IAAI,CAAC;IACrC,yBAAyB,CAAC,EAAE,MAAM,IAAI,CAAC;IACvC,qBAAqB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;CACpC;AAyBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GACxC,cAAc,CAchB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAO9E;AAMD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,iBAAiB,GAAG,SAAS,CAAC,WAAW,CAAC,CAGpF;
|
|
1
|
+
{"version":3,"file":"toolHandlerFactory.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/utils/toolHandlerFactory.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,8CAA8C,CAAC;AACxF,OAAO,KAAK,EACV,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACd,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAY,KAAK,SAAS,EAAE,KAAK,WAAW,EAAE,MAAM,KAAK,CAAC;AAGjE,OAAO,KAAK,EAAE,OAAO,EAAiC,MAAM,mBAAmB,CAAC;AAGhF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,KAAK,gBAAgB,EAAY,MAAM,0BAA0B,CAAC;AAE3E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAIzD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAM7D,KAAK,QAAQ,GAAG,mBAAmB,CAAC,aAAa,EAAE,kBAAkB,CAAC,CAAC;AAOvE,qEAAqE;AACrE,MAAM,WAAW,sBAAsB;IACrC;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,cAAc,CAAC;CACzB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,uBAAuB,CAAC,EAAE,MAAM,IAAI,CAAC;IACrC,yBAAyB,CAAC,EAAE,MAAM,IAAI,CAAC;IACvC,qBAAqB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,qBAAqB,CAAC,EAAE,MAAM,IAAI,CAAC;CACpC;AAyBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,gBAAgB,EACtB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GACxC,cAAc,CAchB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAO9E;AAMD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,iBAAiB,GAAG,SAAS,CAAC,WAAW,CAAC,CAGpF;AAmED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,iBAAiB,EACtB,GAAG,EAAE,OAAO,EACZ,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,aAAa,EAAE,YAAY,EAAE,GAC5B,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,mBAAmB,CAAC,CAkBvD;AAwBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,iBAAiB,EACtB,QAAQ,EAAE,sBAAsB,EAChC,SAAS,EAAE,gBAAgB,GAC1B,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,QAAQ,KAAK,OAAO,CAAC,cAAc,CAAC,CA6G9E"}
|
|
@@ -103,13 +103,34 @@ function formatEnrichmentScalar(value) {
|
|
|
103
103
|
/**
|
|
104
104
|
* Renders accumulated enrichment as a single `content[]` trailer block, so
|
|
105
105
|
* `content[]`-only clients see the same context `structuredContent` clients get
|
|
106
|
-
* from the merged output.
|
|
107
|
-
*
|
|
108
|
-
*
|
|
106
|
+
* from the merged output. Per field, rendering resolves in order:
|
|
107
|
+
* 1. a per-field `enrichmentTrailer.render` (the author's full control; wins
|
|
108
|
+
* over everything — the escape hatch for structured fields that would
|
|
109
|
+
* otherwise JSON-blob),
|
|
110
|
+
* 2. the kind-tag set by a field-helper (notice → blockquote, total →
|
|
111
|
+
* "N total", echo → "Query: …", delta → "field: before → after"),
|
|
112
|
+
* 3. the generic `**key:** value` fallback (key overridable via `label`),
|
|
113
|
+
* `JSON.stringify`-ing non-scalars.
|
|
114
|
+
*
|
|
115
|
+
* `parsed` is the validated effective output, so renderers receive the typed,
|
|
116
|
+
* post-parse value rather than the raw accumulator entry. The rendered markdown
|
|
117
|
+
* is appended to `content[]` only — `structuredContent` is built separately from
|
|
118
|
+
* the raw merged values and never carries this trailer text. Returns `[]` when
|
|
119
|
+
* nothing was enriched.
|
|
109
120
|
*/
|
|
110
|
-
function renderEnrichmentTrailer(store) {
|
|
121
|
+
function renderEnrichmentTrailer(store, trailer, parsed) {
|
|
111
122
|
const lines = [];
|
|
112
|
-
for (const
|
|
123
|
+
for (const key of Object.keys(store.values)) {
|
|
124
|
+
// Skip keys the effective-output parse stripped (enriched but not declared in
|
|
125
|
+
// the block) — the trailer mirrors what reached structuredContent.
|
|
126
|
+
if (!(key in parsed))
|
|
127
|
+
continue;
|
|
128
|
+
const value = parsed[key];
|
|
129
|
+
const cfg = trailer?.[key];
|
|
130
|
+
if (cfg?.render) {
|
|
131
|
+
lines.push(cfg.render(value));
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
113
134
|
switch (store.kinds.get(key)) {
|
|
114
135
|
case 'notice':
|
|
115
136
|
lines.push(`> ${String(value)}`);
|
|
@@ -120,8 +141,13 @@ function renderEnrichmentTrailer(store) {
|
|
|
120
141
|
case 'echo':
|
|
121
142
|
lines.push(`Query: ${String(value)}`);
|
|
122
143
|
break;
|
|
144
|
+
case 'delta': {
|
|
145
|
+
const d = (value ?? {});
|
|
146
|
+
lines.push(`**${cfg?.label ?? key}:** ${formatEnrichmentScalar(d.before)} → ${formatEnrichmentScalar(d.after)}`);
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
123
149
|
default:
|
|
124
|
-
lines.push(`**${key}:** ${formatEnrichmentScalar(value)}`);
|
|
150
|
+
lines.push(`**${cfg?.label ?? key}:** ${formatEnrichmentScalar(value)}`);
|
|
125
151
|
}
|
|
126
152
|
}
|
|
127
153
|
return lines.length > 0 ? [{ type: 'text', text: lines.join('\n') }] : [];
|
|
@@ -149,7 +175,9 @@ export function buildToolSuccessResult(def, ctx, domainValidated, domainContent)
|
|
|
149
175
|
...domainValidated,
|
|
150
176
|
...values,
|
|
151
177
|
});
|
|
152
|
-
const trailer = store && Object.keys(values).length > 0
|
|
178
|
+
const trailer = store && Object.keys(values).length > 0
|
|
179
|
+
? renderEnrichmentTrailer(store, def.enrichmentTrailer, structuredContent)
|
|
180
|
+
: [];
|
|
153
181
|
return {
|
|
154
182
|
structuredContent,
|
|
155
183
|
content: trailer.length > 0 ? [...domainContent, ...trailer] : domainContent,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toolHandlerFactory.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/utils/toolHandlerFactory.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"toolHandlerFactory.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/utils/toolHandlerFactory.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyanheads/mcp-ts-core",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.15",
|
|
4
4
|
"mcpName": "io.github.cyanheads/mcp-ts-core",
|
|
5
5
|
"description": "Agent-native TypeScript framework for building MCP servers. Declarative definitions with auth, multi-backend storage, OpenTelemetry, and first-class support for Bun/Node/Cloudflare Workers.",
|
|
6
6
|
"main": "dist/core/index.js",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"scripts/tree.ts",
|
|
22
22
|
"skills/",
|
|
23
23
|
"templates/",
|
|
24
|
+
"AGENTS.md",
|
|
24
25
|
"CLAUDE.md",
|
|
25
26
|
"tsconfig.base.json",
|
|
26
27
|
"vitest.config.base.mjs",
|
|
@@ -134,7 +135,8 @@
|
|
|
134
135
|
"lint": "biome check",
|
|
135
136
|
"lint:mcp": "bun run scripts/lint-mcp.ts",
|
|
136
137
|
"lint:packaging": "bun run scripts/lint-packaging.ts",
|
|
137
|
-
"format": "biome check --write
|
|
138
|
+
"format": "biome check --write .",
|
|
139
|
+
"format:unsafe": "biome check --write --unsafe .",
|
|
138
140
|
"typecheck": "bunx tsc --noEmit",
|
|
139
141
|
"typecheck:scripts": "bunx tsc --project tsconfig.scripts.json --noEmit",
|
|
140
142
|
"tree": "bun run scripts/tree.ts",
|
|
@@ -169,7 +171,7 @@
|
|
|
169
171
|
"devDependencies": {
|
|
170
172
|
"@biomejs/biome": "2.4.16",
|
|
171
173
|
"@cloudflare/vitest-pool-workers": "^0.16.10",
|
|
172
|
-
"@cloudflare/workers-types": "^4.
|
|
174
|
+
"@cloudflare/workers-types": "^4.20260530.1",
|
|
173
175
|
"@duckdb/node-api": "^1.5.3-r.2",
|
|
174
176
|
"@hono/otel": "^1.1.2",
|
|
175
177
|
"@opentelemetry/exporter-metrics-otlp-http": "^0.218.0",
|
|
@@ -198,7 +198,9 @@ function reportMissingSummaries(missing: string[]): void {
|
|
|
198
198
|
console.warn(`\nWarning: ${missing.length} file(s) missing 'summary' frontmatter:`);
|
|
199
199
|
for (const file of shown) console.warn(` - ${file}`);
|
|
200
200
|
if (extra > 0) console.warn(` ... and ${extra} more`);
|
|
201
|
-
console.warn(
|
|
201
|
+
console.warn(
|
|
202
|
+
`\nBackfill these — see CLAUDE.md/AGENTS.md § Changelog for the frontmatter format.`,
|
|
203
|
+
);
|
|
202
204
|
}
|
|
203
205
|
|
|
204
206
|
function main(): void {
|
|
@@ -5,15 +5,17 @@
|
|
|
5
5
|
* updates `skills/` for downstream servers; the mirrors are what local agent
|
|
6
6
|
* toolchains actually read, and silent drift means agents run on stale guidance.
|
|
7
7
|
*
|
|
8
|
-
* Propagation is one-way (`skills/` → mirrors), so
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* Propagation is one-way (`skills/` → mirrors), so missing or content-drifted
|
|
9
|
+
* files are reported. A skill that exists *only* in a mirror is left alone when
|
|
10
|
+
* it's externally sourced (globally installed, other tools), but flagged as
|
|
11
|
+
* stale when its `SKILL.md` carries `metadata.audience: external` — a framework
|
|
12
|
+
* skill removed from `skills/` upstream that the mirror never had pruned.
|
|
12
13
|
*
|
|
13
14
|
* Behavior:
|
|
14
15
|
* • In sync → pass
|
|
15
16
|
* • Mirrors missing entirely → skip (no mirrors to sync)
|
|
16
17
|
* • Drift (missing or changed files) → exit 1 with details (devcheck demotes to warning)
|
|
18
|
+
* • Stale framework skill in mirror → exit 1 (mirror-only dir with audience: external)
|
|
17
19
|
*
|
|
18
20
|
* Ignore specific skills or files via `devcheck.config.json`:
|
|
19
21
|
*
|
|
@@ -79,6 +81,20 @@ function walkFiles(root: string): string[] {
|
|
|
79
81
|
return files;
|
|
80
82
|
}
|
|
81
83
|
|
|
84
|
+
/** Top-level skill directory names under a path (empty when the path is absent). */
|
|
85
|
+
function skillDirNames(root: string): string[] {
|
|
86
|
+
if (!existsSync(root)) return [];
|
|
87
|
+
return readdirSync(root, { withFileTypes: true })
|
|
88
|
+
.filter((e) => e.isDirectory())
|
|
89
|
+
.map((e) => e.name);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** True when a skill is framework-managed — its SKILL.md carries `audience: external`. */
|
|
93
|
+
function isFrameworkManaged(skillMdPath: string): boolean {
|
|
94
|
+
if (!existsSync(skillMdPath)) return false;
|
|
95
|
+
return /^\s*audience:\s*external\s*$/m.test(readFileSync(skillMdPath, 'utf-8'));
|
|
96
|
+
}
|
|
97
|
+
|
|
82
98
|
if (!existsSync(SKILLS_DIR)) {
|
|
83
99
|
console.log('Skipped: no skills/ directory.');
|
|
84
100
|
process.exit(0);
|
|
@@ -114,11 +130,26 @@ for (const mirror of presentMirrors) {
|
|
|
114
130
|
if (driftedHere.length) drifted[mirror.label] = driftedHere.sort();
|
|
115
131
|
}
|
|
116
132
|
|
|
133
|
+
// Stale framework skills: a skill dir present only in a mirror (absent from
|
|
134
|
+
// canonical skills/) is fine when externally sourced, but stale when it carries
|
|
135
|
+
// `audience: external` — a framework skill removed from skills/ that was never
|
|
136
|
+
// pruned from the mirror. User/external skills (no marker) are left alone.
|
|
137
|
+
const canonicalDirs = new Set(skillDirNames(SKILLS_DIR));
|
|
138
|
+
const stale: Record<string, string[]> = {};
|
|
139
|
+
for (const mirror of presentMirrors) {
|
|
140
|
+
const staleHere = skillDirNames(mirror.path)
|
|
141
|
+
.filter((name) => !canonicalDirs.has(name) && !isIgnored(name, ignore))
|
|
142
|
+
.filter((name) => isFrameworkManaged(resolve(mirror.path, name, 'SKILL.md')))
|
|
143
|
+
.sort();
|
|
144
|
+
if (staleHere.length) stale[mirror.label] = staleHere;
|
|
145
|
+
}
|
|
146
|
+
|
|
117
147
|
const totals = {
|
|
118
148
|
missing: Object.values(missing).reduce((n, arr) => n + arr.length, 0),
|
|
119
149
|
drifted: Object.values(drifted).reduce((n, arr) => n + arr.length, 0),
|
|
150
|
+
stale: Object.values(stale).reduce((n, arr) => n + arr.length, 0),
|
|
120
151
|
};
|
|
121
|
-
const driftCount = totals.missing + totals.drifted;
|
|
152
|
+
const driftCount = totals.missing + totals.drifted + totals.stale;
|
|
122
153
|
|
|
123
154
|
if (driftCount === 0) {
|
|
124
155
|
console.log(`skills/ is in sync with ${presentMirrors.map((m) => m.label).join(' and ')}.`);
|
|
@@ -128,7 +159,7 @@ if (driftCount === 0) {
|
|
|
128
159
|
const lines: string[] = [];
|
|
129
160
|
lines.push(
|
|
130
161
|
`skills/ has drifted from ${presentMirrors.length > 1 ? 'mirrors' : 'its mirror'} ` +
|
|
131
|
-
`(${totals.missing} missing, ${totals.drifted} changed).`,
|
|
162
|
+
`(${totals.missing} missing, ${totals.drifted} changed, ${totals.stale} stale).`,
|
|
132
163
|
);
|
|
133
164
|
|
|
134
165
|
const renderSection = (title: string, groups: Record<string, string[]>) => {
|
|
@@ -141,10 +172,13 @@ const renderSection = (title: string, groups: Record<string, string[]>) => {
|
|
|
141
172
|
|
|
142
173
|
renderSection('Missing in', missing);
|
|
143
174
|
renderSection('Content differs in', drifted);
|
|
175
|
+
renderSection('Stale framework skill (deleted upstream) in', stale);
|
|
144
176
|
|
|
145
177
|
lines.push('');
|
|
146
|
-
lines.push('Fix: propagate skills/ to the mirror(s)
|
|
147
|
-
lines.push(
|
|
178
|
+
lines.push('Fix: propagate skills/ to the mirror(s) and delete stale framework skills from them,');
|
|
179
|
+
lines.push(
|
|
180
|
+
' or add entries to devcheck.config.json `skillsSync.ignore` to silence specific paths.',
|
|
181
|
+
);
|
|
148
182
|
|
|
149
183
|
console.log(lines.join('\n'));
|
|
150
184
|
process.exit(1);
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Scaffold an MCP App tool + UI resource pair. Use when the user asks to add a tool with interactive UI, create an MCP App, or build a visual/interactive tool.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.4"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -30,7 +30,7 @@ MCP Apps extend the standard tool pattern with an interactive HTML UI rendered i
|
|
|
30
30
|
|
|
31
31
|
Both builders are exported from `@cyanheads/mcp-ts-core`. They handle `_meta.ui.resourceUri`, the compat key (`ui/resourceUri`), and the correct MIME type (`text/html;profile=mcp-app`) automatically.
|
|
32
32
|
|
|
33
|
-
For the full API, Context interface, and error codes, read the framework's `CLAUDE.md` (loaded at session start).
|
|
33
|
+
For the full API, Context interface, and error codes, read the framework's `CLAUDE.md`/`AGENTS.md` (loaded at session start).
|
|
34
34
|
|
|
35
35
|
## Steps
|
|
36
36
|
|
|
@@ -4,14 +4,14 @@ description: >
|
|
|
4
4
|
Add a new subpath export to the @cyanheads/mcp-ts-core package. Use when creating a new public API surface that consumers import from a dedicated subpath (e.g., @cyanheads/mcp-ts-core/newutil).
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.1"
|
|
8
8
|
audience: internal
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
## Context
|
|
13
13
|
|
|
14
|
-
Subpath exports are defined in `package.json` under the `exports` field. Each subpath maps to a source entry point that gets compiled to `dist/`. The exports catalog in `CLAUDE.md` must stay in sync with `package.json`.
|
|
14
|
+
Subpath exports are defined in `package.json` under the `exports` field. Each subpath maps to a source entry point that gets compiled to `dist/`. The exports catalog in `CLAUDE.md`/`AGENTS.md` must stay in sync with `package.json`.
|
|
15
15
|
|
|
16
16
|
The build uses `tsconfig.build.json` (not `tsconfig.json`) with `rootDir: ./src` and `include: ["src/**/*"]`. This means every source file at `src/foo/bar.ts` compiles to `dist/foo/bar.js` — the `dist/` path in each export entry must match wherever `tsc` produces the compiled output for the named source file. Choose your source file location to produce the `dist/` path you want in the export entry.
|
|
17
17
|
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Scaffold a new service integration. Use when the user asks to add a service, integrate an external API, or create a reusable domain module with its own initialization and state.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.6"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -15,7 +15,7 @@ Services use the init/accessor pattern: initialized once in `createApp`'s `setup
|
|
|
15
15
|
|
|
16
16
|
Service methods receive `Context` for correlated logging (`ctx.log`) and tenant-scoped storage (`ctx.state`). Convention: `ctx.elicit` and `ctx.sample` should only be called from tool handlers, not from services.
|
|
17
17
|
|
|
18
|
-
For the full service pattern, `CoreServices`, and `Context` interface, read the framework's `CLAUDE.md` (loaded at session start).
|
|
18
|
+
For the full service pattern, `CoreServices`, and `Context` interface, read the framework's `CLAUDE.md`/`AGENTS.md` (loaded at session start).
|
|
19
19
|
|
|
20
20
|
## Steps
|
|
21
21
|
|
package/skills/add-tool/SKILL.md
CHANGED
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Scaffold a new MCP tool definition. Use when the user asks to add a tool, create a new tool, or implement a new capability for the server.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "2.
|
|
7
|
+
version: "2.11"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -237,6 +237,21 @@ async handler(input, ctx) {
|
|
|
237
237
|
|
|
238
238
|
A *required* enrichment field the handler never populates fails the effective-output parse — surfacing the bug rather than dropping it silently. Enrichment keys must be disjoint from `output` keys (lint-enforced). The sections below are applications of this rule.
|
|
239
239
|
|
|
240
|
+
**Trailer rendering is a per-field call.** Each field's `content[]` trailer line resolves as: its kind-tag if set (`notice`/`total`/`echo`/`delta`), else the definition's per-field `enrichmentTrailer.render`/`label`, else the generic `**key:** value` (objects/arrays `JSON.stringify`'d). A structured (object/array) field with no `render` ships as a one-line JSON blob — the `enrichment-trailer-render` lint rule errors on that. Give it a renderer, or a `label` to relabel a scalar key:
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
enrichment: {
|
|
244
|
+
totalFound: z.number().describe('Matches before the page limit.'),
|
|
245
|
+
appliedFilters: z.object({ /* … */ }).describe('Filters the server applied.'),
|
|
246
|
+
},
|
|
247
|
+
enrichmentTrailer: {
|
|
248
|
+
totalFound: { label: 'Total Found' }, // → "**Total Found:** 2990"
|
|
249
|
+
appliedFilters: { render: (f) => `### Filters\n- Range: ${f.dateRange}` }, // markdown, not JSON
|
|
250
|
+
},
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
`structuredContent` always keeps the full structured value; `enrichmentTrailer` only controls the human-facing `content[]` line.
|
|
254
|
+
|
|
240
255
|
### Communicate filtering and exclusions
|
|
241
256
|
|
|
242
257
|
If the tool omitted, truncated, or filtered anything, say what and how to get it back. Silent omission is invisible to the agent — it can't act on what it doesn't know about.
|
|
@@ -329,6 +344,19 @@ output: z.object({
|
|
|
329
344
|
|
|
330
345
|
The agent reads `created: true, previousSizeInBytes: 0, currentSizeInBytes: 68` and knows: brand new target, the full file content is the body. If that matches intent, fine; if not (typo path, uninitialized periodic note), the agent self-corrects without re-fetching. Anti-pattern: server-side `>=` integrity throws on mutators — the server can't distinguish intentional shrink from bug, so it throws on every shrink, including the deliberate ones.
|
|
331
346
|
|
|
347
|
+
When the before/after is agent-facing context rather than primary payload, the `enrichment`-native form is `ctx.enrich.delta({ field, before, after })` — it writes `{ before, after }` to `structuredContent` and renders `**field:** before → after` in the `content[]` trailer. Declare the field in the `enrichment` block as `z.object({ before, after })`; the linter recognizes the shape, so it needs no custom `enrichmentTrailer.render`. Same stance — surface raw state, never a verdict:
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
enrichment: {
|
|
351
|
+
sizeInBytes: z.object({
|
|
352
|
+
before: z.number().describe('Byte size before the mutation.'),
|
|
353
|
+
after: z.number().describe('Byte size after the mutation.'),
|
|
354
|
+
}).describe('Raw size before/after — the agent judges whether a shrink was intended.'),
|
|
355
|
+
},
|
|
356
|
+
// handler:
|
|
357
|
+
ctx.enrich.delta({ field: 'sizeInBytes', before: prev, after: next });
|
|
358
|
+
```
|
|
359
|
+
|
|
332
360
|
### Sparse upstream data must stay honest
|
|
333
361
|
|
|
334
362
|
When tool output comes from a third-party API, don't overstate certainty. Upstream systems often omit fields entirely; the tool schema and `format()` should preserve that uncertainty instead of collapsing it into fake `false`, `0`, or empty-string facts.
|
|
@@ -492,7 +520,7 @@ throw invalidParams(
|
|
|
492
520
|
|
|
493
521
|
Counts, applied filters, truncation notices, and chaining IDs help the agent decide its next action without extra round trips.
|
|
494
522
|
|
|
495
|
-
Counts, applied-filter summaries, and query echo that describe the *result set* (rather than being the result) are textbook `enrichment` — `ctx.enrich.total(n)`, `ctx.enrich.echo(parsedQuery)`, or `ctx.enrich({ appliedFilters })` put them on both client surfaces with no `format()` entry. Reserve domain `output` for the payload itself and post-action state (e.g. `currentStatus` after a write), as below:
|
|
523
|
+
Counts, applied-filter summaries, and query echo that describe the *result set* (rather than being the result) are textbook `enrichment` — `ctx.enrich.total(n)`, `ctx.enrich.echo(parsedQuery)`, or `ctx.enrich({ appliedFilters })` put them on both client surfaces with no `format()` entry (a structured field like `appliedFilters` also needs an `enrichmentTrailer.render` so its trailer line is markdown, not a JSON blob — see **Tool Response Design**). Reserve domain `output` for the payload itself and post-action state (e.g. `currentStatus` after a write), as below:
|
|
496
524
|
|
|
497
525
|
```typescript
|
|
498
526
|
return {
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Canonical reference for the unified `Context` object passed to every tool and resource handler in `@cyanheads/mcp-ts-core`. Covers the full interface, all sub-APIs (`ctx.log`, `ctx.state`, `ctx.elicit`, `ctx.sample`, `ctx.progress`, `ctx.enrich`), and when to use each.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.5"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -214,7 +214,7 @@ Use this only when downstream code is structured around `ctx.sessionId` and acce
|
|
|
214
214
|
|
|
215
215
|
### Capability-token model
|
|
216
216
|
|
|
217
|
-
Surfacing `sessionId` does not change the framework's capability-as-token rule (possession of an opaque ID grants access — see CLAUDE.md `# Core Rules`). It is an opt-in *discovery-scoping* axis, not an access boundary.
|
|
217
|
+
Surfacing `sessionId` does not change the framework's capability-as-token rule (possession of an opaque ID grants access — see CLAUDE.md/AGENTS.md `# Core Rules`). It is an opt-in *discovery-scoping* axis, not an access boundary.
|
|
218
218
|
|
|
219
219
|
- Tokens shared across sessions (e.g. `df_<uuid>` handed from Agent A to Agent B) still resolve on the receiving side. The lookup key is the token, not the session.
|
|
220
220
|
- Session-scoped *enumeration* (e.g. `dataframe_describe` returning only items registered by the current session) is a per-server pattern: maintain a session-keyed lookup of known names, gate list-all on it, but route direct lookups against the shared backing store.
|
|
@@ -564,6 +564,7 @@ ctx.enrich(fields: Partial<z.infer<ZodObject<E>>>): void
|
|
|
564
564
|
ctx.enrich.notice(text: string): void // writes `notice` → blockquote
|
|
565
565
|
ctx.enrich.total(count: number): void // writes `totalCount` → "N total"
|
|
566
566
|
ctx.enrich.echo(query: string): void // writes `effectiveQuery` → "Query: …"
|
|
567
|
+
ctx.enrich.delta({ field, before, after }): void // writes `{before, after}` → "field: before → after"
|
|
567
568
|
```
|
|
568
569
|
|
|
569
570
|
### Behavior
|
|
@@ -577,6 +578,7 @@ ctx.enrich.echo(query: string): void // writes `effectiveQuery` → "Que
|
|
|
577
578
|
| No block | Calling `ctx.enrich` on a tool that declared no `enrichment` is a silent no-op (values are stripped by the parse) — the price of service-layer callability. |
|
|
578
579
|
| Service usage | Services accepting `ctx: Context` can call `ctx.enrich(...)`; the value reaches `structuredContent` exactly as if the handler had. |
|
|
579
580
|
| `format-parity` | Enrichment lives outside `output`, so the `format-parity` lint never requires it in `format()`. |
|
|
581
|
+
| Trailer rendering | Per field: kind-tag if set (notice/total/echo/delta), else the definition's `enrichmentTrailer.render`/`label`, else `**key:** value` (objects/arrays `JSON.stringify`'d). A structured field with no `render` errors under `enrichment-trailer-render` — supply one so it renders as markdown; `structuredContent` keeps the full value regardless. |
|
|
580
582
|
|
|
581
583
|
See `add-tool`'s **Tool Response Design** and `skills/api-linter` (`enrichment-*` rules) for the full pattern. Test enrichment with `getEnrichment(ctx)` from `@cyanheads/mcp-ts-core/testing`.
|
|
582
584
|
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
MCP definition linter rules reference. Use when `bun run lint:mcp` or `bun run devcheck` reports a lint error or warning (`format-parity`, `schema-is-object`, `name-format`, `server-json-*`, etc.) and you need to understand the rule, its severity, and how to fix it. Every rule ID the linter emits has an entry in this doc.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.5"
|
|
8
8
|
audience: external
|
|
9
9
|
type: reference
|
|
10
10
|
---
|
|
@@ -53,7 +53,7 @@ Grouped by family. Jump to any rule ID via its anchor.
|
|
|
53
53
|
| Handler body | `prefer-mcp-error-in-handler`, `prefer-error-factory`, `preserve-cause-on-rethrow`, `no-stringify-upstream-error` | [Handler body rules](#handler-body-rules) |
|
|
54
54
|
| Error contract (structural) | `error-contract-type`, `error-contract-empty`, `error-contract-entry-type`, `error-contract-code-type`, `error-contract-code-unknown`, `error-contract-code-unknown-error`, `error-contract-reason-required`, `error-contract-reason-format`, `error-contract-reason-unique`, `error-contract-when-required`, `error-contract-retryable-type`, `error-contract-recovery-required`, `error-contract-recovery-empty`, `error-contract-recovery-min-words` | [Error contract rules](#error-contract-rules) |
|
|
55
55
|
| Error contract (conformance) | `error-contract-conformance`, `error-contract-prefer-fail` | [Error contract rules](#error-contract-rules) |
|
|
56
|
-
| Enrichment | `enrichment-type`, `enrichment-empty`, `enrichment-field-type`, `enrichment-output-collision`, `enrichment-prefer-block` | [Enrichment rules](#enrichment-rules) |
|
|
56
|
+
| Enrichment | `enrichment-type`, `enrichment-empty`, `enrichment-field-type`, `enrichment-output-collision`, `enrichment-prefer-block`, `enrichment-trailer-render`, `enrichment-trailer-orphan`, `enrichment-trailer-unknown-field` | [Enrichment rules](#enrichment-rules) |
|
|
57
57
|
| server.json | ~40 rules prefixed `server-json-*` | [server.json rules](#server-json-rules) |
|
|
58
58
|
|
|
59
59
|
---
|
|
@@ -515,7 +515,7 @@ Heuristic source-text checks that scan `handler.toString()` for common error-han
|
|
|
515
515
|
|
|
516
516
|
Fires when a handler contains `throw new Error(...)`. Plain `Error` doesn't carry a JSON-RPC code — the framework's auto-classifier degrades to `InternalError`, hiding the actual failure mode.
|
|
517
517
|
|
|
518
|
-
Plain `Error` is acceptable for "don't care" cases where the specific code doesn't matter (per CLAUDE.md: "plain `Error` for don't-care cases"). This rule targets domain-specific failures that deserve a concrete code — upgrade those to factories or `ctx.fail`, and accept the warning for the rest.
|
|
518
|
+
Plain `Error` is acceptable for "don't care" cases where the specific code doesn't matter (per CLAUDE.md/AGENTS.md: "plain `Error` for don't-care cases"). This rule targets domain-specific failures that deserve a concrete code — upgrade those to factories or `ctx.fail`, and accept the warning for the rest.
|
|
519
519
|
|
|
520
520
|
**Fix:** use `McpError` or a factory for domain-specific failures:
|
|
521
521
|
|
|
@@ -753,6 +753,30 @@ Advisory. Fires when a tool has **no** `enrichment` block but an `output` field
|
|
|
753
753
|
|
|
754
754
|
**Fix:** move the field into an `enrichment` block and populate it via `ctx.enrich(...)` — it reaches both client surfaces without a `format()` entry. Ignore if the field is genuinely domain data. Deliberately conservative — common domain fields like `totalCount` are not flagged.
|
|
755
755
|
|
|
756
|
+
### enrichment-trailer-render
|
|
757
|
+
|
|
758
|
+
**Severity:** error
|
|
759
|
+
|
|
760
|
+
Fires when a non-scalar (object/array) enrichment field has no `enrichmentTrailer.render`. It would `JSON.stringify` into a one-line blob in the `content[]` trailer (`structuredContent` keeps the full value either way). The `delta` shape (`z.object({ before, after })`, populated by `ctx.enrich.delta()`) is exempt — it renders natively as `field: before → after`.
|
|
761
|
+
|
|
762
|
+
**Fix:** add a renderer — `enrichmentTrailer: { <field>: { render: (v) => … } }` — use `ctx.enrich.delta()` for before/after state, or opt into the JSON blob explicitly with `render: (v) => JSON.stringify(v)`.
|
|
763
|
+
|
|
764
|
+
### enrichment-trailer-orphan
|
|
765
|
+
|
|
766
|
+
**Severity:** error
|
|
767
|
+
|
|
768
|
+
Fires when `enrichmentTrailer` is declared without an `enrichment` block — trailer config only renders enrichment fields.
|
|
769
|
+
|
|
770
|
+
**Fix:** add the `enrichment` block, or drop the `enrichmentTrailer`.
|
|
771
|
+
|
|
772
|
+
### enrichment-trailer-unknown-field
|
|
773
|
+
|
|
774
|
+
**Severity:** error
|
|
775
|
+
|
|
776
|
+
Fires when an `enrichmentTrailer` key doesn't match any declared `enrichment` field (a typo or drift the `keyof`-typed config already catches for TS authors).
|
|
777
|
+
|
|
778
|
+
**Fix:** rename the trailer key to a declared enrichment field, or remove it.
|
|
779
|
+
|
|
756
780
|
---
|
|
757
781
|
|
|
758
782
|
## Escape hatches
|
|
@@ -4,7 +4,7 @@ description: >
|
|
|
4
4
|
Design the tool surface, resources, and service layer for a new MCP server. Use when starting a new server, planning a major feature expansion, or when the user describes a domain/API they want to expose via MCP. Produces a design doc at docs/design.md that drives implementation.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "2.
|
|
7
|
+
version: "2.14"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
@@ -351,7 +351,7 @@ output: z.object({
|
|
|
351
351
|
- **Truncate large output with counts.** When a list exceeds a reasonable display size, show the top N and append "...and X more". Don't silently drop results.
|
|
352
352
|
- **Spill big tabular results to a queryable surface.** When a tool's row set can exceed any reasonable context budget — paginated APIs, streamed exports, big query results — pair an inline preview with a `DataCanvas` table holding the full set, returned as a token the agent can SQL. Compute distributions or refinement hints across the full result, not the preview, so aggregate signal stays honest. See `api-canvas` for the `spillover()` helper.
|
|
353
353
|
- **`format()` is the markdown twin of `structuredContent` — make both content-complete.** Different MCP clients forward different surfaces to the model: some (e.g., Claude Code) read `structuredContent` from `output`, others (e.g., Claude Desktop) read `content[]` from `format()`. Both must carry the same data so every client sees the same picture — `format()` just dresses it up with markdown. A thin `format()` that returns only a count or title leaves `content[]`-only clients blind to data that `structuredContent` clients can see. Render all fields the LLM needs, with structured markdown (headers, bold labels, lists) for readability.
|
|
354
|
-
- **Agent-facing context must reach both client surfaces — put it in `enrichment`.** `structuredContent` (from `output`) and `content[]` (from `format()`) are read by different clients. Empty-result notices, the query/filter as the server parsed it, and pagination totals — the context the agent *reasons with*, distinct from the domain payload — reach only `content[]` if hand-authored into `format()` text alone, leaving `structuredContent`-only clients (Claude Code) blind. (The reverse can't happen: `format-parity` drags every `output` field into `format()`, so `output`-authored context already reaches both.) An `enrichment` block — the success-path counterpart to `errors[]`, populated via `ctx.enrich(...)` — reaches both automatically: merged into `structuredContent`, advertised as `output.extend(enrichment)`, mirrored into a `content[]` trailer, no `format()` entry needed. See `add-tool`'s **Tool Response Design**.
|
|
354
|
+
- **Agent-facing context must reach both client surfaces — put it in `enrichment`.** `structuredContent` (from `output`) and `content[]` (from `format()`) are read by different clients. Empty-result notices, the query/filter as the server parsed it, and pagination totals — the context the agent *reasons with*, distinct from the domain payload — reach only `content[]` if hand-authored into `format()` text alone, leaving `structuredContent`-only clients (Claude Code) blind. (The reverse can't happen: `format-parity` drags every `output` field into `format()`, so `output`-authored context already reaches both.) An `enrichment` block — the success-path counterpart to `errors[]`, populated via `ctx.enrich(...)` — reaches both automatically: merged into `structuredContent`, advertised as `output.extend(enrichment)`, mirrored into a `content[]` trailer, no `format()` entry needed. How each field renders in that trailer is a per-tool call — a kind-tag (`notice`/`total`/`echo`/`delta`) when a canonical form fits, a domain key like `totalFound` otherwise, and an `enrichmentTrailer.render` for any structured (object/array) field so it doesn't ship as a JSON blob. See `add-tool`'s **Tool Response Design**.
|
|
355
355
|
|
|
356
356
|
#### Batch input design
|
|
357
357
|
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: git-wrapup
|
|
3
3
|
description: >
|
|
4
|
-
Land working-tree changes as
|
|
4
|
+
Land working-tree changes as logical commits — the work grouped by concern, topped by a release commit (version bump, changelog, regenerated artifacts) and an annotated tag. Verify, commit, tag. Stops at "committed and tagged locally" — no push, no publish. The release-and-publish skill picks up from here. Distilled from the git_wrapup_instructions protocol.
|
|
5
5
|
metadata:
|
|
6
6
|
author: cyanheads
|
|
7
|
-
version: "1.
|
|
7
|
+
version: "1.2"
|
|
8
8
|
audience: external
|
|
9
9
|
type: workflow
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
## When to use
|
|
13
13
|
|
|
14
|
-
Working-tree or staged changes are ready to ship as a new version. This skill lands them as a
|
|
14
|
+
Working-tree or staged changes are ready to ship as a new version. This skill lands them as a stack of logical commits — the work grouped by concern, topped by a release commit (version + changelog + tree) — with an annotated tag. It does NOT push or publish — that's a separate step (`release-and-publish`).
|
|
15
15
|
|
|
16
16
|
Common triggers:
|
|
17
17
|
- Feature work, bug fixes, or dependency updates are done and tested
|
|
@@ -119,27 +119,34 @@ bun run test:all # or `bun run test` if no test:all script exists
|
|
|
119
119
|
|
|
120
120
|
**If either fails, halt.** Do not bypass verification to land the commit. Fix the issue first, then re-run from step 6.
|
|
121
121
|
|
|
122
|
-
### 7. Commit
|
|
122
|
+
### 7. Commit — group by concern, release artifacts on top
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
Do NOT `git add -A` into one commit. Group the working tree into a handful of logical commits — never one blob:
|
|
125
|
+
|
|
126
|
+
1. **The work — one commit per concern.** A feature spanning multiple layers splits by layer: runtime/logic, linter/tooling, docs/skills. Unrelated changes (two separate fixes, an incidental doc tweak) are their own commits. Work commits do not carry the version.
|
|
127
|
+
2. **The release commit — last, on top.** Version bumps (`package.json`, `server.json`, README badge, `CLAUDE.md`/`AGENTS.md`), the changelog entry, `CHANGELOG.md`, and `docs/tree.md` go in a single final commit that sits on top of the work stack — never mixed into a feature commit.
|
|
128
|
+
|
|
129
|
+
Stage each group explicitly, commit it, then move to the next — the release commit goes last:
|
|
125
130
|
|
|
126
131
|
```bash
|
|
127
|
-
git add -
|
|
132
|
+
git add <paths-for-this-concern>
|
|
128
133
|
git commit -m "<subject>"
|
|
134
|
+
# repeat per concern; version + changelog + tree are the final commit
|
|
129
135
|
```
|
|
130
136
|
|
|
131
|
-
**
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
- `
|
|
137
|
+
**The file is the atomic boundary:** NEVER split a single file's changes across commits. When one file serves two concerns, it ships whole in the commit of its dominant concern.
|
|
138
|
+
|
|
139
|
+
**Subject format:** Conventional Commits.
|
|
140
|
+
- Work commits (no version): `feat: hosted server endpoint`, `fix: handle empty SPARQL result sets`, `feat(linter): enrichment contract rules`, `docs: document the enrichment block`
|
|
141
|
+
- Release commit (subject leads with the version): `chore(release): 0.2.1 — empty SPARQL result handling`
|
|
135
142
|
|
|
136
143
|
**Rules:**
|
|
137
144
|
- Plain `-m` flag only — no heredoc, no command substitution
|
|
138
145
|
- No `Co-authored-by` or `Generated with` trailers
|
|
139
146
|
- No marketing adjectives ("comprehensive", "robust", "enhanced", "seamless", "improved")
|
|
140
|
-
-
|
|
147
|
+
- Each commit message stands alone for someone reading `git log` — no chat context, option numbers, or "as discussed"
|
|
141
148
|
|
|
142
|
-
**
|
|
149
|
+
**Right-size it.** "Group by concern" is not "always split." A genuinely single-concern change — one fix, a dependency bump, a small doc edit — is one work commit plus the release commit; when the change and its version bump are inseparable for a tiny patch, a single commit whose subject leads with the version is fine. The failure mode to prevent is the inverse: a large, multi-layer feature crammed into one commit alongside the release artifacts.
|
|
143
150
|
|
|
144
151
|
### 8. Create an annotated tag
|
|
145
152
|
|
|
@@ -183,8 +190,8 @@ Dependency bumps:
|
|
|
183
190
|
### 9. Verify end state
|
|
184
191
|
|
|
185
192
|
```bash
|
|
186
|
-
git log --oneline -
|
|
187
|
-
git show v<version> --stat | head -20 # confirm tag points at HEAD
|
|
193
|
+
git log --oneline -8 # confirm the commit stack: work commits + release commit on top
|
|
194
|
+
git show v<version> --stat | head -20 # confirm tag points at HEAD (the release commit)
|
|
188
195
|
git status # must be clean
|
|
189
196
|
```
|
|
190
197
|
|
|
@@ -211,7 +218,7 @@ If the working tree isn't clean or the tag doesn't point at HEAD, something went
|
|
|
211
218
|
- [ ] `docs/tree.md` regenerated if structure changed (`bun run tree`)
|
|
212
219
|
- [ ] `bun run devcheck` passes
|
|
213
220
|
- [ ] `bun run test:all` (or `test`) passes
|
|
214
|
-
- [ ]
|
|
221
|
+
- [ ] Work grouped into logical commits (large features split by layer); release artifacts (version + changelog + tree) committed separately on top, subject leading with the version
|
|
215
222
|
- [ ] Annotated tag `v<version>` with structured markdown message
|
|
216
223
|
- [ ] Working tree clean
|
|
217
224
|
- [ ] Nothing pushed — local only
|