@long.dg/le-restaurant 0.1.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.
@@ -0,0 +1,160 @@
1
+ import { Command } from 'commander';
2
+
3
+ /**
4
+ * Core domain types shared across the registry, adapters, and writer.
5
+ * These are the foundational shared surfaces — every later ticket builds on them.
6
+ */
7
+ /** A normalized source skill, parsed from a `SKILL.md` (frontmatter + body). */
8
+ interface Skill {
9
+ /** Skill identifier, e.g. "chef-de-rang" (from frontmatter, falls back to dir name). */
10
+ name: string;
11
+ /** One-line description from frontmatter. */
12
+ description: string;
13
+ /** Full parsed frontmatter as a key/value map. */
14
+ frontmatter: Record<string, unknown>;
15
+ /** The markdown body after the frontmatter, verbatim. */
16
+ body: string;
17
+ /** Optional relative paths to bundled assets that ship with the skill. */
18
+ assets?: string[];
19
+ }
20
+ /** A coding agent we can translate skills for. */
21
+ interface Agent {
22
+ id: "claude" | "codex" | "gemini";
23
+ label: string;
24
+ }
25
+ /** How a single emitted file should be written to the target tree. */
26
+ type WriteMode = "create" | "overwrite" | "merge";
27
+ /** One file an adapter wants written to the target project. */
28
+ interface FileOutput {
29
+ /** Path relative to `InstallContext.targetDir`. */
30
+ path: string;
31
+ /** Full file contents to write. */
32
+ contents: string;
33
+ /** Write strategy the writer should apply. */
34
+ mode: WriteMode;
35
+ }
36
+ /** How the writer resolves a collision with an existing file. */
37
+ type ConflictPolicy = "skip" | "overwrite" | "merge";
38
+ /** Everything an adapter needs to know to translate a set of skills. */
39
+ interface InstallContext {
40
+ /** Absolute path to the target project root. */
41
+ targetDir: string;
42
+ /** The skills the user chose to install. */
43
+ selectedSkills: Skill[];
44
+ /** What to do when an output file already exists. */
45
+ conflictPolicy: ConflictPolicy;
46
+ }
47
+
48
+ /**
49
+ * The single contract every agent adapter implements.
50
+ *
51
+ * An adapter is a pure transform: given the normalized skills and an install
52
+ * context, it returns the list of files to write — it does no I/O itself.
53
+ * The writer is responsible for putting `FileOutput`s on disk.
54
+ *
55
+ * Pulled forward into Order 01 (rather than Phase 1) so the Claude passthrough
56
+ * adapter implements the same interface every later adapter (Codex, Gemini) will.
57
+ */
58
+ interface Adapter {
59
+ /** The agent this adapter targets. */
60
+ readonly id: "claude" | "codex" | "gemini";
61
+ /** Human-readable label for summaries/prompts. */
62
+ readonly label: string;
63
+ /** Translate skills into the files this agent expects. */
64
+ translate(skills: Skill[], ctx: InstallContext): FileOutput[];
65
+ }
66
+
67
+ /** What the writer did with a single output file. */
68
+ type WriteAction = "created" | "overwritten" | "merged" | "skipped";
69
+ /** A file the writer processed, for the post-install summary. */
70
+ interface WriteResult {
71
+ /** Path relative to the target dir (as the adapter emitted it). */
72
+ path: string;
73
+ /** The action the conflict policy resolved to. */
74
+ action: WriteAction;
75
+ }
76
+ /**
77
+ * Write a set of adapter outputs into the install target.
78
+ *
79
+ * Each `FileOutput.path` is resolved relative to `ctx.targetDir` and written
80
+ * atomically. When a target file already exists, the `conflictPolicy` decides:
81
+ * - `skip` — leave the existing file untouched;
82
+ * - `overwrite` — replace it with the emitted contents;
83
+ * - `merge` — for `mode: "merge"` outputs, fold the managed block into the
84
+ * existing file (preserving user content outside the markers); for other
85
+ * outputs, fall back to overwrite.
86
+ *
87
+ * A file that does not yet exist is always created regardless of policy.
88
+ */
89
+ declare function writeOutputs(outputs: FileOutput[], ctx: InstallContext): WriteResult[];
90
+
91
+ /** The agents we can install for. */
92
+ type AgentId = "claude" | "codex" | "gemini";
93
+ /** Valid agent ids, for validation + help text. */
94
+ declare const AGENT_IDS: AgentId[];
95
+ /**
96
+ * Map a chosen agent id to its adapter.
97
+ *
98
+ * Throws a clear error on an unknown id so both the CLI (non-zero exit) and
99
+ * library callers fail loudly rather than silently installing the wrong thing.
100
+ */
101
+ declare function selectAdapter(agentId: string): Adapter;
102
+ /**
103
+ * The conflict policy to use for an agent by default.
104
+ *
105
+ * Claude's passthrough files are fully tool-owned, so they overwrite. The
106
+ * Codex/Gemini marker targets (`AGENTS.md`, `GEMINI.md`) merge so user content
107
+ * outside the managed markers is preserved on re-runs.
108
+ */
109
+ declare function defaultConflictPolicy(agentId: string): ConflictPolicy;
110
+ /**
111
+ * Resolve a `--skills` value into the concrete list of skills to install.
112
+ *
113
+ * Accepts the literal `all` (every available skill) or a comma-separated list
114
+ * of skill names (order/whitespace tolerant). Throws clearly on an empty
115
+ * selection or any unknown name.
116
+ */
117
+ declare function resolveSkillSelection(spec: string, available: Skill[]): Skill[];
118
+ /** A resolved, ready-to-run install request. */
119
+ interface InstallRequest {
120
+ agentId: AgentId;
121
+ skills: Skill[];
122
+ targetDir: string;
123
+ conflictPolicy?: ConflictPolicy;
124
+ }
125
+ /**
126
+ * Translate the selected skills with the chosen adapter and write them to the
127
+ * target dir. The single install primitive shared by the flag and wizard paths.
128
+ */
129
+ declare function runInstall(req: InstallRequest): {
130
+ adapter: Adapter;
131
+ results: WriteResult[];
132
+ };
133
+ /** Options collected from the non-interactive flags. */
134
+ interface InstallFlags {
135
+ agent?: string;
136
+ skills?: string;
137
+ yes?: boolean;
138
+ }
139
+ /**
140
+ * Resolve raw `--agent`/`--skills` flags into a validated install request.
141
+ * Throws clearly (non-zero exit upstream) on any invalid value.
142
+ */
143
+ declare function resolveFlags(flags: InstallFlags, targetDir: string, available: Skill[]): InstallRequest;
144
+ /**
145
+ * Build the commander program. Factored out so tests can introspect the
146
+ * wired-up commands/options without spawning a process.
147
+ */
148
+ declare function buildProgram(): Command;
149
+ /**
150
+ * Parse argv, run the CLI, and resolve to the process exit code.
151
+ *
152
+ * commander is put in `exitOverride` mode so it throws instead of calling
153
+ * `process.exit`, letting us (and tests) observe the exit code. Help/version
154
+ * exit 0; parse/validation errors exit non-zero.
155
+ */
156
+ declare function runCli(argv?: string[]): Promise<number>;
157
+ /** Convenience entry: run and let the process adopt the resolved exit code. */
158
+ declare function run(argv?: string[]): Promise<number>;
159
+
160
+ export { type Adapter as A, type ConflictPolicy as C, type FileOutput as F, type InstallContext as I, type Skill as S, type WriteResult as W, AGENT_IDS as a, type Agent as b, type AgentId as c, type InstallRequest as d, type WriteAction as e, type WriteMode as f, buildProgram as g, defaultConflictPolicy as h, resolveSkillSelection as i, run as j, runCli as k, runInstall as l, resolveFlags as r, selectAdapter as s, writeOutputs as w };
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import 'commander';
2
+ export { a as AGENT_IDS, c as AgentId, d as InstallRequest, g as buildProgram, h as defaultConflictPolicy, r as resolveFlags, i as resolveSkillSelection, j as run, k as runCli, l as runInstall, s as selectAdapter } from './cli-BDBD_kfX.js';
package/dist/cli.js ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ AGENT_IDS,
4
+ buildProgram,
5
+ defaultConflictPolicy,
6
+ resolveFlags,
7
+ resolveSkillSelection,
8
+ run,
9
+ runCli,
10
+ runInstall,
11
+ selectAdapter
12
+ } from "./chunk-5DTTVPAO.js";
13
+ export {
14
+ AGENT_IDS,
15
+ buildProgram,
16
+ defaultConflictPolicy,
17
+ resolveFlags,
18
+ resolveSkillSelection,
19
+ run,
20
+ runCli,
21
+ runInstall,
22
+ selectAdapter
23
+ };
24
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,95 @@
1
+ import { A as Adapter, S as Skill, W as WriteResult } from './cli-BDBD_kfX.js';
2
+ export { a as AGENT_IDS, b as Agent, c as AgentId, C as ConflictPolicy, F as FileOutput, I as InstallContext, d as InstallRequest, e as WriteAction, f as WriteMode, g as buildProgram, h as defaultConflictPolicy, r as resolveFlags, i as resolveSkillSelection, j as run, k as runCli, l as runInstall, s as selectAdapter, w as writeOutputs } from './cli-BDBD_kfX.js';
3
+ import 'commander';
4
+
5
+ /**
6
+ * Claude Code — passthrough strategy.
7
+ *
8
+ * Claude Code natively supports on-demand skills, so we copy each source
9
+ * `SKILL.md` verbatim to `.claude/skills/<name>/SKILL.md`. To guarantee
10
+ * byte-for-byte fidelity we read the original vendored file rather than
11
+ * re-serialize the normalized `Skill` (which would lose exact formatting).
12
+ */
13
+ declare const claudeAdapter: Adapter;
14
+
15
+ /**
16
+ * Codex — merge strategy.
17
+ *
18
+ * Codex reads a single monolithic `AGENTS.md` per directory with no named or
19
+ * on-demand skills, so every selected skill is folded into one `AGENTS.md` as
20
+ * a delimited `## Skill: <name>` section. The whole set lives inside managed
21
+ * markers, so the writer's merge mode updates only that span on re-runs and
22
+ * never disturbs surrounding user content. Triggering degrades to always-on
23
+ * guidance (flagged to the user elsewhere).
24
+ */
25
+ declare const codexAdapter: Adapter;
26
+
27
+ /**
28
+ * Gemini — import strategy.
29
+ *
30
+ * Gemini concatenates `GEMINI.md` hierarchically and supports `@file.md`
31
+ * imports, so each skill is written to its own modular
32
+ * `.gemini/skills/<name>.md` and referenced by an `@./.gemini/skills/<name>.md`
33
+ * import line. The import lines live inside managed markers in `GEMINI.md`, so
34
+ * re-runs update only that span; the modular skill files are fully tool-owned
35
+ * (overwrite). Skills are always-on — no on-demand triggering.
36
+ */
37
+ declare const geminiAdapter: Adapter;
38
+
39
+ /**
40
+ * Managed-marker helper, shared by the merge-style adapters (Codex, Gemini).
41
+ *
42
+ * Tool-owned content inside files a user may also edit (`AGENTS.md`,
43
+ * `GEMINI.md`) is bounded by managed markers. Only the span between the
44
+ * markers is ever rewritten on a re-run; everything outside is the user's and
45
+ * is preserved verbatim.
46
+ */
47
+ declare const MARKER_START = "<!-- le-restaurant:start -->";
48
+ declare const MARKER_END = "<!-- le-restaurant:end -->";
49
+ /** Notice rendered as the first line inside every managed block. */
50
+ declare const MANAGED_NOTICE = "<!-- Managed by le-restaurant. Do not edit between these markers; re-run the installer to update. -->";
51
+ /** Wrap `inner` content in managed markers, returning a complete block. */
52
+ declare function renderManagedBlock(inner: string): string;
53
+ /** Does `content` already contain a managed block? */
54
+ declare function hasManagedBlock(content: string): boolean;
55
+ /**
56
+ * Merge a freshly rendered managed `block` into `existing` file content.
57
+ *
58
+ * If `existing` already contains a managed span, that span (markers included)
59
+ * is replaced in place. Otherwise the block is appended, separated from any
60
+ * prior content by a blank line. User content outside the markers is never
61
+ * touched, so repeated merges are idempotent for a stable block.
62
+ */
63
+ declare function mergeManagedBlock(existing: string, block: string): string;
64
+
65
+ /**
66
+ * Absolute path to the vendored source skills.
67
+ *
68
+ * This module lives at `src/registry.ts` in dev and `dist/registry.js` once
69
+ * built; in both cases the vendored `skills/` directory sits one level up.
70
+ */
71
+ declare const skillsDir: string;
72
+ /** Absolute path to a vendored skill's source `SKILL.md`. */
73
+ declare function skillSourcePath(name: string): string;
74
+ /** Read one vendored `SKILL.md` and normalize it into a `Skill`. */
75
+ declare function loadSkill(name: string): Skill;
76
+ /** Discover and load every vendored source skill. */
77
+ declare function loadSkills(): Skill[];
78
+
79
+ /** Everything the post-install summary needs to render. */
80
+ interface SummaryInput {
81
+ /** The agent the skills were installed for. */
82
+ agentId: "claude" | "codex" | "gemini";
83
+ /** Human-readable agent label (e.g. "Claude Code"). */
84
+ agentLabel: string;
85
+ /** What the writer did with each file. */
86
+ results: WriteResult[];
87
+ }
88
+ /**
89
+ * Render the post-install summary: the target agent, every file written (with
90
+ * the action taken), and how to use the installed skills — including the
91
+ * always-on caveat for Codex/Gemini.
92
+ */
93
+ declare function renderSummary(input: SummaryInput): string;
94
+
95
+ export { Adapter, MANAGED_NOTICE, MARKER_END, MARKER_START, Skill, type SummaryInput, WriteResult, claudeAdapter, codexAdapter, geminiAdapter, hasManagedBlock, loadSkill, loadSkills, mergeManagedBlock, renderManagedBlock, renderSummary, skillSourcePath, skillsDir };
package/dist/index.js ADDED
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ AGENT_IDS,
4
+ MANAGED_NOTICE,
5
+ MARKER_END,
6
+ MARKER_START,
7
+ buildProgram,
8
+ claudeAdapter,
9
+ codexAdapter,
10
+ defaultConflictPolicy,
11
+ geminiAdapter,
12
+ hasManagedBlock,
13
+ loadSkill,
14
+ loadSkills,
15
+ mergeManagedBlock,
16
+ renderManagedBlock,
17
+ renderSummary,
18
+ resolveFlags,
19
+ resolveSkillSelection,
20
+ run,
21
+ runCli,
22
+ runInstall,
23
+ selectAdapter,
24
+ skillSourcePath,
25
+ skillsDir,
26
+ writeOutputs
27
+ } from "./chunk-5DTTVPAO.js";
28
+ export {
29
+ AGENT_IDS,
30
+ MANAGED_NOTICE,
31
+ MARKER_END,
32
+ MARKER_START,
33
+ buildProgram,
34
+ claudeAdapter,
35
+ codexAdapter,
36
+ defaultConflictPolicy,
37
+ geminiAdapter,
38
+ hasManagedBlock,
39
+ loadSkill,
40
+ loadSkills,
41
+ mergeManagedBlock,
42
+ renderManagedBlock,
43
+ renderSummary,
44
+ resolveFlags,
45
+ resolveSkillSelection,
46
+ run,
47
+ runCli,
48
+ runInstall,
49
+ selectAdapter,
50
+ skillSourcePath,
51
+ skillsDir,
52
+ writeOutputs
53
+ };
54
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/wizard.ts
4
+ import {
5
+ cancel,
6
+ intro,
7
+ isCancel,
8
+ multiselect,
9
+ outro,
10
+ select
11
+ } from "@clack/prompts";
12
+ var AGENT_CHOICES = [
13
+ { value: "claude", label: "Claude Code", hint: "native on-demand skills" },
14
+ { value: "codex", label: "Codex", hint: "merged into AGENTS.md (always-on)" },
15
+ { value: "gemini", label: "Gemini", hint: "@import into GEMINI.md (always-on)" }
16
+ ];
17
+ async function runWizard(available) {
18
+ intro("le-restaurant \u2014 install the brigade of agent skills");
19
+ const agentId = await select({
20
+ message: "Which coding agent are you installing for?",
21
+ options: AGENT_CHOICES.map((c) => ({
22
+ value: c.value,
23
+ label: c.label,
24
+ hint: c.hint
25
+ }))
26
+ });
27
+ if (isCancel(agentId)) {
28
+ cancel("Cancelled.");
29
+ return null;
30
+ }
31
+ const picked = await multiselect({
32
+ message: "Which skills do you want? (space to toggle, enter to confirm)",
33
+ options: available.map((s) => ({
34
+ value: s.name,
35
+ label: s.name,
36
+ hint: s.description
37
+ })),
38
+ required: true
39
+ });
40
+ if (isCancel(picked)) {
41
+ cancel("Cancelled.");
42
+ return null;
43
+ }
44
+ const names = picked;
45
+ const skills = available.filter((s) => names.includes(s.name));
46
+ outro(`Installing ${skills.length} skill(s) for ${agentId}\u2026`);
47
+ return { agentId, skills };
48
+ }
49
+ export {
50
+ runWizard
51
+ };
52
+ //# sourceMappingURL=wizard-4VNJ64P6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/wizard.ts"],"sourcesContent":["import {\n cancel,\n intro,\n isCancel,\n multiselect,\n outro,\n select,\n} from \"@clack/prompts\";\nimport type { Skill } from \"./types.js\";\n\n/** Agents offered in the interactive picker. */\nconst AGENT_CHOICES = [\n { value: \"claude\", label: \"Claude Code\", hint: \"native on-demand skills\" },\n { value: \"codex\", label: \"Codex\", hint: \"merged into AGENTS.md (always-on)\" },\n { value: \"gemini\", label: \"Gemini\", hint: \"@import into GEMINI.md (always-on)\" },\n] as const;\n\n/** The user's choices from the wizard, or null if they cancelled. */\nexport interface WizardResult {\n agentId: \"claude\" | \"codex\" | \"gemini\";\n skills: Skill[];\n}\n\n/**\n * Walk the user through pick-agent → multiselect-skills.\n *\n * This is intentionally thin: all the testable logic (dispatch, validation,\n * summary) lives in pure functions elsewhere. The wizard only orchestrates the\n * interactive prompts and hands back the resolved selection.\n */\nexport async function runWizard(\n available: Skill[],\n): Promise<WizardResult | null> {\n intro(\"le-restaurant — install the brigade of agent skills\");\n\n const agentId = await select({\n message: \"Which coding agent are you installing for?\",\n options: AGENT_CHOICES.map((c) => ({\n value: c.value,\n label: c.label,\n hint: c.hint,\n })),\n });\n if (isCancel(agentId)) {\n cancel(\"Cancelled.\");\n return null;\n }\n\n const picked = await multiselect({\n message: \"Which skills do you want? (space to toggle, enter to confirm)\",\n options: available.map((s) => ({\n value: s.name,\n label: s.name,\n hint: s.description,\n })),\n required: true,\n });\n if (isCancel(picked)) {\n cancel(\"Cancelled.\");\n return null;\n }\n\n const names = picked as string[];\n const skills = available.filter((s) => names.includes(s.name));\n outro(`Installing ${skills.length} skill(s) for ${agentId}…`);\n\n return { agentId: agentId as WizardResult[\"agentId\"], skills };\n}\n"],"mappings":";;;AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAIP,IAAM,gBAAgB;AAAA,EACpB,EAAE,OAAO,UAAU,OAAO,eAAe,MAAM,0BAA0B;AAAA,EACzE,EAAE,OAAO,SAAS,OAAO,SAAS,MAAM,oCAAoC;AAAA,EAC5E,EAAE,OAAO,UAAU,OAAO,UAAU,MAAM,qCAAqC;AACjF;AAeA,eAAsB,UACpB,WAC8B;AAC9B,QAAM,0DAAqD;AAE3D,QAAM,UAAU,MAAM,OAAO;AAAA,IAC3B,SAAS;AAAA,IACT,SAAS,cAAc,IAAI,CAAC,OAAO;AAAA,MACjC,OAAO,EAAE;AAAA,MACT,OAAO,EAAE;AAAA,MACT,MAAM,EAAE;AAAA,IACV,EAAE;AAAA,EACJ,CAAC;AACD,MAAI,SAAS,OAAO,GAAG;AACrB,WAAO,YAAY;AACnB,WAAO;AAAA,EACT;AAEA,QAAM,SAAS,MAAM,YAAY;AAAA,IAC/B,SAAS;AAAA,IACT,SAAS,UAAU,IAAI,CAAC,OAAO;AAAA,MAC7B,OAAO,EAAE;AAAA,MACT,OAAO,EAAE;AAAA,MACT,MAAM,EAAE;AAAA,IACV,EAAE;AAAA,IACF,UAAU;AAAA,EACZ,CAAC;AACD,MAAI,SAAS,MAAM,GAAG;AACpB,WAAO,YAAY;AACnB,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ;AACd,QAAM,SAAS,UAAU,OAAO,CAAC,MAAM,MAAM,SAAS,EAAE,IAAI,CAAC;AAC7D,QAAM,cAAc,OAAO,MAAM,iBAAiB,OAAO,QAAG;AAE5D,SAAO,EAAE,SAA6C,OAAO;AAC/D;","names":[]}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@long.dg/le-restaurant",
3
+ "version": "0.1.0",
4
+ "description": "Install the brigade of agent skills into your project, translated for your coding agent.",
5
+ "type": "module",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "bin": {
10
+ "le-restaurant": "dist/cli.js"
11
+ },
12
+ "main": "./dist/index.js",
13
+ "module": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "files": [
16
+ "dist",
17
+ "skills"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsup",
21
+ "dev": "tsup --watch",
22
+ "test": "vitest run",
23
+ "test:watch": "vitest",
24
+ "typecheck": "tsc --noEmit",
25
+ "check:sync": "vitest run test/check-skills-sync.test.ts"
26
+ },
27
+ "keywords": [
28
+ "claude",
29
+ "codex",
30
+ "gemini",
31
+ "skills",
32
+ "cli"
33
+ ],
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/dglong/le-restaurant.git"
37
+ },
38
+ "homepage": "https://github.com/dglong/le-restaurant#readme",
39
+ "bugs": {
40
+ "url": "https://github.com/dglong/le-restaurant/issues"
41
+ },
42
+ "author": "",
43
+ "license": "MIT",
44
+ "dependencies": {
45
+ "@clack/prompts": "^1.6.0",
46
+ "commander": "^14.0.1",
47
+ "gray-matter": "^4.0.3"
48
+ },
49
+ "devDependencies": {
50
+ "@types/node": "^24.10.1",
51
+ "tsup": "^8.5.0",
52
+ "typescript": "^5.9.3",
53
+ "vitest": "^3.2.4"
54
+ }
55
+ }
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: chef-de-rang
3
+ description: Builds one feature to completion from a single order ticket. Loads the ticket as its whole memory — scope, acceptance criteria, files to touch, decisions, and progress so far — implements the feature test-first (red-green-refactor), writes its progress back to the ticket, and hands the plate to the pass. Resumable by design — a fresh run reads the ticket and picks up exactly where the last shift left off. Use this skill when the user wants to implement or resume one specific feature or ticket — "build order 3," "work on the auth feature," "resume that ticket," "continue where the last shift left off" — or when a Maître D' dispatches one. Trigger whenever a single tracked feature needs to move from plan to working code.
4
+ ---
5
+
6
+ # Chef de Rang
7
+
8
+ The station waiter who owns one table — a single feature — and serves it from order to the pass. **The order ticket is your memory.** You don't own the feature; the *ticket* does. Anything not written to it is forgotten the moment you clock out, so the ticket, not your context, is the source of truth. Serious engineering content; keep the floor flavor to a pinch.
9
+
10
+ Done well = the feature works, its acceptance criteria pass, and the ticket reflects reality closely enough that anyone could take over the table on the next read.
11
+
12
+ ## Load the table first
13
+
14
+ Read the order ticket named in the request (in `<docs-root>/<idea-slug>/service/order-NN-<slug>.md`). Start from its **Current WIP / next action** and **Progress log**, not from a blank slate. Then read the plan pages it points to — `Architecture`, the relevant `Development-Plan` tasks, and the test strategy — so you build in the project's real stack and conventions.
15
+
16
+ ## Check the order is ready
17
+
18
+ Before cooking, confirm the **Definition of Ready**: acceptance criteria and touches are present, and there's no blocking unknown. If the order is vague or under-specified, don't guess scope into existence — note what's missing on the ticket and kick it back to the Maître D'. A bad order cooked confidently is worse than one sent back.
19
+
20
+ ## Stay at your table
21
+
22
+ Work only within the ticket's **Touches**. Shared surfaces the plan foresaw — routing, shared services/types, the component library, build config — have already been resolved by the Maître D' at seating, so use them as given. If you hit a shared surface that *wasn't* foreseen and genuinely needs changing, don't quietly do it: record it as an escalation on the ticket and flag the Maître D'. Keeping the dining room coherent is the manager's job, and a stray shared-surface edit produces drift the manager can't see.
23
+
24
+ ## Serve the course
25
+
26
+ 1. **Set your station.** Turn the ticket into a short, ordered list of slices, each mapping to one or more **Done when** criteria. Use Plan Mode if it's available. Confirm the test runner exists — if the repo has none yet (an early course), stand one up *now*, before any cooking, and note it on the ticket. No runner, no service.
27
+ 2. **Red — write the failing test first.** For the next slice, write a test that asserts its acceptance criterion, run it, and watch it fail for the right reason. The taste comes *before* the dish. **Don't write implementation before a failing test exists** — if you catch yourself having cooked first, delete that code and start from the test. A failing test is the only kind of progress that counts here.
28
+ 3. **Green — cook the minimum to pass.** Write the smallest change that makes the test pass, in the plan's stack and conventions. Run the test; watch it go green. Don't gold-plate beyond what the test demands.
29
+ 4. **Refactor — tidy with the tests green.** Clean up names, duplication, and shape while the suite stays green. Re-run after.
30
+ 5. **Loop.** Repeat red → green → refactor for each slice until every **Done when** criterion is covered by a passing test. Never mark something passing you haven't actually run.
31
+ 6. **Write the ticket back.** Append to the **Progress log**, update **Current WIP / next action**, record any **Decisions**, list **Tests**. This is the step that survives a crash — do it as you go, not "later."
32
+
33
+ ## Hand off to the pass
34
+
35
+ When the criteria are met and tests are green, summarize what changed and hand back to the Maître D' for the pass — it verifies **Done when**, a clean whole-app build, and no regressions. **You don't mark your own plate Served**; the pass does. If you run out of road before finishing, leave **Current WIP / next action** precise enough that the next chef de rang resumes in a single read.
36
+
37
+ ## Resuming is the normal case
38
+
39
+ A re-run isn't an exception — it's the expected path. Read the ticket, trust **Current WIP / next action**, then verify the log against the actual code. If they disagree, the code wins — note the correction on the ticket and continue. Don't restart work the log says is already done without checking it.
40
+
41
+ ## Tempo
42
+
43
+ Same toggle as the suite: "service mode" for terse, "prep" / "normal" to relax. Never skip writing the ticket back to save words — that's the one step that can't be regenerated.
44
+
45
+ ## Anti-patterns
46
+
47
+ - Working from the request alone without loading the ticket.
48
+ - Cooking a vague order instead of kicking it back to the Maître D'.
49
+ - Touching shared surfaces outside the ticket without escalating.
50
+ - Cooking before the taste — writing implementation before a failing test exists. Delete it and start from the test.
51
+ - Marking tests passing without running them.
52
+ - Finishing a shift without writing progress back — the next shift loses everything.
53
+ - Marking your own plate Served — that's the pass's call.
54
+ - Gold-plating past the ticket's acceptance criteria.
@@ -0,0 +1,131 @@
1
+ ---
2
+ name: maitre-d
3
+ description: Front-of-house orchestrator for building a planned project. Reads a mise-en-place plan wiki, cuts it into per-feature "order tickets," and runs service one course at a time — fires the next ready ticket, sends a chef-de-rang to build it, then works the pass to verify the feature is done before moving on. The natural follow-up once a build plan exists. Use this skill whenever the user wants to start or continue implementing a planned project — "let's build this," "start development," "what's next to build," "continue the build," "run the implementation," "work through the plan" — or hands over a plan folder and asks to execute it. Trigger it even without the word "build" when the user is moving from a finished plan toward shipping code.
4
+ ---
5
+
6
+ # Maître D'
7
+
8
+ Front-of-house manager for the build. The kitchen (`sous-chef`, `mise-en-place`) settled *what* and *how*; the Maître D' runs **service** — turning the plan into features served one course at a time, and checking every plate at the pass before it leaves the floor. Serious engineering content; keep the floor flavor to a pinch.
9
+
10
+ Done well = features ship from the plan in dependency order, each verified against its acceptance criteria, with state that survives any interruption.
11
+
12
+ ## Default: one course at a time
13
+
14
+ Fire a single ticket, see it through the pass, mark it **Served**, then fire the next. Sequential service is slower but precise — no parallel writes to trip over, every plate checked before the next is started, and the whole floor resumable from disk. Parallel brigade service is intentionally out of scope for now; don't fake it.
15
+
16
+ ## Check the kitchen first
17
+
18
+ Find the plan: scan the docs root for a `<slug>/plan/` wiki (`mise-en-place` output).
19
+ - **No plan, or only a `recipe.md`** → don't improvise service. Send it back to the kitchen — suggest `mise-en-place` to plan the build first.
20
+ - **Plan exists but the fundamentals are still `Draft`** (no real scope or stack) → flag it and offer to firm those up via `mise-en-place` before firing anything.
21
+
22
+ ## The service layer
23
+
24
+ Everything lives in `<docs-root>/<idea-slug>/service/`, beside `plan/` and `recipe.md`, so one idea stays in one folder:
25
+ - `Board.md` — the floor at a glance: every ticket, its status, and the course order.
26
+ - `order-NN-<slug>.md` — one ticket per feature/phase. The unit of work **and** its memory.
27
+
28
+ ## Flow
29
+
30
+ ### 1. Seat the room (first run)
31
+ From `Phases.md` and `Development-Plan.md`, cut the work into tickets — usually one per phase or coherent feature. Seed each ticket straight from the plan: its tasks, their **Done when** criteria, and **Touches** list. Write `Board.md` with the course order, respecting dependencies.
32
+
33
+ **Resolve shared surfaces up front.** Scan every ticket's **Touches** for shared surfaces the plan already names — routing, shared services/types, the component library, build config. Don't leave these to be discovered mid-cook: pre-own or pre-stub them now (e.g. register a route placeholder, declare a shared interface) so a chef-de-rang is never blocked halfway through a course by something the plan already foresaw. Only genuinely *unforeseen* shared needs should surface as a mid-cook escalation.
34
+
35
+ Show the breakdown — tickets, course order, and the shared surfaces you're holding — and confirm before firing.
36
+
37
+ ### 2. Own the dining room
38
+ Some surfaces are shared — routing, shared services/types, the component library, build config. The Maître D' holds these. When a ticket needs a cross-cutting change, it's made deliberately (at seating or at the pass), not buried inside one feature. Note shared touches on the ticket so nothing collides. When a shared decision is non-obvious, log it in the plan's `Decisions & Glossary` (ADR log) so the *why* outlives the build — a ticket's local **Decisions** are fine for feature-scoped choices, but cross-cutting ones graduate to the plan.
39
+
40
+ ### 3. Fire the next ticket
41
+ Pick the next ticket whose dependencies are **Served** and whose **Definition of Ready** is met (criteria + touches present, no blocking unknown). If it isn't ready, fix the ticket first — never fire a vague order. Set Status → **Firing**, then send a `chef-de-rang` with the ticket path and the plan pages it points to.
42
+
43
+ ### 4. Work the pass
44
+ When the chef-de-rang hands back, check the plate before it ships. **Proof, not claims** — a plate is passed on shown command output, never on a "tests pass" assertion. For each check, *run the command yourself*, read the actual output, and only then judge:
45
+ - Every **Done when** criterion on the ticket is verifiably met.
46
+ - The tests the plan's test strategy names for this work pass — run them, don't take it on faith.
47
+ - The whole app still builds and the existing suite stays green — no regressions.
48
+ - Lint is clean.
49
+
50
+ Record the verification on the ticket's **Tests** section: the commands run and their result (e.g. `vitest run → 12 passed`, `tsc --noEmit → clean`). An "I'm done" with no command output behind it is not done.
51
+
52
+ A plate that fails goes back to the same ticket with notes; Status stays **Firing**, it does not ship. Only when the output proves it: Status → **Served**, update `Board.md`, fire the next.
53
+
54
+ ### 5. Last orders
55
+ When every ticket is Served, say so plainly and point at what the plan parked under "beyond MVP." Don't invent new courses.
56
+
57
+ ## The order ticket
58
+
59
+ One per feature, at `service/order-NN-<slug>.md`. This is the durable memory — a chef-de-rang owns the *ticket*, not the feature, so anything not written here is lost between shifts.
60
+
61
+ ```markdown
62
+ # Order NN · <feature name> · Status: To fire
63
+ **Covers:** <plan phase / tasks this ticket delivers>
64
+ **Depends on:** <other tickets, or none>
65
+
66
+ ## Done when (acceptance — checked at the pass)
67
+ - [ ] <criterion, observable>
68
+
69
+ ## Touches
70
+ - <files / areas> · shared surfaces: <routing / services / types, or none>
71
+
72
+ ## Progress log (append-only, newest last)
73
+ - <date> — <what was done>
74
+
75
+ ## Current WIP / next action
76
+ - <the single resume pointer: exactly what to do next>
77
+
78
+ ## Decisions
79
+ - <choice · why>
80
+
81
+ ## Blockers / escalations
82
+ - <anything needing the Maître D' or the user; or none>
83
+
84
+ ## Tests
85
+ - <which tests written / passing>
86
+ ```
87
+
88
+ ## Board.md
89
+
90
+ ```markdown
91
+ # <Idea name> — Service Board
92
+ **Plan:** ../plan/Home.md
93
+
94
+ ## Course order
95
+ 1. [[order-01-<slug>]] — <feature>
96
+ 2. [[order-02-<slug>]] — <feature> (depends on 01)
97
+
98
+ | # | Feature | Status | Depends on |
99
+ |---|---------|--------|------------|
100
+ | 01 | <feature> | To fire | — |
101
+
102
+ Status flow: To fire → Firing → At the pass → Served ( 86'd = dropped )
103
+ ```
104
+
105
+ ## Resuming
106
+
107
+ A re-run is normal. Don't re-decompose — read `Board.md` for where service stands, read the in-flight ticket's **Current WIP / next action**, and continue from there.
108
+
109
+ ## Re-seat when the plan moves
110
+
111
+ Tickets copy **Done when** and **Touches** from the plan at seating, so a later `mise-en-place` refine can leave them stale. Before firing on a resumed build, check whether `plan/` changed since the tickets were cut. If it did, **reconcile**: update the not-yet-Served tickets to match the new plan (criteria, touches, course order), leave **Served** tickets alone but note any drift they now carry, and confirm the reconciled board before firing. Never fire a ticket you know contradicts the current plan.
112
+
113
+ ## Tempo
114
+
115
+ Same toggle as the rest of the suite: "service mode" / "in the weeds" for terse, "prep" / "family meal" / "normal" to relax. It trims the talk, never the pass checks.
116
+
117
+ ## Right-size
118
+
119
+ A weekend tool might be two or three tickets with no shared-surface ceremony. A multi-feature app earns the full floor. Don't seat twelve tables for a one-course meal.
120
+
121
+ ## Anti-patterns
122
+
123
+ - Improvising service with no plan — send it to `mise-en-place`.
124
+ - Firing more than one ticket at a time (not supported yet — keep it sequential).
125
+ - Passing a plate that fails its **Done when** or breaks the app build.
126
+ - Marking a plate Served on a claim of green instead of shown command output.
127
+ - Letting a feature rewrite shared routing/services/types on its own.
128
+ - Tickets without acceptance criteria — a vague order can't be cooked or checked.
129
+ - Leaving a plan-named shared surface to be discovered mid-cook instead of resolving it at seating.
130
+ - Firing tickets that contradict a plan that changed under you — reconcile first.
131
+ - Re-decomposing from scratch on resume instead of reading the board.