@akanjs/devkit 3.0.0-beta.3 → 3.0.0-beta.30

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 (341) hide show
  1. package/DEV_RUNTIME_KNOBS.md +44 -26
  2. package/README.ko.md +2 -2
  3. package/README.md +3 -2
  4. package/abstractDoc.test.ts +6 -15
  5. package/abstractDoc.ts +6 -8
  6. package/agentsIndex.test.ts +4 -7
  7. package/agentsIndex.ts +6 -22
  8. package/akanApp/BackendImportGraph.test.ts +8 -22
  9. package/akanApp/BackendImportGraph.ts +1 -20
  10. package/akanApp/akanApp.host.test.ts +56 -3
  11. package/akanApp/akanApp.host.ts +218 -278
  12. package/akanApp/devBootLatch.test.ts +39 -0
  13. package/akanApp/devBootLatch.ts +39 -0
  14. package/akanApp/devHostPolicy.test.ts +81 -35
  15. package/akanApp/devHostPolicy.ts +41 -102
  16. package/akanConfig/akanBin.test.ts +62 -0
  17. package/akanConfig/akanBin.ts +78 -0
  18. package/akanConfig/akanConfig.test.ts +404 -141
  19. package/akanConfig/akanConfig.ts +317 -274
  20. package/akanConfig/index.ts +1 -0
  21. package/akanConfig/types.ts +21 -8
  22. package/akanContext.ts +110 -160
  23. package/akanMcpContract.ts +28 -73
  24. package/appSelectionMemory.ts +1 -4
  25. package/applicationBuildReporter.ts +37 -5
  26. package/applicationBuildRunner.test.ts +41 -5
  27. package/applicationBuildRunner.ts +71 -63
  28. package/applicationTestPreload.ts +3 -12
  29. package/artifact/implicitRootLayout.test.ts +32 -30
  30. package/artifact/implicitRootLayout.ts +58 -106
  31. package/artifact/routeSeedIndex.test.ts +7 -6
  32. package/artifact/routeSeedIndex.ts +11 -44
  33. package/biome.base.json +35 -2
  34. package/biomeBase.ts +2 -5
  35. package/cloud/cloudApi.test.ts +202 -0
  36. package/cloud/cloudApi.ts +77 -39
  37. package/cloud/configLock.test.ts +64 -0
  38. package/cloud/configLock.ts +96 -0
  39. package/cloud/constants.ts +26 -5
  40. package/cloud/globalConfig.ts +71 -32
  41. package/codeAgent/agent/AkanCodeServices.ts +97 -0
  42. package/codeAgent/agent/AkanContextFiles.ts +94 -0
  43. package/codeAgent/agent/CodeAgent.ts +551 -0
  44. package/codeAgent/agent/CodeAgentAsks.ts +66 -0
  45. package/codeAgent/agent/CodeAgentEventMapper.ts +225 -0
  46. package/codeAgent/agent/CodeAgentGate.ts +93 -0
  47. package/codeAgent/agent/CodeAgentProxy.ts +82 -0
  48. package/codeAgent/agent/CodeAgentRpcHost.ts +103 -0
  49. package/codeAgent/agent/CodeAgentRpcListener.ts +109 -0
  50. package/codeAgent/agent/CodeAgentStreamPrinter.ts +86 -0
  51. package/codeAgent/agent/CodeAgentSuspended.ts +85 -0
  52. package/codeAgent/agent/CodeAgentUi.ts +47 -0
  53. package/codeAgent/agent/CodeMailbox.ts +148 -0
  54. package/codeAgent/agent/CodeSessionFork.ts +56 -0
  55. package/codeAgent/agent/CodeSessionIndex.ts +78 -0
  56. package/codeAgent/agent/SubagentPool.ts +214 -0
  57. package/codeAgent/agent/akanCodeModel.ts +47 -0
  58. package/codeAgent/agent/akanCodePaths.ts +31 -0
  59. package/codeAgent/agent/akanContextFiles.test.ts +114 -0
  60. package/codeAgent/agent/akanSystemPrompt.ts +46 -0
  61. package/codeAgent/agent/codeAgent.test.ts +178 -0
  62. package/codeAgent/agent/codeAgentContract.test.ts +523 -0
  63. package/codeAgent/agent/codeAgentProxy.test.ts +74 -0
  64. package/codeAgent/agent/codeAgentRpcListener.test.ts +113 -0
  65. package/codeAgent/agent/codeAgentSuspended.test.ts +47 -0
  66. package/codeAgent/agent/codeMailbox.test.ts +76 -0
  67. package/codeAgent/agent/codeSessionFork.test.ts +58 -0
  68. package/codeAgent/agent/codeSessionIndex.test.ts +77 -0
  69. package/codeAgent/agent/consoleToStderr.ts +8 -0
  70. package/codeAgent/agent/jsonLines.ts +29 -0
  71. package/codeAgent/agent/stringArg.ts +5 -0
  72. package/codeAgent/feedback/DevLogFeedback.ts +82 -0
  73. package/codeAgent/feedback/PreviewChrome.ts +82 -0
  74. package/codeAgent/feedback/PreviewFeedback.ts +46 -0
  75. package/codeAgent/feedback/PreviewView.ts +95 -0
  76. package/codeAgent/feedback/TurnFeedback.ts +98 -0
  77. package/codeAgent/feedback/VerifyFeedback.ts +40 -0
  78. package/codeAgent/feedback/previewChrome.test.ts +73 -0
  79. package/codeAgent/index.ts +34 -0
  80. package/codeAgent/skills/akan-code/SKILL.md +130 -0
  81. package/codeAgent/skills/akan-debug/SKILL.md +42 -0
  82. package/codeAgent/skills/akan-deps/SKILL.md +50 -0
  83. package/codeAgent/skills/akan-err/SKILL.md +39 -0
  84. package/codeAgent/skills/akan-module/SKILL.md +45 -0
  85. package/codeAgent/skills/akan-scaffold/SKILL.md +76 -0
  86. package/codeAgent/skills/akan-scalar/SKILL.md +52 -0
  87. package/codeAgent/skills/akan-service/SKILL.md +34 -0
  88. package/codeAgent/skills/akan-store/SKILL.md +67 -0
  89. package/codeAgent/skills/akan-ui/SKILL.md +63 -0
  90. package/codeAgent/skills/akan-validate/SKILL.md +44 -0
  91. package/codeAgent/tools/AkanCli.ts +50 -0
  92. package/codeAgent/tools/AkanCodePlugins.ts +136 -0
  93. package/codeAgent/tools/AkanEditScope.ts +48 -0
  94. package/codeAgent/tools/AkanEnvKeys.ts +50 -0
  95. package/codeAgent/tools/AkanToolPack.ts +92 -0
  96. package/codeAgent/tools/AkanVerifier.ts +48 -0
  97. package/codeAgent/tools/AskToolPack.ts +99 -0
  98. package/codeAgent/tools/MailToolPack.ts +76 -0
  99. package/codeAgent/tools/McpClient.ts +187 -0
  100. package/codeAgent/tools/McpOAuth.ts +233 -0
  101. package/codeAgent/tools/McpOAuthLoopback.ts +64 -0
  102. package/codeAgent/tools/McpServerConfig.ts +129 -0
  103. package/codeAgent/tools/McpSignIn.ts +158 -0
  104. package/codeAgent/tools/McpTokenStore.ts +81 -0
  105. package/codeAgent/tools/McpToolPack.ts +199 -0
  106. package/codeAgent/tools/SessionToolPack.ts +91 -0
  107. package/codeAgent/tools/WebToolPack.ts +147 -0
  108. package/codeAgent/tools/akanCodePlugins.test.ts +677 -0
  109. package/codeAgent/tools/askToolPack.test.ts +116 -0
  110. package/codeAgent/tools/mcpOAuth.test.ts +612 -0
  111. package/codeAgent/tools/mcpServerConfig.test.ts +212 -0
  112. package/codegenLock.test.ts +4 -13
  113. package/codegenLock.ts +3 -17
  114. package/commandDecorators/argMeta.ts +1 -5
  115. package/commandDecorators/camelToKebabCase.ts +1 -0
  116. package/commandDecorators/command.ts +46 -102
  117. package/commandDecorators/commandBuilder.ts +30 -61
  118. package/commandDecorators/commandDecorators.test.ts +2 -10
  119. package/commandDecorators/dependencyBuilder.ts +10 -1
  120. package/commandDecorators/helpFormatter.ts +68 -145
  121. package/commandDecorators/index.ts +0 -2
  122. package/commandDecorators/targetMeta.ts +9 -7
  123. package/createTunnel.ts +7 -14
  124. package/csrE2e/csrE2eHarness.fixture.ts +505 -0
  125. package/csrE2e/csrFastRefresh.e2e.test.ts +233 -0
  126. package/csrE2e/csrPageStack.e2e.test.ts +252 -0
  127. package/csrE2e/csrPatchFirst.e2e.test.ts +87 -0
  128. package/csrE2e/csrRootLayout.e2e.test.ts +134 -0
  129. package/csrE2e/devHmrLatency.e2e.test.ts +391 -0
  130. package/csrE2e/registryVendor.e2e.test.ts +221 -0
  131. package/csrE2e/ssrRegistry.e2e.test.ts +644 -0
  132. package/dependencyScanner.test.ts +4 -16
  133. package/dependencyScanner.ts +31 -80
  134. package/devkitUtils.test.ts +48 -20
  135. package/executors.linkFallback.test.ts +103 -0
  136. package/executors.test.ts +176 -106
  137. package/executors.ts +212 -498
  138. package/fileEditor.ts +9 -37
  139. package/{getDirname.test.ts → fileSys.test.ts} +5 -3
  140. package/fileSys.ts +22 -9
  141. package/formSetterScanner.ts +3 -16
  142. package/frontendBuild/allRoutesBuilder.ts +7 -21
  143. package/frontendBuild/autoImportSync.test.ts +27 -38
  144. package/frontendBuild/autoImportSync.ts +31 -65
  145. package/frontendBuild/buildRouteClient.test.ts +123 -37
  146. package/frontendBuild/bundleDefine.test.ts +12 -0
  147. package/frontendBuild/bundleDefine.ts +16 -0
  148. package/frontendBuild/clientBuildTypes.ts +5 -36
  149. package/frontendBuild/clientEntriesBundler.ts +9 -40
  150. package/frontendBuild/clientEntryDiscovery.test.ts +112 -0
  151. package/frontendBuild/clientEntryDiscovery.ts +122 -110
  152. package/frontendBuild/csrArtifactBuilder.ts +9 -31
  153. package/frontendBuild/csrDevArtifactWriter.test.ts +151 -0
  154. package/frontendBuild/csrDevArtifactWriter.ts +307 -0
  155. package/frontendBuild/csrDevBundler.test.ts +66 -0
  156. package/frontendBuild/csrDevBundler.ts +95 -0
  157. package/frontendBuild/csrDevFactoryCheck.test.ts +33 -0
  158. package/frontendBuild/csrDevFactoryCheck.ts +39 -0
  159. package/frontendBuild/csrDevModuleCompiler.test.ts +293 -0
  160. package/frontendBuild/csrDevModuleCompiler.ts +422 -0
  161. package/frontendBuild/csrDevPatcher.test.ts +572 -0
  162. package/frontendBuild/csrDevPatcher.ts +548 -0
  163. package/frontendBuild/csrDevPaths.ts +145 -0
  164. package/frontendBuild/csrDevResolver.test.ts +239 -0
  165. package/frontendBuild/csrDevResolver.ts +363 -0
  166. package/frontendBuild/csrDevTypes.ts +61 -0
  167. package/frontendBuild/csrEntryFiles.ts +24 -0
  168. package/frontendBuild/cssCandidateCache.test.ts +0 -6
  169. package/frontendBuild/cssCandidateCache.ts +6 -41
  170. package/frontendBuild/cssCompiler.ts +31 -82
  171. package/frontendBuild/cssImportResolver.ts +6 -19
  172. package/frontendBuild/devChangePlanner.ts +7 -10
  173. package/frontendBuild/devGeneratedIndexSync.test.ts +12 -27
  174. package/frontendBuild/devGeneratedIndexSync.ts +1 -4
  175. package/frontendBuild/devRegistryBundler.ts +192 -0
  176. package/frontendBuild/fontOptimizer.test.ts +6 -14
  177. package/frontendBuild/fontOptimizer.ts +15 -72
  178. package/frontendBuild/fontPruner.test.ts +5 -10
  179. package/frontendBuild/fontPruner.ts +5 -20
  180. package/frontendBuild/frontendBuild.test.ts +57 -70
  181. package/frontendBuild/hmrChangeClassifier.ts +2 -2
  182. package/frontendBuild/hmrWatcher.test.ts +59 -32
  183. package/frontendBuild/hmrWatcher.ts +31 -74
  184. package/frontendBuild/index.ts +4 -0
  185. package/frontendBuild/pagesBundleBuilder.ts +28 -30
  186. package/frontendBuild/pagesEntrySourceGenerator.ts +56 -6
  187. package/frontendBuild/precompressArtifacts.ts +5 -18
  188. package/frontendBuild/registryMetadataFingerprint.test.ts +63 -0
  189. package/frontendBuild/registryMetadataFingerprint.ts +28 -0
  190. package/frontendBuild/routeClientBuilder.ts +100 -62
  191. package/frontendBuild/routesManifestArtifactSerializer.ts +5 -36
  192. package/frontendBuild/serverGraphFile.test.ts +77 -0
  193. package/frontendBuild/serverGraphFile.ts +93 -0
  194. package/frontendBuild/sourceMtimeIndex.test.ts +41 -97
  195. package/frontendBuild/sourceMtimeIndex.ts +19 -130
  196. package/frontendBuild/ssrBaseArtifactBuilder.ts +19 -13
  197. package/frontendBuild/ssrDevBundler.ts +85 -0
  198. package/frontendBuild/styleContract.ts +1 -8
  199. package/frontendBuild/themeValidator.ts +4 -26
  200. package/frontendBuild/watchRootResolver.test.ts +23 -42
  201. package/frontendBuild/watchRootResolver.ts +5 -27
  202. package/getRelatedCnsts.ts +12 -92
  203. package/incrementalBuilder/buildBatch.proc.ts +147 -63
  204. package/incrementalBuilder/buildBatchProtocol.ts +13 -38
  205. package/incrementalBuilder/buildBatchRunner.test.ts +65 -0
  206. package/incrementalBuilder/buildBatchRunner.ts +26 -38
  207. package/incrementalBuilder/builderChannel.test.ts +24 -49
  208. package/incrementalBuilder/builderChannel.ts +6 -37
  209. package/incrementalBuilder/builderRequestRouter.test.ts +0 -8
  210. package/incrementalBuilder/builderRequestRouter.ts +5 -24
  211. package/incrementalBuilder/builderWorkQueue.test.ts +94 -0
  212. package/incrementalBuilder/builderWorkQueue.ts +126 -0
  213. package/incrementalBuilder/devWatchBatch.test.ts +21 -24
  214. package/incrementalBuilder/incrementalBuilder.host.test.ts +53 -56
  215. package/incrementalBuilder/incrementalBuilder.host.ts +68 -81
  216. package/incrementalBuilder/incrementalBuilder.proc.test.ts +269 -0
  217. package/incrementalBuilder/incrementalBuilder.proc.ts +596 -208
  218. package/index.ts +5 -19
  219. package/integration/devResourceProbe.ts +50 -56
  220. package/integration/devStability.integration.test.ts +105 -199
  221. package/integration/devStabilityHarness.test.ts +13 -28
  222. package/integration/devStabilityHarness.ts +65 -330
  223. package/integration/ssrMemoryProbe.ts +41 -167
  224. package/libSource.test.ts +4 -15
  225. package/libSource.ts +19 -31
  226. package/lint/__fixtures__/no-document-cookie/bad.tsx +4 -0
  227. package/lint/__fixtures__/no-document-cookie/good.tsx +7 -0
  228. package/lint/__fixtures__/no-web-only-api-outside-webkit/bad.tsx +7 -0
  229. package/lint/__fixtures__/no-web-only-api-outside-webkit/good.tsx +11 -0
  230. package/lint/__fixtures__/no-web-storage/bad.tsx +6 -0
  231. package/lint/__fixtures__/no-web-storage/good.tsx +7 -0
  232. package/lint/gritRules.test.ts +8 -24
  233. package/lint/no-deep-internal-import.grit +2 -2
  234. package/lint/no-document-cookie.grit +29 -0
  235. package/lint/no-redeclare-predefined-endpoint.grit +2 -1
  236. package/lint/no-web-only-api-outside-webkit.grit +44 -0
  237. package/lint/no-web-storage.grit +30 -0
  238. package/linter.test.ts +1 -2
  239. package/linter.ts +26 -116
  240. package/mobile/desktopBin.test.ts +177 -0
  241. package/mobile/desktopBin.ts +143 -0
  242. package/mobile/desktopServerStage.test.ts +204 -0
  243. package/mobile/desktopServerStage.ts +138 -0
  244. package/mobile/index.ts +9 -0
  245. package/mobile/mobileTarget.test.ts +57 -8
  246. package/mobile/mobileTarget.ts +45 -26
  247. package/mobile/nativeApi.test.ts +30 -0
  248. package/mobile/nativeApi.ts +40 -0
  249. package/mobile/nativeApp.test.ts +151 -0
  250. package/mobile/nativeApp.ts +405 -0
  251. package/mobile/nativeAppLine.test.ts +69 -0
  252. package/mobile/nativeAppLine.ts +71 -0
  253. package/mobile/nativeConfig.test.ts +414 -0
  254. package/mobile/nativeConfig.ts +296 -0
  255. package/mobile/nativePluginFolders.test.ts +81 -0
  256. package/mobile/nativePluginFolders.ts +60 -0
  257. package/mobile/nativeWebDir.test.ts +80 -0
  258. package/mobile/nativeWebDir.ts +66 -0
  259. package/mobile/placeholderAppId.test.ts +13 -0
  260. package/mobile/placeholderAppId.ts +16 -0
  261. package/mobile/usageDescriptions.test.ts +27 -0
  262. package/mobile/usageDescriptions.ts +24 -0
  263. package/package.json +11 -11
  264. package/packageExports.test.ts +3 -18
  265. package/packageExportsMap.ts +3 -19
  266. package/piDependencyPins.test.ts +46 -0
  267. package/platformTest/BunTestReport.test.ts +60 -0
  268. package/platformTest/BunTestReport.ts +65 -0
  269. package/platformTest/IdleWatchedProcess.test.ts +56 -0
  270. package/platformTest/IdleWatchedProcess.ts +73 -0
  271. package/platformTest/LinuxDockerTarget.ts +105 -0
  272. package/platformTest/PlatformTestRun.test.ts +64 -0
  273. package/platformTest/PlatformTestRun.ts +141 -0
  274. package/platformTest/PlatformTestTarget.ts +142 -0
  275. package/platformTest/TestSnapshot.test.ts +72 -0
  276. package/platformTest/TestSnapshot.ts +103 -0
  277. package/platformTest/WindowsSshTarget.ts +151 -0
  278. package/prompter.ts +3 -13
  279. package/qualityScanner.test.ts +9 -26
  280. package/qualityScanner.ts +200 -286
  281. package/recipeScanner.test.ts +7 -12
  282. package/recipeScanner.ts +9 -38
  283. package/repoIdentity.ts +2 -9
  284. package/routeSourceValidator.test.ts +0 -1
  285. package/routeSourceValidator.ts +7 -42
  286. package/scanInfo.ts +91 -225
  287. package/selectModel.ts +1 -2
  288. package/semver.ts +3 -5
  289. package/slicePlanner.test.ts +6 -24
  290. package/slicePlanner.ts +11 -32
  291. package/spinner.test.ts +3 -8
  292. package/spinner.ts +4 -16
  293. package/sshTunnel.ts +1 -6
  294. package/ssrScanner.test.ts +1 -6
  295. package/ssrScanner.ts +103 -113
  296. package/stripAnsi.ts +3 -0
  297. package/subspace.test.ts +30 -25
  298. package/subspace.ts +81 -115
  299. package/subspaceConfig.ts +11 -9
  300. package/testHelpers.ts +194 -0
  301. package/transforms/asyncDefaultExportDetector.ts +3 -11
  302. package/transforms/barrelAnalyzer.ts +43 -135
  303. package/transforms/barrelImportsPlugin.ts +38 -175
  304. package/transforms/externalizeFrameworkPlugin.test.ts +39 -118
  305. package/transforms/externalizeFrameworkPlugin.ts +13 -87
  306. package/transforms/moduleSyntax.ts +32 -0
  307. package/transforms/rscUseClientTransform.ts +42 -20
  308. package/transforms/serverEnvPlugin.test.ts +103 -0
  309. package/transforms/serverEnvPlugin.ts +41 -0
  310. package/transforms/transforms.test.ts +166 -44
  311. package/transforms/tsconfigPackageResolver.test.ts +6 -24
  312. package/transforms/useClientBundlePlugin.ts +6 -23
  313. package/typeChecker.ts +7 -63
  314. package/types.ts +2 -0
  315. package/uploadRelease.ts +10 -25
  316. package/workflow/artifacts.ts +18 -0
  317. package/workflow/executor.test.ts +1 -12
  318. package/workflow/executor.ts +101 -110
  319. package/workflow/index.ts +0 -1
  320. package/workflow/moduleIndex.test.ts +4 -15
  321. package/workflow/moduleIndex.ts +9 -49
  322. package/workflow/plan.ts +18 -25
  323. package/workflow/render.ts +80 -96
  324. package/workflow/rolloutGate.test.ts +1 -1
  325. package/workflow/source.ts +52 -69
  326. package/workflow/sourceAst.ts +50 -0
  327. package/workspaceLayout.test.ts +18 -3
  328. package/workspaceLayout.ts +22 -23
  329. package/abstractCompactor.ts +0 -112
  330. package/aiEditor.test.ts +0 -60
  331. package/aiEditor.ts +0 -387
  332. package/applicationReleasePackager.ts +0 -209
  333. package/capacitorApp.test.ts +0 -372
  334. package/capacitorApp.ts +0 -1648
  335. package/commandDecorators/commandMeta.ts +0 -7
  336. package/commandDecorators/types.ts +0 -10
  337. package/getDirname.ts +0 -4
  338. package/guideline.ts +0 -19
  339. package/local/optimize-resource/ipcprobe/child.ts +0 -1
  340. package/local/optimize-resource/ipcprobe/parent.ts +0 -11
  341. package/workflow/primitive.ts +0 -59
