@akanjs/cli 2.3.9-rc.1 → 2.3.9-rc.2
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 +79 -31
- package/index.js +238 -71
- package/package.json +2 -2
- package/templates/workspaceRoot/package.json.template +19 -2
|
@@ -2809,7 +2809,7 @@ class Executor {
|
|
|
2809
2809
|
this.logger.verbose(`Remove directory ${readPath}`);
|
|
2810
2810
|
return this;
|
|
2811
2811
|
}
|
|
2812
|
-
async writeFile(filePath, content, { overwrite = true } = {}) {
|
|
2812
|
+
async writeFile(filePath, content, { overwrite = true, silent = false } = {}) {
|
|
2813
2813
|
const writePath = this.getPath(filePath);
|
|
2814
2814
|
const dir = path7.dirname(writePath);
|
|
2815
2815
|
if (!await FileSys.dirExists(dir))
|
|
@@ -2827,7 +2827,8 @@ class Executor {
|
|
|
2827
2827
|
}
|
|
2828
2828
|
} else {
|
|
2829
2829
|
await FileSys.writeText(writePath, contentStr);
|
|
2830
|
-
|
|
2830
|
+
if (!silent)
|
|
2831
|
+
this.logger.rawLog(chalk4.green(`File Create: ${filePath}`));
|
|
2831
2832
|
}
|
|
2832
2833
|
return { filePath: writePath, content: contentStr };
|
|
2833
2834
|
}
|
|
@@ -3491,14 +3492,15 @@ class AppExecutor extends SysExecutor {
|
|
|
3491
3492
|
getCommandEnv(env = {}) {
|
|
3492
3493
|
const basePort = 8282;
|
|
3493
3494
|
const portOffset = WorkspaceExecutor.getBaseDevEnv().portOffset;
|
|
3494
|
-
const PORT =
|
|
3495
|
+
const PORT = (basePort + portOffset).toString();
|
|
3495
3496
|
const AKAN_PUBLIC_SERVER_PORT = portOffset ? (8282 + portOffset).toString() : undefined;
|
|
3496
3497
|
return {
|
|
3497
3498
|
...process.env,
|
|
3498
3499
|
AKAN_PUBLIC_APP_NAME: this.name,
|
|
3499
3500
|
AKAN_WORKSPACE_ROOT: this.workspace.workspaceRoot,
|
|
3500
3501
|
NODE_NO_WARNINGS: "1",
|
|
3501
|
-
|
|
3502
|
+
PORT,
|
|
3503
|
+
AKAN_PUBLIC_CLIENT_PORT: PORT,
|
|
3502
3504
|
...AKAN_PUBLIC_SERVER_PORT ? { AKAN_PUBLIC_SERVER_PORT } : {},
|
|
3503
3505
|
...env
|
|
3504
3506
|
};
|
|
@@ -11886,6 +11888,15 @@ var createWorkflowPlan = (spec, rawInputs) => {
|
|
|
11886
11888
|
}
|
|
11887
11889
|
inputs[name] = value;
|
|
11888
11890
|
}
|
|
11891
|
+
const fieldType = inputs.type;
|
|
11892
|
+
if (spec.name === "add-field" && typeof fieldType === "string" && (fieldType.toLowerCase() === "number" || fieldType.toLowerCase() === "numeric")) {
|
|
11893
|
+
diagnostics.push({
|
|
11894
|
+
severity: "error",
|
|
11895
|
+
code: "primitive-field-type-unsupported",
|
|
11896
|
+
input: "type",
|
|
11897
|
+
message: `Field type "${fieldType}" is ambiguous in Akan. Use Int for integer fields or Float for decimal fields.`
|
|
11898
|
+
});
|
|
11899
|
+
}
|
|
11889
11900
|
return {
|
|
11890
11901
|
schemaVersion: 1,
|
|
11891
11902
|
workflow: spec.name,
|
|
@@ -11971,29 +11982,37 @@ var createWorkflowApplyReport = ({
|
|
|
11971
11982
|
mode,
|
|
11972
11983
|
changedFiles = [],
|
|
11973
11984
|
generatedFiles: generatedFiles2 = [],
|
|
11985
|
+
appliedCommands = [],
|
|
11986
|
+
recommendedValidationCommands,
|
|
11974
11987
|
commands = [],
|
|
11975
11988
|
diagnostics = [],
|
|
11976
11989
|
nextActions = [],
|
|
11977
11990
|
plan
|
|
11978
|
-
}) =>
|
|
11979
|
-
|
|
11980
|
-
|
|
11981
|
-
|
|
11982
|
-
|
|
11983
|
-
|
|
11984
|
-
|
|
11985
|
-
|
|
11986
|
-
|
|
11987
|
-
|
|
11988
|
-
|
|
11989
|
-
|
|
11991
|
+
}) => {
|
|
11992
|
+
const validationCommands2 = recommendedValidationCommands ?? commands;
|
|
11993
|
+
return {
|
|
11994
|
+
schemaVersion: 1,
|
|
11995
|
+
workflow,
|
|
11996
|
+
mode,
|
|
11997
|
+
status: workflowStatus(diagnostics),
|
|
11998
|
+
changedFiles: uniqueBy(changedFiles, (file) => `${file.action}:${file.path}:${file.reason}`),
|
|
11999
|
+
generatedFiles: uniqueBy(generatedFiles2, (file) => `${file.action}:${file.path}:${file.reason}`),
|
|
12000
|
+
appliedCommands: uniqueBy(appliedCommands, (command3) => command3.command),
|
|
12001
|
+
recommendedValidationCommands: uniqueBy(validationCommands2, (command3) => command3.command),
|
|
12002
|
+
commands: uniqueBy(validationCommands2, (command3) => command3.command),
|
|
12003
|
+
diagnostics,
|
|
12004
|
+
nextActions: uniqueBy(nextActions, (action) => action.command),
|
|
12005
|
+
plan
|
|
12006
|
+
};
|
|
12007
|
+
};
|
|
11990
12008
|
var resolveWorkflowCommand = (command3, plan) => {
|
|
11991
12009
|
const target = typeof plan.inputs.app === "string" ? plan.inputs.app : "<app-or-lib>";
|
|
11992
12010
|
return command3.replaceAll("<app-or-lib-or-pkg>", target).replaceAll("<app-or-lib>", target).replaceAll("<app-name>", target);
|
|
11993
12011
|
};
|
|
11994
12012
|
var workflowCommandsForPlan = (plan) => plan.validation.map((validation) => ({
|
|
11995
12013
|
command: resolveWorkflowCommand(validation.command, plan),
|
|
11996
|
-
reason: validation.reason
|
|
12014
|
+
reason: validation.reason,
|
|
12015
|
+
kind: validation.kind
|
|
11997
12016
|
}));
|
|
11998
12017
|
var workflowRunsDir = ".akan/workflows/runs";
|
|
11999
12018
|
var createWorkflowRunId = (prefix = "run") => `${prefix}-${new Date().toISOString().replace(/[-:.TZ]/g, "").slice(0, 14)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -12006,7 +12025,7 @@ var workflowRunArtifactPath = (runId) => `${workflowRunsDir}/${runId}.json`;
|
|
|
12006
12025
|
var writeWorkflowRunArtifact = async (workspace, artifact) => {
|
|
12007
12026
|
const runId = getRunId(artifact);
|
|
12008
12027
|
const artifactPath = workflowRunArtifactPath(runId);
|
|
12009
|
-
await workspace.writeFile(artifactPath, jsonText(artifact));
|
|
12028
|
+
await workspace.writeFile(artifactPath, jsonText(artifact), { silent: true });
|
|
12010
12029
|
return { runId, path: artifactPath };
|
|
12011
12030
|
};
|
|
12012
12031
|
var readWorkflowRunArtifact = async (workspace, runId) => {
|
|
@@ -12033,7 +12052,10 @@ var createWorkflowValidationRunReport = async ({
|
|
|
12033
12052
|
{
|
|
12034
12053
|
severity: "error",
|
|
12035
12054
|
code: "workflow-validation-command-failed",
|
|
12036
|
-
message: `Validation command failed: ${result.command}
|
|
12055
|
+
message: `Validation command failed: ${result.command}`,
|
|
12056
|
+
command: result.command,
|
|
12057
|
+
kind: result.kind,
|
|
12058
|
+
failureScope: result.failureScope
|
|
12037
12059
|
}
|
|
12038
12060
|
] : []);
|
|
12039
12061
|
return {
|
|
@@ -12216,8 +12238,11 @@ var renderWorkflowApplyReport = (report) => [
|
|
|
12216
12238
|
"## Generated Files",
|
|
12217
12239
|
...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
|
|
12218
12240
|
"",
|
|
12219
|
-
"## Commands",
|
|
12220
|
-
...report.
|
|
12241
|
+
"## Applied Commands",
|
|
12242
|
+
...report.appliedCommands.length ? report.appliedCommands.map((command3) => `- \`${command3.command}\`: ${command3.reason}`) : ["- none"],
|
|
12243
|
+
"",
|
|
12244
|
+
"## Recommended Validation Commands",
|
|
12245
|
+
...report.recommendedValidationCommands.length ? report.recommendedValidationCommands.map((command3) => `- \`${command3.command}\`: ${command3.reason}`) : ["- none"],
|
|
12221
12246
|
"",
|
|
12222
12247
|
"## Diagnostics",
|
|
12223
12248
|
...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
@@ -12235,7 +12260,10 @@ var renderWorkflowValidationRunReport = (report) => [
|
|
|
12235
12260
|
`- Status: ${report.status}`,
|
|
12236
12261
|
"",
|
|
12237
12262
|
"## Commands",
|
|
12238
|
-
...report.commands.length ? report.commands.map((command3) =>
|
|
12263
|
+
...report.commands.length ? report.commands.map((command3) => {
|
|
12264
|
+
const scope = command3.failureScope ? ` (${command3.failureScope})` : "";
|
|
12265
|
+
return `- [${command3.status}] \`${command3.command}\`${scope}: ${command3.reason}`;
|
|
12266
|
+
}) : ["- none"],
|
|
12239
12267
|
"",
|
|
12240
12268
|
"## Diagnostics",
|
|
12241
12269
|
...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
@@ -12305,7 +12333,7 @@ class WorkflowExecutor {
|
|
|
12305
12333
|
async apply(plan) {
|
|
12306
12334
|
const changedFiles = [];
|
|
12307
12335
|
const generatedFiles2 = [];
|
|
12308
|
-
const
|
|
12336
|
+
const recommendedValidationCommands = [];
|
|
12309
12337
|
const diagnostics = [...plan.diagnostics];
|
|
12310
12338
|
const nextActions = [];
|
|
12311
12339
|
if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
@@ -12314,13 +12342,13 @@ class WorkflowExecutor {
|
|
|
12314
12342
|
mode: "apply",
|
|
12315
12343
|
changedFiles,
|
|
12316
12344
|
generatedFiles: generatedFiles2,
|
|
12317
|
-
|
|
12345
|
+
recommendedValidationCommands,
|
|
12318
12346
|
diagnostics,
|
|
12319
12347
|
nextActions,
|
|
12320
12348
|
plan
|
|
12321
12349
|
});
|
|
12322
12350
|
}
|
|
12323
|
-
|
|
12351
|
+
recommendedValidationCommands.push(...workflowCommandsForPlan(plan));
|
|
12324
12352
|
nextActions.push(...workflowCommandsForPlan(plan));
|
|
12325
12353
|
for (const step of plan.steps) {
|
|
12326
12354
|
const runner2 = this.registry[workflowStepKey(plan.workflow, step.id)] ?? this.registry[step.tool] ?? this.registry[step.id];
|
|
@@ -12341,7 +12369,7 @@ class WorkflowExecutor {
|
|
|
12341
12369
|
continue;
|
|
12342
12370
|
changedFiles.push(...result.changedFiles ?? []);
|
|
12343
12371
|
generatedFiles2.push(...result.generatedFiles ?? []);
|
|
12344
|
-
|
|
12372
|
+
recommendedValidationCommands.push(...result.commands ?? []);
|
|
12345
12373
|
diagnostics.push(...result.diagnostics ?? []);
|
|
12346
12374
|
nextActions.push(...result.nextActions ?? []);
|
|
12347
12375
|
if (diagnostics.some((diagnostic) => diagnostic.severity === "error"))
|
|
@@ -12352,7 +12380,7 @@ class WorkflowExecutor {
|
|
|
12352
12380
|
mode: "apply",
|
|
12353
12381
|
changedFiles,
|
|
12354
12382
|
generatedFiles: generatedFiles2,
|
|
12355
|
-
|
|
12383
|
+
recommendedValidationCommands,
|
|
12356
12384
|
diagnostics,
|
|
12357
12385
|
nextActions,
|
|
12358
12386
|
plan
|
|
@@ -12438,14 +12466,34 @@ var scalarChangedFiles = (sys3, scalarName, files) => Object.values(files).map((
|
|
|
12438
12466
|
var titleize = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
12439
12467
|
var lowerlize = (value) => `${value.slice(0, 1).toLowerCase()}${value.slice(1)}`;
|
|
12440
12468
|
var compactDiagnostics = (diagnostics) => diagnostics.filter((diagnostic) => !!diagnostic);
|
|
12441
|
-
var
|
|
12469
|
+
var normalizeFieldType = (typeName) => {
|
|
12442
12470
|
const normalizedTypes = {
|
|
12443
12471
|
string: "String",
|
|
12444
|
-
number: "Number",
|
|
12445
12472
|
boolean: "Boolean",
|
|
12446
|
-
date: "Date"
|
|
12473
|
+
date: "Date",
|
|
12474
|
+
int: "Int",
|
|
12475
|
+
integer: "Int",
|
|
12476
|
+
float: "Float",
|
|
12477
|
+
double: "Float",
|
|
12478
|
+
decimal: "Float"
|
|
12447
12479
|
};
|
|
12448
|
-
|
|
12480
|
+
return normalizedTypes[typeName.toLowerCase()] ?? typeName;
|
|
12481
|
+
};
|
|
12482
|
+
var ensureBaseTypeImport = (content, typeName) => {
|
|
12483
|
+
if (typeName !== "Int" && typeName !== "Float")
|
|
12484
|
+
return content;
|
|
12485
|
+
if (new RegExp(`import \\{[^}]*\\b${typeName}\\b[^}]*\\} from "akanjs/base";`).test(content))
|
|
12486
|
+
return content;
|
|
12487
|
+
const baseImport = /import \{ ([^}]+) \} from "akanjs\/base";/.exec(content);
|
|
12488
|
+
if (baseImport) {
|
|
12489
|
+
const names = baseImport[1].split(",").map((name) => name.trim()).filter(Boolean);
|
|
12490
|
+
return content.replace(baseImport[0], `import { ${[...names, typeName].sort().join(", ")} } from "akanjs/base";`);
|
|
12491
|
+
}
|
|
12492
|
+
return `import { ${typeName} } from "akanjs/base";
|
|
12493
|
+
${content}`;
|
|
12494
|
+
};
|
|
12495
|
+
var fieldExpression = (typeName, defaultValue) => {
|
|
12496
|
+
const typeExpression = normalizeFieldType(typeName);
|
|
12449
12497
|
const defaultOption = defaultValue ? `, { default: ${JSON.stringify(defaultValue)} }` : "";
|
|
12450
12498
|
return `field(${typeExpression}${defaultOption})`;
|
|
12451
12499
|
};
|
package/index.js
CHANGED
|
@@ -2807,7 +2807,7 @@ class Executor {
|
|
|
2807
2807
|
this.logger.verbose(`Remove directory ${readPath}`);
|
|
2808
2808
|
return this;
|
|
2809
2809
|
}
|
|
2810
|
-
async writeFile(filePath, content, { overwrite = true } = {}) {
|
|
2810
|
+
async writeFile(filePath, content, { overwrite = true, silent = false } = {}) {
|
|
2811
2811
|
const writePath = this.getPath(filePath);
|
|
2812
2812
|
const dir = path7.dirname(writePath);
|
|
2813
2813
|
if (!await FileSys.dirExists(dir))
|
|
@@ -2825,7 +2825,8 @@ class Executor {
|
|
|
2825
2825
|
}
|
|
2826
2826
|
} else {
|
|
2827
2827
|
await FileSys.writeText(writePath, contentStr);
|
|
2828
|
-
|
|
2828
|
+
if (!silent)
|
|
2829
|
+
this.logger.rawLog(chalk4.green(`File Create: ${filePath}`));
|
|
2829
2830
|
}
|
|
2830
2831
|
return { filePath: writePath, content: contentStr };
|
|
2831
2832
|
}
|
|
@@ -3489,14 +3490,15 @@ class AppExecutor extends SysExecutor {
|
|
|
3489
3490
|
getCommandEnv(env = {}) {
|
|
3490
3491
|
const basePort = 8282;
|
|
3491
3492
|
const portOffset = WorkspaceExecutor.getBaseDevEnv().portOffset;
|
|
3492
|
-
const PORT =
|
|
3493
|
+
const PORT = (basePort + portOffset).toString();
|
|
3493
3494
|
const AKAN_PUBLIC_SERVER_PORT = portOffset ? (8282 + portOffset).toString() : undefined;
|
|
3494
3495
|
return {
|
|
3495
3496
|
...process.env,
|
|
3496
3497
|
AKAN_PUBLIC_APP_NAME: this.name,
|
|
3497
3498
|
AKAN_WORKSPACE_ROOT: this.workspace.workspaceRoot,
|
|
3498
3499
|
NODE_NO_WARNINGS: "1",
|
|
3499
|
-
|
|
3500
|
+
PORT,
|
|
3501
|
+
AKAN_PUBLIC_CLIENT_PORT: PORT,
|
|
3500
3502
|
...AKAN_PUBLIC_SERVER_PORT ? { AKAN_PUBLIC_SERVER_PORT } : {},
|
|
3501
3503
|
...env
|
|
3502
3504
|
};
|
|
@@ -11884,6 +11886,15 @@ var createWorkflowPlan = (spec, rawInputs) => {
|
|
|
11884
11886
|
}
|
|
11885
11887
|
inputs[name] = value;
|
|
11886
11888
|
}
|
|
11889
|
+
const fieldType = inputs.type;
|
|
11890
|
+
if (spec.name === "add-field" && typeof fieldType === "string" && (fieldType.toLowerCase() === "number" || fieldType.toLowerCase() === "numeric")) {
|
|
11891
|
+
diagnostics.push({
|
|
11892
|
+
severity: "error",
|
|
11893
|
+
code: "primitive-field-type-unsupported",
|
|
11894
|
+
input: "type",
|
|
11895
|
+
message: `Field type "${fieldType}" is ambiguous in Akan. Use Int for integer fields or Float for decimal fields.`
|
|
11896
|
+
});
|
|
11897
|
+
}
|
|
11887
11898
|
return {
|
|
11888
11899
|
schemaVersion: 1,
|
|
11889
11900
|
workflow: spec.name,
|
|
@@ -11969,29 +11980,37 @@ var createWorkflowApplyReport = ({
|
|
|
11969
11980
|
mode,
|
|
11970
11981
|
changedFiles = [],
|
|
11971
11982
|
generatedFiles: generatedFiles2 = [],
|
|
11983
|
+
appliedCommands = [],
|
|
11984
|
+
recommendedValidationCommands,
|
|
11972
11985
|
commands = [],
|
|
11973
11986
|
diagnostics = [],
|
|
11974
11987
|
nextActions = [],
|
|
11975
11988
|
plan
|
|
11976
|
-
}) =>
|
|
11977
|
-
|
|
11978
|
-
|
|
11979
|
-
|
|
11980
|
-
|
|
11981
|
-
|
|
11982
|
-
|
|
11983
|
-
|
|
11984
|
-
|
|
11985
|
-
|
|
11986
|
-
|
|
11987
|
-
|
|
11989
|
+
}) => {
|
|
11990
|
+
const validationCommands2 = recommendedValidationCommands ?? commands;
|
|
11991
|
+
return {
|
|
11992
|
+
schemaVersion: 1,
|
|
11993
|
+
workflow,
|
|
11994
|
+
mode,
|
|
11995
|
+
status: workflowStatus(diagnostics),
|
|
11996
|
+
changedFiles: uniqueBy(changedFiles, (file) => `${file.action}:${file.path}:${file.reason}`),
|
|
11997
|
+
generatedFiles: uniqueBy(generatedFiles2, (file) => `${file.action}:${file.path}:${file.reason}`),
|
|
11998
|
+
appliedCommands: uniqueBy(appliedCommands, (command3) => command3.command),
|
|
11999
|
+
recommendedValidationCommands: uniqueBy(validationCommands2, (command3) => command3.command),
|
|
12000
|
+
commands: uniqueBy(validationCommands2, (command3) => command3.command),
|
|
12001
|
+
diagnostics,
|
|
12002
|
+
nextActions: uniqueBy(nextActions, (action) => action.command),
|
|
12003
|
+
plan
|
|
12004
|
+
};
|
|
12005
|
+
};
|
|
11988
12006
|
var resolveWorkflowCommand = (command3, plan) => {
|
|
11989
12007
|
const target = typeof plan.inputs.app === "string" ? plan.inputs.app : "<app-or-lib>";
|
|
11990
12008
|
return command3.replaceAll("<app-or-lib-or-pkg>", target).replaceAll("<app-or-lib>", target).replaceAll("<app-name>", target);
|
|
11991
12009
|
};
|
|
11992
12010
|
var workflowCommandsForPlan = (plan) => plan.validation.map((validation) => ({
|
|
11993
12011
|
command: resolveWorkflowCommand(validation.command, plan),
|
|
11994
|
-
reason: validation.reason
|
|
12012
|
+
reason: validation.reason,
|
|
12013
|
+
kind: validation.kind
|
|
11995
12014
|
}));
|
|
11996
12015
|
var workflowRunsDir = ".akan/workflows/runs";
|
|
11997
12016
|
var createWorkflowRunId = (prefix = "run") => `${prefix}-${new Date().toISOString().replace(/[-:.TZ]/g, "").slice(0, 14)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
@@ -12004,7 +12023,7 @@ var workflowRunArtifactPath = (runId) => `${workflowRunsDir}/${runId}.json`;
|
|
|
12004
12023
|
var writeWorkflowRunArtifact = async (workspace, artifact) => {
|
|
12005
12024
|
const runId = getRunId(artifact);
|
|
12006
12025
|
const artifactPath = workflowRunArtifactPath(runId);
|
|
12007
|
-
await workspace.writeFile(artifactPath, jsonText(artifact));
|
|
12026
|
+
await workspace.writeFile(artifactPath, jsonText(artifact), { silent: true });
|
|
12008
12027
|
return { runId, path: artifactPath };
|
|
12009
12028
|
};
|
|
12010
12029
|
var readWorkflowRunArtifact = async (workspace, runId) => {
|
|
@@ -12031,7 +12050,10 @@ var createWorkflowValidationRunReport = async ({
|
|
|
12031
12050
|
{
|
|
12032
12051
|
severity: "error",
|
|
12033
12052
|
code: "workflow-validation-command-failed",
|
|
12034
|
-
message: `Validation command failed: ${result.command}
|
|
12053
|
+
message: `Validation command failed: ${result.command}`,
|
|
12054
|
+
command: result.command,
|
|
12055
|
+
kind: result.kind,
|
|
12056
|
+
failureScope: result.failureScope
|
|
12035
12057
|
}
|
|
12036
12058
|
] : []);
|
|
12037
12059
|
return {
|
|
@@ -12214,8 +12236,11 @@ var renderWorkflowApplyReport = (report) => [
|
|
|
12214
12236
|
"## Generated Files",
|
|
12215
12237
|
...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
|
|
12216
12238
|
"",
|
|
12217
|
-
"## Commands",
|
|
12218
|
-
...report.
|
|
12239
|
+
"## Applied Commands",
|
|
12240
|
+
...report.appliedCommands.length ? report.appliedCommands.map((command3) => `- \`${command3.command}\`: ${command3.reason}`) : ["- none"],
|
|
12241
|
+
"",
|
|
12242
|
+
"## Recommended Validation Commands",
|
|
12243
|
+
...report.recommendedValidationCommands.length ? report.recommendedValidationCommands.map((command3) => `- \`${command3.command}\`: ${command3.reason}`) : ["- none"],
|
|
12219
12244
|
"",
|
|
12220
12245
|
"## Diagnostics",
|
|
12221
12246
|
...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
@@ -12233,7 +12258,10 @@ var renderWorkflowValidationRunReport = (report) => [
|
|
|
12233
12258
|
`- Status: ${report.status}`,
|
|
12234
12259
|
"",
|
|
12235
12260
|
"## Commands",
|
|
12236
|
-
...report.commands.length ? report.commands.map((command3) =>
|
|
12261
|
+
...report.commands.length ? report.commands.map((command3) => {
|
|
12262
|
+
const scope = command3.failureScope ? ` (${command3.failureScope})` : "";
|
|
12263
|
+
return `- [${command3.status}] \`${command3.command}\`${scope}: ${command3.reason}`;
|
|
12264
|
+
}) : ["- none"],
|
|
12237
12265
|
"",
|
|
12238
12266
|
"## Diagnostics",
|
|
12239
12267
|
...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
@@ -12303,7 +12331,7 @@ class WorkflowExecutor {
|
|
|
12303
12331
|
async apply(plan) {
|
|
12304
12332
|
const changedFiles = [];
|
|
12305
12333
|
const generatedFiles2 = [];
|
|
12306
|
-
const
|
|
12334
|
+
const recommendedValidationCommands = [];
|
|
12307
12335
|
const diagnostics = [...plan.diagnostics];
|
|
12308
12336
|
const nextActions = [];
|
|
12309
12337
|
if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
@@ -12312,13 +12340,13 @@ class WorkflowExecutor {
|
|
|
12312
12340
|
mode: "apply",
|
|
12313
12341
|
changedFiles,
|
|
12314
12342
|
generatedFiles: generatedFiles2,
|
|
12315
|
-
|
|
12343
|
+
recommendedValidationCommands,
|
|
12316
12344
|
diagnostics,
|
|
12317
12345
|
nextActions,
|
|
12318
12346
|
plan
|
|
12319
12347
|
});
|
|
12320
12348
|
}
|
|
12321
|
-
|
|
12349
|
+
recommendedValidationCommands.push(...workflowCommandsForPlan(plan));
|
|
12322
12350
|
nextActions.push(...workflowCommandsForPlan(plan));
|
|
12323
12351
|
for (const step of plan.steps) {
|
|
12324
12352
|
const runner2 = this.registry[workflowStepKey(plan.workflow, step.id)] ?? this.registry[step.tool] ?? this.registry[step.id];
|
|
@@ -12339,7 +12367,7 @@ class WorkflowExecutor {
|
|
|
12339
12367
|
continue;
|
|
12340
12368
|
changedFiles.push(...result.changedFiles ?? []);
|
|
12341
12369
|
generatedFiles2.push(...result.generatedFiles ?? []);
|
|
12342
|
-
|
|
12370
|
+
recommendedValidationCommands.push(...result.commands ?? []);
|
|
12343
12371
|
diagnostics.push(...result.diagnostics ?? []);
|
|
12344
12372
|
nextActions.push(...result.nextActions ?? []);
|
|
12345
12373
|
if (diagnostics.some((diagnostic) => diagnostic.severity === "error"))
|
|
@@ -12350,7 +12378,7 @@ class WorkflowExecutor {
|
|
|
12350
12378
|
mode: "apply",
|
|
12351
12379
|
changedFiles,
|
|
12352
12380
|
generatedFiles: generatedFiles2,
|
|
12353
|
-
|
|
12381
|
+
recommendedValidationCommands,
|
|
12354
12382
|
diagnostics,
|
|
12355
12383
|
nextActions,
|
|
12356
12384
|
plan
|
|
@@ -12436,14 +12464,34 @@ var scalarChangedFiles = (sys3, scalarName, files) => Object.values(files).map((
|
|
|
12436
12464
|
var titleize = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
12437
12465
|
var lowerlize = (value) => `${value.slice(0, 1).toLowerCase()}${value.slice(1)}`;
|
|
12438
12466
|
var compactDiagnostics = (diagnostics) => diagnostics.filter((diagnostic) => !!diagnostic);
|
|
12439
|
-
var
|
|
12467
|
+
var normalizeFieldType = (typeName) => {
|
|
12440
12468
|
const normalizedTypes = {
|
|
12441
12469
|
string: "String",
|
|
12442
|
-
number: "Number",
|
|
12443
12470
|
boolean: "Boolean",
|
|
12444
|
-
date: "Date"
|
|
12471
|
+
date: "Date",
|
|
12472
|
+
int: "Int",
|
|
12473
|
+
integer: "Int",
|
|
12474
|
+
float: "Float",
|
|
12475
|
+
double: "Float",
|
|
12476
|
+
decimal: "Float"
|
|
12445
12477
|
};
|
|
12446
|
-
|
|
12478
|
+
return normalizedTypes[typeName.toLowerCase()] ?? typeName;
|
|
12479
|
+
};
|
|
12480
|
+
var ensureBaseTypeImport = (content, typeName) => {
|
|
12481
|
+
if (typeName !== "Int" && typeName !== "Float")
|
|
12482
|
+
return content;
|
|
12483
|
+
if (new RegExp(`import \\{[^}]*\\b${typeName}\\b[^}]*\\} from "akanjs/base";`).test(content))
|
|
12484
|
+
return content;
|
|
12485
|
+
const baseImport = /import \{ ([^}]+) \} from "akanjs\/base";/.exec(content);
|
|
12486
|
+
if (baseImport) {
|
|
12487
|
+
const names = baseImport[1].split(",").map((name) => name.trim()).filter(Boolean);
|
|
12488
|
+
return content.replace(baseImport[0], `import { ${[...names, typeName].sort().join(", ")} } from "akanjs/base";`);
|
|
12489
|
+
}
|
|
12490
|
+
return `import { ${typeName} } from "akanjs/base";
|
|
12491
|
+
${content}`;
|
|
12492
|
+
};
|
|
12493
|
+
var fieldExpression = (typeName, defaultValue) => {
|
|
12494
|
+
const typeExpression = normalizeFieldType(typeName);
|
|
12447
12495
|
const defaultOption = defaultValue ? `, { default: ${JSON.stringify(defaultValue)} }` : "";
|
|
12448
12496
|
return `field(${typeExpression}${defaultOption})`;
|
|
12449
12497
|
};
|
|
@@ -14194,6 +14242,9 @@ class CloudCommand extends command("cloud", [CloudScript], ({ public: target })
|
|
|
14194
14242
|
// pkgs/@akanjs/cli/context/context.script.ts
|
|
14195
14243
|
import { Logger as Logger17 } from "akanjs/common";
|
|
14196
14244
|
|
|
14245
|
+
// pkgs/@akanjs/cli/context/context.runner.ts
|
|
14246
|
+
import path43 from "path";
|
|
14247
|
+
|
|
14197
14248
|
// pkgs/@akanjs/cli/module/module.script.ts
|
|
14198
14249
|
import { input as input6 } from "@inquirer/prompts";
|
|
14199
14250
|
import { capitalize as capitalize8, randomPicks } from "akanjs/common";
|
|
@@ -14833,6 +14884,8 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
|
|
|
14833
14884
|
}
|
|
14834
14885
|
async addFieldToSources(workspace, input7, { enumValues }) {
|
|
14835
14886
|
const sys3 = await this.resolveSys(workspace, input7.app);
|
|
14887
|
+
const ambiguousNumberTypes = new Set(["number", "numeric"]);
|
|
14888
|
+
const normalizedType = input7.type ? normalizeFieldType(input7.type) : null;
|
|
14836
14889
|
const diagnostics = compactDiagnostics([
|
|
14837
14890
|
!sys3 && { severity: "error", code: "primitive-target-missing", message: "Target app or library was not found." },
|
|
14838
14891
|
!input7.module && {
|
|
@@ -14853,7 +14906,13 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
|
|
|
14853
14906
|
message: "Type is required.",
|
|
14854
14907
|
input: "type"
|
|
14855
14908
|
},
|
|
14856
|
-
enumValues && enumValues.length === 0 ? { severity: "error", code: "primitive-input-missing", message: "Enum values are required.", input: "values" } : null
|
|
14909
|
+
enumValues && enumValues.length === 0 ? { severity: "error", code: "primitive-input-missing", message: "Enum values are required.", input: "values" } : null,
|
|
14910
|
+
input7.type && ambiguousNumberTypes.has(input7.type.toLowerCase()) ? {
|
|
14911
|
+
severity: "error",
|
|
14912
|
+
code: "primitive-field-type-unsupported",
|
|
14913
|
+
message: `Field type "${input7.type}" is ambiguous in Akan. Use Int for integer fields or Float for decimal fields.`,
|
|
14914
|
+
input: "type"
|
|
14915
|
+
} : null
|
|
14857
14916
|
]);
|
|
14858
14917
|
if (!sys3 || !input7.module || !input7.field || !input7.type || diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
14859
14918
|
return createPrimitiveWriteReport({
|
|
@@ -14923,6 +14982,10 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
|
|
|
14923
14982
|
dictionaryContent = enumDictionary;
|
|
14924
14983
|
}
|
|
14925
14984
|
}
|
|
14985
|
+
if (!enumValues && normalizedType) {
|
|
14986
|
+
input7.type = normalizedType;
|
|
14987
|
+
constantContent = ensureBaseTypeImport(constantContent, input7.type);
|
|
14988
|
+
}
|
|
14926
14989
|
const nextConstantContent = insertIntoObject(constantContent, inputClassName, `${input7.field}: ${fieldExpression(input7.type, input7.defaultValue)},`);
|
|
14927
14990
|
const nextDictionaryContent = insertDictionaryModelField(dictionaryContent, moduleClassName, input7.field);
|
|
14928
14991
|
if (!nextConstantContent) {
|
|
@@ -15310,8 +15373,12 @@ var moduleInput = {
|
|
|
15310
15373
|
}
|
|
15311
15374
|
};
|
|
15312
15375
|
var baseValidation = [
|
|
15313
|
-
{ command: "akan sync <app-or-lib>", reason: "Refresh generated Akan files from source conventions." },
|
|
15314
|
-
{
|
|
15376
|
+
{ command: "akan sync <app-or-lib>", reason: "Refresh generated Akan files from source conventions.", kind: "sync" },
|
|
15377
|
+
{
|
|
15378
|
+
command: "akan lint <app-or-lib-or-pkg>",
|
|
15379
|
+
reason: "Validate formatting, imports, and static lint rules.",
|
|
15380
|
+
kind: "lint"
|
|
15381
|
+
}
|
|
15315
15382
|
];
|
|
15316
15383
|
|
|
15317
15384
|
// pkgs/@akanjs/cli/workflows/addEnumField.ts
|
|
@@ -15381,7 +15448,7 @@ var addEnumFieldWorkflowSpec = {
|
|
|
15381
15448
|
],
|
|
15382
15449
|
validation: [
|
|
15383
15450
|
...baseValidation,
|
|
15384
|
-
{ command: "akan typecheck <app-name>", reason: "Validate enum usage across module surfaces." }
|
|
15451
|
+
{ command: "akan typecheck <app-name>", reason: "Validate enum usage across module surfaces.", kind: "typecheck" }
|
|
15385
15452
|
],
|
|
15386
15453
|
completionCriteria: ["Enum values are represented.", "Labels and options exist.", "Sync, lint, and typecheck pass."]
|
|
15387
15454
|
};
|
|
@@ -15396,7 +15463,11 @@ var addFieldWorkflowSpec = {
|
|
|
15396
15463
|
...sysInputs,
|
|
15397
15464
|
...moduleInput,
|
|
15398
15465
|
field: { type: "string", required: true, description: "Name of the field to add." },
|
|
15399
|
-
type: {
|
|
15466
|
+
type: {
|
|
15467
|
+
type: "string",
|
|
15468
|
+
required: true,
|
|
15469
|
+
description: "Field type or scalar name. Use Int for integer fields or Float for decimal fields; do not use Number."
|
|
15470
|
+
},
|
|
15400
15471
|
values: { type: "string-list", description: "Comma-separated enum values when type is enum." },
|
|
15401
15472
|
default: { type: "string", description: "Optional default value for the field." }
|
|
15402
15473
|
},
|
|
@@ -15461,7 +15532,8 @@ var addFieldWorkflowSpec = {
|
|
|
15461
15532
|
...baseValidation,
|
|
15462
15533
|
{
|
|
15463
15534
|
command: "akan typecheck <app-name>",
|
|
15464
|
-
reason: "Validate cross-surface TypeScript contracts after field changes."
|
|
15535
|
+
reason: "Validate cross-surface TypeScript contracts after field changes.",
|
|
15536
|
+
kind: "typecheck"
|
|
15465
15537
|
}
|
|
15466
15538
|
],
|
|
15467
15539
|
completionCriteria: [
|
|
@@ -15538,7 +15610,11 @@ var addMutationWorkflowSpec = {
|
|
|
15538
15610
|
],
|
|
15539
15611
|
validation: [
|
|
15540
15612
|
...baseValidation,
|
|
15541
|
-
{
|
|
15613
|
+
{
|
|
15614
|
+
command: "akan typecheck <app-name>",
|
|
15615
|
+
reason: "Validate mutation contracts across service and signal.",
|
|
15616
|
+
kind: "typecheck"
|
|
15617
|
+
}
|
|
15542
15618
|
],
|
|
15543
15619
|
completionCriteria: ["Service and signal contracts align.", "Generated files are refreshed.", "Validation passes."]
|
|
15544
15620
|
};
|
|
@@ -15609,7 +15685,8 @@ var addSliceWorkflowSpec = {
|
|
|
15609
15685
|
...baseValidation,
|
|
15610
15686
|
{
|
|
15611
15687
|
command: "akan build <app-name>",
|
|
15612
|
-
reason: "Validate page and list surfaces when a slice changes app behavior."
|
|
15688
|
+
reason: "Validate page and list surfaces when a slice changes app behavior.",
|
|
15689
|
+
kind: "custom"
|
|
15613
15690
|
}
|
|
15614
15691
|
],
|
|
15615
15692
|
completionCriteria: [
|
|
@@ -15828,7 +15905,36 @@ var failedApplyReport = (workflow2, diagnostics, plan) => createWorkflowApplyRep
|
|
|
15828
15905
|
plan: plan ?? failedPlan(workflow2, diagnostics)
|
|
15829
15906
|
});
|
|
15830
15907
|
var commandForShell2 = (command3) => command3.startsWith("akan ") ? `bun run ${command3}` : command3;
|
|
15908
|
+
var inferValidationKind = (command3) => {
|
|
15909
|
+
if (command3.kind)
|
|
15910
|
+
return command3.kind;
|
|
15911
|
+
if (/\bakan\s+sync\b/.test(command3.command))
|
|
15912
|
+
return "sync";
|
|
15913
|
+
if (/\bakan\s+lint\b/.test(command3.command))
|
|
15914
|
+
return "lint";
|
|
15915
|
+
if (/\bakan\s+typecheck\b/.test(command3.command))
|
|
15916
|
+
return "typecheck";
|
|
15917
|
+
if (/\bakan\s+doctor\b/.test(command3.command))
|
|
15918
|
+
return "doctor";
|
|
15919
|
+
return "custom";
|
|
15920
|
+
};
|
|
15921
|
+
var classifyValidationFailure = (command3, error) => {
|
|
15922
|
+
const output = `${error.stdout ?? ""}
|
|
15923
|
+
${error.stderr ?? ""}
|
|
15924
|
+
${error.message ?? ""}`.toLowerCase();
|
|
15925
|
+
if (error.code === 127 || output.includes("command not found") || output.includes("bun: command not found")) {
|
|
15926
|
+
return "environment";
|
|
15927
|
+
}
|
|
15928
|
+
if (output.includes("biome.json") || output.includes("biome configuration") || output.includes("configuration file") || output.includes("invalid configuration") || output.includes("failed to load")) {
|
|
15929
|
+
return "workspace-config";
|
|
15930
|
+
}
|
|
15931
|
+
const kind = inferValidationKind(command3);
|
|
15932
|
+
if (kind === "lint" || kind === "typecheck" || kind === "sync")
|
|
15933
|
+
return "source-change";
|
|
15934
|
+
return "unknown";
|
|
15935
|
+
};
|
|
15831
15936
|
var defaultValidationExecutor = (workspace) => async (command3) => {
|
|
15937
|
+
const kind = inferValidationKind(command3);
|
|
15832
15938
|
try {
|
|
15833
15939
|
const stdout = await workspace.spawn("bash", ["-lc", commandForShell2(command3.command)], {
|
|
15834
15940
|
cwd: workspace.workspaceRoot
|
|
@@ -15836,6 +15942,7 @@ var defaultValidationExecutor = (workspace) => async (command3) => {
|
|
|
15836
15942
|
return {
|
|
15837
15943
|
command: command3.command,
|
|
15838
15944
|
reason: command3.reason,
|
|
15945
|
+
kind,
|
|
15839
15946
|
status: "passed",
|
|
15840
15947
|
exitCode: 0,
|
|
15841
15948
|
stdout
|
|
@@ -15845,8 +15952,10 @@ var defaultValidationExecutor = (workspace) => async (command3) => {
|
|
|
15845
15952
|
return {
|
|
15846
15953
|
command: command3.command,
|
|
15847
15954
|
reason: command3.reason,
|
|
15955
|
+
kind,
|
|
15848
15956
|
status: "failed",
|
|
15849
15957
|
exitCode: commandError.code ?? 1,
|
|
15958
|
+
failureScope: classifyValidationFailure(command3, commandError),
|
|
15850
15959
|
stdout: commandError.stdout,
|
|
15851
15960
|
stderr: commandError.stderr ?? commandError.message
|
|
15852
15961
|
};
|
|
@@ -16060,6 +16169,21 @@ var stringProperty = { type: "string" };
|
|
|
16060
16169
|
var booleanProperty = { type: "boolean" };
|
|
16061
16170
|
var objectProperty = { type: "object", additionalProperties: true };
|
|
16062
16171
|
var parseJsonOutput = (output) => JSON.parse(output);
|
|
16172
|
+
var slugPart = (value) => typeof value === "string" ? value.trim().replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-+|-+$/g, "").toLowerCase() : "";
|
|
16173
|
+
var defaultWorkflowPlanPath = (workflow2, inputs) => {
|
|
16174
|
+
const slug = [
|
|
16175
|
+
slugPart(workflow2),
|
|
16176
|
+
slugPart(inputs.app),
|
|
16177
|
+
slugPart(inputs.module),
|
|
16178
|
+
slugPart(inputs.field),
|
|
16179
|
+
slugPart(inputs.scalar),
|
|
16180
|
+
slugPart(inputs.surface),
|
|
16181
|
+
slugPart(inputs.mutation),
|
|
16182
|
+
slugPart(inputs.slice)
|
|
16183
|
+
].filter(Boolean).join("-");
|
|
16184
|
+
return `.akan/workflows/plans/${slug || "workflow-plan"}.json`;
|
|
16185
|
+
};
|
|
16186
|
+
var workspacePath = (workspace, filePath) => path43.isAbsolute(filePath) ? filePath : path43.join(workspace.workspaceRoot, filePath);
|
|
16063
16187
|
var stringArg = (args, key) => {
|
|
16064
16188
|
const value = args[key];
|
|
16065
16189
|
if (typeof value !== "string" || !value)
|
|
@@ -16112,7 +16236,7 @@ var planMcpTools = [
|
|
|
16112
16236
|
name: "plan_workflow",
|
|
16113
16237
|
inputSchema: {
|
|
16114
16238
|
type: "object",
|
|
16115
|
-
properties: { workflow: stringProperty, inputs: objectProperty },
|
|
16239
|
+
properties: { workflow: stringProperty, inputs: objectProperty, out: stringProperty },
|
|
16116
16240
|
required: ["workflow"]
|
|
16117
16241
|
}
|
|
16118
16242
|
}
|
|
@@ -16238,6 +16362,8 @@ class ContextRunner extends runner("context") {
|
|
|
16238
16362
|
"akan workflow report <run-id> --format json",
|
|
16239
16363
|
"akan doctor --strict --format json"
|
|
16240
16364
|
],
|
|
16365
|
+
validationFailureScopes: ["workspace-config", "environment", "source-change", "unknown"],
|
|
16366
|
+
applyReportFields: ["appliedCommands", "recommendedValidationCommands", "commands"],
|
|
16241
16367
|
repairCommands: [
|
|
16242
16368
|
"akan repair generated --app <app-or-lib> --format json",
|
|
16243
16369
|
"akan repair format --target <app-or-lib-or-pkg> --format json",
|
|
@@ -16250,18 +16376,27 @@ class ContextRunner extends runner("context") {
|
|
|
16250
16376
|
return parseJsonOutput(new WorkflowRunner().list({ format: "json" }));
|
|
16251
16377
|
if (name === "explain_workflow")
|
|
16252
16378
|
return parseJsonOutput(new WorkflowRunner().explain(stringArg(args, "workflow"), { format: "json" }));
|
|
16253
|
-
if (name === "plan_workflow")
|
|
16254
|
-
|
|
16255
|
-
|
|
16379
|
+
if (name === "plan_workflow") {
|
|
16380
|
+
const workflow2 = stringArg(args, "workflow");
|
|
16381
|
+
const inputs = workflowInputsArg(args);
|
|
16382
|
+
const planPath = typeof args.out === "string" && args.out ? args.out : defaultWorkflowPlanPath(workflow2, inputs);
|
|
16383
|
+
const plan = parseJsonOutput(await new WorkflowRunner().plan(workflow2, inputs, {
|
|
16384
|
+
format: "json",
|
|
16385
|
+
out: workspacePath(workspace, planPath)
|
|
16256
16386
|
}));
|
|
16387
|
+
return { ...plan, planPath };
|
|
16388
|
+
}
|
|
16257
16389
|
if (name === "apply_workflow")
|
|
16258
|
-
return parseJsonOutput(await new WorkflowRunner().apply(stringArg(args, "planPath"), {
|
|
16390
|
+
return parseJsonOutput(await new WorkflowRunner().apply(workspacePath(workspace, stringArg(args, "planPath")), {
|
|
16259
16391
|
format: "json",
|
|
16260
16392
|
dryRun: !!args.dryRun,
|
|
16261
16393
|
registry: createCliWorkflowStepRegistry(workspace)
|
|
16262
16394
|
}));
|
|
16263
16395
|
if (name === "run_validation")
|
|
16264
|
-
return parseJsonOutput(await new WorkflowRunner().validate(stringArg(args, "runIdOrPlan"), {
|
|
16396
|
+
return parseJsonOutput(await new WorkflowRunner().validate(workspacePath(workspace, stringArg(args, "runIdOrPlan")), {
|
|
16397
|
+
format: "json",
|
|
16398
|
+
workspace
|
|
16399
|
+
}));
|
|
16265
16400
|
if (name === "repair_generated")
|
|
16266
16401
|
return parseJsonOutput(await new RepairRunner().repair("generated", { workspace, app: stringArg(args, "app"), format: "json" }));
|
|
16267
16402
|
if (name === "repair_imports")
|
|
@@ -16427,7 +16562,7 @@ ${payload}`);
|
|
|
16427
16562
|
"create-unit": "`akan create-unit --app <app> --module <module>` creates a module Unit component.",
|
|
16428
16563
|
"create-template": "`akan create-template --app <app> --module <module>` creates a module Template component.",
|
|
16429
16564
|
"create-ui": "`akan create-ui --app <app-or-lib> --module <module> --surface <view|unit|template> --format json` creates one UI surface and returns a primitive write report.",
|
|
16430
|
-
"add-field": "`akan add-field --app <app-or-lib> --module <module> --field <field> --type <
|
|
16565
|
+
"add-field": "`akan add-field --app <app-or-lib> --module <module> --field <field> --type <String|Boolean|Date|Int|Float|scalar> --format json` updates source constant/dictionary files. Use Int or Float for numeric fields, not Number.",
|
|
16431
16566
|
"add-enum-field": "`akan add-enum-field --app <app-or-lib> --module <module> --field <field> --values a,b --format json` adds an enum field to source constant/dictionary files without editing generated files.",
|
|
16432
16567
|
sync: "`akan sync <app-or-lib>` refreshes generated Akan files from source conventions.",
|
|
16433
16568
|
lint: "`akan lint <app-or-lib-or-pkg>` runs Biome linting after preparing generated files.",
|
|
@@ -16451,6 +16586,7 @@ ${payload}`);
|
|
|
16451
16586
|
"repair module-shape": "`akan repair module-shape --app <app-or-lib> --module <module> --format json` reports missing module source files and source-safe next actions.",
|
|
16452
16587
|
agent: "`akan agent install <target>` writes editor-specific agent rules with overwrite protection.",
|
|
16453
16588
|
mcp: "`akan mcp --mode readonly|plan|apply` starts the Akan MCP server over stdio with an explicit permission mode.",
|
|
16589
|
+
"mcp-call": '`akan mcp-call <tool> --mode plan --args \'{"workflow":"add-field","inputs":{"app":"demo"}}\'` calls one MCP tool through the same runner path for debugging.',
|
|
16454
16590
|
"mcp-install": "`akan mcp-install cursor --mode readonly|plan|apply` installs the Akan MCP server config for Cursor."
|
|
16455
16591
|
};
|
|
16456
16592
|
return explanations[command3] ?? `No detailed explanation is available for ${command3}. Run \`akan --help\` for command help.`;
|
|
@@ -16475,6 +16611,22 @@ class ContextScript extends script("context", [ContextRunner]) {
|
|
|
16475
16611
|
async mcp(workspace, { mode = "readonly" } = {}) {
|
|
16476
16612
|
await this.contextRunner.runMcp(workspace, { mode });
|
|
16477
16613
|
}
|
|
16614
|
+
async mcpCall(workspace, tool, {
|
|
16615
|
+
mode = "readonly",
|
|
16616
|
+
args = null,
|
|
16617
|
+
format = "json"
|
|
16618
|
+
} = {}) {
|
|
16619
|
+
let parsedArgs = {};
|
|
16620
|
+
if (args) {
|
|
16621
|
+
const parsed = JSON.parse(args);
|
|
16622
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
16623
|
+
throw new Error("MCP call args must be a JSON object.");
|
|
16624
|
+
}
|
|
16625
|
+
parsedArgs = parsed;
|
|
16626
|
+
}
|
|
16627
|
+
const result = await this.contextRunner.callMcpTool(workspace, tool, parsedArgs, { mode });
|
|
16628
|
+
Logger17.rawLog(format === "json" ? jsonText(result) : String(result));
|
|
16629
|
+
}
|
|
16478
16630
|
}
|
|
16479
16631
|
|
|
16480
16632
|
// pkgs/@akanjs/cli/context/context.command.ts
|
|
@@ -16509,6 +16661,21 @@ class ContextCommand extends command("context", [ContextScript], ({ public: targ
|
|
|
16509
16661
|
enum: ["readonly", "plan", "apply"]
|
|
16510
16662
|
}).with(Workspace).exec(async function(mode, workspace) {
|
|
16511
16663
|
await this.contextScript.mcp(workspace, { mode });
|
|
16664
|
+
}),
|
|
16665
|
+
mcpCall: target({ desc: "Call one Akan MCP tool for debugging without stdio JSON-RPC" }).arg("tool", String, { desc: "MCP tool name" }).option("mode", String, {
|
|
16666
|
+
desc: "MCP permission mode",
|
|
16667
|
+
default: "readonly",
|
|
16668
|
+
enum: ["readonly", "plan", "apply"]
|
|
16669
|
+
}).option("args", String, { desc: "JSON object to pass as MCP tool arguments", nullable: true }).option("format", String, {
|
|
16670
|
+
desc: "output format",
|
|
16671
|
+
default: "json",
|
|
16672
|
+
enum: ["json"]
|
|
16673
|
+
}).with(Workspace).exec(async function(tool, mode, args, format, workspace) {
|
|
16674
|
+
await this.contextScript.mcpCall(workspace, tool, {
|
|
16675
|
+
mode,
|
|
16676
|
+
args,
|
|
16677
|
+
format
|
|
16678
|
+
});
|
|
16512
16679
|
})
|
|
16513
16680
|
})) {
|
|
16514
16681
|
}
|
|
@@ -16537,14 +16704,14 @@ class GuidelinePrompt extends Prompter {
|
|
|
16537
16704
|
async#getScanFilePaths(matchPattern, { avoidDirs = ["node_modules", ".next"], filterText } = {}) {
|
|
16538
16705
|
const glob = new Bun.Glob(matchPattern);
|
|
16539
16706
|
const paths = [];
|
|
16540
|
-
for await (const
|
|
16541
|
-
if (avoidDirs.some((dir) =>
|
|
16707
|
+
for await (const path44 of glob.scan({ cwd: this.workspace.workspaceRoot, absolute: true })) {
|
|
16708
|
+
if (avoidDirs.some((dir) => path44.includes(dir)))
|
|
16542
16709
|
continue;
|
|
16543
|
-
const fileContent = await FileSys.readText(
|
|
16710
|
+
const fileContent = await FileSys.readText(path44);
|
|
16544
16711
|
const textFilter = filterText ? new RegExp(filterText) : null;
|
|
16545
16712
|
if (filterText && !textFilter?.test(fileContent))
|
|
16546
16713
|
continue;
|
|
16547
|
-
paths.push(
|
|
16714
|
+
paths.push(path44);
|
|
16548
16715
|
}
|
|
16549
16716
|
return paths;
|
|
16550
16717
|
}
|
|
@@ -16864,7 +17031,7 @@ class LibraryCommand extends command("library", [LibraryScript], ({ public: targ
|
|
|
16864
17031
|
|
|
16865
17032
|
// pkgs/@akanjs/cli/localRegistry/localRegistry.runner.ts
|
|
16866
17033
|
import { mkdir as mkdir13, rm as rm6 } from "fs/promises";
|
|
16867
|
-
import
|
|
17034
|
+
import path44 from "path";
|
|
16868
17035
|
import { Logger as Logger19 } from "akanjs/common";
|
|
16869
17036
|
var defaultLocalRegistryUrl = "http://127.0.0.1:4873";
|
|
16870
17037
|
var containerName = "akan-verdaccio";
|
|
@@ -16882,8 +17049,8 @@ class LocalRegistryRunner extends runner("localRegistry") {
|
|
|
16882
17049
|
Logger19.info(`Local registry is already running at ${registry}`);
|
|
16883
17050
|
return registry;
|
|
16884
17051
|
} catch {}
|
|
16885
|
-
const configPath2 =
|
|
16886
|
-
const storagePath =
|
|
17052
|
+
const configPath2 = path44.join(workspace.workspaceRoot, "pkgs/@akanjs/cli/localRegistry/verdaccio.yaml");
|
|
17053
|
+
const storagePath = path44.join(workspace.workspaceRoot, ".akan/verdaccio/storage");
|
|
16887
17054
|
await mkdir13(storagePath, { recursive: true });
|
|
16888
17055
|
await workspace.spawn("docker", [
|
|
16889
17056
|
"run",
|
|
@@ -16906,13 +17073,13 @@ class LocalRegistryRunner extends runner("localRegistry") {
|
|
|
16906
17073
|
try {
|
|
16907
17074
|
await workspace.spawn("docker", ["rm", "-f", containerName], { stdio: "inherit" });
|
|
16908
17075
|
} catch {}
|
|
16909
|
-
await rm6(
|
|
17076
|
+
await rm6(path44.join(workspace.workspaceRoot, ".akan/verdaccio"), { recursive: true, force: true });
|
|
16910
17077
|
Logger19.info("Local registry storage has been reset");
|
|
16911
17078
|
}
|
|
16912
17079
|
async smoke(workspace, { registryUrl } = {}) {
|
|
16913
17080
|
const registry = this.getRegistryUrl(registryUrl);
|
|
16914
|
-
const smokeRoot =
|
|
16915
|
-
await rm6(
|
|
17081
|
+
const smokeRoot = path44.join(workspace.workspaceRoot, ".akan/e2e");
|
|
17082
|
+
await rm6(path44.join(smokeRoot, smokeRepoName), { recursive: true, force: true });
|
|
16916
17083
|
await workspace.spawn(process.execPath, [
|
|
16917
17084
|
"dist/pkgs/create-akan-workspace/index.js",
|
|
16918
17085
|
smokeRepoName,
|
|
@@ -16929,12 +17096,12 @@ class LocalRegistryRunner extends runner("localRegistry") {
|
|
|
16929
17096
|
stdio: "inherit"
|
|
16930
17097
|
});
|
|
16931
17098
|
await workspace.spawn("akan", ["typecheck", smokeAppName], {
|
|
16932
|
-
cwd:
|
|
17099
|
+
cwd: path44.join(smokeRoot, smokeRepoName),
|
|
16933
17100
|
env: { ...process.env, AKAN_NPM_REGISTRY: registry, NPM_CONFIG_REGISTRY: registry },
|
|
16934
17101
|
stdio: "inherit"
|
|
16935
17102
|
});
|
|
16936
17103
|
await workspace.spawn("akan", ["build", smokeAppName], {
|
|
16937
|
-
cwd:
|
|
17104
|
+
cwd: path44.join(smokeRoot, smokeRepoName),
|
|
16938
17105
|
env: { ...process.env, AKAN_NPM_REGISTRY: registry, NPM_CONFIG_REGISTRY: registry },
|
|
16939
17106
|
stdio: "inherit"
|
|
16940
17107
|
});
|
|
@@ -17249,11 +17416,11 @@ class WorkflowCommand extends command("workflow", [WorkflowScript], ({ public: t
|
|
|
17249
17416
|
}
|
|
17250
17417
|
|
|
17251
17418
|
// pkgs/@akanjs/cli/workspace/workspace.script.ts
|
|
17252
|
-
import
|
|
17419
|
+
import path46 from "path";
|
|
17253
17420
|
import { Logger as Logger25 } from "akanjs/common";
|
|
17254
17421
|
|
|
17255
17422
|
// pkgs/@akanjs/cli/workspace/workspace.runner.ts
|
|
17256
|
-
import
|
|
17423
|
+
import path45 from "path";
|
|
17257
17424
|
var defaultWorkspacePeerDependencies = new Set([
|
|
17258
17425
|
"@react-spring/web",
|
|
17259
17426
|
"@use-gesture/react",
|
|
@@ -17304,7 +17471,7 @@ class WorkspaceRunner extends runner("workspace") {
|
|
|
17304
17471
|
owner = ""
|
|
17305
17472
|
}) {
|
|
17306
17473
|
const cwdPath = process.cwd();
|
|
17307
|
-
const workspaceRoot =
|
|
17474
|
+
const workspaceRoot = path45.join(cwdPath, dirname2, repoName);
|
|
17308
17475
|
const normalizedRegistryUrl = registryUrl ? getNpmRegistryUrl(registryUrl) : undefined;
|
|
17309
17476
|
const workspace = WorkspaceExecutor.fromRoot({ workspaceRoot, repoName });
|
|
17310
17477
|
const templateSpinner = workspace.spinning(`Creating workspace template files in ${dirname2}/${repoName}...`);
|
|
@@ -17360,9 +17527,9 @@ class WorkspaceRunner extends runner("workspace") {
|
|
|
17360
17527
|
}
|
|
17361
17528
|
async#getCliPackageJson() {
|
|
17362
17529
|
const packageJsonCandidates = [
|
|
17363
|
-
|
|
17364
|
-
|
|
17365
|
-
|
|
17530
|
+
path45.join(import.meta.dir, "../package.json"),
|
|
17531
|
+
path45.join(import.meta.dir, "package.json"),
|
|
17532
|
+
path45.join(path45.dirname(Bun.main), "package.json")
|
|
17366
17533
|
];
|
|
17367
17534
|
try {
|
|
17368
17535
|
packageJsonCandidates.unshift(Bun.resolveSync("@akanjs/cli/package.json", import.meta.dir));
|
|
@@ -17378,9 +17545,9 @@ class WorkspaceRunner extends runner("workspace") {
|
|
|
17378
17545
|
}
|
|
17379
17546
|
async#getAkanPackageJson() {
|
|
17380
17547
|
const packageJsonCandidates = [
|
|
17381
|
-
|
|
17382
|
-
|
|
17383
|
-
|
|
17548
|
+
path45.join(import.meta.dir, "../../../akanjs/package.json"),
|
|
17549
|
+
path45.join(process.cwd(), "pkgs/akanjs/package.json"),
|
|
17550
|
+
path45.join(path45.dirname(Bun.main), "node_modules/akanjs/package.json")
|
|
17384
17551
|
];
|
|
17385
17552
|
try {
|
|
17386
17553
|
packageJsonCandidates.unshift(Bun.resolveSync("akanjs/package.json", import.meta.dir));
|
|
@@ -17394,13 +17561,13 @@ class WorkspaceRunner extends runner("workspace") {
|
|
|
17394
17561
|
}
|
|
17395
17562
|
let current = import.meta.dir;
|
|
17396
17563
|
for (let depth = 0;depth < 6; depth++) {
|
|
17397
|
-
const packageJsonPath =
|
|
17564
|
+
const packageJsonPath = path45.join(current, "package.json");
|
|
17398
17565
|
if (await Bun.file(packageJsonPath).exists()) {
|
|
17399
17566
|
const packageJson = await FileSys.readJson(packageJsonPath);
|
|
17400
17567
|
if (packageJson.name === "akanjs")
|
|
17401
17568
|
return packageJson;
|
|
17402
17569
|
}
|
|
17403
|
-
const parent =
|
|
17570
|
+
const parent = path45.dirname(current);
|
|
17404
17571
|
if (parent === current)
|
|
17405
17572
|
break;
|
|
17406
17573
|
current = parent;
|
|
@@ -17477,10 +17644,10 @@ class WorkspaceScript extends script("workspace", [
|
|
|
17477
17644
|
} catch (_) {
|
|
17478
17645
|
gitSpinner.fail("Git repository initialization failed. It's not fatal, you can commit manually");
|
|
17479
17646
|
}
|
|
17480
|
-
const
|
|
17647
|
+
const workspacePath2 = path46.join(dirname2, repoName);
|
|
17481
17648
|
Logger25.rawLog(`
|
|
17482
17649
|
\uD83C\uDF89 Welcome aboard! Workspace created in ${dirname2}/${repoName}`);
|
|
17483
|
-
Logger25.rawLog(`\uD83D\uDE80 Run \`cd ${
|
|
17650
|
+
Logger25.rawLog(`\uD83D\uDE80 Run \`cd ${workspacePath2} && akan start ${appName}\` to start the development server.`);
|
|
17484
17651
|
Logger25.rawLog(`
|
|
17485
17652
|
\uD83D\uDC4B Happy coding!`);
|
|
17486
17653
|
}
|
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.2",
|
|
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.2",
|
|
38
38
|
"chalk": "^5.6.2",
|
|
39
39
|
"commander": "^14.0.3",
|
|
40
40
|
"daisyui": "5.5.23",
|
|
@@ -8,8 +8,25 @@
|
|
|
8
8
|
"lint": "akan lint",
|
|
9
9
|
"typecheck": "akan typecheck",
|
|
10
10
|
"test": "akan test",
|
|
11
|
-
"
|
|
12
|
-
"
|
|
11
|
+
"build": "akan build",
|
|
12
|
+
"setup:agent": "akan agent install all --force",
|
|
13
|
+
"agent:setup": "akan agent install all --force",
|
|
14
|
+
"agent:doctor": "akan doctor --strict --format json",
|
|
15
|
+
"agent:context": "akan context --format json",
|
|
16
|
+
"agent:mcp:readonly": "akan mcp-install cursor --mode readonly --force",
|
|
17
|
+
"agent:mcp:plan": "akan mcp-install cursor --mode plan --force",
|
|
18
|
+
"agent:mcp:apply": "akan mcp-install cursor --mode apply --force",
|
|
19
|
+
"agent:workflows": "akan workflow list",
|
|
20
|
+
"agent:sample:context": "akan context --format markdown --app <%= appName %> --module project",
|
|
21
|
+
"agent:sample:service": "akan create-service billing <%= appName %> --format json",
|
|
22
|
+
"agent:sample:module": "akan create-module project <%= appName %> --page=true --format json",
|
|
23
|
+
"agent:sample:field": "akan add-field --app <%= appName %> --module project --field budget --type Int --default 0 --format json",
|
|
24
|
+
"agent:sample:status": "akan add-enum-field --app <%= appName %> --module project --field status --values draft,active,archived --default draft --format json",
|
|
25
|
+
"agent:sample:sync": "akan sync <%= appName %>",
|
|
26
|
+
"agent:sample:check": "akan doctor --strict --format json",
|
|
27
|
+
"agent:sample:workflow:explain": "akan workflow explain add-field",
|
|
28
|
+
"agent:sample:workflow:plan": "akan workflow plan add-field --app <%= appName %> --module task --field priority --type enum --values low,medium,high --default medium --format json --out .akan/workflows/plans/task-priority.json",
|
|
29
|
+
"agent:sample:workflow:dry-run": "akan workflow apply .akan/workflows/plans/task-priority.json --dry-run --format json"
|
|
13
30
|
},
|
|
14
31
|
"dependencies": {},
|
|
15
32
|
"devDependencies": {},
|