@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/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 capitalize3 } from "akanjs/common";
4779
+ import { capitalize as capitalize4 } from "akanjs/common";
4780
4780
 
4781
- // pkgs/@akanjs/devkit/workflow/index.ts
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: workflowStatus([...diagnostics, ...commandDiagnostics]) === "failed" ? "failed" : commandStatus(results),
4960
+ status: statuses.overallStatus === "passed" ? "passed" : "failed",
4961
+ ...statuses,
4962
+ knownBlockers: createKnownBlockers(results, scopedDiagnostics),
5138
4963
  commands: results,
5139
- diagnostics: [...diagnostics, ...commandDiagnostics],
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 = `*/lib/${module}/${moduleClassName}.Template.tsx`;
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 renderPrimitiveReport = (report, format = "markdown") => format === "json" ? jsonText(report) : renderPrimitiveWriteReport(report);
5540
- var getSysRoot = (sys2) => `${sys2.type}s/${sys2.name}`;
5541
- var sourceFile = (sys2, path10, action, reason) => ({
5542
- path: `${getSysRoot(sys2)}/${path10}`,
5543
- action,
5544
- reason
5545
- });
5546
- var generatedFilesForSync = (sys2, reason = "Generated files may change after sync.") => generatedFilePathsForTarget(getSysRoot(sys2), reason);
5547
- var validationCommandsForTarget = (target) => [
5548
- { command: `akan sync ${target}`, reason: "Refresh generated Akan files from source conventions." },
5549
- { command: `akan lint ${target}`, reason: "Validate formatting, imports, and static lint rules." }
5550
- ];
5551
- var nextActionsForTarget = (target) => [
5552
- { command: `akan sync ${target}`, reason: "Refresh generated Akan files after source changes." },
5553
- { command: `akan lint ${target}`, reason: "Validate the target after generated files are refreshed." }
5554
- ];
5555
- var createPassedPrimitiveReport = ({
5556
- command,
5557
- changedFiles,
5558
- generatedFiles,
5559
- target,
5560
- nextActions
5561
- }) => createPrimitiveWriteReport({
5562
- command,
5563
- changedFiles,
5564
- generatedFiles: generatedFiles ?? [],
5565
- validationCommands: validationCommandsForTarget(target),
5566
- diagnostics: [],
5567
- nextActions: nextActions ?? nextActionsForTarget(target)
5568
- });
5569
- var scalarChangedFiles = (sys2, scalarName, files) => Object.values(files).map((file) => sourceFile(sys2, `lib/__scalar/${scalarName}/${file.filename}`, "create", "Scalar source file was created."));
5570
- var titleize = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
5571
- var lowerlize = (value) => `${value.slice(0, 1).toLowerCase()}${value.slice(1)}`;
5572
- var compactDiagnostics = (diagnostics) => diagnostics.filter((diagnostic) => !!diagnostic);
5573
- var normalizeFieldType = (typeName) => {
5574
- const normalizedTypes = {
5575
- string: "String",
5576
- boolean: "Boolean",
5577
- date: "Date",
5578
- int: "Int",
5579
- integer: "Int",
5580
- float: "Float",
5581
- double: "Float",
5582
- decimal: "Float"
5583
- };
5584
- return normalizedTypes[typeName.toLowerCase()] ?? typeName;
5585
- };
5586
- var ensureBaseTypeImport = (content, typeName) => {
5587
- if (typeName !== "Int" && typeName !== "Float")
5588
- return content;
5589
- if (new RegExp(`import \\{[^}]*\\b${typeName}\\b[^}]*\\} from "akanjs/base";`).test(content))
5590
- return content;
5591
- const baseImport = /import \{ ([^}]+) \} from "akanjs\/base";/.exec(content);
5592
- if (baseImport) {
5593
- const names = baseImport[1].split(",").map((name) => name.trim()).filter(Boolean);
5594
- return content.replace(baseImport[0], `import { ${[...names, typeName].sort().join(", ")} } from "akanjs/base";`);
5595
- }
5596
- return `import { ${typeName} } from "akanjs/base";
5597
- ${content}`;
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 `import type { ${typeName} } from "${constantPath}";
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 = (plan, key) => {
5823
- const value = plan.inputs[key];
5940
+ var planInputString = (plan2, key) => {
5941
+ const value = plan2.inputs[key];
5824
5942
  return typeof value === "string" ? value : "";
5825
5943
  };
5826
- var expandWorkflowTarget = (target, plan) => {
5827
- const app = planInputString(plan, "app");
5828
- const module = planInputString(plan, "module");
5829
- const moduleClass = module ? capitalize3(module) : "<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 = (plan) => plan.predictedChanges.map((change) => expandWorkflowTarget(change.target, plan));
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: `${capitalize3(module.kind)} module ${sys2.name}:${module.name} should include ${module.abstract.path}`,
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: `${capitalize3(module.kind)} module ${sys2.name}:${module.name} is missing required files: ${missingFiles.join(", ")}`,
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 source = opts.includeSystemProvider ? `import type { LayoutProps, PageProps } from "akanjs/client";
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, source);
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 source = await readIfExists(absFile);
6535
- if (source === null)
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(source, absFile);
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(source);
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(source);
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(source);
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(source);
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(source, absFile) {
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(source);
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 source = await Bun.file(realPath).text();
6727
- const hasMacroAttr = MACRO_ATTR_RE.test(source);
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 (source.includes(b)) {
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(source, barrels, analyzer);
6855
+ const rewritten = await rewriteBarrelImports(source2, barrels, analyzer);
6738
6856
  if (rewritten !== null)
6739
- source = rewritten;
6857
+ source2 = rewritten;
6740
6858
  }
6741
6859
  }
