@akanjs/cli 2.3.9-rc.3 → 2.3.9-rc.4
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 +563 -445
- package/index.js +873 -635
- package/package.json +2 -2
- package/templates/module/__model__.constant.ts +1 -2
package/index.js
CHANGED
|
@@ -4776,238 +4776,11 @@ class AkanAppHost {
|
|
|
4776
4776
|
// pkgs/@akanjs/devkit/akanContext.ts
|
|
4777
4777
|
import { readdir } from "fs/promises";
|
|
4778
4778
|
import path10 from "path";
|
|
4779
|
-
import { capitalize as
|
|
4779
|
+
import { capitalize as capitalize4 } from "akanjs/common";
|
|
4780
4780
|
|
|
4781
|
-
// pkgs/@akanjs/devkit/workflow/
|
|
4782
|
-
import { capitalize as capitalize2 } from "akanjs/common";
|
|
4781
|
+
// pkgs/@akanjs/devkit/workflow/utils.ts
|
|
4783
4782
|
var jsonText = (value, { trailingNewline = true } = {}) => `${JSON.stringify(value, null, 2)}${trailingNewline ? `
|
|
4784
4783
|
` : ""}`;
|
|
4785
|
-
var surfaceModes = new Set(["infer", "include", "skip"]);
|
|
4786
|
-
var parseStringList = (value) => {
|
|
4787
|
-
if (Array.isArray(value)) {
|
|
4788
|
-
const values = value.filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
4789
|
-
return values.length === value.length ? values : null;
|
|
4790
|
-
}
|
|
4791
|
-
if (typeof value !== "string")
|
|
4792
|
-
return null;
|
|
4793
|
-
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
4794
|
-
};
|
|
4795
|
-
var normalizeInputValue = (name, spec, value) => {
|
|
4796
|
-
if (spec.type === "string")
|
|
4797
|
-
return typeof value === "string" && value.length > 0 ? value : null;
|
|
4798
|
-
if (spec.type === "string-list") {
|
|
4799
|
-
const values = parseStringList(value);
|
|
4800
|
-
return values && values.length > 0 ? values : null;
|
|
4801
|
-
}
|
|
4802
|
-
if (typeof value === "string" && surfaceModes.has(value))
|
|
4803
|
-
return value;
|
|
4804
|
-
throw new Error(`Unsupported workflow input value for ${name}`);
|
|
4805
|
-
};
|
|
4806
|
-
var listWorkflowSpecs = (specs) => [...specs].sort((a, b) => a.name.localeCompare(b.name));
|
|
4807
|
-
var getWorkflowSpec = (specs, name) => specs.find((spec) => spec.name === name) ?? null;
|
|
4808
|
-
var compactWorkflowInputs = (inputs) => Object.fromEntries(Object.entries(inputs).filter(([, value]) => value !== null && value !== ""));
|
|
4809
|
-
var addFieldComponentForType = (typeName) => {
|
|
4810
|
-
const normalizedType = normalizeFieldType(typeName);
|
|
4811
|
-
if (normalizedType === "Boolean")
|
|
4812
|
-
return "Field.ToggleSelect";
|
|
4813
|
-
if (normalizedType === "Date")
|
|
4814
|
-
return "Field.Date";
|
|
4815
|
-
if (normalizedType === "Int" || normalizedType === "Float")
|
|
4816
|
-
return "Field.ToggleSelect or Field.Text";
|
|
4817
|
-
if (typeName.toLowerCase() === "enum")
|
|
4818
|
-
return "Field.ToggleSelect";
|
|
4819
|
-
return "Field.Text";
|
|
4820
|
-
};
|
|
4821
|
-
var createAddFieldRecommendations = (inputs) => {
|
|
4822
|
-
const app = typeof inputs.app === "string" ? inputs.app : "<app-or-lib>";
|
|
4823
|
-
const module = typeof inputs.module === "string" ? inputs.module : "<module>";
|
|
4824
|
-
const field = typeof inputs.field === "string" ? inputs.field : "<field>";
|
|
4825
|
-
const typeName = typeof inputs.type === "string" ? inputs.type : null;
|
|
4826
|
-
if (!typeName)
|
|
4827
|
-
return [];
|
|
4828
|
-
const normalizedType = typeName.toLowerCase() === "enum" ? "enum" : normalizeFieldType(typeName);
|
|
4829
|
-
const constantPath = `*/lib/${module}/${module}.constant.ts`;
|
|
4830
|
-
const dictionaryPath = `*/lib/${module}/${module}.dictionary.ts`;
|
|
4831
|
-
const templatePath = `*/lib/${module}/${capitalize2(module)}.Template.tsx`;
|
|
4832
|
-
return [
|
|
4833
|
-
...normalizedType === "Int" || normalizedType === "Float" ? [
|
|
4834
|
-
{
|
|
4835
|
-
code: "add-field-import",
|
|
4836
|
-
kind: "import",
|
|
4837
|
-
target: constantPath,
|
|
4838
|
-
confidence: "high",
|
|
4839
|
-
message: `Import ${normalizedType} from "akanjs/base" before writing field(${normalizedType}).`
|
|
4840
|
-
}
|
|
4841
|
-
] : [],
|
|
4842
|
-
{
|
|
4843
|
-
code: "add-field-placement-constant",
|
|
4844
|
-
kind: "placement",
|
|
4845
|
-
target: constantPath,
|
|
4846
|
-
confidence: "high",
|
|
4847
|
-
message: `Insert ${field}: field(${normalizedType}) in ${capitalize2(module)}Input.`
|
|
4848
|
-
},
|
|
4849
|
-
{
|
|
4850
|
-
code: "add-field-placement-dictionary",
|
|
4851
|
-
kind: "placement",
|
|
4852
|
-
target: dictionaryPath,
|
|
4853
|
-
confidence: "high",
|
|
4854
|
-
message: `Add dictionary labels for ${module}.${field}.`
|
|
4855
|
-
},
|
|
4856
|
-
{
|
|
4857
|
-
code: "add-field-component",
|
|
4858
|
-
kind: "ui-component",
|
|
4859
|
-
target: templatePath,
|
|
4860
|
-
confidence: normalizedType === "Int" || normalizedType === "Float" ? "medium" : "high",
|
|
4861
|
-
message: `Recommended UI component for ${field} (${normalizedType}): ${addFieldComponentForType(typeName)}.`
|
|
4862
|
-
},
|
|
4863
|
-
{
|
|
4864
|
-
code: "add-field-ui-manual-review",
|
|
4865
|
-
kind: "manual-action",
|
|
4866
|
-
target: templatePath,
|
|
4867
|
-
action: `Review ${app}:${module} Template/Unit/View/Store surfaces and add ${field} only where the existing pattern is clear.`,
|
|
4868
|
-
confidence: "medium",
|
|
4869
|
-
message: "UI surface edits are planned as manual-review unless a safe existing field-list pattern is detected."
|
|
4870
|
-
}
|
|
4871
|
-
];
|
|
4872
|
-
};
|
|
4873
|
-
var createWorkflowPlanRecommendations = (spec, inputs) => [
|
|
4874
|
-
{
|
|
4875
|
-
code: "workflow-apply-first",
|
|
4876
|
-
kind: "auto-apply",
|
|
4877
|
-
confidence: "high",
|
|
4878
|
-
action: "Call apply_workflow with the MCP planPath before editing source files directly.",
|
|
4879
|
-
message: `Apply the ${spec.name} plan through apply_workflow when MCP returns planPath or next.tool=apply_workflow.`
|
|
4880
|
-
},
|
|
4881
|
-
{
|
|
4882
|
-
code: "workflow-validate-apply-report",
|
|
4883
|
-
kind: "validation",
|
|
4884
|
-
confidence: "high",
|
|
4885
|
-
action: "After apply_workflow, call run_validation with validationTarget when present; otherwise use applyReportPath.",
|
|
4886
|
-
message: "Validate the apply report artifact so diagnostics and recommendations follow the actual apply result."
|
|
4887
|
-
},
|
|
4888
|
-
...spec.name === "add-field" ? createAddFieldRecommendations(inputs) : []
|
|
4889
|
-
];
|
|
4890
|
-
var createWorkflowPlan = (spec, rawInputs) => {
|
|
4891
|
-
const inputs = {};
|
|
4892
|
-
const diagnostics = [];
|
|
4893
|
-
for (const [name, inputSpec] of Object.entries(spec.inputs)) {
|
|
4894
|
-
const rawValue = rawInputs[name];
|
|
4895
|
-
if (rawValue === undefined || rawValue === null || rawValue === "") {
|
|
4896
|
-
if (inputSpec.required) {
|
|
4897
|
-
diagnostics.push({
|
|
4898
|
-
severity: "error",
|
|
4899
|
-
code: "workflow-input-missing",
|
|
4900
|
-
input: name,
|
|
4901
|
-
message: `Workflow ${spec.name} requires input "${name}".`
|
|
4902
|
-
});
|
|
4903
|
-
}
|
|
4904
|
-
continue;
|
|
4905
|
-
}
|
|
4906
|
-
const value = normalizeInputValue(name, inputSpec, rawValue);
|
|
4907
|
-
if (value === null) {
|
|
4908
|
-
diagnostics.push({
|
|
4909
|
-
severity: "error",
|
|
4910
|
-
code: "workflow-input-invalid",
|
|
4911
|
-
input: name,
|
|
4912
|
-
message: `Workflow ${spec.name} input "${name}" must be ${inputSpec.type}.`
|
|
4913
|
-
});
|
|
4914
|
-
continue;
|
|
4915
|
-
}
|
|
4916
|
-
if (inputSpec.allowedValues && typeof value === "string" && !inputSpec.allowedValues.includes(value)) {
|
|
4917
|
-
diagnostics.push({
|
|
4918
|
-
severity: "error",
|
|
4919
|
-
code: "workflow-input-not-allowed",
|
|
4920
|
-
input: name,
|
|
4921
|
-
message: `Workflow ${spec.name} input "${name}" must be one of: ${inputSpec.allowedValues.join(", ")}.`
|
|
4922
|
-
});
|
|
4923
|
-
continue;
|
|
4924
|
-
}
|
|
4925
|
-
inputs[name] = value;
|
|
4926
|
-
}
|
|
4927
|
-
const fieldType = inputs.type;
|
|
4928
|
-
if (spec.name === "add-field" && typeof fieldType === "string" && (fieldType.toLowerCase() === "number" || fieldType.toLowerCase() === "numeric")) {
|
|
4929
|
-
diagnostics.push({
|
|
4930
|
-
severity: "error",
|
|
4931
|
-
code: "primitive-field-type-unsupported",
|
|
4932
|
-
input: "type",
|
|
4933
|
-
message: `Field type "${fieldType}" is ambiguous in Akan. Use Int for integer fields or Float for decimal fields.`
|
|
4934
|
-
});
|
|
4935
|
-
}
|
|
4936
|
-
return {
|
|
4937
|
-
schemaVersion: 1,
|
|
4938
|
-
workflow: spec.name,
|
|
4939
|
-
mode: "plan",
|
|
4940
|
-
inputs,
|
|
4941
|
-
optionalSurfaces: spec.optionalSurfaces ?? {},
|
|
4942
|
-
steps: spec.steps,
|
|
4943
|
-
predictedChanges: spec.predictedChanges,
|
|
4944
|
-
validation: spec.validation,
|
|
4945
|
-
diagnostics,
|
|
4946
|
-
recommendations: createWorkflowPlanRecommendations(spec, inputs),
|
|
4947
|
-
requiresApproval: true
|
|
4948
|
-
};
|
|
4949
|
-
};
|
|
4950
|
-
var renderWorkflowList = (specs) => [
|
|
4951
|
-
"# Akan Workflows",
|
|
4952
|
-
"",
|
|
4953
|
-
...specs.flatMap((spec) => [`- \`${spec.name}\`: ${spec.description}`, ` - When: ${spec.whenToUse}`]),
|
|
4954
|
-
""
|
|
4955
|
-
].join(`
|
|
4956
|
-
`);
|
|
4957
|
-
var renderWorkflowExplain = (spec) => [
|
|
4958
|
-
`# Workflow: ${spec.name}`,
|
|
4959
|
-
"",
|
|
4960
|
-
spec.description,
|
|
4961
|
-
"",
|
|
4962
|
-
"## When To Use",
|
|
4963
|
-
spec.whenToUse,
|
|
4964
|
-
"",
|
|
4965
|
-
"## Inputs",
|
|
4966
|
-
...Object.entries(spec.inputs).map(([name, input2]) => `- \`${name}\`${input2.required ? " (required)" : ""}: ${input2.description}${input2.allowedValues ? ` Allowed: ${input2.allowedValues.join(", ")}.` : ""}`),
|
|
4967
|
-
"",
|
|
4968
|
-
"## Optional Surfaces",
|
|
4969
|
-
...Object.entries(spec.optionalSurfaces ?? {}).map(([name, mode]) => `- \`${name}\`: ${mode}`),
|
|
4970
|
-
"",
|
|
4971
|
-
"## Steps",
|
|
4972
|
-
...spec.steps.map((step, index) => `${index + 1}. \`${step.id}\` (${step.tool}): ${step.description}`),
|
|
4973
|
-
"",
|
|
4974
|
-
"## Validation",
|
|
4975
|
-
...spec.validation.map((validation) => `- \`${validation.command}\`: ${validation.reason}`),
|
|
4976
|
-
""
|
|
4977
|
-
].join(`
|
|
4978
|
-
`);
|
|
4979
|
-
var renderWorkflowPlan = (plan) => [
|
|
4980
|
-
`# Workflow Plan: ${plan.workflow}`,
|
|
4981
|
-
"",
|
|
4982
|
-
`- Mode: ${plan.mode}`,
|
|
4983
|
-
`- Requires approval: ${plan.requiresApproval}`,
|
|
4984
|
-
"",
|
|
4985
|
-
"## Inputs",
|
|
4986
|
-
...Object.entries(plan.inputs).map(([name, value]) => `- \`${name}\`: ${Array.isArray(value) ? value.join(", ") : value}`),
|
|
4987
|
-
"",
|
|
4988
|
-
"## Optional Surfaces",
|
|
4989
|
-
...Object.entries(plan.optionalSurfaces).map(([name, mode]) => `- \`${name}\`: ${mode}`),
|
|
4990
|
-
"",
|
|
4991
|
-
"## Steps",
|
|
4992
|
-
...plan.steps.map((step, index) => `${index + 1}. \`${step.id}\` (${step.tool}): ${step.description}`),
|
|
4993
|
-
"",
|
|
4994
|
-
"## Predicted Changes",
|
|
4995
|
-
...plan.predictedChanges.map((change) => {
|
|
4996
|
-
const scope = change.applyScope ? ` (${change.applyScope})` : "";
|
|
4997
|
-
return `- \`${change.action}\`${scope} ${change.target}: ${change.reason}`;
|
|
4998
|
-
}),
|
|
4999
|
-
"",
|
|
5000
|
-
"## Validation",
|
|
5001
|
-
...plan.validation.map((validation) => `- \`${validation.command}\`: ${validation.reason}`),
|
|
5002
|
-
"",
|
|
5003
|
-
"## Diagnostics",
|
|
5004
|
-
...plan.diagnostics.length ? plan.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
5005
|
-
"",
|
|
5006
|
-
"## Recommendations",
|
|
5007
|
-
...plan.recommendations.length ? plan.recommendations.map((recommendation) => `- [${recommendation.kind}] ${recommendation.message}`) : ["- none"],
|
|
5008
|
-
""
|
|
5009
|
-
].join(`
|
|
5010
|
-
`);
|
|
5011
4784
|
var workflowStatus = (diagnostics) => diagnostics.some((diagnostic) => diagnostic.severity === "error") ? "failed" : "passed";
|
|
5012
4785
|
var commandStatus = (commands) => commands.some((command) => command.status === "failed") ? "failed" : "passed";
|
|
5013
4786
|
var uniqueBy = (values, key) => {
|
|
@@ -5020,6 +4793,9 @@ var uniqueBy = (values, key) => {
|
|
|
5020
4793
|
return true;
|
|
5021
4794
|
});
|
|
5022
4795
|
};
|
|
4796
|
+
var compactDiagnostics = (diagnostics) => diagnostics.filter((diagnostic) => !!diagnostic);
|
|
4797
|
+
|
|
4798
|
+
// pkgs/@akanjs/devkit/workflow/artifacts.ts
|
|
5023
4799
|
var createWorkflowApplyReport = ({
|
|
5024
4800
|
workflow,
|
|
5025
4801
|
mode,
|
|
@@ -5102,6 +4878,50 @@ var readWorkflowRunArtifact = async (workspace, runId) => {
|
|
|
5102
4878
|
throw new Error(`Workflow run artifact does not exist: ${artifactPath}`);
|
|
5103
4879
|
return await workspace.readJson(artifactPath);
|
|
5104
4880
|
};
|
|
4881
|
+
var failedCommandScopes = (commands) => commands.filter((command) => command.status === "failed").map((command) => command.failureScope ?? "unknown");
|
|
4882
|
+
var errorDiagnosticScopes = (diagnostics) => diagnostics.filter((diagnostic) => diagnostic.severity === "error").map((diagnostic) => diagnostic.failureScope ?? (diagnostic.scope === "baseline" ? "workspace-config" : diagnostic.scope === "workflow" ? "source-change" : "unknown"));
|
|
4883
|
+
var hasScopeFailure = (scopes, scope, diagnostics = []) => scopes.includes(scope) || diagnostics.some((diagnostic) => diagnostic.severity === "error" && diagnostic.failureScope === scope);
|
|
4884
|
+
var statusForScope = (commands, diagnostics, scopes, expectedScope) => {
|
|
4885
|
+
const hasCommands = commands.length > 0 || diagnostics.length > 0;
|
|
4886
|
+
if (!hasCommands)
|
|
4887
|
+
return "unknown";
|
|
4888
|
+
return hasScopeFailure(scopes, expectedScope, diagnostics) ? "failed" : "passed";
|
|
4889
|
+
};
|
|
4890
|
+
var createKnownBlockers = (commands, diagnostics) => {
|
|
4891
|
+
const commandBlockers = commands.filter((command) => command.status === "failed" && (command.failureScope === "workspace-config" || command.failureScope === "environment")).map((command) => ({
|
|
4892
|
+
code: `workflow-validation-${command.failureScope}`,
|
|
4893
|
+
message: `${command.failureScope === "environment" ? "Environment" : "Workspace configuration"} blocker: ${command.command}`,
|
|
4894
|
+
failureScope: command.failureScope ?? "unknown",
|
|
4895
|
+
command: command.command,
|
|
4896
|
+
kind: command.kind,
|
|
4897
|
+
count: 1
|
|
4898
|
+
}));
|
|
4899
|
+
const diagnosticBlockers = diagnostics.filter((diagnostic) => diagnostic.severity === "error" && (diagnostic.failureScope === "workspace-config" || diagnostic.failureScope === "environment" || diagnostic.scope === "baseline")).map((diagnostic) => ({
|
|
4900
|
+
code: diagnostic.code,
|
|
4901
|
+
message: diagnostic.message,
|
|
4902
|
+
failureScope: diagnostic.failureScope ?? "workspace-config",
|
|
4903
|
+
command: diagnostic.command,
|
|
4904
|
+
kind: diagnostic.kind,
|
|
4905
|
+
count: 1
|
|
4906
|
+
}));
|
|
4907
|
+
const grouped = new Map;
|
|
4908
|
+
for (const blocker of [...commandBlockers, ...diagnosticBlockers]) {
|
|
4909
|
+
const key = `${blocker.failureScope}:${blocker.code}:${blocker.command ?? ""}:${blocker.message}`;
|
|
4910
|
+
const existing = grouped.get(key);
|
|
4911
|
+
if (existing)
|
|
4912
|
+
existing.count += blocker.count;
|
|
4913
|
+
else
|
|
4914
|
+
grouped.set(key, blocker);
|
|
4915
|
+
}
|
|
4916
|
+
return [...grouped.values()];
|
|
4917
|
+
};
|
|
4918
|
+
var createValidationStatuses = (commands, diagnostics) => {
|
|
4919
|
+
const scopes = [...failedCommandScopes(commands), ...errorDiagnosticScopes(diagnostics)];
|
|
4920
|
+
const sourceStatus = statusForScope(commands, diagnostics, scopes, "source-change");
|
|
4921
|
+
const workspaceStatus = hasScopeFailure(scopes, "workspace-config", diagnostics) || hasScopeFailure(scopes, "environment", diagnostics) ? "failed" : commands.length || diagnostics.length ? "passed" : "unknown";
|
|
4922
|
+
const overallStatus = hasScopeFailure(scopes, "source-change", diagnostics) ? "failed" : hasScopeFailure(scopes, "workspace-config", diagnostics) ? "blocked-by-workspace-config" : hasScopeFailure(scopes, "environment", diagnostics) ? "blocked-by-environment" : workflowStatus(diagnostics) === "failed" || commandStatus(commands) === "failed" ? "failed" : "passed";
|
|
4923
|
+
return { sourceStatus, workspaceStatus, overallStatus };
|
|
4924
|
+
};
|
|
5105
4925
|
var createWorkflowValidationRunReport = async ({
|
|
5106
4926
|
runId = createWorkflowRunId("validation"),
|
|
5107
4927
|
workflow,
|
|
@@ -5128,15 +4948,20 @@ var createWorkflowValidationRunReport = async ({
|
|
|
5128
4948
|
failureScope: result.failureScope
|
|
5129
4949
|
}
|
|
5130
4950
|
] : []);
|
|
4951
|
+
const reportDiagnostics = [...diagnostics, ...commandDiagnostics];
|
|
4952
|
+
const scopedDiagnostics = [...reportDiagnostics, ...baselineDiagnostics, ...workflowDiagnostics];
|
|
4953
|
+
const statuses = createValidationStatuses(results, scopedDiagnostics);
|
|
5131
4954
|
return {
|
|
5132
4955
|
schemaVersion: 1,
|
|
5133
4956
|
runId,
|
|
5134
4957
|
workflow,
|
|
5135
4958
|
mode: "validate",
|
|
5136
4959
|
source,
|
|
5137
|
-
status:
|
|
4960
|
+
status: statuses.overallStatus === "passed" ? "passed" : "failed",
|
|
4961
|
+
...statuses,
|
|
4962
|
+
knownBlockers: createKnownBlockers(results, scopedDiagnostics),
|
|
5138
4963
|
commands: results,
|
|
5139
|
-
diagnostics:
|
|
4964
|
+
diagnostics: reportDiagnostics,
|
|
5140
4965
|
baselineDiagnostics,
|
|
5141
4966
|
workflowDiagnostics,
|
|
5142
4967
|
repairActions: uniqueBy(repairActions, (action) => action.command),
|
|
@@ -5180,6 +5005,31 @@ var createDryRunWorkflowApplyReport = (plan) => {
|
|
|
5180
5005
|
plan
|
|
5181
5006
|
});
|
|
5182
5007
|
};
|
|
5008
|
+
var createRepairReport = ({
|
|
5009
|
+
command,
|
|
5010
|
+
kind,
|
|
5011
|
+
target = null,
|
|
5012
|
+
diagnostics = [],
|
|
5013
|
+
repairActions = [],
|
|
5014
|
+
nextActions = [],
|
|
5015
|
+
commands = [],
|
|
5016
|
+
generatedFiles = [],
|
|
5017
|
+
syncedAt
|
|
5018
|
+
}) => ({
|
|
5019
|
+
schemaVersion: 1,
|
|
5020
|
+
command,
|
|
5021
|
+
kind,
|
|
5022
|
+
target,
|
|
5023
|
+
status: workflowStatus(diagnostics) === "failed" || commandStatus(commands) === "failed" ? "failed" : "passed",
|
|
5024
|
+
diagnostics,
|
|
5025
|
+
repairActions: uniqueBy(repairActions, (action) => action.command),
|
|
5026
|
+
nextActions: uniqueBy(nextActions, (action) => action.command),
|
|
5027
|
+
commands,
|
|
5028
|
+
generatedFiles,
|
|
5029
|
+
...syncedAt ? { syncedAt } : {}
|
|
5030
|
+
});
|
|
5031
|
+
// pkgs/@akanjs/devkit/workflow/executor.ts
|
|
5032
|
+
import { capitalize as capitalize2 } from "akanjs/common";
|
|
5183
5033
|
var workflowStepKey = (workflow, stepId) => `${workflow}:${stepId}`;
|
|
5184
5034
|
var primitiveReportToWorkflowStepResult = (report) => ({
|
|
5185
5035
|
changedFiles: report.changedFiles,
|
|
@@ -5220,19 +5070,22 @@ var unsupportedInput = (input2, message) => ({
|
|
|
5220
5070
|
message
|
|
5221
5071
|
});
|
|
5222
5072
|
var addFieldUiSurfaceInspection = (plan) => {
|
|
5073
|
+
const app = workflowStringInput(plan.inputs.app);
|
|
5223
5074
|
const module = workflowStringInput(plan.inputs.module) ?? "<module>";
|
|
5224
5075
|
const field = workflowStringInput(plan.inputs.field) ?? "<field>";
|
|
5076
|
+
const typeName = workflowStringInput(plan.inputs.type);
|
|
5077
|
+
const isNumeric = typeName === "Int" || typeName === "Float" || typeName === "integer" || typeName === "float";
|
|
5225
5078
|
const moduleClassName = capitalize2(module);
|
|
5226
|
-
const target =
|
|
5079
|
+
const target = `${app ? `apps/${app}` : "*"}/lib/${module}/${moduleClassName}.Template.tsx`;
|
|
5227
5080
|
return {
|
|
5228
5081
|
recommendations: [
|
|
5229
5082
|
{
|
|
5230
5083
|
code: "add-field-ui-surface-review",
|
|
5231
5084
|
kind: "manual-action",
|
|
5232
5085
|
target,
|
|
5233
|
-
action: `Add ${field} to ${moduleClassName} UI surfaces only when the local Field.* pattern is clear.`,
|
|
5086
|
+
action: isNumeric ? `Review ${moduleClassName} Template/Unit/View/Store surfaces before adding ${field}; confirm the local Field.Text numeric parser/formatter, validation rule, and dictionary label pattern.` : `Add ${field} to ${moduleClassName} UI surfaces only when the local Field.* pattern is clear.`,
|
|
5234
5087
|
confidence: "medium",
|
|
5235
|
-
message: `Review Template/Unit/View/Store surfaces for ${module}.${field}; no UI file was modified automatically.`
|
|
5088
|
+
message: isNumeric ? `No UI file was modified automatically because a safe numeric input component pattern is not yet detected for ${module}.${field}.` : `Review Template/Unit/View/Store surfaces for ${module}.${field}; no UI file was modified automatically.`
|
|
5236
5089
|
}
|
|
5237
5090
|
],
|
|
5238
5091
|
nextActions: [
|
|
@@ -5324,112 +5177,6 @@ var createWorkflowStepRegistry = ({
|
|
|
5324
5177
|
[workflowStepKey("add-enum-field", "update-option")]: inspect
|
|
5325
5178
|
};
|
|
5326
5179
|
};
|
|
5327
|
-
var renderWorkflowApplyReport = (report) => [
|
|
5328
|
-
`# Workflow Apply: ${report.workflow}`,
|
|
5329
|
-
"",
|
|
5330
|
-
`- Mode: ${report.mode}`,
|
|
5331
|
-
`- Status: ${report.status}`,
|
|
5332
|
-
"",
|
|
5333
|
-
"## Changed Files",
|
|
5334
|
-
...report.changedFiles.length ? report.changedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
|
|
5335
|
-
"",
|
|
5336
|
-
"## Generated Files",
|
|
5337
|
-
...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
|
|
5338
|
-
"",
|
|
5339
|
-
"## Applied Commands",
|
|
5340
|
-
...report.appliedCommands.length ? report.appliedCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
|
|
5341
|
-
"",
|
|
5342
|
-
"## Recommended Validation Commands",
|
|
5343
|
-
...report.recommendedValidationCommands.length ? report.recommendedValidationCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
|
|
5344
|
-
"",
|
|
5345
|
-
"## Diagnostics",
|
|
5346
|
-
...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
5347
|
-
"",
|
|
5348
|
-
"## Recommendations",
|
|
5349
|
-
...report.recommendations.length ? report.recommendations.map((recommendation) => `- [${recommendation.kind}] ${recommendation.message}`) : ["- none"],
|
|
5350
|
-
"",
|
|
5351
|
-
"## Next Actions",
|
|
5352
|
-
...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
|
|
5353
|
-
""
|
|
5354
|
-
].join(`
|
|
5355
|
-
`);
|
|
5356
|
-
var renderWorkflowApply = (report, format = "markdown") => format === "json" ? jsonText(report) : renderWorkflowApplyReport(report);
|
|
5357
|
-
var renderWorkflowValidationRunReport = (report) => [
|
|
5358
|
-
`# Workflow Validation: ${report.workflow}`,
|
|
5359
|
-
"",
|
|
5360
|
-
`- Run: ${report.runId}`,
|
|
5361
|
-
`- Status: ${report.status}`,
|
|
5362
|
-
"",
|
|
5363
|
-
"## Commands",
|
|
5364
|
-
...report.commands.length ? report.commands.map((command) => {
|
|
5365
|
-
const scope = command.failureScope ? ` (${command.failureScope})` : "";
|
|
5366
|
-
return `- [${command.status}] \`${command.command}\`${scope}: ${command.reason}`;
|
|
5367
|
-
}) : ["- none"],
|
|
5368
|
-
"",
|
|
5369
|
-
"## Diagnostics",
|
|
5370
|
-
...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
5371
|
-
"",
|
|
5372
|
-
"## Repair Actions",
|
|
5373
|
-
...report.repairActions.length ? report.repairActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
|
|
5374
|
-
"",
|
|
5375
|
-
"## Next Actions",
|
|
5376
|
-
...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
|
|
5377
|
-
""
|
|
5378
|
-
].join(`
|
|
5379
|
-
`);
|
|
5380
|
-
var renderWorkflowValidation = (report, format = "markdown") => format === "json" ? jsonText(report) : renderWorkflowValidationRunReport(report);
|
|
5381
|
-
var renderWorkflowRunArtifact = (artifact, format = "markdown") => {
|
|
5382
|
-
if ("kind" in artifact)
|
|
5383
|
-
return renderRepairReport(artifact, format);
|
|
5384
|
-
if ("mode" in artifact && artifact.mode === "validate")
|
|
5385
|
-
return renderWorkflowValidation(artifact, format);
|
|
5386
|
-
if ("mode" in artifact && (artifact.mode === "apply" || artifact.mode === "dry-run")) {
|
|
5387
|
-
return renderWorkflowApply(artifact, format);
|
|
5388
|
-
}
|
|
5389
|
-
return jsonText(artifact);
|
|
5390
|
-
};
|
|
5391
|
-
var createRepairReport = ({
|
|
5392
|
-
command,
|
|
5393
|
-
kind,
|
|
5394
|
-
target = null,
|
|
5395
|
-
diagnostics = [],
|
|
5396
|
-
repairActions = [],
|
|
5397
|
-
nextActions = [],
|
|
5398
|
-
commands = [],
|
|
5399
|
-
generatedFiles = [],
|
|
5400
|
-
syncedAt
|
|
5401
|
-
}) => ({
|
|
5402
|
-
schemaVersion: 1,
|
|
5403
|
-
command,
|
|
5404
|
-
kind,
|
|
5405
|
-
target,
|
|
5406
|
-
status: workflowStatus(diagnostics) === "failed" || commandStatus(commands) === "failed" ? "failed" : "passed",
|
|
5407
|
-
diagnostics,
|
|
5408
|
-
repairActions: uniqueBy(repairActions, (action) => action.command),
|
|
5409
|
-
nextActions: uniqueBy(nextActions, (action) => action.command),
|
|
5410
|
-
commands,
|
|
5411
|
-
generatedFiles,
|
|
5412
|
-
...syncedAt ? { syncedAt } : {}
|
|
5413
|
-
});
|
|
5414
|
-
var renderRepairReportMarkdown = (report) => [
|
|
5415
|
-
`# Akan Repair: ${report.kind}`,
|
|
5416
|
-
"",
|
|
5417
|
-
`- Status: ${report.status}`,
|
|
5418
|
-
`- Target: ${report.target ?? "none"}`,
|
|
5419
|
-
"",
|
|
5420
|
-
"## Commands",
|
|
5421
|
-
...report.commands.length ? report.commands.map((command) => `- [${command.status}] \`${command.command}\`: ${command.reason}`) : ["- none"],
|
|
5422
|
-
"",
|
|
5423
|
-
"## Diagnostics",
|
|
5424
|
-
...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
5425
|
-
"",
|
|
5426
|
-
"## Next Actions",
|
|
5427
|
-
...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
|
|
5428
|
-
""
|
|
5429
|
-
].join(`
|
|
5430
|
-
`);
|
|
5431
|
-
var renderRepairReport = (report, format = "markdown") => format === "json" ? jsonText(report) : renderRepairReportMarkdown(report);
|
|
5432
|
-
|
|
5433
5180
|
class WorkflowExecutor {
|
|
5434
5181
|
registry;
|
|
5435
5182
|
constructor(registry) {
|
|
@@ -5496,6 +5243,10 @@ class WorkflowExecutor {
|
|
|
5496
5243
|
});
|
|
5497
5244
|
}
|
|
5498
5245
|
}
|
|
5246
|
+
// pkgs/@akanjs/devkit/workflow/plan.ts
|
|
5247
|
+
import { capitalize as capitalize3 } from "akanjs/common";
|
|
5248
|
+
|
|
5249
|
+
// pkgs/@akanjs/devkit/workflow/primitive.ts
|
|
5499
5250
|
var createPrimitiveWriteReport = ({
|
|
5500
5251
|
command,
|
|
5501
5252
|
status,
|
|
@@ -5525,165 +5276,532 @@ var renderPrimitiveWriteReport = (report) => [
|
|
|
5525
5276
|
"## Generated Files",
|
|
5526
5277
|
...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
|
|
5527
5278
|
"",
|
|
5528
|
-
"## Validation Commands",
|
|
5529
|
-
...report.validationCommands.length ? report.validationCommands.map((validation) => `- \`${validation.command}\`: ${validation.reason}`) : ["- none"],
|
|
5279
|
+
"## Validation Commands",
|
|
5280
|
+
...report.validationCommands.length ? report.validationCommands.map((validation) => `- \`${validation.command}\`: ${validation.reason}`) : ["- none"],
|
|
5281
|
+
"",
|
|
5282
|
+
"## Diagnostics",
|
|
5283
|
+
...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
5284
|
+
"",
|
|
5285
|
+
"## Next Actions",
|
|
5286
|
+
...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
|
|
5287
|
+
""
|
|
5288
|
+
].join(`
|
|
5289
|
+
`);
|
|
5290
|
+
var renderPrimitiveReport = (report, format = "markdown") => format === "json" ? jsonText(report) : renderPrimitiveWriteReport(report);
|
|
5291
|
+
|
|
5292
|
+
// pkgs/@akanjs/devkit/workflow/source.ts
|
|
5293
|
+
var getSysRoot = (sys2) => `${sys2.type}s/${sys2.name}`;
|
|
5294
|
+
var sourceFile = (sys2, path10, action, reason) => ({
|
|
5295
|
+
path: `${getSysRoot(sys2)}/${path10}`,
|
|
5296
|
+
action,
|
|
5297
|
+
reason
|
|
5298
|
+
});
|
|
5299
|
+
var generatedFilesForSync = (sys2, reason = "Generated files may change after sync.") => generatedFilePathsForTarget(getSysRoot(sys2), reason);
|
|
5300
|
+
var validationCommandsForTarget = (target) => [
|
|
5301
|
+
{ command: `akan sync ${target}`, reason: "Refresh generated Akan files from source conventions." },
|
|
5302
|
+
{ command: `akan lint ${target}`, reason: "Validate formatting, imports, and static lint rules." }
|
|
5303
|
+
];
|
|
5304
|
+
var nextActionsForTarget = (target) => [
|
|
5305
|
+
{ command: `akan sync ${target}`, reason: "Refresh generated Akan files after source changes." },
|
|
5306
|
+
{ command: `akan lint ${target}`, reason: "Validate the target after generated files are refreshed." }
|
|
5307
|
+
];
|
|
5308
|
+
var createPassedPrimitiveReport = ({
|
|
5309
|
+
command,
|
|
5310
|
+
changedFiles,
|
|
5311
|
+
generatedFiles,
|
|
5312
|
+
target,
|
|
5313
|
+
nextActions
|
|
5314
|
+
}) => createPrimitiveWriteReport({
|
|
5315
|
+
command,
|
|
5316
|
+
changedFiles,
|
|
5317
|
+
generatedFiles: generatedFiles ?? [],
|
|
5318
|
+
validationCommands: validationCommandsForTarget(target),
|
|
5319
|
+
diagnostics: [],
|
|
5320
|
+
nextActions: nextActions ?? nextActionsForTarget(target)
|
|
5321
|
+
});
|
|
5322
|
+
var scalarChangedFiles = (sys2, scalarName, files) => Object.values(files).map((file) => sourceFile(sys2, `lib/__scalar/${scalarName}/${file.filename}`, "create", "Scalar source file was created."));
|
|
5323
|
+
var titleize = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
5324
|
+
var lowerlize = (value) => `${value.slice(0, 1).toLowerCase()}${value.slice(1)}`;
|
|
5325
|
+
var normalizeFieldType = (typeName) => {
|
|
5326
|
+
const normalizedTypes = {
|
|
5327
|
+
string: "String",
|
|
5328
|
+
boolean: "Boolean",
|
|
5329
|
+
date: "Date",
|
|
5330
|
+
int: "Int",
|
|
5331
|
+
integer: "Int",
|
|
5332
|
+
float: "Float",
|
|
5333
|
+
double: "Float",
|
|
5334
|
+
decimal: "Float"
|
|
5335
|
+
};
|
|
5336
|
+
return normalizedTypes[typeName.toLowerCase()] ?? typeName;
|
|
5337
|
+
};
|
|
5338
|
+
var ensureBaseTypeImport = (content, typeName) => {
|
|
5339
|
+
if (typeName !== "Int" && typeName !== "Float")
|
|
5340
|
+
return content;
|
|
5341
|
+
if (new RegExp(`import \\{[^}]*\\b${typeName}\\b[^}]*\\} from "akanjs/base";`).test(content))
|
|
5342
|
+
return content;
|
|
5343
|
+
const baseImport = /import \{ ([^}]+) \} from "akanjs\/base";/.exec(content);
|
|
5344
|
+
if (baseImport) {
|
|
5345
|
+
const names = baseImport[1].split(",").map((name) => name.trim()).filter(Boolean);
|
|
5346
|
+
return content.replace(baseImport[0], `import { ${[...names, typeName].sort().join(", ")} } from "akanjs/base";`);
|
|
5347
|
+
}
|
|
5348
|
+
return `import { ${typeName} } from "akanjs/base";
|
|
5349
|
+
${content}`;
|
|
5350
|
+
};
|
|
5351
|
+
var numericDefault = (typeName, rawDefault) => {
|
|
5352
|
+
const trimmed = rawDefault.trim();
|
|
5353
|
+
if (!trimmed || !Number.isFinite(Number(trimmed)))
|
|
5354
|
+
return null;
|
|
5355
|
+
if (typeName === "Int" && !/^-?\d+$/.test(trimmed))
|
|
5356
|
+
return null;
|
|
5357
|
+
return trimmed;
|
|
5358
|
+
};
|
|
5359
|
+
var coerceFieldDefault = (typeName, defaultValue) => {
|
|
5360
|
+
if (defaultValue === undefined || defaultValue === null || defaultValue === "")
|
|
5361
|
+
return { expression: null };
|
|
5362
|
+
const normalizedType = normalizeFieldType(typeName);
|
|
5363
|
+
if (normalizedType === "Int" || normalizedType === "Float") {
|
|
5364
|
+
const expression = numericDefault(normalizedType, defaultValue);
|
|
5365
|
+
if (expression !== null)
|
|
5366
|
+
return { expression };
|
|
5367
|
+
return {
|
|
5368
|
+
expression: null,
|
|
5369
|
+
diagnostic: {
|
|
5370
|
+
severity: "error",
|
|
5371
|
+
code: "primitive-default-value-invalid",
|
|
5372
|
+
input: "default",
|
|
5373
|
+
failureScope: "source-change",
|
|
5374
|
+
message: `Default value for ${normalizedType} must be a numeric literal. Received: ${JSON.stringify(defaultValue)}.`
|
|
5375
|
+
}
|
|
5376
|
+
};
|
|
5377
|
+
}
|
|
5378
|
+
if (normalizedType === "Boolean") {
|
|
5379
|
+
const lowered = defaultValue.trim().toLowerCase();
|
|
5380
|
+
if (lowered === "true" || lowered === "false")
|
|
5381
|
+
return { expression: lowered };
|
|
5382
|
+
return {
|
|
5383
|
+
expression: null,
|
|
5384
|
+
diagnostic: {
|
|
5385
|
+
severity: "error",
|
|
5386
|
+
code: "primitive-default-value-invalid",
|
|
5387
|
+
input: "default",
|
|
5388
|
+
failureScope: "source-change",
|
|
5389
|
+
message: `Default value for Boolean must be true or false. Received: ${JSON.stringify(defaultValue)}.`
|
|
5390
|
+
}
|
|
5391
|
+
};
|
|
5392
|
+
}
|
|
5393
|
+
return { expression: JSON.stringify(defaultValue) };
|
|
5394
|
+
};
|
|
5395
|
+
var fieldExpression = (typeName, defaultValue) => {
|
|
5396
|
+
const typeExpression = normalizeFieldType(typeName);
|
|
5397
|
+
const defaultExpression = coerceFieldDefault(typeExpression, defaultValue).expression;
|
|
5398
|
+
const defaultOption = defaultExpression ? `, { default: ${defaultExpression} }` : "";
|
|
5399
|
+
return `field(${typeExpression}${defaultOption})`;
|
|
5400
|
+
};
|
|
5401
|
+
var insertIntoObject = (content, className, line) => {
|
|
5402
|
+
const classIndex = content.indexOf(`export class ${className} extends via`);
|
|
5403
|
+
if (classIndex < 0)
|
|
5404
|
+
return null;
|
|
5405
|
+
const objectEndIndex = content.indexOf("}))", classIndex);
|
|
5406
|
+
if (objectEndIndex < 0)
|
|
5407
|
+
return null;
|
|
5408
|
+
const prefix = content.slice(0, objectEndIndex);
|
|
5409
|
+
const suffix = content.slice(objectEndIndex);
|
|
5410
|
+
const insertion = prefix.endsWith(`
|
|
5411
|
+
`) ? ` ${line}
|
|
5412
|
+
` : `
|
|
5413
|
+
${line}
|
|
5414
|
+
`;
|
|
5415
|
+
return `${prefix}${insertion}${suffix}`;
|
|
5416
|
+
};
|
|
5417
|
+
var ensureEnumImport = (content) => {
|
|
5418
|
+
if (content.includes("enumOf"))
|
|
5419
|
+
return content;
|
|
5420
|
+
const baseImport = /import \{ ([^}]+) \} from "akanjs\/base";/.exec(content);
|
|
5421
|
+
if (baseImport) {
|
|
5422
|
+
const names = baseImport[1]?.split(",").map((name) => name.trim()) ?? [];
|
|
5423
|
+
return content.replace(baseImport[0], `import { ${[...names, "enumOf"].sort().join(", ")} } from "akanjs/base";`);
|
|
5424
|
+
}
|
|
5425
|
+
return `import { enumOf } from "akanjs/base";
|
|
5426
|
+
${content}`;
|
|
5427
|
+
};
|
|
5428
|
+
var insertEnumClass = (content, enumClassName, enumName, values) => {
|
|
5429
|
+
if (content.includes(`export class ${enumClassName} extends enumOf`))
|
|
5430
|
+
return content;
|
|
5431
|
+
const enumClass = `export class ${enumClassName} extends enumOf("${enumName}", [
|
|
5432
|
+
${values.map((value) => ` ${JSON.stringify(value)},`).join(`
|
|
5433
|
+
`)}
|
|
5434
|
+
] as const) {}
|
|
5435
|
+
|
|
5436
|
+
`;
|
|
5437
|
+
const firstClassIndex = content.indexOf("export class ");
|
|
5438
|
+
if (firstClassIndex < 0)
|
|
5439
|
+
return `${content}
|
|
5440
|
+
${enumClass}`;
|
|
5441
|
+
return `${content.slice(0, firstClassIndex)}${enumClass}${content.slice(firstClassIndex)}`;
|
|
5442
|
+
};
|
|
5443
|
+
var insertDictionaryModelField = (content, moduleClassName, fieldName) => {
|
|
5444
|
+
if (new RegExp(`\\b${fieldName}\\s*:`).test(content))
|
|
5445
|
+
return content;
|
|
5446
|
+
const label = titleize(fieldName);
|
|
5447
|
+
const modelIndex = content.indexOf(`.model<${moduleClassName}>((t) => ({`);
|
|
5448
|
+
if (modelIndex < 0)
|
|
5449
|
+
return null;
|
|
5450
|
+
const objectEndIndex = content.indexOf(" }))", modelIndex);
|
|
5451
|
+
if (objectEndIndex < 0)
|
|
5452
|
+
return null;
|
|
5453
|
+
return `${content.slice(0, objectEndIndex)} ${fieldName}: t([${JSON.stringify(label)}, ${JSON.stringify(label)}]).desc([${JSON.stringify(label)}, ${JSON.stringify(label)}]),
|
|
5454
|
+
${content.slice(objectEndIndex)}`;
|
|
5455
|
+
};
|
|
5456
|
+
var ensureConstantTypeImport = (content, constantPath, typeName) => {
|
|
5457
|
+
if (new RegExp(`import type \\{[^}]*\\b${typeName}\\b[^}]*\\} from "${constantPath}";`).test(content))
|
|
5458
|
+
return content;
|
|
5459
|
+
const importPattern = new RegExp(`import type \\{ ([^}]+) \\} from "${constantPath}";`);
|
|
5460
|
+
const existingImport = content.match(importPattern);
|
|
5461
|
+
if (existingImport !== null) {
|
|
5462
|
+
const names = existingImport[1]?.split(",").map((name) => name.trim()) ?? [];
|
|
5463
|
+
return content.replace(existingImport[0], `import type { ${[...names, typeName].sort().join(", ")} } from "${constantPath}";`);
|
|
5464
|
+
}
|
|
5465
|
+
return `import type { ${typeName} } from "${constantPath}";
|
|
5466
|
+
${content}`;
|
|
5467
|
+
};
|
|
5468
|
+
var insertDictionaryEnum = (content, enumClassName, enumName, values) => {
|
|
5469
|
+
if (content.includes(`.enum<${enumClassName}>("${enumName}"`))
|
|
5470
|
+
return content;
|
|
5471
|
+
const enumBlock = ` .enum<${enumClassName}>("${enumName}", (t) => ({
|
|
5472
|
+
${values.map((value) => ` ${value}: t([${JSON.stringify(titleize(value))}, ${JSON.stringify(titleize(value))}]),`).join(`
|
|
5473
|
+
`)}
|
|
5474
|
+
}))
|
|
5475
|
+
`;
|
|
5476
|
+
const chainEndIndex = content.lastIndexOf(";");
|
|
5477
|
+
const insertBeforeIndex = [content.indexOf(".error("), content.indexOf(".translate("), chainEndIndex].filter((index) => index >= 0).sort((a, b) => a - b)[0];
|
|
5478
|
+
if (insertBeforeIndex === undefined)
|
|
5479
|
+
return null;
|
|
5480
|
+
return `${content.slice(0, insertBeforeIndex)}${enumBlock}${content.slice(insertBeforeIndex)}`;
|
|
5481
|
+
};
|
|
5482
|
+
var parseValues = (value) => value?.split(",").map((item) => item.trim()).filter(Boolean) ?? [];
|
|
5483
|
+
|
|
5484
|
+
// pkgs/@akanjs/devkit/workflow/plan.ts
|
|
5485
|
+
var surfaceModes = new Set(["infer", "include", "skip"]);
|
|
5486
|
+
var parseStringList = (value) => {
|
|
5487
|
+
if (Array.isArray(value)) {
|
|
5488
|
+
const values = value.filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
5489
|
+
return values.length === value.length ? values : null;
|
|
5490
|
+
}
|
|
5491
|
+
if (typeof value !== "string")
|
|
5492
|
+
return null;
|
|
5493
|
+
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
5494
|
+
};
|
|
5495
|
+
var normalizeInputValue = (name, spec, value) => {
|
|
5496
|
+
if (spec.type === "string")
|
|
5497
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
5498
|
+
if (spec.type === "string-list") {
|
|
5499
|
+
const values = parseStringList(value);
|
|
5500
|
+
return values && values.length > 0 ? values : null;
|
|
5501
|
+
}
|
|
5502
|
+
if (typeof value === "string" && surfaceModes.has(value))
|
|
5503
|
+
return value;
|
|
5504
|
+
throw new Error(`Unsupported workflow input value for ${name}`);
|
|
5505
|
+
};
|
|
5506
|
+
var listWorkflowSpecs = (specs) => [...specs].sort((a, b) => a.name.localeCompare(b.name));
|
|
5507
|
+
var getWorkflowSpec = (specs, name) => specs.find((spec) => spec.name === name) ?? null;
|
|
5508
|
+
var compactWorkflowInputs = (inputs) => Object.fromEntries(Object.entries(inputs).filter(([, value]) => value !== null && value !== ""));
|
|
5509
|
+
var addFieldComponentForType = (typeName) => {
|
|
5510
|
+
const normalizedType = normalizeFieldType(typeName);
|
|
5511
|
+
if (normalizedType === "Boolean")
|
|
5512
|
+
return "Field.ToggleSelect";
|
|
5513
|
+
if (normalizedType === "Date")
|
|
5514
|
+
return "Field.Date";
|
|
5515
|
+
if (normalizedType === "Int" || normalizedType === "Float")
|
|
5516
|
+
return "manual numeric input review";
|
|
5517
|
+
if (typeName.toLowerCase() === "enum")
|
|
5518
|
+
return "Field.ToggleSelect";
|
|
5519
|
+
return "Field.Text";
|
|
5520
|
+
};
|
|
5521
|
+
var createAddFieldRecommendations = (inputs) => {
|
|
5522
|
+
const app = typeof inputs.app === "string" ? inputs.app : "<app-or-lib>";
|
|
5523
|
+
const module = typeof inputs.module === "string" ? inputs.module : "<module>";
|
|
5524
|
+
const field = typeof inputs.field === "string" ? inputs.field : "<field>";
|
|
5525
|
+
const typeName = typeof inputs.type === "string" ? inputs.type : null;
|
|
5526
|
+
if (!typeName)
|
|
5527
|
+
return [];
|
|
5528
|
+
const normalizedType = typeName.toLowerCase() === "enum" ? "enum" : normalizeFieldType(typeName);
|
|
5529
|
+
const constantPath = `*/lib/${module}/${module}.constant.ts`;
|
|
5530
|
+
const dictionaryPath = `*/lib/${module}/${module}.dictionary.ts`;
|
|
5531
|
+
const templatePath = `*/lib/${module}/${capitalize3(module)}.Template.tsx`;
|
|
5532
|
+
return [
|
|
5533
|
+
...normalizedType === "Int" || normalizedType === "Float" ? [
|
|
5534
|
+
{
|
|
5535
|
+
code: "add-field-import",
|
|
5536
|
+
kind: "import",
|
|
5537
|
+
target: constantPath,
|
|
5538
|
+
confidence: "high",
|
|
5539
|
+
message: `Import ${normalizedType} from "akanjs/base" before writing field(${normalizedType}).`
|
|
5540
|
+
}
|
|
5541
|
+
] : [],
|
|
5542
|
+
{
|
|
5543
|
+
code: "add-field-placement-constant",
|
|
5544
|
+
kind: "placement",
|
|
5545
|
+
target: constantPath,
|
|
5546
|
+
confidence: "high",
|
|
5547
|
+
message: `Insert ${field}: field(${normalizedType}) in ${capitalize3(module)}Input.`
|
|
5548
|
+
},
|
|
5549
|
+
{
|
|
5550
|
+
code: "add-field-placement-dictionary",
|
|
5551
|
+
kind: "placement",
|
|
5552
|
+
target: dictionaryPath,
|
|
5553
|
+
confidence: "high",
|
|
5554
|
+
message: `Add dictionary labels for ${module}.${field}.`
|
|
5555
|
+
},
|
|
5556
|
+
{
|
|
5557
|
+
code: "add-field-component",
|
|
5558
|
+
kind: "ui-component",
|
|
5559
|
+
target: templatePath,
|
|
5560
|
+
confidence: normalizedType === "Int" || normalizedType === "Float" ? "low" : "high",
|
|
5561
|
+
action: normalizedType === "Int" || normalizedType === "Float" ? "Do not auto-edit UI. Check local Template/Unit/View patterns for Field.Text parsing, formatting, validation rules, and dictionary labels before adding a numeric input." : undefined,
|
|
5562
|
+
message: normalizedType === "Int" || normalizedType === "Float" ? `Numeric UI for ${field} (${normalizedType}) requires manual review; a safe numeric input component pattern is not yet detected.` : `Recommended UI component for ${field} (${normalizedType}): ${addFieldComponentForType(typeName)}.`
|
|
5563
|
+
},
|
|
5564
|
+
{
|
|
5565
|
+
code: "add-field-ui-manual-review",
|
|
5566
|
+
kind: "manual-action",
|
|
5567
|
+
target: templatePath,
|
|
5568
|
+
action: `Review ${app}:${module} Template/Unit/View/Store surfaces and add ${field} only where the existing pattern is clear. For numeric fields, confirm parser/formatter behavior and validation before using Field.Text.`,
|
|
5569
|
+
confidence: "medium",
|
|
5570
|
+
message: normalizedType === "Int" || normalizedType === "Float" ? "UI surface edits stay manual because a safe numeric input component pattern is not yet detected." : "UI surface edits are planned as manual-review unless a safe existing field-list pattern is detected."
|
|
5571
|
+
}
|
|
5572
|
+
];
|
|
5573
|
+
};
|
|
5574
|
+
var createWorkflowPlanRecommendations = (spec, inputs) => [
|
|
5575
|
+
{
|
|
5576
|
+
code: "workflow-apply-first",
|
|
5577
|
+
kind: "auto-apply",
|
|
5578
|
+
confidence: "high",
|
|
5579
|
+
action: "Call apply_workflow with the MCP planPath before editing source files directly.",
|
|
5580
|
+
message: `Apply the ${spec.name} plan through apply_workflow when MCP returns planPath or next.tool=apply_workflow.`
|
|
5581
|
+
},
|
|
5582
|
+
{
|
|
5583
|
+
code: "workflow-validate-apply-report",
|
|
5584
|
+
kind: "validation",
|
|
5585
|
+
confidence: "high",
|
|
5586
|
+
action: "After apply_workflow, call run_validation with validationTarget when present; otherwise use applyReportPath.",
|
|
5587
|
+
message: "Validate the apply report artifact so diagnostics and recommendations follow the actual apply result."
|
|
5588
|
+
},
|
|
5589
|
+
...spec.name === "add-field" ? createAddFieldRecommendations(inputs) : []
|
|
5590
|
+
];
|
|
5591
|
+
var createWorkflowPlan = (spec, rawInputs) => {
|
|
5592
|
+
const inputs = {};
|
|
5593
|
+
const diagnostics = [];
|
|
5594
|
+
for (const [name, inputSpec] of Object.entries(spec.inputs)) {
|
|
5595
|
+
const rawValue = rawInputs[name];
|
|
5596
|
+
if (rawValue === undefined || rawValue === null || rawValue === "") {
|
|
5597
|
+
if (inputSpec.required) {
|
|
5598
|
+
diagnostics.push({
|
|
5599
|
+
severity: "error",
|
|
5600
|
+
code: "workflow-input-missing",
|
|
5601
|
+
input: name,
|
|
5602
|
+
message: `Workflow ${spec.name} requires input "${name}".`
|
|
5603
|
+
});
|
|
5604
|
+
}
|
|
5605
|
+
continue;
|
|
5606
|
+
}
|
|
5607
|
+
const value = normalizeInputValue(name, inputSpec, rawValue);
|
|
5608
|
+
if (value === null) {
|
|
5609
|
+
diagnostics.push({
|
|
5610
|
+
severity: "error",
|
|
5611
|
+
code: "workflow-input-invalid",
|
|
5612
|
+
input: name,
|
|
5613
|
+
message: `Workflow ${spec.name} input "${name}" must be ${inputSpec.type}.`
|
|
5614
|
+
});
|
|
5615
|
+
continue;
|
|
5616
|
+
}
|
|
5617
|
+
if (inputSpec.allowedValues && typeof value === "string" && !inputSpec.allowedValues.includes(value)) {
|
|
5618
|
+
diagnostics.push({
|
|
5619
|
+
severity: "error",
|
|
5620
|
+
code: "workflow-input-not-allowed",
|
|
5621
|
+
input: name,
|
|
5622
|
+
message: `Workflow ${spec.name} input "${name}" must be one of: ${inputSpec.allowedValues.join(", ")}.`
|
|
5623
|
+
});
|
|
5624
|
+
continue;
|
|
5625
|
+
}
|
|
5626
|
+
inputs[name] = value;
|
|
5627
|
+
}
|
|
5628
|
+
const fieldType = inputs.type;
|
|
5629
|
+
if (spec.name === "add-field" && typeof fieldType === "string" && (fieldType.toLowerCase() === "number" || fieldType.toLowerCase() === "numeric")) {
|
|
5630
|
+
diagnostics.push({
|
|
5631
|
+
severity: "error",
|
|
5632
|
+
code: "primitive-field-type-unsupported",
|
|
5633
|
+
input: "type",
|
|
5634
|
+
message: `Field type "${fieldType}" is ambiguous in Akan. Use Int for integer fields or Float for decimal fields.`
|
|
5635
|
+
});
|
|
5636
|
+
}
|
|
5637
|
+
return {
|
|
5638
|
+
schemaVersion: 1,
|
|
5639
|
+
workflow: spec.name,
|
|
5640
|
+
mode: "plan",
|
|
5641
|
+
inputs,
|
|
5642
|
+
optionalSurfaces: spec.optionalSurfaces ?? {},
|
|
5643
|
+
steps: spec.steps,
|
|
5644
|
+
predictedChanges: spec.predictedChanges,
|
|
5645
|
+
validation: spec.validation,
|
|
5646
|
+
diagnostics,
|
|
5647
|
+
recommendations: createWorkflowPlanRecommendations(spec, inputs),
|
|
5648
|
+
requiresApproval: true
|
|
5649
|
+
};
|
|
5650
|
+
};
|
|
5651
|
+
// pkgs/@akanjs/devkit/workflow/render.ts
|
|
5652
|
+
var renderWorkflowList = (specs) => [
|
|
5653
|
+
"# Akan Workflows",
|
|
5654
|
+
"",
|
|
5655
|
+
...specs.flatMap((spec) => [`- \`${spec.name}\`: ${spec.description}`, ` - When: ${spec.whenToUse}`]),
|
|
5656
|
+
""
|
|
5657
|
+
].join(`
|
|
5658
|
+
`);
|
|
5659
|
+
var renderWorkflowExplain = (spec) => [
|
|
5660
|
+
`# Workflow: ${spec.name}`,
|
|
5661
|
+
"",
|
|
5662
|
+
spec.description,
|
|
5663
|
+
"",
|
|
5664
|
+
"## When To Use",
|
|
5665
|
+
spec.whenToUse,
|
|
5666
|
+
"",
|
|
5667
|
+
"## Inputs",
|
|
5668
|
+
...Object.entries(spec.inputs).map(([name, input2]) => `- \`${name}\`${input2.required ? " (required)" : ""}: ${input2.description}${input2.allowedValues ? ` Allowed: ${input2.allowedValues.join(", ")}.` : ""}`),
|
|
5669
|
+
"",
|
|
5670
|
+
"## Optional Surfaces",
|
|
5671
|
+
...Object.entries(spec.optionalSurfaces ?? {}).map(([name, mode]) => `- \`${name}\`: ${mode}`),
|
|
5672
|
+
"",
|
|
5673
|
+
"## Steps",
|
|
5674
|
+
...spec.steps.map((step, index) => `${index + 1}. \`${step.id}\` (${step.tool}): ${step.description}`),
|
|
5675
|
+
"",
|
|
5676
|
+
"## Validation",
|
|
5677
|
+
...spec.validation.map((validation) => `- \`${validation.command}\`: ${validation.reason}`),
|
|
5678
|
+
""
|
|
5679
|
+
].join(`
|
|
5680
|
+
`);
|
|
5681
|
+
var renderWorkflowPlan = (plan) => [
|
|
5682
|
+
`# Workflow Plan: ${plan.workflow}`,
|
|
5683
|
+
"",
|
|
5684
|
+
`- Mode: ${plan.mode}`,
|
|
5685
|
+
`- Requires approval: ${plan.requiresApproval}`,
|
|
5686
|
+
"",
|
|
5687
|
+
"## Inputs",
|
|
5688
|
+
...Object.entries(plan.inputs).map(([name, value]) => `- \`${name}\`: ${Array.isArray(value) ? value.join(", ") : value}`),
|
|
5689
|
+
"",
|
|
5690
|
+
"## Optional Surfaces",
|
|
5691
|
+
...Object.entries(plan.optionalSurfaces).map(([name, mode]) => `- \`${name}\`: ${mode}`),
|
|
5692
|
+
"",
|
|
5693
|
+
"## Steps",
|
|
5694
|
+
...plan.steps.map((step, index) => `${index + 1}. \`${step.id}\` (${step.tool}): ${step.description}`),
|
|
5695
|
+
"",
|
|
5696
|
+
"## Predicted Changes",
|
|
5697
|
+
...plan.predictedChanges.map((change) => {
|
|
5698
|
+
const scope = change.applyScope ? ` (${change.applyScope})` : "";
|
|
5699
|
+
return `- \`${change.action}\`${scope} ${change.target}: ${change.reason}`;
|
|
5700
|
+
}),
|
|
5701
|
+
"",
|
|
5702
|
+
"## Validation",
|
|
5703
|
+
...plan.validation.map((validation) => `- \`${validation.command}\`: ${validation.reason}`),
|
|
5704
|
+
"",
|
|
5705
|
+
"## Diagnostics",
|
|
5706
|
+
...plan.diagnostics.length ? plan.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
5707
|
+
"",
|
|
5708
|
+
"## Recommendations",
|
|
5709
|
+
...plan.recommendations.length ? plan.recommendations.map((recommendation) => `- [${recommendation.kind}] ${recommendation.message}`) : ["- none"],
|
|
5710
|
+
""
|
|
5711
|
+
].join(`
|
|
5712
|
+
`);
|
|
5713
|
+
var renderWorkflowApplyReport = (report) => [
|
|
5714
|
+
`# Workflow Apply: ${report.workflow}`,
|
|
5715
|
+
"",
|
|
5716
|
+
`- Mode: ${report.mode}`,
|
|
5717
|
+
`- Status: ${report.status}`,
|
|
5718
|
+
"",
|
|
5719
|
+
"## Changed Files",
|
|
5720
|
+
...report.changedFiles.length ? report.changedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
|
|
5721
|
+
"",
|
|
5722
|
+
"## Generated Files",
|
|
5723
|
+
...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
|
|
5724
|
+
"",
|
|
5725
|
+
"## Applied Commands",
|
|
5726
|
+
...report.appliedCommands.length ? report.appliedCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
|
|
5727
|
+
"",
|
|
5728
|
+
"## Recommended Validation Commands",
|
|
5729
|
+
...report.recommendedValidationCommands.length ? report.recommendedValidationCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
|
|
5530
5730
|
"",
|
|
5531
5731
|
"## Diagnostics",
|
|
5532
5732
|
...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
5533
5733
|
"",
|
|
5734
|
+
"## Recommendations",
|
|
5735
|
+
...report.recommendations.length ? report.recommendations.map((recommendation) => `- [${recommendation.kind}] ${recommendation.message}`) : ["- none"],
|
|
5736
|
+
"",
|
|
5534
5737
|
"## Next Actions",
|
|
5535
5738
|
...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
|
|
5536
5739
|
""
|
|
5537
5740
|
].join(`
|
|
5538
5741
|
`);
|
|
5539
|
-
var
|
|
5540
|
-
var
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
}
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
]
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
var
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
return
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
var fieldExpression = (typeName, defaultValue) => {
|
|
5600
|
-
const typeExpression = normalizeFieldType(typeName);
|
|
5601
|
-
const defaultOption = defaultValue ? `, { default: ${JSON.stringify(defaultValue)} }` : "";
|
|
5602
|
-
return `field(${typeExpression}${defaultOption})`;
|
|
5603
|
-
};
|
|
5604
|
-
var insertIntoObject = (content, className, line) => {
|
|
5605
|
-
const classIndex = content.indexOf(`export class ${className} extends via`);
|
|
5606
|
-
if (classIndex < 0)
|
|
5607
|
-
return null;
|
|
5608
|
-
const objectEndIndex = content.indexOf("}))", classIndex);
|
|
5609
|
-
if (objectEndIndex < 0)
|
|
5610
|
-
return null;
|
|
5611
|
-
const prefix = content.slice(0, objectEndIndex);
|
|
5612
|
-
const suffix = content.slice(objectEndIndex);
|
|
5613
|
-
const insertion = prefix.endsWith(`
|
|
5614
|
-
`) ? ` ${line}
|
|
5615
|
-
` : `
|
|
5616
|
-
${line}
|
|
5617
|
-
`;
|
|
5618
|
-
return `${prefix}${insertion}${suffix}`;
|
|
5619
|
-
};
|
|
5620
|
-
var ensureEnumImport = (content) => {
|
|
5621
|
-
if (content.includes("enumOf"))
|
|
5622
|
-
return content;
|
|
5623
|
-
const baseImport = /import \{ ([^}]+) \} from "akanjs\/base";/.exec(content);
|
|
5624
|
-
if (baseImport) {
|
|
5625
|
-
const names = baseImport[1]?.split(",").map((name) => name.trim()) ?? [];
|
|
5626
|
-
return content.replace(baseImport[0], `import { ${[...names, "enumOf"].sort().join(", ")} } from "akanjs/base";`);
|
|
5627
|
-
}
|
|
5628
|
-
return `import { enumOf } from "akanjs/base";
|
|
5629
|
-
${content}`;
|
|
5630
|
-
};
|
|
5631
|
-
var insertEnumClass = (content, enumClassName, enumName, values) => {
|
|
5632
|
-
if (content.includes(`export class ${enumClassName} extends enumOf`))
|
|
5633
|
-
return content;
|
|
5634
|
-
const enumClass = `export class ${enumClassName} extends enumOf("${enumName}", [
|
|
5635
|
-
${values.map((value) => ` ${JSON.stringify(value)},`).join(`
|
|
5636
|
-
`)}
|
|
5637
|
-
] as const) {}
|
|
5638
|
-
|
|
5639
|
-
`;
|
|
5640
|
-
const firstClassIndex = content.indexOf("export class ");
|
|
5641
|
-
if (firstClassIndex < 0)
|
|
5642
|
-
return `${content}
|
|
5643
|
-
${enumClass}`;
|
|
5644
|
-
return `${content.slice(0, firstClassIndex)}${enumClass}${content.slice(firstClassIndex)}`;
|
|
5645
|
-
};
|
|
5646
|
-
var insertDictionaryModelField = (content, moduleClassName, fieldName) => {
|
|
5647
|
-
if (new RegExp(`\\b${fieldName}\\s*:`).test(content))
|
|
5648
|
-
return content;
|
|
5649
|
-
const label = titleize(fieldName);
|
|
5650
|
-
const modelIndex = content.indexOf(`.model<${moduleClassName}>((t) => ({`);
|
|
5651
|
-
if (modelIndex < 0)
|
|
5652
|
-
return null;
|
|
5653
|
-
const objectEndIndex = content.indexOf(" }))", modelIndex);
|
|
5654
|
-
if (objectEndIndex < 0)
|
|
5655
|
-
return null;
|
|
5656
|
-
return `${content.slice(0, objectEndIndex)} ${fieldName}: t([${JSON.stringify(label)}, ${JSON.stringify(label)}]).desc([${JSON.stringify(label)}, ${JSON.stringify(label)}]),
|
|
5657
|
-
${content.slice(objectEndIndex)}`;
|
|
5658
|
-
};
|
|
5659
|
-
var ensureConstantTypeImport = (content, constantPath, typeName) => {
|
|
5660
|
-
if (new RegExp(`import type \\{[^}]*\\b${typeName}\\b[^}]*\\} from "${constantPath}";`).test(content))
|
|
5661
|
-
return content;
|
|
5662
|
-
const importPattern = new RegExp(`import type \\{ ([^}]+) \\} from "${constantPath}";`);
|
|
5663
|
-
const existingImport = content.match(importPattern);
|
|
5664
|
-
if (existingImport !== null) {
|
|
5665
|
-
const names = existingImport[1]?.split(",").map((name) => name.trim()) ?? [];
|
|
5666
|
-
return content.replace(existingImport[0], `import type { ${[...names, typeName].sort().join(", ")} } from "${constantPath}";`);
|
|
5742
|
+
var renderWorkflowApply = (report, format = "markdown") => format === "json" ? jsonText(report) : renderWorkflowApplyReport(report);
|
|
5743
|
+
var renderWorkflowValidationRunReport = (report) => [
|
|
5744
|
+
`# Workflow Validation: ${report.workflow}`,
|
|
5745
|
+
"",
|
|
5746
|
+
`- Run: ${report.runId}`,
|
|
5747
|
+
`- Status: ${report.status}`,
|
|
5748
|
+
`- Source status: ${report.sourceStatus}`,
|
|
5749
|
+
`- Workspace status: ${report.workspaceStatus}`,
|
|
5750
|
+
`- Overall status: ${report.overallStatus}`,
|
|
5751
|
+
"",
|
|
5752
|
+
"## Known Blockers",
|
|
5753
|
+
...report.knownBlockers.length ? report.knownBlockers.map((blocker) => {
|
|
5754
|
+
const command = blocker.command ? ` \`${blocker.command}\`` : "";
|
|
5755
|
+
const count = blocker.count > 1 ? ` (${blocker.count}x)` : "";
|
|
5756
|
+
return `- [${blocker.failureScope}]${command}${count}: ${blocker.message}`;
|
|
5757
|
+
}) : ["- none"],
|
|
5758
|
+
"",
|
|
5759
|
+
"## Commands",
|
|
5760
|
+
...report.commands.length ? report.commands.map((command) => {
|
|
5761
|
+
const scope = command.failureScope ? ` (${command.failureScope})` : "";
|
|
5762
|
+
return `- [${command.status}] \`${command.command}\`${scope}: ${command.reason}`;
|
|
5763
|
+
}) : ["- none"],
|
|
5764
|
+
"",
|
|
5765
|
+
"## Diagnostics",
|
|
5766
|
+
...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
5767
|
+
"",
|
|
5768
|
+
"## Repair Actions",
|
|
5769
|
+
...report.repairActions.length ? report.repairActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
|
|
5770
|
+
"",
|
|
5771
|
+
"## Next Actions",
|
|
5772
|
+
...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
|
|
5773
|
+
""
|
|
5774
|
+
].join(`
|
|
5775
|
+
`);
|
|
5776
|
+
var renderWorkflowValidation = (report, format = "markdown") => format === "json" ? jsonText(report) : renderWorkflowValidationRunReport(report);
|
|
5777
|
+
var renderRepairReportMarkdown = (report) => [
|
|
5778
|
+
`# Akan Repair: ${report.kind}`,
|
|
5779
|
+
"",
|
|
5780
|
+
`- Status: ${report.status}`,
|
|
5781
|
+
`- Target: ${report.target ?? "none"}`,
|
|
5782
|
+
"",
|
|
5783
|
+
"## Commands",
|
|
5784
|
+
...report.commands.length ? report.commands.map((command) => `- [${command.status}] \`${command.command}\`: ${command.reason}`) : ["- none"],
|
|
5785
|
+
"",
|
|
5786
|
+
"## Diagnostics",
|
|
5787
|
+
...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
5788
|
+
"",
|
|
5789
|
+
"## Next Actions",
|
|
5790
|
+
...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
|
|
5791
|
+
""
|
|
5792
|
+
].join(`
|
|
5793
|
+
`);
|
|
5794
|
+
var renderRepairReport = (report, format = "markdown") => format === "json" ? jsonText(report) : renderRepairReportMarkdown(report);
|
|
5795
|
+
var renderWorkflowRunArtifact = (artifact, format = "markdown") => {
|
|
5796
|
+
if ("kind" in artifact)
|
|
5797
|
+
return renderRepairReport(artifact, format);
|
|
5798
|
+
if ("mode" in artifact && artifact.mode === "validate")
|
|
5799
|
+
return renderWorkflowValidation(artifact, format);
|
|
5800
|
+
if ("mode" in artifact && (artifact.mode === "apply" || artifact.mode === "dry-run")) {
|
|
5801
|
+
return renderWorkflowApply(artifact, format);
|
|
5667
5802
|
}
|
|
5668
|
-
return
|
|
5669
|
-
${content}`;
|
|
5670
|
-
};
|
|
5671
|
-
var insertDictionaryEnum = (content, enumClassName, enumName, values) => {
|
|
5672
|
-
if (content.includes(`.enum<${enumClassName}>("${enumName}"`))
|
|
5673
|
-
return content;
|
|
5674
|
-
const enumBlock = ` .enum<${enumClassName}>("${enumName}", (t) => ({
|
|
5675
|
-
${values.map((value) => ` ${value}: t([${JSON.stringify(titleize(value))}, ${JSON.stringify(titleize(value))}]),`).join(`
|
|
5676
|
-
`)}
|
|
5677
|
-
}))
|
|
5678
|
-
`;
|
|
5679
|
-
const chainEndIndex = content.lastIndexOf(";");
|
|
5680
|
-
const insertBeforeIndex = [content.indexOf(".error("), content.indexOf(".translate("), chainEndIndex].filter((index) => index >= 0).sort((a, b) => a - b)[0];
|
|
5681
|
-
if (insertBeforeIndex === undefined)
|
|
5682
|
-
return null;
|
|
5683
|
-
return `${content.slice(0, insertBeforeIndex)}${enumBlock}${content.slice(insertBeforeIndex)}`;
|
|
5803
|
+
return jsonText(artifact);
|
|
5684
5804
|
};
|
|
5685
|
-
var parseValues = (value) => value?.split(",").map((item) => item.trim()).filter(Boolean) ?? [];
|
|
5686
|
-
|
|
5687
5805
|
// pkgs/@akanjs/devkit/akanContext.ts
|
|
5688
5806
|
var resourceList = [
|
|
5689
5807
|
{ uri: "akan://docs/framework", name: "Akan framework guide", mimeType: "text/markdown" },
|
|
@@ -5819,17 +5937,17 @@ var safeReadJson = async (filePath) => {
|
|
|
5819
5937
|
var isWorkflowPlan = (value) => typeof value === "object" && value !== null && ("schemaVersion" in value) && value.schemaVersion === 1 && ("mode" in value) && value.mode === "plan";
|
|
5820
5938
|
var isWorkflowApplyReport = (value) => typeof value === "object" && value !== null && ("schemaVersion" in value) && value.schemaVersion === 1 && ("mode" in value) && (value.mode === "apply" || value.mode === "dry-run");
|
|
5821
5939
|
var isWorkflowRunArtifact = (value) => typeof value === "object" && value !== null && ("schemaVersion" in value) && value.schemaVersion === 1;
|
|
5822
|
-
var planInputString = (
|
|
5823
|
-
const value =
|
|
5940
|
+
var planInputString = (plan2, key) => {
|
|
5941
|
+
const value = plan2.inputs[key];
|
|
5824
5942
|
return typeof value === "string" ? value : "";
|
|
5825
5943
|
};
|
|
5826
|
-
var expandWorkflowTarget = (target,
|
|
5827
|
-
const app = planInputString(
|
|
5828
|
-
const module = planInputString(
|
|
5829
|
-
const moduleClass = module ?
|
|
5944
|
+
var expandWorkflowTarget = (target, plan2) => {
|
|
5945
|
+
const app = planInputString(plan2, "app");
|
|
5946
|
+
const module = planInputString(plan2, "module");
|
|
5947
|
+
const moduleClass = module ? capitalize4(module) : "<Module>";
|
|
5830
5948
|
return target.replace(/^\*\//, app ? `apps/${app}/` : "").replaceAll("<module>", module || "<module>").replaceAll("<Module>", moduleClass);
|
|
5831
5949
|
};
|
|
5832
|
-
var workflowPathsForPlan = (
|
|
5950
|
+
var workflowPathsForPlan = (plan2) => plan2.predictedChanges.map((change) => expandWorkflowTarget(change.target, plan2));
|
|
5833
5951
|
var workflowPathsForArtifact = (artifact) => {
|
|
5834
5952
|
if (isWorkflowPlan(artifact))
|
|
5835
5953
|
return workflowPathsForPlan(artifact);
|
|
@@ -6044,7 +6162,7 @@ class AkanContextAnalyzer {
|
|
|
6044
6162
|
severity: strict ? "error" : "warning",
|
|
6045
6163
|
code: "module-abstract-missing",
|
|
6046
6164
|
path: module.abstract.path,
|
|
6047
|
-
message: `${
|
|
6165
|
+
message: `${capitalize4(module.kind)} module ${sys2.name}:${module.name} should include ${module.abstract.path}`,
|
|
6048
6166
|
repairActions: [action]
|
|
6049
6167
|
});
|
|
6050
6168
|
repairActions.push(action);
|
|
@@ -6056,7 +6174,7 @@ class AkanContextAnalyzer {
|
|
|
6056
6174
|
severity: "error",
|
|
6057
6175
|
code: "module-shape-invalid",
|
|
6058
6176
|
path: module.path,
|
|
6059
|
-
message: `${
|
|
6177
|
+
message: `${capitalize4(module.kind)} module ${sys2.name}:${module.name} is missing required files: ${missingFiles.join(", ")}`,
|
|
6060
6178
|
repairActions: [action]
|
|
6061
6179
|
});
|
|
6062
6180
|
repairActions.push(action);
|
|
@@ -6107,8 +6225,8 @@ class AkanContextAnalyzer {
|
|
|
6107
6225
|
...workflowPaths.length ? { baselineDiagnostics, workflowDiagnostics } : {}
|
|
6108
6226
|
};
|
|
6109
6227
|
}
|
|
6110
|
-
static findModules(context, moduleName) {
|
|
6111
|
-
const modules = [...context.apps, ...context.libs].flatMap((sys2) => sys2.modules);
|
|
6228
|
+
static findModules(context, moduleName, { app = null } = {}) {
|
|
6229
|
+
const modules = [...context.apps, ...context.libs].filter((sys2) => !app || sys2.name === app).flatMap((sys2) => sys2.modules);
|
|
6112
6230
|
return moduleName ? modules.filter((module) => module.name === moduleName || module.folderName === moduleName) : modules;
|
|
6113
6231
|
}
|
|
6114
6232
|
static renderMarkdown(context, { module: moduleName } = {}) {
|
|
@@ -6298,7 +6416,7 @@ void st;
|
|
|
6298
6416
|
` : `const UserLayout = ({ children }) => children;
|
|
6299
6417
|
const userLayout = {};
|
|
6300
6418
|
`;
|
|
6301
|
-
const
|
|
6419
|
+
const source2 = opts.includeSystemProvider ? `import type { LayoutProps, PageProps } from "akanjs/client";
|
|
6302
6420
|
import { loadFonts } from "akanjs/client";
|
|
6303
6421
|
import { System } from "akanjs/ui";
|
|
6304
6422
|
import { env } from "@apps/${opts.appName}/env/env.client";
|
|
@@ -6374,7 +6492,7 @@ export default function GeneratedLayout({ children, params, searchParams }: Layo
|
|
|
6374
6492
|
return <UserLayout params={params} searchParams={searchParams}>{children}</UserLayout>;
|
|
6375
6493
|
}
|
|
6376
6494
|
`;
|
|
6377
|
-
await Bun.write(absPath,
|
|
6495
|
+
await Bun.write(absPath, source2);
|
|
6378
6496
|
return absPath;
|
|
6379
6497
|
}
|
|
6380
6498
|
async function resolveSsrPageEntries(opts) {
|
|
@@ -6531,23 +6649,23 @@ class BarrelAnalyzer {
|
|
|
6531
6649
|
if (visited.has(absFile))
|
|
6532
6650
|
return;
|
|
6533
6651
|
visited.add(absFile);
|
|
6534
|
-
const
|
|
6535
|
-
if (
|
|
6652
|
+
const source2 = await readIfExists(absFile);
|
|
6653
|
+
if (source2 === null)
|
|
6536
6654
|
return;
|
|
6537
6655
|
const currentSubpath = this.#subpathFor(pkg, absFile);
|
|
6538
6656
|
if (!currentSubpath)
|
|
6539
6657
|
return;
|
|
6540
|
-
const authoritative = this.#scanExports(
|
|
6658
|
+
const authoritative = this.#scanExports(source2, absFile);
|
|
6541
6659
|
authoritative.delete("default");
|
|
6542
6660
|
const attributed = new Set;
|
|
6543
6661
|
REEXPORT_RE.lastIndex = 0;
|
|
6544
|
-
let m = REEXPORT_RE.exec(
|
|
6662
|
+
let m = REEXPORT_RE.exec(source2);
|
|
6545
6663
|
while (m !== null) {
|
|
6546
6664
|
const star = m[1];
|
|
6547
6665
|
const nsAs = m[2];
|
|
6548
6666
|
const namedList = m[3];
|
|
6549
6667
|
const spec = m[5] ?? "";
|
|
6550
|
-
m = REEXPORT_RE.exec(
|
|
6668
|
+
m = REEXPORT_RE.exec(source2);
|
|
6551
6669
|
if (!isRelative(spec))
|
|
6552
6670
|
continue;
|
|
6553
6671
|
if (star) {
|
|
@@ -6583,10 +6701,10 @@ class BarrelAnalyzer {
|
|
|
6583
6701
|
}
|
|
6584
6702
|
}
|
|
6585
6703
|
LOCAL_NAMED_RE.lastIndex = 0;
|
|
6586
|
-
let n = LOCAL_NAMED_RE.exec(
|
|
6704
|
+
let n = LOCAL_NAMED_RE.exec(source2);
|
|
6587
6705
|
while (n !== null) {
|
|
6588
6706
|
const body = n[1] ?? "";
|
|
6589
|
-
n = LOCAL_NAMED_RE.exec(
|
|
6707
|
+
n = LOCAL_NAMED_RE.exec(source2);
|
|
6590
6708
|
for (const item of parseNamedList(body)) {
|
|
6591
6709
|
if (item.isType)
|
|
6592
6710
|
continue;
|
|
@@ -6608,10 +6726,10 @@ class BarrelAnalyzer {
|
|
|
6608
6726
|
map.set(name, { subpath: currentSubpath, originalName: name });
|
|
6609
6727
|
}
|
|
6610
6728
|
}
|
|
6611
|
-
#scanExports(
|
|
6729
|
+
#scanExports(source2, absFile) {
|
|
6612
6730
|
try {
|
|
6613
6731
|
const transpiler = [".tsx", ".jsx"].includes(path13.extname(absFile)) ? this.#tsxTranspiler : this.#tsTranspiler;
|
|
6614
|
-
const { exports } = transpiler.scan(
|
|
6732
|
+
const { exports } = transpiler.scan(source2);
|
|
6615
6733
|
return new Set(exports);
|
|
6616
6734
|
} catch (err) {
|
|
6617
6735
|
this.#logger.error(`scan failed: ${err.message}`);
|
|
@@ -6723,28 +6841,28 @@ var createBarrelImportsPlugin = async (app, { skipPath = defaultSkipPath, pipeAf
|
|
|
6723
6841
|
const raw = await Bun.file(realPath).text();
|
|
6724
6842
|
return { contents: raw, loader };
|
|
6725
6843
|
}
|
|
6726
|
-
let
|
|
6727
|
-
const hasMacroAttr = MACRO_ATTR_RE.test(
|
|
6844
|
+
let source2 = await Bun.file(realPath).text();
|
|
6845
|
+
const hasMacroAttr = MACRO_ATTR_RE.test(source2);
|
|
6728
6846
|
if (!hasMacroAttr && barrels.length > 0) {
|
|
6729
6847
|
let maybe = false;
|
|
6730
6848
|
for (const b of barrels) {
|
|
6731
|
-
if (
|
|
6849
|
+
if (source2.includes(b)) {
|
|
6732
6850
|
maybe = true;
|
|
6733
6851
|
break;
|
|
6734
6852
|
}
|
|
6735
6853
|
}
|
|
6736
6854
|
if (maybe) {
|
|
6737
|
-
const rewritten = await rewriteBarrelImports(
|
|
6855
|
+
const rewritten = await rewriteBarrelImports(source2, barrels, analyzer);
|
|
6738
6856
|
if (rewritten !== null)
|
|
6739
|
-
|
|
6857
|
+
source2 = rewritten;
|
|
6740
6858
|
}
|
|
6741
6859
|
}
|
|
6742
6860
|
if (pipeAfter) {
|
|
6743
|
-
const piped = await pipeAfter(
|
|
6861
|
+
const piped = await pipeAfter(source2, { path: realPath });
|
|
6744
6862
|
if (piped !== null)
|
|
6745
|
-
|
|
6863
|
+
source2 = piped;
|
|
6746
6864
|
}
|
|
6747
|
-
return { contents:
|
|
6865
|
+
return { contents: source2, loader };
|
|
6748
6866
|
});
|
|
6749
6867
|
}
|
|
6750
6868
|
};
|
|
@@ -6920,12 +7038,12 @@ var resolveFileCandidate = async (candidate) => {
|
|
|
6920
7038
|
return null;
|
|
6921
7039
|
};
|
|
6922
7040
|
var MACRO_ATTR_RE = /with\s*\{\s*type\s*:\s*["']macro["']\s*\}/;
|
|
6923
|
-
var rewriteBarrelImports = async (
|
|
6924
|
-
const statements = findImportStatements(
|
|
7041
|
+
var rewriteBarrelImports = async (source2, barrels, analyzer) => {
|
|
7042
|
+
const statements = findImportStatements(source2);
|
|
6925
7043
|
if (statements.length === 0)
|
|
6926
7044
|
return null;
|
|
6927
7045
|
let changed = false;
|
|
6928
|
-
let out =
|
|
7046
|
+
let out = source2;
|
|
6929
7047
|
for (let i = statements.length - 1;i >= 0; i--) {
|
|
6930
7048
|
const stmt = statements[i];
|
|
6931
7049
|
if (!stmt)
|
|
@@ -6943,9 +7061,9 @@ var rewriteBarrelImports = async (source, barrels, analyzer) => {
|
|
|
6943
7061
|
}
|
|
6944
7062
|
return changed ? out : null;
|
|
6945
7063
|
};
|
|
6946
|
-
var findImportStatements = (
|
|
7064
|
+
var findImportStatements = (source2) => {
|
|
6947
7065
|
const statements = [];
|
|
6948
|
-
const sourceFile2 = ts4.createSourceFile("barrel-imports.tsx",
|
|
7066
|
+
const sourceFile2 = ts4.createSourceFile("barrel-imports.tsx", source2, ts4.ScriptTarget.Latest, true, ts4.ScriptKind.TSX);
|
|
6949
7067
|
for (const statement of sourceFile2.statements) {
|
|
6950
7068
|
if (!ts4.isImportDeclaration(statement))
|
|
6951
7069
|
continue;
|
|
@@ -6959,10 +7077,10 @@ var findImportStatements = (source) => {
|
|
|
6959
7077
|
statements.push({
|
|
6960
7078
|
start: statementStart,
|
|
6961
7079
|
end: statementEnd,
|
|
6962
|
-
clause:
|
|
7080
|
+
clause: source2.slice(importClause.getStart(sourceFile2), importClause.end).trim(),
|
|
6963
7081
|
specifier: statement.moduleSpecifier.text,
|
|
6964
|
-
trailingSemicolon:
|
|
6965
|
-
raw:
|
|
7082
|
+
trailingSemicolon: source2.slice(statement.moduleSpecifier.end, statement.end).includes(";"),
|
|
7083
|
+
raw: source2.slice(statementStart, statementEnd)
|
|
6966
7084
|
});
|
|
6967
7085
|
}
|
|
6968
7086
|
return statements;
|
|
@@ -7212,13 +7330,13 @@ class GraphClientEntryDiscovery {
|
|
|
7212
7330
|
}
|
|
7213
7331
|
return cached;
|
|
7214
7332
|
}
|
|
7215
|
-
async#getImports(file,
|
|
7333
|
+
async#getImports(file, source2) {
|
|
7216
7334
|
const absPath = path15.resolve(file);
|
|
7217
7335
|
let cached = this.#importCache.get(absPath);
|
|
7218
7336
|
if (!cached) {
|
|
7219
7337
|
cached = Promise.resolve().then(() => {
|
|
7220
7338
|
try {
|
|
7221
|
-
return this.#tsTranspiler.scanImports(
|
|
7339
|
+
return this.#tsTranspiler.scanImports(source2);
|
|
7222
7340
|
} catch {
|
|
7223
7341
|
return [];
|
|
7224
7342
|
}
|
|
@@ -7243,8 +7361,8 @@ class GraphClientEntryDiscovery {
|
|
|
7243
7361
|
entries.add(absPath);
|
|
7244
7362
|
return this.#finishDiscovery(absPath, visiting, entries);
|
|
7245
7363
|
}
|
|
7246
|
-
const
|
|
7247
|
-
const imports = await this.#getImports(absPath,
|
|
7364
|
+
const source2 = await this.#getRewrittenSource(absPath, content);
|
|
7365
|
+
const imports = await this.#getImports(absPath, source2);
|
|
7248
7366
|
const importerDir = path15.dirname(absPath);
|
|
7249
7367
|
for (const imp of imports) {
|
|
7250
7368
|
const spec = imp.path;
|
|
@@ -7280,13 +7398,13 @@ var IMPLICIT_ROOT_LAYOUT_RE = /[/\\]\.akan[/\\]generated[/\\](?:implicit-root-la
|
|
|
7280
7398
|
function toClientReferencePath(absPath, workspaceRoot) {
|
|
7281
7399
|
return path16.relative(path16.resolve(workspaceRoot), path16.resolve(absPath)).split(path16.sep).join("/");
|
|
7282
7400
|
}
|
|
7283
|
-
function transformUseClient(
|
|
7284
|
-
if (!USE_CLIENT_RE2.test(
|
|
7401
|
+
function transformUseClient(source2, args) {
|
|
7402
|
+
if (!USE_CLIENT_RE2.test(source2))
|
|
7285
7403
|
return null;
|
|
7286
7404
|
if (IMPLICIT_ROOT_LAYOUT_RE.test(args.path))
|
|
7287
7405
|
return null;
|
|
7288
7406
|
const transpiler = new Bun.Transpiler({ loader: loaderFor2(args.path) });
|
|
7289
|
-
const { exports } = transpiler.scan(
|
|
7407
|
+
const { exports } = transpiler.scan(source2);
|
|
7290
7408
|
if (exports.length === 0)
|
|
7291
7409
|
return null;
|
|
7292
7410
|
const referencePath = args.workspaceRoot ? toClientReferencePath(args.path, args.workspaceRoot) : args.path;
|
|
@@ -7432,9 +7550,9 @@ ${absEntry}`).toString(36);
|
|
|
7432
7550
|
`;
|
|
7433
7551
|
}
|
|
7434
7552
|
async#scanEntryExportNames(absEntry) {
|
|
7435
|
-
const
|
|
7553
|
+
const source2 = await Bun.file(absEntry).text();
|
|
7436
7554
|
const transpiler = new Bun.Transpiler({ loader: this.#loaderForEntry(absEntry) });
|
|
7437
|
-
return transpiler.scan(
|
|
7555
|
+
return transpiler.scan(source2).exports;
|
|
7438
7556
|
}
|
|
7439
7557
|
#loaderForEntry(absPath) {
|
|
7440
7558
|
if (absPath.endsWith(".tsx"))
|
|
@@ -7481,14 +7599,14 @@ ${absEntry}`).toString(36);
|
|
|
7481
7599
|
if (Object.keys(this.#externalAliases).length === 0)
|
|
7482
7600
|
return;
|
|
7483
7601
|
await Promise.all(outputs.filter((output) => output.path.endsWith(".js")).map(async (output) => {
|
|
7484
|
-
const
|
|
7485
|
-
const rewritten = ClientEntriesBundler.rewriteExternalImportSpecifiers(
|
|
7486
|
-
if (rewritten !==
|
|
7602
|
+
const source2 = await Bun.file(output.path).text();
|
|
7603
|
+
const rewritten = ClientEntriesBundler.rewriteExternalImportSpecifiers(source2, this.#externalAliases);
|
|
7604
|
+
if (rewritten !== source2)
|
|
7487
7605
|
await Bun.write(output.path, rewritten);
|
|
7488
7606
|
}));
|
|
7489
7607
|
}
|
|
7490
|
-
static rewriteExternalImportSpecifiers(
|
|
7491
|
-
let rewritten =
|
|
7608
|
+
static rewriteExternalImportSpecifiers(source2, aliases) {
|
|
7609
|
+
let rewritten = source2;
|
|
7492
7610
|
for (const [specifier, alias] of Object.entries(aliases)) {
|
|
7493
7611
|
if (!alias)
|
|
7494
7612
|
continue;
|
|
@@ -7743,9 +7861,9 @@ ${absEntry}`).toString(36);
|
|
|
7743
7861
|
return { buildEntries, originalByBuildEntry };
|
|
7744
7862
|
}
|
|
7745
7863
|
async#scanExportNames(absEntry) {
|
|
7746
|
-
const
|
|
7864
|
+
const source2 = await Bun.file(absEntry).text();
|
|
7747
7865
|
const transpiler = new Bun.Transpiler({ loader: this.#loaderFor(absEntry) });
|
|
7748
|
-
return transpiler.scan(
|
|
7866
|
+
return transpiler.scan(source2).exports;
|
|
7749
7867
|
}
|
|
7750
7868
|
#loaderFor(absPath) {
|
|
7751
7869
|
if (absPath.endsWith(".tsx"))
|
|
@@ -7756,10 +7874,10 @@ ${absEntry}`).toString(36);
|
|
|
7756
7874
|
return "ts";
|
|
7757
7875
|
return "js";
|
|
7758
7876
|
}
|
|
7759
|
-
static normalizeNamedDefaultFunctionForFastRefresh(
|
|
7877
|
+
static normalizeNamedDefaultFunctionForFastRefresh(source2) {
|
|
7760
7878
|
let changed = false;
|
|
7761
7879
|
const defaultNames = [];
|
|
7762
|
-
const next =
|
|
7880
|
+
const next = source2.replace(/(^|\n)(\s*)export\s+default\s+(async\s+)?function\s+([A-Za-z_$][\w$]*)(?=\s*(?:<|\())/g, (match, lineStart, indent, asyncKeyword, name) => {
|
|
7763
7881
|
changed = true;
|
|
7764
7882
|
defaultNames.push(name);
|
|
7765
7883
|
return `${lineStart}${indent}${asyncKeyword ?? ""}function ${name}`;
|
|
@@ -7987,8 +8105,8 @@ ${entries.join(`
|
|
|
7987
8105
|
}
|
|
7988
8106
|
static #hasAsyncDefaultExport(moduleAbsPath) {
|
|
7989
8107
|
try {
|
|
7990
|
-
const
|
|
7991
|
-
const sourceFile2 = ts5.createSourceFile(moduleAbsPath,
|
|
8108
|
+
const source2 = fs3.readFileSync(path21.resolve(moduleAbsPath), "utf8");
|
|
8109
|
+
const sourceFile2 = ts5.createSourceFile(moduleAbsPath, source2, ts5.ScriptTarget.Latest, true, PagesEntrySourceGenerator.#scriptKind(moduleAbsPath));
|
|
7992
8110
|
return PagesEntrySourceGenerator.#sourceFileHasAsyncDefaultExport(sourceFile2);
|
|
7993
8111
|
} catch {
|
|
7994
8112
|
return false;
|
|
@@ -8248,8 +8366,8 @@ ${CsrArtifactBuilder.escapeInlineScript(await loadScript(src))}
|
|
|
8248
8366
|
return html;
|
|
8249
8367
|
return result + html.slice(lastIndex);
|
|
8250
8368
|
}
|
|
8251
|
-
static escapeInlineScript(
|
|
8252
|
-
return
|
|
8369
|
+
static escapeInlineScript(source2) {
|
|
8370
|
+
return source2.replace(/<\/script/gi, "<\\/script");
|
|
8253
8371
|
}
|
|
8254
8372
|
static resolveHtmlAssetPath(htmlPath, src) {
|
|
8255
8373
|
if (/^[a-z][a-z0-9+.-]*:/i.test(src) || src.startsWith("//")) {
|
|
@@ -8480,17 +8598,17 @@ class CssCompiler {
|
|
|
8480
8598
|
} catch {
|
|
8481
8599
|
continue;
|
|
8482
8600
|
}
|
|
8483
|
-
let
|
|
8601
|
+
let source2 = content;
|
|
8484
8602
|
if (akanConfig2.barrelImports.length > 0) {
|
|
8485
8603
|
try {
|
|
8486
8604
|
const rewritten = await rewriteBarrelImports(content, akanConfig2.barrelImports, analyzer);
|
|
8487
8605
|
if (rewritten !== null)
|
|
8488
|
-
|
|
8606
|
+
source2 = rewritten;
|
|
8489
8607
|
} catch {}
|
|
8490
8608
|
}
|
|
8491
8609
|
let imports;
|
|
8492
8610
|
try {
|
|
8493
|
-
imports = this.#transpiler.scanImports(
|
|
8611
|
+
imports = this.#transpiler.scanImports(source2);
|
|
8494
8612
|
} catch {
|
|
8495
8613
|
continue;
|
|
8496
8614
|
}
|
|
@@ -8929,7 +9047,7 @@ class DevGeneratedIndexSync {
|
|
|
8929
9047
|
const rawModel = moduleSegment.startsWith("_") ? moduleSegment.slice(1) : moduleSegment;
|
|
8930
9048
|
if (!rawModel)
|
|
8931
9049
|
return null;
|
|
8932
|
-
const modelName =
|
|
9050
|
+
const modelName = capitalize5(rawModel);
|
|
8933
9051
|
const allowedTypes = isScalar ? SCALAR_UI_TYPES : moduleSegment.startsWith("_") ? SERVICE_UI_TYPES : MODULE_UI_TYPES;
|
|
8934
9052
|
const fileTypes = [];
|
|
8935
9053
|
for (const type of allowedTypes) {
|
|
@@ -8946,7 +9064,7 @@ export const ${modelName} = { ${fileTypes.join(", ")} };`;
|
|
|
8946
9064
|
}
|
|
8947
9065
|
}
|
|
8948
9066
|
var exists = async (file) => stat3(file).then(() => true).catch(() => false);
|
|
8949
|
-
var
|
|
9067
|
+
var capitalize5 = (value) => `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
|
|
8950
9068
|
var formatError = (err) => err instanceof Error ? err.message : String(err);
|
|
8951
9069
|
// pkgs/@akanjs/devkit/frontendBuild/fontOptimizer.ts
|
|
8952
9070
|
import { mkdir as mkdir7 } from "fs/promises";
|
|
@@ -9023,8 +9141,8 @@ class FontOptimizer {
|
|
|
9023
9141
|
if (faceCss.length > 0)
|
|
9024
9142
|
this.#cssParts.push(...faceCss, this.#buildRootVariableRule(font));
|
|
9025
9143
|
}
|
|
9026
|
-
#extractFontsExport(
|
|
9027
|
-
const sourceFile2 = ts6.createSourceFile(filePath,
|
|
9144
|
+
#extractFontsExport(source2, filePath) {
|
|
9145
|
+
const sourceFile2 = ts6.createSourceFile(filePath, source2, ts6.ScriptTarget.Latest, true, ts6.ScriptKind.TSX);
|
|
9028
9146
|
const fonts = [];
|
|
9029
9147
|
for (const statement of sourceFile2.statements) {
|
|
9030
9148
|
if (!ts6.isVariableStatement(statement))
|
|
@@ -9580,13 +9698,13 @@ function createUseClientBundlePlugin(options = {}) {
|
|
|
9580
9698
|
build.onLoad({ filter: /\.(tsx|ts|jsx|js)$/ }, async (args) => {
|
|
9581
9699
|
if (args.path.includes("/node_modules/"))
|
|
9582
9700
|
return;
|
|
9583
|
-
let
|
|
9701
|
+
let source2;
|
|
9584
9702
|
try {
|
|
9585
|
-
|
|
9703
|
+
source2 = await Bun.file(args.path).text();
|
|
9586
9704
|
} catch {
|
|
9587
9705
|
return;
|
|
9588
9706
|
}
|
|
9589
|
-
const stubbed = transformUseClient(
|
|
9707
|
+
const stubbed = transformUseClient(source2, {
|
|
9590
9708
|
path: args.path,
|
|
9591
9709
|
workspaceRoot: options.workspaceRoot
|
|
9592
9710
|
});
|
|
@@ -9644,7 +9762,7 @@ class PagesBundleBuilder {
|
|
|
9644
9762
|
PagesBundleBuilder.createServerUseClientFetchPlugin(),
|
|
9645
9763
|
await createExternalizeFrameworkPlugin({ app: this.#app, extra: akanConfig2.externalLibs }),
|
|
9646
9764
|
akanConfig2.barrelImports.length > 0 ? await createBarrelImportsPlugin(this.#app, {
|
|
9647
|
-
pipeAfter: (
|
|
9765
|
+
pipeAfter: (source2, args) => transformUseClient(source2, {
|
|
9648
9766
|
path: args.path,
|
|
9649
9767
|
workspaceRoot
|
|
9650
9768
|
})
|
|
@@ -9685,7 +9803,7 @@ class PagesBundleBuilder {
|
|
|
9685
9803
|
...Object.fromEntries(Object.entries(this.#app.getPublicEnv()).map(([key, value]) => [`process.env.${key}`, JSON.stringify(value)]))
|
|
9686
9804
|
};
|
|
9687
9805
|
}
|
|
9688
|
-
static createPagesEntryPlugin(
|
|
9806
|
+
static createPagesEntryPlugin(source2) {
|
|
9689
9807
|
return {
|
|
9690
9808
|
name: "akan-pages-entry",
|
|
9691
9809
|
setup(build) {
|
|
@@ -9694,7 +9812,7 @@ class PagesBundleBuilder {
|
|
|
9694
9812
|
namespace: "akan-virtual"
|
|
9695
9813
|
}));
|
|
9696
9814
|
build.onLoad({ filter: /^akan-pages-entry$/, namespace: "akan-virtual" }, () => ({
|
|
9697
|
-
contents:
|
|
9815
|
+
contents: source2,
|
|
9698
9816
|
loader: "tsx"
|
|
9699
9817
|
}));
|
|
9700
9818
|
}
|
|
@@ -9716,19 +9834,19 @@ class PagesBundleBuilder {
|
|
|
9716
9834
|
name: "akan-server-use-client-fetch",
|
|
9717
9835
|
setup(build) {
|
|
9718
9836
|
build.onLoad({ filter: /[/\\]lib[/\\]useClient\.(ts|tsx|js|jsx)$/ }, async (args) => {
|
|
9719
|
-
const
|
|
9720
|
-
const transformed = PagesBundleBuilder.transformServerUseClientFetchSource(
|
|
9721
|
-
if (transformed ===
|
|
9837
|
+
const source2 = await Bun.file(args.path).text();
|
|
9838
|
+
const transformed = PagesBundleBuilder.transformServerUseClientFetchSource(source2);
|
|
9839
|
+
if (transformed === source2)
|
|
9722
9840
|
return;
|
|
9723
9841
|
return { contents: transformed, loader: loaderFor4(args.path) };
|
|
9724
9842
|
});
|
|
9725
9843
|
}
|
|
9726
9844
|
};
|
|
9727
9845
|
}
|
|
9728
|
-
static transformServerUseClientFetchSource(
|
|
9729
|
-
if (!
|
|
9730
|
-
return
|
|
9731
|
-
return
|
|
9846
|
+
static transformServerUseClientFetchSource(source2) {
|
|
9847
|
+
if (!source2.includes(`with { type: "macro" }`) || !source2.includes("FetchClient.build"))
|
|
9848
|
+
return source2;
|
|
9849
|
+
return source2.replace(/import\s+\{\s*getSerializedSignal\s*\}\s+from\s+["']\.\/sig["']\s+with\s+\{\s*type\s*:\s*["']macro["']\s*\};/, `import { fetch as serverFetch } from "./sig";`).replace(/const\s+fetchProto\s*=\s*FetchClient\.build<[^;]+;/, "const fetchProto = FetchClient.build<typeof signal>(cnst, serverFetch.serializedSignal, { Err: pageProto.Err, base: serverFetch });");
|
|
9732
9850
|
}
|
|
9733
9851
|
}
|
|
9734
9852
|
function loaderFor4(absPath) {
|
|
@@ -10494,8 +10612,8 @@ class Builder {
|
|
|
10494
10612
|
#executor;
|
|
10495
10613
|
#distExecutor;
|
|
10496
10614
|
#pkgJson;
|
|
10497
|
-
constructor({ executor, distExecutor, pkgJson }) {
|
|
10498
|
-
this.#executor =
|
|
10615
|
+
constructor({ executor: executor2, distExecutor, pkgJson }) {
|
|
10616
|
+
this.#executor = executor2;
|
|
10499
10617
|
this.#distExecutor = distExecutor;
|
|
10500
10618
|
this.#pkgJson = pkgJson;
|
|
10501
10619
|
}
|
|
@@ -10621,7 +10739,7 @@ import os from "os";
|
|
|
10621
10739
|
import path38 from "path";
|
|
10622
10740
|
import { select as select2 } from "@inquirer/prompts";
|
|
10623
10741
|
import { MobileProject } from "@trapezedev/project";
|
|
10624
|
-
import { capitalize as
|
|
10742
|
+
import { capitalize as capitalize6 } from "akanjs/common";
|
|
10625
10743
|
|
|
10626
10744
|
// pkgs/@akanjs/devkit/fileEditor.ts
|
|
10627
10745
|
class FileEditor {
|
|
@@ -11712,7 +11830,7 @@ ${error.message}`;
|
|
|
11712
11830
|
this.#setPermissionsInAndroid(["POST_NOTIFICATIONS"]);
|
|
11713
11831
|
}
|
|
11714
11832
|
async#setPermissionInIos(permissions) {
|
|
11715
|
-
const updateNs = Object.fromEntries(Object.entries(permissions).map(([key, value]) => [`NS${
|
|
11833
|
+
const updateNs = Object.fromEntries(Object.entries(permissions).map(([key, value]) => [`NS${capitalize6(key)}`, value]));
|
|
11716
11834
|
await Promise.all([
|
|
11717
11835
|
this.project.ios.updateInfoPlist(this.iosTargetName, "Debug", updateNs),
|
|
11718
11836
|
this.project.ios.updateInfoPlist(this.iosTargetName, "Release", updateNs)
|
|
@@ -11804,8 +11922,8 @@ import chalk6 from "chalk";
|
|
|
11804
11922
|
import { program } from "commander";
|
|
11805
11923
|
|
|
11806
11924
|
// pkgs/@akanjs/devkit/commandDecorators/dependencyBuilder.ts
|
|
11807
|
-
var
|
|
11808
|
-
var createDependencyKey = (refName, kind) => `${refName}${
|
|
11925
|
+
var capitalize7 = (value) => value.slice(0, 1).toUpperCase() + value.slice(1);
|
|
11926
|
+
var createDependencyKey = (refName, kind) => `${refName}${capitalize7(kind)}`;
|
|
11809
11927
|
|
|
11810
11928
|
class CommandContainer {
|
|
11811
11929
|
static #instances = new Map;
|
|
@@ -12285,13 +12403,13 @@ var getInternalArgumentValue = async (argMeta, value, workspace) => {
|
|
|
12285
12403
|
...libNames.map((name2) => ({ name: name2, value: { type: "lib", name: name2 } }))
|
|
12286
12404
|
]
|
|
12287
12405
|
});
|
|
12288
|
-
const
|
|
12289
|
-
const modules = await
|
|
12406
|
+
const executor2 = type === "app" ? AppExecutor.from(workspace, name) : LibExecutor.from(workspace, name);
|
|
12407
|
+
const modules = await executor2.getModules();
|
|
12290
12408
|
const moduleName = await select3({
|
|
12291
12409
|
message: `Select the module name`,
|
|
12292
|
-
choices: modules.map((name2) => ({ name: `${
|
|
12410
|
+
choices: modules.map((name2) => ({ name: `${executor2.name}:${name2}`, value: name2 }))
|
|
12293
12411
|
});
|
|
12294
|
-
return ModuleExecutor.from(
|
|
12412
|
+
return ModuleExecutor.from(executor2, moduleName);
|
|
12295
12413
|
} else
|
|
12296
12414
|
throw new Error(`Invalid system type: ${argMeta.type}`);
|
|
12297
12415
|
};
|
|
@@ -12550,18 +12668,18 @@ import * as ts7 from "typescript";
|
|
|
12550
12668
|
var tsTranspiler = new Bun.Transpiler({ loader: "ts" });
|
|
12551
12669
|
var tsxTranspiler = new Bun.Transpiler({ loader: "tsx" });
|
|
12552
12670
|
var getTranspiler = (filePath) => filePath.endsWith(".tsx") ? tsxTranspiler : tsTranspiler;
|
|
12553
|
-
var scanModuleSpecifiers = (
|
|
12554
|
-
const scannedImports = getTranspiler(filePath).scanImports(
|
|
12671
|
+
var scanModuleSpecifiers = (source2, filePath, includeExports) => {
|
|
12672
|
+
const scannedImports = getTranspiler(filePath).scanImports(source2).map((imp) => imp.path).filter(Boolean);
|
|
12555
12673
|
if (includeExports) {
|
|
12556
12674
|
const specifiers = new Set(scannedImports);
|
|
12557
12675
|
const typeOnlyModuleRegex = /\b(?:import|export)\s+type\s+[\s\S]*?\s+from\s*["']([^"']+)["']/g;
|
|
12558
|
-
for (const match of
|
|
12676
|
+
for (const match of source2.matchAll(typeOnlyModuleRegex)) {
|
|
12559
12677
|
const importPath = match[1];
|
|
12560
12678
|
if (importPath)
|
|
12561
12679
|
specifiers.add(importPath);
|
|
12562
12680
|
}
|
|
12563
12681
|
const exportFromRegex = /\bexport\s+(?:type\s+)?(?:\*|{[\s\S]*?})\s+from\s*["']([^"']+)["']/g;
|
|
12564
|
-
for (const match of
|
|
12682
|
+
for (const match of source2.matchAll(exportFromRegex)) {
|
|
12565
12683
|
const importPath = match[1];
|
|
12566
12684
|
if (importPath)
|
|
12567
12685
|
specifiers.add(importPath);
|
|
@@ -12570,7 +12688,7 @@ var scanModuleSpecifiers = (source, filePath, includeExports) => {
|
|
|
12570
12688
|
}
|
|
12571
12689
|
const importSpecifiers = new Set;
|
|
12572
12690
|
const importDeclarationRegex = /\bimport\s+(?:type\s+)?(?:["']([^"']+)["']|[\s\S]*?\s+from\s*["']([^"']+)["'])/g;
|
|
12573
|
-
for (const match of
|
|
12691
|
+
for (const match of source2.matchAll(importDeclarationRegex)) {
|
|
12574
12692
|
const importPath = match[1] ?? match[2];
|
|
12575
12693
|
if (importPath && (scannedImports.includes(importPath) || match[0].startsWith("import type"))) {
|
|
12576
12694
|
importSpecifiers.add(importPath);
|
|
@@ -12593,8 +12711,8 @@ var collectImportedFiles = (constantFilePath, parsedConfig) => {
|
|
|
12593
12711
|
if (analyzedFiles.has(filePath))
|
|
12594
12712
|
return;
|
|
12595
12713
|
analyzedFiles.add(filePath);
|
|
12596
|
-
const
|
|
12597
|
-
for (const importPath of scanModuleSpecifiers(
|
|
12714
|
+
const source2 = readFileSync4(filePath, "utf-8");
|
|
12715
|
+
for (const importPath of scanModuleSpecifiers(source2, filePath, false)) {
|
|
12598
12716
|
if (!importPath.startsWith("."))
|
|
12599
12717
|
continue;
|
|
12600
12718
|
const resolved = ts7.resolveModuleName(importPath, filePath, parsedConfig.options, ts7.sys).resolvedModule?.resolvedFileName;
|
|
@@ -12643,21 +12761,21 @@ var analyzeProperties = (filesToAnalyze, program2, checker) => {
|
|
|
12643
12761
|
if (analyzedFiles.has(filePath))
|
|
12644
12762
|
return;
|
|
12645
12763
|
analyzedFiles.add(filePath);
|
|
12646
|
-
const
|
|
12647
|
-
if (!
|
|
12764
|
+
const source2 = program2.getSourceFile(filePath);
|
|
12765
|
+
if (!source2)
|
|
12648
12766
|
return;
|
|
12649
12767
|
if (!sourceLineCache.has(filePath)) {
|
|
12650
|
-
sourceLineCache.set(filePath,
|
|
12768
|
+
sourceLineCache.set(filePath, source2.getFullText().split(`
|
|
12651
12769
|
`));
|
|
12652
12770
|
}
|
|
12653
12771
|
const sourceLines = sourceLineCache.get(filePath);
|
|
12654
12772
|
function visit(node) {
|
|
12655
|
-
if (!
|
|
12773
|
+
if (!source2)
|
|
12656
12774
|
return;
|
|
12657
12775
|
if (ts7.isPropertyAccessExpression(node)) {
|
|
12658
12776
|
const left = node.expression;
|
|
12659
12777
|
const right = node.name;
|
|
12660
|
-
const { line } = ts7.getLineAndCharacterOfPosition(
|
|
12778
|
+
const { line } = ts7.getLineAndCharacterOfPosition(source2, node.getStart());
|
|
12661
12779
|
if (ts7.isIdentifier(left) && sourceLines && sourceLines.length > line && sourceLines[line] && (sourceLines[line]?.includes(`@Field.Prop(() => ${left.text}.${right.text}`) || sourceLines[line].includes(`base.Filter(${left.text}.${right.text},`))) {
|
|
12662
12780
|
const symbol = getCachedSymbol(right);
|
|
12663
12781
|
if (symbol?.declarations && symbol.declarations.length > 0) {
|
|
@@ -12708,7 +12826,7 @@ var analyzeProperties = (filesToAnalyze, program2, checker) => {
|
|
|
12708
12826
|
}
|
|
12709
12827
|
ts7.forEachChild(node, visit);
|
|
12710
12828
|
}
|
|
12711
|
-
visit(
|
|
12829
|
+
visit(source2);
|
|
12712
12830
|
}
|
|
12713
12831
|
for (const filePath of filesToAnalyze) {
|
|
12714
12832
|
analyzeFileProperties(filePath);
|
|
@@ -14112,8 +14230,8 @@ class CloudRunner extends runner("cloud") {
|
|
|
14112
14230
|
#getSshTarget(config) {
|
|
14113
14231
|
return `${config.username ? `${config.username}@` : ""}${config.host}`;
|
|
14114
14232
|
}
|
|
14115
|
-
#getScpArgs(config,
|
|
14116
|
-
return [...config.port ? ["-P", config.port.toString()] : [],
|
|
14233
|
+
#getScpArgs(config, source2, target) {
|
|
14234
|
+
return [...config.port ? ["-P", config.port.toString()] : [], source2, target];
|
|
14117
14235
|
}
|
|
14118
14236
|
#getSshArgs(config, command3) {
|
|
14119
14237
|
return [...config.port ? ["-p", config.port.toString()] : [], this.#getSshTarget(config), command3];
|
|
@@ -14493,7 +14611,7 @@ import path43 from "path";
|
|
|
14493
14611
|
|
|
14494
14612
|
// pkgs/@akanjs/cli/module/module.script.ts
|
|
14495
14613
|
import { input as input6 } from "@inquirer/prompts";
|
|
14496
|
-
import { capitalize as
|
|
14614
|
+
import { capitalize as capitalize10, randomPicks } from "akanjs/common";
|
|
14497
14615
|
|
|
14498
14616
|
// pkgs/@akanjs/cli/page/page.runner.ts
|
|
14499
14617
|
class PageRunner extends runner("page") {
|
|
@@ -14523,7 +14641,7 @@ function pluralizeName(name) {
|
|
|
14523
14641
|
}
|
|
14524
14642
|
|
|
14525
14643
|
// pkgs/@akanjs/cli/module/module.request.ts
|
|
14526
|
-
import { capitalize as
|
|
14644
|
+
import { capitalize as capitalize8 } from "akanjs/common";
|
|
14527
14645
|
|
|
14528
14646
|
// pkgs/@akanjs/cli/module/module.prompt.ts
|
|
14529
14647
|
var componentDefaultDescription = ({
|
|
@@ -14648,7 +14766,7 @@ var requestTemplate = ({
|
|
|
14648
14766
|
${componentDefaultDescription({
|
|
14649
14767
|
sysName,
|
|
14650
14768
|
modelName,
|
|
14651
|
-
ModelName: ModelName ??
|
|
14769
|
+
ModelName: ModelName ?? capitalize8(modelName),
|
|
14652
14770
|
exampleFiles,
|
|
14653
14771
|
constant,
|
|
14654
14772
|
properties
|
|
@@ -14705,7 +14823,7 @@ var requestView = ({
|
|
|
14705
14823
|
${componentDefaultDescription({
|
|
14706
14824
|
sysName,
|
|
14707
14825
|
modelName,
|
|
14708
|
-
ModelName: ModelName ??
|
|
14826
|
+
ModelName: ModelName ?? capitalize8(modelName),
|
|
14709
14827
|
exampleFiles,
|
|
14710
14828
|
constant,
|
|
14711
14829
|
properties
|
|
@@ -14766,7 +14884,7 @@ var requestUnit = ({
|
|
|
14766
14884
|
${componentDefaultDescription({
|
|
14767
14885
|
sysName,
|
|
14768
14886
|
modelName,
|
|
14769
|
-
ModelName: ModelName ??
|
|
14887
|
+
ModelName: ModelName ?? capitalize8(modelName),
|
|
14770
14888
|
exampleFiles,
|
|
14771
14889
|
constant,
|
|
14772
14890
|
properties
|
|
@@ -14815,7 +14933,39 @@ var requestUnit = ({
|
|
|
14815
14933
|
`;
|
|
14816
14934
|
|
|
14817
14935
|
// pkgs/@akanjs/cli/module/module.runner.ts
|
|
14818
|
-
import { capitalize as
|
|
14936
|
+
import { capitalize as capitalize9 } from "akanjs/common";
|
|
14937
|
+
var moduleAbstractContent = (moduleName) => {
|
|
14938
|
+
const title = capitalize9(moduleName);
|
|
14939
|
+
return `# ${title} Module Abstract
|
|
14940
|
+
|
|
14941
|
+
## Purpose
|
|
14942
|
+
|
|
14943
|
+
${title} owns persisted ${moduleName} records.
|
|
14944
|
+
|
|
14945
|
+
## Domain Rules
|
|
14946
|
+
|
|
14947
|
+
- No durable business invariants yet.
|
|
14948
|
+
|
|
14949
|
+
## Data Meaning
|
|
14950
|
+
|
|
14951
|
+
No additional data meaning has been documented yet.
|
|
14952
|
+
|
|
14953
|
+
## Workflows
|
|
14954
|
+
|
|
14955
|
+
No lifecycle workflow yet.
|
|
14956
|
+
|
|
14957
|
+
## Agent Notes
|
|
14958
|
+
|
|
14959
|
+
- Read this abstract before changing the module.
|
|
14960
|
+
- Update this file when business invariants, workflows, or public behavior change.
|
|
14961
|
+
- Do not update this file for formatting-only, import-only, or style-only changes.
|
|
14962
|
+
|
|
14963
|
+
## Related Modules
|
|
14964
|
+
|
|
14965
|
+
- None yet.
|
|
14966
|
+
`;
|
|
14967
|
+
};
|
|
14968
|
+
|
|
14819
14969
|
class ModuleRunner extends runner("module") {
|
|
14820
14970
|
async createService(module) {
|
|
14821
14971
|
const serviceName = module.name.replace(/^_+/, "");
|
|
@@ -14845,13 +14995,13 @@ class ModuleRunner extends runner("module") {
|
|
|
14845
14995
|
async createComponentTemplate(module, type) {
|
|
14846
14996
|
await module.sys.applyTemplate({
|
|
14847
14997
|
basePath: `./lib/${module.name}`,
|
|
14848
|
-
template: `module/__Model__.${
|
|
14998
|
+
template: `module/__Model__.${capitalize9(type)}.tsx`,
|
|
14849
14999
|
dict: { model: module.name, appName: module.sys.name }
|
|
14850
15000
|
});
|
|
14851
15001
|
return {
|
|
14852
15002
|
component: {
|
|
14853
|
-
filename: `${module.name}.${
|
|
14854
|
-
content: await module.sys.readFile(`lib/${module.name}/${
|
|
15003
|
+
filename: `${module.name}.${capitalize9(type)}.tsx`,
|
|
15004
|
+
content: await module.sys.readFile(`lib/${module.name}/${capitalize9(module.name)}.${capitalize9(type)}.tsx`)
|
|
14855
15005
|
}
|
|
14856
15006
|
};
|
|
14857
15007
|
}
|
|
@@ -14862,6 +15012,7 @@ class ModuleRunner extends runner("module") {
|
|
|
14862
15012
|
template: "module",
|
|
14863
15013
|
dict: { model: module.name, models: names, sysName: module.sys.name }
|
|
14864
15014
|
});
|
|
15015
|
+
await module.writeFile(`${module.name}.abstract.md`, moduleAbstractContent(module.name));
|
|
14865
15016
|
const [
|
|
14866
15017
|
abstractContent,
|
|
14867
15018
|
constantContent,
|
|
@@ -14928,11 +15079,11 @@ class ModuleScript extends script("module", [ModuleRunner, PageScript]) {
|
|
|
14928
15079
|
const isContinued = await sys3.exists(`lib/${name}/${name}.constant.ts`);
|
|
14929
15080
|
const session = new AiSession("createModule", { workspace: sys3.workspace, cacheKey: name, isContinued });
|
|
14930
15081
|
const moduleConstantExampleFiles = await sys3.workspace.getConstantFiles();
|
|
14931
|
-
const
|
|
14932
|
-
const files = await this.moduleRunner.createModuleTemplate(
|
|
15082
|
+
const executor2 = ModuleExecutor.from(sys3, name);
|
|
15083
|
+
const files = await this.moduleRunner.createModuleTemplate(executor2);
|
|
14933
15084
|
const { constant, dictionary } = files;
|
|
14934
15085
|
if (page && sys3.type === "app")
|
|
14935
|
-
await this.pageScript.createCrudPage(
|
|
15086
|
+
await this.pageScript.createCrudPage(executor2, { app: sys3, basePath: null, single: false });
|
|
14936
15087
|
const modelDesc = description ?? await input6({ message: "description of module" });
|
|
14937
15088
|
const modelSchemaDesign = schemaDescription ?? await input6({ message: "schema description of module" });
|
|
14938
15089
|
const config = await sys3.getConfig();
|
|
@@ -15001,7 +15152,7 @@ class ModuleScript extends script("module", [ModuleRunner, PageScript]) {
|
|
|
15001
15152
|
async createTemplate(mod) {
|
|
15002
15153
|
const { component: template } = await this.moduleRunner.createComponentTemplate(mod, "template");
|
|
15003
15154
|
const templateExampleFiles = (await mod.sys.getTemplatesSourceCode()).filter((f) => !f.filePath.includes(`${mod.name}.Template.tsx`));
|
|
15004
|
-
const Name =
|
|
15155
|
+
const Name = capitalize10(mod.name);
|
|
15005
15156
|
const relatedCnsts = getRelatedCnsts(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
|
|
15006
15157
|
const constant = await FileSys.readText(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
|
|
15007
15158
|
const session = new AiSession("createTemplate", { workspace: mod.sys.workspace, cacheKey: mod.name });
|
|
@@ -15027,7 +15178,7 @@ class ModuleScript extends script("module", [ModuleRunner, PageScript]) {
|
|
|
15027
15178
|
}
|
|
15028
15179
|
async createUnit(mod) {
|
|
15029
15180
|
const { component: unit } = await this.moduleRunner.createComponentTemplate(mod, "unit");
|
|
15030
|
-
const Name =
|
|
15181
|
+
const Name = capitalize10(mod.name);
|
|
15031
15182
|
const unitExampleFiles = (await mod.sys.getUnitsSourceCode()).filter((f) => !f.filePath.includes(`${mod.name}.Unit.tsx`));
|
|
15032
15183
|
const relatedCnsts = getRelatedCnsts(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
|
|
15033
15184
|
const constant = await FileSys.readText(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
|
|
@@ -15053,7 +15204,7 @@ class ModuleScript extends script("module", [ModuleRunner, PageScript]) {
|
|
|
15053
15204
|
async createView(mod) {
|
|
15054
15205
|
const { component: view } = await this.moduleRunner.createComponentTemplate(mod, "view");
|
|
15055
15206
|
const viewExampleFiles = (await mod.sys.getViewsSourceCode()).filter((f) => !f.filePath.includes(`${mod.name}.View.tsx`));
|
|
15056
|
-
const Name =
|
|
15207
|
+
const Name = capitalize10(mod.name);
|
|
15057
15208
|
const relatedCnsts = getRelatedCnsts(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
|
|
15058
15209
|
const constant = await FileSys.readText(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
|
|
15059
15210
|
const session = new AiSession("createView", { workspace: mod.sys.workspace, cacheKey: mod.name });
|
|
@@ -15078,7 +15229,7 @@ class ModuleScript extends script("module", [ModuleRunner, PageScript]) {
|
|
|
15078
15229
|
}
|
|
15079
15230
|
|
|
15080
15231
|
// pkgs/@akanjs/cli/primitive/primitive.script.ts
|
|
15081
|
-
import { capitalize as
|
|
15232
|
+
import { capitalize as capitalize11 } from "akanjs/common";
|
|
15082
15233
|
class PrimitiveScript extends script("primitive", [ModuleScript]) {
|
|
15083
15234
|
async resolveSys(workspace, target) {
|
|
15084
15235
|
if (!target)
|
|
@@ -15126,7 +15277,7 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
|
|
|
15126
15277
|
}
|
|
15127
15278
|
async addEnumField(workspace, input7) {
|
|
15128
15279
|
const values = parseValues(input7.values);
|
|
15129
|
-
return await this.addFieldToSources(workspace, { ...input7, type: `${
|
|
15280
|
+
return await this.addFieldToSources(workspace, { ...input7, type: `${capitalize11(input7.field ?? "")}` }, { enumValues: values });
|
|
15130
15281
|
}
|
|
15131
15282
|
async addFieldToSources(workspace, input7, { enumValues }) {
|
|
15132
15283
|
const sys3 = await this.resolveSys(workspace, input7.app);
|
|
@@ -15170,7 +15321,7 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
|
|
|
15170
15321
|
nextActions: []
|
|
15171
15322
|
});
|
|
15172
15323
|
}
|
|
15173
|
-
const moduleClassName =
|
|
15324
|
+
const moduleClassName = capitalize11(input7.module);
|
|
15174
15325
|
const inputClassName = `${moduleClassName}Input`;
|
|
15175
15326
|
const constantPath = `lib/${input7.module}/${input7.module}.constant.ts`;
|
|
15176
15327
|
const dictionaryPath = `lib/${input7.module}/${input7.module}.dictionary.ts`;
|
|
@@ -15212,8 +15363,8 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
|
|
|
15212
15363
|
});
|
|
15213
15364
|
}
|
|
15214
15365
|
if (enumValues) {
|
|
15215
|
-
const enumClassName = `${moduleClassName}${
|
|
15216
|
-
const enumName = `${lowerlize(moduleClassName)}${
|
|
15366
|
+
const enumClassName = `${moduleClassName}${capitalize11(input7.field)}`;
|
|
15367
|
+
const enumName = `${lowerlize(moduleClassName)}${capitalize11(input7.field)}`;
|
|
15217
15368
|
constantContent = insertEnumClass(ensureEnumImport(constantContent), enumClassName, enumName, enumValues);
|
|
15218
15369
|
dictionaryContent = ensureConstantTypeImport(dictionaryContent, `./${input7.module}.constant`, enumClassName);
|
|
15219
15370
|
input7.type = enumClassName;
|
|
@@ -15230,10 +15381,30 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
|
|
|
15230
15381
|
}
|
|
15231
15382
|
if (!enumValues && normalizedType) {
|
|
15232
15383
|
input7.type = normalizedType;
|
|
15384
|
+
const defaultCoercion = coerceFieldDefault(input7.type, input7.defaultValue);
|
|
15385
|
+
if (defaultCoercion.diagnostic)
|
|
15386
|
+
diagnostics.push(defaultCoercion.diagnostic);
|
|
15233
15387
|
constantContent = ensureBaseTypeImport(constantContent, input7.type);
|
|
15234
15388
|
}
|
|
15235
15389
|
const nextConstantContent = insertIntoObject(constantContent, inputClassName, `${input7.field}: ${fieldExpression(input7.type, input7.defaultValue)},`);
|
|
15236
15390
|
const nextDictionaryContent = insertDictionaryModelField(dictionaryContent, moduleClassName, input7.field);
|
|
15391
|
+
if (nextConstantContent && (input7.type === "Int" || input7.type === "Float") && new RegExp(`\\b${input7.field}\\s*:\\s*field\\(${input7.type}, \\{ default: "`, "m").test(nextConstantContent)) {
|
|
15392
|
+
diagnostics.push({
|
|
15393
|
+
severity: "error",
|
|
15394
|
+
code: "primitive-default-value-invalid",
|
|
15395
|
+
input: "default",
|
|
15396
|
+
failureScope: "source-change",
|
|
15397
|
+
message: `Generated ${input7.type} default for "${input7.field}" would be a string literal; refusing to write source.`
|
|
15398
|
+
});
|
|
15399
|
+
}
|
|
15400
|
+
if (nextConstantContent && (input7.type === "Int" || input7.type === "Float") && !new RegExp(`import \\{[^}]*\\b${input7.type}\\b[^}]*\\} from "akanjs/base";`).test(nextConstantContent)) {
|
|
15401
|
+
diagnostics.push({
|
|
15402
|
+
severity: "error",
|
|
15403
|
+
code: "primitive-base-type-import-missing",
|
|
15404
|
+
failureScope: "source-change",
|
|
15405
|
+
message: `Generated source for ${input7.field} requires ${input7.type} import from "akanjs/base".`
|
|
15406
|
+
});
|
|
15407
|
+
}
|
|
15237
15408
|
if (!nextConstantContent) {
|
|
15238
15409
|
diagnostics.push({
|
|
15239
15410
|
severity: "error",
|
|
@@ -15312,8 +15483,8 @@ class RepairRunner extends runner("repair") {
|
|
|
15312
15483
|
format = "markdown",
|
|
15313
15484
|
execute
|
|
15314
15485
|
}) {
|
|
15315
|
-
const
|
|
15316
|
-
const report = await this.createRepairReport(kind, { workspace, app, module, target, execute:
|
|
15486
|
+
const executor2 = execute ?? defaultExecutor(workspace);
|
|
15487
|
+
const report = await this.createRepairReport(kind, { workspace, app, module, target, execute: executor2 });
|
|
15317
15488
|
const { artifact: artifact2, runId } = await writeWorkflowRunArtifact(workspace, report);
|
|
15318
15489
|
const reportWithArtifact = artifact2;
|
|
15319
15490
|
if (kind === "generated" && reportWithArtifact.status === "passed" && reportWithArtifact.target) {
|
|
@@ -15620,6 +15791,7 @@ class ScalarScript extends script("scalar", [ScalarRunner]) {
|
|
|
15620
15791
|
// pkgs/@akanjs/cli/workflow/workflow.runner.ts
|
|
15621
15792
|
import { mkdir as mkdir12, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
|
|
15622
15793
|
import path42 from "path";
|
|
15794
|
+
import { capitalize as capitalize12 } from "akanjs/common";
|
|
15623
15795
|
|
|
15624
15796
|
// pkgs/@akanjs/cli/workflows/shared.ts
|
|
15625
15797
|
var sysInputs = {
|
|
@@ -15733,7 +15905,10 @@ var addFieldWorkflowSpec = {
|
|
|
15733
15905
|
description: "Field type or scalar name. Use Int for integer fields or Float for decimal fields; do not use Number."
|
|
15734
15906
|
},
|
|
15735
15907
|
values: { type: "string-list", description: "Comma-separated enum values when type is enum." },
|
|
15736
|
-
default: {
|
|
15908
|
+
default: {
|
|
15909
|
+
type: "string",
|
|
15910
|
+
description: "Optional default value. apply_workflow coerces by field type: Int/Float to numeric literals, Boolean to true/false, String/enum/scalar to string literals; invalid numeric or boolean defaults fail before source is written."
|
|
15911
|
+
}
|
|
15737
15912
|
},
|
|
15738
15913
|
optionalSurfaces: {
|
|
15739
15914
|
dictionary: "include",
|
|
@@ -16180,7 +16355,7 @@ var failedPlan = (workflow2, diagnostics) => ({
|
|
|
16180
16355
|
recommendations: [],
|
|
16181
16356
|
requiresApproval: true
|
|
16182
16357
|
});
|
|
16183
|
-
var failedApplyReport = (workflow2, diagnostics,
|
|
16358
|
+
var failedApplyReport = (workflow2, diagnostics, plan2) => createWorkflowApplyReport({
|
|
16184
16359
|
workflow: workflow2,
|
|
16185
16360
|
mode: "apply",
|
|
16186
16361
|
changedFiles: [],
|
|
@@ -16188,7 +16363,7 @@ var failedApplyReport = (workflow2, diagnostics, plan) => createWorkflowApplyRep
|
|
|
16188
16363
|
commands: [],
|
|
16189
16364
|
diagnostics,
|
|
16190
16365
|
nextActions: [],
|
|
16191
|
-
plan:
|
|
16366
|
+
plan: plan2 ?? failedPlan(workflow2, diagnostics)
|
|
16192
16367
|
});
|
|
16193
16368
|
var commandForShell2 = (command3) => command3.startsWith("akan ") ? `bun run ${command3}` : command3;
|
|
16194
16369
|
var inferValidationKind = (command3) => {
|
|
@@ -16248,6 +16423,24 @@ var defaultValidationExecutor = (workspace) => async (command3) => {
|
|
|
16248
16423
|
}
|
|
16249
16424
|
};
|
|
16250
16425
|
var readJsonFile = async (filePath) => JSON.parse(await readFile2(resolvePath(filePath), "utf8"));
|
|
16426
|
+
var planInputString2 = (plan2, key) => {
|
|
16427
|
+
const value = plan2.inputs[key];
|
|
16428
|
+
return typeof value === "string" ? value : "";
|
|
16429
|
+
};
|
|
16430
|
+
var workflowPathsForPlanLike = (plan2) => {
|
|
16431
|
+
const app = planInputString2(plan2, "app");
|
|
16432
|
+
const module = planInputString2(plan2, "module");
|
|
16433
|
+
const moduleClass = module ? capitalize12(module) : "<Module>";
|
|
16434
|
+
return plan2.predictedChanges.map((change) => change.target.replace(/^\*\//, app ? `apps/${app}/` : "").replaceAll("<module>", module || "<module>").replaceAll("<Module>", moduleClass));
|
|
16435
|
+
};
|
|
16436
|
+
var workflowDiagnosticFromDoctor = (diagnostic, fallbackScope) => ({
|
|
16437
|
+
severity: diagnostic.severity,
|
|
16438
|
+
code: diagnostic.code,
|
|
16439
|
+
message: diagnostic.message,
|
|
16440
|
+
scope: diagnostic.scope ?? fallbackScope,
|
|
16441
|
+
failureScope: (diagnostic.scope ?? fallbackScope) === "baseline" ? "workspace-config" : (diagnostic.scope ?? fallbackScope) === "workflow" ? "source-change" : "unknown",
|
|
16442
|
+
context: diagnostic.context
|
|
16443
|
+
});
|
|
16251
16444
|
|
|
16252
16445
|
class WorkflowRunner extends runner("workflow") {
|
|
16253
16446
|
list({ format = "markdown" } = {}) {
|
|
@@ -16269,13 +16462,13 @@ class WorkflowRunner extends runner("workflow") {
|
|
|
16269
16462
|
const spec = getWorkflowSpec(workflowSpecs, workflow2);
|
|
16270
16463
|
if (!spec)
|
|
16271
16464
|
throw new Error(`Unknown workflow: ${workflow2}. Run \`akan workflow list\` to see available workflows.`);
|
|
16272
|
-
const
|
|
16465
|
+
const plan2 = createWorkflowPlan(spec, compactWorkflowInputs(inputs));
|
|
16273
16466
|
if (out) {
|
|
16274
16467
|
const outPath = resolvePath(out);
|
|
16275
16468
|
await mkdir12(path42.dirname(outPath), { recursive: true });
|
|
16276
|
-
await writeFile2(outPath, jsonText(
|
|
16469
|
+
await writeFile2(outPath, jsonText(plan2));
|
|
16277
16470
|
}
|
|
16278
|
-
return format === "json" ? jsonText(
|
|
16471
|
+
return format === "json" ? jsonText(plan2) : renderWorkflowPlan(plan2);
|
|
16279
16472
|
}
|
|
16280
16473
|
async apply(planPath, {
|
|
16281
16474
|
dryRun = false,
|
|
@@ -16289,7 +16482,7 @@ class WorkflowRunner extends runner("workflow") {
|
|
|
16289
16482
|
const { artifact: artifact2 } = await writeWorkflowRunArtifact(workspace, report);
|
|
16290
16483
|
return renderWorkflowApply(artifact2, format);
|
|
16291
16484
|
};
|
|
16292
|
-
let
|
|
16485
|
+
let plan2;
|
|
16293
16486
|
try {
|
|
16294
16487
|
const parsed = JSON.parse(await readFile2(resolvePath(planPath), "utf8"));
|
|
16295
16488
|
if (!isWorkflowPlan2(parsed)) {
|
|
@@ -16302,7 +16495,7 @@ class WorkflowRunner extends runner("workflow") {
|
|
|
16302
16495
|
]);
|
|
16303
16496
|
return await renderApplyReport(report);
|
|
16304
16497
|
}
|
|
16305
|
-
|
|
16498
|
+
plan2 = parsed;
|
|
16306
16499
|
} catch (error) {
|
|
16307
16500
|
const report = failedApplyReport("unknown", [
|
|
16308
16501
|
{
|
|
@@ -16313,30 +16506,30 @@ class WorkflowRunner extends runner("workflow") {
|
|
|
16313
16506
|
]);
|
|
16314
16507
|
return await renderApplyReport(report);
|
|
16315
16508
|
}
|
|
16316
|
-
const spec = getWorkflowSpec(workflowSpecs,
|
|
16509
|
+
const spec = getWorkflowSpec(workflowSpecs, plan2.workflow);
|
|
16317
16510
|
if (!spec) {
|
|
16318
|
-
const report = failedApplyReport(
|
|
16511
|
+
const report = failedApplyReport(plan2.workflow, [
|
|
16319
16512
|
{
|
|
16320
16513
|
severity: "error",
|
|
16321
16514
|
code: "workflow-unknown",
|
|
16322
|
-
message: `Unknown workflow in plan: ${
|
|
16515
|
+
message: `Unknown workflow in plan: ${plan2.workflow}.`
|
|
16323
16516
|
}
|
|
16324
|
-
],
|
|
16517
|
+
], plan2);
|
|
16325
16518
|
return await renderApplyReport(report);
|
|
16326
16519
|
}
|
|
16327
16520
|
if (dryRun)
|
|
16328
|
-
return await renderApplyReport(createDryRunWorkflowApplyReport(
|
|
16521
|
+
return await renderApplyReport(createDryRunWorkflowApplyReport(plan2));
|
|
16329
16522
|
if (!registry) {
|
|
16330
|
-
const report = failedApplyReport(
|
|
16523
|
+
const report = failedApplyReport(plan2.workflow, [
|
|
16331
16524
|
{
|
|
16332
16525
|
severity: "error",
|
|
16333
16526
|
code: "workflow-registry-missing",
|
|
16334
16527
|
message: "Workflow apply requires a step runner registry."
|
|
16335
16528
|
}
|
|
16336
|
-
],
|
|
16529
|
+
], plan2);
|
|
16337
16530
|
return await renderApplyReport(report);
|
|
16338
16531
|
}
|
|
16339
|
-
return await renderApplyReport(await new WorkflowExecutor(registry).apply(
|
|
16532
|
+
return await renderApplyReport(await new WorkflowExecutor(registry).apply(plan2));
|
|
16340
16533
|
}
|
|
16341
16534
|
async validate(runIdOrPlan, {
|
|
16342
16535
|
format = "markdown",
|
|
@@ -16344,6 +16537,11 @@ class WorkflowRunner extends runner("workflow") {
|
|
|
16344
16537
|
execute
|
|
16345
16538
|
}) {
|
|
16346
16539
|
const loaded = await this.loadValidationTarget(runIdOrPlan, workspace);
|
|
16540
|
+
const doctor = await AkanContextAnalyzer.doctor(workspace, {
|
|
16541
|
+
strict: true,
|
|
16542
|
+
runIdOrPlan,
|
|
16543
|
+
changedFiles: loaded.changedFiles
|
|
16544
|
+
});
|
|
16347
16545
|
const report = await createWorkflowValidationRunReport({
|
|
16348
16546
|
workflow: loaded.plan?.workflow ?? loaded.workflow,
|
|
16349
16547
|
source: loaded.source,
|
|
@@ -16351,6 +16549,8 @@ class WorkflowRunner extends runner("workflow") {
|
|
|
16351
16549
|
commands: loaded.commands,
|
|
16352
16550
|
execute: execute ?? defaultValidationExecutor(workspace),
|
|
16353
16551
|
diagnostics: loaded.diagnostics,
|
|
16552
|
+
baselineDiagnostics: (doctor.baselineDiagnostics ?? []).map((diagnostic) => workflowDiagnosticFromDoctor(diagnostic, "baseline")),
|
|
16553
|
+
workflowDiagnostics: (doctor.workflowDiagnostics ?? []).map((diagnostic) => workflowDiagnosticFromDoctor(diagnostic, "workflow")),
|
|
16354
16554
|
repairActions: loaded.repairActions
|
|
16355
16555
|
});
|
|
16356
16556
|
await writeWorkflowRunArtifact(workspace, report);
|
|
@@ -16391,6 +16591,7 @@ class WorkflowRunner extends runner("workflow") {
|
|
|
16391
16591
|
plan: artifact2.plan,
|
|
16392
16592
|
commands: artifact2.recommendedValidationCommands.length ? artifact2.recommendedValidationCommands : workflowCommandsForPlan(artifact2.plan),
|
|
16393
16593
|
diagnostics: artifact2.diagnostics,
|
|
16594
|
+
changedFiles: artifact2.changedFiles.map((file) => file.path),
|
|
16394
16595
|
repairActions: []
|
|
16395
16596
|
};
|
|
16396
16597
|
}
|
|
@@ -16401,6 +16602,7 @@ class WorkflowRunner extends runner("workflow") {
|
|
|
16401
16602
|
plan: artifact2.plan,
|
|
16402
16603
|
commands: artifact2.plan ? workflowCommandsForPlan(artifact2.plan) : [],
|
|
16403
16604
|
diagnostics: artifact2.diagnostics,
|
|
16605
|
+
changedFiles: [],
|
|
16404
16606
|
repairActions: artifact2.repairActions
|
|
16405
16607
|
};
|
|
16406
16608
|
}
|
|
@@ -16416,6 +16618,7 @@ class WorkflowRunner extends runner("workflow") {
|
|
|
16416
16618
|
message: `Workflow validation source is not supported: ${runIdOrPlan}.`
|
|
16417
16619
|
}
|
|
16418
16620
|
],
|
|
16621
|
+
changedFiles: [],
|
|
16419
16622
|
repairActions: []
|
|
16420
16623
|
};
|
|
16421
16624
|
};
|
|
@@ -16428,6 +16631,7 @@ class WorkflowRunner extends runner("workflow") {
|
|
|
16428
16631
|
plan: parsed,
|
|
16429
16632
|
commands: workflowCommandsForPlan(parsed),
|
|
16430
16633
|
diagnostics: parsed.diagnostics,
|
|
16634
|
+
changedFiles: workflowPathsForPlanLike(parsed),
|
|
16431
16635
|
repairActions: []
|
|
16432
16636
|
};
|
|
16433
16637
|
}
|
|
@@ -16450,6 +16654,7 @@ class WorkflowRunner extends runner("workflow") {
|
|
|
16450
16654
|
message: `Could not read workflow validation source: ${runIdOrPlan}. ${error instanceof Error ? error.message : ""}`.trim()
|
|
16451
16655
|
}
|
|
16452
16656
|
],
|
|
16657
|
+
changedFiles: [],
|
|
16453
16658
|
repairActions: []
|
|
16454
16659
|
};
|
|
16455
16660
|
}
|
|
@@ -16516,7 +16721,8 @@ var readonlyMcpTools = [
|
|
|
16516
16721
|
{ name: "list_modules", inputSchema: emptySchema },
|
|
16517
16722
|
{
|
|
16518
16723
|
name: "get_module_context",
|
|
16519
|
-
|
|
16724
|
+
description: "Return module context. Pass app in monorepos to disambiguate duplicate module names.",
|
|
16725
|
+
inputSchema: { type: "object", properties: { app: stringProperty, module: stringProperty }, required: ["module"] }
|
|
16520
16726
|
},
|
|
16521
16727
|
{
|
|
16522
16728
|
name: "get_guideline",
|
|
@@ -16651,6 +16857,7 @@ class ContextRunner extends runner("context") {
|
|
|
16651
16857
|
}
|
|
16652
16858
|
if (name === "get_workspace_summary" || name === "list_apps" || name === "list_modules" || name === "get_module_context") {
|
|
16653
16859
|
const context = await AkanContextAnalyzer.analyze(workspace, {
|
|
16860
|
+
app: args.app ?? null,
|
|
16654
16861
|
module: args.module ?? null,
|
|
16655
16862
|
includeAbstractContent: name === "get_module_context"
|
|
16656
16863
|
});
|
|
@@ -16660,7 +16867,28 @@ class ContextRunner extends runner("context") {
|
|
|
16660
16867
|
return context.apps;
|
|
16661
16868
|
if (name === "list_modules")
|
|
16662
16869
|
return AkanContextAnalyzer.findModules(context);
|
|
16663
|
-
|
|
16870
|
+
const modules = AkanContextAnalyzer.findModules(context, args.module, {
|
|
16871
|
+
app: args.app ?? null
|
|
16872
|
+
});
|
|
16873
|
+
if (name === "get_module_context" && !args.app && modules.length > 1) {
|
|
16874
|
+
return {
|
|
16875
|
+
diagnostics: [
|
|
16876
|
+
{
|
|
16877
|
+
severity: "error",
|
|
16878
|
+
code: "module-context-ambiguous",
|
|
16879
|
+
message: `Multiple modules match "${args.module}". Re-run get_module_context with an app argument.`,
|
|
16880
|
+
input: "app"
|
|
16881
|
+
}
|
|
16882
|
+
],
|
|
16883
|
+
candidates: modules.map((module) => ({
|
|
16884
|
+
app: module.sysName,
|
|
16885
|
+
sysType: module.sysType,
|
|
16886
|
+
module: module.name,
|
|
16887
|
+
path: module.path
|
|
16888
|
+
}))
|
|
16889
|
+
};
|
|
16890
|
+
}
|
|
16891
|
+
return modules;
|
|
16664
16892
|
}
|
|
16665
16893
|
if (name === "get_guideline")
|
|
16666
16894
|
return await Prompter.getInstruction(stringArg(args, "name"));
|
|
@@ -16689,6 +16917,16 @@ class ContextRunner extends runner("context") {
|
|
|
16689
16917
|
validationFailureScopes: ["workspace-config", "environment", "source-change", "unknown"],
|
|
16690
16918
|
artifactChainFields: ["planPath", "applyReportPath", "repairReportPath", "runId", "validationTarget", "next"],
|
|
16691
16919
|
diagnosticScopes: ["baseline", "workflow", "unknown"],
|
|
16920
|
+
validationStatuses: {
|
|
16921
|
+
sourceStatus: ["passed", "failed", "unknown"],
|
|
16922
|
+
workspaceStatus: ["passed", "failed", "unknown"],
|
|
16923
|
+
overallStatus: ["passed", "failed", "blocked-by-workspace-config", "blocked-by-environment"],
|
|
16924
|
+
knownBlockers: "Repeated workspace-config/environment failures are summarized before command output."
|
|
16925
|
+
},
|
|
16926
|
+
moduleContextInputs: {
|
|
16927
|
+
module: "required",
|
|
16928
|
+
app: "recommended in monorepos; required when module names are ambiguous"
|
|
16929
|
+
},
|
|
16692
16930
|
generatedFreshnessStatuses: ["fresh", "stale", "missing", "unknown"],
|
|
16693
16931
|
directEditFallbackPolicy: applyFirstPolicy,
|
|
16694
16932
|
applyReportFields: [
|
|
@@ -16714,12 +16952,12 @@ class ContextRunner extends runner("context") {
|
|
|
16714
16952
|
const workflow2 = stringArg(args, "workflow");
|
|
16715
16953
|
const inputs = workflowInputsArg(args);
|
|
16716
16954
|
const planPath = typeof args.out === "string" && args.out ? args.out : defaultWorkflowPlanPath(workflow2, inputs);
|
|
16717
|
-
const
|
|
16955
|
+
const plan2 = parseJsonOutput(await new WorkflowRunner().plan(workflow2, inputs, {
|
|
16718
16956
|
format: "json",
|
|
16719
16957
|
out: workspacePath(workspace, planPath)
|
|
16720
16958
|
}));
|
|
16721
16959
|
return {
|
|
16722
|
-
...
|
|
16960
|
+
...plan2,
|
|
16723
16961
|
planPath,
|
|
16724
16962
|
next: { tool: "apply_workflow", args: { planPath } },
|
|
16725
16963
|
policy: applyFirstPolicy
|