@akanjs/cli 2.4.0 → 2.4.1-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/agent.command-h4afc69n.js +26 -0
  2. package/application.command-47mj9qsy.js +165 -0
  3. package/applicationBuildRunner-yz144508.js +284 -0
  4. package/applicationReleasePackager-brvth6rs.js +245 -0
  5. package/capacitorApp-y0h6cgft.js +58 -0
  6. package/cloud.command-qgjxja3n.js +94 -0
  7. package/commandManifest.json +1 -0
  8. package/context.command-nqbtak4f.js +82 -0
  9. package/dependencyScanner-m4x5maek.js +9 -0
  10. package/guideline.command-ya0dh44f.js +356 -0
  11. package/incrementalBuilder.proc.js +89 -17394
  12. package/index-1xdrsbry.js +1447 -0
  13. package/index-45aj5ry0.js +990 -0
  14. package/index-5vvwc0cz.js +559 -0
  15. package/index-61keag0s.js +40 -0
  16. package/index-6pz1j0zj.js +62 -0
  17. package/index-73pr2cmy.js +534 -0
  18. package/index-76rn3g2c.js +76 -0
  19. package/index-77crfweb.js +1884 -0
  20. package/index-85msc0wg.js +161 -0
  21. package/index-8pkbzj26.js +840 -0
  22. package/index-8rc0bm04.js +514 -0
  23. package/index-a5rmdgy4.js +4359 -0
  24. package/index-a6sbyy0b.js +2769 -0
  25. package/index-fgc8r6dj.js +33 -0
  26. package/index-h6ca6qg0.js +2777 -0
  27. package/index-hdqztm58.js +758 -0
  28. package/index-hwzpw9c1.js +202 -0
  29. package/index-pmm9e2jf.js +120 -0
  30. package/index-qaq13qk3.js +80 -0
  31. package/index-qhtr07v8.js +1072 -0
  32. package/index-r24hmh0q.js +4 -0
  33. package/index-sgmas1fc.js +462 -0
  34. package/index-ss469dec.js +11 -0
  35. package/index-swf4bmbg.js +25 -0
  36. package/index-wnp7hwq7.js +193 -0
  37. package/index-wq8jwx8z.js +224 -0
  38. package/index-x53a5nya.js +301 -0
  39. package/index-y3hdhy4p.js +229 -0
  40. package/index-z9gvz7b0.js +83 -0
  41. package/index.js +54 -23340
  42. package/library.command-r15zdqvp.js +33 -0
  43. package/localRegistry.command-p1pgxw78.js +178 -0
  44. package/module.command-qrj3kmyz.js +54 -0
  45. package/package.command-r8sq5kzp.js +43 -0
  46. package/package.json +2 -2
  47. package/page.command-c6xdx0xm.js +24 -0
  48. package/primitive.command-pv9ssmtf.js +62 -0
  49. package/quality.command-es67wvdp.js +809 -0
  50. package/repair.command-677675vw.js +67 -0
  51. package/routeSourceValidator-wbhmbwpj.js +132 -0
  52. package/scalar.command-kabkd6wd.js +35 -0
  53. package/templates/facetIndex/index.ts +1 -1
  54. package/typeChecker-kravn7ns.js +8 -0
  55. package/typecheck.proc.js +4 -194
  56. package/workflow.command-64r6cw0w.js +108 -0
  57. package/workspace.command-xk68sd6c.js +435 -0
