@akanjs/cli 2.4.0-rc.9 → 2.4.1-rc.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 (69) hide show
  1. package/agent.command-h4afc69n.js +26 -0
  2. package/application.command-4ctkfdan.js +165 -0
  3. package/applicationBuildRunner-esa1kj7v.js +284 -0
  4. package/applicationReleasePackager-brvth6rs.js +245 -0
  5. package/capacitorApp-y0h6cgft.js +58 -0
  6. package/cloud.command-rpztn4fh.js +94 -0
  7. package/commandManifest.json +1 -0
  8. package/context.command-nqbtak4f.js +82 -0
  9. package/dependencyScanner-m4x5maek.js +9 -0
  10. package/guideline.command-ya0dh44f.js +356 -0
  11. package/incrementalBuilder.proc.js +89 -17394
  12. package/index-1xdrsbry.js +1447 -0
  13. package/index-45aj5ry0.js +990 -0
  14. package/index-5vvwc0cz.js +559 -0
  15. package/index-61keag0s.js +40 -0
  16. package/index-6pz1j0zj.js +62 -0
  17. package/index-73pr2cmy.js +534 -0
  18. package/index-76rn3g2c.js +76 -0
  19. package/index-85msc0wg.js +161 -0
  20. package/index-8pkbzj26.js +840 -0
  21. package/index-8rc0bm04.js +514 -0
  22. package/index-9sp6fsc5.js +1884 -0
  23. package/index-a6sbyy0b.js +2769 -0
  24. package/index-b0brjbp3.js +83 -0
  25. package/index-fgc8r6dj.js +33 -0
  26. package/index-h6ca6qg0.js +2777 -0
  27. package/index-hdqztm58.js +758 -0
  28. package/index-hwzpw9c1.js +202 -0
  29. package/index-pmm9e2jf.js +120 -0
  30. package/index-qaq13qk3.js +80 -0
  31. package/index-qhtr07v8.js +1072 -0
  32. package/index-r24hmh0q.js +4 -0
  33. package/index-ss469dec.js +11 -0
  34. package/index-swf4bmbg.js +25 -0
  35. package/index-w7fyqjrw.js +462 -0
  36. package/index-wnp7hwq7.js +193 -0
  37. package/index-wq8jwx8z.js +224 -0
  38. package/index-x53a5nya.js +301 -0
  39. package/index-xmc2w32q.js +4359 -0
  40. package/index-y3hdhy4p.js +229 -0
  41. package/index.js +54 -23340
  42. package/library.command-r15zdqvp.js +33 -0
  43. package/localRegistry.command-5tcahs3f.js +178 -0
  44. package/module.command-qrj3kmyz.js +54 -0
  45. package/package.command-r8sq5kzp.js +43 -0
  46. package/package.json +2 -2
  47. package/page.command-c6xdx0xm.js +24 -0
  48. package/primitive.command-pv9ssmtf.js +62 -0
  49. package/quality.command-es67wvdp.js +809 -0
  50. package/repair.command-677675vw.js +67 -0
  51. package/routeSourceValidator-wbhmbwpj.js +132 -0
  52. package/scalar.command-kabkd6wd.js +35 -0
  53. package/templates/app/page/_layout.tsx +19 -1
  54. package/templates/app/public/fonts/Pretendard-Black.woff2 +0 -0
  55. package/templates/app/public/fonts/Pretendard-Bold.woff2 +0 -0
  56. package/templates/app/public/fonts/Pretendard-ExtraBold.woff2 +0 -0
  57. package/templates/app/public/fonts/Pretendard-ExtraLight.woff2 +0 -0
  58. package/templates/app/public/fonts/Pretendard-Light.woff2 +0 -0
  59. package/templates/app/public/fonts/Pretendard-Medium.woff2 +0 -0
  60. package/templates/app/public/fonts/Pretendard-Regular.woff2 +0 -0
  61. package/templates/app/public/fonts/Pretendard-SemiBold.woff2 +0 -0
  62. package/templates/app/public/fonts/Pretendard-Thin.woff2 +0 -0
  63. package/templates/facetIndex/index.ts +1 -1
  64. package/typeChecker-kravn7ns.js +8 -0
  65. package/typecheck.proc.js +4 -194
  66. package/workflow.command-64r6cw0w.js +108 -0
  67. package/workspace.command-vrws0rgx.js +435 -0
  68. package/README.ko.md +0 -72
  69. package/README.md +0 -85