6742
6860
  if (pipeAfter) {
6743
- const piped = await pipeAfter(source, { path: realPath });
6861
+ const piped = await pipeAfter(source2, { path: realPath });
6744
6862
  if (piped !== null)
6745
- source = piped;
6863
+ source2 = piped;
6746
6864
  }
6747
- return { contents: source, loader };
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 (source, barrels, analyzer) => {
6924
- const statements = findImportStatements(source);
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 = source;
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 = (source) => {
7064
+ var findImportStatements = (source2) => {
6947
7065
  const statements = [];
6948
- const sourceFile2 = ts4.createSourceFile("barrel-imports.tsx", source, ts4.ScriptTarget.Latest, true, ts4.ScriptKind.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: source.slice(importClause.getStart(sourceFile2), importClause.end).trim(),
7080
+ clause: source2.slice(importClause.getStart(sourceFile2), importClause.end).trim(),
6963
7081
  specifier: statement.moduleSpecifier.text,
6964
- trailingSemicolon: source.slice(statement.moduleSpecifier.end, statement.end).includes(";"),
6965
- raw: source.slice(statementStart, statementEnd)
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, source) {
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(source);
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 source = await this.#getRewrittenSource(absPath, content);
7247
- const imports = await this.#getImports(absPath, source);
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(source, args) {
7284
- if (!USE_CLIENT_RE2.test(source))
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(source);
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 source = await Bun.file(absEntry).text();
7553
+ const source2 = await Bun.file(absEntry).text();
7436
7554
  const transpiler = new Bun.Transpiler({ loader: this.#loaderForEntry(absEntry) });
7437
- return transpiler.scan(source).exports;
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 source = await Bun.file(output.path).text();
7485
- const rewritten = ClientEntriesBundler.rewriteExternalImportSpecifiers(source, this.#externalAliases);
7486
- if (rewritten !== source)
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(source, aliases) {
7491
- let rewritten = source;
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 source = await Bun.file(absEntry).text();
7864
+ const source2 = await Bun.file(absEntry).text();
7747
7865
  const transpiler = new Bun.Transpiler({ loader: this.#loaderFor(absEntry) });
7748
- return transpiler.scan(source).exports;
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(source) {
7877
+ static normalizeNamedDefaultFunctionForFastRefresh(source2) {
7760
7878
  let changed = false;
7761
7879
  const defaultNames = [];
7762
- const next = source.replace(/(^|\n)(\s*)export\s+default\s+(async\s+)?function\s+([A-Za-z_$][\w$]*)(?=\s*(?:<|\())/g, (match, lineStart, indent, asyncKeyword, name) => {
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 source = fs3.readFileSync(path21.resolve(moduleAbsPath), "utf8");
7991
- const sourceFile2 = ts5.createSourceFile(moduleAbsPath, source, ts5.ScriptTarget.Latest, true, PagesEntrySourceGenerator.#scriptKind(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(source) {
8252
- return source.replace(/<\/script/gi, "<\\/script");
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 source = content;
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
- source = rewritten;
8606
+ source2 = rewritten;
8489
8607
  } catch {}
8490
8608
  }
8491
8609
  let imports;
8492
8610
  try {
8493
- imports = this.#transpiler.scanImports(source);
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 = capitalize4(rawModel);
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 capitalize4 = (value) => `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
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(source, filePath) {
9027
- const sourceFile2 = ts6.createSourceFile(filePath, source, ts6.ScriptTarget.Latest, true, ts6.ScriptKind.TSX);
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 source;
9701
+ let source2;
9584
9702
  try {
9585
- source = await Bun.file(args.path).text();
9703
+ source2 = await Bun.file(args.path).text();
9586
9704
  } catch {
9587
9705
  return;
9588
9706
  }
9589
- const stubbed = transformUseClient(source, {
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: (source, args) => transformUseClient(source, {
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(source) {
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: source,
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 source = await Bun.file(args.path).text();
9720
- const transformed = PagesBundleBuilder.transformServerUseClientFetchSource(source);
9721
- if (transformed === source)
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(source) {
9729
- if (!source.includes(`with { type: "macro" }`) || !source.includes("FetchClient.build"))
9730
- return source;
9731
- return source.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 });");
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 = 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 capitalize5 } from "akanjs/common";
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${capitalize5(key)}`, value]));
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 capitalize6 = (value) => value.slice(0, 1).toUpperCase() + value.slice(1);
11808
- var createDependencyKey = (refName, kind) => `${refName}${capitalize6(kind)}`;
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 executor = type === "app" ? AppExecutor.from(workspace, name) : LibExecutor.from(workspace, name);
12289
- const modules = await executor.getModules();
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: `${executor.name}:${name2}`, value: name2 }))
12410
+ choices: modules.map((name2) => ({ name: `${executor2.name}:${name2}`, value: name2 }))
12293
12411
  });
12294
- return ModuleExecutor.from(executor, moduleName);
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 = (source, filePath, includeExports) => {
12554
- const scannedImports = getTranspiler(filePath).scanImports(source).map((imp) => imp.path).filter(Boolean);
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 source.matchAll(typeOnlyModuleRegex)) {
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 source.matchAll(exportFromRegex)) {
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 source.matchAll(importDeclarationRegex)) {
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 source = readFileSync4(filePath, "utf-8");
12597
- for (const importPath of scanModuleSpecifiers(source, filePath, false)) {
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 source = program2.getSourceFile(filePath);
12647
- if (!source)
12764
+ const source2 = program2.getSourceFile(filePath);
12765
+ if (!source2)
12648
12766
  return;
12649
12767
  if (!sourceLineCache.has(filePath)) {
12650
- sourceLineCache.set(filePath, source.getFullText().split(`
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 (!source)
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(source, node.getStart());
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(source);
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, source, target) {
14116
- return [...config.port ? ["-P", config.port.toString()] : [], source, target];
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 capitalize9, randomPicks } from "akanjs/common";
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 capitalize7 } from "akanjs/common";
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 ?? capitalize7(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 ?? capitalize7(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 ?? capitalize7(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 capitalize8 } from "akanjs/common";
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__.${capitalize8(type)}.tsx`,
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}.${capitalize8(type)}.tsx`,
14854
- content: await module.sys.readFile(`lib/${module.name}/${capitalize8(module.name)}.${capitalize8(type)}.tsx`)
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 executor = ModuleExecutor.from(sys3, name);
14932
- const files = await this.moduleRunner.createModuleTemplate(executor);
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(executor, { app: sys3, basePath: null, single: false });
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 = capitalize9(mod.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 = capitalize9(mod.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 = capitalize9(mod.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 capitalize10 } from "akanjs/common";
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: `${capitalize10(input7.field ?? "")}` }, { enumValues: values });
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 = capitalize10(input7.module);
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}${capitalize10(input7.field)}`;
15216
- const enumName = `${lowerlize(moduleClassName)}${capitalize10(input7.field)}`;
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 executor = execute ?? defaultExecutor(workspace);
15316
- const report = await this.createRepairReport(kind, { workspace, app, module, target, execute: executor });
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: { type: "string", description: "Optional default value for the field." }
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, plan) => createWorkflowApplyReport({
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: plan ?? failedPlan(workflow2, diagnostics)
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 plan = createWorkflowPlan(spec, compactWorkflowInputs(inputs));
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(plan));
16469
+ await writeFile2(outPath, jsonText(plan2));
16277
16470
  }
16278
- return format === "json" ? jsonText(plan) : renderWorkflowPlan(plan);
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 plan;
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
- plan = parsed;
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, plan.workflow);
16509
+ const spec = getWorkflowSpec(workflowSpecs, plan2.workflow);
16317
16510
  if (!spec) {
16318
- const report = failedApplyReport(plan.workflow, [
16511
+ const report = failedApplyReport(plan2.workflow, [
16319
16512
  {
16320
16513
  severity: "error",
16321
16514
  code: "workflow-unknown",
16322
- message: `Unknown workflow in plan: ${plan.workflow}.`
16515
+ message: `Unknown workflow in plan: ${plan2.workflow}.`
16323
16516
  }
16324
- ], plan);
16517
+ ], plan2);
16325
16518
  return await renderApplyReport(report);
16326
16519
  }
16327
16520
  if (dryRun)
16328
- return await renderApplyReport(createDryRunWorkflowApplyReport(plan));
16521
+ return await renderApplyReport(createDryRunWorkflowApplyReport(plan2));
16329
16522
  if (!registry) {
16330
- const report = failedApplyReport(plan.workflow, [
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
- ], plan);
16529
+ ], plan2);
16337
16530
  return await renderApplyReport(report);
16338
16531
  }
16339
- return await renderApplyReport(await new WorkflowExecutor(registry).apply(plan));
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
- inputSchema: { type: "object", properties: { module: stringProperty }, required: ["module"] }
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
- return AkanContextAnalyzer.findModules(context, args.module);
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 plan = parseJsonOutput(await new WorkflowRunner().plan(workflow2, inputs, {
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
- ...plan,
16960
+ ...plan2,
16723
16961
  planPath,
16724
16962
  next: { tool: "apply_workflow", args: { planPath } },
16725
16963
  policy: applyFirstPolicy