@akanjs/cli 2.3.9-rc.0 → 2.3.9-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 (32) hide show
  1. package/incrementalBuilder.proc.js +855 -14
  2. package/index.js +3760 -1485
  3. package/package.json +2 -2
  4. package/templates/appSample/common/formatters.ts +1 -1
  5. package/templates/appSample/common/validators.ts +1 -1
  6. package/templates/appSample/lib/_noti/noti.dictionary.ts +1 -1
  7. package/templates/appSample/lib/_noti/noti.service.ts +1 -1
  8. package/templates/appSample/lib/_noti/noti.signal.ts +1 -1
  9. package/templates/appSample/lib/_noti/noti.store.ts +1 -1
  10. package/templates/appSample/lib/task/task.abstract.ts +1 -1
  11. package/templates/appSample/lib/task/task.constant.ts +1 -1
  12. package/templates/appSample/lib/task/task.dictionary.ts +1 -1
  13. package/templates/appSample/lib/task/task.document.ts +3 -3
  14. package/templates/appSample/lib/task/task.service.ts +2 -2
  15. package/templates/appSample/lib/task/task.signal.spec.ts +70 -0
  16. package/templates/appSample/lib/task/task.signal.test.ts +20 -0
  17. package/templates/appSample/lib/task/task.signal.ts +2 -2
  18. package/templates/appSample/lib/task/task.store.ts +2 -2
  19. package/templates/appSample/srvkit/AuthGuard.ts +1 -1
  20. package/templates/appSample/srvkit/SessionInternalArg.ts +1 -1
  21. package/templates/appSample/ui/GlobalLoading.tsx +1 -1
  22. package/templates/appSample/ui/QuantityControl.tsx +1 -1
  23. package/templates/appSample/webkit/useDebounce.ts +1 -1
  24. package/templates/lib/useClient.ts +1 -4
  25. package/templates/module/__Model__.Unit.tsx +1 -4
  26. package/templates/workspaceRoot/.cursor/rules/{akan-scan-conventions.mdc.template → akan-sync-conventions.mdc.template} +3 -3
  27. package/templates/workspaceRoot/.cursor/rules/akan.mdc.template +5 -0
  28. package/templates/workspaceRoot/AGENTS.md.template +77 -58
  29. package/templates/workspaceRoot/docs/AI-DEVELOPMENT.md.template +51 -0
  30. package/templates/workspaceRoot/docs/GENERATED.md.template +54 -0
  31. package/templates/workspaceRoot/package.json.template +9 -0
  32. package/templates/workspaceRoot/Untitled +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akanjs/cli",
3
- "version": "2.3.9-rc.0",
3
+ "version": "2.3.9-rc.1",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -34,7 +34,7 @@
34
34
  "@langchain/openai": "^1.4.6",
35
35
  "@tailwindcss/node": "^4.3.0",
36
36
  "@trapezedev/project": "^7.1.4",
37
- "akanjs": "2.3.9-rc.0",
37
+ "akanjs": "2.3.9-rc.1",
38
38
  "chalk": "^5.6.2",
39
39
  "commander": "^14.0.3",
40
40
  "daisyui": "5.5.23",
