@kamkom/awb-mcp 0.0.1

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/.ai/backlog.md ADDED
@@ -0,0 +1,435 @@
1
+ # AWB MCP — Product Backlog
2
+
3
+ Stories and acceptance criteria derived from [.ai/prd.md](.ai/prd.md), grouped by milestone.
4
+
5
+ ---
6
+
7
+ ## v0.1 — MVP core
8
+
9
+ ### Story 1: MCP server runs in VS Code over stdio
10
+
11
+ **User story**
12
+
13
+ - As a developer using VS Code with an MCP-capable agent/chat client
14
+ - I want to run the AWB MCP server as a stdio-based server so that I can use its tools from my editor without extra infrastructure
15
+
16
+ **Acceptance criteria**
17
+
18
+ - Given a Windows or macOS machine with Node.js and VS Code configured for MCP
19
+ - When I install the package (e.g. `npm i -g awb-mcp`) and add the server to my MCP settings with the command path
20
+ - Then the server starts over stdio, responds to MCP protocol messages, and exposes tools (e.g. list_tools returns AWB tools)
21
+
22
+ **Edge cases**
23
+
24
+ - Linux: document as optional; no hard requirement to fix Linux-only issues in v0.1
25
+ - Invalid or missing `rootPath`: server returns clear error, does not crash
26
+ - Server receives malformed or unexpected JSON: responds with protocol error, does not exit ungracefully
27
+
28
+ **Technical considerations**
29
+
30
+ - Use `@modelcontextprotocol/sdk` StdioServerTransport; single executable entrypoint (e.g. `awb-mcp` or `npx awb-mcp`)
31
+ - No network listener; stdio only
32
+ - Package `bin` in package.json so `awb-mcp` is on PATH after global install
33
+
34
+ **Design notes**
35
+
36
+ - Keep VS Code MCP config minimal: only the command (e.g. `awb-mcp` or `npx awb-mcp`). No required env vars or args for basic use
37
+
38
+ ---
39
+
40
+ ### Story 2: plan_bootstrap returns a deterministic plan with full contents and warnings
41
+
42
+ **User story**
43
+
44
+ - As a user in an agentic workflow
45
+ - I want to call `awb.plan_bootstrap` with a repo root and options so that I get a clear, deterministic plan of file operations (create/update/skip) with full file contents and warnings for existing files
46
+
47
+ **Acceptance criteria**
48
+
49
+ - Given a valid `rootPath` (existing directory) and optional `options` (mode, includeReadmeProposal, overwritePolicy, files allowlist)
50
+ - When I call the tool `awb.plan_bootstrap` with those inputs
51
+ - Then I receive a response containing: a unique `planId`; an `operations[]` array with for each path: `path`, `action` (CREATE | UPDATE_MANAGED | SKIP), `content` when action is CREATE or UPDATE_MANAGED, `reason` (especially for SKIP), and `warnings[]`; optional `repoHints` and `readmeProposal` when requested
52
+
53
+ - Given a path that does not exist on disk
54
+ - When the plan is computed
55
+ - Then that path has action CREATE and includes full static template content
56
+
57
+ - Given a path that exists and has no AWB managed block and overwritePolicy is not "overwrite"
58
+ - When the plan is computed
59
+ - Then that path has action SKIP, a reason, and a warning that the file exists and is not being overwritten
60
+
61
+ **Edge cases**
62
+
63
+ - `rootPath` is a file or non-existent: return clear error, no plan
64
+ - `files` allowlist: only those paths appear in operations; others are omitted
65
+ - Empty repo (no existing scaffold): all scaffold paths are CREATE
66
+ - overwritePolicy `"skip"`: existing files always SKIP (even if they contain a managed block). `"managed-only"`: SKIP unless file has AWB managed block (then UPDATE_MANAGED)
67
+
68
+ **Technical considerations**
69
+
70
+ - planId: UUID or timestamp-based unique string; same inputs produce same plan content (deterministic)
71
+ - Read only: directory listing and file existence (and optionally small config files for hints). No writes in plan
72
+ - Input schema: validate rootPath (string), options optional with zod or equivalent
73
+
74
+ **Design notes**
75
+
76
+ - Static mode default: all templates are prefilled content from server bundle. No LLM calls in server
77
+
78
+ ---
79
+
80
+ ### Story 3: apply_bootstrap applies an approved plan with atomic writes and conflict handling
81
+
82
+ **User story**
83
+
84
+ - As a user who has reviewed a bootstrap plan
85
+ - I want to call `awb.apply_bootstrap` with the planId and operations so that only approved changes are written to disk using atomic writes and explicit conflict handling
86
+
87
+ **Acceptance criteria**
88
+
89
+ - Given a valid `rootPath`, the `planId` from a prior `plan_bootstrap` call, and the same `operations[]` (or server-validated subset)
90
+ - When I call `awb.apply_bootstrap` without dryRun
91
+ - Then each CREATE/UPDATE_MANAGED operation is written to disk; each SKIP is not written; the response includes `results[]` with per-path `status` (WRITTEN | SKIPPED | FAILED) and `details`
92
+
93
+ - Given applyOptions.dryRun is true
94
+ - When I call `awb.apply_bootstrap`
95
+ - Then no files are modified and the response describes what would have been done (e.g. same result structure with WRITTEN/SKIPPED/FAILED as if applied)
96
+
97
+ - Given writeMode is "atomic" (default)
98
+ - When a file is written
99
+ - Then content is written to a temp file in the same directory and then renamed into place so that partial writes are not visible
100
+
101
+ **Edge cases**
102
+
103
+ - planId or operations do not match a recent plan: server may validate and return FAILED for mismatches if failOnConflict is true
104
+ - Disk full or permission error: that path returns status FAILED with details; other paths still attempted where possible
105
+ - apply_bootstrap called with operations from a different rootPath: server validates and rejects or marks FAILED
106
+
107
+ **Technical considerations**
108
+
109
+ - Atomic write: write to `*.tmp` (or similar) in same directory, fs.rename to final path
110
+ - Idempotency: applying the same plan twice with same overwrite policy should yield same outcome (second run no unexpected changes)
111
+ - applyOptions: dryRun, writeMode "atomic", failOnConflict (default true)
112
+
113
+ **Design notes**
114
+
115
+ - Server does not re-read disk to “re-plan”; it applies the operations as given, enforcing overwrite policy per operation
116
+
117
+ ---
118
+
119
+ ### Story 4: Overwrite and idempotency — SKIP by default, managed-block and explicit overwrite
120
+
121
+ **User story**
122
+
123
+ - As a user running bootstrap in a repo that may already have some files
124
+ - I want the server to never overwrite my content unless a file is under AWB management or I explicitly allow overwrite so that I do not lose work
125
+
126
+ **Acceptance criteria**
127
+
128
+ - Given a target path where the file does not exist
129
+ - When the plan is computed and applied
130
+ - Then the action is CREATE and the file is created
131
+
132
+ - Given a target path where the file exists and does not contain the AWB managed block marker
133
+ - When overwritePolicy is "skip" or "managed-only" (default)
134
+ - Then the action is SKIP, reason and warnings are set, and the file is not modified on apply
135
+
136
+ - Given a target path where the file exists and contains the AWB managed block marker
137
+ - When overwritePolicy is "managed-only" (default)
138
+ - Then the action is UPDATE_MANAGED, content is the new managed block content, and on apply only the managed section is updated (or full file replace per implementation)
139
+
140
+ - Given the user passes overwrite: true for a specific file (or overwritePolicy "overwrite")
141
+ - When the plan is computed and applied
142
+ - Then that path can be CREATE/UPDATE and existing content may be replaced; user has explicitly opted in
143
+
144
+ **Edge cases**
145
+
146
+ - Managed block marker missing or malformed: treat as unmanaged, SKIP unless overwrite
147
+ - Partial file (e.g. only start marker): define behavior (e.g. SKIP or treat as managed); document
148
+
149
+ **Technical considerations**
150
+
151
+ - Define a single, documented AWB managed block format (e.g. `<!-- AWB MANAGED BEGIN -->` … `<!-- AWB MANAGED END -->` or similar)
152
+ - plan_bootstrap decides CREATE | UPDATE_MANAGED | SKIP per path based on existence and overwrite policy; apply_bootstrap enforces the same policy
153
+
154
+ **Design notes**
155
+
156
+ - Safe-by-default: “plan first, then apply” with clear SKIP behavior and no blind overwrites
157
+
158
+ ---
159
+
160
+ ### Story 5: Baseline scaffold — static templates for .github and AGENTS.md
161
+
162
+ **User story**
163
+
164
+ - As a user bootstrapping a repo for agentic workflows
165
+ - I want the server to generate a fixed set of high-quality, generic template files so that my repo has a standard structure for instructions, prompts, and agents without me creating them by hand
166
+
167
+ **Acceptance criteria**
168
+
169
+ - Given a successful `plan_bootstrap` in static mode with no files allowlist (or allowlist including the baseline paths)
170
+ - When the plan is generated
171
+ - Then operations include the full baseline tree: `.github/copilot-instructions.md`, `.github/instructions/general.instructions.md`, `.github/instructions/testing.instructions.md`, `.github/prompts/readme.prompt.md`, `.github/prompts/onboarding.prompt.md`, `.github/prompts/change-summary.prompt.md`, `.github/agents/architect.agent.md`, `.github/agents/implementer.agent.md`, `.github/agents/reviewer.agent.md`, `AGENTS.md`
172
+
173
+ - Given the template content returned in the plan
174
+ - When I read it
175
+ - Then it is generic, high-signal, and safe: emphasizes “don’t guess,” “prefer repo scripts,” “write tests,” “avoid destructive commands”
176
+ - And placeholders (e.g. `{{packageManager}}`, `{{testCommand}}`) are minimal and optional; may be left as defaults in v0.1
177
+
178
+ **Edge cases**
179
+
180
+ - `files` allowlist provided: only listed paths that belong to the baseline appear in operations
181
+ - Paths outside repo root (e.g. `..` in path): server rejects or normalizes
182
+
183
+ **Technical considerations**
184
+
185
+ - Templates shipped as static files in the server package (e.g. in a `templates/` directory); no runtime fetch
186
+ - Use path.join(rootPath, ...) for all paths; normalize and reject paths not under rootPath
187
+
188
+ **Design notes**
189
+
190
+ - Templates work for any repo/stack; no stack-specific logic required in v0.1
191
+
192
+ ---
193
+
194
+ ### Story 6: Safety — no network, no shell, minimal reads, atomic writes
195
+
196
+ **User story**
197
+
198
+ - As a user running the AWB MCP server
199
+ - I want it to never perform network calls, shell execution, or broad filesystem reads so that I can trust it in sensitive or offline environments
200
+
201
+ **Acceptance criteria**
202
+
203
+ - Given the server is running
204
+ - When any tool is invoked
205
+ - Then the server does not open outbound network connections or spawn shell/child processes
206
+
207
+ - Given plan_bootstrap or apply_bootstrap is called
208
+ - When the server needs to check files
209
+ - Then it only checks existence of target paths and, if needed, reads small config files (e.g. package.json) for hints; it does not read arbitrary or sensitive paths beyond what is documented
210
+
211
+ - Given apply_bootstrap writes a file
212
+ - When writeMode is atomic
213
+ - Then content is written to a temp file and then renamed; no partial content is visible at the final path on failure
214
+
215
+ - Given a file already exists and policy says SKIP
216
+ - When apply_bootstrap runs
217
+ - Then the user sees clear warnings in the plan and the apply result reflects SKIPPED with a reason
218
+
219
+ **Edge cases**
220
+
221
+ - Symlinks: document whether we follow or reject; prefer not following symlinks outside rootPath
222
+ - Very long paths or huge files: avoid reading large files; existence check only where sufficient
223
+
224
+ **Technical considerations**
225
+
226
+ - No require('child_process') or fetch/axios; no external HTTP
227
+ - Atomic write: temp file in same directory as target, then rename
228
+ - Explicit allowlist of files that may be read for hints (e.g. package.json, pyproject.toml) in v0.2
229
+
230
+ **Design notes**
231
+
232
+ - Security section of PRD is non-negotiable for MVP; tests or checklist can enforce “no network, no shell”
233
+
234
+ ---
235
+
236
+ ### Story 7: Early deployment and validation — works on other computers
237
+
238
+ **User story**
239
+
240
+ - As a maintainer or a user trying the package on a clean machine
241
+ - I want a clear packaging, install, and validation flow so that we can confirm the server runs on other computers (Windows and macOS) and works with VS Code MCP
242
+
243
+ **Acceptance criteria**
244
+
245
+ - Given the project is built and published (e.g. to npm as `awb-mcp` or installed from tarball)
246
+ - When another user on a clean Windows or macOS machine runs `npm i -g awb-mcp` (or equivalent)
247
+ - Then the install completes and the `awb-mcp` (or documented) command is on PATH
248
+
249
+ - Given the server is installed globally
250
+ - When the user adds the server to VS Code MCP settings (e.g. in settings.json or Cursor MCP config) with only the command path
251
+ - Then VS Code/Cursor can start the server over stdio and list tools
252
+
253
+ - Given the server is running in VS Code
254
+ - When the user (or a script) invokes `awb.plan_bootstrap` with a valid repo root
255
+ - Then the tool returns a valid plan (planId, operations array, no crash)
256
+
257
+ - Given a release checklist exists (in repo or docs)
258
+ - When preparing a release
259
+ - Then the checklist includes: build, pack or publish, install on clean Windows, install on clean macOS, configure VS Code MCP, run smoke test (plan_bootstrap + optional apply_bootstrap in a temp dir)
260
+
261
+ **Edge cases**
262
+
263
+ - Node version: document minimum (e.g. Node 18+); package.json engines optional
264
+ - npx vs global install: both documented; smoke test covers at least one
265
+
266
+ **Technical considerations**
267
+
268
+ - package.json: `bin` entry for the executable; `main` or type module and entrypoint that runs the MCP server
269
+ - Build step that produces runnable code (e.g. tsc + node, or tsx in bin); ensure dependencies are bundled or declared so install is self-contained
270
+ - Optional: simple smoke script (e.g. node -e "call MCP list_tools") or doc that says “run plan_bootstrap from client”
271
+
272
+ **Design notes**
273
+
274
+ - Early deployment is crucial: first milestone should include “works on another machine” validation; checklist prevents regressions
275
+
276
+ ---
277
+
278
+ ## v0.2 — Repo hints and README proposal
279
+
280
+ ### Story 8: detect_repo_hints and placeholder fill
281
+
282
+ **User story**
283
+
284
+ - As a user with an existing repo (e.g. with package.json or pyproject.toml)
285
+ - I want the server to run lightweight heuristics to detect hints (e.g. package manager, test command) so that bootstrap templates can be filled with sensible defaults without deep stack detection
286
+
287
+ **Acceptance criteria**
288
+
289
+ - Given a repo root that contains one or more of: package.json, lockfiles (package-lock.json, yarn.lock, pnpm-lock.yaml), pyproject.toml
290
+ - When `awb.detect_repo_hints` (or plan_bootstrap with hint detection) runs
291
+ - Then the result includes a small `repoHints` object with fields such as preferred package manager, test command placeholder, or similar; no heavy or framework-specific detection
292
+
293
+ - Given repoHints are available
294
+ - When plan_bootstrap returns template content
295
+ - Then optional placeholders (e.g. `{{packageManager}}`, `{{testCommand}}`) are replaced with hint values where possible; otherwise left as safe defaults
296
+
297
+ - Given a repo with no known config files
298
+ - When detect runs
299
+ - Then repoHints is empty or has defaults; no error
300
+
301
+ **Edge cases**
302
+
303
+ - Multiple lockfiles: define precedence (e.g. pnpm > yarn > npm) and document
304
+ - Malformed package.json: do not throw; return partial or empty hints
305
+ - Must not become a “stack detection framework”: only a small, fixed set of heuristics
306
+
307
+ **Technical considerations**
308
+
309
+ - detect_repo_hints: read only package.json, pyproject.toml, lockfile presence; no install or parse of dependencies
310
+ - Keep logic in one place; call from plan_bootstrap when options request hints
311
+
312
+ **Design notes**
313
+
314
+ - v0.2 scope: minimal placeholders; avoid scope creep into full stack detection
315
+
316
+ ---
317
+
318
+ ### Story 9: Optional README proposal (separate file or patch, no overwrite by default)
319
+
320
+ **User story**
321
+
322
+ - As a user who has an existing README.md
323
+ - I want the server to optionally suggest README content or a patch (e.g. README.agentic.md or a patch output) so that I can choose whether to integrate it without ever overwriting README.md by default
324
+
325
+ **Acceptance criteria**
326
+
327
+ - Given plan_bootstrap is called with includeReadmeProposal: false (default)
328
+ - When the plan is returned
329
+ - Then no readmeProposal field or README-related write is included; README.md is never in operations as an overwrite
330
+
331
+ - Given plan_bootstrap is called with includeReadmeProposal: true
332
+ - When the plan is returned
333
+ - Then the response includes a readmeProposal (e.g. suggested content or a separate file path like README.agentic.md or a patch); README.md is still not in operations unless user explicitly opts in elsewhere
334
+ - And the client (LLM/user) decides whether to apply any README changes
335
+
336
+ - Given the server suggests README content
337
+ - When the suggestion is returned
338
+ - Then it is clearly a proposal (e.g. “suggested addition” or separate file); default behavior never modifies README.md
339
+
340
+ **Edge cases**
341
+
342
+ - No README.md exists: readmeProposal can still suggest initial README content
343
+ - Very long README: server may suggest a patch or appendix only; avoid loading huge files unnecessarily
344
+
345
+ **Technical considerations**
346
+
347
+ - readmeProposal: string content or { path, content } for a separate file; optional patch format
348
+ - If README.md is ever in operations, it is only when overwritePolicy or explicit overwrite is set and documented
349
+
350
+ **Design notes**
351
+
352
+ - Narrow MVP: README handling is optional and safe-by-default; no automatic overwrite
353
+
354
+ ---
355
+
356
+ ## v0.3 — Prompt mode and optional resources (if needed)
357
+
358
+ ### Story 10: Prompt mode — server returns prompts and skeletons, client LLM refines
359
+
360
+ **User story**
361
+
362
+ - As a user who wants AI-generated bootstrap content tailored to my repo
363
+ - I want the server to support a “prompt” mode where it returns prompts and minimal skeleton files so that my client LLM (e.g. Copilot) can refine content before I apply it, without the server calling any external LLM
364
+
365
+ **Acceptance criteria**
366
+
367
+ - Given plan_bootstrap is called with mode: "prompt"
368
+ - When the plan is computed
369
+ - Then the server returns either (a) prompts plus minimal skeleton file content, or (b) static templates with a note that the client LLM may refine them before apply
370
+ - And the server does not call any third-party LLM API; any generation is done by the client using the user’s model
371
+
372
+ - Given the client has refined or generated content
373
+ - When the user calls apply_bootstrap with the modified plan/operations
374
+ - Then the server applies the provided content with the same overwrite and atomic-write rules as static mode
375
+
376
+ **Edge cases**
377
+
378
+ - mode "prompt" with no client LLM: user can still apply skeletons as-is
379
+ - Mixed mode (some static, some prompt): v0.3 can define per-file or global; keep simple
380
+
381
+ **Technical considerations**
382
+
383
+ - No fetch to OpenAI/Anthropic/etc. in server; prompts are strings returned to client
384
+ - Client is responsible for calling LLM and passing back refined content in the plan/operations
385
+
386
+ **Design notes**
387
+
388
+ - MVP: prompt mode is optional; static mode remains default and primary
389
+
390
+ ---
391
+
392
+ ### Story 11: Optional MCP prompts and resources for client workflows
393
+
394
+ **User story**
395
+
396
+ - As a user or client LLM
397
+ - I want to discover bootstrap-related prompts and read template resources via MCP so that I can run guided workflows (e.g. “bootstrap_agentic_repo”) or preview templates without calling tools
398
+
399
+ **Acceptance criteria**
400
+
401
+ - Given the server supports optional Prompts
402
+ - When the client lists prompts
403
+ - Then at least one prompt is available (e.g. bootstrap_agentic_repo) that instructs the client to call plan_bootstrap, review output, then call apply_bootstrap
404
+
405
+ - Given the server supports optional Resources
406
+ - When the client requests a resource URI (e.g. awb://templates/copilot-instructions.md or awb://templates/AGENTS.md)
407
+ - Then the server returns read-only template content for that path
408
+
409
+ - Given tools already return full template content in plan_bootstrap
410
+ - When implementing resources
411
+ - Then resources are optional; same content can be exposed via tools only
412
+
413
+ **Edge cases**
414
+
415
+ - Client does not support prompts/resources: server still works with tools only
416
+ - Unknown resource URI: return 404 or equivalent error
417
+
418
+ **Technical considerations**
419
+
420
+ - MCP Prompts: register with the SDK; bootstrap_agentic_repo and optionally draft_readme_patch
421
+ - MCP Resources: register template URIs; resolve to static files from package
422
+
423
+ **Design notes**
424
+
425
+ - v0.3 “only if needed”; if ship is faster without resources, tools-only is acceptable
426
+
427
+ ---
428
+
429
+ ## Coverage and consistency
430
+
431
+ - All PRD MVP goals and tool contracts (plan_bootstrap, apply_bootstrap, overwrite rules, baseline scaffold, safety, distribution) are covered by v0.1 stories.
432
+ - v0.2 covers detect_repo_hints and README proposal.
433
+ - v0.3 covers prompt mode and optional prompts/resources.
434
+ - Early deployment (Story 7) is part of v0.1 so that “works on other computers” is validated before or with the first usable release.
435
+ - Acceptance criteria are written in Given/When/Then form and are testable (manual or automated).
package/.ai/prd.md ADDED
@@ -0,0 +1,215 @@
1
+ # PRD — Agentic Bootstrap MCP Server (working name)
2
+
3
+ ## 1) Summary
4
+
5
+ A minimal **MCP server** that helps bootstrap an “agentic-ready” repository by **creating a small set of standard files** (e.g., `.github/copilot-instructions.md`, prompts, agents, `AGENTS.md`). The server provides **static, high-quality starter templates** and (optionally) supports **prompt-driven generation** via the client’s LLM (e.g., VS Code Copilot). The goal is to ship quickly with a narrow scope.
6
+
7
+ ## 2) Goals
8
+
9
+ **MVP goals**
10
+
11
+ - Provide an MCP server usable from **VS Code** (Windows + macOS; Linux optional).
12
+ - Generate and/or write a baseline file set that makes a repo immediately usable with agentic workflows.
13
+ - Keep behavior **safe-by-default**: _plan first_, then apply changes with clear overwrite rules.
14
+ - Templates can be:
15
+ - **Static** (prefilled content shipped with the server), and/or
16
+ - **Prompt-driven** (server supplies prompts; the client LLM produces content).
17
+
18
+ **Non-goals (MVP)**
19
+
20
+ - No remote downloads / resource pack syncing
21
+ - No shell/git command execution “recipes”
22
+ - No PR creation or GitHub integration
23
+ - No deep stack detection or extensive customization packs
24
+ - No background automation
25
+
26
+ ## 3) Target users & environment
27
+
28
+ - Users working in **VS Code** with an MCP-capable agent/chat client.
29
+ - Primary platforms: **Windows + macOS** (Linux optional).
30
+ - Works in any repo/stack (templates are generic and useful everywhere).
31
+
32
+ ## 4) Product shape: MCP server capabilities
33
+
34
+ The MCP server exposes **Tools**, optional **Prompts**, and optional **Resources**:
35
+
36
+ ### Tools (MVP)
37
+
38
+ 1. `awb.plan_bootstrap`
39
+
40
+ - Purpose: compute a deterministic plan of file operations (create/update/skip).
41
+ - Output includes full file contents (static templates), plus warnings when files already exist.
42
+
43
+ 2. `awb.apply_bootstrap`
44
+
45
+ - Purpose: apply a previously returned plan to disk (atomic writes, idempotent behavior).
46
+ - Server enforces overwrite policy; never blindly clobbers existing user content unless explicitly allowed.
47
+
48
+ _(Optional but still “small”)_ 3) `awb.detect_repo_hints`
49
+
50
+ - Purpose: very lightweight heuristics (presence of lockfiles, `package.json`, `pyproject.toml`, etc.) to fill small placeholders like “preferred package manager” in templates.
51
+ - Must not turn into full “stack detection framework.”
52
+
53
+ ### Prompts (MVP, optional)
54
+
55
+ - `bootstrap_agentic_repo`: a prompt that instructs the client LLM to call `plan_bootstrap`, review output, then call `apply_bootstrap`.
56
+ - `draft_readme_patch`: optional prompt for the client LLM to propose a README patch based on existing README + repo hints.
57
+
58
+ ### Resources (MVP, optional)
59
+
60
+ - Expose template files as MCP “resources” (read-only), e.g.:
61
+ - `awb://templates/copilot-instructions.md`
62
+ - `awb://templates/AGENTS.md`
63
+ This is optional if your tools already return templates.
64
+
65
+ ## 5) Baseline scaffold (MVP)
66
+
67
+ The server supports generating the following tree:
68
+
69
+ ```
70
+ .github/
71
+ copilot-instructions.md
72
+ instructions/
73
+ general.instructions.md
74
+ testing.instructions.md
75
+ prompts/
76
+ readme.prompt.md
77
+ onboarding.prompt.md
78
+ change-summary.prompt.md
79
+ agents/
80
+ architect.agent.md
81
+ implementer.agent.md
82
+ reviewer.agent.md
83
+ AGENTS.md
84
+ ```
85
+
86
+ ### Template content strategy (MVP)
87
+
88
+ - Templates are **generic**, high-signal, and safe:
89
+ - emphasize “don’t guess,” “prefer repo scripts,” “write tests,” “avoid destructive commands”
90
+
91
+ - Keep placeholders minimal and optional (e.g., `{{packageManager}}`, `{{testCommand}}`) filled via `detect_repo_hints` or left as defaults.
92
+
93
+ ## 6) Generation modes
94
+
95
+ ### Mode A: Static templates (default)
96
+
97
+ - `plan_bootstrap` returns prefilled content for all files.
98
+ - Fast, deterministic, easiest to ship.
99
+
100
+ ### Mode B: Prompt-driven content (optional)
101
+
102
+ - Server returns:
103
+ - either (a) prompts + minimal skeleton files, or
104
+ - (b) static templates that the client LLM is encouraged to refine before applying.
105
+
106
+ - **Important:** In MVP, the MCP server itself does **not** call third-party LLM APIs.
107
+ - Any “LLM generation” happens in the **client** (e.g., Copilot), using the user’s configured model/provider.
108
+
109
+ ## 7) README handling (narrow MVP)
110
+
111
+ - If `README.md` exists, the server **does not modify it** by default.
112
+ - Optional flag to include a README suggestion:
113
+ - `plan_bootstrap` can return a _separate_ proposed patch (or a new `README.agentic.md`) rather than overwriting `README.md`.
114
+
115
+ - The client (LLM/user) decides whether to apply README changes.
116
+
117
+ ## 8) Overwrite & idempotency rules (MVP)
118
+
119
+ - If a target file **does not exist** → `CREATE`
120
+ - If it **exists** → default `SKIP`, unless:
121
+ - file contains an **AWB managed block** marker, or
122
+ - user passes `overwrite: true` for that specific file
123
+
124
+ ## 9) Tool contracts (MVP)
125
+
126
+ ### 9.1 `awb.plan_bootstrap`
127
+
128
+ **Input**
129
+
130
+ - `rootPath`: string (repo/workspace root)
131
+ - `options` (optional):
132
+ - `mode`: `"static"` | `"prompt"` (default `"static"`)
133
+ - `includeReadmeProposal`: boolean (default `false`)
134
+ - `overwritePolicy`: `"skip"` | `"managed-only"` | `"overwrite"` (default `"managed-only"`)
135
+ - `files`: optional allowlist of paths to generate (to keep scope minimal)
136
+
137
+ **Output**
138
+
139
+ - `planId`: string
140
+ - `operations[]`:
141
+ - `path`
142
+ - `action`: `CREATE` | `UPDATE_MANAGED` | `SKIP`
143
+ - `content` (when CREATE/UPDATE_MANAGED)
144
+ - `reason` (especially for SKIP)
145
+ - `warnings[]` (e.g., “file exists; not overwriting”)
146
+
147
+ - `repoHints` (optional): from detect logic
148
+ - `readmeProposal` (optional): separate content/patch suggestion
149
+
150
+ ### 9.2 `awb.apply_bootstrap`
151
+
152
+ **Input**
153
+
154
+ - `rootPath`
155
+ - `planId`
156
+ - `operations[]` (echo back from plan; server can validate)
157
+ - `applyOptions` (optional):
158
+ - `dryRun`: boolean
159
+ - `writeMode`: `"atomic"` (default)
160
+ - `failOnConflict`: boolean (default true)
161
+
162
+ **Output**
163
+
164
+ - `results[]`:
165
+ - `path`
166
+ - `status`: `WRITTEN` | `SKIPPED` | `FAILED`
167
+ - `details`
168
+
169
+ ## 10) Security & safety (MVP)
170
+
171
+ - No network calls required.
172
+ - No shell execution.
173
+ - No reading sensitive files beyond what’s needed to check existence and optionally read small config files for hints (e.g., `package.json`).
174
+ - Writes are atomic (temp file + rename).
175
+ - Clear warnings when files exist.
176
+
177
+ ## 11) Distribution & deployment (ship-fast)
178
+
179
+ ### Packaging recommendation (fastest)
180
+
181
+ - Implement in **Node + TypeScript** using the official MCP SDK.
182
+ - Publish to npm as: `awb-mcp` (working name)
183
+ - Provide a single executable entrypoint that starts the MCP server over stdio.
184
+
185
+ ### Install / run
186
+
187
+ - `npm i -g awb-mcp`
188
+ - Configure VS Code MCP settings to launch `awb-mcp` as a server (stdio).
189
+ - Keep configuration minimal: just the command path.
190
+
191
+ ### Versioning
192
+
193
+ - SemVer, but start `0.x` while iterating quickly.
194
+
195
+ ## 12) Success metrics (MVP)
196
+
197
+ - Time-to-bootstrap: user can scaffold in < 1 minute.
198
+ - Idempotency: second run produces zero unexpected changes.
199
+ - Safety: no accidental overwrites; clear SKIP behavior.
200
+
201
+ ## 13) MVP milestones (tight)
202
+
203
+ - **v0.1**
204
+ - MCP server runs in VS Code
205
+ - `plan_bootstrap` + `apply_bootstrap`
206
+ - static templates, managed-block support, atomic writes
207
+
208
+ - **v0.2**
209
+ - optional `detect_repo_hints` + placeholder fill
210
+ - optional README proposal (separate file or patch output)
211
+
212
+ - **v0.3 (only if needed)**
213
+ - “prompt mode” where server returns prompt(s) + skeletons (client LLM refines)
214
+
215
+ ---
@@ -0,0 +1,24 @@
1
+ You are an implementation-focused coding agent working in this repo.
2
+
3
+ ## Source of truth
4
+
5
+ Use `@.ai/backlog.md` as the authoritative spec. Do not re-ask for requirements that are already in the story.
6
+
7
+ ## Task
8
+
9
+ Implement Story [N] from `@.ai/backlog.md`. Use subagent to gather info about user story.
10
+
11
+ ## Operating rules
12
+
13
+ - First, open `@.ai/backlog.md`, locate Story [N], and copy its Acceptance Criteria into a checklist.
14
+ - Treat “Edge Cases”, “Technical Considerations”, “Design Notes”, and “Feature context” as hard constraints.
15
+ - Safe by default. Avoid destructive actions unless the story explicitly requires them.
16
+ - Don’t guess: inspect existing code/scripts/config before choosing an approach. If something is unclear, state assumptions explicitly and proceed with the most conservative option.
17
+
18
+ - Find the correct integration points in the current codebase (minimize new architecture).
19
+ - Implement the smallest end-to-end change that satisfies all Acceptance Criteria and Edge Cases.
20
+ - Add/update tests as appropriate; run relevant build/lint/test commands.
21
+
22
+ ## Done means
23
+
24
+ - Provide a final summary mapping: each Acceptance Criterion → where/how it’s satisfied.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,128 @@
1
+ import process from "node:process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ import z from "zod";
7
+ // Process-level safety: avoid ungraceful exit on uncaught errors (e.g. malformed JSON)
8
+ // SDK transport already catches parse errors; these handlers are a last resort.
9
+ process.on("uncaughtException", (err) => {
10
+ console.error("[awb-mcp] uncaughtException:", err?.message ?? err);
11
+ process.exitCode = 1;
12
+ setTimeout(() => process.exit(1), 100);
13
+ });
14
+ process.on("unhandledRejection", (reason, promise) => {
15
+ console.error("[awb-mcp] unhandledRejection:", reason);
16
+ process.exitCode = 1;
17
+ setTimeout(() => process.exit(1), 100);
18
+ });
19
+ const server = new McpServer({
20
+ name: "awb-mcp",
21
+ version: "0.0.1",
22
+ description: "AWB MCP Server — bootstrap agentic-ready repos via MCP",
23
+ });
24
+ function validateRootPath(rootPath) {
25
+ if (rootPath === undefined || rootPath === null) {
26
+ return { ok: false, error: "rootPath is required and must be a non-empty string." };
27
+ }
28
+ if (typeof rootPath !== "string") {
29
+ return { ok: false, error: "rootPath must be a string." };
30
+ }
31
+ const trimmed = rootPath.trim();
32
+ if (trimmed === "") {
33
+ return { ok: false, error: "rootPath cannot be empty." };
34
+ }
35
+ let resolved;
36
+ try {
37
+ resolved = path.resolve(trimmed);
38
+ }
39
+ catch {
40
+ return { ok: false, error: `rootPath could not be resolved: ${trimmed}` };
41
+ }
42
+ if (!fs.existsSync(resolved)) {
43
+ return { ok: false, error: `rootPath does not exist: ${resolved}` };
44
+ }
45
+ try {
46
+ const stat = fs.statSync(resolved);
47
+ if (!stat.isDirectory()) {
48
+ return { ok: false, error: `rootPath is not a directory: ${resolved}` };
49
+ }
50
+ }
51
+ catch (e) {
52
+ const msg = e instanceof Error ? e.message : String(e);
53
+ return { ok: false, error: `rootPath could not be read: ${msg}` };
54
+ }
55
+ return { ok: true, resolved };
56
+ }
57
+ function errorContent(text) {
58
+ return { content: [{ type: "text", text }] };
59
+ }
60
+ const PlanBootstrapInputSchema = z.object({
61
+ rootPath: z.string().min(1, "rootPath is required"),
62
+ });
63
+ const ApplyBootstrapInputSchema = z.object({
64
+ rootPath: z.string().min(1, "rootPath is required"),
65
+ planId: z.string(),
66
+ operations: z.array(z.unknown()).optional(),
67
+ });
68
+ server.registerTool("awb.plan_bootstrap", {
69
+ title: "Plan Bootstrap",
70
+ description: "Compute a deterministic plan of file operations (create/update/skip) for bootstrapping an agentic-ready repo. Full implementation in Story 2.",
71
+ inputSchema: PlanBootstrapInputSchema,
72
+ annotations: {
73
+ readOnlyHint: true,
74
+ destructiveHint: false,
75
+ idempotentHint: true,
76
+ openWorldHint: false,
77
+ },
78
+ }, async (args) => {
79
+ const parsed = PlanBootstrapInputSchema.safeParse(args);
80
+ if (!parsed.success) {
81
+ return errorContent(`Invalid input: ${parsed.error.message}`);
82
+ }
83
+ const { rootPath } = parsed.data;
84
+ const validation = validateRootPath(rootPath);
85
+ if (!validation.ok) {
86
+ return errorContent(`Error: ${validation.error}`);
87
+ }
88
+ return {
89
+ content: [
90
+ {
91
+ type: "text",
92
+ text: `plan_bootstrap stub: rootPath validated (${validation.resolved}). Full plan generation is implemented in Story 2.`,
93
+ },
94
+ ],
95
+ };
96
+ });
97
+ server.registerTool("awb.apply_bootstrap", {
98
+ title: "Apply Bootstrap",
99
+ description: "Apply a previously returned bootstrap plan to disk (atomic writes). Full implementation in Story 3.",
100
+ inputSchema: ApplyBootstrapInputSchema,
101
+ annotations: {
102
+ readOnlyHint: false,
103
+ destructiveHint: false,
104
+ idempotentHint: true,
105
+ openWorldHint: false,
106
+ },
107
+ }, async (args) => {
108
+ const parsed = ApplyBootstrapInputSchema.safeParse(args);
109
+ if (!parsed.success) {
110
+ return errorContent(`Invalid input: ${parsed.error.message}`);
111
+ }
112
+ const { rootPath } = parsed.data;
113
+ const validation = validateRootPath(rootPath);
114
+ if (!validation.ok) {
115
+ return errorContent(`Error: ${validation.error}`);
116
+ }
117
+ return {
118
+ content: [
119
+ {
120
+ type: "text",
121
+ text: `apply_bootstrap stub: rootPath validated (${validation.resolved}). No files written. Full apply is implemented in Story 3.`,
122
+ },
123
+ ],
124
+ };
125
+ });
126
+ const transport = new StdioServerTransport();
127
+ await server.connect(transport);
128
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@kamkom/awb-mcp",
3
+ "version": "0.0.1",
4
+ "description": "AWB MCP Server — bootstrap agentic-ready repos via MCP",
5
+ "license": "ISC",
6
+ "author": "",
7
+ "type": "module",
8
+ "main": "dist/index.js",
9
+ "bin": {
10
+ "awb-mcp": "dist/index.js"
11
+ },
12
+ "engines": {
13
+ "node": ">=18"
14
+ },
15
+ "scripts": {
16
+ "start": "npx tsx src/index.ts",
17
+ "dev": "tsx watch src/index.ts",
18
+ "build": "tsc"
19
+ },
20
+ "dependencies": {
21
+ "tsx": "^4.19.2",
22
+ "typescript": "^5.8.2",
23
+ "@modelcontextprotocol/sdk": "^1.6.1",
24
+ "zod": "^3.24.2"
25
+ },
26
+ "devDependencies": {
27
+ "typescript": "^5.8.2",
28
+ "tsx": "^4.19.2",
29
+ "@types/node": "^22.13.10"
30
+ }
31
+ }
package/src/index.ts ADDED
@@ -0,0 +1,142 @@
1
+ import process from "node:process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ import z from "zod";
7
+
8
+ // Process-level safety: avoid ungraceful exit on uncaught errors (e.g. malformed JSON)
9
+ // SDK transport already catches parse errors; these handlers are a last resort.
10
+ process.on("uncaughtException", (err) => {
11
+ console.error("[awb-mcp] uncaughtException:", err?.message ?? err);
12
+ process.exitCode = 1;
13
+ setTimeout(() => process.exit(1), 100);
14
+ });
15
+ process.on("unhandledRejection", (reason, promise) => {
16
+ console.error("[awb-mcp] unhandledRejection:", reason);
17
+ process.exitCode = 1;
18
+ setTimeout(() => process.exit(1), 100);
19
+ });
20
+
21
+ const server = new McpServer({
22
+ name: "awb-mcp",
23
+ version: "0.0.1",
24
+ description: "AWB MCP Server — bootstrap agentic-ready repos via MCP",
25
+ });
26
+
27
+ function validateRootPath(rootPath: unknown): { ok: true; resolved: string } | { ok: false; error: string } {
28
+ if (rootPath === undefined || rootPath === null) {
29
+ return { ok: false, error: "rootPath is required and must be a non-empty string." };
30
+ }
31
+ if (typeof rootPath !== "string") {
32
+ return { ok: false, error: "rootPath must be a string." };
33
+ }
34
+ const trimmed = rootPath.trim();
35
+ if (trimmed === "") {
36
+ return { ok: false, error: "rootPath cannot be empty." };
37
+ }
38
+ let resolved: string;
39
+ try {
40
+ resolved = path.resolve(trimmed);
41
+ } catch {
42
+ return { ok: false, error: `rootPath could not be resolved: ${trimmed}` };
43
+ }
44
+ if (!fs.existsSync(resolved)) {
45
+ return { ok: false, error: `rootPath does not exist: ${resolved}` };
46
+ }
47
+ try {
48
+ const stat = fs.statSync(resolved);
49
+ if (!stat.isDirectory()) {
50
+ return { ok: false, error: `rootPath is not a directory: ${resolved}` };
51
+ }
52
+ } catch (e) {
53
+ const msg = e instanceof Error ? e.message : String(e);
54
+ return { ok: false, error: `rootPath could not be read: ${msg}` };
55
+ }
56
+ return { ok: true, resolved };
57
+ }
58
+
59
+ function errorContent(text: string) {
60
+ return { content: [{ type: "text" as const, text }] };
61
+ }
62
+
63
+ const PlanBootstrapInputSchema = z.object({
64
+ rootPath: z.string().min(1, "rootPath is required"),
65
+ });
66
+
67
+ const ApplyBootstrapInputSchema = z.object({
68
+ rootPath: z.string().min(1, "rootPath is required"),
69
+ planId: z.string(),
70
+ operations: z.array(z.unknown()).optional(),
71
+ });
72
+
73
+ server.registerTool(
74
+ "awb.plan_bootstrap",
75
+ {
76
+ title: "Plan Bootstrap",
77
+ description: "Compute a deterministic plan of file operations (create/update/skip) for bootstrapping an agentic-ready repo. Full implementation in Story 2.",
78
+ inputSchema: PlanBootstrapInputSchema,
79
+ annotations: {
80
+ readOnlyHint: true,
81
+ destructiveHint: false,
82
+ idempotentHint: true,
83
+ openWorldHint: false,
84
+ },
85
+ },
86
+ async (args) => {
87
+ const parsed = PlanBootstrapInputSchema.safeParse(args);
88
+ if (!parsed.success) {
89
+ return errorContent(`Invalid input: ${parsed.error.message}`);
90
+ }
91
+ const { rootPath } = parsed.data;
92
+ const validation = validateRootPath(rootPath);
93
+ if (!validation.ok) {
94
+ return errorContent(`Error: ${validation.error}`);
95
+ }
96
+ return {
97
+ content: [
98
+ {
99
+ type: "text" as const,
100
+ text: `plan_bootstrap stub: rootPath validated (${validation.resolved}). Full plan generation is implemented in Story 2.`,
101
+ },
102
+ ],
103
+ };
104
+ },
105
+ );
106
+
107
+ server.registerTool(
108
+ "awb.apply_bootstrap",
109
+ {
110
+ title: "Apply Bootstrap",
111
+ description: "Apply a previously returned bootstrap plan to disk (atomic writes). Full implementation in Story 3.",
112
+ inputSchema: ApplyBootstrapInputSchema,
113
+ annotations: {
114
+ readOnlyHint: false,
115
+ destructiveHint: false,
116
+ idempotentHint: true,
117
+ openWorldHint: false,
118
+ },
119
+ },
120
+ async (args) => {
121
+ const parsed = ApplyBootstrapInputSchema.safeParse(args);
122
+ if (!parsed.success) {
123
+ return errorContent(`Invalid input: ${parsed.error.message}`);
124
+ }
125
+ const { rootPath } = parsed.data;
126
+ const validation = validateRootPath(rootPath);
127
+ if (!validation.ok) {
128
+ return errorContent(`Error: ${validation.error}`);
129
+ }
130
+ return {
131
+ content: [
132
+ {
133
+ type: "text" as const,
134
+ text: `apply_bootstrap stub: rootPath validated (${validation.resolved}). No files written. Full apply is implemented in Story 3.`,
135
+ },
136
+ ],
137
+ };
138
+ },
139
+ );
140
+
141
+ const transport = new StdioServerTransport();
142
+ await server.connect(transport);
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "nodenext",
5
+ "moduleResolution": "nodenext",
6
+ "rootDir": "src",
7
+ "outDir": "dist",
8
+ "declaration": true,
9
+ "declarationMap": true,
10
+ "sourceMap": true,
11
+ "strict": true,
12
+ "esModuleInterop": true,
13
+ "forceConsistentCasingInFileNames": true,
14
+ "skipLibCheck": true
15
+ },
16
+ "include": ["src"]
17
+ }