@@ -0,0 +1,1072 @@
1
+ // @bun
2
+ import {
3
+ WorkflowRunner
4
+ } from "./index-45aj5ry0.js";
5
+ import {
6
+ ScalarScript
7
+ } from "./index-y3hdhy4p.js";
8
+ import {
9
+ PrimitiveScript
10
+ } from "./index-73pr2cmy.js";
11
+ import {
12
+ RepairRunner
13
+ } from "./index-85msc0wg.js";
14
+ import {
15
+ isPlaceholderAppId
16
+ } from "./index-1xdrsbry.js";
17
+ import {
18
+ AkanContextAnalyzer,
19
+ akanMcpInstallConfigPaths,
20
+ akanMcpInstallTargets,
21
+ codexMcpConfigPath,
22
+ createAkanCodexMcpServerBlock,
23
+ createAkanMcpServer,
24
+ renderDoctorText,
25
+ resourceList,
26
+ upsertCodexMcpServerBlock
27
+ } from "./index-8rc0bm04.js";
28
+ import {
29
+ getMobileTargets
30
+ } from "./index-76rn3g2c.js";
31
+ import {
32
+ ModuleScript
33
+ } from "./index-8pkbzj26.js";
34
+ import {
35
+ Prompter
36
+ } from "./index-qaq13qk3.js";
37
+ import {
38
+ buildAkanModuleContextIndex,
39
+ createWorkflowBaselineSummary,
40
+ createWorkflowStepRegistry,
41
+ jsonText,
42
+ toolingRolloutGate
43
+ } from "./index-h6ca6qg0.js";
44
+ import {
45
+ CommandContainer,
46
+ runner,
47
+ script
48
+ } from "./index-hdqztm58.js";
49
+ import {
50
+ AppExecutor
51
+ } from "./index-a6sbyy0b.js";
52
+
53
+ // pkgs/@akanjs/cli/context/context.script.ts
54
+ import { Logger } from "akanjs/common";
55
+
56
+ // pkgs/@akanjs/devkit/akanMcpContract.ts
57
+ import path from "path";
58
+ var emptySchema = { type: "object", properties: {} };
59
+ var stringProperty = { type: "string" };
60
+ var booleanProperty = { type: "boolean" };
61
+ var objectProperty = { type: "object", additionalProperties: true };
62
+ var stringArrayProperty = { type: "array", items: stringProperty };
63
+ var inspectAkanContextRequestTypes = [
64
+ "workspaceOverview",
65
+ "moduleContext",
66
+ "fieldInsertionContext",
67
+ "workflowDiagnostics",
68
+ "escape"
69
+ ];
70
+ var inspectAkanContextRequestTypeProperty = { type: "string", enum: inspectAkanContextRequestTypes };
71
+ var inspectAkanContextRequestBranches = [
72
+ {
73
+ type: "object",
74
+ properties: { type: { const: "workspaceOverview" } },
75
+ required: ["type"]
76
+ },
77
+ {
78
+ type: "object",
79
+ properties: { type: { const: "moduleContext" }, app: stringProperty, module: stringProperty },
80
+ required: ["type", "app", "module"]
81
+ },
82
+ {
83
+ type: "object",
84
+ properties: {
85
+ type: { const: "fieldInsertionContext" },
86
+ app: stringProperty,
87
+ module: stringProperty,
88
+ field: stringProperty,
89
+ fieldType: stringProperty
90
+ },
91
+ required: ["type", "app", "module", "field", "fieldType"]
92
+ },
93
+ {
94
+ type: "object",
95
+ properties: { type: { const: "workflowDiagnostics" }, runIdOrPlan: stringProperty },
96
+ required: ["type", "runIdOrPlan"]
97
+ },
98
+ {
99
+ type: "object",
100
+ properties: { type: { const: "escape" }, reason: stringProperty, nextStep: stringProperty },
101
+ required: ["type", "reason"]
102
+ }
103
+ ];
104
+ var inspectAkanContextInputSchema = {
105
+ type: "object",
106
+ properties: {
107
+ question: {
108
+ ...stringProperty,
109
+ description: "The user-facing question the agent is trying to answer before reading source bodies."
110
+ },
111
+ draft: {
112
+ type: "object",
113
+ properties: {
114
+ reason: stringProperty,
115
+ type: inspectAkanContextRequestTypeProperty
116
+ },
117
+ required: ["reason", "type"]
118
+ },
119
+ review: {
120
+ ...stringProperty,
121
+ description: "A short self-review explaining why this minimal request is sufficient."
122
+ },
123
+ request: {
124
+ type: "object",
125
+ properties: {
126
+ type: inspectAkanContextRequestTypeProperty,
127
+ app: stringProperty,
128
+ module: stringProperty,
129
+ field: stringProperty,
130
+ fieldType: stringProperty,
131
+ runIdOrPlan: stringProperty,
132
+ reason: stringProperty,
133
+ nextStep: stringProperty
134
+ },
135
+ required: ["type"],
136
+ oneOf: inspectAkanContextRequestBranches
137
+ }
138
+ },
139
+ required: ["question", "draft", "review", "request"]
140
+ };
141
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
142
+ var slugPart = (value) => typeof value === "string" ? value.trim().replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() : "";
143
+ var optionalString = (args, key) => {
144
+ const value = args[key];
145
+ return typeof value === "string" && value ? value : undefined;
146
+ };
147
+ var nestedStringArg = (args, key) => {
148
+ const value = args[key];
149
+ if (typeof value !== "string" || !value)
150
+ throw new Error(`MCP tool argument "${key}" is required.`);
151
+ return value;
152
+ };
153
+ var isInspectAkanContextRequestType = (value) => typeof value === "string" && inspectAkanContextRequestTypes.includes(value);
154
+ var parseJsonOutput = (output) => JSON.parse(output);
155
+ var workspacePath = (workspace, filePath) => path.isAbsolute(filePath) ? filePath : path.join(workspace.workspaceRoot, filePath);
156
+ var defaultWorkflowPlanPath = (workflow, inputs) => {
157
+ const slug = [
158
+ slugPart(workflow),
159
+ slugPart(inputs.app),
160
+ slugPart(inputs.module),
161
+ slugPart(inputs.field),
162
+ slugPart(inputs.scalar),
163
+ slugPart(inputs.surface),
164
+ slugPart(inputs.mutation),
165
+ slugPart(inputs.slice)
166
+ ].filter(Boolean).join("-");
167
+ return `.akan/workflows/plans/${slug || "workflow-plan"}.json`;
168
+ };
169
+ var applyFirstPolicy = {
170
+ mode: "apply-first",
171
+ directSourceEdits: "fallback-only",
172
+ applyRequiredWhen: ["plan_workflow returns planPath", "plan_workflow returns next.tool=apply_workflow"],
173
+ approvalMeaning: "requiresApproval is an agent/user review signal before apply_workflow mutates files; it is not a separate MCP permission gate.",
174
+ validationRequiredAfterApply: ["validationTarget", "applyReportPath"],
175
+ fallbackAllowedWhen: [
176
+ "list_workflows and explain_workflow show no matching workflow",
177
+ "apply_workflow reports unsupported/no-op/failed diagnostics that require manual action",
178
+ "recommendations include manual-action follow-up after workflow apply and repairs"
179
+ ],
180
+ baselineDiagnosticsPolicy: "Do not fix unrelated baselineDiagnostics unless the user asks."
181
+ };
182
+ var stringArg = (args, key) => {
183
+ const value = args[key];
184
+ if (typeof value !== "string" || !value)
185
+ throw new Error(`MCP tool argument "${key}" is required.`);
186
+ return value;
187
+ };
188
+ var workflowInputsArg = (args) => {
189
+ const value = args.inputs;
190
+ if (!value || typeof value !== "object" || Array.isArray(value))
191
+ return {};
192
+ return value;
193
+ };
194
+ var inspectAkanContextPropsArg = (args) => {
195
+ const question = stringArg(args, "question");
196
+ const review = stringArg(args, "review");
197
+ if (!isRecord(args.draft))
198
+ throw new Error('MCP tool argument "draft" is required.');
199
+ if (!isRecord(args.request))
200
+ throw new Error('MCP tool argument "request" is required.');
201
+ if (!isInspectAkanContextRequestType(args.draft.type)) {
202
+ throw new Error('MCP tool argument "draft.type" must be a supported inspect_akan_context request type.');
203
+ }
204
+ if (!isInspectAkanContextRequestType(args.request.type)) {
205
+ throw new Error('MCP tool argument "request.type" must be a supported inspect_akan_context request type.');
206
+ }
207
+ const draft = { reason: nestedStringArg(args.draft, "reason"), type: args.draft.type };
208
+ const request = args.request;
209
+ if (request.type === "workspaceOverview")
210
+ return { question, draft, review, request: { type: request.type } };
211
+ if (request.type === "moduleContext")
212
+ return {
213
+ question,
214
+ draft,
215
+ review,
216
+ request: {
217
+ type: request.type,
218
+ app: nestedStringArg(request, "app"),
219
+ module: nestedStringArg(request, "module")
220
+ }
221
+ };
222
+ if (request.type === "fieldInsertionContext")
223
+ return {
224
+ question,
225
+ draft,
226
+ review,
227
+ request: {
228
+ type: request.type,
229
+ app: nestedStringArg(request, "app"),
230
+ module: nestedStringArg(request, "module"),
231
+ field: nestedStringArg(request, "field"),
232
+ fieldType: nestedStringArg(request, "fieldType")
233
+ }
234
+ };
235
+ if (request.type === "workflowDiagnostics")
236
+ return {
237
+ question,
238
+ draft,
239
+ review,
240
+ request: { type: request.type, runIdOrPlan: nestedStringArg(request, "runIdOrPlan") }
241
+ };
242
+ return {
243
+ question,
244
+ draft,
245
+ review,
246
+ request: {
247
+ type: "escape",
248
+ reason: nestedStringArg(request, "reason"),
249
+ nextStep: optionalString(request, "nextStep")
250
+ }
251
+ };
252
+ };
253
+ var inspectDiagnostic = (severity, code, message) => ({ severity, code, message });
254
+ var moduleEvidence = (module) => ({
255
+ kind: "module",
256
+ target: `${module.sysName}:${module.name}`,
257
+ path: module.path,
258
+ summary: `${module.kind} module at ${module.path} with ${module.files.length} source file(s).`
259
+ });
260
+ var moduleCandidate = (module) => ({
261
+ app: module.sysName,
262
+ sysType: module.sysType,
263
+ module: module.name,
264
+ kind: module.kind,
265
+ path: module.path,
266
+ files: module.files
267
+ });
268
+ var readonlyMcpTools = [
269
+ {
270
+ name: "inspect_akan_context",
271
+ description: "Read-only typed context inspection. Use question -> draft -> review -> request before source body reads; choose fieldInsertionContext for add-field evidence and escape when source body or outside evidence is required.",
272
+ inputSchema: inspectAkanContextInputSchema
273
+ },
274
+ {
275
+ name: "get_workspace_summary",
276
+ description: "Legacy broad workspace summary. Prefer inspect_akan_context.workspaceOverview for typed source-body-free evidence.",
277
+ inputSchema: emptySchema
278
+ },
279
+ {
280
+ name: "list_apps",
281
+ description: "List Akan apps from the workspace context without reading source bodies.",
282
+ inputSchema: emptySchema
283
+ },
284
+ {
285
+ name: "list_modules",
286
+ description: "List Akan modules across apps and libs; use inspect_akan_context.moduleContext for typed evidence.",
287
+ inputSchema: emptySchema
288
+ },
289
+ {
290
+ name: "get_module_context",
291
+ description: "Legacy broad module context. Pass app in monorepos; prefer inspect_akan_context.moduleContext or fieldInsertionContext for typed add-field evidence.",
292
+ inputSchema: { type: "object", properties: { app: stringProperty, module: stringProperty }, required: ["module"] }
293
+ },
294
+ {
295
+ name: "get_guideline",
296
+ description: "Return an Akan agent guideline resource by name.",
297
+ inputSchema: { type: "object", properties: { name: stringProperty }, required: ["name"] }
298
+ },
299
+ {
300
+ name: "explain_command",
301
+ description: "Explain one Akan CLI command and its agent-facing workflow policy.",
302
+ inputSchema: { type: "object", properties: { command: stringProperty }, required: ["command"] }
303
+ },
304
+ {
305
+ name: "doctor_workspace",
306
+ description: "Report workspace diagnostics, optionally split into baseline and workflow scopes.",
307
+ inputSchema: {
308
+ type: "object",
309
+ properties: {
310
+ strict: booleanProperty,
311
+ runIdOrPlan: stringProperty,
312
+ changedFiles: stringArrayProperty,
313
+ includeBaselineDetails: booleanProperty
314
+ }
315
+ }
316
+ },
317
+ {
318
+ name: "get_validation_contract",
319
+ description: "Return validation, artifact chain, and apply-first fallback policy.",
320
+ inputSchema: emptySchema
321
+ }
322
+ ];
323
+ var planMcpTools = [
324
+ {
325
+ name: "list_workflows",
326
+ description: "List available read-only workflow specs before creating a plan.",
327
+ inputSchema: emptySchema
328
+ },
329
+ {
330
+ name: "explain_workflow",
331
+ description: "Explain one workflow's inputs, predicted changes, validation, and completion criteria.",
332
+ inputSchema: { type: "object", properties: { workflow: stringProperty }, required: ["workflow"] }
333
+ },
334
+ {
335
+ name: "plan_workflow",
336
+ description: "Create a read-only workflow plan after context inspection and return planPath plus next.tool=apply_workflow.",
337
+ inputSchema: {
338
+ type: "object",
339
+ properties: { workflow: stringProperty, inputs: objectProperty, out: stringProperty },
340
+ required: ["workflow"]
341
+ }
342
+ }
343
+ ];
344
+ var applyMcpTools = [
345
+ {
346
+ name: "apply_workflow",
347
+ description: "Apply a stored workflow plan before direct edits and return validationTarget for run_validation.",
348
+ inputSchema: {
349
+ type: "object",
350
+ properties: { planPath: stringProperty, dryRun: booleanProperty },
351
+ required: ["planPath"]
352
+ }
353
+ },
354
+ {
355
+ name: "run_validation",
356
+ description: "Validate a plan, apply report, validationTarget, or run artifact.",
357
+ inputSchema: {
358
+ type: "object",
359
+ properties: { runIdOrPlan: stringProperty, includeBaselineDetails: booleanProperty },
360
+ required: ["runIdOrPlan"]
361
+ }
362
+ },
363
+ {
364
+ name: "repair_generated",
365
+ description: "Refresh generated Akan files before direct generated-file edits.",
366
+ inputSchema: { type: "object", properties: { app: stringProperty }, required: ["app"] }
367
+ },
368
+ {
369
+ name: "repair_imports",
370
+ description: "Run the import repair path before manual import edits.",
371
+ inputSchema: { type: "object", properties: { target: stringProperty }, required: ["target"] }
372
+ },
373
+ {
374
+ name: "repair_module_shape",
375
+ description: "Report module-shape repair actions before direct module file fixes.",
376
+ inputSchema: {
377
+ type: "object",
378
+ properties: { app: stringProperty, module: stringProperty },
379
+ required: ["app", "module"]
380
+ }
381
+ }
382
+ ];
383
+ var listAkanMcpTools = (mode = "readonly") => {
384
+ if (mode === "readonly")
385
+ return readonlyMcpTools;
386
+ if (mode === "plan")
387
+ return [...readonlyMcpTools, ...planMcpTools];
388
+ return [...readonlyMcpTools, ...planMcpTools, ...applyMcpTools];
389
+ };
390
+ var createAkanValidationContract = (listTools = listAkanMcpTools) => ({
391
+ schemaVersion: 1,
392
+ reports: ["WorkflowPlan", "WorkflowApplyReport", "WorkflowValidationRunReport", "RepairReport"],
393
+ modes: {
394
+ readonly: listTools("readonly").map((tool) => tool.name),
395
+ plan: listTools("plan").map((tool) => tool.name),
396
+ apply: listTools("apply").map((tool) => tool.name)
397
+ },
398
+ validationCommands: [
399
+ "akan workflow validate <run-id-or-plan> --format json",
400
+ "akan workflow report <run-id> --format json",
401
+ "akan doctor --strict --format json"
402
+ ],
403
+ validationFailureScopes: ["workspace-config", "environment", "source-change", "unknown"],
404
+ artifactChainFields: ["planPath", "applyReportPath", "repairReportPath", "runId", "validationTarget", "next"],
405
+ diagnosticScopes: ["baseline", "workflow", "unknown"],
406
+ validationStatuses: {
407
+ sourceStatus: ["passed", "failed", "unknown"],
408
+ workspaceStatus: ["passed", "failed", "unknown"],
409
+ validationCommandsStatus: ["passed", "failed", "unknown"],
410
+ baselineStatus: ["passed", "failed", "unknown"],
411
+ overallStatus: [
412
+ "passed",
413
+ "failed",
414
+ "passed-with-baseline-blockers",
415
+ "blocked-by-workspace-config",
416
+ "blocked-by-environment"
417
+ ],
418
+ baselineSummary: "Default MCP validation output summarizes baseline diagnostics; pass includeBaselineDetails=true for full baselineDiagnostics.",
419
+ knownBlockers: "Repeated workspace-config/environment failures are summarized before command output."
420
+ },
421
+ moduleContextInputs: {
422
+ module: "required",
423
+ app: "required"
424
+ },
425
+ generatedFreshnessStatuses: ["fresh", "stale", "missing", "unknown"],
426
+ toolingRolloutGate,
427
+ directEditFallbackPolicy: applyFirstPolicy,
428
+ applyReportFields: [
429
+ "summary",
430
+ "appliedCommands",
431
+ "recommendedValidationCommands",
432
+ "commands",
433
+ "recommendations",
434
+ "validationTarget"
435
+ ],
436
+ repairCommands: [
437
+ "akan repair generated --app <app-or-lib> --format json",
438
+ "akan repair format --target <app-or-lib-or-pkg> --format json",
439
+ "akan repair imports --target <app-or-lib-or-pkg> --format json",
440
+ "akan repair dictionary --app <app-or-lib> --module <module> --format json",
441
+ "akan repair module-shape --app <app-or-lib> --module <module> --format json"
442
+ ]
443
+ });
444
+ var inspectAkanContext = async (workspace, args) => {
445
+ const props = inspectAkanContextPropsArg(args);
446
+ const diagnostics = [];
447
+ if (props.draft.type !== props.request.type) {
448
+ diagnostics.push(inspectDiagnostic("warning", "inspect-draft-request-mismatch", `Draft chose ${props.draft.type}, but request uses ${props.request.type}.`));
449
+ }
450
+ const baseResult = (type, evidence, next, data) => ({
451
+ schemaVersion: 1,
452
+ type,
453
+ question: props.question,
454
+ diagnostics,
455
+ evidence,
456
+ next,
457
+ ...data ? { data } : {}
458
+ });
459
+ if (props.request.type === "escape") {
460
+ return baseResult("escape", [{ kind: "escape", summary: props.request.reason }], {
461
+ action: "escape",
462
+ reason: props.request.reason,
463
+ ...props.request.nextStep ? { args: { nextStep: props.request.nextStep } } : {}
464
+ }, { reason: props.request.reason, nextStep: props.request.nextStep ?? null });
465
+ }
466
+ if (props.request.type === "workflowDiagnostics") {
467
+ const doctor = await AkanContextAnalyzer.doctor(workspace, {
468
+ strict: true,
469
+ runIdOrPlan: workspacePath(workspace, props.request.runIdOrPlan)
470
+ });
471
+ const baselineSummary = createWorkflowBaselineSummary((doctor.baselineDiagnostics ?? []).map((diagnostic) => ({
472
+ severity: diagnostic.severity,
473
+ code: diagnostic.code,
474
+ message: diagnostic.message,
475
+ scope: diagnostic.scope,
476
+ context: diagnostic.context
477
+ })), { detailsIncluded: false });
478
+ diagnostics.push(...(doctor.workflowDiagnostics ?? doctor.diagnostics).map((diagnostic) => ({
479
+ severity: diagnostic.severity,
480
+ code: diagnostic.code,
481
+ message: diagnostic.message,
482
+ scope: diagnostic.scope,
483
+ context: {
484
+ ...diagnostic.context,
485
+ ...diagnostic.path ? { paths: [diagnostic.path] } : {}
486
+ }
487
+ })));
488
+ return baseResult("workflowDiagnostics", [
489
+ {
490
+ kind: "workflow",
491
+ target: props.request.runIdOrPlan,
492
+ summary: `Workflow diagnostics status is ${doctor.status}.`
493
+ }
494
+ ], {
495
+ action: doctor.status === "passed" ? "answer" : "validate",
496
+ reason: doctor.status === "passed" ? "No strict workspace diagnostics were found for the workflow context." : "Review diagnostics before applying or manually editing source files."
497
+ }, {
498
+ status: doctor.status,
499
+ baselineSummary,
500
+ baselineDiagnostics: 0,
501
+ workflowDiagnostics: doctor.workflowDiagnostics?.length ?? 0
502
+ });
503
+ }
504
+ const context = await AkanContextAnalyzer.analyze(workspace, {
505
+ app: props.request.type === "moduleContext" || props.request.type === "fieldInsertionContext" ? props.request.app : null,
506
+ module: props.request.type === "moduleContext" || props.request.type === "fieldInsertionContext" ? props.request.module : null,
507
+ includeAbstractContent: false
508
+ });
509
+ if (props.request.type === "workspaceOverview") {
510
+ return baseResult("workspaceOverview", [
511
+ {
512
+ kind: "workspace",
513
+ summary: `${context.repoName} has ${context.apps.length} app(s), ${context.libs.length} lib(s), and ${context.pkgs.length} package(s).`
514
+ }
515
+ ], {
516
+ action: "inspect",
517
+ reason: "Choose moduleContext or fieldInsertionContext for module-scoped evidence before planning a workflow.",
518
+ tool: "inspect_akan_context"
519
+ }, {
520
+ repoName: context.repoName,
521
+ apps: context.apps.map((app) => ({ name: app.name, path: app.path, modules: app.modules.length })),
522
+ libs: context.libs.map((lib) => ({ name: lib.name, path: lib.path, modules: lib.modules.length })),
523
+ pkgs: context.pkgs.map((pkg) => ({ name: pkg.name, path: pkg.path, version: pkg.version ?? null })),
524
+ generatedFiles: context.generatedFiles,
525
+ validationCommands: context.validationCommands
526
+ });
527
+ }
528
+ const modules = AkanContextAnalyzer.findModules(context, props.request.module, {
529
+ app: props.request.app ?? null
530
+ });
531
+ if (modules.length === 0) {
532
+ diagnostics.push(inspectDiagnostic("error", "inspect-module-not-found", `No module matched ${props.request.app ? `${props.request.app}:` : ""}${props.request.module}.`));
533
+ return baseResult(props.request.type, [], {
534
+ action: "escape",
535
+ reason: "The requested module was not found in the workspace index; inspect workspace files or clarify the target."
536
+ }, { candidates: [] });
537
+ }
538
+ if (!props.request.app && modules.length > 1) {
539
+ diagnostics.push(inspectDiagnostic("error", "inspect-module-ambiguous", `Multiple modules match "${props.request.module}". Re-run with app to disambiguate.`));
540
+ return baseResult(props.request.type, modules.map(moduleEvidence), {
541
+ action: "clarify",
542
+ reason: "Multiple modules have the same name; choose an app before planning a workflow."
543
+ }, { candidates: modules.map(moduleCandidate) });
544
+ }
545
+ const module = modules[0];
546
+ if (props.request.type === "moduleContext") {
547
+ return baseResult("moduleContext", [moduleEvidence(module)], {
548
+ action: "inspect",
549
+ reason: "Use fieldInsertionContext when preparing add-field or escape when source body details are required.",
550
+ tool: "inspect_akan_context"
551
+ }, { module: moduleCandidate(module) });
552
+ }
553
+ if (module.kind !== "domain") {
554
+ diagnostics.push(inspectDiagnostic("error", "field-insertion-unsupported-module-kind", `fieldInsertionContext currently targets domain modules; ${module.sysName}:${module.name} is ${module.kind}.`));
555
+ }
556
+ const moduleIndex = await buildAkanModuleContextIndex(workspace, module, { field: props.request.field });
557
+ diagnostics.push(...moduleIndex.diagnostics);
558
+ return baseResult("fieldInsertionContext", [
559
+ moduleEvidence(module),
560
+ {
561
+ kind: "field-insertion",
562
+ target: `${module.sysName}:${module.name}.${props.request.field}`,
563
+ path: module.path,
564
+ summary: "M2 returns source-body-free AST index evidence for constant fields, dictionary model labels, and light projection fields."
565
+ }
566
+ ], module.kind === "domain" ? {
567
+ action: "plan_workflow",
568
+ reason: "The module target is unambiguous; create an add-field workflow plan before source edits.",
569
+ tool: "plan_workflow",
570
+ args: {
571
+ workflow: "add-field",
572
+ inputs: {
573
+ app: module.sysName,
574
+ module: module.name,
575
+ field: props.request.field,
576
+ type: props.request.fieldType
577
+ }
578
+ }
579
+ } : {
580
+ action: "escape",
581
+ reason: "This module kind is not a supported add-field target in the P1 context contract."
582
+ }, {
583
+ target: {
584
+ app: module.sysName,
585
+ module: module.name,
586
+ field: props.request.field,
587
+ fieldType: props.request.fieldType
588
+ },
589
+ files: moduleIndex.files,
590
+ moduleIndex
591
+ });
592
+ };
593
+
594
+ // pkgs/@akanjs/cli/context/context.workflowRegistry.ts
595
+ var createCliWorkflowStepRegistry = (workspace) => createWorkflowStepRegistry({
596
+ workspace,
597
+ createModule: (sys, module) => CommandContainer.get(ModuleScript).createModuleTemplate(sys, module),
598
+ createScalar: (sys, scalar) => CommandContainer.get(ScalarScript).createScalar(sys, scalar),
599
+ createUi: (input) => CommandContainer.get(PrimitiveScript).createUi(workspace, input),
600
+ addField: (input) => CommandContainer.get(PrimitiveScript).addField(workspace, input),
601
+ addEnumField: (input) => CommandContainer.get(PrimitiveScript).addEnumField(workspace, input),
602
+ addMutation: (input) => CommandContainer.get(PrimitiveScript).addMutation(workspace, input),
603
+ addSlice: (input) => CommandContainer.get(PrimitiveScript).addSlice(workspace, input)
604
+ });
605
+
606
+ // pkgs/@akanjs/cli/context/context.runner.ts
607
+ var workflowDiagnosticFromContext = (diagnostic) => ({
608
+ severity: diagnostic.severity,
609
+ code: diagnostic.code,
610
+ message: diagnostic.message,
611
+ scope: diagnostic.scope,
612
+ context: diagnostic.context
613
+ });
614
+ var compactDoctorWorkspaceResult = (result, includeBaselineDetails) => {
615
+ const baselineDiagnostics = result.baselineDiagnostics ?? [];
616
+ if (baselineDiagnostics.length === 0) {
617
+ return {
618
+ ...result,
619
+ baselineSummary: createWorkflowBaselineSummary([], { detailsIncluded: includeBaselineDetails })
620
+ };
621
+ }
622
+ const baselineSummary = createWorkflowBaselineSummary(baselineDiagnostics.map(workflowDiagnosticFromContext), {
623
+ detailsIncluded: includeBaselineDetails
624
+ });
625
+ return {
626
+ ...result,
627
+ baselineSummary,
628
+ diagnostics: includeBaselineDetails ? result.diagnostics : result.diagnostics.filter((diagnostic) => diagnostic.scope !== "baseline"),
629
+ baselineDiagnostics: includeBaselineDetails ? baselineDiagnostics : []
630
+ };
631
+ };
632
+
633
+ class ContextRunner extends runner("context") {
634
+ async getContext(workspace, {
635
+ format = "markdown",
636
+ app = null,
637
+ module = null
638
+ } = {}) {
639
+ const context = await AkanContextAnalyzer.analyze(workspace, {
640
+ app,
641
+ module,
642
+ includeAbstractContent: !!module
643
+ });
644
+ return format === "json" ? jsonText(context) : AkanContextAnalyzer.renderMarkdown(context, { module });
645
+ }
646
+ async doctor(workspace, {
647
+ format = "text",
648
+ strict = false,
649
+ ios = false
650
+ } = {}) {
651
+ if (ios)
652
+ return await this.#doctorIos(workspace, format);
653
+ const result = await AkanContextAnalyzer.doctor(workspace, { strict });
654
+ return format === "json" ? jsonText(result) : renderDoctorText(result);
655
+ }
656
+ async#doctorIos(workspace, format) {
657
+ const appNames = await workspace.getApps();
658
+ const diagnostics = [];
659
+ for (const appName of appNames) {
660
+ const app = AppExecutor.from(workspace, appName);
661
+ const config = await app.getConfig();
662
+ if (!config.hasMobileConfig)
663
+ continue;
664
+ for (const { name, config: target } of await getMobileTargets(app)) {
665
+ if (!isPlaceholderAppId(target.appId))
666
+ continue;
667
+ diagnostics.push({
668
+ severity: "warning",
669
+ code: "mobile-appid-placeholder",
670
+ path: `apps/${appName}/akan.config.ts`,
671
+ message: `Mobile target '${name}' uses placeholder bundle id '${target.appId}'. Apple's developer portal almost always already claims it, so signing to a physical device fails with "cannot be registered to your development team". Set a unique mobile.appId (reverse-DNS of your org).`
672
+ });
673
+ }
674
+ }
675
+ const status = diagnostics.some((diagnostic) => diagnostic.severity === "error") ? "failed" : "passed";
676
+ if (format === "json")
677
+ return jsonText({ schemaVersion: 1, kind: "ios", status, diagnostics });
678
+ const lines = [`Akan iOS diagnostics for ${workspace.repoName}`];
679
+ if (diagnostics.length === 0)
680
+ lines.push(" No mobile configuration issues found.");
681
+ else
682
+ for (const diagnostic of diagnostics) {
683
+ lines.push(` [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`);
684
+ lines.push(` ${diagnostic.path}`);
685
+ }
686
+ return lines.join(`
687
+ `);
688
+ }
689
+ async getGuidelineResource(name) {
690
+ return await Prompter.getInstruction(name);
691
+ }
692
+ async installMcp(workspace, target, { force = false, mode = "readonly" } = {}) {
693
+ if (target === "codex")
694
+ return await this.#installCodexMcp(workspace, { force, mode });
695
+ return await this.#installJsonMcp(workspace, target, { force, mode });
696
+ }
697
+ async#installJsonMcp(workspace, target, { force, mode }) {
698
+ const configPath = akanMcpInstallConfigPaths[target];
699
+ const existing = await workspace.exists(configPath) ? await workspace.readJson(configPath) : {};
700
+ const mcpServers = existing.mcpServers ?? {};
701
+ const currentAkanServer = mcpServers.akan;
702
+ const nextAkanServer = createAkanMcpServer(target, mode);
703
+ if (currentAkanServer && !force && JSON.stringify(currentAkanServer) !== JSON.stringify(nextAkanServer)) {
704
+ throw new Error(`${configPath} already has an "akan" MCP server. Re-run with --force to overwrite it.`);
705
+ }
706
+ const nextConfig = {
707
+ ...existing,
708
+ mcpServers: {
709
+ ...mcpServers,
710
+ akan: nextAkanServer
711
+ }
712
+ };
713
+ await workspace.writeFile(configPath, `${JSON.stringify(nextConfig, null, 2)}
714
+ `);
715
+ return configPath;
716
+ }
717
+ async#installCodexMcp(workspace, { force, mode }) {
718
+ const existing = await workspace.exists(codexMcpConfigPath) ? await workspace.readFile(codexMcpConfigPath) : "";
719
+ const merged = upsertCodexMcpServerBlock(existing, createAkanCodexMcpServerBlock(mode), { force });
720
+ await workspace.writeFile(codexMcpConfigPath, merged);
721
+ return codexMcpConfigPath;
722
+ }
723
+ listMcpTools(mode = "readonly") {
724
+ return listAkanMcpTools(mode);
725
+ }
726
+ async callMcpTool(workspace, name, args = {}, { mode = "readonly" } = {}) {
727
+ const availableTools = this.listMcpTools(mode);
728
+ if (!availableTools.some((tool) => tool.name === name)) {
729
+ throw new Error(`MCP tool "${name}" is not available in ${mode} mode.`);
730
+ }
731
+ if (name === "inspect_akan_context")
732
+ return await inspectAkanContext(workspace, args);
733
+ if (name === "get_workspace_summary" || name === "list_apps" || name === "list_modules" || name === "get_module_context") {
734
+ const context = await AkanContextAnalyzer.analyze(workspace, {
735
+ app: args.app ?? null,
736
+ module: args.module ?? null,
737
+ includeAbstractContent: name === "get_module_context"
738
+ });
739
+ if (name === "get_workspace_summary")
740
+ return context;
741
+ if (name === "list_apps")
742
+ return context.apps;
743
+ if (name === "list_modules")
744
+ return AkanContextAnalyzer.findModules(context);
745
+ const modules = AkanContextAnalyzer.findModules(context, args.module, {
746
+ app: args.app ?? null
747
+ });
748
+ if (name === "get_module_context" && !args.app && modules.length > 1) {
749
+ return {
750
+ diagnostics: [
751
+ {
752
+ severity: "error",
753
+ code: "module-context-ambiguous",
754
+ message: `Multiple modules match "${args.module}". Re-run get_module_context with an app argument.`,
755
+ input: "app"
756
+ }
757
+ ],
758
+ candidates: modules.map((module) => ({
759
+ app: module.sysName,
760
+ sysType: module.sysType,
761
+ module: module.name,
762
+ path: module.path
763
+ }))
764
+ };
765
+ }
766
+ return modules;
767
+ }
768
+ if (name === "get_guideline")
769
+ return await Prompter.getInstruction(stringArg(args, "name"));
770
+ if (name === "doctor_workspace")
771
+ return compactDoctorWorkspaceResult(await AkanContextAnalyzer.doctor(workspace, {
772
+ strict: !!args.strict,
773
+ runIdOrPlan: typeof args.runIdOrPlan === "string" ? workspacePath(workspace, args.runIdOrPlan) : null,
774
+ changedFiles: Array.isArray(args.changedFiles) ? args.changedFiles.filter((file) => typeof file === "string") : []
775
+ }), !!args.includeBaselineDetails);
776
+ if (name === "explain_command")
777
+ return this.explainCommand(stringArg(args, "command"));
778
+ if (name === "get_validation_contract")
779
+ return createAkanValidationContract((mode2) => this.listMcpTools(mode2));
780
+ if (name === "list_workflows")
781
+ return parseJsonOutput(new WorkflowRunner().list({ format: "json" }));
782
+ if (name === "explain_workflow")
783
+ return parseJsonOutput(new WorkflowRunner().explain(stringArg(args, "workflow"), { format: "json" }));
784
+ if (name === "plan_workflow") {
785
+ const workflow = stringArg(args, "workflow");
786
+ const inputs = workflowInputsArg(args);
787
+ const planPath = typeof args.out === "string" && args.out ? args.out : defaultWorkflowPlanPath(workflow, inputs);
788
+ const plan = parseJsonOutput(await new WorkflowRunner().plan(workflow, inputs, {
789
+ format: "json",
790
+ out: workspacePath(workspace, planPath)
791
+ }));
792
+ return {
793
+ ...plan,
794
+ planPath,
795
+ approval: typeof plan === "object" && plan && "approval" in plan ? { ...plan.approval, canApplyWith: { planPath } } : undefined,
796
+ next: { tool: "apply_workflow", args: { planPath } },
797
+ policy: applyFirstPolicy
798
+ };
799
+ }
800
+ if (name === "apply_workflow") {
801
+ const report = parseJsonOutput(await new WorkflowRunner().apply(workspacePath(workspace, stringArg(args, "planPath")), {
802
+ format: "json",
803
+ dryRun: !!args.dryRun,
804
+ workspace,
805
+ registry: createCliWorkflowStepRegistry(workspace)
806
+ }));
807
+ const validationTarget = typeof report.validationTarget === "string" ? report.validationTarget : typeof report.applyReportPath === "string" ? report.applyReportPath : stringArg(args, "planPath");
808
+ return {
809
+ ...report,
810
+ validationTarget,
811
+ next: { tool: "run_validation", args: { runIdOrPlan: validationTarget } },
812
+ policy: applyFirstPolicy
813
+ };
814
+ }
815
+ if (name === "run_validation")
816
+ return parseJsonOutput(await new WorkflowRunner().validate(workspacePath(workspace, stringArg(args, "runIdOrPlan")), {
817
+ format: "json",
818
+ workspace,
819
+ includeBaselineDetails: !!args.includeBaselineDetails
820
+ }));
821
+ if (name === "repair_generated") {
822
+ const report = parseJsonOutput(await new RepairRunner().repair("generated", { workspace, app: stringArg(args, "app"), format: "json" }));
823
+ return { ...report, next: { tool: "doctor_workspace", args: { strict: true } } };
824
+ }
825
+ if (name === "repair_imports")
826
+ return parseJsonOutput(await new RepairRunner().repair("imports", {
827
+ workspace,
828
+ target: stringArg(args, "target"),
829
+ format: "json"
830
+ }));
831
+ if (name === "repair_module_shape")
832
+ return parseJsonOutput(await new RepairRunner().repair("module-shape", {
833
+ workspace,
834
+ app: stringArg(args, "app"),
835
+ module: stringArg(args, "module"),
836
+ format: "json"
837
+ }));
838
+ throw new Error(`Unknown tool: ${name}`);
839
+ }
840
+ async runMcp(workspace, { mode = "readonly" } = {}) {
841
+ const decoder = new TextDecoder;
842
+ let buffer = "";
843
+ const writeMessage = (message, framing) => {
844
+ const payload = JSON.stringify(message);
845
+ if (framing === "newline")
846
+ process.stdout.write(`${payload}
847
+ `);
848
+ else
849
+ process.stdout.write(`Content-Length: ${Buffer.byteLength(payload)}\r
850
+ \r
851
+ ${payload}`);
852
+ };
853
+ const respond = (id, result, framing) => {
854
+ writeMessage({ jsonrpc: "2.0", id, result }, framing);
855
+ };
856
+ const respondError = (id, code, message, framing) => {
857
+ writeMessage({ jsonrpc: "2.0", id, error: { code, message } }, framing);
858
+ };
859
+ const readResource = async (uri) => {
860
+ const context = await AkanContextAnalyzer.analyze(workspace);
861
+ if (uri === "akan://docs/framework" || uri === "akan://guidelines/framework")
862
+ return { uri, mimeType: "text/markdown", text: await Prompter.getInstruction("framework") };
863
+ if (uri === "akan://guidelines/modelSignal")
864
+ return { uri, mimeType: "text/markdown", text: await Prompter.getInstruction("modelSignal") };
865
+ if (uri === "akan://workspace/summary")
866
+ return { uri, mimeType: "application/json", text: jsonText(context, { trailingNewline: false }) };
867
+ if (uri === "akan://workspace/apps")
868
+ return { uri, mimeType: "application/json", text: jsonText(context.apps, { trailingNewline: false }) };
869
+ if (uri === "akan://workspace/modules")
870
+ return {
871
+ uri,
872
+ mimeType: "application/json",
873
+ text: jsonText(AkanContextAnalyzer.findModules(context), { trailingNewline: false })
874
+ };
875
+ const abstractMatch = uri.match(/^akan:\/\/workspace\/modules\/(.+)\/abstract$/);
876
+ if (abstractMatch) {
877
+ const detailed = await AkanContextAnalyzer.analyze(workspace, {
878
+ module: abstractMatch[1],
879
+ includeAbstractContent: true
880
+ });
881
+ const abstract = AkanContextAnalyzer.findModules(detailed, abstractMatch[1])[0]?.abstract;
882
+ return { uri, mimeType: "text/markdown", text: abstract?.content ?? "" };
883
+ }
884
+ throw new Error(`Unknown resource: ${uri}`);
885
+ };
886
+ const handle = async (request, framing) => {
887
+ const params = request.params ?? {};
888
+ if (request.method === "initialize") {
889
+ respond(request.id, {
890
+ protocolVersion: "2024-11-05",
891
+ capabilities: { tools: {}, resources: {} },
892
+ serverInfo: { name: "akan", version: process.env.AKAN_VERSION ?? "0.0.0" }
893
+ }, framing);
894
+ } else if (request.method === "tools/list") {
895
+ respond(request.id, {
896
+ tools: this.listMcpTools(mode)
897
+ }, framing);
898
+ } else if (request.method === "tools/call") {
899
+ const name = params.name;
900
+ const args = params.arguments ?? {};
901
+ try {
902
+ const result = await this.callMcpTool(workspace, name, args, { mode });
903
+ respond(request.id, {
904
+ content: [
905
+ {
906
+ type: "text",
907
+ text: typeof result === "string" ? result : jsonText(result, { trailingNewline: false })
908
+ }
909
+ ]
910
+ }, framing);
911
+ } catch (error) {
912
+ respondError(request.id, -32602, error instanceof Error ? error.message : String(error), framing);
913
+ }
914
+ } else if (request.method === "resources/list") {
915
+ respond(request.id, { resources: resourceList }, framing);
916
+ } else if (request.method === "resources/read") {
917
+ respond(request.id, { contents: [await readResource(params.uri)] }, framing);
918
+ } else if (!request.method.endsWith("/initialized")) {
919
+ respondError(request.id, -32601, `Unknown method: ${request.method}`, framing);
920
+ }
921
+ };
922
+ const parseContentLengthMessage = async () => {
923
+ const headerEnd = buffer.indexOf(`\r
924
+ \r
925
+ `);
926
+ if (headerEnd < 0)
927
+ return false;
928
+ const header = buffer.slice(0, headerEnd);
929
+ const match = /Content-Length:\s*(\d+)/i.exec(header);
930
+ if (!match) {
931
+ buffer = buffer.slice(headerEnd + 4);
932
+ return true;
933
+ }
934
+ const length = Number(match[1]);
935
+ const bodyStart = headerEnd + 4;
936
+ const bodyEnd = bodyStart + length;
937
+ if (buffer.length < bodyEnd)
938
+ return false;
939
+ const body = buffer.slice(bodyStart, bodyEnd);
940
+ buffer = buffer.slice(bodyEnd);
941
+ await handle(JSON.parse(body), "content-length");
942
+ return true;
943
+ };
944
+ const parseLineMessage = async () => {
945
+ const lineEnd = buffer.indexOf(`
946
+ `);
947
+ if (lineEnd < 0)
948
+ return false;
949
+ const line = buffer.slice(0, lineEnd).trim();
950
+ buffer = buffer.slice(lineEnd + 1);
951
+ if (!line)
952
+ return true;
953
+ await handle(JSON.parse(line), "newline");
954
+ return true;
955
+ };
956
+ const parse = async () => {
957
+ for (;; ) {
958
+ buffer = buffer.trimStart();
959
+ if (/^Content-Length:/i.test(buffer)) {
960
+ if (await parseContentLengthMessage())
961
+ continue;
962
+ return;
963
+ }
964
+ if (buffer.includes(`\r
965
+ \r
966
+ `) && await parseContentLengthMessage())
967
+ continue;
968
+ if (await parseLineMessage())
969
+ continue;
970
+ return;
971
+ }
972
+ };
973
+ for await (const chunk of Bun.stdin.stream()) {
974
+ buffer += decoder.decode(chunk);
975
+ await parse();
976
+ }
977
+ }
978
+ explainCommand(command) {
979
+ const explanations = {
980
+ context: "`akan context` prints agent-readable workspace, app, module, and abstract metadata.",
981
+ "create-module": "`akan create-module <name> --app <app>` scaffolds a database-backed domain module.",
982
+ "create-scalar": "`akan create-scalar <name> --app <app>` scaffolds a reusable scalar value module.",
983
+ "create-service": "`akan create-service <name> --app <app>` scaffolds a non-database service module.",
984
+ "create-view": "`akan create-view --app <app> --module <module>` creates a module View component.",
985
+ "create-unit": "`akan create-unit --app <app> --module <module>` creates a module Unit component.",
986
+ "create-template": "`akan create-template --app <app> --module <module>` creates a module Template component.",
987
+ "create-ui": "`akan create-ui --app <app-or-lib> --module <module> --surface <view|unit|template> --format json` creates one UI surface and returns a primitive write report.",
988
+ "add-field": "`akan add-field --app <app-or-lib> --module <module> --field <field> --type <String|Boolean|Date|Int|Float|scalar> --format json` updates source constant/dictionary files. Use Int or Float for numeric fields, not Number.",
989
+ "add-enum-field": "`akan add-enum-field --app <app-or-lib> --module <module> --field <field> --values a,b --format json` adds an enum field to source constant/dictionary files without editing generated files.",
990
+ sync: "`akan sync <app-or-lib>` refreshes generated Akan files from source conventions.",
991
+ lint: "`akan lint <app-or-lib-or-pkg>` runs Biome linting after preparing generated files.",
992
+ typecheck: "`akan typecheck <app-name>` runs an application typecheck after preparing generated files.",
993
+ test: "`akan test <app-or-lib-or-pkg>` prepares the target and runs its tests.",
994
+ build: "`akan build <app-name>` creates a production build after preparing generated files.",
995
+ doctor: "`akan doctor --strict --format json` reports agent-readable workspace convention diagnostics and validation hints.",
996
+ "guideline show": "`akan guideline show <name>` prints an Akan codegen guideline instruction.",
997
+ workflow: "`akan workflow list|explain|plan|apply|validate|report` lists, plans, applies, validates, and reports agent-readable Akan workflows.",
998
+ "workflow list": "`akan workflow list` lists parseable read-only workflow specs.",
999
+ "workflow explain": "`akan workflow explain <workflow>` explains inputs, optional surfaces, steps, and validation.",
1000
+ "workflow plan": "`akan workflow plan <workflow> ... --out .akan/workflows/plans/<name>.json --format json` returns a read-only plan and can store a local plan artifact.",
1001
+ "workflow apply": "`akan workflow apply [--dry-run] <plan-path> --format json` reads an approved plan artifact and returns a workflow apply report.",
1002
+ "workflow validate": "`akan workflow validate <run-id-or-plan> --format json` runs validation commands from a plan or apply report and stores a structured run report.",
1003
+ "workflow report": "`akan workflow report <run-id> --format json` reads .akan/workflows/runs/<run-id>.json and prints the stored report.",
1004
+ repair: "`akan repair generated|format|imports|dictionary|module-shape --format json` runs narrow safe repairs or returns repair-oriented next actions.",
1005
+ "repair generated": "`akan repair generated --app <app-or-lib> --format json` refreshes generated Akan files.",
1006
+ "repair format": "`akan repair format --target <app-or-lib-or-pkg> --format json` runs the lint/format repair path.",
1007
+ "repair imports": "`akan repair imports --target <app-or-lib-or-pkg> --format json` runs the import organization repair path.",
1008
+ "repair dictionary": "`akan repair dictionary --app <app-or-lib> --module <module> --format json` reports dictionary label repair candidates.",
1009
+ "repair module-shape": "`akan repair module-shape --app <app-or-lib> --module <module> --format json` reports missing module source files and source-safe next actions.",
1010
+ agent: "`akan agent install <target>` writes editor-specific agent rules with overwrite protection.",
1011
+ mcp: "`akan mcp --mode readonly|plan|apply` starts the Akan MCP server over stdio with an explicit permission mode.",
1012
+ "mcp-call": '`akan mcp-call <tool> --mode plan --args \'{"workflow":"add-field","inputs":{"app":"demo"}}\'` calls one MCP tool through the same runner path for debugging.',
1013
+ "mcp-install": "`akan mcp-install [cursor|claude|codex|all] --mode readonly|plan|apply` installs the Akan MCP server config for Cursor (.cursor/mcp.json), Claude Code (.mcp.json), and Codex (.codex/config.toml). Defaults to all targets."
1014
+ };
1015
+ return explanations[command] ?? `No detailed explanation is available for ${command}. Run \`akan --help\` for command help.`;
1016
+ }
1017
+ }
1018
+
1019
+ // pkgs/@akanjs/cli/context/context.script.ts
1020
+ var resolveMcpInstallTargets = (target) => {
1021
+ if (!target || target === "all")
1022
+ return [...akanMcpInstallTargets];
1023
+ if (akanMcpInstallTargets.includes(target))
1024
+ return [target];
1025
+ throw new Error(`Unknown MCP install target: ${target}. Use cursor, claude, codex, or all.`);
1026
+ };
1027
+ var mcpTargetLabels = {
1028
+ cursor: "Cursor",
1029
+ claude: "Claude Code",
1030
+ codex: "Codex"
1031
+ };
1032
+
1033
+ class ContextScript extends script("context", [ContextRunner]) {
1034
+ async context(workspace, options = {}) {
1035
+ Logger.rawLog(await this.contextRunner.getContext(workspace, options));
1036
+ }
1037
+ async doctor(workspace, options = {}) {
1038
+ Logger.rawLog(await this.contextRunner.doctor(workspace, options));
1039
+ }
1040
+ async mcpInstall(workspace, target, { force = false, mode = "apply" } = {}) {
1041
+ const targets = resolveMcpInstallTargets(target);
1042
+ const written = [];
1043
+ for (const t of targets) {
1044
+ const configPath = await this.contextRunner.installMcp(workspace, t, { force, mode });
1045
+ written.push(`${mcpTargetLabels[t]}: ${configPath}`);
1046
+ }
1047
+ Logger.rawLog(`Akan MCP server installed (${mode} mode):
1048
+ ${written.map((line) => `- ${line}`).join(`
1049
+ `)}`);
1050
+ }
1051
+ async mcp(workspace, { mode = "apply" } = {}) {
1052
+ await this.contextRunner.runMcp(workspace, { mode });
1053
+ }
1054
+ async mcpCall(workspace, tool, {
1055
+ mode = "readonly",
1056
+ args = null,
1057
+ format = "json"
1058
+ } = {}) {
1059
+ let parsedArgs = {};
1060
+ if (args) {
1061
+ const parsed = JSON.parse(args);
1062
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1063
+ throw new Error("MCP call args must be a JSON object.");
1064
+ }
1065
+ parsedArgs = parsed;
1066
+ }
1067
+ const result = await this.contextRunner.callMcpTool(workspace, tool, parsedArgs, { mode });
1068
+ Logger.rawLog(format === "json" ? jsonText(result) : String(result));
1069
+ }
1070
+ }
1071
+
1072
+ export { ContextScript };