@@ -1,2 +1,3 @@
1
+ export * from "./akanBin";
1
2
  export * from "./akanConfig";
2
3
  export * from "./types";
@@ -1,13 +1,26 @@
1
1
  export type {
2
2
  AkanApiConfig,
3
3
  AkanAssetsConfig,
4
+ AkanBinConfig,
5
+ AkanBinPathSource,
6
+ AkanBinSource,
7
+ AkanBinUrlSource,
4
8
  AkanConfigFile,
9
+ AkanDatabaseConfig,
5
10
  AkanExecutor,
6
- AkanMobileConfig,
7
- AkanMobileTargetConfig,
8
- AkanNativeContext,
11
+ AkanNativeAndroidConfig,
12
+ AkanNativeAppConfig,
13
+ AkanNativeAppId,
14
+ AkanNativeAppResult,
15
+ AkanNativeDeepLinks,
16
+ AkanNativeDesktopConfig,
17
+ AkanNativeIosConfig,
18
+ AkanNativeSettings,
19
+ AkanNativeTarget,
20
+ AkanNativeUpdatesConfig,
21
+ AkanNativeValue,
9
22
  AkanPlugin,
10
- AkanPluginCapacitorConfig,
23
+ AkanPluginNativeConfig,
11
24
  AkanRouteConfig,
12
25
  AkanScanInfo,
13
26
  AkanSyncContext,
@@ -18,6 +31,7 @@ export type {
18
31
  AppConfigResult,
19
32
  AppScanResult,
20
33
  Arch,
34
+ BinPlatform,
21
35
  DatabaseMode,
22
36
  DeepPartial,
23
37
  DockerConfig,
@@ -31,11 +45,10 @@ export type {
31
45
  LibConfigResult,
32
46
  LibDockerConfig,
33
47
  LibScanResult,
34
- MobileEnv,
35
- MobilePermission,
48
+ NativeEnv,
49
+ NativePermission,
36
50
  PkgScanResult,
37
- PluginRuntimeContext,
38
51
  ScanResult,
39
52
  WorkspaceScanResult,
40
53
  } from "akanjs";
41
- export { archs } from "akanjs";
54
+ export { archs, binPlatforms } from "akanjs";
package/akanContext.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { readdir } from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { capitalize } from "akanjs/common";
3
+ import { capitalize, isRecord } from "akanjs/common";
4
4
  import { extractBlockVersion, readDevkitVersion } from "./agentsIndex";
5
5
  import { AppExecutor, LibExecutor, type SysExecutor, type WorkspaceExecutor } from "./executors";
6
6
  import { FileSys } from "./fileSys";
@@ -12,10 +12,11 @@ import {
12
12
  type WorkflowApplyReport,
13
13
  type WorkflowPlan,
14
14
  type WorkflowRunArtifact,
15
+ workflowPathsForPlan,
15
16
  workflowRunArtifactPath,
16
17
  workflowSyncDir,
17
18
  } from "./workflow";
18
- import { isScannedRootEntry, rootAllowedDirs, rootAllowedFiles } from "./workspaceLayout";
19
+ import { isScannedRootEntry, rootAllowedDirs, rootAllowedFiles, rootEntryHintOf } from "./workspaceLayout";
19
20
 
20
21
  export type AkanContextFormat = "json" | "markdown";
21
22
  export type AkanModuleKind = "domain" | "service" | "scalar";
@@ -122,8 +123,7 @@ export type JsonRpcRequest = {
122
123
  export type McpFraming = "content-length" | "newline";
123
124
  export type AkanMcpMode = "readonly" | "plan" | "apply";
124
125
 
125
- // Coding-agent tools that can host the Akan MCP server. Cursor and Claude Code both read a JSON
126
- // `mcpServers` map; Codex reads a TOML `[mcp_servers.<name>]` table.
126
+ // Cursor and Claude Code read a JSON `mcpServers` map; Codex reads a TOML `[mcp_servers.<name>]` table.
127
127
  export type AkanMcpInstallTarget = "cursor" | "claude" | "codex";
128
128
 
129
129
  export type CursorMcpConfig = {
@@ -132,11 +132,6 @@ export type CursorMcpConfig = {
132
132
 
133
133
  export const guidelineResourceUri = (name: string) => `akan://guidelines/${name}`;
134
134
 
135
- /**
136
- * Resources that exist whatever the workspace holds. The guideline entries are **not** here: they are
137
- * one per directory under `cli/guidelines/`, and a hardcoded pair listed 2 of 31 while `readResource`
138
- * threw on the other 29. `buildResourceList` is the only place the two halves meet.
139
- */
140
135
  const staticResourceList = [
141
136
  { uri: "akan://docs/framework", name: "Akan framework guide", mimeType: "text/markdown" },
142
137
  { uri: "akan://workspace/summary", name: "Workspace summary", mimeType: "application/json" },
@@ -167,42 +162,35 @@ export const akanMcpInstallConfigPaths: Record<AkanMcpInstallTarget, string> = {
167
162
  codex: codexMcpConfigPath,
168
163
  };
169
164
 
170
- // `akan mcp` resolves the workspace from process.cwd(), so every launcher must run it from the
171
- // workspace root. Cursor expands its own ${workspaceFolder} variable. Claude Code does not guarantee
172
- // the server's cwd but sets CLAUDE_PROJECT_DIR in its environment, so we cd into that at runtime.
173
- // Codex inherits its own launch cwd (it also discovers .codex/config.toml from cwd), so it runs the
174
- // command directly and must be started from the workspace root.
165
+ // `akan mcp` resolves the workspace from its cwd: Cursor expands ${workspaceFolder}, Claude Code only sets
166
+ // CLAUDE_PROJECT_DIR (its cwd is not guaranteed), and Codex runs from its own launch cwd.
175
167
  const cursorWorkspaceFolder = "$" + "{workspaceFolder}";
176
168
  const claudeProjectDir = "$CLAUDE_PROJECT_DIR";
177
169
 
178
170
  const akanMcpCommand = (mode: AkanMcpMode, { cd }: { cd?: string } = {}) =>
179
171
  cd ? `cd "${cd}" && akan mcp --mode ${mode}` : `akan mcp --mode ${mode}`;
180
172
 
181
- export const createAkanCursorMcpServer = (mode: AkanMcpMode = "readonly") => ({
173
+ const bashMcpServer = (mode: AkanMcpMode, cd: string) => ({
182
174
  type: "stdio",
183
175
  command: "bash",
184
- args: ["-lc", akanMcpCommand(mode, { cd: cursorWorkspaceFolder })],
176
+ args: ["-lc", akanMcpCommand(mode, { cd })],
185
177
  });
186
178
 
187
- export const createAkanClaudeMcpServer = (mode: AkanMcpMode = "readonly") => ({
188
- type: "stdio",
189
- command: "bash",
190
- args: ["-lc", akanMcpCommand(mode, { cd: claudeProjectDir })],
191
- });
179
+ export const createAkanCursorMcpServer = (mode: AkanMcpMode = "readonly") => bashMcpServer(mode, cursorWorkspaceFolder);
180
+
181
+ export const createAkanClaudeMcpServer = (mode: AkanMcpMode = "readonly") => bashMcpServer(mode, claudeProjectDir);
192
182
 
193
- // JSON-config targets (Cursor, Claude Code) share the same `mcpServers` entry shape.
194
183
  export const createAkanMcpServer = (target: "cursor" | "claude", mode: AkanMcpMode = "readonly") =>
195
184
  target === "cursor" ? createAkanCursorMcpServer(mode) : createAkanClaudeMcpServer(mode);
196
185
 
197
186
  export const akanCursorMcpServer = createAkanCursorMcpServer();
198
187
 
199
- // Codex config is TOML and we have no TOML serializer, so we build the `[mcp_servers.akan]` table as text.
188
+ // No TOML serializer is bundled, so the `[mcp_servers.akan]` table is written as text.
200
189
  export const codexMcpServerTableHeader = "[mcp_servers.akan]";
201
190
  export const createAkanCodexMcpServerBlock = (mode: AkanMcpMode = "readonly") =>
202
191
  `${codexMcpServerTableHeader}\ncommand = "bash"\nargs = ["-lc", "${akanMcpCommand(mode)}"]\n`;
203
192
 
204
- // A TOML table runs from its header until the next top-level `[header]` or EOF. We upsert only the
205
- // akan table and preserve everything else in the file, mirroring the JSON merge behavior.
193
+ // A TOML table runs from its header until the next top-level `[header]` or EOF.
206
194
  const codexAkanTablePattern = /^\[mcp_servers\.akan\][^\n]*\n(?:(?!\[)[^\n]*(?:\n|$))*/m;
207
195
 
208
196
  export const upsertCodexMcpServerBlock = (
@@ -331,29 +319,12 @@ const moduleShapeFiles = (module: AkanModuleContext) => {
331
319
  const constantFieldNames = (content: string) =>
332
320
  [...content.matchAll(/\b([A-Za-z_$][\w$]*)\s*:\s*field\(/g)].map((match) => match[1]).filter(Boolean);
333
321
 
334
- const safeReadDir = async (dirPath: string) => {
335
- try {
336
- return (await readdir(dirPath, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
337
- } catch {
338
- return [];
339
- }
340
- };
322
+ const safeReadDir = async (dirPath: string) =>
323
+ (await readdir(dirPath, { withFileTypes: true }).catch(() => [])).sort((a, b) => a.name.localeCompare(b.name));
341
324
 
342
- const safeReadText = async (filePath: string) => {
343
- try {
344
- return await FileSys.readText(filePath);
345
- } catch {
346
- return null;
347
- }
348
- };
325
+ const safeReadText = (filePath: string) => FileSys.readText(filePath).catch(() => null);
349
326
 
350
- const safeReadJson = async <T>(filePath: string) => {
351
- try {
352
- return await FileSys.readJson<T>(filePath);
353
- } catch {
354
- return null;
355
- }
356
- };
327
+ const safeReadJson = <T>(filePath: string) => FileSys.readJson<T>(filePath).catch(() => null);
357
328
 
358
329
  const isWorkflowPlan = (value: unknown): value is WorkflowPlan =>
359
330
  typeof value === "object" &&
@@ -361,7 +332,15 @@ const isWorkflowPlan = (value: unknown): value is WorkflowPlan =>
361
332
  "schemaVersion" in value &&
362
333
  value.schemaVersion === 1 &&
363
334
  "mode" in value &&
364
- value.mode === "plan";
335
+ value.mode === "plan" &&
336
+ "inputs" in value &&
337
+ isRecord(value.inputs) &&
338
+ "predictedChanges" in value &&
339
+ Array.isArray(value.predictedChanges) &&
340
+ value.predictedChanges.every((change) => isRecord(change) && typeof change.target === "string");
341
+
342
+ const isPathList = (value: unknown) =>
343
+ Array.isArray(value) && value.every((file) => isRecord(file) && typeof file.path === "string");
365
344
 
366
345
  const isWorkflowApplyReport = (value: unknown): value is WorkflowApplyReport =>
367
346
  typeof value === "object" &&
@@ -369,29 +348,17 @@ const isWorkflowApplyReport = (value: unknown): value is WorkflowApplyReport =>
369
348
  "schemaVersion" in value &&
370
349
  value.schemaVersion === 1 &&
371
350
  "mode" in value &&
372
- (value.mode === "apply" || value.mode === "dry-run");
351
+ (value.mode === "apply" || value.mode === "dry-run") &&
352
+ "changedFiles" in value &&
353
+ isPathList(value.changedFiles) &&
354
+ "generatedFiles" in value &&
355
+ isPathList(value.generatedFiles) &&
356
+ "plan" in value &&
357
+ isWorkflowPlan(value.plan);
373
358
 
374
359
  const isWorkflowRunArtifact = (value: unknown): value is WorkflowRunArtifact =>
375
360
  typeof value === "object" && value !== null && "schemaVersion" in value && value.schemaVersion === 1;
376
361
 
377
- const planInputString = (plan: WorkflowPlan, key: string) => {
378
- const value = plan.inputs[key];
379
- return typeof value === "string" ? value : "";
380
- };
381
-
382
- const expandWorkflowTarget = (target: string, plan: WorkflowPlan) => {
383
- const app = planInputString(plan, "app");
384
- const module = planInputString(plan, "module");
385
- const moduleClass = module ? capitalize(module) : "<Module>";
386
- return target
387
- .replace(/^\*\//, app ? `apps/${app}/` : "")
388
- .replaceAll("<module>", module || "<module>")
389
- .replaceAll("<Module>", moduleClass);
390
- };
391
-
392
- const workflowPathsForPlan = (plan: WorkflowPlan) =>
393
- plan.predictedChanges.map((change) => expandWorkflowTarget(change.target, plan));
394
-
395
362
  const workflowPathsForArtifact = (artifact: WorkflowRunArtifact) => {
396
363
  if (isWorkflowPlan(artifact)) return workflowPathsForPlan(artifact);
397
364
  if (isWorkflowApplyReport(artifact)) {
@@ -401,7 +368,8 @@ const workflowPathsForArtifact = (artifact: WorkflowRunArtifact) => {
401
368
  ...workflowPathsForPlan(artifact.plan),
402
369
  ];
403
370
  }
404
- if ("mode" in artifact && artifact.mode === "validate" && artifact.plan) return workflowPathsForPlan(artifact.plan);
371
+ if ("mode" in artifact && artifact.mode === "validate" && isWorkflowPlan(artifact.plan))
372
+ return workflowPathsForPlan(artifact.plan);
405
373
  return [];
406
374
  };
407
375
 
@@ -635,9 +603,7 @@ export class AkanContextAnalyzer {
635
603
  };
636
604
  }
637
605
 
638
- // The conventions in AGENTS.md ship with the package, but nothing re-renders them on `bun update` — a workspace
639
- // keeps whichever release wrote its block until someone re-runs the install. Comparing the stamp against the
640
- // running devkit is the only signal that the guide an agent is reading is older than the framework it describes.
606
+ // Nothing re-renders the AGENTS.md block on `bun update`, so the version stamp is the only staleness signal.
641
607
  static async #agentGuideDrift(workspace: WorkspaceExecutor) {
642
608
  const installed = await readDevkitVersion();
643
609
  if (!installed) return null;
@@ -679,6 +645,10 @@ export class AkanContextAnalyzer {
679
645
  true,
680
646
  ),
681
647
  ];
648
+ const report = (diagnostic: AkanDiagnostic, action: RepairAction) => {
649
+ diagnostics.push({ ...diagnostic, repairActions: [action] });
650
+ repairActions.push(action);
651
+ };
682
652
 
683
653
  for (const sys of [...context.apps, ...context.libs]) {
684
654
  const sysPath = path.join(workspace.workspaceRoot, sys.path);
@@ -687,73 +657,63 @@ export class AkanContextAnalyzer {
687
657
  const allowed = entry.isDirectory()
688
658
  ? rootAllowedDirs[sys.type].has(entry.name)
689
659
  : rootAllowedFiles[sys.type].has(entry.name);
690
- if (!allowed) {
691
- const action = repairAction(
692
- "module-shape",
693
- `akan repair module-shape --app ${sys.name}`,
694
- `Review ${sys.type} root shape and remove or move the unknown entry.`,
695
- false,
660
+ const rootEntryHint = rootEntryHintOf(sys.type, entry.name);
661
+ if (!allowed)
662
+ report(
663
+ {
664
+ severity: "error",
665
+ code: `${sys.type}-root-unknown-entry`,
666
+ path: `${sys.path}/${entry.name}`,
667
+ message: `Unexpected ${entry.isDirectory() ? "folder" : "file"} in ${sys.type} root: ${sys.path}/${entry.name}${
668
+ rootEntryHint ? ` (${rootEntryHint})` : ""
669
+ }`,
670
+ },
671
+ repairAction(
672
+ "module-shape",
673
+ `akan repair module-shape --app ${sys.name}`,
674
+ `Review ${sys.type} root shape and remove or move the unknown entry.`,
675
+ false,
676
+ ),
696
677
  );
697
- diagnostics.push({
698
- severity: "error",
699
- code: `${sys.type}-root-unknown-entry`,
700
- path: `${sys.path}/${entry.name}`,
701
- message: `Unexpected ${entry.isDirectory() ? "folder" : "file"} in ${sys.type} root: ${sys.path}/${entry.name}`,
702
- repairActions: [action],
703
- });
704
- repairActions.push(action);
705
- }
706
678
  }
707
679
  }
708
680
 
709
681
  const agentDrift = await AkanContextAnalyzer.#agentGuideDrift(workspace);
710
- if (agentDrift) {
711
- const action = repairAction("generated", "akan agent install agents-md", agentDrift.hint, true);
712
- diagnostics.push({
713
- severity: "warning",
714
- code: agentDrift.code,
715
- path: "AGENTS.md",
716
- message: agentDrift.message,
717
- repairActions: [action],
718
- });
719
- repairActions.push(action);
720
- }
682
+ if (agentDrift)
683
+ report(
684
+ { severity: "warning", code: agentDrift.code, path: "AGENTS.md", message: agentDrift.message },
685
+ repairAction("generated", "akan agent install agents-md", agentDrift.hint, true),
686
+ );
721
687
 
722
688
  for (const sys of [...context.apps, ...context.libs]) {
723
689
  for (const module of sys.modules) {
724
- if (!module.abstract.exists) {
725
- const action = repairAction(
726
- "module-shape",
727
- `akan repair module-shape --app ${sys.name} --module ${module.name}`,
728
- "Create the missing module abstract or inspect required source files.",
729
- false,
690
+ const moduleShapeCommand = `akan repair module-shape --app ${sys.name} --module ${module.name}`;
691
+ if (!module.abstract.exists)
692
+ report(
693
+ {
694
+ severity: strict ? "error" : "warning",
695
+ code: "module-abstract-missing",
696
+ path: module.abstract.path,
697
+ message: `${capitalize(module.kind)} module ${sys.name}:${module.name} should include ${module.abstract.path}`,
698
+ },
699
+ repairAction(
700
+ "module-shape",
701
+ moduleShapeCommand,
702
+ "Create the missing module abstract or inspect required source files.",
703
+ false,
704
+ ),
730
705
  );
731
- diagnostics.push({
732
- severity: strict ? "error" : "warning",
733
- code: "module-abstract-missing",
734
- path: module.abstract.path,
735
- message: `${capitalize(module.kind)} module ${sys.name}:${module.name} should include ${module.abstract.path}`,
736
- repairActions: [action],
737
- });
738
- repairActions.push(action);
739
- }
740
706
  const missingFiles = moduleShapeFiles(module).filter((filename) => !module.files.includes(filename));
741
- if (missingFiles.length) {
742
- const action = repairAction(
743
- "module-shape",
744
- `akan repair module-shape --app ${sys.name} --module ${module.name}`,
745
- "Review missing required module source files.",
746
- false,
707
+ if (missingFiles.length)
708
+ report(
709
+ {
710
+ severity: "error",
711
+ code: "module-shape-invalid",
712
+ path: module.path,
713
+ message: `${capitalize(module.kind)} module ${sys.name}:${module.name} is missing required files: ${missingFiles.join(", ")}`,
714
+ },
715
+ repairAction("module-shape", moduleShapeCommand, "Review missing required module source files.", false),
747
716
  );
748
- diagnostics.push({
749
- severity: "error",
750
- code: "module-shape-invalid",
751
- path: module.path,
752
- message: `${capitalize(module.kind)} module ${sys.name}:${module.name} is missing required files: ${missingFiles.join(", ")}`,
753
- repairActions: [action],
754
- });
755
- repairActions.push(action);
756
- }
757
717
  if (module.kind !== "service" && module.files.includes(`${module.name}.dictionary.ts`)) {
758
718
  const constantPath = path.join(workspace.workspaceRoot, module.path, `${module.name}.constant.ts`);
759
719
  const dictionaryPath = path.join(workspace.workspaceRoot, module.path, `${module.name}.dictionary.ts`);
@@ -764,28 +724,27 @@ export class AkanContextAnalyzer {
764
724
  if (constantContent && dictionaryContent) {
765
725
  for (const fieldName of constantFieldNames(constantContent)) {
766
726
  if (new RegExp(`\\b${fieldName}\\s*:`).test(dictionaryContent)) continue;
767
- const action = repairAction(
768
- "dictionary",
769
- `akan repair dictionary --app ${sys.name} --module ${module.name}`,
770
- "Add missing dictionary labels for source constant fields.",
771
- false,
727
+ report(
728
+ {
729
+ severity: "warning",
730
+ code: "dictionary-label-missing",
731
+ path: `${module.path}/${module.name}.dictionary.ts`,
732
+ message: `Dictionary labels for ${sys.name}:${module.name}.${fieldName} were not found.`,
733
+ },
734
+ repairAction(
735
+ "dictionary",
736
+ `akan repair dictionary --app ${sys.name} --module ${module.name}`,
737
+ "Add missing dictionary labels for source constant fields.",
738
+ false,
739
+ ),
772
740
  );
773
- diagnostics.push({
774
- severity: "warning",
775
- code: "dictionary-label-missing",
776
- path: `${module.path}/${module.name}.dictionary.ts`,
777
- message: `Dictionary labels for ${sys.name}:${module.name}.${fieldName} were not found.`,
778
- repairActions: [action],
779
- });
780
- repairActions.push(action);
781
741
  }
782
742
  }
783
743
  }
784
744
  }
785
745
  }
786
746
 
787
- // Recipe SSOT advisory (항상 warning — 차단하지 않음): recipe 지문이 인라인 className 으로 재작성된
788
- // 곳의 추이를 보이게 한다. 유입이 실제로 재발하면 그때 lint 승격을 검토한다 — 증거 기반 에스컬레이션.
747
+ //* Advisory only (warning): promoted to lint only if inline re-authoring actually recurs.
789
748
  for (const sys of [...context.apps, ...context.libs]) {
790
749
  const sources = await collectRecipeSources(path.join(workspace.workspaceRoot, sys.path, "ui"), "ui");
791
750
  if (sources.length === 0) continue;
@@ -815,11 +774,7 @@ export class AkanContextAnalyzer {
815
774
  });
816
775
  }
817
776
 
818
- // Recipe index freshness. The recipe indexes are generated and read as authoritative — a recipe missing
819
- // from its index gets re-invented inline, and a name lingering in it gets imported and fails. The index is
820
- // split by ownership: the root AGENTS.md `## Recipes` lists framework recipes only, and every app/lib lists
821
- // what it may additionally import in its own AGENTS.md `## Recipes In Scope`. Doctor never writes, so this
822
- // is the check that catches a *committed* stale index (lint/sync self-heal the working tree instead).
777
+ // Doctor never writes, so this is what catches a committed stale recipe index (lint and sync self-heal the tree).
823
778
  const scanNames = async (uiDirPath: string, basename?: string) =>
824
779
  new Set(scanRecipes(await collectRecipeSources(uiDirPath, "ui", basename)).map((info) => info.name));
825
780
  const declaredByImport = new Map<string, Set<string>>();
@@ -853,24 +808,19 @@ export class AkanContextAnalyzer {
853
808
  };
854
809
  const pushIndexDiagnostic = (indexPath: string, missing: string[], stale: string[], repairCommand: string) => {
855
810
  if (missing.length === 0 && stale.length === 0) return;
856
- const action = repairAction(
857
- "generated",
858
- repairCommand,
859
- "Regenerate the recipe index from the scanned recipes.",
860
- true,
861
- );
862
811
  const parts = [
863
812
  missing.length > 0 ? `${missing.length} declared but unlisted (${missing.slice(0, 5).join(", ")})` : "",
864
813
  stale.length > 0 ? `${stale.length} listed but gone (${stale.slice(0, 5).join(", ")})` : "",
865
814
  ].filter(Boolean);
866
- diagnostics.push({
867
- severity: "error",
868
- code: "recipe-index-stale",
869
- path: indexPath,
870
- message: `${indexPath} recipe index is out of date — ${parts.join("; ")}. Agents read this list as authoritative.`,
871
- repairActions: [action],
872
- });
873
- repairActions.push(action);
815
+ report(
816
+ {
817
+ severity: "error",
818
+ code: "recipe-index-stale",
819
+ path: indexPath,
820
+ message: `${indexPath} recipe index is out of date — ${parts.join("; ")}. Agents read this list as authoritative.`,
821
+ },
822
+ repairAction("generated", repairCommand, "Regenerate the recipe index from the scanned recipes.", true),
823
+ );
874
824
  };
875
825
  const frameworkDeclared = declaredByImport.get("akanjs/ui") ?? new Set<string>();
876
826
  if (frameworkDeclared.size > 0) {
@@ -1,5 +1,6 @@
1
1
  import path from "node:path";
2
- import { AkanContextAnalyzer, type AkanModuleContext } from "./akanContext";
2
+ import { isRecord } from "akanjs/common";
3
+ import { AkanContextAnalyzer, type AkanDiagnostic, type AkanModuleContext } from "./akanContext";
3
4
  import type { Workspace } from "./commandDecorators";
4
5
  import type {
5
6
  AkanContextEvidence,
@@ -107,9 +108,6 @@ const inspectAkanContextInputSchema = {
107
108
  required: ["question", "draft", "review", "request"],
108
109
  };
109
110
 
110
- const isRecord = (value: unknown): value is Record<string, unknown> =>
111
- typeof value === "object" && value !== null && !Array.isArray(value);
112
-
113
111
  const slugPart = (value: unknown) =>
114
112
  typeof value === "string"
115
113
  ? value
@@ -124,12 +122,6 @@ const optionalString = (args: Record<string, unknown>, key: string) => {
124
122
  return typeof value === "string" && value ? value : undefined;
125
123
  };
126
124
 
127
- const nestedStringArg = (args: Record<string, unknown>, key: string) => {
128
- const value = args[key];
129
- if (typeof value !== "string" || !value) throw new Error(`MCP tool argument "${key}" is required.`);
130
- return value;
131
- };
132
-
133
125
  const isInspectAkanContextRequestType = (value: unknown): value is InspectAkanContextRequest["type"] =>
134
126
  typeof value === "string" && inspectAkanContextRequestTypes.includes(value as InspectAkanContextRequest["type"]);
135
127
 
@@ -192,49 +184,30 @@ export const inspectAkanContextPropsArg = (args: Record<string, unknown>): Inspe
192
184
  if (!isInspectAkanContextRequestType(args.request.type)) {
193
185
  throw new Error('MCP tool argument "request.type" must be a supported inspect_akan_context request type.');
194
186
  }
195
- const draft = { reason: nestedStringArg(args.draft, "reason"), type: args.draft.type };
187
+ const base = { question, draft: { reason: stringArg(args.draft, "reason"), type: args.draft.type }, review };
196
188
  const request = args.request;
197
- if (request.type === "workspaceOverview") return { question, draft, review, request: { type: request.type } };
189
+ if (request.type === "workspaceOverview") return { ...base, request: { type: request.type } };
198
190
  if (request.type === "moduleContext")
199
191
  return {
200
- question,
201
- draft,
202
- review,
203
- request: {
204
- type: request.type,
205
- app: nestedStringArg(request, "app"),
206
- module: nestedStringArg(request, "module"),
207
- },
192
+ ...base,
193
+ request: { type: request.type, app: stringArg(request, "app"), module: stringArg(request, "module") },
208
194
  };
209
195
  if (request.type === "fieldInsertionContext")
210
196
  return {
211
- question,
212
- draft,
213
- review,
197
+ ...base,
214
198
  request: {
215
199
  type: request.type,
216
- app: nestedStringArg(request, "app"),
217
- module: nestedStringArg(request, "module"),
218
- field: nestedStringArg(request, "field"),
219
- fieldType: nestedStringArg(request, "fieldType"),
200
+ app: stringArg(request, "app"),
201
+ module: stringArg(request, "module"),
202
+ field: stringArg(request, "field"),
203
+ fieldType: stringArg(request, "fieldType"),
220
204
  },
221
205
  };
222
206
  if (request.type === "workflowDiagnostics")
223
- return {
224
- question,
225
- draft,
226
- review,
227
- request: { type: request.type, runIdOrPlan: nestedStringArg(request, "runIdOrPlan") },
228
- };
207
+ return { ...base, request: { type: request.type, runIdOrPlan: stringArg(request, "runIdOrPlan") } };
229
208
  return {
230
- question,
231
- draft,
232
- review,
233
- request: {
234
- type: "escape",
235
- reason: nestedStringArg(request, "reason"),
236
- nextStep: optionalString(request, "nextStep"),
237
- },
209
+ ...base,
210
+ request: { type: "escape", reason: stringArg(request, "reason"), nextStep: optionalString(request, "nextStep") },
238
211
  };
239
212
  };
240
213
 
@@ -391,11 +364,7 @@ export const applyMcpTools: McpToolDefinition[] = [
391
364
  },
392
365
  ];
393
366
 
394
- /**
395
- * `guidelineNames` fills the `get_guideline` enum. Without it the argument is a bare string and a model
396
- * has to guess one of the ~30 directory names, which it cannot see from here — the names are a
397
- * filesystem listing, so they can only arrive from a caller that already read it.
398
- */
367
+ // `guidelineNames` fills the `get_guideline` enum; they are a filesystem listing only the caller has read.
399
368
  export const listAkanMcpTools = (
400
369
  mode: "readonly" | "plan" | "apply" = "readonly",
401
370
  { guidelineNames = [] }: { guidelineNames?: readonly string[] } = {},
@@ -523,29 +492,19 @@ export const inspectAkanContext = async (
523
492
  strict: true,
524
493
  runIdOrPlan: workspacePath(workspace, props.request.runIdOrPlan),
525
494
  });
495
+ const toWorkflowDiagnostic = (
496
+ { severity, code, message, scope }: AkanDiagnostic,
497
+ context: AkanDiagnostic["context"],
498
+ ): WorkflowDiagnostic => ({ severity, code, message, scope, context });
526
499
  const baselineSummary = createWorkflowBaselineSummary(
527
- (doctor.baselineDiagnostics ?? []).map(
528
- (diagnostic): WorkflowDiagnostic => ({
529
- severity: diagnostic.severity,
530
- code: diagnostic.code,
531
- message: diagnostic.message,
532
- scope: diagnostic.scope,
533
- context: diagnostic.context,
534
- }),
535
- ),
500
+ (doctor.baselineDiagnostics ?? []).map((diagnostic) => toWorkflowDiagnostic(diagnostic, diagnostic.context)),
536
501
  { detailsIncluded: false },
537
502
  );
538
503
  diagnostics.push(
539
- ...(doctor.workflowDiagnostics ?? doctor.diagnostics).map(
540
- (diagnostic): WorkflowDiagnostic => ({
541
- severity: diagnostic.severity,
542
- code: diagnostic.code,
543
- message: diagnostic.message,
544
- scope: diagnostic.scope,
545
- context: {
546
- ...diagnostic.context,
547
- ...(diagnostic.path ? { paths: [diagnostic.path] } : {}),
548
- },
504
+ ...(doctor.workflowDiagnostics ?? doctor.diagnostics).map((diagnostic) =>
505
+ toWorkflowDiagnostic(diagnostic, {
506
+ ...diagnostic.context,
507
+ ...(diagnostic.path ? { paths: [diagnostic.path] } : {}),
549
508
  }),
550
509
  ),
551
510
  );
@@ -574,15 +533,11 @@ export const inspectAkanContext = async (
574
533
  );
575
534
  }
576
535
 
536
+ const moduleRequest =
537
+ props.request.type === "moduleContext" || props.request.type === "fieldInsertionContext" ? props.request : null;
577
538
  const context = await AkanContextAnalyzer.analyze(workspace, {
578
- app:
579
- props.request.type === "moduleContext" || props.request.type === "fieldInsertionContext"
580
- ? props.request.app
581
- : null,
582
- module:
583
- props.request.type === "moduleContext" || props.request.type === "fieldInsertionContext"
584
- ? props.request.module
585
- : null,
539
+ app: moduleRequest?.app ?? null,
540
+ module: moduleRequest?.module ?? null,
586
541
  includeAbstractContent: false,
587
542
  });
588
543
 
@@ -5,10 +5,7 @@ interface StoredSelection {
5
5
  apps: string[];
6
6
  }
7
7
 
8
- /**
9
- * The apps the last interactive pick chose, so `akan start` + Enter repeats a multi-app session instead
10
- * of re-ticking it. Advisory only: a missing, unreadable or stale file just means nothing is pre-ticked.
11
- */
8
+ // Advisory only: a missing, unreadable or stale file just means nothing is pre-ticked.
12
9
  export class AppSelectionMemory {
13
10
  static pathIn(workspaceRoot: string) {
14
11
  return path.join(workspaceRoot, "local", ".akan", "lastStart.json");