@akanjs/cli 2.3.9-rc.0 → 2.3.9-rc.10
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/incrementalBuilder.proc.js +3945 -532
- package/index.js +12712 -7496
- package/package.json +2 -2
- package/templates/appSample/common/formatters.ts +1 -1
- package/templates/appSample/common/validators.ts +1 -1
- package/templates/appSample/lib/_noti/noti.dictionary.ts +1 -1
- package/templates/appSample/lib/_noti/noti.service.ts +1 -1
- package/templates/appSample/lib/_noti/noti.signal.ts +1 -1
- package/templates/appSample/lib/_noti/noti.store.ts +1 -1
- package/templates/appSample/lib/task/task.abstract.ts +1 -1
- package/templates/appSample/lib/task/task.constant.ts +1 -1
- package/templates/appSample/lib/task/task.dictionary.ts +1 -1
- package/templates/appSample/lib/task/task.document.ts +3 -3
- package/templates/appSample/lib/task/task.service.ts +2 -2
- package/templates/appSample/lib/task/task.signal.spec.ts +70 -0
- package/templates/appSample/lib/task/task.signal.test.ts +20 -0
- package/templates/appSample/lib/task/task.signal.ts +2 -2
- package/templates/appSample/lib/task/task.store.ts +2 -2
- package/templates/appSample/srvkit/AuthGuard.ts +1 -1
- package/templates/appSample/srvkit/SessionInternalArg.ts +1 -1
- package/templates/appSample/ui/GlobalLoading.tsx +1 -1
- package/templates/appSample/ui/QuantityControl.tsx +1 -1
- package/templates/appSample/webkit/useDebounce.ts +1 -1
- package/templates/lib/useClient.ts +1 -4
- package/templates/module/__Model__.Unit.tsx +1 -4
- package/templates/module/__model__.constant.ts +0 -1
- package/templates/module/__model__.dictionary.ts +10 -4
- package/templates/workspaceRoot/.cursor/rules/{akan-scan-conventions.mdc.template → akan-sync-conventions.mdc.template} +3 -3
- package/templates/workspaceRoot/.cursor/rules/akan.mdc.template +12 -0
- package/templates/workspaceRoot/AGENTS.md.template +91 -58
- package/templates/workspaceRoot/biome.json.template +6 -6
- package/templates/workspaceRoot/docs/AI-DEVELOPMENT.md.template +51 -0
- package/templates/workspaceRoot/docs/GENERATED.md.template +54 -0
- package/templates/workspaceRoot/package.json.template +26 -0
- 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.
|
|
3
|
+
"version": "2.3.9-rc.10",
|
|
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.
|
|
37
|
+
"akanjs": "2.3.9-rc.10",
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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: "
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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: `
|
|
@@ -10,7 +10,6 @@ export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dic
|
|
|
10
10
|
import { via } from "akanjs/constant";
|
|
11
11
|
|
|
12
12
|
export class ${dict.Model}Input extends via((field) => ({
|
|
13
|
-
field: field(String).optional(),
|
|
14
13
|
})) {}
|
|
15
14
|
|
|
16
15
|
export class ${dict.Model}Object extends via(${dict.Model}Input, (field) => ({})) {}
|
|
@@ -3,9 +3,17 @@ import type { AppInfo, LibInfo } from "akanjs";
|
|
|
3
3
|
interface Dict {
|
|
4
4
|
Model: string;
|
|
5
5
|
model: string;
|
|
6
|
+
modelDescEn?: string;
|
|
7
|
+
modelDescKo?: string;
|
|
8
|
+
modelLabelEn?: string;
|
|
9
|
+
modelLabelKo?: string;
|
|
6
10
|
sysName: string;
|
|
7
11
|
}
|
|
8
12
|
export default function getContent(scanInfo: AppInfo | LibInfo | null, dict: Dict) {
|
|
13
|
+
const modelLabelEn = dict.modelLabelEn ?? dict.Model;
|
|
14
|
+
const modelLabelKo = dict.modelLabelKo ?? dict.Model;
|
|
15
|
+
const modelDescEn = dict.modelDescEn ?? `Manage ${modelLabelEn.toLowerCase()}.`;
|
|
16
|
+
const modelDescKo = dict.modelDescKo ?? `${modelLabelKo}을 관리합니다.`;
|
|
9
17
|
return `
|
|
10
18
|
import { modelDictionary } from "akanjs/dictionary";
|
|
11
19
|
|
|
@@ -14,11 +22,9 @@ import type { ${dict.Model}Endpoint, ${dict.Model}Slice } from "./${dict.model}.
|
|
|
14
22
|
|
|
15
23
|
export const dictionary = modelDictionary(["en", "ko"])
|
|
16
24
|
.of((t) =>
|
|
17
|
-
t(["${
|
|
25
|
+
t(["${modelLabelEn}", "${modelLabelKo}"]).desc(["${modelDescEn}", "${modelDescKo}"])
|
|
18
26
|
)
|
|
19
|
-
.model<${dict.Model}>((t) => ({
|
|
20
|
-
field: t(["Field", "필드"]).desc(["Field description", "필드 설명"]),
|
|
21
|
-
}))
|
|
27
|
+
.model<${dict.Model}>((t) => ({}))
|
|
22
28
|
.insight<${dict.Model}Insight>((t) => ({}))
|
|
23
29
|
.slice<${dict.Model}Slice>((fn) => ({
|
|
24
30
|
inPublic: fn(["${dict.Model} In Public", "${dict.Model} 공개"]).arg((t) => ({})),
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
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
|
|
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
|
|
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,18 @@ 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
|
+
- If `plan_workflow` returns `planPath` or `next.tool=apply_workflow`, call `apply_workflow({ planPath })` before
|
|
21
|
+
editing source files directly.
|
|
22
|
+
- After `apply_workflow`, run `run_validation` with `validationTarget` when present; otherwise use `applyReportPath`.
|
|
23
|
+
- Direct source edits are denied when an allowlisted Akan workflow or repair tool can perform the change.
|
|
24
|
+
- Direct edits are fallback only after `list_workflows`/`explain_workflow` show no matching workflow, or after
|
|
25
|
+
`apply_workflow` reports unsupported/no-op/failed diagnostics that require manual action.
|
|
26
|
+
- For compound requests, split the request into workflows and apply each `planPath` in order, such as `create-module`
|
|
27
|
+
followed by `add-field`.
|
|
28
|
+
- If generated output is stale or broken, update the owning source file and run `akan repair generated` or
|
|
29
|
+
`akan sync <app-or-lib>` instead of patching generated files.
|
|
18
30
|
- For new domain behavior, inspect sibling `constant`, `dictionary`, `signal`, `document`, `service`, `store`, and UI
|
|
19
31
|
module files before changing shape.
|
|
20
32
|
- Update `*.abstract.md` when business invariants, workflows, or public behavior change. Do not update it for
|