@akanjs/devkit 3.0.0-beta.9 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DEV_RUNTIME_KNOBS.md +44 -26
- package/README.ko.md +21 -7
- package/README.md +17 -8
- 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 +439 -141
- package/akanConfig/akanConfig.ts +359 -275
- package/akanConfig/index.ts +1 -0
- package/akanConfig/types.ts +22 -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 +270 -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 +182 -0
- package/mobile/desktopBin.ts +144 -0
- package/mobile/desktopServerStage.test.ts +252 -0
- package/mobile/desktopServerStage.ts +178 -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 +184 -0
- package/mobile/nativeApp.ts +454 -0
- package/mobile/nativeAppLine.test.ts +69 -0
- package/mobile/nativeAppLine.ts +71 -0
- package/mobile/nativeConfig.test.ts +438 -0
- package/mobile/nativeConfig.ts +294 -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 +9 -25
- package/subspace.ts +48 -115
- package/subspaceConfig.ts +3 -11
- 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
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { PreviewChrome } from "./PreviewChrome";
|
|
2
|
+
|
|
3
|
+
export interface PreviewProbeResult {
|
|
4
|
+
url: string;
|
|
5
|
+
title: string;
|
|
6
|
+
consoleErrors: string[];
|
|
7
|
+
pageErrors: string[];
|
|
8
|
+
/** Cheap structural smells that read as "the page is broken" without anyone looking at it. */
|
|
9
|
+
domFindings: string[];
|
|
10
|
+
screenshot?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface WebViewLike {
|
|
14
|
+
navigate(url: string): Promise<void>;
|
|
15
|
+
evaluate(expression: string): Promise<unknown>;
|
|
16
|
+
screenshot(options: { encoding: "base64" }): Promise<string>;
|
|
17
|
+
close(): void | Promise<void>;
|
|
18
|
+
readonly title: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Bun.WebView rather than a downloaded browser: on macOS its webkit backend needs no Chromium at all.
|
|
22
|
+
// It is experimental, so its whole surface stays behind this adapter; a Bun minor that changes it breaks one file.
|
|
23
|
+
export class PreviewView {
|
|
24
|
+
static get available() {
|
|
25
|
+
return typeof (Bun as unknown as { WebView?: unknown }).WebView === "function" && PreviewChrome.available;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
static unavailableReason() {
|
|
29
|
+
if (typeof (Bun as unknown as { WebView?: unknown }).WebView !== "function")
|
|
30
|
+
return "This Bun build has no WebView, so the page preview is off.";
|
|
31
|
+
return PreviewChrome.unavailableReason();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
static async probe(url: string, { screenshot = false, width = 1280, height = 800 } = {}) {
|
|
35
|
+
const logs: string[] = [];
|
|
36
|
+
const view = PreviewView.#open({ width, height, onConsole: (line) => logs.push(line) });
|
|
37
|
+
try {
|
|
38
|
+
await view.navigate(url);
|
|
39
|
+
const result: PreviewProbeResult = {
|
|
40
|
+
url,
|
|
41
|
+
title: view.title,
|
|
42
|
+
consoleErrors: logs.filter((line) => line.startsWith("error") || line.startsWith("warn")),
|
|
43
|
+
pageErrors: PreviewView.#strings(await view.evaluate(pageErrorsExpression)),
|
|
44
|
+
domFindings: PreviewView.#strings(await view.evaluate(domFindingsExpression)),
|
|
45
|
+
};
|
|
46
|
+
if (screenshot) result.screenshot = await view.screenshot({ encoding: "base64" });
|
|
47
|
+
return result;
|
|
48
|
+
} finally {
|
|
49
|
+
await view.close();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static #open({ width, height, onConsole }: { width: number; height: number; onConsole: (line: string) => void }) {
|
|
54
|
+
const Ctor = (Bun as unknown as { WebView: new (options: unknown) => WebViewLike }).WebView;
|
|
55
|
+
return new Ctor({
|
|
56
|
+
width,
|
|
57
|
+
height,
|
|
58
|
+
dataStore: "ephemeral",
|
|
59
|
+
backend: PreviewChrome.backendOption(),
|
|
60
|
+
// The console option is a callback taking `(type, ...args)`, not an object of console methods.
|
|
61
|
+
console: (type: string, ...args: unknown[]) => onConsole(`${type} ${args.map(String).join(" ")}`),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static #strings(value: unknown) {
|
|
66
|
+
return Array.isArray(value) ? value.filter((entry): entry is string => typeof entry === "string") : [];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// evaluate() takes one expression (a statement list is a SyntaxError), hence the IIFEs; one call in flight per view.
|
|
71
|
+
const pageErrorsExpression = `(() => {
|
|
72
|
+
const found = [];
|
|
73
|
+
const root = document.getElementById("__next") || document.body;
|
|
74
|
+
const rendered = root ? Array.from(root.children).filter((el) => !["SCRIPT", "STYLE", "TEMPLATE", "LINK", "NOSCRIPT"].includes(el.tagName)) : [];
|
|
75
|
+
if (!rendered.length && !(root && root.innerText && root.innerText.trim())) found.push("The page body rendered empty.");
|
|
76
|
+
const overlay = document.querySelector("[data-nextjs-dialog], .error-overlay, #akan-error");
|
|
77
|
+
if (overlay) found.push("An error overlay is on the page: " + overlay.textContent.slice(0, 400));
|
|
78
|
+
return found;
|
|
79
|
+
})()`;
|
|
80
|
+
|
|
81
|
+
const domFindingsExpression = `(() => {
|
|
82
|
+
const found = [];
|
|
83
|
+
const all = Array.from(document.querySelectorAll("body *"));
|
|
84
|
+
const overflowing = all.filter((el) => el.scrollWidth > document.documentElement.clientWidth + 8);
|
|
85
|
+
if (overflowing.length) found.push(overflowing.length + " element(s) overflow the viewport horizontally, first: " + (overflowing[0].tagName + "." + overflowing[0].className).slice(0, 120));
|
|
86
|
+
const invisibleText = all.filter((el) => {
|
|
87
|
+
if (!el.textContent || !el.textContent.trim()) return false;
|
|
88
|
+
const style = getComputedStyle(el);
|
|
89
|
+
return style.color === style.backgroundColor && style.backgroundColor !== "rgba(0, 0, 0, 0)";
|
|
90
|
+
});
|
|
91
|
+
if (invisibleText.length) found.push(invisibleText.length + " element(s) render text in the same colour as their background.");
|
|
92
|
+
const unresolved = all.filter((el) => /(^|\\s)(bg|text|border)-(base-100|base-content|primary-content|error)(\\s|$)/.test(el.className || ""));
|
|
93
|
+
if (unresolved.length) found.push(unresolved.length + " element(s) use a dropped daisyUI colour slot that renders no CSS.");
|
|
94
|
+
return found;
|
|
95
|
+
})()`;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { ExtensionAPI, InlineExtension } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
/** A base64 PNG, attached only when the active model can read one. */
|
|
4
|
+
export interface TurnFeedbackImage {
|
|
5
|
+
data: string;
|
|
6
|
+
mimeType: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface TurnFeedbackFinding {
|
|
10
|
+
text: string;
|
|
11
|
+
images?: TurnFeedbackImage[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface TurnFeedbackSource {
|
|
15
|
+
/** Budget is counted per key, so one stuck source cannot spend another's attempts. */
|
|
16
|
+
key: string;
|
|
17
|
+
/** Record whatever "before" this source compares against. Never throws — a source that cannot arm is skipped. */
|
|
18
|
+
begin(): Promise<void> | void;
|
|
19
|
+
/** What the turn caused that the agent cannot see, or `undefined` when there is nothing to say. */
|
|
20
|
+
observe(): Promise<TurnFeedbackFinding | string | undefined> | TurnFeedbackFinding | string | undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface TurnFeedbackOptions {
|
|
24
|
+
/** How many times one source may reopen a turn before it gives up and lets the human see it. */
|
|
25
|
+
budget: number;
|
|
26
|
+
onNotice?: (message: string) => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// The engine's `agent_end` hook cannot veto the end of a turn, so a finding is a follow-up that opens a new one.
|
|
30
|
+
// The budget is load-bearing: without it one unfixable error reopens turns until the context or the wallet runs out.
|
|
31
|
+
export class TurnFeedback {
|
|
32
|
+
readonly #sources: TurnFeedbackSource[];
|
|
33
|
+
readonly #options: TurnFeedbackOptions;
|
|
34
|
+
readonly #spent = new Map<string, number>();
|
|
35
|
+
|
|
36
|
+
constructor(sources: TurnFeedbackSource[], options: TurnFeedbackOptions) {
|
|
37
|
+
this.#sources = sources;
|
|
38
|
+
this.#options = options;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
extension(): InlineExtension {
|
|
42
|
+
return {
|
|
43
|
+
name: "akan-turn-feedback",
|
|
44
|
+
factory: (pi: ExtensionAPI) => {
|
|
45
|
+
pi.on("agent_start", async () => {
|
|
46
|
+
await Promise.all(this.#sources.map((source) => TurnFeedback.#quietly(() => source.begin())));
|
|
47
|
+
});
|
|
48
|
+
pi.on("agent_end", async () => {
|
|
49
|
+
const message = await this.collect();
|
|
50
|
+
if (message) pi.sendUserMessage(message, { deliverAs: "followUp" });
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async collect() {
|
|
57
|
+
const texts: string[] = [];
|
|
58
|
+
const images: TurnFeedbackImage[] = [];
|
|
59
|
+
const reopened: string[] = [];
|
|
60
|
+
for (const source of this.#sources) {
|
|
61
|
+
const finding = await TurnFeedback.#quietly(() => source.observe());
|
|
62
|
+
if (!finding) {
|
|
63
|
+
this.#spent.delete(source.key);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const spent = this.#spent.get(source.key) ?? 0;
|
|
67
|
+
if (spent >= this.#options.budget) {
|
|
68
|
+
this.#options.onNotice?.(`${source.key}: still failing after ${spent} attempts — handing back to you.`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
this.#spent.set(source.key, spent + 1);
|
|
72
|
+
reopened.push(source.key);
|
|
73
|
+
if (typeof finding === "string") texts.push(finding);
|
|
74
|
+
else {
|
|
75
|
+
texts.push(finding.text);
|
|
76
|
+
images.push(...(finding.images ?? []));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (!texts.length) return undefined;
|
|
80
|
+
this.#options.onNotice?.(`${reopened.join(" · ")} reopened the turn — esc stops it`);
|
|
81
|
+
const text = [
|
|
82
|
+
"The previous turn left problems you cannot see from the transcript. Fix them, then stop.",
|
|
83
|
+
...texts,
|
|
84
|
+
].join("\n\n");
|
|
85
|
+
return images.length
|
|
86
|
+
? [{ type: "text" as const, text }, ...images.map((image) => ({ type: "image" as const, ...image }))]
|
|
87
|
+
: text;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Every source is best-effort: no dev server, no git, no preview — all of them mean "nothing to report". */
|
|
91
|
+
static async #quietly<T>(fn: () => T | Promise<T>) {
|
|
92
|
+
try {
|
|
93
|
+
return await fn();
|
|
94
|
+
} catch {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { AkanEditScope } from "../tools/AkanEditScope";
|
|
2
|
+
import { AkanVerifier } from "../tools/AkanVerifier";
|
|
3
|
+
import type { TurnFeedbackSource } from "./TurnFeedback";
|
|
4
|
+
|
|
5
|
+
export interface VerifyFeedbackOptions {
|
|
6
|
+
cwd: string;
|
|
7
|
+
/** A read-only profile reports what is stale instead of running a chain that would write generated files. */
|
|
8
|
+
readOnly?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// A hook, not a reliance on the akan_verify tool: the observed failure is a model finishing without calling it.
|
|
12
|
+
export class VerifyFeedback implements TurnFeedbackSource {
|
|
13
|
+
readonly key = "verify";
|
|
14
|
+
readonly #options: VerifyFeedbackOptions;
|
|
15
|
+
readonly #verifier: AkanVerifier;
|
|
16
|
+
#baseline: ReadonlySet<string> = new Set();
|
|
17
|
+
|
|
18
|
+
constructor(options: VerifyFeedbackOptions) {
|
|
19
|
+
this.#options = options;
|
|
20
|
+
this.#verifier = new AkanVerifier(options.cwd);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async begin() {
|
|
24
|
+
this.#baseline = await AkanEditScope.baseline(this.#options.cwd);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async observe() {
|
|
28
|
+
const scope = await AkanEditScope.since(this.#options.cwd, this.#baseline);
|
|
29
|
+
if (!scope.apps.length && !scope.libs.length) return undefined;
|
|
30
|
+
if (this.#options.readOnly) return VerifyFeedback.#readOnlyNotice(scope.needsSync, [...scope.apps, ...scope.libs]);
|
|
31
|
+
const report = await this.#verifier.verify(scope);
|
|
32
|
+
if (report.ok) return undefined;
|
|
33
|
+
return AkanVerifier.summarize(report);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static #readOnlyNotice(needsSync: boolean, targets: string[]) {
|
|
37
|
+
if (!needsSync) return undefined;
|
|
38
|
+
return `Files were added or removed under ${targets.join(", ")}, so the generated barrels are stale. This profile does not write, so run \`akan sync ${targets.join(" ")}\` yourself before trusting a typecheck.`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import { PreviewChrome } from "./PreviewChrome";
|
|
3
|
+
|
|
4
|
+
const saved = {
|
|
5
|
+
backend: process.env[PreviewChrome.backendEnvKey],
|
|
6
|
+
path: process.env[PreviewChrome.pathEnvKey],
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
afterEach(() => {
|
|
10
|
+
if (saved.backend === undefined) delete process.env[PreviewChrome.backendEnvKey];
|
|
11
|
+
else process.env[PreviewChrome.backendEnvKey] = saved.backend;
|
|
12
|
+
if (saved.path === undefined) delete process.env[PreviewChrome.pathEnvKey];
|
|
13
|
+
else process.env[PreviewChrome.pathEnvKey] = saved.path;
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe("PreviewChrome", () => {
|
|
17
|
+
test("picks the system framework on macOS and Chrome everywhere else", () => {
|
|
18
|
+
expect(PreviewChrome.backend).toBe(process.platform === "darwin" ? "webkit" : "chrome");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("an explicit backend wins over the platform default", () => {
|
|
22
|
+
process.env[PreviewChrome.backendEnvKey] = "chrome";
|
|
23
|
+
expect(PreviewChrome.backend).toBe("chrome");
|
|
24
|
+
process.env[PreviewChrome.backendEnvKey] = "webkit";
|
|
25
|
+
expect(PreviewChrome.backend).toBe("webkit");
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("an unknown backend name falls back rather than reaching the launcher", () => {
|
|
29
|
+
process.env[PreviewChrome.backendEnvKey] = "firefox";
|
|
30
|
+
expect(PreviewChrome.backend).toBe(process.platform === "darwin" ? "webkit" : "chrome");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("the Chrome option carries the two flags a container needs and Bun does not add", () => {
|
|
34
|
+
process.env[PreviewChrome.backendEnvKey] = "chrome";
|
|
35
|
+
const option = PreviewChrome.backendOption();
|
|
36
|
+
expect(typeof option).toBe("object");
|
|
37
|
+
expect((option as { argv: string[] }).argv).toContain("--disable-dev-shm-usage");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("the webkit option is the bare string the constructor validates", () => {
|
|
41
|
+
process.env[PreviewChrome.backendEnvKey] = "webkit";
|
|
42
|
+
expect(PreviewChrome.backendOption()).toBe("webkit");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("BUN_CHROME_PATH is honoured, because Bun's own launcher reads it", () => {
|
|
46
|
+
process.env[PreviewChrome.pathEnvKey] = "/opt/custom/chrome";
|
|
47
|
+
expect(PreviewChrome.executable()).toBe("/opt/custom/chrome");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("availability tracks the resolved backend", () => {
|
|
51
|
+
process.env[PreviewChrome.backendEnvKey] = "chrome";
|
|
52
|
+
process.env[PreviewChrome.pathEnvKey] = "/opt/custom/chrome";
|
|
53
|
+
expect(PreviewChrome.available).toBe(true);
|
|
54
|
+
expect(PreviewChrome.unavailableReason()).toBeUndefined();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("the unavailable message names the install step rather than only the symptom", () => {
|
|
58
|
+
process.env[PreviewChrome.backendEnvKey] = "chrome";
|
|
59
|
+
delete process.env[PreviewChrome.pathEnvKey];
|
|
60
|
+
if (PreviewChrome.available) return;
|
|
61
|
+
const reason = PreviewChrome.unavailableReason() ?? "";
|
|
62
|
+
expect(reason).toContain("chromium");
|
|
63
|
+
expect(reason).toContain(PreviewChrome.pathEnvKey);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("the image steps install fonts, or every rendered glyph is a box", () => {
|
|
67
|
+
const step = PreviewChrome.dockerRuns.join(" ");
|
|
68
|
+
expect(step).toContain("chromium");
|
|
69
|
+
expect(step).toContain("fonts-noto-cjk");
|
|
70
|
+
// Leftover apt lists bloat every image layer that takes this step.
|
|
71
|
+
expect(step).toContain("rm -rf /var/lib/apt/lists/*");
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Importing this barrel loads the engine (~122MiB resident); a host needing cheap pieces imports the owning module.
|
|
2
|
+
// agent/consoleToStderr is deliberately absent: it takes over globalThis.console, so an RPC entry imports it first.
|
|
3
|
+
export * from "./agent/AkanCodeServices";
|
|
4
|
+
export * from "./agent/akanCodeModel";
|
|
5
|
+
export * from "./agent/akanCodePaths";
|
|
6
|
+
export * from "./agent/akanSystemPrompt";
|
|
7
|
+
export * from "./agent/CodeAgent";
|
|
8
|
+
export * from "./agent/CodeAgentAsks";
|
|
9
|
+
export * from "./agent/CodeAgentEventMapper";
|
|
10
|
+
export * from "./agent/CodeAgentGate";
|
|
11
|
+
export * from "./agent/CodeAgentRpcHost";
|
|
12
|
+
export * from "./agent/CodeAgentStreamPrinter";
|
|
13
|
+
export * from "./agent/CodeAgentUi";
|
|
14
|
+
export * from "./agent/CodeMailbox";
|
|
15
|
+
export * from "./agent/CodeSessionFork";
|
|
16
|
+
export * from "./agent/CodeSessionIndex";
|
|
17
|
+
export * from "./agent/SubagentPool";
|
|
18
|
+
export * from "./feedback/DevLogFeedback";
|
|
19
|
+
export * from "./feedback/PreviewChrome";
|
|
20
|
+
export * from "./feedback/PreviewFeedback";
|
|
21
|
+
export * from "./feedback/PreviewView";
|
|
22
|
+
export * from "./feedback/TurnFeedback";
|
|
23
|
+
export * from "./feedback/VerifyFeedback";
|
|
24
|
+
export * from "./tools/AkanCli";
|
|
25
|
+
export * from "./tools/AkanCodePlugins";
|
|
26
|
+
export * from "./tools/AkanEditScope";
|
|
27
|
+
export * from "./tools/AkanEnvKeys";
|
|
28
|
+
export * from "./tools/AkanToolPack";
|
|
29
|
+
export * from "./tools/AkanVerifier";
|
|
30
|
+
export * from "./tools/MailToolPack";
|
|
31
|
+
export * from "./tools/McpClient";
|
|
32
|
+
export * from "./tools/McpToolPack";
|
|
33
|
+
export * from "./tools/SessionToolPack";
|
|
34
|
+
export * from "./tools/WebToolPack";
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: akan-code
|
|
3
|
+
description: "Answer a question about `akan code` itself — how to add or remove an MCP server, sign one in, switch model, fork or resume a session, what the slash commands and keys are, and where its files live. Read this before telling anyone how to use this tool."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# akan code — the manual
|
|
7
|
+
|
|
8
|
+
This is the tool the user is talking to right now. When they ask how to do something *with it* — rather than
|
|
9
|
+
with their code — answer from here, and quote the exact gesture: the slash command to type, or the file to
|
|
10
|
+
edit. Do not guess a command; everything this tool offers is listed below.
|
|
11
|
+
|
|
12
|
+
You cannot type a slash command yourself. They are the host's, not yours — say which one to type. You *can*
|
|
13
|
+
edit the files named below, which is the useful half of most of these answers: write the change, then say
|
|
14
|
+
which command makes it take effect.
|
|
15
|
+
|
|
16
|
+
## MCP servers
|
|
17
|
+
|
|
18
|
+
Declared in **`~/.akan/code/mcp.json`**, which every repo reads, or in this repo's **`.akan/code/mcp.json`**,
|
|
19
|
+
which wins a name both declare — in the same `mcpServers` shape Claude, Cursor and VS Code already write, so a
|
|
20
|
+
block can be pasted across rather than translated. VS Code's `servers` spelling is read too, and
|
|
21
|
+
whichever key the file already uses is the one a write goes back into.
|
|
22
|
+
|
|
23
|
+
```jsonc
|
|
24
|
+
{
|
|
25
|
+
"mcpServers": {
|
|
26
|
+
"github": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"] },
|
|
27
|
+
"linear": { "url": "https://mcp.linear.app/sse" },
|
|
28
|
+
"internal": { "url": "https://…/mcp", "headers": { "x-api-key": "…" } },
|
|
29
|
+
"old": { "command": "x", "disabled": true }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
A `url` is an http server; anything else is a command with its argv. `env` passes variables to a stdio
|
|
35
|
+
server, `headers` goes on every http request (a server behind a static token needs nothing else), and
|
|
36
|
+
`disabled: true` keeps an entry without connecting it.
|
|
37
|
+
|
|
38
|
+
From the prompt:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
/mcp what is declared, what connected, and how many tools each gave
|
|
42
|
+
/mcp <name> the tools that one server published
|
|
43
|
+
/mcp add <name> <command> [args…]
|
|
44
|
+
/mcp add <name> <https://…>
|
|
45
|
+
/mcp add --local <name> … this repo's file only, not ~/.akan/code/mcp.json
|
|
46
|
+
/mcp remove <name> this repo's file first, else ~/.akan/code/mcp.json
|
|
47
|
+
/mcp remove --local <name>
|
|
48
|
+
/mcp login <name> OAuth, in the browser
|
|
49
|
+
/mcp logout <name> forget the token, leaving the server declared
|
|
50
|
+
/mcp reload reopen this session so the file takes effect
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**A server added now is not reachable until the session reloads.** The tool allowlist is fixed when the
|
|
54
|
+
session is created, so `add`, `remove`, `login` and `logout` all reload by themselves — a session that has
|
|
55
|
+
said nothing yet has nothing on disk to reopen, and says so. If you edit `mcp.json` yourself, tell the user to
|
|
56
|
+
run `/mcp reload`.
|
|
57
|
+
|
|
58
|
+
`/mcp remove` on a name both files declare takes this repo's entry and leaves the home file's in force; its
|
|
59
|
+
notice says so, and running it again removes that one too.
|
|
60
|
+
|
|
61
|
+
Sign-in is discovered, not configured: the server's `401` names its resource metadata, that names the
|
|
62
|
+
authorization server, and most hosted servers register a client on demand — so a url is usually the whole
|
|
63
|
+
entry. For a provider that issues client ids by hand, add `"oauth": { "clientId": "…", "scope": "…" }`.
|
|
64
|
+
Tokens are stored per server in `~/.akan/code/mcpAuth.json` at mode 0600, never in the repo, and refreshed
|
|
65
|
+
silently — at connect when one has expired, and mid-session when a server refuses one, after which the call is
|
|
66
|
+
retried once. When a refresh cannot fix it, `/mcp` shows `sign-in needed` and that server's tools answer with
|
|
67
|
+
`/mcp login <name>` until the user signs in; the sign-in reloads the session, which brings the server back.
|
|
68
|
+
|
|
69
|
+
## Sessions
|
|
70
|
+
|
|
71
|
+
Stored under `.akan/code/sessions` — project history, so a second checkout of the same repo has its own.
|
|
72
|
+
|
|
73
|
+
- `←` at the start of the prompt lists past sessions; enter reopens the chosen one.
|
|
74
|
+
- `akan code --resume <id>` (`-R`) reopens one from the shell.
|
|
75
|
+
- `/fork [name]` continues the conversation twice: the copy takes over here, and the notice names the command
|
|
76
|
+
that reopens the original.
|
|
77
|
+
- `/name <text>` names this one, which is what the session list is read by.
|
|
78
|
+
- `/peers` lists other `akan code` sessions running in this workspace; `/msg <peer> <text>` hands one a
|
|
79
|
+
message.
|
|
80
|
+
- `/clear` wipes the screen and keeps the conversation; `/compact [notes]` shortens what the model carries.
|
|
81
|
+
|
|
82
|
+
## Model, effort, keys
|
|
83
|
+
|
|
84
|
+
- `/model <provider>/<id>`, e.g. `/model deepseek/deepseek-v4-pro`. `/effort <level>` for how hard it thinks.
|
|
85
|
+
- A provider is authorized by **`<PROVIDER>_API_KEY`** — `DEEPSEEK_API_KEY`, `ANTHROPIC_API_KEY`. The
|
|
86
|
+
workspace's own `.env` is read as well as the environment, and a key found there is used for the run and
|
|
87
|
+
never copied anywhere.
|
|
88
|
+
- Credentials and the model catalogue live in `~/.akan/code/` (`auth.json`, `models.json`). **`AKAN_CODE_HOME`
|
|
89
|
+
moves the whole directory** — for a container with no home, or a checkout that wants its own.
|
|
90
|
+
|
|
91
|
+
## What the agent reads, and how to change it
|
|
92
|
+
|
|
93
|
+
- Every `AGENTS.md` / `CLAUDE.md` from the workspace root down to the working directory, `.cursorrules`, and
|
|
94
|
+
any `.cursor/rules/*.mdc` that applies unconditionally (`alwaysApply: true`, or no frontmatter at all).
|
|
95
|
+
Edit those to change how this agent behaves in this repo — they are the files the editor beside it reads.
|
|
96
|
+
- Skills: drop a `<name>/SKILL.md` into `.akan/code/skills`. Extensions: `.akan/code/extensions`. Neither is
|
|
97
|
+
read from a home directory, so a session behaves the same in two checkouts.
|
|
98
|
+
|
|
99
|
+
## Keys
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
enter send · shift+enter newline · ↑↓ move (history at the edges) · ←→ move
|
|
103
|
+
esc interrupt · pgup/pgdn scroll · ^c quit · tab completes a slash command
|
|
104
|
+
@ completes a file in this repo · wheel scrolls the transcript (hold shift to select text instead)
|
|
105
|
+
cmd+v attaches an image from the clipboard · ^v does too, without the terminal in the way
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Most terminals send the same byte for enter and shift+enter, so the key only arrives where the terminal can
|
|
109
|
+
say which was pressed. In VS Code, bind it with `workbench.action.terminal.sendSequence` and a `text` of
|
|
110
|
+
`"\\\u001b\r"`; iTerm2 calls the same thing "Send Text". Either spelling of that sequence works here, and
|
|
111
|
+
the backslash a shell would read as a line continuation never reaches the prompt.
|
|
112
|
+
|
|
113
|
+
## Starting it
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
akan code open the session
|
|
117
|
+
akan code "<prompt>" run one turn and print the answer
|
|
118
|
+
akan code -i "<prompt>" open the session with the prompt already sent
|
|
119
|
+
--app <name> narrow the agent to one app
|
|
120
|
+
--profile local|pod|review|web what it may do (default local)
|
|
121
|
+
--model <provider>/<id> --thinking, --json, --rpc, --resume <id>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`review` reads and never writes, loads no project context files, and keeps no session on disk. `pod` has
|
|
125
|
+
nobody in front of it: it cannot ask a question. MCP is off in both.
|
|
126
|
+
|
|
127
|
+
## The rest of the commands
|
|
128
|
+
|
|
129
|
+
`/help` prints all of this list · `/tools` what this session can call · `/agents` what a sub-agent may be and
|
|
130
|
+
what one may spend · `/thinking` shows or folds the reasoning · `/abort` stops the running turn · `/quit`.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: akan-debug
|
|
3
|
+
description: "Debug a running Akan app: where the dev-server log is, how to read it, and how to tell a runtime failure from a build one."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Akan debug
|
|
7
|
+
|
|
8
|
+
## The log is a file, and that file is the whole session
|
|
9
|
+
|
|
10
|
+
A supervised `akan start` writes **`local/apps/<app>/runtime/dev.log`** — no ANSI, nothing truncated, every
|
|
11
|
+
process of the app in arrival order, including the dev host's own build output, which reaches no other
|
|
12
|
+
file. The previous session is kept beside it as `dev.prev.log`.
|
|
13
|
+
|
|
14
|
+
Read that file. Do not ask for a paste of the terminal: the full-screen view repaints one frame in place, so
|
|
15
|
+
what survives in the scrollback is bordered and truncated to the pane.
|
|
16
|
+
|
|
17
|
+
- `tail -n 200 local/apps/<app>/runtime/dev.log` after an edit, to see what the reload produced.
|
|
18
|
+
- `akan logs <app>` tails a running server with `--level --grep --endpoint --trace --child --role --origin
|
|
19
|
+
--since` and `--json`.
|
|
20
|
+
- The dev server's URL is printed in that log, and the port is allocated rather than fixed — the log is the
|
|
21
|
+
only source for it.
|
|
22
|
+
|
|
23
|
+
## Which loop you are in
|
|
24
|
+
|
|
25
|
+
- **A type or lint failure** is the `akan-validate` loop. `akan_verify`.
|
|
26
|
+
- **A runtime or SSR failure** is the dev log. The page renders blank, an overlay appears, or a request
|
|
27
|
+
500s — none of that shows up in a typecheck.
|
|
28
|
+
|
|
29
|
+
If a dev server is already running for the app, do **not** start another. If it looks wedged, surface the
|
|
30
|
+
log tail in your answer rather than killing it.
|
|
31
|
+
|
|
32
|
+
## Reading a log line
|
|
33
|
+
|
|
34
|
+
Every `Logger` call carries `traceId`, `endpoint` and `origin` alongside the message, so one request's lines
|
|
35
|
+
share a trace — `akan logs <app> --trace <id>` pulls just that request. The level ladder is
|
|
36
|
+
`trace verbose debug info warn error`; `.log()` is deprecated and emits at `info`, which is why a call that
|
|
37
|
+
reads like its own level is not one.
|
|
38
|
+
|
|
39
|
+
## Before you conclude
|
|
40
|
+
|
|
41
|
+
A change that renders is not a change that passed. Run `akan_verify`, and for a `.tsx` change check
|
|
42
|
+
`akan quality ssr` too — a UI edit that moves markup into a client bundle typechecks perfectly.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: akan-deps
|
|
3
|
+
description: "Fix an import, dependency, or barrel problem in an Akan workspace — barrels are generated, and a third-party package may not be imported from most files."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Akan imports, deps and barrels
|
|
7
|
+
|
|
8
|
+
## Barrels are generated
|
|
9
|
+
|
|
10
|
+
Every `index.ts` under `ui/`, `webkit/`, `srvkit/`, `common/`, `plugin/` and `lib/**` is scanner output, as
|
|
11
|
+
are `cnst.ts`, `db.ts`, `dict.ts`, `sig.ts`, `srv.ts`, `st.ts`, `useClient.ts`, `useServer.ts`, `client.ts`
|
|
12
|
+
and `server.ts`. Do not hand-edit them and do not rely on their exact contents.
|
|
13
|
+
|
|
14
|
+
**If an import "cannot be found" but the file exists, the barrel is stale** — run `akan sync <app-or-lib>`,
|
|
15
|
+
or `repair_generated`. A `ui/<Folder>/index.tsx` that builds a namespace is hand-written source; the
|
|
16
|
+
distinguishing test is that a generated barrel contains nothing but `export * from "./X";` lines.
|
|
17
|
+
|
|
18
|
+
## Where a dependency is declared
|
|
19
|
+
|
|
20
|
+
Add the package to the `package.json` of the app or lib that imports it —
|
|
21
|
+
`apps/<app>/package.json` or `libs/<lib>/package.json` — then `bun install` from the repo root.
|
|
22
|
+
|
|
23
|
+
## A third-party package may not be imported from most files
|
|
24
|
+
|
|
25
|
+
`no-import-external-library.grit` refuses a third-party import from `page/**`, from any barrel, and from any
|
|
26
|
+
`*.{constant,dictionary,document,service,signal,store}.ts` or `*.{Template,Unit,Util,View,Zone}.tsx`.
|
|
27
|
+
Re-export the symbol through a lib first. The one-line re-export shims in a lib's `common/`, `webkit/` and
|
|
28
|
+
`ui/` exist for exactly this — they are load-bearing, not cruft.
|
|
29
|
+
|
|
30
|
+
## Two more import rules the build enforces
|
|
31
|
+
|
|
32
|
+
- **No deep import past a barrel** (`no-deep-internal-import.grit`). Cross-module constant references such as
|
|
33
|
+
`../map/map.constant` are the sanctioned exception.
|
|
34
|
+
- **No import across the client/server boundary.** A client file may not import a `*.document.ts` /
|
|
35
|
+
`*.dictionary.ts` / `*.service.ts` / `*.signal.ts`, `srvkit/`, a package `server` entrypoint, or the `db` /
|
|
36
|
+
`srv` / `sig` / `dict` / `option` / `useServer` barrels. A server file may not import a `*.store.ts`, a
|
|
37
|
+
module component, `ui/`, `webkit/`, a package `client` entrypoint, or `st` / `store` / `useClient`. Shared
|
|
38
|
+
files — `common/` and `*.constant.ts` — are held to **both**. `import type` is erased and stays legal in
|
|
39
|
+
every direction; a mixed value-and-type import is not exempt.
|
|
40
|
+
|
|
41
|
+
## Aliases
|
|
42
|
+
|
|
43
|
+
`akanjs/*` for framework facets, `@apps/*`, `@libs/*`, `@contract/*`. In `.tsx`, one flat named import from
|
|
44
|
+
the package client path — `import { cnst, fetch, st, usePage } from "@apps/<app>/client"` — never a relative
|
|
45
|
+
`../` import. In backend `.ts`, namespace the generated barrels: `import * as cnst from "../cnst"`.
|
|
46
|
+
|
|
47
|
+
## Validate
|
|
48
|
+
|
|
49
|
+
`akan_verify`, or `akan sync <app>` then `akan lint <app>` then `akan typecheck <app>`. Raw `tsc` misses the
|
|
50
|
+
generated barrels and reports false errors.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: akan-err
|
|
3
|
+
description: "Throw errors the Akan way — a typed Err with a dictionary key, never a bare throw new Error. Lint-enforced."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# throw → Err
|
|
7
|
+
|
|
8
|
+
Domain code throws `new Err("<module>.error.<key>")` and registers the key as `[en, ko]` in that module's
|
|
9
|
+
dictionary `.error({})`. `throw new Error(...)` fails the build (`no-throw-raw-error.grit`).
|
|
10
|
+
|
|
11
|
+
## Getting the import side right
|
|
12
|
+
|
|
13
|
+
This is the common failure. Import `Err` from the source matching the **runtime side of the file**:
|
|
14
|
+
|
|
15
|
+
- server code: `import { Err } from "../dict"`
|
|
16
|
+
- UI: `from "@libs/<lib>/client"` or `from "@apps/<app>/client"`
|
|
17
|
+
|
|
18
|
+
## Where the rule does not apply
|
|
19
|
+
|
|
20
|
+
The lint rule exempts tests, `*.constant.ts`, `common/**`, and `env/**`. The last two have **no legal `Err`
|
|
21
|
+
import path at all**, so keep throwing code out of them entirely rather than reaching for a workaround.
|
|
22
|
+
|
|
23
|
+
## Where each error belongs
|
|
24
|
+
|
|
25
|
+
- a state-machine precondition → `document.ts`
|
|
26
|
+
- a cross-document rule → `service.ts`
|
|
27
|
+
- request-level policy → a guard in `signal.ts`
|
|
28
|
+
|
|
29
|
+
Best-effort code returns a sentinel (`null`, `undefined`, `{}`) instead of throwing. `try/catch` is rare and
|
|
30
|
+
always converts an exception into a decision: a guard catches, warns, and returns `false`; an adapter
|
|
31
|
+
catches, logs, and returns `null`. A store action never catches — let the framework toast the `Err`.
|
|
32
|
+
Client-side validation failure is `msg.error("<key>")` plus an early return, never a throw.
|
|
33
|
+
|
|
34
|
+
**A failure another process reported travels as itself.** A server-to-server `fetch.<endpoint>(…, { origin })`
|
|
35
|
+
restores the remote `Err` with its class, key and data. Rethrow it as-is — wrapping it in a `new Error`
|
|
36
|
+
discards what the endpoint chose to say, and a bare `Error` is generalized to "Internal Server Error" on the
|
|
37
|
+
way out.
|
|
38
|
+
|
|
39
|
+
Korean `.error()` messages end in `다.`.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: akan-module
|
|
3
|
+
description: "Add or change an Akan database module: the constant→dictionary→document→service→signal→store→UI flow and which layer owns what."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Akan module flow
|
|
7
|
+
|
|
8
|
+
Creating a **new** module? Do not hand-write these files — scaffold with `create-module` first (the
|
|
9
|
+
`akan-scaffold` skill), then fill in the layers below.
|
|
10
|
+
|
|
11
|
+
A stored model flows through layers, one file each under `lib/<model>/`:
|
|
12
|
+
|
|
13
|
+
`constant → dictionary → document → service → signal → store → UI`
|
|
14
|
+
|
|
15
|
+
| file | owns |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| `<model>.constant.ts` | the five classes, in order: `XInput → XObject → LightX → X → XInsight`. Write `XInsight` even when empty. |
|
|
18
|
+
| `<model>.dictionary.ts` | `[en, ko]` labels and the `Err` keys, in the fixed chain with empty stages still written. |
|
|
19
|
+
| `<model>.document.ts` | `XFilter → X → XModel`. Chain methods validate, mutate, `return this`, and never `save()`. |
|
|
20
|
+
| `<model>.service.ts` | load → chain → `return await ….save()`. Side effects go in `_preUpdate` / `_postCreate`. |
|
|
21
|
+
| `<model>.signal.ts` | `XInternal → XSlice → XEndpoint`, all three declared even when empty. |
|
|
22
|
+
| `<model>.store.ts` | only what `store()` does not already generate — see `akan-store`. |
|
|
23
|
+
| `<Model>.*.tsx` | rendering. `Template` / `Zone` / `Util` are client, `Unit` / `View` are server. |
|
|
24
|
+
|
|
25
|
+
## The rule most often missed
|
|
26
|
+
|
|
27
|
+
**Display and predicate logic belongs on `Light<Model>`** — `isNew()`, `canWrite(user?)`, `formatTimes()`.
|
|
28
|
+
The Light class is the one both server and client hold, so shared logic lives there instead of in a util
|
|
29
|
+
module. Collection-level helpers go `static` on the full model.
|
|
30
|
+
|
|
31
|
+
## Rules
|
|
32
|
+
|
|
33
|
+
- New module ⇒ `create-module`. New field / action / list on an existing one ⇒ `add-field` /
|
|
34
|
+
`add-mutation` / `add-slice`. Hand-writing is the fallback, not the default.
|
|
35
|
+
- A module is for data that must be **stored**. Static page content is a page.
|
|
36
|
+
- Keep business decisions in constant, document and service; API exposure in signal; client coordination in
|
|
37
|
+
store; rendering in UI.
|
|
38
|
+
- Never hand-edit a generated registry file — `cnst.ts`, `db.ts`, `dict.ts`, `option.ts`, `sig.ts`, `srv.ts`,
|
|
39
|
+
`st.ts`, `useClient.ts`, `useServer.ts`, and every `index.ts` barrel. They are scanner output; run
|
|
40
|
+
`akan sync <app>` or `repair_generated`.
|
|
41
|
+
- Read the module's `<module>.abstract.md` before changing it, and update it when an invariant or workflow
|
|
42
|
+
changes — not for formatting.
|
|
43
|
+
|
|
44
|
+
Deeper detail per layer is a `get_guideline` call away: `moduleOverview`, `modelConstant`, `modelDocument`,
|
|
45
|
+
`modelService`, `modelSignal`, `modelStore`, `modelDictionary`.
|