@akanjs/cli 2.3.9-rc.3 → 2.3.9-rc.5

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.
@@ -3396,28 +3396,28 @@ class SysExecutor extends Executor {
3396
3396
  return scalarModules;
3397
3397
  }
3398
3398
  async getViewComponents() {
3399
- const viewComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.View.tsx`).exists());
3399
+ const viewComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${capitalize(name)}.View.tsx`).exists());
3400
3400
  return viewComponents;
3401
3401
  }
3402
3402
  async getUnitComponents() {
3403
- const unitComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.Unit.tsx`).exists());
3403
+ const unitComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${capitalize(name)}.Unit.tsx`).exists());
3404
3404
  return unitComponents;
3405
3405
  }
3406
3406
  async getTemplateComponents() {
3407
- const templateComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.Template.tsx`).exists());
3407
+ const templateComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${capitalize(name)}.Template.tsx`).exists());
3408
3408
  return templateComponents;
3409
3409
  }
3410
3410
  async getViewsSourceCode() {
3411
3411
  const viewComponents = await this.getViewComponents();
3412
- return Promise.all(viewComponents.map((viewComponent) => this.getLocalFile(`lib/${viewComponent}/${viewComponent}.View.tsx`)));
3412
+ return Promise.all(viewComponents.map((viewComponent) => this.getLocalFile(`lib/${viewComponent}/${capitalize(viewComponent)}.View.tsx`)));
3413
3413
  }
3414
3414
  async getUnitsSourceCode() {
3415
3415
  const unitComponents = await this.getUnitComponents();
3416
- return Promise.all(unitComponents.map((unitComponent) => this.getLocalFile(`lib/${unitComponent}/${unitComponent}.Unit.tsx`)));
3416
+ return Promise.all(unitComponents.map((unitComponent) => this.getLocalFile(`lib/${unitComponent}/${capitalize(unitComponent)}.Unit.tsx`)));
3417
3417
  }
3418
3418
  async getTemplatesSourceCode() {
3419
3419
  const templateComponents = await this.getTemplateComponents();
3420
- return Promise.all(templateComponents.map((templateComponent) => this.getLocalFile(`lib/${templateComponent}/${templateComponent}.Template.tsx`)));
3420
+ return Promise.all(templateComponents.map((templateComponent) => this.getLocalFile(`lib/${templateComponent}/${capitalize(templateComponent)}.Template.tsx`)));
3421
3421
  }
