@akanjs/cli 2.4.0-rc.0 → 2.4.0-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.
- package/guidelines/componentRule/componentRule.instruction.md +8 -0
- package/guidelines/cssRule/cssRule.instruction.md +18 -0
- package/guidelines/framework/framework.instruction.md +6 -0
- package/incrementalBuilder.proc.js +47 -8
- package/index.js +54 -6
- package/package.json +2 -2
- package/templates/app/page/styles.css.template +22 -0
|
@@ -15,6 +15,14 @@ Use this for shared UI rules across module, scalar, app UI, and docs components.
|
|
|
15
15
|
- Client components can use store hooks and event handlers where interaction is required.
|
|
16
16
|
- Use generated model types from app client or module constants; do not invent duplicate UI-only model shapes.
|
|
17
17
|
|
|
18
|
+
## Customizing Framework Components (Slot Overrides)
|
|
19
|
+
- When a default `akanjs/ui` component (Button, Select, Modal, Table, Input, Radio, DatePicker, Loading, …) is too restrictive for the design, re-skin it per route with a slot override instead of forking, wrapping every call site, or fighting it with `!important`.
|
|
20
|
+
- Write the drop-in replacement in `apps/<app>/ui/`, typed against the slot contract — `AkanModalComponent`, or `AkanUiOverrides["<Slot>"]` for any other slot — so it is compile-checked as a real substitute. Compose the framework's headless parts (e.g. `Dialog`) rather than re-implementing focus trapping, portals, or scroll-lock.
|
|
21
|
+
- Bind it in a logic-free `page/**/_overrides.tsx` manifest: imports plus a single `export default override({ Slot: BrandComponent })` (from `akanjs/ui`), no `"use client"`.
|
|
22
|
+
- Place the manifest at `page/` for an app-wide skin, or inside a route group/segment to scope it; nested manifests merge over ancestors slot-by-slot (closest ancestor wins, unlisted slots keep inheriting).
|
|
23
|
+
- Compound components expose one slot per leaf named `<Base><Sub>` (e.g. `InputPassword`, `RadioItem`, `LoadingSpin`); override only the leaves you need.
|
|
24
|
+
- See the `references/ui/customize` docs page for the full slot list and examples.
|
|
25
|
+
|
|
18
26
|
## Codegen Rules
|
|
19
27
|
- Do not put business workflow decisions in render code.
|
|
20
28
|
- Do not use undocumented UI components or props.
|
|
@@ -15,6 +15,24 @@ Use TailwindCSS and DaisyUI in a theme-safe, composable way for Akan UI and docs
|
|
|
15
15
|
- Use consistent density and spacing within one component family.
|
|
16
16
|
- Keep custom CSS files rare and scoped to cases utilities cannot express cleanly.
|
|
17
17
|
|
|
18
|
+
## Theme Customization (`apps/<app>/page/styles.css`)
|
|
19
|
+
- The app theme is defined in `apps/<app>/page/styles.css` with daisyUI v5 `@plugin "daisyui/theme"` blocks (typically `light` and a `default: true` `dark`).
|
|
20
|
+
- Brand-level customization means tuning the whole theme block, not only the color tokens. A theme that only changes `--color-*` still looks like the default framework skin.
|
|
21
|
+
- Color tokens: `--color-primary`, `--color-secondary`, `--color-accent`, `--color-neutral`, `--color-info`, `--color-success`, `--color-warning`, `--color-error`, `--color-base-100/200/300`, and their `*-content` pairs.
|
|
22
|
+
- Shape and feel tokens (set these too):
|
|
23
|
+
- `--radius-selector` — rounding for checkbox, toggle, badge.
|
|
24
|
+
- `--radius-field` — rounding for button, input, select, tab.
|
|
25
|
+
- `--radius-box` — rounding for card, modal, alert.
|
|
26
|
+
- `--size-selector`, `--size-field` — base scale (density) of selector and field controls.
|
|
27
|
+
- `--border` — component border width (e.g. `1px`, `2px`).
|
|
28
|
+
- `--depth` — `0` flat, `1` adds a subtle 3D lift to components.
|
|
29
|
+
- `--noise` — `0` off, `1` adds a grain texture to surfaces.
|
|
30
|
+
- Keep shape/feel tokens consistent across the light and dark theme blocks unless the design deliberately differs by mode.
|
|
31
|
+
|
|
32
|
+
## When Utilities Are Not Enough
|
|
33
|
+
- Prefer theme tokens and DaisyUI component classes over one-off utility stacks that re-implement a component's look.
|
|
34
|
+
- When a framework `akanjs/ui` component is structurally too restrictive, re-skin it with a `page/**/_overrides.tsx` slot override (see the componentRule guideline), not with `!important` utilities or a fork.
|
|
35
|
+
|
|
18
36
|
## Codegen Rules
|
|
19
37
|
- Do not hardcode hex colors or one-off brand colors unless the existing file already defines that design system.
|
|
20
38
|
- Do not use important flags to fight component composition.
|
|
@@ -22,6 +22,12 @@ Use this as the compact framework context for AI codegen. It should explain how
|
|
|
22
22
|
- Keep business decisions in constant, document, or service; keep API exposure in signal; keep client coordination in store; keep rendering in UI files.
|
|
23
23
|
- Use direct module imports where scanner rules expect them, and avoid inventing new top-level app folders.
|
|
24
24
|
|
|
25
|
+
## Theming And UI Customization
|
|
26
|
+
When a request implies a distinct look and feel, do not stop at colors — customize both the theme and, when needed, the components.
|
|
27
|
+
|
|
28
|
+
- **Theme (`apps/<app>/page/styles.css`).** The app theme is one or more daisyUI v5 `@plugin "daisyui/theme"` blocks. Match the brand by tuning the whole block, not only the `--color-*` tokens: set corner rounding (`--radius-selector`, `--radius-field`, `--radius-box`), control density (`--size-selector`, `--size-field`), outline weight (`--border`), and surface treatment (`--depth`, `--noise`). New workspaces ship these knobs at neutral defaults so they are visible to tune. Fetch `get_guideline` with `cssRule` for the full variable reference before a deep theme pass.
|
|
29
|
+
- **Components (`page/**/_overrides.tsx`).** When a default `akanjs/ui` component (Button, Modal, Table, Input, Select, …) is too restrictive for the design, re-skin it per route instead of forking, wrapping, or fighting it with utility classes. Write a drop-in replacement in `apps/<app>/ui/` typed against the slot contract (`AkanModalComponent`, or `AkanUiOverrides["<Slot>"]`), composing the framework's headless parts, then bind it in a `page/**/_overrides.tsx` manifest with a single `export default override({ Slot: BrandComponent })`. Overrides cascade down the route tree like layouts (closest ancestor wins). Fetch `get_guideline` with `componentRule` and read the `references/ui/customize` docs page for the slot list and patterns.
|
|
30
|
+
|
|
25
31
|
## Review Checklist
|
|
26
32
|
- The instruction points to current docs pages, not removed docs routes.
|
|
27
33
|
- Generated examples use current Akan builder APIs and scanner-friendly filenames.
|
|
@@ -4287,9 +4287,10 @@ class IncrementalBuilderHost {
|
|
|
4287
4287
|
// pkgs/@akanjs/devkit/akanApp/akanApp.host.ts
|
|
4288
4288
|
var backendMsgTypeSet = new Set(["build-route"]);
|
|
4289
4289
|
var BACKEND_RESTART_DEBOUNCE_MS = 120;
|
|
4290
|
-
var BACKEND_GRACEFUL_TIMEOUT_MS =
|
|
4290
|
+
var BACKEND_GRACEFUL_TIMEOUT_MS = 8000;
|
|
4291
4291
|
var BACKEND_RECOVERY_BASE_DELAY_MS = 1000;
|
|
4292
4292
|
var BACKEND_RECOVERY_MAX_DELAY_MS = 30000;
|
|
4293
|
+
var BACKEND_RECOVERY_MAX_ATTEMPTS = 5;
|
|
4293
4294
|
var BACKEND_STDERR_TAIL_LIMIT = 40;
|
|
4294
4295
|
var BUILDER_READY_TIMEOUT_MS = 150000;
|
|
4295
4296
|
var BUILDER_START_MAX_ATTEMPTS = 3;
|
|
@@ -4314,6 +4315,8 @@ var shouldRestartBackendByDevPlan = (message) => {
|
|
|
4314
4315
|
return message.devPlan.actions.includes("restart-backend");
|
|
4315
4316
|
};
|
|
4316
4317
|
var shouldRestartBuilderByDevPlan = (message) => message.devPlan?.actions.includes("restart-builder") ?? false;
|
|
4318
|
+
var shouldAbandonBackendRecovery = (attempts, maxAttempts = BACKEND_RECOVERY_MAX_ATTEMPTS) => attempts >= maxAttempts;
|
|
4319
|
+
var normalizeBackendReportedGeneration = (generation) => generation >= 0 ? generation : undefined;
|
|
4317
4320
|
var shouldRestartDevHostByDevPlan = (message) => message.devPlan?.actions.includes("restart-dev-host") ?? message.kinds.includes("config");
|
|
4318
4321
|
var RESTART_ROLE_ORDER = ["server", "shared", "barrel", "config"];
|
|
4319
4322
|
var generationValue = (generation) => generation ?? -1;
|
|
@@ -4562,6 +4565,16 @@ class AkanAppHost {
|
|
|
4562
4565
|
this.#replayBuilderState();
|
|
4563
4566
|
return;
|
|
4564
4567
|
}
|
|
4568
|
+
if (msg.type === "build-status") {
|
|
4569
|
+
const status = this.#recordBackendBuildStatus({
|
|
4570
|
+
generation: normalizeBackendReportedGeneration(msg.data.generation),
|
|
4571
|
+
ok: msg.data.ok,
|
|
4572
|
+
files: msg.data.files,
|
|
4573
|
+
message: msg.data.message
|
|
4574
|
+
});
|
|
4575
|
+
this.#sendOrQueueBuildStatus(status);
|
|
4576
|
+
return;
|
|
4577
|
+
}
|
|
4565
4578
|
if (backendMsgTypeSet.has(msg.type))
|
|
4566
4579
|
this.#sendToBuilder(msg);
|
|
4567
4580
|
},
|
|
@@ -4723,6 +4736,19 @@ class AkanAppHost {
|
|
|
4723
4736
|
#scheduleBackendRecovery(reason) {
|
|
4724
4737
|
if (this.#backendRecoveryTimer || this.#backend)
|
|
4725
4738
|
return;
|
|
4739
|
+
if (shouldAbandonBackendRecovery(this.#backendRecoveryAttempts)) {
|
|
4740
|
+
const message = `Backend exited ${this.#backendRecoveryAttempts} times in a row (${reason}); waiting for a server-side edit to retry.`;
|
|
4741
|
+
this.#setBackendLifecycleState("stopped", `gave up after ${this.#backendRecoveryAttempts} recovery attempts`);
|
|
4742
|
+
this.logger.error(`[backend-recovery] ${message}`);
|
|
4743
|
+
if (this.#backendStderrTail.length > 0) {
|
|
4744
|
+
this.logger.error(`[backend-recovery] recent backend stderr:
|
|
4745
|
+
${this.#backendStderrTail.join(`
|
|
4746
|
+
`)}`);
|
|
4747
|
+
}
|
|
4748
|
+
const abandonedStatus = this.#recordBackendBuildStatus({ ok: false, files: [], message });
|
|
4749
|
+
this.#sendOrQueueBuildStatus(abandonedStatus);
|
|
4750
|
+
return;
|
|
4751
|
+
}
|
|
4726
4752
|
this.#setBackendLifecycleState("recovering", reason);
|
|
4727
4753
|
const attempt = this.#backendRecoveryAttempts;
|
|
4728
4754
|
const delay = Math.min(BACKEND_RECOVERY_BASE_DELAY_MS * 2 ** attempt, BACKEND_RECOVERY_MAX_DELAY_MS);
|
|
@@ -4771,6 +4797,7 @@ ${this.#backendStderrTail.join(`
|
|
|
4771
4797
|
this.#sendToBackend(message);
|
|
4772
4798
|
}
|
|
4773
4799
|
async#handleInvalidate(message) {
|
|
4800
|
+
this.#logDevPlan(message);
|
|
4774
4801
|
if (shouldRestartBuilderByDevPlan(message)) {
|
|
4775
4802
|
try {
|
|
4776
4803
|
await this.#restartDevChildren(message);
|
|
@@ -4882,12 +4909,16 @@ ${this.#backendStderrTail.join(`
|
|
|
4882
4909
|
this.#sendToBackend({ type: "build-status", data: status });
|
|
4883
4910
|
}
|
|
4884
4911
|
}
|
|
4912
|
+
#logDevPlan(message) {
|
|
4913
|
+
if (!message.devPlan)
|
|
4914
|
+
return;
|
|
4915
|
+
const { generation, roles, actions, reasonByFile } = message.devPlan;
|
|
4916
|
+
this.logger.verbose(`[dev-plan] generation=${generation} roles=${roles.join(",") || "(none)"} actions=${actions.join(",") || "(none)"} reasons=${Object.keys(reasonByFile).length}`);
|
|
4917
|
+
}
|
|
4885
4918
|
async#shouldRestartBackend(message) {
|
|
4886
4919
|
if (message.kinds.length === 1 && message.kinds[0] === "css")
|
|
4887
4920
|
return false;
|
|
4888
4921
|
if (message.devPlan) {
|
|
4889
|
-
const { generation, roles, actions, reasonByFile } = message.devPlan;
|
|
4890
|
-
this.logger.verbose(`[dev-plan] generation=${generation} roles=${roles.join(",") || "(none)"} actions=${actions.join(",") || "(none)"} reasons=${Object.keys(reasonByFile).length}`);
|
|
4891
4922
|
const shouldRestart = shouldRestartBackendByDevPlan(message) ?? false;
|
|
4892
4923
|
if (shouldRestart && message.kinds.includes("code"))
|
|
4893
4924
|
await this.#backendGraph.refresh();
|
|
@@ -17269,14 +17300,18 @@ ${cssText}`).toString(36);
|
|
|
17269
17300
|
}
|
|
17270
17301
|
if (indexSync.changedFiles.length > 0)
|
|
17271
17302
|
this.#sendBuildStatus("barrel", { generation, ok: true, files });
|
|
17272
|
-
|
|
17303
|
+
const rebuildClient = devPlan.actions.includes("rebuild-client");
|
|
17304
|
+
if (kinds.includes("code") && !rebuildClient) {
|
|
17305
|
+
this.#logger.verbose(`client rebuild skipped; devPlan actions=${devPlan.actions.join(",") || "(none)"}`);
|
|
17306
|
+
}
|
|
17307
|
+
if (kinds.includes("code") && rebuildClient && await this.batchMayChangePageKeys(appDir, expandedBatch)) {
|
|
17273
17308
|
const started = Date.now();
|
|
17274
17309
|
await this.#app.getPageKeys({ refresh: true });
|
|
17275
17310
|
this.#logger.verbose(`pageKeys updated, app pageKeys are refreshed (${Date.now() - started}ms)`);
|
|
17276
|
-
} else if (kinds.includes("code") && this.batchTouchesPagesTree(appDir, expandedBatch)) {
|
|
17311
|
+
} else if (kinds.includes("code") && rebuildClient && this.batchTouchesPagesTree(appDir, expandedBatch)) {
|
|
17277
17312
|
this.#logger.verbose("pageKeys refresh skipped; changed page source cannot add/remove a route key");
|
|
17278
17313
|
}
|
|
17279
|
-
if (kinds.includes("code") && this.#shouldRebuildCsr()) {
|
|
17314
|
+
if (kinds.includes("code") && rebuildClient && this.#shouldRebuildCsr()) {
|
|
17280
17315
|
try {
|
|
17281
17316
|
const started = Date.now();
|
|
17282
17317
|
await new CsrArtifactBuilder(this.#app).build();
|
|
@@ -17287,11 +17322,11 @@ ${cssText}`).toString(36);
|
|
|
17287
17322
|
this.#logger.error(`csr-rebundle failed: ${message}`);
|
|
17288
17323
|
this.#sendBuildStatus("csr", { generation, ok: false, files, message });
|
|
17289
17324
|
}
|
|
17290
|
-
} else if (kinds.includes("code")) {
|
|
17325
|
+
} else if (kinds.includes("code") && rebuildClient) {
|
|
17291
17326
|
this.#logger.verbose(`csr-rebundle skipped; set AKAN_DEV_CSR_REBUILD=1 to enable per-save CSR rebuilds`);
|
|
17292
17327
|
}
|
|
17293
17328
|
process.send?.(event);
|
|
17294
|
-
if (kinds.includes("code")) {
|
|
17329
|
+
if (kinds.includes("code") && rebuildClient) {
|
|
17295
17330
|
try {
|
|
17296
17331
|
const started = Date.now();
|
|
17297
17332
|
const next = await new PagesBundleBuilder(this.#app).build();
|
|
@@ -17326,6 +17361,10 @@ ${cssText}`).toString(36);
|
|
|
17326
17361
|
return;
|
|
17327
17362
|
}
|
|
17328
17363
|
});
|
|
17364
|
+
process.on("disconnect", () => {
|
|
17365
|
+
this.#logger.warn("host IPC channel closed; exiting builder");
|
|
17366
|
+
process.exit(0);
|
|
17367
|
+
});
|
|
17329
17368
|
if (this.#watch)
|
|
17330
17369
|
await this.installWatcher();
|
|
17331
17370
|
process.send?.({ type: "builder-ready" });
|
package/index.js
CHANGED
|
@@ -4285,9 +4285,10 @@ class IncrementalBuilderHost {
|
|
|
4285
4285
|
// pkgs/@akanjs/devkit/akanApp/akanApp.host.ts
|
|
4286
4286
|
var backendMsgTypeSet = new Set(["build-route"]);
|
|
4287
4287
|
var BACKEND_RESTART_DEBOUNCE_MS = 120;
|
|
4288
|
-
var BACKEND_GRACEFUL_TIMEOUT_MS =
|
|
4288
|
+
var BACKEND_GRACEFUL_TIMEOUT_MS = 8000;
|
|
4289
4289
|
var BACKEND_RECOVERY_BASE_DELAY_MS = 1000;
|
|
4290
4290
|
var BACKEND_RECOVERY_MAX_DELAY_MS = 30000;
|
|
4291
|
+
var BACKEND_RECOVERY_MAX_ATTEMPTS = 5;
|
|
4291
4292
|
var BACKEND_STDERR_TAIL_LIMIT = 40;
|
|
4292
4293
|
var BUILDER_READY_TIMEOUT_MS = 150000;
|
|
4293
4294
|
var BUILDER_START_MAX_ATTEMPTS = 3;
|
|
@@ -4312,6 +4313,8 @@ var shouldRestartBackendByDevPlan = (message) => {
|
|
|
4312
4313
|
return message.devPlan.actions.includes("restart-backend");
|
|
4313
4314
|
};
|
|
4314
4315
|
var shouldRestartBuilderByDevPlan = (message) => message.devPlan?.actions.includes("restart-builder") ?? false;
|
|
4316
|
+
var shouldAbandonBackendRecovery = (attempts, maxAttempts = BACKEND_RECOVERY_MAX_ATTEMPTS) => attempts >= maxAttempts;
|
|
4317
|
+
var normalizeBackendReportedGeneration = (generation) => generation >= 0 ? generation : undefined;
|
|
4315
4318
|
var shouldRestartDevHostByDevPlan = (message) => message.devPlan?.actions.includes("restart-dev-host") ?? message.kinds.includes("config");
|
|
4316
4319
|
var RESTART_ROLE_ORDER = ["server", "shared", "barrel", "config"];
|
|
4317
4320
|
var generationValue = (generation) => generation ?? -1;
|
|
@@ -4560,6 +4563,16 @@ class AkanAppHost {
|
|
|
4560
4563
|
this.#replayBuilderState();
|
|
4561
4564
|
return;
|
|
4562
4565
|
}
|
|
4566
|
+
if (msg.type === "build-status") {
|
|
4567
|
+
const status = this.#recordBackendBuildStatus({
|
|
4568
|
+
generation: normalizeBackendReportedGeneration(msg.data.generation),
|
|
4569
|
+
ok: msg.data.ok,
|
|
4570
|
+
files: msg.data.files,
|
|
4571
|
+
message: msg.data.message
|
|
4572
|
+
});
|
|
4573
|
+
this.#sendOrQueueBuildStatus(status);
|
|
4574
|
+
return;
|
|
4575
|
+
}
|
|
4563
4576
|
if (backendMsgTypeSet.has(msg.type))
|
|
4564
4577
|
this.#sendToBuilder(msg);
|
|
4565
4578
|
},
|
|
@@ -4721,6 +4734,19 @@ class AkanAppHost {
|
|
|
4721
4734
|
#scheduleBackendRecovery(reason) {
|
|
4722
4735
|
if (this.#backendRecoveryTimer || this.#backend)
|
|
4723
4736
|
return;
|
|
4737
|
+
if (shouldAbandonBackendRecovery(this.#backendRecoveryAttempts)) {
|
|
4738
|
+
const message = `Backend exited ${this.#backendRecoveryAttempts} times in a row (${reason}); waiting for a server-side edit to retry.`;
|
|
4739
|
+
this.#setBackendLifecycleState("stopped", `gave up after ${this.#backendRecoveryAttempts} recovery attempts`);
|
|
4740
|
+
this.logger.error(`[backend-recovery] ${message}`);
|
|
4741
|
+
if (this.#backendStderrTail.length > 0) {
|
|
4742
|
+
this.logger.error(`[backend-recovery] recent backend stderr:
|
|
4743
|
+
${this.#backendStderrTail.join(`
|
|
4744
|
+
`)}`);
|
|
4745
|
+
}
|
|
4746
|
+
const abandonedStatus = this.#recordBackendBuildStatus({ ok: false, files: [], message });
|
|
4747
|
+
this.#sendOrQueueBuildStatus(abandonedStatus);
|
|
4748
|
+
return;
|
|
4749
|
+
}
|
|
4724
4750
|
this.#setBackendLifecycleState("recovering", reason);
|
|
4725
4751
|
const attempt = this.#backendRecoveryAttempts;
|
|
4726
4752
|
const delay = Math.min(BACKEND_RECOVERY_BASE_DELAY_MS * 2 ** attempt, BACKEND_RECOVERY_MAX_DELAY_MS);
|
|
@@ -4769,6 +4795,7 @@ ${this.#backendStderrTail.join(`
|
|
|
4769
4795
|
this.#sendToBackend(message);
|
|
4770
4796
|
}
|
|
4771
4797
|
async#handleInvalidate(message) {
|
|
4798
|
+
this.#logDevPlan(message);
|
|
4772
4799
|
if (shouldRestartBuilderByDevPlan(message)) {
|
|
4773
4800
|
try {
|
|
4774
4801
|
await this.#restartDevChildren(message);
|
|
@@ -4880,12 +4907,16 @@ ${this.#backendStderrTail.join(`
|
|
|
4880
4907
|
this.#sendToBackend({ type: "build-status", data: status });
|
|
4881
4908
|
}
|
|
4882
4909
|
}
|
|
4910
|
+
#logDevPlan(message) {
|
|
4911
|
+
if (!message.devPlan)
|
|
4912
|
+
return;
|
|
4913
|
+
const { generation, roles, actions, reasonByFile } = message.devPlan;
|
|
4914
|
+
this.logger.verbose(`[dev-plan] generation=${generation} roles=${roles.join(",") || "(none)"} actions=${actions.join(",") || "(none)"} reasons=${Object.keys(reasonByFile).length}`);
|
|
4915
|
+
}
|
|
4883
4916
|
async#shouldRestartBackend(message) {
|
|
4884
4917
|
if (message.kinds.length === 1 && message.kinds[0] === "css")
|
|
4885
4918
|
return false;
|
|
4886
4919
|
if (message.devPlan) {
|
|
4887
|
-
const { generation, roles, actions, reasonByFile } = message.devPlan;
|
|
4888
|
-
this.logger.verbose(`[dev-plan] generation=${generation} roles=${roles.join(",") || "(none)"} actions=${actions.join(",") || "(none)"} reasons=${Object.keys(reasonByFile).length}`);
|
|
4889
4920
|
const shouldRestart = shouldRestartBackendByDevPlan(message) ?? false;
|
|
4890
4921
|
if (shouldRestart && message.kinds.includes("code"))
|
|
4891
4922
|
await this.#backendGraph.refresh();
|
|
@@ -22756,14 +22787,18 @@ class WorkspaceScript extends script("workspace", [
|
|
|
22756
22787
|
ApplicationScript,
|
|
22757
22788
|
LibraryScript,
|
|
22758
22789
|
PackageScript,
|
|
22759
|
-
CloudScript
|
|
22790
|
+
CloudScript,
|
|
22791
|
+
ContextScript,
|
|
22792
|
+
AgentScript
|
|
22760
22793
|
]) {
|
|
22761
22794
|
async createWorkspace(repoName, appName, {
|
|
22762
22795
|
dirname: dirname2 = ".",
|
|
22763
22796
|
installLibs = false,
|
|
22764
22797
|
init = true,
|
|
22765
22798
|
registryUrl,
|
|
22766
|
-
owner
|
|
22799
|
+
owner,
|
|
22800
|
+
mcpInstall = true,
|
|
22801
|
+
agentInstall = true
|
|
22767
22802
|
}) {
|
|
22768
22803
|
const akanVersion = await this.packageScript.version(null, { log: false });
|
|
22769
22804
|
const workspace = await this.workspaceRunner.createWorkspace(repoName, appName, {
|
|
@@ -22784,6 +22819,10 @@ class WorkspaceScript extends script("workspace", [
|
|
|
22784
22819
|
dict: { appName },
|
|
22785
22820
|
options: { libs: installLibs ? ["util", "shared"] : [] }
|
|
22786
22821
|
});
|
|
22822
|
+
if (agentInstall)
|
|
22823
|
+
await this.agentScript.agent(workspace, "install", "all", { force: true });
|
|
22824
|
+
if (mcpInstall)
|
|
22825
|
+
await this.contextScript.mcpInstall(workspace, "all", { force: true });
|
|
22787
22826
|
const gitSpinner = workspace.spinning("Initializing git repository and commit...");
|
|
22788
22827
|
try {
|
|
22789
22828
|
await workspace.commit("Initial commit", { init: true });
|
|
@@ -22882,13 +22921,22 @@ class WorkspaceCommand extends command("workspace", [WorkspaceScript], ({ public
|
|
|
22882
22921
|
desc: "owner of the workspace",
|
|
22883
22922
|
default: process.env.GITHUB_OWNER,
|
|
22884
22923
|
nullable: true
|
|
22885
|
-
}).
|
|
22924
|
+
}).option("mcpInstall", Boolean, {
|
|
22925
|
+
desc: "Install the Akan MCP server config for Cursor, Claude Code, and Codex? (Recommended)",
|
|
22926
|
+
default: true
|
|
22927
|
+
}).option("agentInstall", Boolean, {
|
|
22928
|
+
flag: "A",
|
|
22929
|
+
desc: "Install Akan agent rules (AGENTS.md, CLAUDE.md, Cursor)? (Recommended)",
|
|
22930
|
+
default: true
|
|
22931
|
+
}).exec(async function(workspaceName, app, dir, libs, init, registry, owner, mcpInstall, agentInstall) {
|
|
22886
22932
|
const appName = app || "app";
|
|
22887
22933
|
await this.workspaceScript.createWorkspace(workspaceName.toLowerCase().replace(/ /g, "-"), appName.toLowerCase().replace(/ /g, "-"), {
|
|
22888
22934
|
dirname: dir,
|
|
22889
22935
|
installLibs: libs,
|
|
22890
22936
|
init,
|
|
22891
22937
|
owner,
|
|
22938
|
+
mcpInstall,
|
|
22939
|
+
agentInstall,
|
|
22892
22940
|
...registry ? { registryUrl: registry } : {}
|
|
22893
22941
|
});
|
|
22894
22942
|
}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akanjs/cli",
|
|
3
|
-
"version": "2.4.0-rc.
|
|
3
|
+
"version": "2.4.0-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.4.0-rc.
|
|
37
|
+
"akanjs": "2.4.0-rc.1",
|
|
38
38
|
"chalk": "^5.6.2",
|
|
39
39
|
"commander": "^14.0.3",
|
|
40
40
|
"daisyui": "5.5.23",
|
|
@@ -28,6 +28,18 @@
|
|
|
28
28
|
--color-error-content: #fafafa;
|
|
29
29
|
--color-open: #27ae60;
|
|
30
30
|
--color-open-content: #fafafa;
|
|
31
|
+
|
|
32
|
+
/* Shape & feel — customize these too, not just colors, to make the theme yours.
|
|
33
|
+
These are daisyUI v5 theme variables; the values below are the neutral defaults.
|
|
34
|
+
See `akan guideline show cssRule` and https://daisyui.com/docs/themes/ for the full set. */
|
|
35
|
+
--radius-selector: 1rem; /* checkbox, toggle, badge corner rounding */
|
|
36
|
+
--radius-field: 0.5rem; /* button, input, select, tab corner rounding */
|
|
37
|
+
--radius-box: 1rem; /* card, modal, alert corner rounding */
|
|
38
|
+
--size-selector: 0.25rem; /* base scale (density) for selector controls */
|
|
39
|
+
--size-field: 0.25rem; /* base scale (density) for field controls */
|
|
40
|
+
--border: 1px; /* component border width */
|
|
41
|
+
--depth: 0; /* 1 = subtle 3D depth on components */
|
|
42
|
+
--noise: 0; /* 1 = grain texture on surfaces */
|
|
31
43
|
}
|
|
32
44
|
|
|
33
45
|
@plugin "daisyui/theme" {
|
|
@@ -51,4 +63,14 @@
|
|
|
51
63
|
--color-error: #f02020;
|
|
52
64
|
--color-error-content: #ffffff;
|
|
53
65
|
--color-open: #10fc00;
|
|
66
|
+
|
|
67
|
+
/* Keep shape & feel consistent with the light theme (usually the same values across modes). */
|
|
68
|
+
--radius-selector: 1rem;
|
|
69
|
+
--radius-field: 0.5rem;
|
|
70
|
+
--radius-box: 1rem;
|
|
71
|
+
--size-selector: 0.25rem;
|
|
72
|
+
--size-field: 0.25rem;
|
|
73
|
+
--border: 1px;
|
|
74
|
+
--depth: 0;
|
|
75
|
+
--noise: 0;
|
|
54
76
|
}
|