agent-bober 0.6.2 → 0.8.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.
Files changed (190) hide show
  1. package/README.md +220 -30
  2. package/dist/cli/commands/eval.d.ts +2 -0
  3. package/dist/cli/commands/eval.d.ts.map +1 -1
  4. package/dist/cli/commands/eval.js +10 -0
  5. package/dist/cli/commands/eval.js.map +1 -1
  6. package/dist/cli/commands/init.d.ts.map +1 -1
  7. package/dist/cli/commands/init.js +357 -62
  8. package/dist/cli/commands/init.js.map +1 -1
  9. package/dist/cli/commands/plan.d.ts +2 -0
  10. package/dist/cli/commands/plan.d.ts.map +1 -1
  11. package/dist/cli/commands/plan.js +10 -0
  12. package/dist/cli/commands/plan.js.map +1 -1
  13. package/dist/cli/commands/run.d.ts +2 -0
  14. package/dist/cli/commands/run.d.ts.map +1 -1
  15. package/dist/cli/commands/run.js +10 -0
  16. package/dist/cli/commands/run.js.map +1 -1
  17. package/dist/cli/commands/sprint.d.ts +2 -0
  18. package/dist/cli/commands/sprint.d.ts.map +1 -1
  19. package/dist/cli/commands/sprint.js +10 -0
  20. package/dist/cli/commands/sprint.js.map +1 -1
  21. package/dist/cli/index.js +22 -2
  22. package/dist/cli/index.js.map +1 -1
  23. package/dist/config/schema.d.ts +160 -43
  24. package/dist/config/schema.d.ts.map +1 -1
  25. package/dist/config/schema.js +11 -7
  26. package/dist/config/schema.js.map +1 -1
  27. package/dist/contracts/sprint-contract.d.ts +8 -8
  28. package/dist/discovery/config-generator.d.ts +28 -0
  29. package/dist/discovery/config-generator.d.ts.map +1 -0
  30. package/dist/discovery/config-generator.js +225 -0
  31. package/dist/discovery/config-generator.js.map +1 -0
  32. package/dist/discovery/index.d.ts +20 -0
  33. package/dist/discovery/index.d.ts.map +1 -0
  34. package/dist/discovery/index.js +19 -0
  35. package/dist/discovery/index.js.map +1 -0
  36. package/dist/discovery/scanner.d.ts +17 -0
  37. package/dist/discovery/scanner.d.ts.map +1 -0
  38. package/dist/discovery/scanner.js +120 -0
  39. package/dist/discovery/scanner.js.map +1 -0
  40. package/dist/discovery/scanners/ci-checks.d.ts +10 -0
  41. package/dist/discovery/scanners/ci-checks.d.ts.map +1 -0
  42. package/dist/discovery/scanners/ci-checks.js +169 -0
  43. package/dist/discovery/scanners/ci-checks.js.map +1 -0
  44. package/dist/discovery/scanners/code-conventions.d.ts +12 -0
  45. package/dist/discovery/scanners/code-conventions.d.ts.map +1 -0
  46. package/dist/discovery/scanners/code-conventions.js +216 -0
  47. package/dist/discovery/scanners/code-conventions.js.map +1 -0
  48. package/dist/discovery/scanners/documentation.d.ts +17 -0
  49. package/dist/discovery/scanners/documentation.d.ts.map +1 -0
  50. package/dist/discovery/scanners/documentation.js +92 -0
  51. package/dist/discovery/scanners/documentation.js.map +1 -0
  52. package/dist/discovery/scanners/git-conventions.d.ts +11 -0
  53. package/dist/discovery/scanners/git-conventions.d.ts.map +1 -0
  54. package/dist/discovery/scanners/git-conventions.js +128 -0
  55. package/dist/discovery/scanners/git-conventions.js.map +1 -0
  56. package/dist/discovery/scanners/package-scripts.d.ts +9 -0
  57. package/dist/discovery/scanners/package-scripts.d.ts.map +1 -0
  58. package/dist/discovery/scanners/package-scripts.js +112 -0
  59. package/dist/discovery/scanners/package-scripts.js.map +1 -0
  60. package/dist/discovery/scanners/test-conventions.d.ts +9 -0
  61. package/dist/discovery/scanners/test-conventions.d.ts.map +1 -0
  62. package/dist/discovery/scanners/test-conventions.js +231 -0
  63. package/dist/discovery/scanners/test-conventions.js.map +1 -0
  64. package/dist/discovery/synthesizer.d.ts +30 -0
  65. package/dist/discovery/synthesizer.d.ts.map +1 -0
  66. package/dist/discovery/synthesizer.js +348 -0
  67. package/dist/discovery/synthesizer.js.map +1 -0
  68. package/dist/discovery/types.d.ts +160 -0
  69. package/dist/discovery/types.d.ts.map +1 -0
  70. package/dist/discovery/types.js +9 -0
  71. package/dist/discovery/types.js.map +1 -0
  72. package/dist/index.d.ts +12 -0
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +12 -0
  75. package/dist/index.js.map +1 -1
  76. package/dist/mcp/index.d.ts +4 -0
  77. package/dist/mcp/index.d.ts.map +1 -0
  78. package/dist/mcp/index.js +4 -0
  79. package/dist/mcp/index.js.map +1 -0
  80. package/dist/mcp/run-manager.d.ts +47 -0
  81. package/dist/mcp/run-manager.d.ts.map +1 -0
  82. package/dist/mcp/run-manager.js +79 -0
  83. package/dist/mcp/run-manager.js.map +1 -0
  84. package/dist/mcp/server.d.ts +15 -0
  85. package/dist/mcp/server.d.ts.map +1 -0
  86. package/dist/mcp/server.js +107 -0
  87. package/dist/mcp/server.js.map +1 -0
  88. package/dist/mcp/tools/config.d.ts +2 -0
  89. package/dist/mcp/tools/config.d.ts.map +1 -0
  90. package/dist/mcp/tools/config.js +153 -0
  91. package/dist/mcp/tools/config.js.map +1 -0
  92. package/dist/mcp/tools/contracts.d.ts +2 -0
  93. package/dist/mcp/tools/contracts.d.ts.map +1 -0
  94. package/dist/mcp/tools/contracts.js +61 -0
  95. package/dist/mcp/tools/contracts.js.map +1 -0
  96. package/dist/mcp/tools/eval.d.ts +2 -0
  97. package/dist/mcp/tools/eval.d.ts.map +1 -0
  98. package/dist/mcp/tools/eval.js +157 -0
  99. package/dist/mcp/tools/eval.js.map +1 -0
  100. package/dist/mcp/tools/index.d.ts +20 -0
  101. package/dist/mcp/tools/index.d.ts.map +1 -0
  102. package/dist/mcp/tools/index.js +47 -0
  103. package/dist/mcp/tools/index.js.map +1 -0
  104. package/dist/mcp/tools/init.d.ts +2 -0
  105. package/dist/mcp/tools/init.d.ts.map +1 -0
  106. package/dist/mcp/tools/init.js +222 -0
  107. package/dist/mcp/tools/init.js.map +1 -0
  108. package/dist/mcp/tools/plan.d.ts +2 -0
  109. package/dist/mcp/tools/plan.d.ts.map +1 -0
  110. package/dist/mcp/tools/plan.js +97 -0
  111. package/dist/mcp/tools/plan.js.map +1 -0
  112. package/dist/mcp/tools/principles.d.ts +2 -0
  113. package/dist/mcp/tools/principles.d.ts.map +1 -0
  114. package/dist/mcp/tools/principles.js +66 -0
  115. package/dist/mcp/tools/principles.js.map +1 -0
  116. package/dist/mcp/tools/registry.d.ts +45 -0
  117. package/dist/mcp/tools/registry.d.ts.map +1 -0
  118. package/dist/mcp/tools/registry.js +23 -0
  119. package/dist/mcp/tools/registry.js.map +1 -0
  120. package/dist/mcp/tools/run.d.ts +2 -0
  121. package/dist/mcp/tools/run.d.ts.map +1 -0
  122. package/dist/mcp/tools/run.js +66 -0
  123. package/dist/mcp/tools/run.js.map +1 -0
  124. package/dist/mcp/tools/spec.d.ts +2 -0
  125. package/dist/mcp/tools/spec.d.ts.map +1 -0
  126. package/dist/mcp/tools/spec.js +32 -0
  127. package/dist/mcp/tools/spec.js.map +1 -0
  128. package/dist/mcp/tools/sprint.d.ts +2 -0
  129. package/dist/mcp/tools/sprint.d.ts.map +1 -0
  130. package/dist/mcp/tools/sprint.js +243 -0
  131. package/dist/mcp/tools/sprint.js.map +1 -0
  132. package/dist/mcp/tools/status.d.ts +2 -0
  133. package/dist/mcp/tools/status.d.ts.map +1 -0
  134. package/dist/mcp/tools/status.js +76 -0
  135. package/dist/mcp/tools/status.js.map +1 -0
  136. package/dist/orchestrator/agentic-loop.d.ts +7 -6
  137. package/dist/orchestrator/agentic-loop.d.ts.map +1 -1
  138. package/dist/orchestrator/agentic-loop.js +33 -40
  139. package/dist/orchestrator/agentic-loop.js.map +1 -1
  140. package/dist/orchestrator/context-handoff.d.ts +20 -20
  141. package/dist/orchestrator/evaluator-agent.d.ts.map +1 -1
  142. package/dist/orchestrator/evaluator-agent.js +2 -2
  143. package/dist/orchestrator/evaluator-agent.js.map +1 -1
  144. package/dist/orchestrator/generator-agent.d.ts.map +1 -1
  145. package/dist/orchestrator/generator-agent.js +2 -2
  146. package/dist/orchestrator/generator-agent.js.map +1 -1
  147. package/dist/orchestrator/model-resolver.d.ts +35 -4
  148. package/dist/orchestrator/model-resolver.d.ts.map +1 -1
  149. package/dist/orchestrator/model-resolver.js +68 -15
  150. package/dist/orchestrator/model-resolver.js.map +1 -1
  151. package/dist/orchestrator/planner-agent.d.ts.map +1 -1
  152. package/dist/orchestrator/planner-agent.js +2 -2
  153. package/dist/orchestrator/planner-agent.js.map +1 -1
  154. package/dist/orchestrator/tools/index.d.ts +3 -4
  155. package/dist/orchestrator/tools/index.d.ts.map +1 -1
  156. package/dist/orchestrator/tools/index.js.map +1 -1
  157. package/dist/orchestrator/tools/schemas.d.ts +11 -12
  158. package/dist/orchestrator/tools/schemas.d.ts.map +1 -1
  159. package/dist/orchestrator/tools/schemas.js +3 -2
  160. package/dist/orchestrator/tools/schemas.js.map +1 -1
  161. package/dist/providers/anthropic.d.ts +15 -0
  162. package/dist/providers/anthropic.d.ts.map +1 -0
  163. package/dist/providers/anthropic.js +133 -0
  164. package/dist/providers/anthropic.js.map +1 -0
  165. package/dist/providers/factory.d.ts +37 -0
  166. package/dist/providers/factory.d.ts.map +1 -0
  167. package/dist/providers/factory.js +119 -0
  168. package/dist/providers/factory.js.map +1 -0
  169. package/dist/providers/google.d.ts +39 -0
  170. package/dist/providers/google.d.ts.map +1 -0
  171. package/dist/providers/google.js +195 -0
  172. package/dist/providers/google.js.map +1 -0
  173. package/dist/providers/index.d.ts +7 -0
  174. package/dist/providers/index.d.ts.map +1 -0
  175. package/dist/providers/index.js +6 -0
  176. package/dist/providers/index.js.map +1 -0
  177. package/dist/providers/openai-compat.d.ts +39 -0
  178. package/dist/providers/openai-compat.d.ts.map +1 -0
  179. package/dist/providers/openai-compat.js +42 -0
  180. package/dist/providers/openai-compat.js.map +1 -0
  181. package/dist/providers/openai.d.ts +41 -0
  182. package/dist/providers/openai.d.ts.map +1 -0
  183. package/dist/providers/openai.js +205 -0
  184. package/dist/providers/openai.js.map +1 -0
  185. package/dist/providers/types.d.ts +144 -0
  186. package/dist/providers/types.d.ts.map +1 -0
  187. package/dist/providers/types.js +8 -0
  188. package/dist/providers/types.js.map +1 -0
  189. package/package.json +22 -4
  190. package/skills/bober.principles/SKILL.md +36 -3
