@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.
- package/DEV_RUNTIME_KNOBS.md +44 -26
- package/README.ko.md +2 -2
- package/README.md +3 -2
- package/abstractDoc.test.ts +6 -15
- package/abstractDoc.ts +6 -8
- package/agentsIndex.test.ts +4 -7
- package/agentsIndex.ts +6 -22
- package/akanApp/BackendImportGraph.test.ts +8 -22
- package/akanApp/BackendImportGraph.ts +1 -20
- package/akanApp/akanApp.host.test.ts +56 -3
- package/akanApp/akanApp.host.ts +218 -278
- package/akanApp/devBootLatch.test.ts +39 -0
- package/akanApp/devBootLatch.ts +39 -0
- package/akanApp/devHostPolicy.test.ts +81 -35
- package/akanApp/devHostPolicy.ts +41 -102
- package/akanConfig/akanBin.test.ts +62 -0
- package/akanConfig/akanBin.ts +78 -0
- package/akanConfig/akanConfig.test.ts +404 -141
- package/akanConfig/akanConfig.ts +317 -274
- package/akanConfig/index.ts +1 -0
- package/akanConfig/types.ts +21 -8
- package/akanContext.ts +110 -160
- package/akanMcpContract.ts +28 -73
- package/appSelectionMemory.ts +1 -4
- package/applicationBuildReporter.ts +37 -5
- package/applicationBuildRunner.test.ts +41 -5
- package/applicationBuildRunner.ts +71 -63
- package/applicationTestPreload.ts +3 -12
- package/artifact/implicitRootLayout.test.ts +32 -30
- package/artifact/implicitRootLayout.ts +58 -106
- package/artifact/routeSeedIndex.test.ts +7 -6
- package/artifact/routeSeedIndex.ts +11 -44
- package/biome.base.json +35 -2
- package/biomeBase.ts +2 -5
- package/cloud/cloudApi.test.ts +202 -0
- package/cloud/cloudApi.ts +77 -39
- package/cloud/configLock.test.ts +64 -0
- package/cloud/configLock.ts +96 -0
- package/cloud/constants.ts +26 -5
- package/cloud/globalConfig.ts +71 -32
- package/codeAgent/agent/AkanCodeServices.ts +97 -0
- package/codeAgent/agent/AkanContextFiles.ts +94 -0
- package/codeAgent/agent/CodeAgent.ts +551 -0
- package/codeAgent/agent/CodeAgentAsks.ts +66 -0
- package/codeAgent/agent/CodeAgentEventMapper.ts +225 -0
- package/codeAgent/agent/CodeAgentGate.ts +93 -0
- package/codeAgent/agent/CodeAgentProxy.ts +82 -0
- package/codeAgent/agent/CodeAgentRpcHost.ts +103 -0
- package/codeAgent/agent/CodeAgentRpcListener.ts +109 -0
- package/codeAgent/agent/CodeAgentStreamPrinter.ts +86 -0
- package/codeAgent/agent/CodeAgentSuspended.ts +85 -0
- package/codeAgent/agent/CodeAgentUi.ts +47 -0
- package/codeAgent/agent/CodeMailbox.ts +148 -0
- package/codeAgent/agent/CodeSessionFork.ts +56 -0
- package/codeAgent/agent/CodeSessionIndex.ts +78 -0
- package/codeAgent/agent/SubagentPool.ts +214 -0
- package/codeAgent/agent/akanCodeModel.ts +47 -0
- package/codeAgent/agent/akanCodePaths.ts +31 -0
- package/codeAgent/agent/akanContextFiles.test.ts +114 -0
- package/codeAgent/agent/akanSystemPrompt.ts +46 -0
- package/codeAgent/agent/codeAgent.test.ts +178 -0
- package/codeAgent/agent/codeAgentContract.test.ts +523 -0
- package/codeAgent/agent/codeAgentProxy.test.ts +74 -0
- package/codeAgent/agent/codeAgentRpcListener.test.ts +113 -0
- package/codeAgent/agent/codeAgentSuspended.test.ts +47 -0
- package/codeAgent/agent/codeMailbox.test.ts +76 -0
- package/codeAgent/agent/codeSessionFork.test.ts +58 -0
- package/codeAgent/agent/codeSessionIndex.test.ts +77 -0
- package/codeAgent/agent/consoleToStderr.ts +8 -0
- package/codeAgent/agent/jsonLines.ts +29 -0
- package/codeAgent/agent/stringArg.ts +5 -0
- package/codeAgent/feedback/DevLogFeedback.ts +82 -0
- package/codeAgent/feedback/PreviewChrome.ts +82 -0
- package/codeAgent/feedback/PreviewFeedback.ts +46 -0
- package/codeAgent/feedback/PreviewView.ts +95 -0
- package/codeAgent/feedback/TurnFeedback.ts +98 -0
- package/codeAgent/feedback/VerifyFeedback.ts +40 -0
- package/codeAgent/feedback/previewChrome.test.ts +73 -0
- package/codeAgent/index.ts +34 -0
- package/codeAgent/skills/akan-code/SKILL.md +130 -0
- package/codeAgent/skills/akan-debug/SKILL.md +42 -0
- package/codeAgent/skills/akan-deps/SKILL.md +50 -0
- package/codeAgent/skills/akan-err/SKILL.md +39 -0
- package/codeAgent/skills/akan-module/SKILL.md +45 -0
- package/codeAgent/skills/akan-scaffold/SKILL.md +76 -0
- package/codeAgent/skills/akan-scalar/SKILL.md +52 -0
- package/codeAgent/skills/akan-service/SKILL.md +34 -0
- package/codeAgent/skills/akan-store/SKILL.md +67 -0
- package/codeAgent/skills/akan-ui/SKILL.md +63 -0
- package/codeAgent/skills/akan-validate/SKILL.md +44 -0
- package/codeAgent/tools/AkanCli.ts +50 -0
- package/codeAgent/tools/AkanCodePlugins.ts +136 -0
- package/codeAgent/tools/AkanEditScope.ts +48 -0
- package/codeAgent/tools/AkanEnvKeys.ts +50 -0
- package/codeAgent/tools/AkanToolPack.ts +92 -0
- package/codeAgent/tools/AkanVerifier.ts +48 -0
- package/codeAgent/tools/AskToolPack.ts +99 -0
- package/codeAgent/tools/MailToolPack.ts +76 -0
- package/codeAgent/tools/McpClient.ts +187 -0
- package/codeAgent/tools/McpOAuth.ts +233 -0
- package/codeAgent/tools/McpOAuthLoopback.ts +64 -0
- package/codeAgent/tools/McpServerConfig.ts +129 -0
- package/codeAgent/tools/McpSignIn.ts +158 -0
- package/codeAgent/tools/McpTokenStore.ts +81 -0
- package/codeAgent/tools/McpToolPack.ts +199 -0
- package/codeAgent/tools/SessionToolPack.ts +91 -0
- package/codeAgent/tools/WebToolPack.ts +147 -0
- package/codeAgent/tools/akanCodePlugins.test.ts +677 -0
- package/codeAgent/tools/askToolPack.test.ts +116 -0
- package/codeAgent/tools/mcpOAuth.test.ts +612 -0
- package/codeAgent/tools/mcpServerConfig.test.ts +212 -0
- package/codegenLock.test.ts +4 -13
- package/codegenLock.ts +3 -17
- package/commandDecorators/argMeta.ts +1 -5
- package/commandDecorators/camelToKebabCase.ts +1 -0
- package/commandDecorators/command.ts +46 -102
- package/commandDecorators/commandBuilder.ts +30 -61
- package/commandDecorators/commandDecorators.test.ts +2 -10
- package/commandDecorators/dependencyBuilder.ts +10 -1
- package/commandDecorators/helpFormatter.ts +68 -145
- package/commandDecorators/index.ts +0 -2
- package/commandDecorators/targetMeta.ts +9 -7
- package/createTunnel.ts +7 -14
- package/csrE2e/csrE2eHarness.fixture.ts +505 -0
- package/csrE2e/csrFastRefresh.e2e.test.ts +233 -0
- package/csrE2e/csrPageStack.e2e.test.ts +252 -0
- package/csrE2e/csrPatchFirst.e2e.test.ts +87 -0
- package/csrE2e/csrRootLayout.e2e.test.ts +134 -0
- package/csrE2e/devHmrLatency.e2e.test.ts +391 -0
- package/csrE2e/registryVendor.e2e.test.ts +221 -0
- package/csrE2e/ssrRegistry.e2e.test.ts +644 -0
- package/dependencyScanner.test.ts +4 -16
- package/dependencyScanner.ts +31 -80
- package/devkitUtils.test.ts +48 -20
- package/executors.linkFallback.test.ts +103 -0
- package/executors.test.ts +176 -106
- package/executors.ts +212 -498
- package/fileEditor.ts +9 -37
- package/{getDirname.test.ts → fileSys.test.ts} +5 -3
- package/fileSys.ts +22 -9
- package/formSetterScanner.ts +3 -16
- package/frontendBuild/allRoutesBuilder.ts +7 -21
- package/frontendBuild/autoImportSync.test.ts +27 -38
- package/frontendBuild/autoImportSync.ts +31 -65
- package/frontendBuild/buildRouteClient.test.ts +123 -37
- package/frontendBuild/bundleDefine.test.ts +12 -0
- package/frontendBuild/bundleDefine.ts +16 -0
- package/frontendBuild/clientBuildTypes.ts +5 -36
- package/frontendBuild/clientEntriesBundler.ts +9 -40
- package/frontendBuild/clientEntryDiscovery.test.ts +112 -0
- package/frontendBuild/clientEntryDiscovery.ts +122 -110
- package/frontendBuild/csrArtifactBuilder.ts +9 -31
- package/frontendBuild/csrDevArtifactWriter.test.ts +151 -0
- package/frontendBuild/csrDevArtifactWriter.ts +307 -0
- package/frontendBuild/csrDevBundler.test.ts +66 -0
- package/frontendBuild/csrDevBundler.ts +95 -0
- package/frontendBuild/csrDevFactoryCheck.test.ts +33 -0
- package/frontendBuild/csrDevFactoryCheck.ts +39 -0
- package/frontendBuild/csrDevModuleCompiler.test.ts +293 -0
- package/frontendBuild/csrDevModuleCompiler.ts +422 -0
- package/frontendBuild/csrDevPatcher.test.ts +572 -0
- package/frontendBuild/csrDevPatcher.ts +548 -0
- package/frontendBuild/csrDevPaths.ts +145 -0
- package/frontendBuild/csrDevResolver.test.ts +239 -0
- package/frontendBuild/csrDevResolver.ts +363 -0
- package/frontendBuild/csrDevTypes.ts +61 -0
- package/frontendBuild/csrEntryFiles.ts +24 -0
- package/frontendBuild/cssCandidateCache.test.ts +0 -6
- package/frontendBuild/cssCandidateCache.ts +6 -41
- package/frontendBuild/cssCompiler.ts +31 -82
- package/frontendBuild/cssImportResolver.ts +6 -19
- package/frontendBuild/devChangePlanner.ts +7 -10
- package/frontendBuild/devGeneratedIndexSync.test.ts +12 -27
- package/frontendBuild/devGeneratedIndexSync.ts +1 -4
- package/frontendBuild/devRegistryBundler.ts +192 -0
- package/frontendBuild/fontOptimizer.test.ts +6 -14
- package/frontendBuild/fontOptimizer.ts +15 -72
- package/frontendBuild/fontPruner.test.ts +5 -10
- package/frontendBuild/fontPruner.ts +5 -20
- package/frontendBuild/frontendBuild.test.ts +57 -70
- package/frontendBuild/hmrChangeClassifier.ts +2 -2
- package/frontendBuild/hmrWatcher.test.ts +59 -32
- package/frontendBuild/hmrWatcher.ts +31 -74
- package/frontendBuild/index.ts +4 -0
- package/frontendBuild/pagesBundleBuilder.ts +28 -30
- package/frontendBuild/pagesEntrySourceGenerator.ts +56 -6
- package/frontendBuild/precompressArtifacts.ts +5 -18
- package/frontendBuild/registryMetadataFingerprint.test.ts +63 -0
- package/frontendBuild/registryMetadataFingerprint.ts +28 -0
- package/frontendBuild/routeClientBuilder.ts +100 -62
- package/frontendBuild/routesManifestArtifactSerializer.ts +5 -36
- package/frontendBuild/serverGraphFile.test.ts +77 -0
- package/frontendBuild/serverGraphFile.ts +93 -0
- package/frontendBuild/sourceMtimeIndex.test.ts +41 -97
- package/frontendBuild/sourceMtimeIndex.ts +19 -130
- package/frontendBuild/ssrBaseArtifactBuilder.ts +19 -13
- package/frontendBuild/ssrDevBundler.ts +85 -0
- package/frontendBuild/styleContract.ts +1 -8
- package/frontendBuild/themeValidator.ts +4 -26
- package/frontendBuild/watchRootResolver.test.ts +23 -42
- package/frontendBuild/watchRootResolver.ts +5 -27
- package/getRelatedCnsts.ts +12 -92
- package/incrementalBuilder/buildBatch.proc.ts +147 -63
- package/incrementalBuilder/buildBatchProtocol.ts +13 -38
- package/incrementalBuilder/buildBatchRunner.test.ts +65 -0
- package/incrementalBuilder/buildBatchRunner.ts +26 -38
- package/incrementalBuilder/builderChannel.test.ts +24 -49
- package/incrementalBuilder/builderChannel.ts +6 -37
- package/incrementalBuilder/builderRequestRouter.test.ts +0 -8
- package/incrementalBuilder/builderRequestRouter.ts +5 -24
- package/incrementalBuilder/builderWorkQueue.test.ts +94 -0
- package/incrementalBuilder/builderWorkQueue.ts +126 -0
- package/incrementalBuilder/devWatchBatch.test.ts +21 -24
- package/incrementalBuilder/incrementalBuilder.host.test.ts +53 -56
- package/incrementalBuilder/incrementalBuilder.host.ts +68 -81
- package/incrementalBuilder/incrementalBuilder.proc.test.ts +269 -0
- package/incrementalBuilder/incrementalBuilder.proc.ts +596 -208
- package/index.ts +5 -19
- package/integration/devResourceProbe.ts +50 -56
- package/integration/devStability.integration.test.ts +105 -199
- package/integration/devStabilityHarness.test.ts +13 -28
- package/integration/devStabilityHarness.ts +65 -330
- package/integration/ssrMemoryProbe.ts +41 -167
- package/libSource.test.ts +4 -15
- package/libSource.ts +19 -31
- package/lint/__fixtures__/no-document-cookie/bad.tsx +4 -0
- package/lint/__fixtures__/no-document-cookie/good.tsx +7 -0
- package/lint/__fixtures__/no-web-only-api-outside-webkit/bad.tsx +7 -0
- package/lint/__fixtures__/no-web-only-api-outside-webkit/good.tsx +11 -0
- package/lint/__fixtures__/no-web-storage/bad.tsx +6 -0
- package/lint/__fixtures__/no-web-storage/good.tsx +7 -0
- package/lint/gritRules.test.ts +8 -24
- package/lint/no-deep-internal-import.grit +2 -2
- package/lint/no-document-cookie.grit +29 -0
- package/lint/no-redeclare-predefined-endpoint.grit +2 -1
- package/lint/no-web-only-api-outside-webkit.grit +44 -0
- package/lint/no-web-storage.grit +30 -0
- package/linter.test.ts +1 -2
- package/linter.ts +26 -116
- package/mobile/desktopBin.test.ts +177 -0
- package/mobile/desktopBin.ts +143 -0
- package/mobile/desktopServerStage.test.ts +204 -0
- package/mobile/desktopServerStage.ts +138 -0
- package/mobile/index.ts +9 -0
- package/mobile/mobileTarget.test.ts +57 -8
- package/mobile/mobileTarget.ts +45 -26
- package/mobile/nativeApi.test.ts +30 -0
- package/mobile/nativeApi.ts +40 -0
- package/mobile/nativeApp.test.ts +151 -0
- package/mobile/nativeApp.ts +405 -0
- package/mobile/nativeAppLine.test.ts +69 -0
- package/mobile/nativeAppLine.ts +71 -0
- package/mobile/nativeConfig.test.ts +414 -0
- package/mobile/nativeConfig.ts +296 -0
- package/mobile/nativePluginFolders.test.ts +81 -0
- package/mobile/nativePluginFolders.ts +60 -0
- package/mobile/nativeWebDir.test.ts +80 -0
- package/mobile/nativeWebDir.ts +66 -0
- package/mobile/placeholderAppId.test.ts +13 -0
- package/mobile/placeholderAppId.ts +16 -0
- package/mobile/usageDescriptions.test.ts +27 -0
- package/mobile/usageDescriptions.ts +24 -0
- package/package.json +11 -11
- package/packageExports.test.ts +3 -18
- package/packageExportsMap.ts +3 -19
- package/piDependencyPins.test.ts +46 -0
- package/platformTest/BunTestReport.test.ts +60 -0
- package/platformTest/BunTestReport.ts +65 -0
- package/platformTest/IdleWatchedProcess.test.ts +56 -0
- package/platformTest/IdleWatchedProcess.ts +73 -0
- package/platformTest/LinuxDockerTarget.ts +105 -0
- package/platformTest/PlatformTestRun.test.ts +64 -0
- package/platformTest/PlatformTestRun.ts +141 -0
- package/platformTest/PlatformTestTarget.ts +142 -0
- package/platformTest/TestSnapshot.test.ts +72 -0
- package/platformTest/TestSnapshot.ts +103 -0
- package/platformTest/WindowsSshTarget.ts +151 -0
- package/prompter.ts +3 -13
- package/qualityScanner.test.ts +9 -26
- package/qualityScanner.ts +200 -286
- package/recipeScanner.test.ts +7 -12
- package/recipeScanner.ts +9 -38
- package/repoIdentity.ts +2 -9
- package/routeSourceValidator.test.ts +0 -1
- package/routeSourceValidator.ts +7 -42
- package/scanInfo.ts +91 -225
- package/selectModel.ts +1 -2
- package/semver.ts +3 -5
- package/slicePlanner.test.ts +6 -24
- package/slicePlanner.ts +11 -32
- package/spinner.test.ts +3 -8
- package/spinner.ts +4 -16
- package/sshTunnel.ts +1 -6
- package/ssrScanner.test.ts +1 -6
- package/ssrScanner.ts +103 -113
- package/stripAnsi.ts +3 -0
- package/subspace.test.ts +30 -25
- package/subspace.ts +81 -115
- package/subspaceConfig.ts +11 -9
- package/testHelpers.ts +194 -0
- package/transforms/asyncDefaultExportDetector.ts +3 -11
- package/transforms/barrelAnalyzer.ts +43 -135
- package/transforms/barrelImportsPlugin.ts +38 -175
- package/transforms/externalizeFrameworkPlugin.test.ts +39 -118
- package/transforms/externalizeFrameworkPlugin.ts +13 -87
- package/transforms/moduleSyntax.ts +32 -0
- package/transforms/rscUseClientTransform.ts +42 -20
- package/transforms/serverEnvPlugin.test.ts +103 -0
- package/transforms/serverEnvPlugin.ts +41 -0
- package/transforms/transforms.test.ts +166 -44
- package/transforms/tsconfigPackageResolver.test.ts +6 -24
- package/transforms/useClientBundlePlugin.ts +6 -23
- package/typeChecker.ts +7 -63
- package/types.ts +2 -0
- package/uploadRelease.ts +10 -25
- package/workflow/artifacts.ts +18 -0
- package/workflow/executor.test.ts +1 -12
- package/workflow/executor.ts +101 -110
- package/workflow/index.ts +0 -1
- package/workflow/moduleIndex.test.ts +4 -15
- package/workflow/moduleIndex.ts +9 -49
- package/workflow/plan.ts +18 -25
- package/workflow/render.ts +80 -96
- package/workflow/rolloutGate.test.ts +1 -1
- package/workflow/source.ts +52 -69
- package/workflow/sourceAst.ts +50 -0
- package/workspaceLayout.test.ts +18 -3
- package/workspaceLayout.ts +22 -23
- package/abstractCompactor.ts +0 -112
- package/aiEditor.test.ts +0 -60
- package/aiEditor.ts +0 -387
- package/applicationReleasePackager.ts +0 -209
- package/capacitorApp.test.ts +0 -372
- package/capacitorApp.ts +0 -1648
- package/commandDecorators/commandMeta.ts +0 -7
- package/commandDecorators/types.ts +0 -10
- package/getDirname.ts +0 -4
- package/guideline.ts +0 -19
- package/local/optimize-resource/ipcprobe/child.ts +0 -1
- package/local/optimize-resource/ipcprobe/parent.ts +0 -11
- package/workflow/primitive.ts +0 -59
package/akanConfig/index.ts
CHANGED
package/akanConfig/types.ts
CHANGED
|
@@ -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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
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
|
-
|
|
35
|
-
|
|
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
|
-
//
|
|
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
|
|
171
|
-
//
|
|
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
|
-
|
|
173
|
+
const bashMcpServer = (mode: AkanMcpMode, cd: string) => ({
|
|
182
174
|
type: "stdio",
|
|
183
175
|
command: "bash",
|
|
184
|
-
args: ["-lc", akanMcpCommand(mode, { cd
|
|
176
|
+
args: ["-lc", akanMcpCommand(mode, { cd })],
|
|
185
177
|
});
|
|
186
178
|
|
|
187
|
-
export const
|
|
188
|
-
|
|
189
|
-
|
|
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
|
-
//
|
|
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.
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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" &&
|
|
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
|
-
//
|
|
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
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
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
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
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
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
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
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
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
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
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) {
|
package/akanMcpContract.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import {
|
|
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
|
|
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 {
|
|
189
|
+
if (request.type === "workspaceOverview") return { ...base, request: { type: request.type } };
|
|
198
190
|
if (request.type === "moduleContext")
|
|
199
191
|
return {
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
212
|
-
draft,
|
|
213
|
-
review,
|
|
197
|
+
...base,
|
|
214
198
|
request: {
|
|
215
199
|
type: request.type,
|
|
216
|
-
app:
|
|
217
|
-
module:
|
|
218
|
-
field:
|
|
219
|
-
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
|
-
|
|
231
|
-
|
|
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
|
|
541
|
-
|
|
542
|
-
|
|
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
|
-
|
|
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
|
|
package/appSelectionMemory.ts
CHANGED
|
@@ -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");
|