@@ -7,7 +7,7 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { a
7
7
  // Convention: common/ folder — only pure functions that run on both server and client.
8
8
  // Cannot import window, Bun, process.env, or any runtime-specific API.
9
9
  // Naming: camelCase .ts, file name = primary export name.
10
- // Scanned by akan scan into common/index.ts barrel automatically.
10
+ // Scanned by akan sync into common/index.ts barrel automatically.
11
11
 
12
12
  export function trimString(str: string, maxLength = 50, suffix = "...") {
13
13
  if (str.length <= maxLength) return str;
@@ -7,7 +7,7 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { a
7
7
  // Convention: common/ folder — only pure functions that run on both server and client.
8
8
  // Cannot import window, Bun, process.env, or any runtime-specific API.
9
9
  // Naming: camelCase .ts, file name = primary export name.
10
- // Scanned by akan scan into common/index.ts barrel automatically.
10
+ // Scanned by akan sync into common/index.ts barrel automatically.
11
11
 
12
12
  export function isValidEmail(email: string): boolean {
13
13
  const emailRegex = /^[^s@]+@[^s@]+.[^s@]+$/;
@@ -9,7 +9,7 @@ import type { NotiEndpoint } from "./noti.signal";
9
9
  // Uses serviceDictionary(["en", "ko"]) from akanjs/dictionary — the framework convention for service dictionaries.
10
10
  // Unlike modelDictionary, no model/lightModel/query/sort/enum sections (service modules have no DB model).
11
11
  // Sections: .endpoint() for signal endpoint names, .translate() for UI messages.
12
- // Registered by akan scan into dict.ts barrel.
12
+ // Registered by akan sync into dict.ts barrel.
13
13
 
14
14
  export const dictionary = serviceDictionary(["en", "ko"])
15
15
  .endpoint<NotiEndpoint>((fn) => ({
@@ -8,7 +8,7 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { a
8
8
  // Extends serve("noti" as const, ...) — named service (string literal), no DB model binding.
9
9
  // DB modules use serve(db.<module>, ...); service modules use serve("<name>" as const, ...).
10
10
  // DI deps available: { service, use, signal, plug, env, memory }.
11
- // Registered by akan scan into srv.ts barrel.
11
+ // Registered by akan sync into srv.ts barrel.
12
12
 
13
13
  export class NotiService extends serve("noti" as const, () => ({})) {}
14
14
 
@@ -10,7 +10,7 @@ import * as srv from "../srv";
10
10
  // Extends endpoint(srv.<module>, ...) — pubsub endpoint for real-time server→client communication.
11
11
  // pubsub() is the Akan.js convention for publish-subscribe: server publishes, all connected clients receive.
12
12
  // Client subscribes via fetch.subscribeSend((data) => { ... }).
13
- // Registered by akan scan into sig.ts barrel.
13
+ // Registered by akan sync into sig.ts barrel.
14
14
 
15
15
  export class NotiInternal extends internal(srv.noti, () => ({})) {}
16
16
 
@@ -9,7 +9,7 @@ import { store } from "akanjs/store";
9
9
  // Extends store("noti" as const, ...) — named store (string literal), no signal binding.
10
10
  // DB modules use store(sig.<module>, ...); service modules use store("<name>" as const, ...).
11
11
  // State definitions use direct property assignment. Actions use this.set() / this.get().
12
- // Registered by akan scan into st.ts barrel.
12
+ // Registered by akan sync into st.ts barrel.
13
13
 
14
14
  export class NotiStore extends store("noti" as const, () => ({
15
15
  notiList: [] as { id: string; type: string; message: string; sentAt: Dayjs }[],
@@ -28,7 +28,7 @@ Every database module in Akan.js follows this layered architecture:
28
28
 
29
29
  ## Convention: scan-registered barrels
30
30
 
31
- akan scan auto-discovers each file and registers it in the corresponding barrel:
31
+ akan sync auto-discovers each file and registers it in the corresponding barrel:
32
32
  cnst.ts, db.ts, dict.ts, srv.ts, sig.ts, st.ts
33
33
 
34
34
  ## Related Modules
@@ -12,7 +12,7 @@ import { WorkHistory } from "../__scalar/workHistory/workHistory.constant";
12
12
  // Scalars are embedded via field([ScalarType], ...) — see WorkHistory embedding below.
13
13
  // Layer order: enum → Input → Object → Light → Full.
14
14
  // Input = user-provided fields; Object = Input + system fields + embedded scalars; Light = subset for list views; Full = Object + Light.
15
- // Registered by akan scan into cnst.ts barrel.
15
+ // Registered by akan sync into cnst.ts barrel.
16
16
 
17
17
  export class TaskStatus extends enumOf("taskStatus", [
18
18
  "todo",
@@ -11,7 +11,7 @@ import type { TaskFilter } from "./task.document";
11
11
  // Uses modelDictionary(["en", "ko"]) from akanjs/dictionary — the framework convention for bilingual module dictionaries.
12
12
  // Sections: .of() module name, .model() field labels, .lightModel() list-view labels, .query() filter labels,
13
13
  // .sort() labels, .enum() value labels, .error() messages (thrown via Err()), .translate() UI messages (used in store via msg.xxx).
14
- // Registered by akan scan into dict.ts barrel.
14
+ // Registered by akan sync into dict.ts barrel.
15
15
 
16
16
  export const dictionary = modelDictionary(["en", "ko"])
17
17
  .of((t) =>
@@ -10,10 +10,10 @@ import * as cnst from "../cnst";
10
10
  // ===== task.document.ts =====
11
11
  // Convention: <module>.document.ts — the database query and persistence layer.
12
12
  // Import from/into/by from akanjs/document — the framework convention for Filter, Document, and Model classes.
13
- // Filter defines query/sort conditions (auto-generates List/Find/Pick/Exists/Count methods via akan scan).
13
+ // Filter defines query/sort conditions (auto-generates List/Find/Pick/Exists/Count methods via akan sync).
14
14
  // Document defines chainable per-document methods (e.g., task.start().save()).
15
15
  // Model = into(Document, Filter, cnst.module, ...) — collection-level operations, schema hooks.
16
- // Registered by akan scan into db.ts barrel.
16
+ // Registered by akan sync into db.ts barrel.
17
17
 
18
18
  export class TaskFilter extends from(cnst.Task, (filter) => ({
19
19
  query: {
@@ -34,7 +34,7 @@ export class Task extends by(cnst.Task) {
34
34
  start() {
35
35
  if (this.status !== "todo") throw new Err("task.error.cannotStartFromNonTodo");
36
36
  this.status = "inProgress";
37
- this.workHistory.push({ action: "completed", at: dayjs(), note: "" });
37
+ this.workHistory.push({ action: "started", at: dayjs(), note: "" });
38
38
  return this;
39
39
  }
40
40
 
@@ -9,11 +9,11 @@ import * as db from "../db";
9
9
  // ===== task.service.ts =====
10
10
  // Convention: <module>.service.ts — business logic orchestration for a database module.
11
11
  // Extends serve(db.<module>, depsCallback) from akanjs/service — binds to the DB model, receives DI deps.
12
- // Auto-generated by akan scan (do not write manually):
12
+ // Auto-generated by akan sync (do not write manually):
13
13
  // getTask(id), createModel(data), updateModel(id, data), removeModel(id),
14
14
  // listByStatus(status), searchDocs(text), and all filter+query methods from document.ts.
15
15
  // Manual below: lifecycle hooks, custom business logic methods.
16
- // Registered by akan scan into srv.ts barrel.
16
+ // Registered by akan sync into srv.ts barrel.
17
17
 
18
18
  export class TaskService extends serve(db.task, () => ({})) {
19
19
  // Lifecycle hook: runs before every document creation.
@@ -0,0 +1,70 @@
1
+ import type { AppInfo, LibInfo } from "akanjs";
2
+
3
+ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { appName: string }) {
4
+ return `import { expect } from "bun:test";
5
+ import type { DocumentModel } from "akanjs/constant";
6
+ import { getOrSetupSignalTestFetch, sampleOf } from "akanjs/test";
7
+
8
+ import * as cnst from "../cnst";
9
+ import type { fetch as appFetch } from "../useServer";
10
+
11
+ type AppFetch = typeof appFetch;
12
+
13
+ const getFetch = async () => await getOrSetupSignalTestFetch<AppFetch>();
14
+
15
+ export interface TaskAgent {
16
+ task: cnst.Task;
17
+ fetch: AppFetch;
18
+ taskInput: DocumentModel<cnst.TaskInput>;
19
+ }
20
+
21
+ export const createTask = async (overrides: Partial<DocumentModel<cnst.TaskInput>> = {}): Promise<TaskAgent> => {
22
+ const fetch = await getFetch();
23
+ const taskInput = {
24
+ ...sampleOf(cnst.TaskInput),
25
+ ...overrides,
26
+ };
27
+
28
+ const task = await fetch.createTask(taskInput);
29
+
30
+ expect(task).toMatchObject({
31
+ title: taskInput.title,
32
+ content: taskInput.content,
33
+ status: "todo",
34
+ });
35
+ expect(task.workHistory.map((entry) => entry.action)).toEqual(["created"]);
36
+
37
+ return {
38
+ task,
39
+ fetch,
40
+ taskInput,
41
+ };
42
+ };
43
+
44
+ export const getStartedTask = async (overrides: Partial<DocumentModel<cnst.TaskInput>> = {}): Promise<TaskAgent> => {
45
+ const agent = await createTask(overrides);
46
+ const task = await agent.fetch.startTask(agent.task.id);
47
+
48
+ expect(task.status).toBe("inProgress");
49
+ expect(task.workHistory.map((entry) => entry.action)).toEqual(["created", "started"]);
50
+
51
+ return {
52
+ ...agent,
53
+ task,
54
+ };
55
+ };
56
+
57
+ export const getCompletedTask = async (overrides: Partial<DocumentModel<cnst.TaskInput>> = {}): Promise<TaskAgent> => {
58
+ const agent = await getStartedTask(overrides);
59
+ const task = await agent.fetch.completeTask(agent.task.id);
60
+
61
+ expect(task.status).toBe("completed");
62
+ expect(task.workHistory.map((entry) => entry.action)).toEqual(["created", "started", "completed"]);
63
+
64
+ return {
65
+ ...agent,
66
+ task,
67
+ };
68
+ };
69
+ `;
70
+ }
@@ -0,0 +1,20 @@
1
+ import type { AppInfo, LibInfo } from "akanjs";
2
+
3
+ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { appName: string }) {
4
+ return `import { describe, expect, test } from "bun:test";
5
+ import { configureSignalTest } from "akanjs/test";
6
+
7
+ import * as taskSpec from "./task.signal.spec";
8
+
9
+ configureSignalTest({ databaseMode: "memory" });
10
+
11
+ describe("Task signal smoke", () => {
12
+ test("exposes custom task mutations over fetch", async () => {
13
+ const { task } = await taskSpec.getCompletedTask({ title: "Signal smoke task" });
14
+
15
+ expect(task.status).toBe("completed");
16
+ expect(task.workHistory.map((entry) => entry.action)).toEqual(["created", "started", "completed"]);
17
+ });
18
+ });
19
+ `;
20
+ }
@@ -12,12 +12,12 @@ import * as srv from "../srv";
12
12
  // endpoint() receives: { query, mutation, pubsub, message } — each typed with the model's return type.
13
13
  // .param() = URL path parameter, .body() = request body, .search() = query string.
14
14
  //
15
- // Auto-generated by akan scan (do not write manually):
15
+ // Auto-generated by akan sync (do not write manually):
16
16
  // viewTask(id) — fetch single task for detail view
17
17
  // editTask(id) — fetch task for edit view
18
18
  // mergeTask(id, data) — create/update task
19
19
  // Manual endpoints below: only define endpoints that need custom business logic.
20
- // Registered by akan scan into sig.ts barrel.
20
+ // Registered by akan sync into sig.ts barrel.
21
21
 
22
22
  export class TaskInternal extends internal(srv.task, ({ interval }) => ({})) {}
23
23
 
@@ -8,13 +8,13 @@ import { fetch, msg, sig } from "../useClient";
8
8
  // ===== task.store.ts =====
9
9
  // Convention: <module>.store.ts — client-side state management for a database module.
10
10
  // Extends store(sig.<module>, ...) from akanjs/store — binds to the signal to auto-generate model states/actions.
11
- // Auto-generated by akan scan (do not write manually):
11
+ // Auto-generated by akan sync (do not write manually):
12
12
  // taskForm — form state bound to model fields (create + edit)
13
13
  // setTitleOnTask, setContentOnTask, setDueOnTask — auto-setters for form fields
14
14
  // createTask(data), updateTask(id, data), removeTask(id) — CRUD actions
15
15
  // task (cached model), taskLoading, taskModal — model display states
16
16
  // Manual below: custom actions wrapping auto-generated fetch with toast feedback.
17
- // Registered by akan scan into st.ts barrel.
17
+ // Registered by akan sync into st.ts barrel.
18
18
 
19
19
  export class TaskStore extends store(sig.task, () => ({})) {
20
20
  async startTask(taskId: string) {
@@ -10,7 +10,7 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { a
10
10
  // Implements the Guard interface from akanjs/signal.
11
11
  // Guards are applied at endpoint/slice declaration: { guards: { root: SignedIn } }.
12
12
  // Naming: PascalCase .ts, static name property matches the guard identifier.
13
- // Scanned by akan scan into srvkit/index.ts barrel automatically.
13
+ // Scanned by akan sync into srvkit/index.ts barrel automatically.
14
14
 
15
15
  export class SignedIn implements Guard {
16
16
  static name = "SignedIn";
@@ -11,7 +11,7 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { a
11
11
  // InternalArg is an auto-injected argument for resolveField/endpoint .with() chains.
12
12
  // Appended to a query/mutation via: .with(CurrentUserId, { nullable: true }).exec(...)
13
13
  // Naming: PascalCase .ts, class name = arg identifier.
14
- // Scanned by akan scan into srvkit/index.ts barrel automatically.
14
+ // Scanned by akan sync into srvkit/index.ts barrel automatically.
15
15
 
16
16
  export class CurrentUserId implements InternalArg<string | null> {
17
17
  getArg(context: SignalContext): string | null {
@@ -10,7 +10,7 @@ import { clsx } from "akanjs/client";
10
10
  // ===== GlobalLoading.tsx =====
11
11
  // Convention: ui/ folder — reusable visual components. PascalCase .tsx, "use client" directive.
12
12
  // File name = exported component name.
13
- // Scanned by akan scan into ui/index.ts barrel automatically.
13
+ // Scanned by akan sync into ui/index.ts barrel automatically.
14
14
 
15
15
  interface GlobalLoadingProps {
16
16
  className?: string;
@@ -10,7 +10,7 @@ import { clsx } from "akanjs/client";
10
10
  // ===== QuantityControl.tsx =====
11
11
  // Convention: ui/ folder — reusable visual components. PascalCase .tsx, "use client" directive.
12
12
  // File name = exported component name.
13
- // Scanned by akan scan into ui/index.ts barrel automatically.
13
+ // Scanned by akan sync into ui/index.ts barrel automatically.
14
14
 
15
15
  interface QuantityControlProps {
16
16
  className?: string;
@@ -11,7 +11,7 @@ import { useEffect, useState } from "react";
11
11
  // Convention: webkit/ folder — browser-only hooks; "use client" directive required.
12
12
  // useEffect/useState are React client-side primitives that only work in the browser.
13
13
  // Naming: camelCase .ts, file name = primary export name.
14
- // Scanned by akan scan into webkit/index.ts barrel automatically.
14
+ // Scanned by akan sync into webkit/index.ts barrel automatically.
15
15
 
16
16
  export function useDebounce<T>(value: T, delay = 300): T {
17
17
  const [debouncedValue, setDebouncedValue] = useState<T>(value);
@@ -1,9 +1,6 @@
1
1
  import type { AppInfo, LibInfo } from "akanjs";
2
2
 
3
- export default function getContent(
4
- scanInfo: AppInfo | LibInfo | null,
5
- dict: { [key: string]: string } = {},
6
- ) {
3
+ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: { [key: string]: string } = {}) {
7
4
  return `
8
5
  import { makePageProto, registerClientRuntime } from "akanjs/client";
9
6
  import { FetchClient } from "akanjs/fetch";
@@ -5,10 +5,7 @@ interface Dict {
5
5
  model: string;
6
6
  sysName: string;
7
7
  }
8
- export default function getContent(
9
- scanInfo: AppInfo | LibInfo | null,
10
- dict: Dict,
11
- ) {
8
+ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dict) {
12
9
  return {
13
10
  filename: `${dict.Model}.Unit.tsx`,
14
11
  content: `
@@ -1,10 +1,10 @@
1
1
  ---
2
- description: scanSync-enforced Akan app/lib layout and generated index conventions
2
+ description: sync-enforced Akan app/lib layout and generated index conventions
3
3
  globs: apps/**/*,libs/**/*
4
4
  alwaysApply: false
5
5
  ---
6
6
 
7
- # Akan Scan Conventions
7
+ # Akan Sync Conventions
8
8
 
9
9
  - `apps/<appName>` root may only contain these files: `akan.app.json`, `akan.config.ts`, `capacitor.config.ts`, `client.ts`, `main.ts`, `package.json`, `server.ts`, `tsconfig.json`.
10
10
  - `apps/<appName>` root may only contain these folders: `.akan`, `android`, `common`, `env`, `ios`, `lib`, `page`, `private`, `public`, `script`, `srvkit`, `ui`, `webkit`.
@@ -15,5 +15,5 @@ alwaysApply: false
15
15
  - Service module UI files are limited to `<Service>.Util.tsx` and `<Service>.Zone.tsx`.
16
16
  - Scalar module UI files are limited to `<Scalar>.Template.tsx` and `<Scalar>.Unit.tsx`.
17
17
  - Module `*.test.ts`, `*.test.tsx`, `*.spec.ts`, and `*.spec.tsx` files are allowed.
18
- - `ui/index.ts`, `webkit/index.ts`, `srvkit/index.ts`, `common/index.ts`, and module `lib/**/index.ts` files are generated by scanSync; do not hand-edit or track them.
18
+ - `ui/index.ts`, `webkit/index.ts`, `srvkit/index.ts`, `common/index.ts`, and module `lib/**/index.ts` files are generated by `akan sync`; do not hand-edit or track them.
19
19
  - Generated facet indexes export only 1-depth files/folders with `export * from "./name";`.
@@ -15,6 +15,11 @@ alwaysApply: true
15
15
  - Do not hand-edit generated Akan files such as `akan.app.json`, `client.ts`, `server.ts`, generated facet indexes,
16
16
  `lib/cnst.ts`, `lib/dict.ts`, `lib/db.ts`, `lib/srv.ts`, `lib/st.ts`, `lib/sig.ts`, `lib/useClient.ts`, or
17
17
  `lib/useServer.ts`.
18
+ - Prefer Akan MCP workflows before direct source edits: use `akan mcp --mode plan` for workflow discovery and
19
+ planning, then `akan mcp --mode apply` only for allowlisted apply, validation, and repair tools.
20
+ - Direct source edits are denied when an allowlisted Akan workflow or repair tool can perform the change.
21
+ - If generated output is stale or broken, update the owning source file and run `akan repair generated` or
22
+ `akan sync <app-or-lib>` instead of patching generated files.
18
23
  - For new domain behavior, inspect sibling `constant`, `dictionary`, `signal`, `document`, `service`, `store`, and UI
19
24
  module files before changing shape.
20
25
  - Update `*.abstract.md` when business invariants, workflows, or public behavior change. Do not update it for
@@ -3,6 +3,9 @@
3
3
  This is an Akan.js workspace. Akan is convention-driven: keep business intent in the expected place and let
4
4
  the framework generate the repeated surfaces.
5
5
 
6
+ For agent-neutral documentation that Claude Code, Codex, Devin, GitHub Copilot Coding Agent, and other tools can
7
+ also read, see `docs/AI-DEVELOPMENT.md` and `docs/GENERATED.md`.
8
+
6
9
  ## Workspace Layout
7
10
 
8
11
  - `apps/<app>` contains application pages, app UI, app domain modules, env files, and `akan.config.ts`.
@@ -16,25 +19,26 @@ the framework generate the repeated surfaces.
16
19
 
17
20
  ## Generated Files
18
21
 
19
- Do not hand-edit generated Akan files. Regenerate them through Akan scan, lint, start, build, or the matching
22
+ Do not hand-edit generated Akan files. Regenerate them through Akan sync, lint, start, build, or the matching
20
23
  CLI command instead.
21
24
 
22
25
  Common generated files include:
23
26
  - `apps/*/client.ts`
24
27
  - `apps/*/server.ts`
25
- - `apps/*/lib/cnst.ts`
26
- - `apps/*/lib/dict.ts`
27
- - `apps/*/lib/db.ts`
28
- - `apps/*/lib/srv.ts`
29
- - `apps/*/lib/st.ts`
30
- - `apps/*/lib/sig.ts`
31
- - `apps/*/lib/useClient.ts`
32
- - `apps/*/lib/useServer.ts`
33
- - `apps/*/lib/*/index.ts`
34
- - `apps/*/ui/index.ts`
35
- - `apps/*/webkit/index.ts`
36
- - `apps/*/srvkit/index.ts`
37
- - `apps/*/common/index.ts`
28
+ - `*/lib/cnst.ts`
29
+ - `*/lib/db.ts`
30
+ - `*/lib/dict.ts`
31
+ - `*/lib/option.ts`
32
+ - `*/lib/sig.ts`
33
+ - `*/lib/srv.ts`
34
+ - `*/lib/st.ts`
35
+ - `*/lib/useClient.ts`
36
+ - `*/lib/useServer.ts`
37
+ - `*/lib/**/index.ts`
38
+ - `*/ui/index.ts`
39
+ - `*/webkit/index.ts`
40
+ - `*/srvkit/index.ts`
41
+ - `*/common/index.ts`
38
42
 
39
43
  ## Domain Module Responsibilities
40
44
 
@@ -58,15 +62,18 @@ Use the local module shape before adding a new abstraction.
58
62
 
59
63
  1. Read the nearby module and convention before creating files. If `*.abstract.md` exists, read it first.
60
64
  2. Put new files in the established Akan location instead of adding parallel architecture.
61
- 3. Prefer CLI generation commands for new apps, libraries, modules, scalars, pages, and generated references.
62
- 4. Keep page and layout files server-oriented: route-level `usePage`, headers, static layout, and `akanjs/ui` `Tab` composition belong there.
63
- 5. Put interactive loading, submit/import actions, local form state, and `router.push` in client `Util`, `Template`, or `Zone` components.
64
- 6. Prefer `Tab` for static mode selection instead of extra `useState`; render one focused client component inside each `Tab.Panel`.
65
- 7. Keep server-only logic out of client surfaces and client-only code out of server imports.
66
- 8. Treat `AKAN_PUBLIC_*` env vars as public. Never put secrets in them.
67
- 9. Add or update tests when behavior, contracts, or CLI output changes.
68
- 10. Update `*.abstract.md` when business invariants, workflows, or public behavior change.
69
- 11. Run the smallest relevant verification command after changes.
65
+ 3. Prefer Akan MCP workflows before direct source edits. Start with `akan mcp --mode plan` for `list_workflows`, `explain_workflow`, and `plan_workflow`.
66
+ 4. Use `akan mcp --mode apply` only for allowlisted `apply_workflow`, `run_validation`, and repair tools.
67
+ 5. Direct source edits are denied when an allowlisted Akan workflow or repair tool can perform the change.
68
+ 6. If no workflow exists, edit only the owning source files and never patch generated files directly.
69
+ 7. Keep page and layout files server-oriented: route-level `usePage`, headers, static layout, and `akanjs/ui` `Tab` composition belong there.
70
+ 8. Put interactive loading, submit/import actions, local form state, and `router.push` in client `Util`, `Template`, or `Zone` components.
71
+ 9. Prefer `Tab` for static mode selection instead of extra `useState`; render one focused client component inside each `Tab.Panel`.
72
+ 10. Keep server-only logic out of client surfaces and client-only code out of server imports.
73
+ 11. Treat `AKAN_PUBLIC_*` env vars as public. Never put secrets in them.
74
+ 12. Add or update tests when behavior, contracts, or CLI output changes.
75
+ 13. Update `*.abstract.md` when business invariants, workflows, or public behavior change.
76
+ 14. Run the smallest relevant verification command after changes.
70
77
 
71
78
  ## Common Commands
72
79
 
@@ -117,77 +124,89 @@ For the default generated app, start with:
117
124
  akan start <%= appName %>
118
125
  ```
119
126
 
120
- ### The Essential Loop: Edit Scan Check
127
+ ### The Essential Loop: Workflow -> Sync -> Check
128
+
129
+ Almost every Akan.js change follows this pattern. **Missing sync or repair is the #1 cause of agent confusion.**
121
130
 
122
- Almost every Akan.js change follows this pattern. **Missing step 2 is the #1 cause of agent confusion.**
131
+ 1. **Plan** Ask the Akan MCP server for the workflow first.
132
+ ```
133
+ akan mcp --mode plan
134
+ # use list_workflows, explain_workflow, and plan_workflow
135
+ ```
123
136
 
124
- 1. **Edit** — Create or modify source files in the correct location.
137
+ 2. **Apply or edit source** — Use allowlisted apply/repair tools when available. If no workflow exists, edit only
138
+ owning source files such as `task.constant.ts`, `task.dictionary.ts`, `Task.Template.tsx`, or `Task.Unit.tsx`.
139
+ Never edit generated files.
125
140
  ```
126
- vi apps/<%= appName %>/lib/task/task.constant.ts # e.g., add a priority field
141
+ akan mcp --mode apply
142
+ # use apply_workflow, run_validation, repair_generated, repair_imports, or repair_module_shape
127
143
  ```
128
144
 
129
- 2. **Scan** — Regenerate barrel files so Akan discovers your change. This regenerates:
145
+ 3. **Sync or repair** — Regenerate barrel files so Akan discovers your change. This regenerates:
130
146
  `cnst.ts`, `db.ts`, `srv.ts`, `sig.ts`, `st.ts`, `dict.ts`, `useClient.ts`, `useServer.ts`,
131
147
  `ui/index.ts`, `webkit/index.ts`, `srvkit/index.ts`, `common/index.ts`, and all module `index.ts` files.
132
148
  ```
133
- akan scan app <%= appName %>
149
+ akan sync <%= appName %>
150
+ # or: akan repair generated --app <%= appName %>
134
151
  ```
135
- **CRITICAL**: Scan after EVERY file add, delete, or rename. Without scan, other modules cannot
152
+ **CRITICAL**: Sync after EVERY file add, delete, or rename. Without sync, other modules cannot
136
153
  `import * as cnst from "../cnst"` and find your new model.
137
154
 
138
- 3. **Check** — Verify your change compiles and lints.
155
+ 4. **Check** — Verify your change compiles and lints.
139
156
  ```
140
157
  akan start <%= appName %> # dev server with live feedback (preferred)
141
158
  akan lint <%= appName %> # quick lint-only check
159
+ akan doctor --strict # structured workspace diagnostics
142
160
  ```
143
161
 
144
- If `akan scan` gives errors, try:
145
- - `akan build <%= appName %>` — full rebuild catches type errors scan may miss
146
- - Re-run `akan create-model <name> --app <%= appName %>` if the scaffold is corrupted
162
+ If `akan sync` gives errors, try:
163
+ - `akan build <%= appName %>` — full rebuild catches type errors sync may miss
164
+ - Re-run `akan create-module <name> --app <%= appName %>` if the scaffold is corrupted
147
165
 
148
166
  ## Quick Decision Matrix — "Where do I put this code?"
149
167
 
150
168
  | You want to... | Create in... | Run after... |
151
169
  |----------------|-------------|--------------|
152
- | Define a new database-backed noun (e.g., User, Product) | `lib/<model>/` → constant, document, service, signal, store, dictionary, abstract | `akan scan app <name>` |
153
- | Add a pure workflow / integration (e.g., Payment, Email) | `lib/_<service>/` → service, signal, store, dictionary, abstract | `akan scan app <name>` |
154
- | Add a reusable value type (e.g., Address, WorkHistory) | `lib/__scalar/<type>/` → constant, dictionary, abstract | `akan scan app <name>` |
170
+ | Define a new database-backed noun (e.g., User, Product) | `lib/<model>/` → constant, document, service, signal, store, dictionary, abstract | `akan sync <name>` |
171
+ | Add a pure workflow / integration (e.g., Payment, Email) | `lib/_<service>/` → service, signal, store, dictionary, abstract | `akan sync <name>` |
172
+ | Add a reusable value type (e.g., Address, WorkHistory) | `lib/__scalar/<type>/` → constant, dictionary, abstract | `akan sync <name>` |
155
173
  | Create a new URL-visitable page | `page/` → `_index.tsx`, `_layout.tsx`, `[param]/_index.tsx` | Rebuild (akan start auto-detects) |
156
- | Add a form or reusable UI component | `ui/` → PascalCase `.tsx` with `"use client"` if needed | `akan scan app <name>` |
157
- | Add a React hook or browser helper | `webkit/` → camelCase `.ts` with `"use client"` | `akan scan app <name>` |
158
- | Add a server-only guard, middleware, or adaptor | `srvkit/` → PascalCase `.ts` | `akan scan app <name>` |
159
- | Add a pure helper (no DOM, no server API) | `common/` → camelCase `.ts` | `akan scan app <name>` |
174
+ | Add a form or reusable UI component | `ui/` → PascalCase `.tsx` with `"use client"` if needed | `akan sync <name>` |
175
+ | Add a React hook or browser helper | `webkit/` → camelCase `.ts` with `"use client"` | `akan sync <name>` |
176
+ | Add a server-only guard, middleware, or adaptor | `srvkit/` → PascalCase `.ts` | `akan sync <name>` |
177
+ | Add a pure helper (no DOM, no server API) | `common/` → camelCase `.ts` | `akan sync <name>` |
160
178
 
161
179
  ## Anti-patterns: Never Do These
162
180
 
163
181
  | Don't | Why | Do Instead |
164
182
  |-------|-----|------------|
165
- | Edit `cnst.ts`, `db.ts`, `srv.ts`, `sig.ts`, `st.ts`, `dict.ts`, `useClient.ts`, `useServer.ts`, or any `index.ts` | These are **generated by `akan scan`**. Your changes will be overwritten. | Edit the source files in `lib/<model>/` directories and run `akan scan` |
166
- | Create a file without running scan | New files won't appear in barrel exports. Imports like `import * as cnst from "../cnst"` will fail. | Always run `akan scan app <name>` after creating, renaming, or deleting any module file |
183
+ | Edit `cnst.ts`, `db.ts`, `srv.ts`, `sig.ts`, `st.ts`, `dict.ts`, `useClient.ts`, `useServer.ts`, or any `index.ts` | These are **generated by `akan sync`**. Your changes will be overwritten. | Edit the source files in `lib/<model>/` directories and run `akan sync <name>` |
184
+ | Create a file without running sync | New files won't appear in barrel exports. Imports like `import * as cnst from "../cnst"` will fail. | Always run `akan sync <name>` after creating, renaming, or deleting any module file |
167
185
  | Use JS `#private` methods in service classes | Akan's build system rejects `#private`. Use TypeScript `private` keyword instead. | `private _methodName()` — never `#_methodName()` |
168
186
  | Use `console.log()` | Biome lint forbids `console.log`. Only `console.error`, `console.info`, `console.warn` are allowed. | Use one of the three allowed console methods |
169
187
  | Import server APIs (`fs`, `Bun`, `process.env`) in `ui/`, `webkit/`, or `common/` | Server-only imports in client code cause build failures. | Keep server dependencies in `lib/`, `srvkit/`, or `private/` only |
170
- | Skip running `akan scan` after deleting a file | Deleted files remain referenced in barrel exports, causing import errors everywhere. | Run `akan scan app <name>` after every file add, remove, or rename |
188
+ | Skip running `akan sync` after deleting a file | Deleted files remain referenced in barrel exports, causing import errors everywhere. | Run `akan sync <name>` after every file add, remove, or rename |
171
189
  | Use "use client" or `useState`/`useEffect` in pages/*.tsx, *.Unit.tsx, and *.View.tsx files | Server code cannot use React hooks. Wrap in a separate `"use client"` component. | Move hook logic to `webkit/` or a `"use client"` UI component |
172
190
  | Use `<a>` tag for internal navigation between pages | Akan.js uses `<Link>` from `akanjs/ui` for client-side navigation — avoids full page reloads. | `import { Link } from "akanjs/ui"` and use `<Link href="/task">...</Link>` |
173
191
 
174
192
  ## Generated File Tracker (Quick Reference)
175
193
 
176
- These files are regenerated by `akan scan` and overwritten on every scan. **Do not hand-edit them.**
194
+ These files are regenerated by `akan sync` and overwritten on every sync. **Do not hand-edit them.**
177
195
 
178
196
  | File | Generated From | Purpose |
179
197
  |------|---------------|---------|
180
- | `apps/*/lib/cnst.ts` | All `*/lib/*/**.constant.ts` | Barrel for all constants |
181
- | `apps/*/lib/dict.ts` | All `*/lib/*/**.dictionary.ts` | Barrel for all dictionaries |
182
- | `apps/*/lib/db.ts` | All `*/lib/<model>/*.document.ts` | Barrel for all document models |
183
- | `apps/*/lib/srv.ts` | All `*/lib/**/**.service.ts` | Barrel for all services |
184
- | `apps/*/lib/sig.ts` | All `*/lib/**/**.signal.ts` | Barrel for all signals |
185
- | `apps/*/lib/st.ts` | All `*/lib/**/**.store.ts` | Barrel for all stores |
186
- | `apps/*/lib/useClient.ts` | Client-safe module re-exports | Client-side import entry |
187
- | `apps/*/lib/useServer.ts` | Server-only module re-exports | Server-side import entry |
198
+ | `*/lib/cnst.ts` | All `*/lib/*/**.constant.ts` | Barrel for all constants |
199
+ | `*/lib/db.ts` | All `*/lib/<model>/*.document.ts` | Barrel for all document models |
200
+ | `*/lib/dict.ts` | All `*/lib/*/**.dictionary.ts` | Barrel for all dictionaries |
201
+ | `*/lib/option.ts` | Generated option helpers | Option helper entry |
202
+ | `*/lib/sig.ts` | All `*/lib/**/**.signal.ts` | Barrel for all signals |
203
+ | `*/lib/srv.ts` | All `*/lib/**/**.service.ts` | Barrel for all services |
204
+ | `*/lib/st.ts` | All `*/lib/**/**.store.ts` | Barrel for all stores |
205
+ | `*/lib/useClient.ts` | Client-safe module re-exports | Client-side import entry |
206
+ | `*/lib/useServer.ts` | Server-only module re-exports | Server-side import entry |
188
207
  | `apps/*/client.ts` | App-wide client barrel | The `fetch` and `st` instances |
189
208
  | `apps/*/server.ts` | App-wide server barrel | Server-side service resolution |
190
- | `*/lib/*/index.ts` | Per-module barrel | Module-level re-exports |
209
+ | `*/lib/**/index.ts` | Per-module barrel | Module-level re-exports |
191
210
  | `*/ui/index.ts` | All UI component files | UI barrel |
192
211
  | `*/webkit/index.ts` | All webkit files | Webkit barrel |
193
212
  | `*/srvkit/index.ts` | All srvkit files | Srvkit barrel |
@@ -206,7 +225,7 @@ rewrite the entire file.
206
225
 
207
226
  ### Recipe 1: Adding a New Field to a Model
208
227
 
209
- **Files to edit (in order):** `constant.ts` → `dictionary.ts` → `Template.tsx` → `Unit.tsx` → `akan scan`
228
+ **Files to edit (in order):** `constant.ts` → `dictionary.ts` → `Template.tsx` → `Unit.tsx` → `akan sync`
210
229
 
211
230
  ```typescript
212
231
  // 1. apps/<app>/lib/<model>/<model>.constant.ts
@@ -249,7 +268,7 @@ const form = st.use.taskForm();
249
268
  })}>{task.priority}</span>
250
269
 
251
270
  // 5. Regenerate barrels
252
- // akan scan app <name>
271
+ // akan sync <name>
253
272
  ```
254
273
 
255
274
  ---
@@ -491,7 +510,7 @@ For each business question, follow this chain:
491
510
 
492
511
  ## Auto-Generated API Reference
493
512
 
494
- akan scan automatically generates APIs across all layers. Only write custom logic — never hand-write what the framework generates.
513
+ akan sync automatically generates APIs across all layers. Only write custom logic — never hand-write what the framework generates.
495
514
 
496
515
  ### Signal — Endpoint Auto-Generation
497
516
 
@@ -542,4 +561,4 @@ akan scan automatically generates APIs across all layers. Only write custom logi
542
561
  | `exists[Query](args)`, `count[Query](args)` | Existence check and count |
543
562
  | `insight[Query](args)`, `query[Query](args)` | Insight and raw query |
544
563
 
545
- **Rule**: Define `Filter` with `.query()` conditions in `document.ts`. akan scan auto-generates all 10 query helper methods per filter. Write `Document` chain methods only for state transitions with validation.
564
+ **Rule**: Define `Filter` with `.query()` conditions in `document.ts`. akan sync auto-generates all 10 query helper methods per filter. Write `Document` chain methods only for state transitions with validation.