@@ -0,0 +1,222 @@
1
+ // ── bober_init tool ──────────────────────────────────────────────────
2
+ //
3
+ // Creates bober.config.json and .bober/ directories in the current
4
+ // working directory. Accepts { preset?: string, provider?: string }.
5
+ // Returns a confirmation message.
6
+ import { writeFile } from "node:fs/promises";
7
+ import { join, basename } from "node:path";
8
+ import { cwd } from "node:process";
9
+ import { configExists } from "../../config/loader.js";
10
+ import { createDefaultConfig } from "../../config/schema.js";
11
+ import { getPresetNames } from "../../config/defaults.js";
12
+ import { ensureBoberDir } from "../../state/index.js";
13
+ import { registerTool } from "./registry.js";
14
+ import { scanProject } from "../../discovery/scanner.js";
15
+ import { generateEvalConfig } from "../../discovery/config-generator.js";
16
+ import { synthesizePrinciples } from "../../discovery/synthesizer.js";
17
+ // ── Registration ─────────────────────────────────────────────────────
18
+ export function registerInitTool() {
19
+ registerTool({
20
+ name: "bober_init",
21
+ description: "Initialise a Bober project in the current working directory. " +
22
+ "Creates bober.config.json and the .bober/ state directory. " +
23
+ "Accepts an optional preset (e.g. nextjs, react-vite, api-node) and " +
24
+ "an optional provider (anthropic, openai, google, openai-compat). " +
25
+ "If a config already exists this will overwrite it.",
26
+ inputSchema: {
27
+ type: "object",
28
+ properties: {
29
+ preset: {
30
+ type: "string",
31
+ description: "Optional project preset. One of: nextjs, react-vite, api-node, " +
32
+ "python-api, solidity, anchor. Leave blank for a generic config.",
33
+ },
34
+ provider: {
35
+ type: "string",
36
+ description: "AI provider to use: anthropic (default), openai, google, openai-compat.",
37
+ default: "anthropic",
38
+ },
39
+ mode: {
40
+ type: "string",
41
+ description: "Project mode: greenfield (default) or brownfield.",
42
+ default: "greenfield",
43
+ },
44
+ projectName: {
45
+ type: "string",
46
+ description: "Project name. Defaults to the name of the current directory.",
47
+ },
48
+ },
49
+ additionalProperties: false,
50
+ },
51
+ handler: async (args) => {
52
+ const projectRoot = cwd();
53
+ const projectName = typeof args.projectName === "string" && args.projectName.trim()
54
+ ? args.projectName.trim()
55
+ : basename(projectRoot);
56
+ const rawPreset = typeof args.preset === "string" && args.preset.trim()
57
+ ? args.preset.trim()
58
+ : undefined;
59
+ const provider = typeof args.provider === "string" && args.provider.trim()
60
+ ? args.provider.trim()
61
+ : "anthropic";
62
+ const rawMode = typeof args.mode === "string" && args.mode.trim()
63
+ ? args.mode.trim()
64
+ : "greenfield";
65
+ const mode = rawMode === "brownfield" ? "brownfield" : "greenfield";
66
+ // Validate preset if provided
67
+ if (rawPreset) {
68
+ const knownPresets = getPresetNames();
69
+ if (!knownPresets.includes(rawPreset)) {
70
+ return JSON.stringify({
71
+ error: `Unknown preset "${rawPreset}".`,
72
+ availablePresets: knownPresets,
73
+ });
74
+ }
75
+ }
76
+ // Warn if already initialised (but proceed — tool contract says overwrite)
77
+ const alreadyExists = await configExists(projectRoot);
78
+ if (alreadyExists) {
79
+ process.stderr.write(`[bober_init] Overwriting existing bober.config.json in ${projectRoot}\n`);
80
+ }
81
+ // ── Brownfield: run auto-discovery pipeline ────────────────────
82
+ if (mode === "brownfield") {
83
+ process.stderr.write(`[bober_init] Running brownfield auto-discovery for ${projectRoot}\n`);
84
+ const report = await scanProject(projectRoot);
85
+ const evalConfig = generateEvalConfig(report);
86
+ const config = createDefaultConfig(projectName, mode, undefined, {
87
+ planner: {
88
+ maxClarifications: 5,
89
+ model: "opus",
90
+ provider,
91
+ },
92
+ generator: {
93
+ model: "sonnet",
94
+ maxTurnsPerSprint: 50,
95
+ autoCommit: true,
96
+ branchPattern: "bober/{feature-name}",
97
+ provider,
98
+ },
99
+ evaluator: {
100
+ model: "sonnet",
101
+ strategies: evalConfig.strategies,
102
+ maxIterations: 3,
103
+ provider,
104
+ },
105
+ commands: evalConfig.commands,
106
+ });
107
+ // Write bober.config.json
108
+ const configPath = join(projectRoot, "bober.config.json");
109
+ await writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
110
+ // Create .bober/ directory structure
111
+ await ensureBoberDir(projectRoot);
112
+ // Synthesize principles
113
+ let principles = null;
114
+ let principlesError = null;
115
+ try {
116
+ principles = await synthesizePrinciples(report, projectRoot, config);
117
+ const principlesPath = join(projectRoot, ".bober", "principles.md");
118
+ await writeFile(principlesPath, principles, "utf-8");
119
+ }
120
+ catch (err) {
121
+ principlesError = err instanceof Error ? err.message : String(err);
122
+ process.stderr.write(`[bober_init] Could not synthesize principles: ${principlesError}\n`);
123
+ }
124
+ // Build discovery summary for response
125
+ const stack = report.detectedStack;
126
+ const detectedTech = [];
127
+ if (stack) {
128
+ if (stack.hasTypescript)
129
+ detectedTech.push("TypeScript");
130
+ if (stack.hasReact)
131
+ detectedTech.push("React");
132
+ if (stack.hasNext)
133
+ detectedTech.push("Next.js");
134
+ if (stack.hasVite)
135
+ detectedTech.push("Vite");
136
+ if (stack.hasEslint)
137
+ detectedTech.push("ESLint");
138
+ if (stack.hasVitest)
139
+ detectedTech.push("Vitest");
140
+ if (stack.hasJest)
141
+ detectedTech.push("Jest");
142
+ if (stack.hasPlaywright)
143
+ detectedTech.push("Playwright");
144
+ if (stack.hasNestjs)
145
+ detectedTech.push("NestJS");
146
+ if (stack.hasFastify)
147
+ detectedTech.push("Fastify");
148
+ if (stack.hasExpress)
149
+ detectedTech.push("Express");
150
+ if (stack.hasPython)
151
+ detectedTech.push("Python");
152
+ if (stack.hasRust)
153
+ detectedTech.push("Rust");
154
+ }
155
+ process.stderr.write(`[bober_init] Initialised brownfield project "${projectName}" in ${projectRoot}\n`);
156
+ return JSON.stringify({
157
+ status: "initialised",
158
+ projectName,
159
+ mode,
160
+ provider,
161
+ configPath,
162
+ boberDir: join(projectRoot, ".bober"),
163
+ discovery: {
164
+ detectedTech,
165
+ packageManager: report.packageManager,
166
+ strategies: evalConfig.strategies.map((s) => s.type),
167
+ commands: evalConfig.commands,
168
+ },
169
+ principles: principles ?? null,
170
+ principlesError,
171
+ message: alreadyExists
172
+ ? "Existing configuration was overwritten using auto-discovery."
173
+ : "Brownfield project initialised with auto-discovered configuration.",
174
+ nextStep: principles
175
+ ? "Run bober_plan with a task description to generate a sprint plan."
176
+ : "Run /bober-principles to generate project principles, then bober_plan.",
177
+ }, null, 2);
178
+ }
179
+ // ── Greenfield: use preset/default config ──────────────────────
180
+ const config = createDefaultConfig(projectName, mode, rawPreset, {
181
+ planner: {
182
+ maxClarifications: 5,
183
+ model: "opus",
184
+ provider,
185
+ },
186
+ generator: {
187
+ model: "sonnet",
188
+ maxTurnsPerSprint: 50,
189
+ autoCommit: true,
190
+ branchPattern: "bober/{feature-name}",
191
+ provider,
192
+ },
193
+ evaluator: {
194
+ model: "sonnet",
195
+ strategies: [],
196
+ maxIterations: 3,
197
+ provider,
198
+ },
199
+ });
200
+ // Write bober.config.json
201
+ const configPath = join(projectRoot, "bober.config.json");
202
+ await writeFile(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
203
+ // Create .bober/ directory structure
204
+ await ensureBoberDir(projectRoot);
205
+ process.stderr.write(`[bober_init] Initialised project "${projectName}" in ${projectRoot}\n`);
206
+ return JSON.stringify({
207
+ status: "initialised",
208
+ projectName,
209
+ mode,
210
+ preset: rawPreset ?? null,
211
+ provider,
212
+ configPath,
213
+ boberDir: join(projectRoot, ".bober"),
214
+ message: alreadyExists
215
+ ? "Existing configuration was overwritten."
216
+ : "Project successfully initialised. Run bober_plan to create your first plan.",
217
+ nextStep: "Run bober_plan with a task description to generate a sprint plan.",
218
+ }, null, 2);
219
+ },
220
+ });
221
+ }
222
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../../src/mcp/tools/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function registerPlanTool(): void;
2
+ //# sourceMappingURL=plan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/plan.ts"],"names":[],"mappings":"AAkBA,wBAAgB,gBAAgB,IAAI,IAAI,CAiGvC"}
@@ -0,0 +1,97 @@
1
+ // ── bober_plan tool ─────────────────────────────────────────────────
2
+ //
3
+ // Accepts { task: string }, calls the planner agent, and returns a
4
+ // JSON summary of the produced PlanSpec.
5
+ import { cwd } from "node:process";
6
+ import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
7
+ import { configExists, loadConfig } from "../../config/loader.js";
8
+ import { createContract } from "../../contracts/sprint-contract.js";
9
+ import { runPlanner } from "../../orchestrator/planner-agent.js";
10
+ import { ensureBoberDir, saveContract } from "../../state/index.js";
11
+ import { registerTool } from "./registry.js";
12
+ // ── Registration ─────────────────────────────────────────────────────
13
+ export function registerPlanTool() {
14
+ registerTool({
15
+ name: "bober_plan",
16
+ description: "Run the Bober planner agent. Accepts a task/feature description and " +
17
+ "produces a PlanSpec with a sprint breakdown saved to .bober/specs/. " +
18
+ "Returns a JSON summary with the plan title, description, and sprint list.",
19
+ inputSchema: {
20
+ type: "object",
21
+ properties: {
22
+ task: {
23
+ type: "string",
24
+ description: "Feature or project description to plan.",
25
+ },
26
+ },
27
+ required: ["task"],
28
+ additionalProperties: false,
29
+ },
30
+ handler: async (args) => {
31
+ const task = String(args.task ?? "").trim();
32
+ if (!task) {
33
+ return JSON.stringify({ error: "task is required and must be a non-empty string." });
34
+ }
35
+ const projectRoot = cwd();
36
+ // Check config exists before attempting to load
37
+ const hasConfig = await configExists(projectRoot);
38
+ if (!hasConfig) {
39
+ throw new McpError(ErrorCode.InvalidRequest, "No bober.config.json found. Run bober_init first.");
40
+ }
41
+ let config;
42
+ try {
43
+ config = await loadConfig(projectRoot);
44
+ }
45
+ catch (err) {
46
+ return JSON.stringify({
47
+ error: `Failed to load config: ${err instanceof Error ? err.message : String(err)}`,
48
+ projectRoot,
49
+ });
50
+ }
51
+ await ensureBoberDir(projectRoot);
52
+ try {
53
+ const spec = await runPlanner(task, projectRoot, config);
54
+ // Generate sprint contracts from features (same as pipeline.ts)
55
+ const contracts = [];
56
+ for (const feature of spec.features) {
57
+ const contract = createContract(feature.title, feature.description, feature.acceptanceCriteria.map((ac, idx) => ({
58
+ id: `${feature.id}-criterion-${idx + 1}`,
59
+ description: ac,
60
+ verificationMethod: "agent-evaluation",
61
+ })));
62
+ contracts.push(contract);
63
+ await saveContract(projectRoot, contract);
64
+ }
65
+ const summary = {
66
+ id: spec.id,
67
+ title: spec.title,
68
+ description: spec.description,
69
+ projectType: spec.projectType,
70
+ techStack: spec.techStack,
71
+ sprintCount: spec.features.length,
72
+ sprints: spec.features.map((f, idx) => ({
73
+ id: f.id,
74
+ contractId: contracts[idx]?.id,
75
+ feature: f.title,
76
+ description: f.description,
77
+ priority: f.priority,
78
+ estimatedSprints: f.estimatedSprints,
79
+ criteriaCount: f.acceptanceCriteria.length,
80
+ status: "proposed",
81
+ })),
82
+ contractIds: contracts.map((c) => c.id),
83
+ nonFunctional: spec.nonFunctional,
84
+ constraints: spec.constraints,
85
+ savedTo: `.bober/specs/${spec.id}.json`,
86
+ };
87
+ return JSON.stringify(summary, null, 2);
88
+ }
89
+ catch (err) {
90
+ return JSON.stringify({
91
+ error: `Planner failed: ${err instanceof Error ? err.message : String(err)}`,
92
+ });
93
+ }
94
+ },
95
+ });
96
+ }
97
+ //# sourceMappingURL=plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plan.js","sourceRoot":"","sources":["../../../src/mcp/tools/plan.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function registerPrinciplesTool(): void;
2
+ //# sourceMappingURL=principles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"principles.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/principles.ts"],"names":[],"mappings":"AAsBA,wBAAgB,sBAAsB,IAAI,IAAI,CAwE7C"}
@@ -0,0 +1,66 @@
1
+ // ── bober_principles tool ─────────────────────────────────────────────
2
+ //
3
+ // No args -> read .bober/principles.md and return content.
4
+ // With { content } -> write/update .bober/principles.md.
5
+ import { readFile, writeFile } from "node:fs/promises";
6
+ import { join } from "node:path";
7
+ import { cwd } from "node:process";
8
+ import { ensureBoberDir } from "../../state/index.js";
9
+ import { registerTool } from "./registry.js";
10
+ // ── Constants ─────────────────────────────────────────────────────────
11
+ const PRINCIPLES_FILENAME = "principles.md";
12
+ function principlesPath(projectRoot) {
13
+ return join(projectRoot, ".bober", PRINCIPLES_FILENAME);
14
+ }
15
+ // ── Registration ─────────────────────────────────────────────────────
16
+ export function registerPrinciplesTool() {
17
+ registerTool({
18
+ name: "bober_principles",
19
+ description: "Read or write the project principles file (.bober/principles.md). " +
20
+ "Without arguments reads the current principles. " +
21
+ "With content writes/replaces the principles file. " +
22
+ "Principles are injected into every generator and evaluator agent prompt.",
23
+ inputSchema: {
24
+ type: "object",
25
+ properties: {
26
+ content: {
27
+ type: "string",
28
+ description: "Principles content to write. Omit to read current principles.",
29
+ },
30
+ },
31
+ additionalProperties: false,
32
+ },
33
+ handler: async (args) => {
34
+ const projectRoot = cwd();
35
+ const filePath = principlesPath(projectRoot);
36
+ const newContent = typeof args.content === "string" ? args.content : undefined;
37
+ // Write mode
38
+ if (newContent !== undefined) {
39
+ await ensureBoberDir(projectRoot);
40
+ await writeFile(filePath, newContent, "utf-8");
41
+ process.stderr.write(`[bober_principles] Wrote ${newContent.length} characters to ${filePath}\n`);
42
+ return JSON.stringify({
43
+ status: "updated",
44
+ path: filePath,
45
+ characters: newContent.length,
46
+ message: "Principles file updated. These will be injected into all future agent prompts.",
47
+ }, null, 2);
48
+ }
49
+ // Read mode
50
+ try {
51
+ const content = await readFile(filePath, "utf-8");
52
+ return JSON.stringify({
53
+ path: filePath,
54
+ content,
55
+ }, null, 2);
56
+ }
57
+ catch {
58
+ return JSON.stringify({
59
+ content: null,
60
+ message: "No principles file. Use bober_principles with content to create one.",
61
+ }, null, 2);
62
+ }
63
+ },
64
+ });
65
+ }
66
+ //# sourceMappingURL=principles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"principles.js","sourceRoot":"","sources":["../../../src/mcp/tools/principles.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,EAAE;AACF,2DAA2D;AAC3D,yDAAyD;AAEzD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAEnC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,yEAAyE;AAEzE,MAAM,mBAAmB,GAAG,eAAe,CAAC;AAE5C,SAAS,cAAc,CAAC,WAAmB;IACzC,OAAO,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,mBAAmB,CAAC,CAAC;AAC1D,CAAC;AAED,wEAAwE;AAExE,MAAM,UAAU,sBAAsB;IACpC,YAAY,CAAC;QACX,IAAI,EAAE,kBAAkB;QACxB,WAAW,EACT,oEAAoE;YACpE,kDAAkD;YAClD,oDAAoD;YACpD,0EAA0E;QAC5E,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,OAAO,EAAE;oBACP,IAAI,EAAE,QAAQ;oBACd,WAAW,EACT,+DAA+D;iBAClE;aACF;YACD,oBAAoB,EAAE,KAAK;SAC5B;QACD,OAAO,EAAE,KAAK,EAAE,IAA6B,EAAmB,EAAE;YAChE,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC;YAC1B,MAAM,QAAQ,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;YAE7C,MAAM,UAAU,GACd,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;YAE9D,aAAa;YACb,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;gBAC7B,MAAM,cAAc,CAAC,WAAW,CAAC,CAAC;gBAClC,MAAM,SAAS,CAAC,QAAQ,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;gBAE/C,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,4BAA4B,UAAU,CAAC,MAAM,kBAAkB,QAAQ,IAAI,CAC5E,CAAC;gBAEF,OAAO,IAAI,CAAC,SAAS,CACnB;oBACE,MAAM,EAAE,SAAS;oBACjB,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE,UAAU,CAAC,MAAM;oBAC7B,OAAO,EACL,gFAAgF;iBACnF,EACD,IAAI,EACJ,CAAC,CACF,CAAC;YACJ,CAAC;YAED,YAAY;YACZ,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;gBAClD,OAAO,IAAI,CAAC,SAAS,CACnB;oBACE,IAAI,EAAE,QAAQ;oBACd,OAAO;iBACR,EACD,IAAI,EACJ,CAAC,CACF,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC,SAAS,CACnB;oBACE,OAAO,EAAE,IAAI;oBACb,OAAO,EACL,sEAAsE;iBACzE,EACD,IAAI,EACJ,CAAC,CACF,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * A JSON Schema object for describing a tool's input parameters.
3
+ */
4
+ export interface JsonSchemaObject {
5
+ type: string;
6
+ properties?: Record<string, JsonSchemaProperty>;
7
+ required?: string[];
8
+ additionalProperties?: boolean;
9
+ description?: string;
10
+ [key: string]: unknown;
11
+ }
12
+ export interface JsonSchemaProperty {
13
+ type?: string;
14
+ description?: string;
15
+ enum?: unknown[];
16
+ default?: unknown;
17
+ [key: string]: unknown;
18
+ }
19
+ /**
20
+ * Definition of a single MCP tool that agent-bober exposes.
21
+ */
22
+ export interface BoberToolDefinition {
23
+ /** The tool name as it will appear in tools/list (e.g. "bober_ping"). */
24
+ name: string;
25
+ /** Human-readable description of what the tool does. */
26
+ description: string;
27
+ /** JSON Schema object describing the tool's input parameters. */
28
+ inputSchema: JsonSchemaObject;
29
+ /** Function that executes the tool and returns a string result. */
30
+ handler: (args: Record<string, unknown>) => Promise<string>;
31
+ }
32
+ /**
33
+ * Register a tool in the global tool registry.
34
+ * If a tool with the same name is already registered, it is overwritten.
35
+ */
36
+ export declare function registerTool(tool: BoberToolDefinition): void;
37
+ /**
38
+ * Returns all registered tool definitions.
39
+ */
40
+ export declare function getAllTools(): BoberToolDefinition[];
41
+ /**
42
+ * Looks up a tool by name. Returns undefined if not found.
43
+ */
44
+ export declare function getTool(name: string): BoberToolDefinition | undefined;
45
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/registry.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAChD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,OAAO,EAAE,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,WAAW,EAAE,gBAAgB,CAAC;IAC9B,mEAAmE;IACnE,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC7D;AAMD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAE5D;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,mBAAmB,EAAE,CAEnD;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAErE"}
@@ -0,0 +1,23 @@
1
+ // ── Tool Registry ───────────────────────────────────────────────────
2
+ // ── Registry implementation ─────────────────────────────────────────
3
+ const registry = new Map();
4
+ /**
5
+ * Register a tool in the global tool registry.
6
+ * If a tool with the same name is already registered, it is overwritten.
7
+ */
8
+ export function registerTool(tool) {
9
+ registry.set(tool.name, tool);
10
+ }
11
+ /**
12
+ * Returns all registered tool definitions.
13
+ */
14
+ export function getAllTools() {
15
+ return Array.from(registry.values());
16
+ }
17
+ /**
18
+ * Looks up a tool by name. Returns undefined if not found.
19
+ */
20
+ export function getTool(name) {
21
+ return registry.get(name);
22
+ }
23
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/mcp/tools/registry.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAoCvE,uEAAuE;AAEvE,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA+B,CAAC;AAExD;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAyB;IACpD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW;IACzB,OAAO,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function registerRunTool(): void;
2
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/run.ts"],"names":[],"mappings":"AAgBA,wBAAgB,eAAe,IAAI,IAAI,CA0EtC"}
@@ -0,0 +1,66 @@
1
+ // ── bober_run tool ───────────────────────────────────────────────────
2
+ //
3
+ // Accepts { task: string }, starts the full pipeline asynchronously,
4
+ // and returns immediately with a runId. The pipeline runs in the
5
+ // background; poll bober_status to track progress.
6
+ import { cwd } from "node:process";
7
+ import { McpError, ErrorCode } from "@modelcontextprotocol/sdk/types.js";
8
+ import { configExists, loadConfig } from "../../config/loader.js";
9
+ import { registerTool } from "./registry.js";
10
+ import { runManager } from "../run-manager.js";
11
+ // ── Registration ─────────────────────────────────────────────────────
12
+ export function registerRunTool() {
13
+ registerTool({
14
+ name: "bober_run",
15
+ description: "Start the full Bober pipeline (plan + sprint + eval) asynchronously. " +
16
+ "Accepts a task description and returns immediately with a runId. " +
17
+ "Use bober_status to poll progress. Only one pipeline can run at a time.",
18
+ inputSchema: {
19
+ type: "object",
20
+ properties: {
21
+ task: {
22
+ type: "string",
23
+ description: "Feature or project description to build. Passed to the planner.",
24
+ },
25
+ },
26
+ required: ["task"],
27
+ additionalProperties: false,
28
+ },
29
+ handler: async (args) => {
30
+ const task = String(args.task ?? "").trim();
31
+ if (!task) {
32
+ return JSON.stringify({
33
+ error: "task is required and must be a non-empty string.",
34
+ });
35
+ }
36
+ const projectRoot = cwd();
37
+ // Require a bober config before starting
38
+ const hasConfig = await configExists(projectRoot);
39
+ if (!hasConfig) {
40
+ throw new McpError(ErrorCode.InvalidRequest, "No bober.config.json found. Run bober_init first.");
41
+ }
42
+ // Reject concurrent runs with a clear error
43
+ if (runManager.isRunning()) {
44
+ const state = runManager.getStatus();
45
+ throw new McpError(ErrorCode.InvalidRequest, `A pipeline is already running (runId: ${state.runId}). Use bober_status to check progress.`);
46
+ }
47
+ let config;
48
+ try {
49
+ config = await loadConfig(projectRoot);
50
+ }
51
+ catch (err) {
52
+ return JSON.stringify({
53
+ error: `Failed to load config: ${err instanceof Error ? err.message : String(err)}`,
54
+ });
55
+ }
56
+ const runId = runManager.startRun(task, projectRoot, config);
57
+ process.stderr.write(`[bober_run] Started pipeline run ${runId} for task: ${task.slice(0, 100)}\n`);
58
+ return JSON.stringify({
59
+ runId,
60
+ status: "running",
61
+ message: "Pipeline started. Use bober_status to check progress.",
62
+ }, null, 2);
63
+ },
64
+ });
65
+ }
66
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/mcp/tools/run.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,EAAE;AACF,qEAAqE;AACrE,iEAAiE;AACjE,mDAAmD;AAEnD,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAEnC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAEzE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAE/C,wEAAwE;AAExE,MAAM,UAAU,eAAe;IAC7B,YAAY,CAAC;QACX,IAAI,EAAE,WAAW;QACjB,WAAW,EACT,uEAAuE;YACvE,mEAAmE;YACnE,yEAAyE;QAC3E,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,IAAI,EAAE;oBACJ,IAAI,EAAE,QAAQ;oBACd,WAAW,EACT,iEAAiE;iBACpE;aACF;YACD,QAAQ,EAAE,CAAC,MAAM,CAAC;YAClB,oBAAoB,EAAE,KAAK;SAC5B;QACD,OAAO,EAAE,KAAK,EAAE,IAA6B,EAAmB,EAAE;YAChE,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YAC5C,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAO,IAAI,CAAC,SAAS,CAAC;oBACpB,KAAK,EAAE,kDAAkD;iBAC1D,CAAC,CAAC;YACL,CAAC;YAED,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC;YAE1B,yCAAyC;YACzC,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,WAAW,CAAC,CAAC;YAClD,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,MAAM,IAAI,QAAQ,CAChB,SAAS,CAAC,cAAc,EACxB,mDAAmD,CACpD,CAAC;YACJ,CAAC;YAED,4CAA4C;YAC5C,IAAI,UAAU,CAAC,SAAS,EAAE,EAAE,CAAC;gBAC3B,MAAM,KAAK,GAAG,UAAU,CAAC,SAAS,EAAE,CAAC;gBACrC,MAAM,IAAI,QAAQ,CAChB,SAAS,CAAC,cAAc,EACxB,yCAAyC,KAAM,CAAC,KAAK,wCAAwC,CAC9F,CAAC;YACJ,CAAC;YAED,IAAI,MAAM,CAAC;YACX,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM,UAAU,CAAC,WAAW,CAAC,CAAC;YACzC,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,OAAO,IAAI,CAAC,SAAS,CAAC;oBACpB,KAAK,EAAE,0BAA0B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;iBACpF,CAAC,CAAC;YACL,CAAC;YAED,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;YAE7D,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,oCAAoC,KAAK,cAAc,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,CAC9E,CAAC;YAEF,OAAO,IAAI,CAAC,SAAS,CACnB;gBACE,KAAK;gBACL,MAAM,EAAE,SAAS;gBACjB,OAAO,EACL,uDAAuD;aAC1D,EACD,IAAI,EACJ,CAAC,CACF,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function registerSpecTool(): void;
2
+ //# sourceMappingURL=spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/spec.ts"],"names":[],"mappings":"AAYA,wBAAgB,gBAAgB,IAAI,IAAI,CA6BvC"}
@@ -0,0 +1,32 @@
1
+ // ── bober_spec tool ───────────────────────────────────────────────────
2
+ //
3
+ // Returns the latest PlanSpec JSON.
4
+ // If no plans exist, returns a descriptive error object.
5
+ import { cwd } from "node:process";
6
+ import { loadLatestSpec } from "../../state/index.js";
7
+ import { registerTool } from "./registry.js";
8
+ // ── Registration ─────────────────────────────────────────────────────
9
+ export function registerSpecTool() {
10
+ registerTool({
11
+ name: "bober_spec",
12
+ description: "Return the latest PlanSpec JSON. " +
13
+ "The spec contains the project overview, feature breakdown, and sprint contracts. " +
14
+ "If no plan exists yet, returns an error message.",
15
+ inputSchema: {
16
+ type: "object",
17
+ properties: {},
18
+ additionalProperties: false,
19
+ },
20
+ handler: async (_args) => {
21
+ const projectRoot = cwd();
22
+ const spec = await loadLatestSpec(projectRoot);
23
+ if (spec === null) {
24
+ return JSON.stringify({
25
+ error: "No plans found. Run bober_plan first.",
26
+ }, null, 2);
27
+ }
28
+ return JSON.stringify(spec, null, 2);
29
+ },
30
+ });
31
+ }
32
+ //# sourceMappingURL=spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec.js","sourceRoot":"","sources":["../../../src/mcp/tools/spec.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,EAAE;AACF,oCAAoC;AACpC,yDAAyD;AAEzD,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAEnC,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,wEAAwE;AAExE,MAAM,UAAU,gBAAgB;IAC9B,YAAY,CAAC;QACX,IAAI,EAAE,YAAY;QAClB,WAAW,EACT,mCAAmC;YACnC,mFAAmF;YACnF,kDAAkD;QACpD,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,EAAE;YACd,oBAAoB,EAAE,KAAK;SAC5B;QACD,OAAO,EAAE,KAAK,EAAE,KAA8B,EAAmB,EAAE;YACjE,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC;YAE1B,MAAM,IAAI,GAAG,MAAM,cAAc,CAAC,WAAW,CAAC,CAAC;YAC/C,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAClB,OAAO,IAAI,CAAC,SAAS,CACnB;oBACE,KAAK,EAAE,uCAAuC;iBAC/C,EACD,IAAI,EACJ,CAAC,CACF,CAAC;YACJ,CAAC;YAED,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACvC,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function registerSprintTool(): void;
2
+ //# sourceMappingURL=sprint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sprint.d.ts","sourceRoot":"","sources":["../../../src/mcp/tools/sprint.ts"],"names":[],"mappings":"AAgEA,wBAAgB,kBAAkB,IAAI,IAAI,CAwPzC"}