3422
3422
  async getScalarConstantFiles() {
3423
3423
  const scalarModules = await this.getScalarModules();
@@ -4778,238 +4778,11 @@ class AkanAppHost {
4778
4778
  // pkgs/@akanjs/devkit/akanContext.ts
4779
4779
  import { readdir } from "fs/promises";
4780
4780
  import path10 from "path";
4781
- import { capitalize as capitalize3 } from "akanjs/common";
4781
+ import { capitalize as capitalize4 } from "akanjs/common";
4782
4782
 
4783
- // pkgs/@akanjs/devkit/workflow/index.ts
4784
- import { capitalize as capitalize2 } from "akanjs/common";
4783
+ // pkgs/@akanjs/devkit/workflow/utils.ts
4785
4784
  var jsonText = (value, { trailingNewline = true } = {}) => `${JSON.stringify(value, null, 2)}${trailingNewline ? `
4786
4785
  ` : ""}`;
4787
- var surfaceModes = new Set(["infer", "include", "skip"]);
4788
- var parseStringList = (value) => {
4789
- if (Array.isArray(value)) {
4790
- const values = value.filter((item) => typeof item === "string" && item.trim().length > 0);
4791
- return values.length === value.length ? values : null;
4792
- }
4793
- if (typeof value !== "string")
4794
- return null;
4795
- return value.split(",").map((item) => item.trim()).filter(Boolean);
4796
- };
4797
- var normalizeInputValue = (name, spec, value) => {
4798
- if (spec.type === "string")
4799
- return typeof value === "string" && value.length > 0 ? value : null;
4800
- if (spec.type === "string-list") {
4801
- const values = parseStringList(value);
4802
- return values && values.length > 0 ? values : null;
4803
- }
4804
- if (typeof value === "string" && surfaceModes.has(value))
4805
- return value;
4806
- throw new Error(`Unsupported workflow input value for ${name}`);
4807
- };
4808
- var listWorkflowSpecs = (specs) => [...specs].sort((a, b) => a.name.localeCompare(b.name));
4809
- var getWorkflowSpec = (specs, name) => specs.find((spec) => spec.name === name) ?? null;
4810
- var compactWorkflowInputs = (inputs) => Object.fromEntries(Object.entries(inputs).filter(([, value]) => value !== null && value !== ""));
4811
- var addFieldComponentForType = (typeName) => {
4812
- const normalizedType = normalizeFieldType(typeName);
4813
- if (normalizedType === "Boolean")
4814
- return "Field.ToggleSelect";
4815
- if (normalizedType === "Date")
4816
- return "Field.Date";
4817
- if (normalizedType === "Int" || normalizedType === "Float")
4818
- return "Field.ToggleSelect or Field.Text";
4819
- if (typeName.toLowerCase() === "enum")
4820
- return "Field.ToggleSelect";
4821
- return "Field.Text";
4822
- };
4823
- var createAddFieldRecommendations = (inputs) => {
4824
- const app = typeof inputs.app === "string" ? inputs.app : "<app-or-lib>";
4825
- const module = typeof inputs.module === "string" ? inputs.module : "<module>";
4826
- const field = typeof inputs.field === "string" ? inputs.field : "<field>";
4827
- const typeName = typeof inputs.type === "string" ? inputs.type : null;
4828
- if (!typeName)
4829
- return [];
4830
- const normalizedType = typeName.toLowerCase() === "enum" ? "enum" : normalizeFieldType(typeName);
4831
- const constantPath = `*/lib/${module}/${module}.constant.ts`;
4832
- const dictionaryPath = `*/lib/${module}/${module}.dictionary.ts`;
4833
- const templatePath = `*/lib/${module}/${capitalize2(module)}.Template.tsx`;
4834
- return [
4835
- ...normalizedType === "Int" || normalizedType === "Float" ? [
4836
- {
4837
- code: "add-field-import",
4838
- kind: "import",
4839
- target: constantPath,
4840
- confidence: "high",
4841
- message: `Import ${normalizedType} from "akanjs/base" before writing field(${normalizedType}).`
4842
- }
4843
- ] : [],
4844
- {
4845
- code: "add-field-placement-constant",
4846
- kind: "placement",
4847
- target: constantPath,
4848
- confidence: "high",
4849
- message: `Insert ${field}: field(${normalizedType}) in ${capitalize2(module)}Input.`
4850
- },
4851
- {
4852
- code: "add-field-placement-dictionary",
4853
- kind: "placement",
4854
- target: dictionaryPath,
4855
- confidence: "high",
4856
- message: `Add dictionary labels for ${module}.${field}.`
4857
- },
4858
- {
4859
- code: "add-field-component",
4860
- kind: "ui-component",
4861
- target: templatePath,
4862
- confidence: normalizedType === "Int" || normalizedType === "Float" ? "medium" : "high",
4863
- message: `Recommended UI component for ${field} (${normalizedType}): ${addFieldComponentForType(typeName)}.`
4864
- },
4865
- {
4866
- code: "add-field-ui-manual-review",
4867
- kind: "manual-action",
4868
- target: templatePath,
4869
- action: `Review ${app}:${module} Template/Unit/View/Store surfaces and add ${field} only where the existing pattern is clear.`,
4870
- confidence: "medium",
4871
- message: "UI surface edits are planned as manual-review unless a safe existing field-list pattern is detected."
4872
- }
4873
- ];
4874
- };
4875
- var createWorkflowPlanRecommendations = (spec, inputs) => [
4876
- {
4877
- code: "workflow-apply-first",
4878
- kind: "auto-apply",
4879
- confidence: "high",
4880
- action: "Call apply_workflow with the MCP planPath before editing source files directly.",
4881
- message: `Apply the ${spec.name} plan through apply_workflow when MCP returns planPath or next.tool=apply_workflow.`
4882
- },
4883
- {
4884
- code: "workflow-validate-apply-report",
4885
- kind: "validation",
4886
- confidence: "high",
4887
- action: "After apply_workflow, call run_validation with validationTarget when present; otherwise use applyReportPath.",
4888
- message: "Validate the apply report artifact so diagnostics and recommendations follow the actual apply result."
4889
- },
4890
- ...spec.name === "add-field" ? createAddFieldRecommendations(inputs) : []
4891
- ];
4892
- var createWorkflowPlan = (spec, rawInputs) => {
4893
- const inputs = {};
4894
- const diagnostics = [];
4895
- for (const [name, inputSpec] of Object.entries(spec.inputs)) {
4896
- const rawValue = rawInputs[name];
4897
- if (rawValue === undefined || rawValue === null || rawValue === "") {
4898
- if (inputSpec.required) {
4899
- diagnostics.push({
4900
- severity: "error",
4901
- code: "workflow-input-missing",
4902
- input: name,
4903
- message: `Workflow ${spec.name} requires input "${name}".`
4904
- });
4905
- }
4906
- continue;
4907
- }
4908
- const value = normalizeInputValue(name, inputSpec, rawValue);
4909
- if (value === null) {
4910
- diagnostics.push({
4911
- severity: "error",
4912
- code: "workflow-input-invalid",
4913
- input: name,
4914
- message: `Workflow ${spec.name} input "${name}" must be ${inputSpec.type}.`
4915
- });
4916
- continue;
4917
- }
4918
- if (inputSpec.allowedValues && typeof value === "string" && !inputSpec.allowedValues.includes(value)) {
4919
- diagnostics.push({
4920
- severity: "error",
4921
- code: "workflow-input-not-allowed",
4922
- input: name,
4923
- message: `Workflow ${spec.name} input "${name}" must be one of: ${inputSpec.allowedValues.join(", ")}.`
4924
- });
4925
- continue;
4926
- }
4927
- inputs[name] = value;
4928
- }
4929
- const fieldType = inputs.type;
4930
- if (spec.name === "add-field" && typeof fieldType === "string" && (fieldType.toLowerCase() === "number" || fieldType.toLowerCase() === "numeric")) {
4931
- diagnostics.push({
4932
- severity: "error",
4933
- code: "primitive-field-type-unsupported",
4934
- input: "type",
4935
- message: `Field type "${fieldType}" is ambiguous in Akan. Use Int for integer fields or Float for decimal fields.`
4936
- });
4937
- }
4938
- return {
4939
- schemaVersion: 1,
4940
- workflow: spec.name,
4941
- mode: "plan",
4942
- inputs,
4943
- optionalSurfaces: spec.optionalSurfaces ?? {},
4944
- steps: spec.steps,
4945
- predictedChanges: spec.predictedChanges,
4946
- validation: spec.validation,
4947
- diagnostics,
4948
- recommendations: createWorkflowPlanRecommendations(spec, inputs),
4949
- requiresApproval: true
4950
- };
4951
- };
4952
- var renderWorkflowList = (specs) => [
4953
- "# Akan Workflows",
4954
- "",
4955
- ...specs.flatMap((spec) => [`- \`${spec.name}\`: ${spec.description}`, ` - When: ${spec.whenToUse}`]),
4956
- ""
4957
- ].join(`
4958
- `);
4959
- var renderWorkflowExplain = (spec) => [
4960
- `# Workflow: ${spec.name}`,
4961
- "",
4962
- spec.description,
4963
- "",
4964
- "## When To Use",
4965
- spec.whenToUse,
4966
- "",
4967
- "## Inputs",
4968
- ...Object.entries(spec.inputs).map(([name, input2]) => `- \`${name}\`${input2.required ? " (required)" : ""}: ${input2.description}${input2.allowedValues ? ` Allowed: ${input2.allowedValues.join(", ")}.` : ""}`),
4969
- "",
4970
- "## Optional Surfaces",
4971
- ...Object.entries(spec.optionalSurfaces ?? {}).map(([name, mode]) => `- \`${name}\`: ${mode}`),
4972
- "",
4973
- "## Steps",
4974
- ...spec.steps.map((step, index) => `${index + 1}. \`${step.id}\` (${step.tool}): ${step.description}`),
4975
- "",
4976
- "## Validation",
4977
- ...spec.validation.map((validation) => `- \`${validation.command}\`: ${validation.reason}`),
4978
- ""
4979
- ].join(`
4980
- `);
4981
- var renderWorkflowPlan = (plan) => [
4982
- `# Workflow Plan: ${plan.workflow}`,
4983
- "",
4984
- `- Mode: ${plan.mode}`,
4985
- `- Requires approval: ${plan.requiresApproval}`,
4986
- "",
4987
- "## Inputs",
4988
- ...Object.entries(plan.inputs).map(([name, value]) => `- \`${name}\`: ${Array.isArray(value) ? value.join(", ") : value}`),
4989
- "",
4990
- "## Optional Surfaces",
4991
- ...Object.entries(plan.optionalSurfaces).map(([name, mode]) => `- \`${name}\`: ${mode}`),
4992
- "",
4993
- "## Steps",
4994
- ...plan.steps.map((step, index) => `${index + 1}. \`${step.id}\` (${step.tool}): ${step.description}`),
4995
- "",
4996
- "## Predicted Changes",
4997
- ...plan.predictedChanges.map((change) => {
4998
- const scope = change.applyScope ? ` (${change.applyScope})` : "";
4999
- return `- \`${change.action}\`${scope} ${change.target}: ${change.reason}`;
5000
- }),
5001
- "",
5002
- "## Validation",
5003
- ...plan.validation.map((validation) => `- \`${validation.command}\`: ${validation.reason}`),
5004
- "",
5005
- "## Diagnostics",
5006
- ...plan.diagnostics.length ? plan.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
5007
- "",
5008
- "## Recommendations",
5009
- ...plan.recommendations.length ? plan.recommendations.map((recommendation) => `- [${recommendation.kind}] ${recommendation.message}`) : ["- none"],
5010
- ""
5011
- ].join(`
5012
- `);
5013
4786
  var workflowStatus = (diagnostics) => diagnostics.some((diagnostic) => diagnostic.severity === "error") ? "failed" : "passed";
5014
4787
  var commandStatus = (commands) => commands.some((command) => command.status === "failed") ? "failed" : "passed";
5015
4788
  var uniqueBy = (values, key) => {
@@ -5022,6 +4795,9 @@ var uniqueBy = (values, key) => {
5022
4795
  return true;
5023
4796
  });
5024
4797
  };
4798
+ var compactDiagnostics = (diagnostics) => diagnostics.filter((diagnostic) => !!diagnostic);
4799
+
4800
+ // pkgs/@akanjs/devkit/workflow/artifacts.ts
5025
4801
  var createWorkflowApplyReport = ({
5026
4802
  workflow,
5027
4803
  mode,
@@ -5104,6 +4880,66 @@ var readWorkflowRunArtifact = async (workspace, runId) => {
5104
4880
  throw new Error(`Workflow run artifact does not exist: ${artifactPath}`);
5105
4881
  return await workspace.readJson(artifactPath);
5106
4882
  };
4883
+ var failedCommandScopes = (commands) => commands.filter((command) => command.status === "failed").map((command) => command.failureScope ?? "unknown");
4884
+ var errorDiagnosticScopes = (diagnostics) => diagnostics.filter((diagnostic) => diagnostic.severity === "error").map((diagnostic) => diagnostic.failureScope ?? (diagnostic.scope === "baseline" ? "workspace-config" : diagnostic.scope === "workflow" ? "source-change" : "unknown"));
4885
+ var hasScopeFailure = (scopes, scope, diagnostics = []) => scopes.includes(scope) || diagnostics.some((diagnostic) => diagnostic.severity === "error" && diagnostic.failureScope === scope);
4886
+ var statusForScope = (commands, diagnostics, scopes, expectedScope) => {
4887
+ const hasCommands = commands.length > 0 || diagnostics.length > 0;
4888
+ if (!hasCommands)
4889
+ return "unknown";
4890
+ return hasScopeFailure(scopes, expectedScope, diagnostics) ? "failed" : "passed";
4891
+ };
4892
+ var statusForValidationKind = (commands, kind) => {
4893
+ const matching = commands.filter((command) => command.kind === kind);
4894
+ if (matching.length === 0)
4895
+ return "unknown";
4896
+ return matching.some((command) => command.status === "failed") ? "failed" : "passed";
4897
+ };
4898
+ var createKnownBlockers = (commands, diagnostics) => {
4899
+ const commandBlockers = commands.filter((command) => command.status === "failed" && (command.failureScope === "workspace-config" || command.failureScope === "environment")).map((command) => ({
4900
+ code: `workflow-validation-${command.failureScope}`,
4901
+ message: `${command.failureScope === "environment" ? "Environment" : "Workspace configuration"} blocker: ${command.command}`,
4902
+ failureScope: command.failureScope ?? "unknown",
4903
+ command: command.command,
4904
+ kind: command.kind,
4905
+ count: 1
4906
+ }));
4907
+ const diagnosticBlockers = diagnostics.filter((diagnostic) => diagnostic.severity === "error" && (diagnostic.failureScope === "workspace-config" || diagnostic.failureScope === "environment" || diagnostic.scope === "baseline")).map((diagnostic) => ({
4908
+ code: diagnostic.code,
4909
+ message: diagnostic.message,
4910
+ failureScope: diagnostic.failureScope ?? "workspace-config",
4911
+ command: diagnostic.command,
4912
+ kind: diagnostic.kind,
4913
+ count: 1
4914
+ }));
4915
+ const grouped = new Map;
4916
+ for (const blocker of [...commandBlockers, ...diagnosticBlockers]) {
4917
+ const key = `${blocker.failureScope}:${blocker.code}:${blocker.command ?? ""}:${blocker.message}`;
4918
+ const existing = grouped.get(key);
4919
+ if (existing)
4920
+ existing.count += blocker.count;
4921
+ else
4922
+ grouped.set(key, blocker);
4923
+ }
4924
+ return [...grouped.values()];
4925
+ };
4926
+ var createValidationStatuses = (commands, diagnostics) => {
4927
+ const scopes = [...failedCommandScopes(commands), ...errorDiagnosticScopes(diagnostics)];
4928
+ const sourceStatus = statusForScope(commands, diagnostics, scopes, "source-change");
4929
+ const workspaceStatus = hasScopeFailure(scopes, "workspace-config", diagnostics) || hasScopeFailure(scopes, "environment", diagnostics) ? "failed" : commands.length || diagnostics.length ? "passed" : "unknown";
4930
+ 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";
4931
+ return {
4932
+ sourceStatus,
4933
+ workspaceStatus,
4934
+ overallStatus,
4935
+ summary: {
4936
+ sourceChange: sourceStatus,
4937
+ generatedSync: statusForValidationKind(commands, "sync"),
4938
+ workspaceConfig: statusForScope(commands, diagnostics, scopes, "workspace-config"),
4939
+ environment: statusForScope(commands, diagnostics, scopes, "environment")
4940
+ }
4941
+ };
4942
+ };
5107
4943
  var createWorkflowValidationRunReport = async ({
5108
4944
  runId = createWorkflowRunId("validation"),
5109
4945
  workflow,
@@ -5130,15 +4966,20 @@ var createWorkflowValidationRunReport = async ({
5130
4966
  failureScope: result.failureScope
5131
4967
  }
5132
4968
  ] : []);
4969
+ const reportDiagnostics = [...diagnostics, ...commandDiagnostics];
4970
+ const scopedDiagnostics = [...reportDiagnostics, ...baselineDiagnostics, ...workflowDiagnostics];
4971
+ const statuses = createValidationStatuses(results, scopedDiagnostics);
5133
4972
  return {
5134
4973
  schemaVersion: 1,
5135
4974
  runId,
5136
4975
  workflow,
5137
4976
  mode: "validate",
5138
4977
  source,
5139
- status: workflowStatus([...diagnostics, ...commandDiagnostics]) === "failed" ? "failed" : commandStatus(results),
4978
+ status: statuses.overallStatus === "passed" ? "passed" : "failed",
4979
+ ...statuses,
4980
+ knownBlockers: createKnownBlockers(results, scopedDiagnostics),
5140
4981
  commands: results,
5141
- diagnostics: [...diagnostics, ...commandDiagnostics],
4982
+ diagnostics: reportDiagnostics,
5142
4983
  baselineDiagnostics,
5143
4984
  workflowDiagnostics,
5144
4985
  repairActions: uniqueBy(repairActions, (action) => action.command),
@@ -5182,27 +5023,514 @@ var createDryRunWorkflowApplyReport = (plan) => {
5182
5023
  plan
5183
5024
  });
5184
5025
  };
5185
- var workflowStepKey = (workflow, stepId) => `${workflow}:${stepId}`;
5186
- var primitiveReportToWorkflowStepResult = (report) => ({
5187
- changedFiles: report.changedFiles,
5188
- generatedFiles: report.generatedFiles,
5189
- commands: report.validationCommands,
5190
- diagnostics: report.diagnostics,
5191
- recommendations: [],
5192
- nextActions: report.nextActions
5026
+ var createRepairReport = ({
5027
+ command,
5028
+ kind,
5029
+ target = null,
5030
+ diagnostics = [],
5031
+ repairActions = [],
5032
+ nextActions = [],
5033
+ commands = [],
5034
+ generatedFiles = [],
5035
+ syncedAt
5036
+ }) => ({
5037
+ schemaVersion: 1,
5038
+ command,
5039
+ kind,
5040
+ target,
5041
+ status: workflowStatus(diagnostics) === "failed" || commandStatus(commands) === "failed" ? "failed" : "passed",
5042
+ diagnostics,
5043
+ repairActions: uniqueBy(repairActions, (action) => action.command),
5044
+ nextActions: uniqueBy(nextActions, (action) => action.command),
5045
+ commands,
5046
+ generatedFiles,
5047
+ ...syncedAt ? { syncedAt } : {}
5193
5048
  });
5194
- var workflowStringInput = (value) => typeof value === "string" ? value : null;
5195
- var workflowStringListInput = (value) => Array.isArray(value) ? value.join(",") : null;
5196
- var resolveWorkflowSys = async (workspace, target) => {
5197
- if (!target)
5198
- return null;
5199
- const [apps, libs] = await workspace.getSyss();
5200
- if (apps.includes(target))
5201
- return AppExecutor.from(workspace, target);
5202
- if (libs.includes(target))
5203
- return LibExecutor.from(workspace, target);
5204
- return null;
5205
- };
5049
+ // pkgs/@akanjs/devkit/workflow/executor.ts
5050
+ import { capitalize as capitalize2 } from "akanjs/common";
5051
+
5052
+ // pkgs/@akanjs/devkit/workflow/primitive.ts
5053
+ var createPrimitiveWriteReport = ({
5054
+ command,
5055
+ status,
5056
+ changedFiles = [],
5057
+ generatedFiles = [],
5058
+ validationCommands = [],
5059
+ diagnostics = [],
5060
+ nextActions = []
5061
+ }) => ({
5062
+ schemaVersion: 1,
5063
+ command,
5064
+ status: status ?? (diagnostics.some((diagnostic) => diagnostic.severity === "error") ? "failed" : "passed"),
5065
+ changedFiles,
5066
+ generatedFiles,
5067
+ validationCommands,
5068
+ diagnostics,
5069
+ nextActions
5070
+ });
5071
+ var renderPrimitiveWriteReport = (report) => [
5072
+ `# Primitive Write: ${report.command}`,
5073
+ "",
5074
+ `- Status: ${report.status}`,
5075
+ "",
5076
+ "## Changed Files",
5077
+ ...report.changedFiles.length ? report.changedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
5078
+ "",
5079
+ "## Generated Files",
5080
+ ...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
5081
+ "",
5082
+ "## Validation Commands",
5083
+ ...report.validationCommands.length ? report.validationCommands.map((validation) => `- \`${validation.command}\`: ${validation.reason}`) : ["- none"],
5084
+ "",
5085
+ "## Diagnostics",
5086
+ ...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
5087
+ "",
5088
+ "## Next Actions",
5089
+ ...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
5090
+ ""
5091
+ ].join(`
5092
+ `);
5093
+ var renderPrimitiveReport = (report, format = "markdown") => format === "json" ? jsonText(report) : renderPrimitiveWriteReport(report);
5094
+
5095
+ // pkgs/@akanjs/devkit/workflow/source.ts
5096
+ var getSysRoot = (sys2) => `${sys2.type}s/${sys2.name}`;
5097
+ var sourceFile = (sys2, path10, action, reason) => ({
5098
+ path: `${getSysRoot(sys2)}/${path10}`,
5099
+ action,
5100
+ reason
5101
+ });
5102
+ var moduleComponentName = (moduleName) => `${moduleName.slice(0, 1).toUpperCase()}${moduleName.slice(1)}`;
5103
+ var moduleSourcePaths = (moduleName) => {
5104
+ const componentName = moduleComponentName(moduleName);
5105
+ return {
5106
+ abstract: `lib/${moduleName}/${moduleName}.abstract.md`,
5107
+ constant: `lib/${moduleName}/${moduleName}.constant.ts`,
5108
+ dictionary: `lib/${moduleName}/${moduleName}.dictionary.ts`,
5109
+ service: `lib/${moduleName}/${moduleName}.service.ts`,
5110
+ signal: `lib/${moduleName}/${moduleName}.signal.ts`,
5111
+ store: `lib/${moduleName}/${moduleName}.store.ts`,
5112
+ template: `lib/${moduleName}/${componentName}.Template.tsx`,
5113
+ unit: `lib/${moduleName}/${componentName}.Unit.tsx`,
5114
+ util: `lib/${moduleName}/${componentName}.Util.tsx`,
5115
+ view: `lib/${moduleName}/${componentName}.View.tsx`,
5116
+ zone: `lib/${moduleName}/${componentName}.Zone.tsx`
5117
+ };
5118
+ };
5119
+ var generatedFilesForSync = (sys2, reason = "Generated files may change after sync.") => generatedFilePathsForTarget(getSysRoot(sys2), reason);
5120
+ var validationCommandsForTarget = (target) => [
5121
+ { command: `akan sync ${target}`, reason: "Refresh generated Akan files from source conventions." },
5122
+ { command: `akan lint ${target}`, reason: "Validate formatting, imports, and static lint rules." }
5123
+ ];
5124
+ var nextActionsForTarget = (target) => [
5125
+ { command: `akan sync ${target}`, reason: "Refresh generated Akan files after source changes." },
5126
+ { command: `akan lint ${target}`, reason: "Validate the target after generated files are refreshed." }
5127
+ ];
5128
+ var createPassedPrimitiveReport = ({
5129
+ command,
5130
+ changedFiles,
5131
+ generatedFiles,
5132
+ target,
5133
+ nextActions
5134
+ }) => createPrimitiveWriteReport({
5135
+ command,
5136
+ changedFiles,
5137
+ generatedFiles: generatedFiles ?? [],
5138
+ validationCommands: validationCommandsForTarget(target),
5139
+ diagnostics: [],
5140
+ nextActions: nextActions ?? nextActionsForTarget(target)
5141
+ });
5142
+ var scalarChangedFiles = (sys2, scalarName, files) => Object.values(files).map((file) => sourceFile(sys2, `lib/__scalar/${scalarName}/${file.filename}`, "create", "Scalar source file was created."));
5143
+ var titleize = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
5144
+ var lowerlize = (value) => `${value.slice(0, 1).toLowerCase()}${value.slice(1)}`;
5145
+ var koLabels = {
5146
+ amount: "\uAE08\uC561",
5147
+ budget: "\uC608\uC0B0",
5148
+ category: "\uCE74\uD14C\uACE0\uB9AC",
5149
+ content: "\uB0B4\uC6A9",
5150
+ count: "\uAC1C\uC218",
5151
+ createdAt: "\uC0DD\uC131\uC77C",
5152
+ date: "\uB0A0\uC9DC",
5153
+ description: "\uC124\uBA85",
5154
+ due: "\uB9C8\uAC10\uC77C",
5155
+ dueAt: "\uB9C8\uAC10\uC77C",
5156
+ email: "\uC774\uBA54\uC77C",
5157
+ enabled: "\uD65C\uC131\uD654",
5158
+ endAt: "\uC885\uB8CC\uC77C",
5159
+ id: "ID",
5160
+ name: "\uC774\uB984",
5161
+ owner: "\uB2F4\uB2F9\uC790",
5162
+ priority: "\uC6B0\uC120\uC21C\uC704",
5163
+ project: "\uD504\uB85C\uC81D\uD2B8",
5164
+ rating: "\uD3C9\uC810",
5165
+ startAt: "\uC2DC\uC791\uC77C",
5166
+ status: "\uC0C1\uD0DC",
5167
+ title: "\uC81C\uBAA9",
5168
+ updatedAt: "\uC218\uC815\uC77C"
5169
+ };
5170
+ var splitFieldWords = (fieldName) => fieldName.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").split(/\s+/).map((word) => word.trim()).filter(Boolean);
5171
+ var koLabelForField = (fieldName) => {
5172
+ if (koLabels[fieldName])
5173
+ return koLabels[fieldName];
5174
+ const words = splitFieldWords(fieldName);
5175
+ const translated = words.map((word) => koLabels[word] ?? koLabels[lowerlize(word)] ?? null);
5176
+ return translated.every(Boolean) ? translated.join(" ") : null;
5177
+ };
5178
+ var bilingualLabelForField = (fieldName) => {
5179
+ const en = titleize(fieldName);
5180
+ return { en, ko: koLabelForField(fieldName) ?? en };
5181
+ };
5182
+ var bilingualDescriptionForField = (fieldName) => {
5183
+ const label = bilingualLabelForField(fieldName);
5184
+ return {
5185
+ en: `Enter ${label.en.toLowerCase()}.`,
5186
+ ko: `${label.ko} \uAC12\uC744 \uC785\uB825\uD569\uB2C8\uB2E4.`
5187
+ };
5188
+ };
5189
+ var normalizeFieldType = (typeName) => {
5190
+ const normalizedTypes = {
5191
+ string: "String",
5192
+ boolean: "Boolean",
5193
+ date: "Date",
5194
+ int: "Int",
5195
+ integer: "Int",
5196
+ float: "Float",
5197
+ double: "Float",
5198
+ decimal: "Float"
5199
+ };
5200
+ return normalizedTypes[typeName.toLowerCase()] ?? typeName;
5201
+ };
5202
+ var ensureBaseTypeImport = (content, typeName) => {
5203
+ if (typeName !== "Int" && typeName !== "Float")
5204
+ return content;
5205
+ if (new RegExp(`import \\{[^}]*\\b${typeName}\\b[^}]*\\} from "akanjs/base";`).test(content))
5206
+ return content;
5207
+ const baseImport = /import \{ ([^}]+) \} from "akanjs\/base";/.exec(content);
5208
+ if (baseImport) {
5209
+ const names = baseImport[1].split(",").map((name) => name.trim()).filter(Boolean);
5210
+ return content.replace(baseImport[0], `import { ${[...names, typeName].sort().join(", ")} } from "akanjs/base";`);
5211
+ }
5212
+ return `import { ${typeName} } from "akanjs/base";
5213
+ ${content}`;
5214
+ };
5215
+ var numericDefault = (typeName, rawDefault) => {
5216
+ if (typeof rawDefault === "number") {
5217
+ if (!Number.isFinite(rawDefault))
5218
+ return null;
5219
+ if (typeName === "Int" && !Number.isInteger(rawDefault))
5220
+ return null;
5221
+ return String(rawDefault);
5222
+ }
5223
+ if (typeof rawDefault !== "string")
5224
+ return null;
5225
+ const trimmed = rawDefault.trim();
5226
+ if (!trimmed || !Number.isFinite(Number(trimmed)))
5227
+ return null;
5228
+ if (typeName === "Int" && !/^-?\d+$/.test(trimmed))
5229
+ return null;
5230
+ return trimmed;
5231
+ };
5232
+ var booleanDefault = (rawDefault) => {
5233
+ if (typeof rawDefault === "boolean")
5234
+ return String(rawDefault);
5235
+ if (typeof rawDefault !== "string")
5236
+ return null;
5237
+ const lowered = rawDefault.trim().toLowerCase();
5238
+ return lowered === "true" || lowered === "false" ? lowered : null;
5239
+ };
5240
+ var dateDefault = (rawDefault) => {
5241
+ if (typeof rawDefault === "number" && Number.isFinite(rawDefault))
5242
+ return `new Date(${rawDefault})`;
5243
+ if (typeof rawDefault !== "string")
5244
+ return null;
5245
+ const trimmed = rawDefault.trim();
5246
+ if (!trimmed)
5247
+ return null;
5248
+ if (trimmed === "now")
5249
+ return "new Date()";
5250
+ if (!Number.isNaN(Date.parse(trimmed)))
5251
+ return `new Date(${JSON.stringify(trimmed)})`;
5252
+ return null;
5253
+ };
5254
+ var coerceFieldDefault = (typeName, defaultValue, options = {}) => {
5255
+ const normalizedType = typeName.toLowerCase() === "enum" ? "enum" : normalizeFieldType(typeName);
5256
+ if (defaultValue === undefined || defaultValue === null || defaultValue === "") {
5257
+ return { expression: null, normalized: false, normalizedType };
5258
+ }
5259
+ if (normalizedType === "Int" || normalizedType === "Float") {
5260
+ const expression = numericDefault(normalizedType, defaultValue);
5261
+ if (expression !== null)
5262
+ return { expression, normalized: typeof defaultValue === "string", normalizedType };
5263
+ return {
5264
+ expression: null,
5265
+ normalized: false,
5266
+ normalizedType,
5267
+ diagnostic: {
5268
+ severity: "error",
5269
+ code: "primitive-default-value-invalid",
5270
+ input: "default",
5271
+ failureScope: "source-change",
5272
+ message: `Default value for ${normalizedType} must be a numeric literal. Received: ${JSON.stringify(defaultValue)}.`
5273
+ }
5274
+ };
5275
+ }
5276
+ if (normalizedType === "Boolean") {
5277
+ const expression = booleanDefault(defaultValue);
5278
+ if (expression !== null)
5279
+ return { expression, normalized: typeof defaultValue === "string", normalizedType };
5280
+ return {
5281
+ expression: null,
5282
+ normalized: false,
5283
+ normalizedType,
5284
+ diagnostic: {
5285
+ severity: "error",
5286
+ code: "primitive-default-value-invalid",
5287
+ input: "default",
5288
+ failureScope: "source-change",
5289
+ message: `Default value for Boolean must be true or false. Received: ${JSON.stringify(defaultValue)}.`
5290
+ }
5291
+ };
5292
+ }
5293
+ if (normalizedType === "Date") {
5294
+ const expression = dateDefault(defaultValue);
5295
+ if (expression !== null)
5296
+ return { expression, normalized: true, normalizedType };
5297
+ return {
5298
+ expression: null,
5299
+ normalized: false,
5300
+ normalizedType,
5301
+ diagnostic: {
5302
+ severity: "error",
5303
+ code: "primitive-default-value-invalid",
5304
+ input: "default",
5305
+ failureScope: "source-change",
5306
+ message: `Default value for Date must be "now", a timestamp, or a parseable date string. Received: ${JSON.stringify(defaultValue)}.`
5307
+ }
5308
+ };
5309
+ }
5310
+ if (normalizedType === "enum") {
5311
+ if (typeof defaultValue === "string" && options.enumValues?.includes(defaultValue)) {
5312
+ return { expression: JSON.stringify(defaultValue), normalized: false, normalizedType };
5313
+ }
5314
+ return {
5315
+ expression: null,
5316
+ normalized: false,
5317
+ normalizedType,
5318
+ diagnostic: {
5319
+ severity: "error",
5320
+ code: "primitive-default-value-invalid",
5321
+ input: "default",
5322
+ failureScope: "source-change",
5323
+ message: `Default value for enum must be one of: ${(options.enumValues ?? []).join(", ")}. Received: ${JSON.stringify(defaultValue)}.`
5324
+ }
5325
+ };
5326
+ }
5327
+ return {
5328
+ expression: JSON.stringify(String(defaultValue)),
5329
+ normalized: typeof defaultValue !== "string",
5330
+ normalizedType
5331
+ };
5332
+ };
5333
+ var fieldExpression = (typeName, defaultValue, options = {}) => {
5334
+ const typeExpression = normalizeFieldType(typeName);
5335
+ const defaultExpression = coerceFieldDefault(options.enumValues ? "enum" : typeExpression, defaultValue, options).expression;
5336
+ const defaultOption = defaultExpression ? `, { default: ${defaultExpression} }` : "";
5337
+ return `field(${typeExpression}${defaultOption})`;
5338
+ };
5339
+ var insertIntoObject = (content, className, line) => {
5340
+ const classIndex = content.indexOf(`export class ${className} extends via`);
5341
+ if (classIndex < 0)
5342
+ return null;
5343
+ const objectEndIndex = content.indexOf("}))", classIndex);
5344
+ if (objectEndIndex < 0)
5345
+ return null;
5346
+ const prefix = content.slice(0, objectEndIndex);
5347
+ const suffix = content.slice(objectEndIndex);
5348
+ const insertion = prefix.endsWith(`
5349
+ `) ? ` ${line}
5350
+ ` : `
5351
+ ${line}
5352
+ `;
5353
+ return `${prefix}${insertion}${suffix}`;
5354
+ };
5355
+ var insertLightProjectionField = (content, moduleClassName, fieldName) => {
5356
+ const classIndex = content.indexOf(`export class Light${moduleClassName} extends via`);
5357
+ if (classIndex < 0)
5358
+ return null;
5359
+ const arrayMatch = /\[([\s\S]*?)\]\s+as const/.exec(content.slice(classIndex));
5360
+ if (!arrayMatch || arrayMatch.index === undefined)
5361
+ return null;
5362
+ const arrayStart = classIndex + arrayMatch.index;
5363
+ const arrayEnd = arrayStart + arrayMatch[0].length;
5364
+ const fields = [...arrayMatch[1].matchAll(/"([^"]+)"/g)].map((match) => match[1]).filter(Boolean);
5365
+ if (fields.includes(fieldName))
5366
+ return content;
5367
+ const nextFields = [...fields, fieldName];
5368
+ const nextArray = nextFields.length === 0 ? "[] as const" : `[
5369
+ ${nextFields.map((field) => ` ${JSON.stringify(field)},`).join(`
5370
+ `)}
5371
+ ] as const`;
5372
+ return `${content.slice(0, arrayStart)}${nextArray}${content.slice(arrayEnd)}`;
5373
+ };
5374
+ var insertTemplateField = ({
5375
+ content,
5376
+ moduleName,
5377
+ moduleClassName,
5378
+ fieldName,
5379
+ component
5380
+ }) => {
5381
+ if (content.includes(`l("${moduleName}.${fieldName}")`) || content.includes(`.${fieldName}`))
5382
+ return content;
5383
+ const layoutEndIndex = content.indexOf(" </Layout.Template>");
5384
+ if (layoutEndIndex < 0)
5385
+ return null;
5386
+ const formName = `${moduleName}Form`;
5387
+ if (!content.includes(`const ${formName} = st.use.${moduleName}Form();`))
5388
+ return null;
5389
+ const fieldSetter = `st.do.set${moduleComponentName(fieldName)}On${moduleClassName}`;
5390
+ const fieldBlock = ` <${component}
5391
+ label={l("${moduleName}.${fieldName}")}
5392
+ desc={l("${moduleName}.${fieldName}.desc")}
5393
+ value={${formName}.${fieldName}}
5394
+ onChange={${fieldSetter}}
5395
+ />
5396
+ `;
5397
+ return `${content.slice(0, layoutEndIndex)}${fieldBlock}${content.slice(layoutEndIndex)}`;
5398
+ };
5399
+ var ensureEnumImport = (content) => {
5400
+ if (content.includes("enumOf"))
5401
+ return content;
5402
+ const baseImport = /import \{ ([^}]+) \} from "akanjs\/base";/.exec(content);
5403
+ if (baseImport) {
5404
+ const names = baseImport[1]?.split(",").map((name) => name.trim()) ?? [];
5405
+ return content.replace(baseImport[0], `import { ${[...names, "enumOf"].sort().join(", ")} } from "akanjs/base";`);
5406
+ }
5407
+ return `import { enumOf } from "akanjs/base";
5408
+ ${content}`;
5409
+ };
5410
+ var insertEnumClass = (content, enumClassName, enumName, values) => {
5411
+ if (content.includes(`export class ${enumClassName} extends enumOf`))
5412
+ return content;
5413
+ const enumClass = `export class ${enumClassName} extends enumOf("${enumName}", [
5414
+ ${values.map((value) => ` ${JSON.stringify(value)},`).join(`
5415
+ `)}
5416
+ ] as const) {}
5417
+
5418
+ `;
5419
+ const firstClassIndex = content.indexOf("export class ");
5420
+ if (firstClassIndex < 0)
5421
+ return `${content}
5422
+ ${enumClass}`;
5423
+ return `${content.slice(0, firstClassIndex)}${enumClass}${content.slice(firstClassIndex)}`;
5424
+ };
5425
+ var insertDictionaryModelField = (content, moduleClassName, fieldName) => {
5426
+ if (new RegExp(`\\b${fieldName}\\s*:`).test(content))
5427
+ return content;
5428
+ const label = bilingualLabelForField(fieldName);
5429
+ const desc = bilingualDescriptionForField(fieldName);
5430
+ const modelIndex = content.indexOf(`.model<${moduleClassName}>((t) => ({`);
5431
+ if (modelIndex < 0)
5432
+ return null;
5433
+ const objectEndIndex = content.indexOf(" }))", modelIndex);
5434
+ if (objectEndIndex < 0)
5435
+ return null;
5436
+ return `${content.slice(0, objectEndIndex)} ${fieldName}: t([${JSON.stringify(label.en)}, ${JSON.stringify(label.ko)}]).desc([${JSON.stringify(desc.en)}, ${JSON.stringify(desc.ko)}]),
5437
+ ${content.slice(objectEndIndex)}`;
5438
+ };
5439
+ var ensureConstantTypeImport = (content, constantPath, typeName) => {
5440
+ if (new RegExp(`import type \\{[^}]*\\b${typeName}\\b[^}]*\\} from "${constantPath}";`).test(content))
5441
+ return content;
5442
+ const importPattern = new RegExp(`import type \\{ ([^}]+) \\} from "${constantPath}";`);
5443
+ const existingImport = content.match(importPattern);
5444
+ if (existingImport !== null) {
5445
+ const names = existingImport[1]?.split(",").map((name) => name.trim()) ?? [];
5446
+ return content.replace(existingImport[0], `import type { ${[...names, typeName].sort().join(", ")} } from "${constantPath}";`);
5447
+ }
5448
+ return `import type { ${typeName} } from "${constantPath}";
5449
+ ${content}`;
5450
+ };
5451
+ var insertDictionaryEnum = (content, enumClassName, enumName, values) => {
5452
+ if (content.includes(`.enum<${enumClassName}>("${enumName}"`))
5453
+ return content;
5454
+ const enumBlock = ` .enum<${enumClassName}>("${enumName}", (t) => ({
5455
+ ${values.map((value) => ` ${value}: t([${JSON.stringify(titleize(value))}, ${JSON.stringify(titleize(value))}]),`).join(`
5456
+ `)}
5457
+ }))
5458
+ `;
5459
+ const chainEndIndex = content.lastIndexOf(";");
5460
+ const insertBeforeIndex = [content.indexOf(".error("), content.indexOf(".translate("), chainEndIndex].filter((index) => index >= 0).sort((a, b) => a - b)[0];
5461
+ if (insertBeforeIndex === undefined)
5462
+ return null;
5463
+ return `${content.slice(0, insertBeforeIndex)}${enumBlock}${content.slice(insertBeforeIndex)}`;
5464
+ };
5465
+ var parseValues = (value) => value?.split(",").map((item) => item.trim()).filter(Boolean) ?? [];
5466
+
5467
+ // pkgs/@akanjs/devkit/workflow/uiPolicy.ts
5468
+ var addFieldUiPolicyForType = (typeName) => {
5469
+ const normalizedType = typeName.toLowerCase() === "enum" ? "enum" : normalizeFieldType(typeName);
5470
+ if (normalizedType === "Int" || normalizedType === "Float") {
5471
+ return {
5472
+ normalizedType,
5473
+ component: "Field.Number",
5474
+ confidence: "high",
5475
+ autoTemplateSupported: true
5476
+ };
5477
+ }
5478
+ if (normalizedType === "Date") {
5479
+ return {
5480
+ normalizedType,
5481
+ component: "Field.Date",
5482
+ confidence: "high",
5483
+ autoTemplateSupported: true
5484
+ };
5485
+ }
5486
+ if (normalizedType === "Boolean" || normalizedType === "enum") {
5487
+ return {
5488
+ normalizedType,
5489
+ component: "Field.ToggleSelect",
5490
+ confidence: "medium",
5491
+ autoTemplateSupported: false
5492
+ };
5493
+ }
5494
+ if (normalizedType === "String") {
5495
+ return {
5496
+ normalizedType,
5497
+ component: "Field.Text",
5498
+ confidence: "high",
5499
+ autoTemplateSupported: true
5500
+ };
5501
+ }
5502
+ return {
5503
+ normalizedType,
5504
+ component: "Field.Text",
5505
+ confidence: "low",
5506
+ autoTemplateSupported: false
5507
+ };
5508
+ };
5509
+
5510
+ // pkgs/@akanjs/devkit/workflow/executor.ts
5511
+ var workflowStepKey = (workflow, stepId) => `${workflow}:${stepId}`;
5512
+ var primitiveReportToWorkflowStepResult = (report) => ({
5513
+ changedFiles: report.changedFiles,
5514
+ generatedFiles: report.generatedFiles,
5515
+ commands: report.validationCommands,
5516
+ diagnostics: report.diagnostics,
5517
+ recommendations: [],
5518
+ nextActions: report.nextActions
5519
+ });
5520
+ var workflowStringInput = (value) => typeof value === "string" ? value : null;
5521
+ var workflowStringListInput = (value) => Array.isArray(value) ? value.join(",") : null;
5522
+ var workflowStringArrayInput = (value) => Array.isArray(value) ? value : null;
5523
+ var workflowBooleanInput = (value) => typeof value === "boolean" ? value : null;
5524
+ var resolveWorkflowSys = async (workspace, target) => {
5525
+ if (!target)
5526
+ return null;
5527
+ const [apps, libs] = await workspace.getSyss();
5528
+ if (apps.includes(target))
5529
+ return AppExecutor.from(workspace, target);
5530
+ if (libs.includes(target))
5531
+ return LibExecutor.from(workspace, target);
5532
+ return null;
5533
+ };
5206
5534
  var targetMissing = (input2 = "app") => ({
5207
5535
  severity: "error",
5208
5536
  code: "workflow-target-missing",
@@ -5222,19 +5550,24 @@ var unsupportedInput = (input2, message) => ({
5222
5550
  message
5223
5551
  });
5224
5552
  var addFieldUiSurfaceInspection = (plan) => {
5553
+ const app = workflowStringInput(plan.inputs.app);
5225
5554
  const module = workflowStringInput(plan.inputs.module) ?? "<module>";
5226
5555
  const field = workflowStringInput(plan.inputs.field) ?? "<field>";
5556
+ const typeName = workflowStringInput(plan.inputs.type);
5557
+ const policy = addFieldUiPolicyForType(typeName ?? "String");
5558
+ const surfaces = workflowStringArrayInput(plan.inputs.surfaces);
5559
+ const templateRequested = surfaces?.includes("template") ?? false;
5227
5560
  const moduleClassName = capitalize2(module);
5228
- const target = `*/lib/${module}/${moduleClassName}.Template.tsx`;
5561
+ const target = `${app ? `apps/${app}` : "*"}/${moduleSourcePaths(module).template}`;
5229
5562
  return {
5230
5563
  recommendations: [
5231
5564
  {
5232
5565
  code: "add-field-ui-surface-review",
5233
5566
  kind: "manual-action",
5234
5567
  target,
5235
- action: `Add ${field} to ${moduleClassName} UI surfaces only when the local Field.* pattern is clear.`,
5568
+ action: templateRequested ? `Template was requested for ${field}. If no Template file changed, auto-edit was skipped because the file was missing, the generated ${module}Form/Layout.Template pattern was not found, or ${policy.component} needs option binding. Candidate position: inside Layout.Template near the existing Field components.` : `Template was not selected, so UI files are intentionally left unchanged. Candidate positions if you expose it later: Layout.Template field list for editing, Light${moduleClassName} projection for list/card data, and Unit/View card sections for display.`,
5236
5569
  confidence: "medium",
5237
- message: `Review Template/Unit/View/Store surfaces for ${module}.${field}; no UI file was modified automatically.`
5570
+ message: `Review UI surfaces for ${module}.${field}; recommended component is ${policy.component}, with manual reasons and candidate positions in action.`
5238
5571
  }
5239
5572
  ],
5240
5573
  nextActions: [
@@ -5272,95 +5605,579 @@ var createWorkflowStepRegistry = ({
5272
5605
  return { diagnostics: [!sys2 ? targetMissing() : inputMissing("module")] };
5273
5606
  return primitiveReportToWorkflowStepResult(await createModule(sys2, module));
5274
5607
  },
5275
- [workflowStepKey("create-scalar", "create-scalar")]: async (_step, plan) => {
5276
- const app = workflowStringInput(plan.inputs.app);
5277
- const scalar = workflowStringInput(plan.inputs.scalar);
5278
- const sys2 = await resolveWorkflowSys(workspace, app);
5279
- if (!sys2 || !scalar)
5280
- return { diagnostics: [!sys2 ? targetMissing() : inputMissing("scalar")] };
5281
- return primitiveReportToWorkflowStepResult(await createScalar(sys2, scalar));
5608
+ [workflowStepKey("create-scalar", "create-scalar")]: async (_step, plan) => {
5609
+ const app = workflowStringInput(plan.inputs.app);
5610
+ const scalar = workflowStringInput(plan.inputs.scalar);
5611
+ const sys2 = await resolveWorkflowSys(workspace, app);
5612
+ if (!sys2 || !scalar)
5613
+ return { diagnostics: [!sys2 ? targetMissing() : inputMissing("scalar")] };
5614
+ return primitiveReportToWorkflowStepResult(await createScalar(sys2, scalar));
5615
+ },
5616
+ [workflowStepKey("create-ui", "create-ui")]: async (_step, plan) => {
5617
+ const surface = workflowStringInput(plan.inputs.surface);
5618
+ if (surface !== "view" && surface !== "unit" && surface !== "template") {
5619
+ return {
5620
+ diagnostics: [
5621
+ unsupportedInput("surface", "Workflow apply currently supports create-ui surfaces: view, unit, template.")
5622
+ ]
5623
+ };
5624
+ }
5625
+ return primitiveReportToWorkflowStepResult(await createUi({
5626
+ app: workflowStringInput(plan.inputs.app),
5627
+ module: workflowStringInput(plan.inputs.module),
5628
+ surface
5629
+ }));
5630
+ },
5631
+ [workflowStepKey("add-field", "update-constant")]: async (_step, plan) => {
5632
+ if (workflowStringInput(plan.inputs.type)?.toLowerCase() === "enum") {
5633
+ return primitiveReportToWorkflowStepResult(await addEnumField({
5634
+ app: workflowStringInput(plan.inputs.app),
5635
+ module: workflowStringInput(plan.inputs.module),
5636
+ field: workflowStringInput(plan.inputs.field),
5637
+ values: workflowStringListInput(plan.inputs.values),
5638
+ defaultValue: workflowStringInput(plan.inputs.default),
5639
+ surfaces: workflowStringArrayInput(plan.inputs.surfaces),
5640
+ includeInLight: workflowBooleanInput(plan.inputs.includeInLight)
5641
+ }));
5642
+ }
5643
+ return primitiveReportToWorkflowStepResult(await addField({
5644
+ app: workflowStringInput(plan.inputs.app),
5645
+ module: workflowStringInput(plan.inputs.module),
5646
+ field: workflowStringInput(plan.inputs.field),
5647
+ type: workflowStringInput(plan.inputs.type),
5648
+ defaultValue: workflowStringInput(plan.inputs.default),
5649
+ surfaces: workflowStringArrayInput(plan.inputs.surfaces),
5650
+ includeInLight: workflowBooleanInput(plan.inputs.includeInLight)
5651
+ }));
5652
+ },
5653
+ [workflowStepKey("add-field", "update-dictionary")]: inspect,
5654
+ [workflowStepKey("add-field", "update-ui-surfaces")]: async (_step, plan) => addFieldUiSurfaceInspection(plan),
5655
+ [workflowStepKey("add-enum-field", "update-constant")]: async (_step, plan) => primitiveReportToWorkflowStepResult(await addEnumField({
5656
+ app: workflowStringInput(plan.inputs.app),
5657
+ module: workflowStringInput(plan.inputs.module),
5658
+ field: workflowStringInput(plan.inputs.field),
5659
+ values: workflowStringListInput(plan.inputs.values),
5660
+ defaultValue: workflowStringInput(plan.inputs.default)
5661
+ })),
5662
+ [workflowStepKey("add-enum-field", "update-dictionary")]: inspect,
5663
+ [workflowStepKey("add-enum-field", "update-option")]: inspect
5664
+ };
5665
+ };
5666
+ class WorkflowExecutor {
5667
+ registry;
5668
+ constructor(registry) {
5669
+ this.registry = registry;
5670
+ }
5671
+ async apply(plan) {
5672
+ const changedFiles = [];
5673
+ const generatedFiles = [];
5674
+ const recommendedValidationCommands = [];
5675
+ const diagnostics = [...plan.diagnostics];
5676
+ const recommendations = [...plan.recommendations];
5677
+ const nextActions = [];
5678
+ if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
5679
+ return createWorkflowApplyReport({
5680
+ workflow: plan.workflow,
5681
+ mode: "apply",
5682
+ changedFiles,
5683
+ generatedFiles,
5684
+ recommendedValidationCommands,
5685
+ diagnostics,
5686
+ recommendations,
5687
+ nextActions,
5688
+ plan
5689
+ });
5690
+ }
5691
+ recommendedValidationCommands.push(...workflowCommandsForPlan(plan));
5692
+ nextActions.push(...workflowCommandsForPlan(plan));
5693
+ for (const step of plan.steps) {
5694
+ const runner = this.registry[workflowStepKey(plan.workflow, step.id)] ?? this.registry[step.tool] ?? this.registry[step.id];
5695
+ if (!runner) {
5696
+ diagnostics.push({
5697
+ severity: "error",
5698
+ code: "workflow-step-unsupported",
5699
+ message: `Workflow ${plan.workflow} step "${step.id}" is not supported by workflow apply yet.`
5700
+ });
5701
+ nextActions.push({
5702
+ command: `akan workflow explain ${plan.workflow}`,
5703
+ reason: "Review the unsupported workflow step before retrying apply."
5704
+ });
5705
+ break;
5706
+ }
5707
+ const result = await runner(step, plan);
5708
+ if (!result)
5709
+ continue;
5710
+ changedFiles.push(...result.changedFiles ?? []);
5711
+ generatedFiles.push(...result.generatedFiles ?? []);
5712
+ recommendedValidationCommands.push(...result.commands ?? []);
5713
+ diagnostics.push(...result.diagnostics ?? []);
5714
+ recommendations.push(...result.recommendations ?? []);
5715
+ nextActions.push(...result.nextActions ?? []);
5716
+ if (diagnostics.some((diagnostic) => diagnostic.severity === "error"))
5717
+ break;
5718
+ }
5719
+ return createWorkflowApplyReport({
5720
+ workflow: plan.workflow,
5721
+ mode: "apply",
5722
+ changedFiles,
5723
+ generatedFiles,
5724
+ recommendedValidationCommands,
5725
+ diagnostics,
5726
+ recommendations,
5727
+ nextActions,
5728
+ plan
5729
+ });
5730
+ }
5731
+ }
5732
+ // pkgs/@akanjs/devkit/workflow/plan.ts
5733
+ import { capitalize as capitalize3 } from "akanjs/common";
5734
+ var surfaceModes = new Set(["infer", "include", "skip"]);
5735
+ var parseStringList = (value) => {
5736
+ if (Array.isArray(value)) {
5737
+ const values = value.filter((item) => typeof item === "string" && item.trim().length > 0);
5738
+ return values.length === value.length ? values : null;
5739
+ }
5740
+ if (typeof value !== "string")
5741
+ return null;
5742
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
5743
+ };
5744
+ var normalizeInputValue = (name, spec, value) => {
5745
+ if (spec.type === "string")
5746
+ return typeof value === "string" && value.length > 0 ? value : null;
5747
+ if (spec.type === "string-list") {
5748
+ const values = parseStringList(value);
5749
+ return values && values.length > 0 ? values : null;
5750
+ }
5751
+ if (spec.type === "boolean") {
5752
+ if (typeof value === "boolean")
5753
+ return value;
5754
+ if (typeof value !== "string")
5755
+ return null;
5756
+ const lowered = value.trim().toLowerCase();
5757
+ if (lowered === "true")
5758
+ return true;
5759
+ if (lowered === "false")
5760
+ return false;
5761
+ return null;
5762
+ }
5763
+ if (typeof value === "string" && surfaceModes.has(value))
5764
+ return value;
5765
+ throw new Error(`Unsupported workflow input value for ${name}`);
5766
+ };
5767
+ var listWorkflowSpecs = (specs) => [...specs].sort((a, b) => a.name.localeCompare(b.name));
5768
+ var getWorkflowSpec = (specs, name) => specs.find((spec) => spec.name === name) ?? null;
5769
+ var compactWorkflowInputs = (inputs) => Object.fromEntries(Object.entries(inputs).filter(([, value]) => value !== null && value !== ""));
5770
+ var addFieldTargetRoot = (app) => app ? `apps/${app}` : "*";
5771
+ var addFieldPaths = (inputs) => {
5772
+ const app = typeof inputs.app === "string" ? inputs.app : null;
5773
+ const module = typeof inputs.module === "string" ? inputs.module : "<module>";
5774
+ const paths = moduleSourcePaths(module);
5775
+ const root = addFieldTargetRoot(app);
5776
+ return {
5777
+ constant: `${root}/${paths.constant}`,
5778
+ dictionary: `${root}/${paths.dictionary}`,
5779
+ template: `${root}/${paths.template}`,
5780
+ unit: `${root}/${paths.unit}`,
5781
+ view: `${root}/${paths.view}`
5782
+ };
5783
+ };
5784
+ var selectedAddFieldSurfaces = (inputs) => Array.isArray(inputs.surfaces) ? new Set(inputs.surfaces) : null;
5785
+ var addFieldDefaultCoercion = (inputs) => {
5786
+ const typeName = typeof inputs.type === "string" ? inputs.type : null;
5787
+ const defaultValue = typeof inputs.default === "string" ? inputs.default : null;
5788
+ if (!typeName || defaultValue === null)
5789
+ return null;
5790
+ if (typeName.toLowerCase() === "number" || typeName.toLowerCase() === "numeric")
5791
+ return null;
5792
+ const enumValues = Array.isArray(inputs.values) ? inputs.values : null;
5793
+ return coerceFieldDefault(typeName.toLowerCase() === "enum" ? "enum" : typeName, defaultValue, { enumValues });
5794
+ };
5795
+ var createAddFieldDefaultRecommendations = (inputs) => {
5796
+ const field = typeof inputs.field === "string" ? inputs.field : "<field>";
5797
+ const coercion = addFieldDefaultCoercion(inputs);
5798
+ if (!coercion?.normalized || !coercion.expression)
5799
+ return [];
5800
+ return [
5801
+ {
5802
+ code: "add-field-default-normalized",
5803
+ kind: "manual-action",
5804
+ confidence: "high",
5805
+ message: `Default for ${field} will be normalized to ${coercion.normalizedType} literal ${coercion.expression}.`,
5806
+ action: "Review the normalized default in the apply report if this value should stay unset."
5807
+ }
5808
+ ];
5809
+ };
5810
+ var createAddFieldRecommendations = (inputs) => {
5811
+ const app = typeof inputs.app === "string" ? inputs.app : "<app-or-lib>";
5812
+ const module = typeof inputs.module === "string" ? inputs.module : "<module>";
5813
+ const field = typeof inputs.field === "string" ? inputs.field : "<field>";
5814
+ const typeName = typeof inputs.type === "string" ? inputs.type : null;
5815
+ if (!typeName)
5816
+ return [];
5817
+ const policy = addFieldUiPolicyForType(typeName);
5818
+ const normalizedType = policy.normalizedType;
5819
+ const paths = addFieldPaths(inputs);
5820
+ const surfaces = selectedAddFieldSurfaces(inputs);
5821
+ const templateRequested = surfaces?.has("template") ?? false;
5822
+ const includeInLight = inputs.includeInLight === true;
5823
+ return [
5824
+ ...normalizedType === "Int" || normalizedType === "Float" ? [
5825
+ {
5826
+ code: "add-field-import",
5827
+ kind: "import",
5828
+ target: paths.constant,
5829
+ confidence: "high",
5830
+ message: `Import ${normalizedType} from "akanjs/base" before writing field(${normalizedType}).`
5831
+ }
5832
+ ] : [],
5833
+ {
5834
+ code: "add-field-placement-constant",
5835
+ kind: "placement",
5836
+ target: paths.constant,
5837
+ confidence: "high",
5838
+ message: `Insert ${field}: field(${normalizedType}) in ${capitalize3(module)}Input.`
5839
+ },
5840
+ {
5841
+ code: "add-field-placement-dictionary",
5842
+ kind: "placement",
5843
+ target: paths.dictionary,
5844
+ confidence: "high",
5845
+ message: `Add dictionary labels for ${module}.${field}.`
5846
+ },
5847
+ {
5848
+ code: "add-field-component",
5849
+ kind: "ui-component",
5850
+ target: paths.template,
5851
+ confidence: policy.confidence,
5852
+ action: templateRequested ? `apply_workflow will try to add ${policy.component} to Template when the existing Template has a safe generated field-list pattern.` : `Recommended component is ${policy.component}. Pass surfaces=["template"] when this field should be rendered in the Template form.`,
5853
+ message: `Recommended UI component for ${field} (${normalizedType}): ${policy.component}.`
5854
+ },
5855
+ ...!surfaces ? [
5856
+ {
5857
+ code: "add-field-template-surface-choice",
5858
+ kind: "manual-action",
5859
+ target: paths.template,
5860
+ action: `Choose whether to expose ${field} in Template by passing surfaces=["template"].`,
5861
+ confidence: "medium",
5862
+ message: `Template exposure for ${module}.${field} is not selected yet.`
5863
+ }
5864
+ ] : [],
5865
+ ...!includeInLight ? [
5866
+ {
5867
+ code: "add-field-light-projection-choice",
5868
+ kind: "manual-action",
5869
+ target: paths.constant,
5870
+ action: `Pass includeInLight=true when ${field} should appear in Light${capitalize3(module)} list/card projections.`,
5871
+ confidence: "medium",
5872
+ message: `Light projection exposure for ${module}.${field} is not selected yet.`
5873
+ }
5874
+ ] : [],
5875
+ ...includeInLight ? [
5876
+ {
5877
+ code: "add-field-light-projection",
5878
+ kind: "placement",
5879
+ target: paths.constant,
5880
+ confidence: "high",
5881
+ message: `Add ${field} to Light${capitalize3(module)} projection fields.`
5882
+ }
5883
+ ] : [],
5884
+ ...surfaces && !templateRequested ? [
5885
+ {
5886
+ code: "add-field-ui-surface-skip",
5887
+ kind: "manual-action",
5888
+ target: paths.template,
5889
+ confidence: "medium",
5890
+ message: `Template is not included in surfaces, so ${module}.${field} will not be auto-rendered there.`
5891
+ }
5892
+ ] : [],
5893
+ {
5894
+ code: "add-field-ui-manual-review",
5895
+ kind: "manual-action",
5896
+ target: paths.template,
5897
+ action: `Review ${app}:${module} UI after apply. Template auto-edit requires a generated ${module}Form hook and Layout.Template field list; Unit/View are not auto-edited because list/card placement depends on local layout. Candidate positions: Layout.Template before the closing tag, Light${capitalize3(module)} projection array for list data, and Unit/View card sections for display.`,
5898
+ confidence: "medium",
5899
+ message: "UI manual review includes the reason auto-edit may be skipped and candidate insertion positions."
5900
+ },
5901
+ ...createAddFieldDefaultRecommendations(inputs)
5902
+ ];
5903
+ };
5904
+ var createWorkflowPlanPredictedChanges = (spec, inputs) => {
5905
+ if (spec.name !== "add-field")
5906
+ return spec.predictedChanges;
5907
+ const paths = addFieldPaths(inputs);
5908
+ const surfaces = selectedAddFieldSurfaces(inputs);
5909
+ const includeInLight = inputs.includeInLight === true;
5910
+ return [
5911
+ {
5912
+ target: paths.constant,
5913
+ action: "modify",
5914
+ applyScope: "auto",
5915
+ reason: includeInLight ? "Field shape and Light projection are added." : "Field shape is added."
5916
+ },
5917
+ {
5918
+ target: paths.dictionary,
5919
+ action: "modify",
5920
+ applyScope: "auto",
5921
+ reason: "Field label is added."
5282
5922
  },
5283
- [workflowStepKey("create-ui", "create-ui")]: async (_step, plan) => {
5284
- const surface = workflowStringInput(plan.inputs.surface);
5285
- if (surface !== "view" && surface !== "unit" && surface !== "template") {
5286
- return {
5287
- diagnostics: [
5288
- unsupportedInput("surface", "Workflow apply currently supports create-ui surfaces: view, unit, template.")
5289
- ]
5290
- };
5923
+ ...!surfaces || surfaces.has("template") ? [
5924
+ {
5925
+ target: paths.template,
5926
+ action: "modify",
5927
+ applyScope: surfaces?.has("template") ? "auto" : "manual-review",
5928
+ reason: surfaces?.has("template") ? "Template form is selected for safe-pattern auto insertion." : "Form surface may include the field."
5291
5929
  }
5292
- return primitiveReportToWorkflowStepResult(await createUi({
5293
- app: workflowStringInput(plan.inputs.app),
5294
- module: workflowStringInput(plan.inputs.module),
5295
- surface
5296
- }));
5930
+ ] : [],
5931
+ {
5932
+ target: `${addFieldTargetRoot(typeof inputs.app === "string" ? inputs.app : null)}/lib/cnst.ts`,
5933
+ action: "sync",
5934
+ applyScope: "generated-sync",
5935
+ reason: "Generated constants may change after sync."
5297
5936
  },
5298
- [workflowStepKey("add-field", "update-constant")]: async (_step, plan) => {
5299
- if (workflowStringInput(plan.inputs.type)?.toLowerCase() === "enum") {
5300
- return primitiveReportToWorkflowStepResult(await addEnumField({
5301
- app: workflowStringInput(plan.inputs.app),
5302
- module: workflowStringInput(plan.inputs.module),
5303
- field: workflowStringInput(plan.inputs.field),
5304
- values: workflowStringListInput(plan.inputs.values),
5305
- defaultValue: workflowStringInput(plan.inputs.default)
5306
- }));
5937
+ {
5938
+ target: `${addFieldTargetRoot(typeof inputs.app === "string" ? inputs.app : null)}/lib/dict.ts`,
5939
+ action: "sync",
5940
+ applyScope: "generated-sync",
5941
+ reason: "Generated dictionary barrel may change after sync."
5942
+ }
5943
+ ];
5944
+ };
5945
+ var createWorkflowPlanOptionalSurfaces = (spec, inputs) => {
5946
+ const optionalSurfaces = spec.optionalSurfaces ?? {};
5947
+ const surfaces = selectedAddFieldSurfaces(inputs);
5948
+ if (spec.name !== "add-field" || !surfaces)
5949
+ return optionalSurfaces;
5950
+ return Object.fromEntries(Object.entries(optionalSurfaces).map(([surface, mode]) => [surface, surfaces.has(surface) ? "include" : mode]));
5951
+ };
5952
+ var createWorkflowPlanRecommendations = (spec, inputs) => [
5953
+ {
5954
+ code: "workflow-apply-first",
5955
+ kind: "auto-apply",
5956
+ confidence: "high",
5957
+ action: "Call apply_workflow with the MCP planPath before editing source files directly.",
5958
+ message: `Apply the ${spec.name} plan through apply_workflow when MCP returns planPath or next.tool=apply_workflow.`
5959
+ },
5960
+ {
5961
+ code: "workflow-validate-apply-report",
5962
+ kind: "validation",
5963
+ confidence: "high",
5964
+ action: "After apply_workflow, call run_validation with validationTarget when present; otherwise use applyReportPath.",
5965
+ message: "Validate the apply report artifact so diagnostics and recommendations follow the actual apply result."
5966
+ },
5967
+ ...spec.name === "add-field" ? createAddFieldRecommendations(inputs) : []
5968
+ ];
5969
+ var createWorkflowPlan = (spec, rawInputs) => {
5970
+ const inputs = {};
5971
+ const diagnostics = [];
5972
+ for (const [name, inputSpec] of Object.entries(spec.inputs)) {
5973
+ const rawValue = rawInputs[name];
5974
+ if (rawValue === undefined || rawValue === null || rawValue === "") {
5975
+ if (inputSpec.required) {
5976
+ diagnostics.push({
5977
+ severity: "error",
5978
+ code: "workflow-input-missing",
5979
+ input: name,
5980
+ message: `Workflow ${spec.name} requires input "${name}".`
5981
+ });
5307
5982
  }
5308
- return primitiveReportToWorkflowStepResult(await addField({
5309
- app: workflowStringInput(plan.inputs.app),
5310
- module: workflowStringInput(plan.inputs.module),
5311
- field: workflowStringInput(plan.inputs.field),
5312
- type: workflowStringInput(plan.inputs.type),
5313
- defaultValue: workflowStringInput(plan.inputs.default)
5314
- }));
5315
- },
5316
- [workflowStepKey("add-field", "update-dictionary")]: inspect,
5317
- [workflowStepKey("add-field", "update-ui-surfaces")]: async (_step, plan) => addFieldUiSurfaceInspection(plan),
5318
- [workflowStepKey("add-enum-field", "update-constant")]: async (_step, plan) => primitiveReportToWorkflowStepResult(await addEnumField({
5319
- app: workflowStringInput(plan.inputs.app),
5320
- module: workflowStringInput(plan.inputs.module),
5321
- field: workflowStringInput(plan.inputs.field),
5322
- values: workflowStringListInput(plan.inputs.values),
5323
- defaultValue: workflowStringInput(plan.inputs.default)
5324
- })),
5325
- [workflowStepKey("add-enum-field", "update-dictionary")]: inspect,
5326
- [workflowStepKey("add-enum-field", "update-option")]: inspect
5983
+ continue;
5984
+ }
5985
+ const value = normalizeInputValue(name, inputSpec, rawValue);
5986
+ if (value === null) {
5987
+ diagnostics.push({
5988
+ severity: "error",
5989
+ code: "workflow-input-invalid",
5990
+ input: name,
5991
+ message: `Workflow ${spec.name} input "${name}" must be ${inputSpec.type}.`
5992
+ });
5993
+ continue;
5994
+ }
5995
+ if (inputSpec.allowedValues && typeof value === "string" && !inputSpec.allowedValues.includes(value)) {
5996
+ diagnostics.push({
5997
+ severity: "error",
5998
+ code: "workflow-input-not-allowed",
5999
+ input: name,
6000
+ message: `Workflow ${spec.name} input "${name}" must be one of: ${inputSpec.allowedValues.join(", ")}.`
6001
+ });
6002
+ continue;
6003
+ }
6004
+ inputs[name] = value;
6005
+ }
6006
+ const fieldType = inputs.type;
6007
+ if (spec.name === "add-field" && typeof fieldType === "string" && (fieldType.toLowerCase() === "number" || fieldType.toLowerCase() === "numeric")) {
6008
+ diagnostics.push({
6009
+ severity: "error",
6010
+ code: "primitive-field-type-unsupported",
6011
+ input: "type",
6012
+ message: `Field type "${fieldType}" is ambiguous in Akan. Use Int for integer fields or Float for decimal fields.`
6013
+ });
6014
+ }
6015
+ if (spec.name === "add-field") {
6016
+ const defaultCoercion = addFieldDefaultCoercion(inputs);
6017
+ if (defaultCoercion?.diagnostic) {
6018
+ diagnostics.push({
6019
+ ...defaultCoercion.diagnostic,
6020
+ code: "workflow-default-value-invalid",
6021
+ message: `Plan input default is not valid for ${defaultCoercion.normalizedType}: ${defaultCoercion.diagnostic.message}`
6022
+ });
6023
+ } else if (defaultCoercion?.normalized && defaultCoercion.expression) {
6024
+ diagnostics.push({
6025
+ severity: "warning",
6026
+ code: "workflow-default-value-normalized",
6027
+ input: "default",
6028
+ failureScope: "source-change",
6029
+ message: `Plan input default will be written as ${defaultCoercion.normalizedType} literal ${defaultCoercion.expression}.`
6030
+ });
6031
+ }
6032
+ }
6033
+ return {
6034
+ schemaVersion: 1,
6035
+ workflow: spec.name,
6036
+ mode: "plan",
6037
+ inputs,
6038
+ optionalSurfaces: createWorkflowPlanOptionalSurfaces(spec, inputs),
6039
+ steps: spec.steps,
6040
+ predictedChanges: createWorkflowPlanPredictedChanges(spec, inputs),
6041
+ validation: spec.validation,
6042
+ diagnostics,
6043
+ recommendations: createWorkflowPlanRecommendations(spec, inputs),
6044
+ requiresApproval: true
5327
6045
  };
5328
6046
  };
5329
- var renderWorkflowApplyReport = (report) => [
5330
- `# Workflow Apply: ${report.workflow}`,
6047
+ // pkgs/@akanjs/devkit/workflow/render.ts
6048
+ var renderWorkflowList = (specs) => [
6049
+ "# Akan Workflows",
5331
6050
  "",
5332
- `- Mode: ${report.mode}`,
5333
- `- Status: ${report.status}`,
6051
+ ...specs.flatMap((spec) => [`- \`${spec.name}\`: ${spec.description}`, ` - When: ${spec.whenToUse}`]),
6052
+ ""
6053
+ ].join(`
6054
+ `);
6055
+ var renderWorkflowExplain = (spec) => [
6056
+ `# Workflow: ${spec.name}`,
5334
6057
  "",
5335
- "## Changed Files",
5336
- ...report.changedFiles.length ? report.changedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
6058
+ spec.description,
5337
6059
  "",
5338
- "## Generated Files",
5339
- ...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
6060
+ "## When To Use",
6061
+ spec.whenToUse,
6062
+ "",
6063
+ "## Inputs",
6064
+ ...Object.entries(spec.inputs).map(([name, input2]) => `- \`${name}\`${input2.required ? " (required)" : ""}: ${input2.description}${input2.allowedValues ? ` Allowed: ${input2.allowedValues.join(", ")}.` : ""}`),
6065
+ "",
6066
+ "## Optional Surfaces",
6067
+ ...Object.entries(spec.optionalSurfaces ?? {}).map(([name, mode]) => `- \`${name}\`: ${mode}`),
6068
+ "",
6069
+ "## Steps",
6070
+ ...spec.steps.map((step, index) => `${index + 1}. \`${step.id}\` (${step.tool}): ${step.description}`),
6071
+ "",
6072
+ "## Validation",
6073
+ ...spec.validation.map((validation) => `- \`${validation.command}\`: ${validation.reason}`),
6074
+ ""
6075
+ ].join(`
6076
+ `);
6077
+ var renderWorkflowPlan = (plan) => [
6078
+ `# Workflow Plan: ${plan.workflow}`,
6079
+ "",
6080
+ `- Mode: ${plan.mode}`,
6081
+ `- Requires approval: ${plan.requiresApproval}`,
6082
+ "",
6083
+ "## Inputs",
6084
+ ...Object.entries(plan.inputs).map(([name, value]) => `- \`${name}\`: ${Array.isArray(value) ? value.join(", ") : value}`),
6085
+ "",
6086
+ "## Optional Surfaces",
6087
+ ...Object.entries(plan.optionalSurfaces).map(([name, mode]) => `- \`${name}\`: ${mode}`),
6088
+ "",
6089
+ "## Steps",
6090
+ ...plan.steps.map((step, index) => `${index + 1}. \`${step.id}\` (${step.tool}): ${step.description}`),
5340
6091
  "",
5341
- "## Applied Commands",
5342
- ...report.appliedCommands.length ? report.appliedCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
6092
+ "## Predicted Changes",
6093
+ ...plan.predictedChanges.map((change) => {
6094
+ const scope = change.applyScope ? ` (${change.applyScope})` : "";
6095
+ return `- \`${change.action}\`${scope} ${change.target}: ${change.reason}`;
6096
+ }),
5343
6097
  "",
5344
- "## Recommended Validation Commands",
5345
- ...report.recommendedValidationCommands.length ? report.recommendedValidationCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
6098
+ "## Validation",
6099
+ ...plan.validation.map((validation) => `- \`${validation.command}\`: ${validation.reason}`),
5346
6100
  "",
5347
6101
  "## Diagnostics",
5348
- ...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
6102
+ ...plan.diagnostics.length ? plan.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
5349
6103
  "",
5350
6104
  "## Recommendations",
5351
- ...report.recommendations.length ? report.recommendations.map((recommendation) => `- [${recommendation.kind}] ${recommendation.message}`) : ["- none"],
5352
- "",
5353
- "## Next Actions",
5354
- ...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
6105
+ ...plan.recommendations.length ? plan.recommendations.map((recommendation) => `- [${recommendation.kind}] ${recommendation.message}`) : ["- none"],
5355
6106
  ""
5356
6107
  ].join(`
5357
6108
  `);
6109
+ var renderRecommendation = (recommendation) => {
6110
+ const target = recommendation.target ? ` ${recommendation.target}` : "";
6111
+ const action = recommendation.action ? ` Action: ${recommendation.action}` : "";
6112
+ return `- [${recommendation.kind}]${target} ${recommendation.message}${action}`;
6113
+ };
6114
+ var renderDiagnostic = (diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`;
6115
+ var renderWorkflowApplyReport = (report) => {
6116
+ const manualReviewItems = [
6117
+ ...report.diagnostics.filter((diagnostic) => diagnostic.severity === "warning").map(renderDiagnostic),
6118
+ ...report.recommendations.filter((recommendation) => recommendation.kind === "manual-action").map(renderRecommendation)
6119
+ ];
6120
+ const validationBlockers = report.diagnostics.filter((diagnostic) => diagnostic.severity === "error" && (diagnostic.failureScope === "workspace-config" || diagnostic.failureScope === "environment")).map(renderDiagnostic);
6121
+ return [
6122
+ `# Workflow Apply: ${report.workflow}`,
6123
+ "",
6124
+ `- Mode: ${report.mode}`,
6125
+ `- Status: ${report.status}`,
6126
+ ...report.validationTarget ? [`- Validation target: ${report.validationTarget}`] : [],
6127
+ "",
6128
+ "## Automatically Modified",
6129
+ ...report.changedFiles.length ? report.changedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
6130
+ "",
6131
+ "## Generated Sync",
6132
+ ...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
6133
+ "",
6134
+ "## Applied Commands",
6135
+ ...report.appliedCommands.length ? report.appliedCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
6136
+ "",
6137
+ "## Recommended Validation Commands",
6138
+ ...report.recommendedValidationCommands.length ? report.recommendedValidationCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
6139
+ "",
6140
+ "## User Review Required",
6141
+ ...manualReviewItems.length ? manualReviewItems : ["- none"],
6142
+ "",
6143
+ "## Validation Blockers",
6144
+ ...validationBlockers.length ? validationBlockers : report.recommendedValidationCommands.length ? ["- none detected during apply; run validation with the validation target for command-level blockers."] : ["- none"],
6145
+ "",
6146
+ "## Diagnostics",
6147
+ ...report.diagnostics.length ? report.diagnostics.map(renderDiagnostic) : ["- none"],
6148
+ "",
6149
+ "## Recommendations",
6150
+ ...report.recommendations.length ? report.recommendations.map(renderRecommendation) : ["- none"],
6151
+ "",
6152
+ "## Next Actions",
6153
+ ...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
6154
+ ""
6155
+ ].join(`
6156
+ `);
6157
+ };
5358
6158
  var renderWorkflowApply = (report, format = "markdown") => format === "json" ? jsonText(report) : renderWorkflowApplyReport(report);
5359
6159
  var renderWorkflowValidationRunReport = (report) => [
5360
6160
  `# Workflow Validation: ${report.workflow}`,
5361
6161
  "",
5362
6162
  `- Run: ${report.runId}`,
5363
6163
  `- Status: ${report.status}`,
6164
+ `- Source status: ${report.sourceStatus}`,
6165
+ `- Workspace status: ${report.workspaceStatus}`,
6166
+ `- Overall status: ${report.overallStatus}`,
6167
+ "",
6168
+ "## Status Summary",
6169
+ `- Required source-change validation: ${report.summary.sourceChange}`,
6170
+ `- Generated sync validation: ${report.summary.generatedSync}`,
6171
+ `- Workspace configuration validation: ${report.summary.workspaceConfig}`,
6172
+ `- Environment validation: ${report.summary.environment}`,
6173
+ "",
6174
+ "## Known Blockers",
6175
+ ...report.knownBlockers.length ? report.knownBlockers.map((blocker) => {
6176
+ const command = blocker.command ? ` \`${blocker.command}\`` : "";
6177
+ const count = blocker.count > 1 ? ` (${blocker.count}x)` : "";
6178
+ const known = blocker.known ? " known" : "";
6179
+ return `- [${blocker.failureScope}${known}]${command}${count}: ${blocker.message}`;
6180
+ }) : ["- none"],
5364
6181
  "",
5365
6182
  "## Commands",
5366
6183
  ...report.commands.length ? report.commands.map((command) => {
@@ -5371,164 +6188,23 @@ var renderWorkflowValidationRunReport = (report) => [
5371
6188
  "## Diagnostics",
5372
6189
  ...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
5373
6190
  "",
5374
- "## Repair Actions",
5375
- ...report.repairActions.length ? report.repairActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
5376
- "",
5377
- "## Next Actions",
5378
- ...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
5379
- ""
5380
- ].join(`
5381
- `);
5382
- var renderWorkflowValidation = (report, format = "markdown") => format === "json" ? jsonText(report) : renderWorkflowValidationRunReport(report);
5383
- var renderWorkflowRunArtifact = (artifact, format = "markdown") => {
5384
- if ("kind" in artifact)
5385
- return renderRepairReport(artifact, format);
5386
- if ("mode" in artifact && artifact.mode === "validate")
5387
- return renderWorkflowValidation(artifact, format);
5388
- if ("mode" in artifact && (artifact.mode === "apply" || artifact.mode === "dry-run")) {
5389
- return renderWorkflowApply(artifact, format);
5390
- }
5391
- return jsonText(artifact);
5392
- };
5393
- var createRepairReport = ({
5394
- command,
5395
- kind,
5396
- target = null,
5397
- diagnostics = [],
5398
- repairActions = [],
5399
- nextActions = [],
5400
- commands = [],
5401
- generatedFiles = [],
5402
- syncedAt
5403
- }) => ({
5404
- schemaVersion: 1,
5405
- command,
5406
- kind,
5407
- target,
5408
- status: workflowStatus(diagnostics) === "failed" || commandStatus(commands) === "failed" ? "failed" : "passed",
5409
- diagnostics,
5410
- repairActions: uniqueBy(repairActions, (action) => action.command),
5411
- nextActions: uniqueBy(nextActions, (action) => action.command),
5412
- commands,
5413
- generatedFiles,
5414
- ...syncedAt ? { syncedAt } : {}
5415
- });
5416
- var renderRepairReportMarkdown = (report) => [
5417
- `# Akan Repair: ${report.kind}`,
5418
- "",
5419
- `- Status: ${report.status}`,
5420
- `- Target: ${report.target ?? "none"}`,
5421
- "",
5422
- "## Commands",
5423
- ...report.commands.length ? report.commands.map((command) => `- [${command.status}] \`${command.command}\`: ${command.reason}`) : ["- none"],
5424
- "",
5425
- "## Diagnostics",
5426
- ...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
5427
- "",
6191
+ "## Repair Actions",
6192
+ ...report.repairActions.length ? report.repairActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
6193
+ "",
5428
6194
  "## Next Actions",
5429
6195
  ...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
5430
6196
  ""
5431
6197
  ].join(`
5432
6198
  `);
5433
- var renderRepairReport = (report, format = "markdown") => format === "json" ? jsonText(report) : renderRepairReportMarkdown(report);
5434
-
5435
- class WorkflowExecutor {
5436
- registry;
5437
- constructor(registry) {
5438
- this.registry = registry;
5439
- }
5440
- async apply(plan) {
5441
- const changedFiles = [];
5442
- const generatedFiles = [];
5443
- const recommendedValidationCommands = [];
5444
- const diagnostics = [...plan.diagnostics];
5445
- const recommendations = [...plan.recommendations];
5446
- const nextActions = [];
5447
- if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
5448
- return createWorkflowApplyReport({
5449
- workflow: plan.workflow,
5450
- mode: "apply",
5451
- changedFiles,
5452
- generatedFiles,
5453
- recommendedValidationCommands,
5454
- diagnostics,
5455
- recommendations,
5456
- nextActions,
5457
- plan
5458
- });
5459
- }
5460
- recommendedValidationCommands.push(...workflowCommandsForPlan(plan));
5461
- nextActions.push(...workflowCommandsForPlan(plan));
5462
- for (const step of plan.steps) {
5463
- const runner = this.registry[workflowStepKey(plan.workflow, step.id)] ?? this.registry[step.tool] ?? this.registry[step.id];
5464
- if (!runner) {
5465
- diagnostics.push({
5466
- severity: "error",
5467
- code: "workflow-step-unsupported",
5468
- message: `Workflow ${plan.workflow} step "${step.id}" is not supported by workflow apply yet.`
5469
- });
5470
- nextActions.push({
5471
- command: `akan workflow explain ${plan.workflow}`,
5472
- reason: "Review the unsupported workflow step before retrying apply."
5473
- });
5474
- break;
5475
- }
5476
- const result = await runner(step, plan);
5477
- if (!result)
5478
- continue;
5479
- changedFiles.push(...result.changedFiles ?? []);
5480
- generatedFiles.push(...result.generatedFiles ?? []);
5481
- recommendedValidationCommands.push(...result.commands ?? []);
5482
- diagnostics.push(...result.diagnostics ?? []);
5483
- recommendations.push(...result.recommendations ?? []);
5484
- nextActions.push(...result.nextActions ?? []);
5485
- if (diagnostics.some((diagnostic) => diagnostic.severity === "error"))
5486
- break;
5487
- }
5488
- return createWorkflowApplyReport({
5489
- workflow: plan.workflow,
5490
- mode: "apply",
5491
- changedFiles,
5492
- generatedFiles,
5493
- recommendedValidationCommands,
5494
- diagnostics,
5495
- recommendations,
5496
- nextActions,
5497
- plan
5498
- });
5499
- }
5500
- }
5501
- var createPrimitiveWriteReport = ({
5502
- command,
5503
- status,
5504
- changedFiles = [],
5505
- generatedFiles = [],
5506
- validationCommands = [],
5507
- diagnostics = [],
5508
- nextActions = []
5509
- }) => ({
5510
- schemaVersion: 1,
5511
- command,
5512
- status: status ?? (diagnostics.some((diagnostic) => diagnostic.severity === "error") ? "failed" : "passed"),
5513
- changedFiles,
5514
- generatedFiles,
5515
- validationCommands,
5516
- diagnostics,
5517
- nextActions
5518
- });
5519
- var renderPrimitiveWriteReport = (report) => [
5520
- `# Primitive Write: ${report.command}`,
6199
+ var renderWorkflowValidation = (report, format = "markdown") => format === "json" ? jsonText(report) : renderWorkflowValidationRunReport(report);
6200
+ var renderRepairReportMarkdown = (report) => [
6201
+ `# Akan Repair: ${report.kind}`,
5521
6202
  "",
5522
6203
  `- Status: ${report.status}`,
6204
+ `- Target: ${report.target ?? "none"}`,
5523
6205
  "",
5524
- "## Changed Files",
5525
- ...report.changedFiles.length ? report.changedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
5526
- "",
5527
- "## Generated Files",
5528
- ...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
5529
- "",
5530
- "## Validation Commands",
5531
- ...report.validationCommands.length ? report.validationCommands.map((validation) => `- \`${validation.command}\`: ${validation.reason}`) : ["- none"],
6206
+ "## Commands",
6207
+ ...report.commands.length ? report.commands.map((command) => `- [${command.status}] \`${command.command}\`: ${command.reason}`) : ["- none"],
5532
6208
  "",
5533
6209
  "## Diagnostics",
5534
6210
  ...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
@@ -5538,154 +6214,17 @@ var renderPrimitiveWriteReport = (report) => [
5538
6214
  ""
5539
6215
  ].join(`
5540
6216
  `);
5541
- var renderPrimitiveReport = (report, format = "markdown") => format === "json" ? jsonText(report) : renderPrimitiveWriteReport(report);
5542
- var getSysRoot = (sys2) => `${sys2.type}s/${sys2.name}`;
5543
- var sourceFile = (sys2, path10, action, reason) => ({
5544
- path: `${getSysRoot(sys2)}/${path10}`,
5545
- action,
5546
- reason
5547
- });
5548
- var generatedFilesForSync = (sys2, reason = "Generated files may change after sync.") => generatedFilePathsForTarget(getSysRoot(sys2), reason);
5549
- var validationCommandsForTarget = (target) => [
5550
- { command: `akan sync ${target}`, reason: "Refresh generated Akan files from source conventions." },
5551
- { command: `akan lint ${target}`, reason: "Validate formatting, imports, and static lint rules." }
5552
- ];
5553
- var nextActionsForTarget = (target) => [
5554
- { command: `akan sync ${target}`, reason: "Refresh generated Akan files after source changes." },
5555
- { command: `akan lint ${target}`, reason: "Validate the target after generated files are refreshed." }
5556
- ];
5557
- var createPassedPrimitiveReport = ({
5558
- command,
5559
- changedFiles,
5560
- generatedFiles,
5561
- target,
5562
- nextActions
5563
- }) => createPrimitiveWriteReport({
5564
- command,
5565
- changedFiles,
5566
- generatedFiles: generatedFiles ?? [],
5567
- validationCommands: validationCommandsForTarget(target),
5568
- diagnostics: [],
5569
- nextActions: nextActions ?? nextActionsForTarget(target)
5570
- });
5571
- var scalarChangedFiles = (sys2, scalarName, files) => Object.values(files).map((file) => sourceFile(sys2, `lib/__scalar/${scalarName}/${file.filename}`, "create", "Scalar source file was created."));
5572
- var titleize = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
5573
- var lowerlize = (value) => `${value.slice(0, 1).toLowerCase()}${value.slice(1)}`;
5574
- var compactDiagnostics = (diagnostics) => diagnostics.filter((diagnostic) => !!diagnostic);
5575
- var normalizeFieldType = (typeName) => {
5576
- const normalizedTypes = {
5577
- string: "String",
5578
- boolean: "Boolean",
5579
- date: "Date",
5580
- int: "Int",
5581
- integer: "Int",
5582
- float: "Float",
5583
- double: "Float",
5584
- decimal: "Float"
5585
- };
5586
- return normalizedTypes[typeName.toLowerCase()] ?? typeName;
5587
- };
5588
- var ensureBaseTypeImport = (content, typeName) => {
5589
- if (typeName !== "Int" && typeName !== "Float")
5590
- return content;
5591
- if (new RegExp(`import \\{[^}]*\\b${typeName}\\b[^}]*\\} from "akanjs/base";`).test(content))
5592
- return content;
5593
- const baseImport = /import \{ ([^}]+) \} from "akanjs\/base";/.exec(content);
5594
- if (baseImport) {
5595
- const names = baseImport[1].split(",").map((name) => name.trim()).filter(Boolean);
5596
- return content.replace(baseImport[0], `import { ${[...names, typeName].sort().join(", ")} } from "akanjs/base";`);
5597
- }
5598
- return `import { ${typeName} } from "akanjs/base";
5599
- ${content}`;
5600
- };
5601
- var fieldExpression = (typeName, defaultValue) => {
5602
- const typeExpression = normalizeFieldType(typeName);
5603
- const defaultOption = defaultValue ? `, { default: ${JSON.stringify(defaultValue)} }` : "";
5604
- return `field(${typeExpression}${defaultOption})`;
5605
- };
5606
- var insertIntoObject = (content, className, line) => {
5607
- const classIndex = content.indexOf(`export class ${className} extends via`);
5608
- if (classIndex < 0)
5609
- return null;
5610
- const objectEndIndex = content.indexOf("}))", classIndex);
5611
- if (objectEndIndex < 0)
5612
- return null;
5613
- const prefix = content.slice(0, objectEndIndex);
5614
- const suffix = content.slice(objectEndIndex);
5615
- const insertion = prefix.endsWith(`
5616
- `) ? ` ${line}
5617
- ` : `
5618
- ${line}
5619
- `;
5620
- return `${prefix}${insertion}${suffix}`;
5621
- };
5622
- var ensureEnumImport = (content) => {
5623
- if (content.includes("enumOf"))
5624
- return content;
5625
- const baseImport = /import \{ ([^}]+) \} from "akanjs\/base";/.exec(content);
5626
- if (baseImport) {
5627
- const names = baseImport[1]?.split(",").map((name) => name.trim()) ?? [];
5628
- return content.replace(baseImport[0], `import { ${[...names, "enumOf"].sort().join(", ")} } from "akanjs/base";`);
5629
- }
5630
- return `import { enumOf } from "akanjs/base";
5631
- ${content}`;
5632
- };
5633
- var insertEnumClass = (content, enumClassName, enumName, values) => {
5634
- if (content.includes(`export class ${enumClassName} extends enumOf`))
5635
- return content;
5636
- const enumClass = `export class ${enumClassName} extends enumOf("${enumName}", [
5637
- ${values.map((value) => ` ${JSON.stringify(value)},`).join(`
5638
- `)}
5639
- ] as const) {}
5640
-
5641
- `;
5642
- const firstClassIndex = content.indexOf("export class ");
5643
- if (firstClassIndex < 0)
5644
- return `${content}
5645
- ${enumClass}`;
5646
- return `${content.slice(0, firstClassIndex)}${enumClass}${content.slice(firstClassIndex)}`;
5647
- };
5648
- var insertDictionaryModelField = (content, moduleClassName, fieldName) => {
5649
- if (new RegExp(`\\b${fieldName}\\s*:`).test(content))
5650
- return content;
5651
- const label = titleize(fieldName);
5652
- const modelIndex = content.indexOf(`.model<${moduleClassName}>((t) => ({`);
5653
- if (modelIndex < 0)
5654
- return null;
5655
- const objectEndIndex = content.indexOf(" }))", modelIndex);
5656
- if (objectEndIndex < 0)
5657
- return null;
5658
- return `${content.slice(0, objectEndIndex)} ${fieldName}: t([${JSON.stringify(label)}, ${JSON.stringify(label)}]).desc([${JSON.stringify(label)}, ${JSON.stringify(label)}]),
5659
- ${content.slice(objectEndIndex)}`;
5660
- };
5661
- var ensureConstantTypeImport = (content, constantPath, typeName) => {
5662
- if (new RegExp(`import type \\{[^}]*\\b${typeName}\\b[^}]*\\} from "${constantPath}";`).test(content))
5663
- return content;
5664
- const importPattern = new RegExp(`import type \\{ ([^}]+) \\} from "${constantPath}";`);
5665
- const existingImport = content.match(importPattern);
5666
- if (existingImport !== null) {
5667
- const names = existingImport[1]?.split(",").map((name) => name.trim()) ?? [];
5668
- return content.replace(existingImport[0], `import type { ${[...names, typeName].sort().join(", ")} } from "${constantPath}";`);
6217
+ var renderRepairReport = (report, format = "markdown") => format === "json" ? jsonText(report) : renderRepairReportMarkdown(report);
6218
+ var renderWorkflowRunArtifact = (artifact, format = "markdown") => {
6219
+ if ("kind" in artifact)
6220
+ return renderRepairReport(artifact, format);
6221
+ if ("mode" in artifact && artifact.mode === "validate")
6222
+ return renderWorkflowValidation(artifact, format);
6223
+ if ("mode" in artifact && (artifact.mode === "apply" || artifact.mode === "dry-run")) {
6224
+ return renderWorkflowApply(artifact, format);
5669
6225
  }
5670
- return `import type { ${typeName} } from "${constantPath}";
5671
- ${content}`;
5672
- };
5673
- var insertDictionaryEnum = (content, enumClassName, enumName, values) => {
5674
- if (content.includes(`.enum<${enumClassName}>("${enumName}"`))
5675
- return content;
5676
- const enumBlock = ` .enum<${enumClassName}>("${enumName}", (t) => ({
5677
- ${values.map((value) => ` ${value}: t([${JSON.stringify(titleize(value))}, ${JSON.stringify(titleize(value))}]),`).join(`
5678
- `)}
5679
- }))
5680
- `;
5681
- const chainEndIndex = content.lastIndexOf(";");
5682
- const insertBeforeIndex = [content.indexOf(".error("), content.indexOf(".translate("), chainEndIndex].filter((index) => index >= 0).sort((a, b) => a - b)[0];
5683
- if (insertBeforeIndex === undefined)
5684
- return null;
5685
- return `${content.slice(0, insertBeforeIndex)}${enumBlock}${content.slice(insertBeforeIndex)}`;
6226
+ return jsonText(artifact);
5686
6227
  };
5687
- var parseValues = (value) => value?.split(",").map((item) => item.trim()).filter(Boolean) ?? [];
5688
-
5689
6228
  // pkgs/@akanjs/devkit/akanContext.ts
5690
6229
  var resourceList = [
5691
6230
  { uri: "akan://docs/framework", name: "Akan framework guide", mimeType: "text/markdown" },
@@ -5821,17 +6360,17 @@ var safeReadJson = async (filePath) => {
5821
6360
  var isWorkflowPlan = (value) => typeof value === "object" && value !== null && ("schemaVersion" in value) && value.schemaVersion === 1 && ("mode" in value) && value.mode === "plan";
5822
6361
  var isWorkflowApplyReport = (value) => typeof value === "object" && value !== null && ("schemaVersion" in value) && value.schemaVersion === 1 && ("mode" in value) && (value.mode === "apply" || value.mode === "dry-run");
5823
6362
  var isWorkflowRunArtifact = (value) => typeof value === "object" && value !== null && ("schemaVersion" in value) && value.schemaVersion === 1;
5824
- var planInputString = (plan, key) => {
5825
- const value = plan.inputs[key];
6363
+ var planInputString = (plan2, key) => {
6364
+ const value = plan2.inputs[key];
5826
6365
  return typeof value === "string" ? value : "";
5827
6366
  };
5828
- var expandWorkflowTarget = (target, plan) => {
5829
- const app = planInputString(plan, "app");
5830
- const module = planInputString(plan, "module");
5831
- const moduleClass = module ? capitalize3(module) : "<Module>";
6367
+ var expandWorkflowTarget = (target, plan2) => {
6368
+ const app = planInputString(plan2, "app");
6369
+ const module = planInputString(plan2, "module");
6370
+ const moduleClass = module ? capitalize4(module) : "<Module>";
5832
6371
  return target.replace(/^\*\//, app ? `apps/${app}/` : "").replaceAll("<module>", module || "<module>").replaceAll("<Module>", moduleClass);
5833
6372
  };
5834
- var workflowPathsForPlan = (plan) => plan.predictedChanges.map((change) => expandWorkflowTarget(change.target, plan));
6373
+ var workflowPathsForPlan = (plan2) => plan2.predictedChanges.map((change) => expandWorkflowTarget(change.target, plan2));
5835
6374
  var workflowPathsForArtifact = (artifact) => {
5836
6375
  if (isWorkflowPlan(artifact))
5837
6376
  return workflowPathsForPlan(artifact);
@@ -6046,7 +6585,7 @@ class AkanContextAnalyzer {
6046
6585
  severity: strict ? "error" : "warning",
6047
6586
  code: "module-abstract-missing",
6048
6587
  path: module.abstract.path,
6049
- message: `${capitalize3(module.kind)} module ${sys2.name}:${module.name} should include ${module.abstract.path}`,
6588
+ message: `${capitalize4(module.kind)} module ${sys2.name}:${module.name} should include ${module.abstract.path}`,
6050
6589
  repairActions: [action]
6051
6590
  });
6052
6591
  repairActions.push(action);
@@ -6058,7 +6597,7 @@ class AkanContextAnalyzer {
6058
6597
  severity: "error",
6059
6598
  code: "module-shape-invalid",
6060
6599
  path: module.path,
6061
- message: `${capitalize3(module.kind)} module ${sys2.name}:${module.name} is missing required files: ${missingFiles.join(", ")}`,
6600
+ message: `${capitalize4(module.kind)} module ${sys2.name}:${module.name} is missing required files: ${missingFiles.join(", ")}`,
6062
6601
  repairActions: [action]
6063
6602
  });
6064
6603
  repairActions.push(action);
@@ -6109,8 +6648,8 @@ class AkanContextAnalyzer {
6109
6648
  ...workflowPaths.length ? { baselineDiagnostics, workflowDiagnostics } : {}
6110
6649
  };
6111
6650
  }
6112
- static findModules(context, moduleName) {
6113
- const modules = [...context.apps, ...context.libs].flatMap((sys2) => sys2.modules);
6651
+ static findModules(context, moduleName, { app = null } = {}) {
6652
+ const modules = [...context.apps, ...context.libs].filter((sys2) => !app || sys2.name === app).flatMap((sys2) => sys2.modules);
6114
6653
  return moduleName ? modules.filter((module) => module.name === moduleName || module.folderName === moduleName) : modules;
6115
6654
  }
6116
6655
  static renderMarkdown(context, { module: moduleName } = {}) {
@@ -6300,7 +6839,7 @@ void st;
6300
6839
  ` : `const UserLayout = ({ children }) => children;
6301
6840
  const userLayout = {};
6302
6841
  `;
6303
- const source = opts.includeSystemProvider ? `import type { LayoutProps, PageProps } from "akanjs/client";
6842
+ const source2 = opts.includeSystemProvider ? `import type { LayoutProps, PageProps } from "akanjs/client";
6304
6843
  import { loadFonts } from "akanjs/client";
6305
6844
  import { System } from "akanjs/ui";
6306
6845
  import { env } from "@apps/${opts.appName}/env/env.client";
@@ -6376,7 +6915,7 @@ export default function GeneratedLayout({ children, params, searchParams }: Layo
6376
6915
  return <UserLayout params={params} searchParams={searchParams}>{children}</UserLayout>;
6377
6916
  }
6378
6917
  `;
6379
- await Bun.write(absPath, source);
6918
+ await Bun.write(absPath, source2);
6380
6919
  return absPath;
6381
6920
  }
6382
6921
  async function resolveSsrPageEntries(opts) {
@@ -6533,23 +7072,23 @@ class BarrelAnalyzer {
6533
7072
  if (visited.has(absFile))
6534
7073
  return;
6535
7074
  visited.add(absFile);
6536
- const source = await readIfExists(absFile);
6537
- if (source === null)
7075
+ const source2 = await readIfExists(absFile);
7076
+ if (source2 === null)
6538
7077
  return;
6539
7078
  const currentSubpath = this.#subpathFor(pkg, absFile);
6540
7079
  if (!currentSubpath)
6541
7080
  return;
6542
- const authoritative = this.#scanExports(source, absFile);
7081
+ const authoritative = this.#scanExports(source2, absFile);
6543
7082
  authoritative.delete("default");
6544
7083
  const attributed = new Set;
6545
7084
  REEXPORT_RE.lastIndex = 0;
6546
- let m = REEXPORT_RE.exec(source);
7085
+ let m = REEXPORT_RE.exec(source2);
6547
7086
  while (m !== null) {
6548
7087
  const star = m[1];
6549
7088
  const nsAs = m[2];
6550
7089
  const namedList = m[3];
6551
7090
  const spec = m[5] ?? "";
6552
- m = REEXPORT_RE.exec(source);
7091
+ m = REEXPORT_RE.exec(source2);
6553
7092
  if (!isRelative(spec))
6554
7093
  continue;
6555
7094
  if (star) {
@@ -6585,10 +7124,10 @@ class BarrelAnalyzer {
6585
7124
  }
6586
7125
  }
6587
7126
  LOCAL_NAMED_RE.lastIndex = 0;
6588
- let n = LOCAL_NAMED_RE.exec(source);
7127
+ let n = LOCAL_NAMED_RE.exec(source2);
6589
7128
  while (n !== null) {
6590
7129
  const body = n[1] ?? "";
6591
- n = LOCAL_NAMED_RE.exec(source);
7130
+ n = LOCAL_NAMED_RE.exec(source2);
6592
7131
  for (const item of parseNamedList(body)) {
6593
7132
  if (item.isType)
6594
7133
  continue;
@@ -6610,10 +7149,10 @@ class BarrelAnalyzer {
6610
7149
  map.set(name, { subpath: currentSubpath, originalName: name });
6611
7150
  }
6612
7151
  }
6613
- #scanExports(source, absFile) {
7152
+ #scanExports(source2, absFile) {
6614
7153
  try {
6615
7154
  const transpiler = [".tsx", ".jsx"].includes(path13.extname(absFile)) ? this.#tsxTranspiler : this.#tsTranspiler;
6616
- const { exports } = transpiler.scan(source);
7155
+ const { exports } = transpiler.scan(source2);
6617
7156
  return new Set(exports);
6618
7157
  } catch (err) {
6619
7158
  this.#logger.error(`scan failed: ${err.message}`);
@@ -6725,28 +7264,28 @@ var createBarrelImportsPlugin = async (app, { skipPath = defaultSkipPath, pipeAf
6725
7264
  const raw = await Bun.file(realPath).text();
6726
7265
  return { contents: raw, loader };
6727
7266
  }
6728
- let source = await Bun.file(realPath).text();
6729
- const hasMacroAttr = MACRO_ATTR_RE.test(source);
7267
+ let source2 = await Bun.file(realPath).text();
7268
+ const hasMacroAttr = MACRO_ATTR_RE.test(source2);
6730
7269
  if (!hasMacroAttr && barrels.length > 0) {
6731
7270
  let maybe = false;
6732
7271
  for (const b of barrels) {
6733
- if (source.includes(b)) {
7272
+ if (source2.includes(b)) {
6734
7273
  maybe = true;
6735
7274
  break;
6736
7275
  }
6737
7276
  }
6738
7277
  if (maybe) {
6739
- const rewritten = await rewriteBarrelImports(source, barrels, analyzer);
7278
+ const rewritten = await rewriteBarrelImports(source2, barrels, analyzer);
6740
7279
  if (rewritten !== null)
6741
- source = rewritten;
7280
+ source2 = rewritten;
6742
7281
  }
6743
7282
  }
6744
7283
  if (pipeAfter) {
6745
- const piped = await pipeAfter(source, { path: realPath });
7284
+ const piped = await pipeAfter(source2, { path: realPath });
6746
7285
  if (piped !== null)
6747
- source = piped;
7286
+ source2 = piped;
6748
7287
  }
6749
- return { contents: source, loader };
7288
+ return { contents: source2, loader };
6750
7289
  });
6751
7290
  }
6752
7291
  };
@@ -6922,12 +7461,12 @@ var resolveFileCandidate = async (candidate) => {
6922
7461
  return null;
6923
7462
  };
6924
7463
  var MACRO_ATTR_RE = /with\s*\{\s*type\s*:\s*["']macro["']\s*\}/;
6925
- var rewriteBarrelImports = async (source, barrels, analyzer) => {
6926
- const statements = findImportStatements(source);
7464
+ var rewriteBarrelImports = async (source2, barrels, analyzer) => {
7465
+ const statements = findImportStatements(source2);
6927
7466
  if (statements.length === 0)
6928
7467
  return null;
6929
7468
  let changed = false;
6930
- let out = source;
7469
+ let out = source2;
6931
7470
  for (let i = statements.length - 1;i >= 0; i--) {
6932
7471
  const stmt = statements[i];
6933
7472
  if (!stmt)
@@ -6945,9 +7484,9 @@ var rewriteBarrelImports = async (source, barrels, analyzer) => {
6945
7484
  }
6946
7485
  return changed ? out : null;
6947
7486
  };
6948
- var findImportStatements = (source) => {
7487
+ var findImportStatements = (source2) => {
6949
7488
  const statements = [];
6950
- const sourceFile2 = ts4.createSourceFile("barrel-imports.tsx", source, ts4.ScriptTarget.Latest, true, ts4.ScriptKind.TSX);
7489
+ const sourceFile2 = ts4.createSourceFile("barrel-imports.tsx", source2, ts4.ScriptTarget.Latest, true, ts4.ScriptKind.TSX);
6951
7490
  for (const statement of sourceFile2.statements) {
6952
7491
  if (!ts4.isImportDeclaration(statement))
6953
7492
  continue;
@@ -6961,10 +7500,10 @@ var findImportStatements = (source) => {
6961
7500
  statements.push({
6962
7501
  start: statementStart,
6963
7502
  end: statementEnd,
6964
- clause: source.slice(importClause.getStart(sourceFile2), importClause.end).trim(),
7503
+ clause: source2.slice(importClause.getStart(sourceFile2), importClause.end).trim(),
6965
7504
  specifier: statement.moduleSpecifier.text,
6966
- trailingSemicolon: source.slice(statement.moduleSpecifier.end, statement.end).includes(";"),
6967
- raw: source.slice(statementStart, statementEnd)
7505
+ trailingSemicolon: source2.slice(statement.moduleSpecifier.end, statement.end).includes(";"),
7506
+ raw: source2.slice(statementStart, statementEnd)
6968
7507
  });
6969
7508
  }
6970
7509
  return statements;
@@ -7214,13 +7753,13 @@ class GraphClientEntryDiscovery {
7214
7753
  }
7215
7754
  return cached;
7216
7755
  }
7217
- async#getImports(file, source) {
7756
+ async#getImports(file, source2) {
7218
7757
  const absPath = path15.resolve(file);
7219
7758
  let cached = this.#importCache.get(absPath);
7220
7759
  if (!cached) {
7221
7760
  cached = Promise.resolve().then(() => {
7222
7761
  try {
7223
- return this.#tsTranspiler.scanImports(source);
7762
+ return this.#tsTranspiler.scanImports(source2);
7224
7763
  } catch {
7225
7764
  return [];
7226
7765
  }
@@ -7245,8 +7784,8 @@ class GraphClientEntryDiscovery {
7245
7784
  entries.add(absPath);
7246
7785
  return this.#finishDiscovery(absPath, visiting, entries);
7247
7786
  }
7248
- const source = await this.#getRewrittenSource(absPath, content);
7249
- const imports = await this.#getImports(absPath, source);
7787
+ const source2 = await this.#getRewrittenSource(absPath, content);
7788
+ const imports = await this.#getImports(absPath, source2);
7250
7789
  const importerDir = path15.dirname(absPath);
7251
7790
  for (const imp of imports) {
7252
7791
  const spec = imp.path;
@@ -7282,13 +7821,13 @@ var IMPLICIT_ROOT_LAYOUT_RE = /[/\\]\.akan[/\\]generated[/\\](?:implicit-root-la
7282
7821
  function toClientReferencePath(absPath, workspaceRoot) {
7283
7822
  return path16.relative(path16.resolve(workspaceRoot), path16.resolve(absPath)).split(path16.sep).join("/");
7284
7823
  }
7285
- function transformUseClient(source, args) {
7286
- if (!USE_CLIENT_RE2.test(source))
7824
+ function transformUseClient(source2, args) {
7825
+ if (!USE_CLIENT_RE2.test(source2))
7287
7826
  return null;
7288
7827
  if (IMPLICIT_ROOT_LAYOUT_RE.test(args.path))
7289
7828
  return null;
7290
7829
  const transpiler = new Bun.Transpiler({ loader: loaderFor2(args.path) });
7291
- const { exports } = transpiler.scan(source);
7830
+ const { exports } = transpiler.scan(source2);
7292
7831
  if (exports.length === 0)
7293
7832
  return null;
7294
7833
  const referencePath = args.workspaceRoot ? toClientReferencePath(args.path, args.workspaceRoot) : args.path;
@@ -7434,9 +7973,9 @@ ${absEntry}`).toString(36);
7434
7973
  `;
7435
7974
  }
7436
7975
  async#scanEntryExportNames(absEntry) {
7437
- const source = await Bun.file(absEntry).text();
7976
+ const source2 = await Bun.file(absEntry).text();
7438
7977
  const transpiler = new Bun.Transpiler({ loader: this.#loaderForEntry(absEntry) });
7439
- return transpiler.scan(source).exports;
7978
+ return transpiler.scan(source2).exports;
7440
7979
  }
7441
7980
  #loaderForEntry(absPath) {
7442
7981
  if (absPath.endsWith(".tsx"))
@@ -7483,14 +8022,14 @@ ${absEntry}`).toString(36);
7483
8022
  if (Object.keys(this.#externalAliases).length === 0)
7484
8023
  return;
7485
8024
  await Promise.all(outputs.filter((output) => output.path.endsWith(".js")).map(async (output) => {
7486
- const source = await Bun.file(output.path).text();
7487
- const rewritten = ClientEntriesBundler.rewriteExternalImportSpecifiers(source, this.#externalAliases);
7488
- if (rewritten !== source)
8025
+ const source2 = await Bun.file(output.path).text();
8026
+ const rewritten = ClientEntriesBundler.rewriteExternalImportSpecifiers(source2, this.#externalAliases);
8027
+ if (rewritten !== source2)
7489
8028
  await Bun.write(output.path, rewritten);
7490
8029
  }));
7491
8030
  }
7492
- static rewriteExternalImportSpecifiers(source, aliases) {
7493
- let rewritten = source;
8031
+ static rewriteExternalImportSpecifiers(source2, aliases) {
8032
+ let rewritten = source2;
7494
8033
  for (const [specifier, alias] of Object.entries(aliases)) {
7495
8034
  if (!alias)
7496
8035
  continue;
@@ -7745,9 +8284,9 @@ ${absEntry}`).toString(36);
7745
8284
  return { buildEntries, originalByBuildEntry };
7746
8285
  }
7747
8286
  async#scanExportNames(absEntry) {
7748
- const source = await Bun.file(absEntry).text();
8287
+ const source2 = await Bun.file(absEntry).text();
7749
8288
  const transpiler = new Bun.Transpiler({ loader: this.#loaderFor(absEntry) });
7750
- return transpiler.scan(source).exports;
8289
+ return transpiler.scan(source2).exports;
7751
8290
  }
7752
8291
  #loaderFor(absPath) {
7753
8292
  if (absPath.endsWith(".tsx"))
@@ -7758,10 +8297,10 @@ ${absEntry}`).toString(36);
7758
8297
  return "ts";
7759
8298
  return "js";
7760
8299
  }
7761
- static normalizeNamedDefaultFunctionForFastRefresh(source) {
8300
+ static normalizeNamedDefaultFunctionForFastRefresh(source2) {
7762
8301
  let changed = false;
7763
8302
  const defaultNames = [];
7764
- 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) => {
8303
+ 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) => {
7765
8304
  changed = true;
7766
8305
  defaultNames.push(name);
7767
8306
  return `${lineStart}${indent}${asyncKeyword ?? ""}function ${name}`;
@@ -7989,8 +8528,8 @@ ${entries.join(`
7989
8528
  }
7990
8529
  static #hasAsyncDefaultExport(moduleAbsPath) {
7991
8530
  try {
7992
- const source = fs3.readFileSync(path21.resolve(moduleAbsPath), "utf8");
7993
- const sourceFile2 = ts5.createSourceFile(moduleAbsPath, source, ts5.ScriptTarget.Latest, true, PagesEntrySourceGenerator.#scriptKind(moduleAbsPath));
8531
+ const source2 = fs3.readFileSync(path21.resolve(moduleAbsPath), "utf8");
8532
+ const sourceFile2 = ts5.createSourceFile(moduleAbsPath, source2, ts5.ScriptTarget.Latest, true, PagesEntrySourceGenerator.#scriptKind(moduleAbsPath));
7994
8533
  return PagesEntrySourceGenerator.#sourceFileHasAsyncDefaultExport(sourceFile2);
7995
8534
  } catch {
7996
8535
  return false;
@@ -8250,8 +8789,8 @@ ${CsrArtifactBuilder.escapeInlineScript(await loadScript(src))}
8250
8789
  return html;
8251
8790
  return result + html.slice(lastIndex);
8252
8791
  }
8253
- static escapeInlineScript(source) {
8254
- return source.replace(/<\/script/gi, "<\\/script");
8792
+ static escapeInlineScript(source2) {
8793
+ return source2.replace(/<\/script/gi, "<\\/script");
8255
8794
  }
8256
8795
  static resolveHtmlAssetPath(htmlPath, src) {
8257
8796
  if (/^[a-z][a-z0-9+.-]*:/i.test(src) || src.startsWith("//")) {
@@ -8482,17 +9021,17 @@ class CssCompiler {
8482
9021
  } catch {
8483
9022
  continue;
8484
9023
  }
8485
- let source = content;
9024
+ let source2 = content;
8486
9025
  if (akanConfig2.barrelImports.length > 0) {
8487
9026
  try {
8488
9027
  const rewritten = await rewriteBarrelImports(content, akanConfig2.barrelImports, analyzer);
8489
9028
  if (rewritten !== null)
8490
- source = rewritten;
9029
+ source2 = rewritten;
8491
9030
  } catch {}
8492
9031
  }
8493
9032
  let imports;
8494
9033
  try {
8495
- imports = this.#transpiler.scanImports(source);
9034
+ imports = this.#transpiler.scanImports(source2);
8496
9035
  } catch {
8497
9036
  continue;
8498
9037
  }
@@ -8931,7 +9470,7 @@ class DevGeneratedIndexSync {
8931
9470
  const rawModel = moduleSegment.startsWith("_") ? moduleSegment.slice(1) : moduleSegment;
8932
9471
  if (!rawModel)
8933
9472
  return null;
8934
- const modelName = capitalize4(rawModel);
9473
+ const modelName = capitalize5(rawModel);
8935
9474
  const allowedTypes = isScalar ? SCALAR_UI_TYPES : moduleSegment.startsWith("_") ? SERVICE_UI_TYPES : MODULE_UI_TYPES;
8936
9475
  const fileTypes = [];
8937
9476
  for (const type of allowedTypes) {
@@ -8948,7 +9487,7 @@ export const ${modelName} = { ${fileTypes.join(", ")} };`;
8948
9487
  }
8949
9488
  }
8950
9489
  var exists = async (file) => stat3(file).then(() => true).catch(() => false);
8951
- var capitalize4 = (value) => `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
9490
+ var capitalize5 = (value) => `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
8952
9491
  var formatError = (err) => err instanceof Error ? err.message : String(err);
8953
9492
  // pkgs/@akanjs/devkit/frontendBuild/fontOptimizer.ts
8954
9493
  import { mkdir as mkdir7 } from "fs/promises";
@@ -9025,8 +9564,8 @@ class FontOptimizer {
9025
9564
  if (faceCss.length > 0)
9026
9565
  this.#cssParts.push(...faceCss, this.#buildRootVariableRule(font));
9027
9566
  }
9028
- #extractFontsExport(source, filePath) {
9029
- const sourceFile2 = ts6.createSourceFile(filePath, source, ts6.ScriptTarget.Latest, true, ts6.ScriptKind.TSX);
9567
+ #extractFontsExport(source2, filePath) {
9568
+ const sourceFile2 = ts6.createSourceFile(filePath, source2, ts6.ScriptTarget.Latest, true, ts6.ScriptKind.TSX);
9030
9569
  const fonts = [];
9031
9570
  for (const statement of sourceFile2.statements) {
9032
9571
  if (!ts6.isVariableStatement(statement))
@@ -9582,13 +10121,13 @@ function createUseClientBundlePlugin(options = {}) {
9582
10121
  build.onLoad({ filter: /\.(tsx|ts|jsx|js)$/ }, async (args) => {
9583
10122
  if (args.path.includes("/node_modules/"))
9584
10123
  return;
9585
- let source;
10124
+ let source2;
9586
10125
  try {
9587
- source = await Bun.file(args.path).text();
10126
+ source2 = await Bun.file(args.path).text();
9588
10127
  } catch {
9589
10128
  return;
9590
10129
  }
9591
- const stubbed = transformUseClient(source, {
10130
+ const stubbed = transformUseClient(source2, {
9592
10131
  path: args.path,
9593
10132
  workspaceRoot: options.workspaceRoot
9594
10133
  });
@@ -9646,7 +10185,7 @@ class PagesBundleBuilder {
9646
10185
  PagesBundleBuilder.createServerUseClientFetchPlugin(),
9647
10186
  await createExternalizeFrameworkPlugin({ app: this.#app, extra: akanConfig2.externalLibs }),
9648
10187
  akanConfig2.barrelImports.length > 0 ? await createBarrelImportsPlugin(this.#app, {
9649
- pipeAfter: (source, args) => transformUseClient(source, {
10188
+ pipeAfter: (source2, args) => transformUseClient(source2, {
9650
10189
  path: args.path,
9651
10190
  workspaceRoot
9652
10191
  })
@@ -9687,7 +10226,7 @@ class PagesBundleBuilder {
9687
10226
  ...Object.fromEntries(Object.entries(this.#app.getPublicEnv()).map(([key, value]) => [`process.env.${key}`, JSON.stringify(value)]))
9688
10227
  };
9689
10228
  }
9690
- static createPagesEntryPlugin(source) {
10229
+ static createPagesEntryPlugin(source2) {
9691
10230
  return {
9692
10231
  name: "akan-pages-entry",
9693
10232
  setup(build) {
@@ -9696,7 +10235,7 @@ class PagesBundleBuilder {
9696
10235
  namespace: "akan-virtual"
9697
10236
  }));
9698
10237
  build.onLoad({ filter: /^akan-pages-entry$/, namespace: "akan-virtual" }, () => ({
9699
- contents: source,
10238
+ contents: source2,
9700
10239
  loader: "tsx"
9701
10240
  }));
9702
10241
  }
@@ -9718,19 +10257,19 @@ class PagesBundleBuilder {
9718
10257
  name: "akan-server-use-client-fetch",
9719
10258
  setup(build) {
9720
10259
  build.onLoad({ filter: /[/\\]lib[/\\]useClient\.(ts|tsx|js|jsx)$/ }, async (args) => {
9721
- const source = await Bun.file(args.path).text();
9722
- const transformed = PagesBundleBuilder.transformServerUseClientFetchSource(source);
9723
- if (transformed === source)
10260
+ const source2 = await Bun.file(args.path).text();
10261
+ const transformed = PagesBundleBuilder.transformServerUseClientFetchSource(source2);
10262
+ if (transformed === source2)
9724
10263
  return;
9725
10264
  return { contents: transformed, loader: loaderFor4(args.path) };
9726
10265
  });
9727
10266
  }
9728
10267
  };
9729
10268
  }
9730
- static transformServerUseClientFetchSource(source) {
9731
- if (!source.includes(`with { type: "macro" }`) || !source.includes("FetchClient.build"))
9732
- return source;
9733
- 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 });");
10269
+ static transformServerUseClientFetchSource(source2) {
10270
+ if (!source2.includes(`with { type: "macro" }`) || !source2.includes("FetchClient.build"))
10271
+ return source2;
10272
+ 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 });");
9734
10273
  }
9735
10274
  }
9736
10275
  function loaderFor4(absPath) {
@@ -10496,8 +11035,8 @@ class Builder {
10496
11035
  #executor;
10497
11036
  #distExecutor;
10498
11037
  #pkgJson;
10499
- constructor({ executor, distExecutor, pkgJson }) {
10500
- this.#executor = executor;
11038
+ constructor({ executor: executor2, distExecutor, pkgJson }) {
11039
+ this.#executor = executor2;
10501
11040
  this.#distExecutor = distExecutor;
10502
11041
  this.#pkgJson = pkgJson;
10503
11042
  }
@@ -10623,7 +11162,7 @@ import os from "os";
10623
11162
  import path38 from "path";
10624
11163
  import { select as select2 } from "@inquirer/prompts";
10625
11164
  import { MobileProject } from "@trapezedev/project";
10626
- import { capitalize as capitalize5 } from "akanjs/common";
11165
+ import { capitalize as capitalize6 } from "akanjs/common";
10627
11166
 
10628
11167
  // pkgs/@akanjs/devkit/fileEditor.ts
10629
11168
  class FileEditor {
@@ -11714,7 +12253,7 @@ ${error.message}`;
11714
12253
  this.#setPermissionsInAndroid(["POST_NOTIFICATIONS"]);
11715
12254
  }
11716
12255
  async#setPermissionInIos(permissions) {
11717
- const updateNs = Object.fromEntries(Object.entries(permissions).map(([key, value]) => [`NS${capitalize5(key)}`, value]));
12256
+ const updateNs = Object.fromEntries(Object.entries(permissions).map(([key, value]) => [`NS${capitalize6(key)}`, value]));
11718
12257
  await Promise.all([
11719
12258
  this.project.ios.updateInfoPlist(this.iosTargetName, "Debug", updateNs),
11720
12259
  this.project.ios.updateInfoPlist(this.iosTargetName, "Release", updateNs)
@@ -11806,8 +12345,8 @@ import chalk6 from "chalk";
11806
12345
  import { program } from "commander";
11807
12346
 
11808
12347
  // pkgs/@akanjs/devkit/commandDecorators/dependencyBuilder.ts
11809
- var capitalize6 = (value) => value.slice(0, 1).toUpperCase() + value.slice(1);
11810
- var createDependencyKey = (refName, kind) => `${refName}${capitalize6(kind)}`;
12348
+ var capitalize7 = (value) => value.slice(0, 1).toUpperCase() + value.slice(1);
12349
+ var createDependencyKey = (refName, kind) => `${refName}${capitalize7(kind)}`;
11811
12350
 
11812
12351
  class CommandContainer {
11813
12352
  static #instances = new Map;
@@ -12287,13 +12826,13 @@ var getInternalArgumentValue = async (argMeta, value, workspace) => {
12287
12826
  ...libNames.map((name2) => ({ name: name2, value: { type: "lib", name: name2 } }))
12288
12827
  ]
12289
12828
  });
12290
- const executor = type === "app" ? AppExecutor.from(workspace, name) : LibExecutor.from(workspace, name);
12291
- const modules = await executor.getModules();
12829
+ const executor2 = type === "app" ? AppExecutor.from(workspace, name) : LibExecutor.from(workspace, name);
12830
+ const modules = await executor2.getModules();
12292
12831
  const moduleName = await select3({
12293
12832
  message: `Select the module name`,
12294
- choices: modules.map((name2) => ({ name: `${executor.name}:${name2}`, value: name2 }))
12833
+ choices: modules.map((name2) => ({ name: `${executor2.name}:${name2}`, value: name2 }))
12295
12834
  });
12296
- return ModuleExecutor.from(executor, moduleName);
12835
+ return ModuleExecutor.from(executor2, moduleName);
12297
12836
  } else
12298
12837
  throw new Error(`Invalid system type: ${argMeta.type}`);
12299
12838
  };
@@ -12545,6 +13084,8 @@ var NODE_NATIVE_MODULE_SET = new Set([
12545
13084
  ]);
12546
13085
  // pkgs/@akanjs/devkit/getCredentials.ts
12547
13086
  import yaml from "js-yaml";
13087
+ // pkgs/@akanjs/devkit/getModelFileData.ts
13088
+ import { capitalize as capitalize8 } from "akanjs/common";
12548
13089
  // pkgs/@akanjs/devkit/getRelatedCnsts.ts
12549
13090
  import { readFileSync as readFileSync4, realpathSync } from "fs";
12550
13091
  import ora2 from "ora";
@@ -12552,18 +13093,18 @@ import * as ts7 from "typescript";
12552
13093
  var tsTranspiler = new Bun.Transpiler({ loader: "ts" });
12553
13094
  var tsxTranspiler = new Bun.Transpiler({ loader: "tsx" });
12554
13095
  var getTranspiler = (filePath) => filePath.endsWith(".tsx") ? tsxTranspiler : tsTranspiler;
12555
- var scanModuleSpecifiers = (source, filePath, includeExports) => {
12556
- const scannedImports = getTranspiler(filePath).scanImports(source).map((imp) => imp.path).filter(Boolean);
13096
+ var scanModuleSpecifiers = (source2, filePath, includeExports) => {
13097
+ const scannedImports = getTranspiler(filePath).scanImports(source2).map((imp) => imp.path).filter(Boolean);
12557
13098
  if (includeExports) {
12558
13099
  const specifiers = new Set(scannedImports);
12559
13100
  const typeOnlyModuleRegex = /\b(?:import|export)\s+type\s+[\s\S]*?\s+from\s*["']([^"']+)["']/g;
12560
- for (const match of source.matchAll(typeOnlyModuleRegex)) {
13101
+ for (const match of source2.matchAll(typeOnlyModuleRegex)) {
12561
13102
  const importPath = match[1];
12562
13103
  if (importPath)
12563
13104
  specifiers.add(importPath);
12564
13105
  }
12565
13106
  const exportFromRegex = /\bexport\s+(?:type\s+)?(?:\*|{[\s\S]*?})\s+from\s*["']([^"']+)["']/g;
12566
- for (const match of source.matchAll(exportFromRegex)) {
13107
+ for (const match of source2.matchAll(exportFromRegex)) {
12567
13108
  const importPath = match[1];
12568
13109
  if (importPath)
12569
13110
  specifiers.add(importPath);
@@ -12572,7 +13113,7 @@ var scanModuleSpecifiers = (source, filePath, includeExports) => {
12572
13113
  }
12573
13114
  const importSpecifiers = new Set;
12574
13115
  const importDeclarationRegex = /\bimport\s+(?:type\s+)?(?:["']([^"']+)["']|[\s\S]*?\s+from\s*["']([^"']+)["'])/g;
12575
- for (const match of source.matchAll(importDeclarationRegex)) {
13116
+ for (const match of source2.matchAll(importDeclarationRegex)) {
12576
13117
  const importPath = match[1] ?? match[2];
12577
13118
  if (importPath && (scannedImports.includes(importPath) || match[0].startsWith("import type"))) {
12578
13119
  importSpecifiers.add(importPath);
@@ -12595,8 +13136,8 @@ var collectImportedFiles = (constantFilePath, parsedConfig) => {
12595
13136
  if (analyzedFiles.has(filePath))
12596
13137
  return;
12597
13138
  analyzedFiles.add(filePath);
12598
- const source = readFileSync4(filePath, "utf-8");
12599
- for (const importPath of scanModuleSpecifiers(source, filePath, false)) {
13139
+ const source2 = readFileSync4(filePath, "utf-8");
13140
+ for (const importPath of scanModuleSpecifiers(source2, filePath, false)) {
12600
13141
  if (!importPath.startsWith("."))
12601
13142
  continue;
12602
13143
  const resolved = ts7.resolveModuleName(importPath, filePath, parsedConfig.options, ts7.sys).resolvedModule?.resolvedFileName;
@@ -12645,21 +13186,21 @@ var analyzeProperties = (filesToAnalyze, program2, checker) => {
12645
13186
  if (analyzedFiles.has(filePath))
12646
13187
  return;
12647
13188
  analyzedFiles.add(filePath);
12648
- const source = program2.getSourceFile(filePath);
12649
- if (!source)
13189
+ const source2 = program2.getSourceFile(filePath);
13190
+ if (!source2)
12650
13191
  return;
12651
13192
  if (!sourceLineCache.has(filePath)) {
12652
- sourceLineCache.set(filePath, source.getFullText().split(`
13193
+ sourceLineCache.set(filePath, source2.getFullText().split(`
12653
13194
  `));
12654
13195
  }
12655
13196
  const sourceLines = sourceLineCache.get(filePath);
12656
13197
  function visit(node) {
12657
- if (!source)
13198
+ if (!source2)
12658
13199
  return;
12659
13200
  if (ts7.isPropertyAccessExpression(node)) {
12660
13201
  const left = node.expression;
12661
13202
  const right = node.name;
12662
- const { line } = ts7.getLineAndCharacterOfPosition(source, node.getStart());
13203
+ const { line } = ts7.getLineAndCharacterOfPosition(source2, node.getStart());
12663
13204
  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},`))) {
12664
13205
  const symbol = getCachedSymbol(right);
12665
13206
  if (symbol?.declarations && symbol.declarations.length > 0) {
@@ -12710,7 +13251,7 @@ var analyzeProperties = (filesToAnalyze, program2, checker) => {
12710
13251
  }
12711
13252
  ts7.forEachChild(node, visit);
12712
13253
  }
12713
- visit(source);
13254
+ visit(source2);
12714
13255
  }
12715
13256
  for (const filePath of filesToAnalyze) {
12716
13257
  analyzeFileProperties(filePath);