@@ -0,0 +1,202 @@
1
+ // @bun
2
+ import {
3
+ AkanContextAnalyzer
4
+ } from "./index-8rc0bm04.js";
5
+ import {
6
+ Prompter
7
+ } from "./index-qaq13qk3.js";
8
+ import {
9
+ runner,
10
+ script
11
+ } from "./index-hdqztm58.js";
12
+
13
+ // pkgs/@akanjs/cli/agent/agent.script.ts
14
+ import { Logger } from "akanjs/common";
15
+
16
+ // pkgs/@akanjs/cli/agent/agent.runner.ts
17
+ var targetPaths = {
18
+ cursor: ".cursor/rules/akan.mdc",
19
+ "agents-md": "AGENTS.md",
20
+ claude: "CLAUDE.md"
21
+ };
22
+ var AGENT_BLOCK_START = "<!-- akan:agent:start -->";
23
+ var AGENT_BLOCK_END = "<!-- akan:agent:end -->";
24
+ var SAMPLE_ARTIFACTS = [
25
+ { probe: "lib/task/task.constant.ts", target: "lib/task", label: "sample database module" },
26
+ { probe: "lib/_noti/noti.service.ts", target: "lib/_noti", label: "sample service module" },
27
+ {
28
+ probe: "lib/__scalar/workHistory/workHistory.dictionary.ts",
29
+ target: "lib/__scalar/workHistory",
30
+ label: "sample scalar module"
31
+ },
32
+ { probe: "page/task/_index.tsx", target: "page/task", label: "sample task pages" }
33
+ ];
34
+ var DEFAULT_INDEX_MARKER = "Akan.js template";
35
+ var renderSampleCleanup = async (workspace, appNames) => {
36
+ const items = [];
37
+ for (const appName of appNames) {
38
+ for (const artifact of SAMPLE_ARTIFACTS) {
39
+ if (await workspace.exists(`apps/${appName}/${artifact.probe}`)) {
40
+ items.push(`- \`apps/${appName}/${artifact.target}\` \u2014 ${artifact.label}; delete it once you no longer need the reference.`);
41
+ }
42
+ }
43
+ const indexPath = `apps/${appName}/page/_index.tsx`;
44
+ if (await workspace.exists(indexPath)) {
45
+ const content = await workspace.readFile(indexPath).catch(() => "");
46
+ if (content.includes(DEFAULT_INDEX_MARKER)) {
47
+ items.push(`- \`${indexPath}\` \u2014 default Akan landing page; replace it with your own home page.`);
48
+ }
49
+ }
50
+ }
51
+ if (items.length === 0)
52
+ return "";
53
+ return `## Start Clean (Remove Scaffolded Samples)
54
+
55
+ This workspace was scaffolded with reference samples so the Akan conventions are visible in real code. They are
56
+ not part of your product. Before building real features, remove the samples below and run \`akan sync <app>\`:
57
+
58
+ ${items.join(`
59
+ `)}
60
+
61
+ Keep a sample only while you are still learning its pattern; delete it once your own modules cover the same ground.
62
+
63
+ `;
64
+ };
65
+ var renderManagedBlock = async (workspace) => {
66
+ const context = await AkanContextAnalyzer.analyze(workspace);
67
+ const frameworkGuide = await Prompter.getInstruction("framework");
68
+ const sampleCleanup = await renderSampleCleanup(workspace, context.apps.map((app) => app.name));
69
+ return `## Workspace
70
+
71
+ - Repo: ${context.repoName}
72
+ - Apps: ${context.apps.map((app) => app.name).join(", ") || "none"}
73
+ - Libraries: ${context.libs.map((lib) => lib.name).join(", ") || "none"}
74
+ - Packages: ${context.pkgs.map((pkg) => pkg.name).join(", ") || "none"}
75
+
76
+ ${sampleCleanup}## Akan Module Abstracts
77
+
78
+ - Before changing a domain, service, or scalar module, read its \`*.abstract.md\` file first.
79
+ - Update the abstract when business invariants, workflows, or public behavior change.
80
+ - Do not update the abstract for formatting-only, import-only, or style-only changes.
81
+ - Service modules live in \`lib/_<service>\`, but their abstract file is \`<service>.abstract.md\`.
82
+
83
+ ## Generated Files
84
+
85
+ Do not hand-edit generated Akan files such as ${context.generatedFiles.map((file) => `\`${file}\``).join(", ")}.
86
+ If generated output is stale or broken, update the owning source file and run \`akan repair generated\` or \`akan sync <app-or-lib>\`.
87
+
88
+ ## MCP Workflow Policy
89
+
90
+ - Prefer Akan MCP workflows before direct source edits.
91
+ - Direct source edits are denied when an allowlisted Akan workflow or repair tool can perform the change.
92
+ - Use \`akan mcp --mode plan\` to inspect \`list_workflows\`, \`explain_workflow\`, and \`plan_workflow\`.
93
+ - If \`plan_workflow\` returns \`planPath\` or \`next.tool=apply_workflow\`, call \`apply_workflow({ planPath })\` before editing source files directly.
94
+ - Use \`akan mcp --mode apply\` only for allowlisted \`apply_workflow\`, \`run_validation\`, and repair tools.
95
+ - After \`apply_workflow\`, run \`run_validation\` with \`validationTarget\` when present; otherwise use \`applyReportPath\`.
96
+ - If no workflow exists, or apply reports unsupported/no-op/failed diagnostics that require manual action, keep edits scoped to owning source files and never patch generated files directly.
97
+ - For compound requests, split the request into workflows and apply each \`planPath\` in order, such as \`create-module\` followed by \`add-field\`.
98
+ - **CLI-only fallback (MCP not connected):** \`akan mcp\` starts a stdio MCP server, so the \`list_workflows\`/\`plan_workflow\`/\`apply_workflow\` tools exist only when your agent is wired to it as an MCP client. When they are unavailable, the CLI is a first-class equivalent: \`akan workflow list\` / \`explain <name>\` / \`plan <name> ... --format json --out <planPath>\` / \`apply <planPath> --format json\`, \`akan doctor --strict --format json\` for validation, and \`akan repair generated|imports|module-shape --app <app> --format json\` for repairs. Scaffolding primitives (\`create-module\`/\`create-scalar\`/\`create-service\` take the target app/lib as a POSITIONAL arg; \`add-field\`/\`add-enum-field\` use \`--app\`/\`--module\` flags) call the same code the workflows do.
99
+
100
+ ## Validation
101
+
102
+ ${context.validationCommands.map((command) => `- \`${command}\``).join(`
103
+ `)}
104
+
105
+ ## Framework Guide
106
+
107
+ ${frameworkGuide.trim()}`;
108
+ };
109
+ var upsertManagedBlock = (existing, block) => {
110
+ const managed = `${AGENT_BLOCK_START}
111
+ ${block}
112
+ ${AGENT_BLOCK_END}`;
113
+ const startIndex = existing.indexOf(AGENT_BLOCK_START);
114
+ const endIndex = existing.indexOf(AGENT_BLOCK_END);
115
+ if (startIndex >= 0 && endIndex > startIndex) {
116
+ return `${existing.slice(0, startIndex)}${managed}${existing.slice(endIndex + AGENT_BLOCK_END.length)}`;
117
+ }
118
+ return `${existing.replace(/\s*$/, "")}
119
+
120
+ ${managed}
121
+ `;
122
+ };
123
+ var renderAgentsMd = async (workspace, existing) => {
124
+ const block = await renderManagedBlock(workspace);
125
+ if (existing?.trim())
126
+ return upsertManagedBlock(existing, block);
127
+ const context = await AkanContextAnalyzer.analyze(workspace);
128
+ return `# ${context.repoName} Agent Guide
129
+
130
+ This file is the single source of truth for coding agents in this workspace. Claude Code reads it through
131
+ \`CLAUDE.md\` (\`@AGENTS.md\`) and Cursor through \`.cursor/rules/akan.mdc\`. The section between the
132
+ \`akan:agent\` markers is regenerated by \`akan agent install\`; edit anything outside the markers freely.
133
+
134
+ ${AGENT_BLOCK_START}
135
+ ${block}
136
+ ${AGENT_BLOCK_END}
137
+ `;
138
+ };
139
+ var renderClaudeMd = async (workspace) => {
140
+ const context = await AkanContextAnalyzer.analyze(workspace);
141
+ return `# ${context.repoName} \u2014 Claude Code Guide
142
+
143
+ @AGENTS.md
144
+ `;
145
+ };
146
+ var renderCursorRule = () => `---
147
+ description: Akan workspace agent guide
148
+ alwaysApply: true
149
+ ---
150
+
151
+ Follow the workspace agent guide, which is the single source of truth for Akan conventions,
152
+ generated-file rules, and the MCP workflow policy.
153
+
154
+ @AGENTS.md
155
+ `;
156
+ var renderTarget = async (workspace, target, existing) => {
157
+ if (target === "agents-md")
158
+ return await renderAgentsMd(workspace, existing);
159
+ if (target === "claude")
160
+ return await renderClaudeMd(workspace);
161
+ return renderCursorRule();
162
+ };
163
+
164
+ class AgentRunner extends runner("agent") {
165
+ async install(workspace, targets, { force = false } = {}) {
166
+ const written = [];
167
+ for (const target of targets) {
168
+ const filePath = targetPaths[target];
169
+ const exists = await workspace.exists(filePath);
170
+ if (exists && !force && target !== "agents-md") {
171
+ throw new Error(`${filePath} already exists. Re-run with --force to overwrite it.`);
172
+ }
173
+ const existing = exists ? await workspace.readFile(filePath) : null;
174
+ const content = await renderTarget(workspace, target, existing);
175
+ await workspace.writeFile(filePath, content, { overwrite: true });
176
+ written.push(filePath);
177
+ }
178
+ return written;
179
+ }
180
+ }
181
+
182
+ // pkgs/@akanjs/cli/agent/agent.script.ts
183
+ var resolveTargets = (target) => {
184
+ if (!target || target === "all")
185
+ return ["cursor", "agents-md", "claude"];
186
+ if (target === "cursor" || target === "agents-md" || target === "claude")
187
+ return [target];
188
+ throw new Error(`Unknown agent target: ${target}. Use cursor, agents-md, claude, or all.`);
189
+ };
190
+
191
+ class AgentScript extends script("agent", [AgentRunner]) {
192
+ async agent(workspace, action, target, { force = false } = {}) {
193
+ if (action !== "install")
194
+ throw new Error(`Unknown agent action: ${action}. Use "install".`);
195
+ const written = await this.agentRunner.install(workspace, resolveTargets(target), { force });
196
+ Logger.rawLog(`Agent rules written:
197
+ ${written.map((file) => `- ${file}`).join(`
198
+ `)}`);
199
+ }
200
+ }
201
+
202
+ export { AgentScript };
@@ -0,0 +1,120 @@
1
+ // @bun
2
+ import {
3
+ runner,
4
+ script
5
+ } from "./index-hdqztm58.js";
6
+ import {
7
+ LibExecutor
8
+ } from "./index-a6sbyy0b.js";
9
+
10
+ // pkgs/@akanjs/cli/semver.ts
11
+ function parseVersion(version) {
12
+ return version.replace(/^[^\d]*/, "").split(/[.-]/).map((part) => Number.parseInt(part, 10)).map((part) => Number.isFinite(part) ? part : 0);
13
+ }
14
+ function compareSemver(a, b) {
15
+ const left = parseVersion(a);
16
+ const right = parseVersion(b);
17
+ const length = Math.max(left.length, right.length);
18
+ for (let i = 0;i < length; i++) {
19
+ const diff = (left[i] ?? 0) - (right[i] ?? 0);
20
+ if (diff !== 0)
21
+ return diff > 0 ? 1 : -1;
22
+ }
23
+ return 0;
24
+ }
25
+
26
+ // pkgs/@akanjs/cli/library/library.runner.ts
27
+ class LibraryRunner extends runner("library") {
28
+ async createLibrary(libName, workspace) {
29
+ await workspace.exec(`mkdir -p libs/${libName}`);
30
+ await workspace.applyTemplate({ basePath: `libs/${libName}`, template: "libRoot", dict: { libName } });
31
+ const lib = LibExecutor.from(workspace, libName);
32
+ return lib;
33
+ }
34
+ async removeLibrary(lib) {
35
+ await lib.workspace.exec(`rm -rf libs/${lib.name}`);
36
+ }
37
+ async#copyInstalledLibrary(workspace, libName) {
38
+ const installedPackageJson = `node_modules/akanjs/libs/${libName}/package.json`;
39
+ if (!await workspace.exists(installedPackageJson))
40
+ return false;
41
+ await workspace.cp(`node_modules/akanjs/libs/${libName}`, `libs/${libName}`);
42
+ return true;
43
+ }
44
+ async#copyLibraryFromRepository(workspace, libName) {
45
+ await workspace.mkdir("node_modules/.akan");
46
+ if (await workspace.exists("node_modules/.akan/akanjs"))
47
+ await workspace.removeDir("node_modules/.akan/akanjs");
48
+ await workspace.exec(`cd node_modules/.akan && git clone https://github.com/akan-team/akanjs.git`);
49
+ await workspace.cp(`node_modules/.akan/akanjs/libs/${libName}`, `libs/${libName}`);
50
+ }
51
+ async installLibrary(workspace, libName) {
52
+ const copiedFromInstalledPackage = await this.#copyInstalledLibrary(workspace, libName);
53
+ if (!copiedFromInstalledPackage)
54
+ await this.#copyLibraryFromRepository(workspace, libName);
55
+ await workspace.cp(`libs/${libName}/env/env.server.example.ts`, `libs/${libName}/env/env.server.testing.ts`);
56
+ await workspace.commit(`Add ${libName} library`);
57
+ return LibExecutor.from(workspace, libName);
58
+ }
59
+ async mergeLibraryDependencies(lib) {
60
+ const libPackageJson = await lib.getPackageJson();
61
+ const rootPackageJson = await lib.workspace.getPackageJson();
62
+ const dependencies = {};
63
+ const devDependencies = {};
64
+ const libDependencies = { ...libPackageJson.dependencies, ...libPackageJson.devDependencies };
65
+ const rootDependencies = { ...rootPackageJson.dependencies, ...rootPackageJson.devDependencies };
66
+ const allDependencies = Object.fromEntries(Object.keys({ ...libDependencies, ...rootDependencies }).map((dep) => {
67
+ const libVersion = libDependencies[dep] ?? "0.0.0";
68
+ const rootVersion = rootDependencies[dep] ?? "0.0.0";
69
+ const newerVersion = compareSemver(rootVersion, libVersion) > 0 ? rootVersion : libVersion;
70
+ return [dep, newerVersion];
71
+ }));
72
+ Object.keys(allDependencies).sort().forEach((dep) => {
73
+ if (!!libPackageJson.dependencies?.[dep] || !!rootPackageJson.dependencies?.[dep])
74
+ dependencies[dep] = allDependencies[dep];
75
+ else
76
+ devDependencies[dep] = allDependencies[dep];
77
+ });
78
+ const newRootPackageJson = { ...rootPackageJson, dependencies, devDependencies };
79
+ await lib.workspace.setPackageJson(newRootPackageJson);
80
+ await lib.workspace.spawn("bun", ["install"]);
81
+ await lib.workspace.commit(`Merge ${lib.name} library dependencies`);
82
+ }
83
+ async testLibrary(lib) {}
84
+ }
85
+
86
+ // pkgs/@akanjs/cli/library/library.script.ts
87
+ class LibraryScript extends script("library", [LibraryRunner]) {
88
+ async syncLibrary(lib) {
89
+ const syncSpinner = lib.spinning("Syncing library...");
90
+ const scanInfo = await lib.scan();
91
+ syncSpinner.succeed(`Library ${lib.name} (libs/${lib.name}) is synced`);
92
+ return scanInfo;
93
+ }
94
+ async createLibrary(libName, workspace) {
95
+ const spinner = workspace.spinning(`Creating ${libName} library`);
96
+ const lib = await this.libraryRunner.createLibrary(libName, workspace);
97
+ spinner.succeed(`${libName} library (libs/${libName}) is created`);
98
+ await this.syncLibrary(lib);
99
+ }
100
+ async removeLibrary(lib) {
101
+ const spinner = lib.spinning("Removing library...");
102
+ await this.libraryRunner.removeLibrary(lib);
103
+ spinner.succeed(`Library ${lib.name} (libs/${lib.name}) is removed`);
104
+ }
105
+ async installLibrary(workspace, libName) {
106
+ const installSpinner = workspace.spinning(`Installing ${libName} library`);
107
+ const lib = await this.libraryRunner.installLibrary(workspace, libName);
108
+ installSpinner.succeed(`${libName} library (libs/${libName}) is installed`);
109
+ const mergeSpinner = lib.spinning("Merging library dependencies...");
110
+ await this.libraryRunner.mergeLibraryDependencies(lib);
111
+ mergeSpinner.succeed(`${libName} library (libs/${libName}) dependencies merged to root package.json`);
112
+ }
113
+ async testLibrary(lib) {
114
+ const spinner = lib.spinning("Testing library...");
115
+ await this.libraryRunner.testLibrary(lib);
116
+ spinner.succeed(`Library ${lib.name} (libs/${lib.name}) test is successful`);
117
+ }
118
+ }
119
+
120
+ export { LibraryScript };
@@ -0,0 +1,80 @@
1
+ // @bun
2
+ import {
3
+ getDirname
4
+ } from "./index-a6sbyy0b.js";
5
+
6
+ // pkgs/@akanjs/devkit/prompter.ts
7
+ import fsPromise from "fs/promises";
8
+ import { input, select } from "@inquirer/prompts";
9
+ class Prompter {
10
+ static async#getGuidelineRoot() {
11
+ const dirname = getDirname(import.meta.url);
12
+ const candidates = [`${dirname}/guidelines`, `${dirname}/../cli/guidelines`];
13
+ for (const candidate of candidates) {
14
+ try {
15
+ await fsPromise.access(candidate);
16
+ return candidate;
17
+ } catch {}
18
+ }
19
+ return candidates[0];
20
+ }
21
+ static async selectGuideline() {
22
+ const guidelineRoot = await Prompter.#getGuidelineRoot();
23
+ const guideNames = await Prompter.listGuidelines();
24
+ return await select({
25
+ message: "Select a guideline",
26
+ choices: guideNames.map((name) => ({ name, value: name }))
27
+ });
28
+ }
29
+ static async listGuidelines() {
30
+ const guidelineRoot = await Prompter.#getGuidelineRoot();
31
+ return (await fsPromise.readdir(guidelineRoot)).filter((name) => !name.startsWith("_")).sort();
32
+ }
33
+ static async getGuideJson(guideName) {
34
+ const guidelineRoot = await Prompter.#getGuidelineRoot();
35
+ const filePath = `${guidelineRoot}/${guideName}/${guideName}.generate.json`;
36
+ const guideJson = await fsPromise.readFile(filePath, "utf-8");
37
+ return JSON.parse(guideJson);
38
+ }
39
+ static async getInstruction(guideName) {
40
+ const guidelineRoot = await Prompter.#getGuidelineRoot();
41
+ const filePath = `${guidelineRoot}/${guideName}/${guideName}.instruction.md`;
42
+ const content = await fsPromise.readFile(filePath, "utf-8");
43
+ return content;
44
+ }
45
+ static async getUpdateRequest(guideName) {
46
+ return await input({
47
+ message: `What do you want to update in ${guideName}?`
48
+ });
49
+ }
50
+ async makeTsFileUpdatePrompt({ context, request }) {
51
+ return `You are a senior developer writing TypeScript-based programs using Akan.js, an in-house framework. Here's an overview of the Akan.js framework:
52
+ ${await this.getDocumentation("framework")}
53
+ Please understand the following background information, write code that meets the requirements, verify that it satisfies the validation conditions, and return the result.
54
+
55
+ # Code Style
56
+ - Use double quotes for all string literals in TypeScript/TSX code. Do not use single quotes.
57
+
58
+ # Background Information
59
+ \`\`\`markdown
60
+ ${context}
61
+ \`\`\`
62
+
63
+ # Requirements
64
+ \`\`\`markdown
65
+ ${request}
66
+ \`\`\`
67
+ `;
68
+ }
69
+ async getDocumentation(guideName) {
70
+ const guidelineRoot = await Prompter.#getGuidelineRoot();
71
+ const filePath = `${guidelineRoot}/${guideName}/${guideName}.instruction.md`;
72
+ const document = await fsPromise.readFile(filePath, "utf-8");
73
+ return `\`\`\`markdown
74
+ ${document}
75
+ \`\`\`
76
+ `;
77
+ }
78
+ }
79
+
80
+ export { Prompter };