@akanjs/cli 2.3.9-rc.4 → 2.3.9-rc.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -3394,28 +3394,28 @@ class SysExecutor extends Executor {
3394
3394
  return scalarModules;
3395
3395
  }
3396
3396
  async getViewComponents() {
3397
- 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());
3397
+ 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());
3398
3398
  return viewComponents;
3399
3399
  }
3400
3400
  async getUnitComponents() {
3401
- 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());
3401
+ 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());
3402
3402
  return unitComponents;
3403
3403
  }
3404
3404
  async getTemplateComponents() {
3405
- 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());
3405
+ 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());
3406
3406
  return templateComponents;
3407
3407
  }
3408
3408
  async getViewsSourceCode() {
3409
3409
  const viewComponents = await this.getViewComponents();
3410
- return Promise.all(viewComponents.map((viewComponent) => this.getLocalFile(`lib/${viewComponent}/${viewComponent}.View.tsx`)));
3410
+ return Promise.all(viewComponents.map((viewComponent) => this.getLocalFile(`lib/${viewComponent}/${capitalize(viewComponent)}.View.tsx`)));
3411
3411
  }
3412
3412
  async getUnitsSourceCode() {
3413
3413
  const unitComponents = await this.getUnitComponents();
3414
- return Promise.all(unitComponents.map((unitComponent) => this.getLocalFile(`lib/${unitComponent}/${unitComponent}.Unit.tsx`)));
3414
+ return Promise.all(unitComponents.map((unitComponent) => this.getLocalFile(`lib/${unitComponent}/${capitalize(unitComponent)}.Unit.tsx`)));
3415
3415
  }
3416
3416
  async getTemplatesSourceCode() {
3417
3417
  const templateComponents = await this.getTemplateComponents();
3418
- return Promise.all(templateComponents.map((templateComponent) => this.getLocalFile(`lib/${templateComponent}/${templateComponent}.Template.tsx`)));
3418
+ return Promise.all(templateComponents.map((templateComponent) => this.getLocalFile(`lib/${templateComponent}/${capitalize(templateComponent)}.Template.tsx`)));
3419
3419
  }
3420
3420
  async getScalarConstantFiles() {
3421
3421
  const scalarModules = await this.getScalarModules();
@@ -4805,11 +4805,17 @@ var createWorkflowApplyReport = ({
4805
4805
  recommendedValidationCommands,
4806
4806
  commands = [],
4807
4807
  diagnostics = [],
4808
+ postApplyChecks = [],
4808
4809
  recommendations = [],
4809
4810
  nextActions = [],
4810
4811
  plan
4811
4812
  }) => {
4812
4813
  const validationCommands = recommendedValidationCommands ?? commands;
4814
+ const orderedNextActions = uniqueBy(nextActions, (action) => action.command).sort((left, right) => {
4815
+ const leftRepair = left.command.startsWith("akan workflow explain") ? 0 : 1;
4816
+ const rightRepair = right.command.startsWith("akan workflow explain") ? 0 : 1;
4817
+ return leftRepair - rightRepair;
4818
+ });
4813
4819
  return {
4814
4820
  schemaVersion: 1,
4815
4821
  workflow,
@@ -4821,8 +4827,9 @@ var createWorkflowApplyReport = ({
4821
4827
  recommendedValidationCommands: uniqueBy(validationCommands, (command) => command.command),
4822
4828
  commands: uniqueBy(validationCommands, (command) => command.command),
4823
4829
  diagnostics,
4830
+ postApplyChecks,
4824
4831
  recommendations: uniqueBy(recommendations, (recommendation) => `${recommendation.kind}:${recommendation.code}`),
4825
- nextActions: uniqueBy(nextActions, (action) => action.command),
4832
+ nextActions: orderedNextActions.slice(0, 3),
4826
4833
  plan
4827
4834
  };
4828
4835
  };
@@ -4887,6 +4894,12 @@ var statusForScope = (commands, diagnostics, scopes, expectedScope) => {
4887
4894
  return "unknown";
4888
4895
  return hasScopeFailure(scopes, expectedScope, diagnostics) ? "failed" : "passed";
4889
4896
  };
4897
+ var statusForValidationKind = (commands, kind) => {
4898
+ const matching = commands.filter((command) => command.kind === kind);
4899
+ if (matching.length === 0)
4900
+ return "unknown";
4901
+ return matching.some((command) => command.status === "failed") ? "failed" : "passed";
4902
+ };
4890
4903
  var createKnownBlockers = (commands, diagnostics) => {
4891
4904
  const commandBlockers = commands.filter((command) => command.status === "failed" && (command.failureScope === "workspace-config" || command.failureScope === "environment")).map((command) => ({
4892
4905
  code: `workflow-validation-${command.failureScope}`,
@@ -4920,7 +4933,17 @@ var createValidationStatuses = (commands, diagnostics) => {
4920
4933
  const sourceStatus = statusForScope(commands, diagnostics, scopes, "source-change");
4921
4934
  const workspaceStatus = hasScopeFailure(scopes, "workspace-config", diagnostics) || hasScopeFailure(scopes, "environment", diagnostics) ? "failed" : commands.length || diagnostics.length ? "passed" : "unknown";
4922
4935
  const overallStatus = hasScopeFailure(scopes, "source-change", diagnostics) ? "failed" : hasScopeFailure(scopes, "workspace-config", diagnostics) ? "blocked-by-workspace-config" : hasScopeFailure(scopes, "environment", diagnostics) ? "blocked-by-environment" : workflowStatus(diagnostics) === "failed" || commandStatus(commands) === "failed" ? "failed" : "passed";
4923
- return { sourceStatus, workspaceStatus, overallStatus };
4936
+ return {
4937
+ sourceStatus,
4938
+ workspaceStatus,
4939
+ overallStatus,
4940
+ summary: {
4941
+ sourceChange: sourceStatus,
4942
+ generatedSync: statusForValidationKind(commands, "sync"),
4943
+ workspaceConfig: statusForScope(commands, diagnostics, scopes, "workspace-config"),
4944
+ environment: statusForScope(commands, diagnostics, scopes, "environment")
4945
+ }
4946
+ };
4924
4947
  };
4925
4948
  var createWorkflowValidationRunReport = async ({
4926
4949
  runId = createWorkflowRunId("validation"),
@@ -5030,221 +5053,7 @@ var createRepairReport = ({
5030
5053
  });
5031
5054
  // pkgs/@akanjs/devkit/workflow/executor.ts
5032
5055
  import { capitalize as capitalize2 } from "akanjs/common";
5033
- var workflowStepKey = (workflow, stepId) => `${workflow}:${stepId}`;
5034
- var primitiveReportToWorkflowStepResult = (report) => ({
5035
- changedFiles: report.changedFiles,
5036
- generatedFiles: report.generatedFiles,
5037
- commands: report.validationCommands,
5038
- diagnostics: report.diagnostics,
5039
- recommendations: [],
5040
- nextActions: report.nextActions
5041
- });
5042
- var workflowStringInput = (value) => typeof value === "string" ? value : null;
5043
- var workflowStringListInput = (value) => Array.isArray(value) ? value.join(",") : null;
5044
- var resolveWorkflowSys = async (workspace, target) => {
5045
- if (!target)
5046
- return null;
5047
- const [apps, libs] = await workspace.getSyss();
5048
- if (apps.includes(target))
5049
- return AppExecutor.from(workspace, target);
5050
- if (libs.includes(target))
5051
- return LibExecutor.from(workspace, target);
5052
- return null;
5053
- };
5054
- var targetMissing = (input2 = "app") => ({
5055
- severity: "error",
5056
- code: "workflow-target-missing",
5057
- input: input2,
5058
- message: "Workflow target app or library was not found."
5059
- });
5060
- var inputMissing = (input2) => ({
5061
- severity: "error",
5062
- code: "workflow-input-missing",
5063
- input: input2,
5064
- message: `Workflow input "${input2}" is required for apply.`
5065
- });
5066
- var unsupportedInput = (input2, message) => ({
5067
- severity: "error",
5068
- code: "workflow-input-unsupported",
5069
- input: input2,
5070
- message
5071
- });
5072
- var addFieldUiSurfaceInspection = (plan) => {
5073
- const app = workflowStringInput(plan.inputs.app);
5074
- const module = workflowStringInput(plan.inputs.module) ?? "<module>";
5075
- const field = workflowStringInput(plan.inputs.field) ?? "<field>";
5076
- const typeName = workflowStringInput(plan.inputs.type);
5077
- const isNumeric = typeName === "Int" || typeName === "Float" || typeName === "integer" || typeName === "float";
5078
- const moduleClassName = capitalize2(module);
5079
- const target = `${app ? `apps/${app}` : "*"}/lib/${module}/${moduleClassName}.Template.tsx`;
5080
- return {
5081
- recommendations: [
5082
- {
5083
- code: "add-field-ui-surface-review",
5084
- kind: "manual-action",
5085
- target,
5086
- action: isNumeric ? `Review ${moduleClassName} Template/Unit/View/Store surfaces before adding ${field}; confirm the local Field.Text numeric parser/formatter, validation rule, and dictionary label pattern.` : `Add ${field} to ${moduleClassName} UI surfaces only when the local Field.* pattern is clear.`,
5087
- confidence: "medium",
5088
- message: isNumeric ? `No UI file was modified automatically because a safe numeric input component pattern is not yet detected for ${module}.${field}.` : `Review Template/Unit/View/Store surfaces for ${module}.${field}; no UI file was modified automatically.`
5089
- }
5090
- ],
5091
- nextActions: [
5092
- {
5093
- command: `akan workflow explain ${plan.workflow}`,
5094
- reason: "Review UI surface guidance before manually editing ambiguous UI files."
5095
- }
5096
- ]
5097
- };
5098
- };
5099
- var createWorkflowStepRegistry = ({
5100
- workspace,
5101
- createModule,
5102
- createScalar,
5103
- createUi,
5104
- addField,
5105
- addEnumField
5106
- }) => {
5107
- const inspect = async () => {
5108
- return;
5109
- };
5110
- const commandOnly = async () => {
5111
- return;
5112
- };
5113
- return {
5114
- inspectSystem: inspect,
5115
- inspectModule: inspect,
5116
- syncTarget: commandOnly,
5117
- lintTarget: commandOnly,
5118
- [workflowStepKey("create-module", "create-module")]: async (_step, plan) => {
5119
- const app = workflowStringInput(plan.inputs.app);
5120
- const module = workflowStringInput(plan.inputs.module);
5121
- const sys2 = await resolveWorkflowSys(workspace, app);
5122
- if (!sys2 || !module)
5123
- return { diagnostics: [!sys2 ? targetMissing() : inputMissing("module")] };
5124
- return primitiveReportToWorkflowStepResult(await createModule(sys2, module));
5125
- },
5126
- [workflowStepKey("create-scalar", "create-scalar")]: async (_step, plan) => {
5127
- const app = workflowStringInput(plan.inputs.app);
5128
- const scalar = workflowStringInput(plan.inputs.scalar);
5129
- const sys2 = await resolveWorkflowSys(workspace, app);
5130
- if (!sys2 || !scalar)
5131
- return { diagnostics: [!sys2 ? targetMissing() : inputMissing("scalar")] };
5132
- return primitiveReportToWorkflowStepResult(await createScalar(sys2, scalar));
5133
- },
5134
- [workflowStepKey("create-ui", "create-ui")]: async (_step, plan) => {
5135
- const surface = workflowStringInput(plan.inputs.surface);
5136
- if (surface !== "view" && surface !== "unit" && surface !== "template") {
5137
- return {
5138
- diagnostics: [
5139
- unsupportedInput("surface", "Workflow apply currently supports create-ui surfaces: view, unit, template.")
5140
- ]
5141
- };
5142
- }
5143
- return primitiveReportToWorkflowStepResult(await createUi({
5144
- app: workflowStringInput(plan.inputs.app),
5145
- module: workflowStringInput(plan.inputs.module),
5146
- surface
5147
- }));
5148
- },
5149
- [workflowStepKey("add-field", "update-constant")]: async (_step, plan) => {
5150
- if (workflowStringInput(plan.inputs.type)?.toLowerCase() === "enum") {
5151
- return primitiveReportToWorkflowStepResult(await addEnumField({
5152
- app: workflowStringInput(plan.inputs.app),
5153
- module: workflowStringInput(plan.inputs.module),
5154
- field: workflowStringInput(plan.inputs.field),
5155
- values: workflowStringListInput(plan.inputs.values),
5156
- defaultValue: workflowStringInput(plan.inputs.default)
5157
- }));
5158
- }
5159
- return primitiveReportToWorkflowStepResult(await addField({
5160
- app: workflowStringInput(plan.inputs.app),
5161
- module: workflowStringInput(plan.inputs.module),
5162
- field: workflowStringInput(plan.inputs.field),
5163
- type: workflowStringInput(plan.inputs.type),
5164
- defaultValue: workflowStringInput(plan.inputs.default)
5165
- }));
5166
- },
5167
- [workflowStepKey("add-field", "update-dictionary")]: inspect,
5168
- [workflowStepKey("add-field", "update-ui-surfaces")]: async (_step, plan) => addFieldUiSurfaceInspection(plan),
5169
- [workflowStepKey("add-enum-field", "update-constant")]: async (_step, plan) => primitiveReportToWorkflowStepResult(await addEnumField({
5170
- app: workflowStringInput(plan.inputs.app),
5171
- module: workflowStringInput(plan.inputs.module),
5172
- field: workflowStringInput(plan.inputs.field),
5173
- values: workflowStringListInput(plan.inputs.values),
5174
- defaultValue: workflowStringInput(plan.inputs.default)
5175
- })),
5176
- [workflowStepKey("add-enum-field", "update-dictionary")]: inspect,
5177
- [workflowStepKey("add-enum-field", "update-option")]: inspect
5178
- };
5179
- };
5180
- class WorkflowExecutor {
5181
- registry;
5182
- constructor(registry) {
5183
- this.registry = registry;
5184
- }
5185
- async apply(plan) {
5186
- const changedFiles = [];
5187
- const generatedFiles = [];
5188
- const recommendedValidationCommands = [];
5189
- const diagnostics = [...plan.diagnostics];
5190
- const recommendations = [...plan.recommendations];
5191
- const nextActions = [];
5192
- if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
5193
- return createWorkflowApplyReport({
5194
- workflow: plan.workflow,
5195
- mode: "apply",
5196
- changedFiles,
5197
- generatedFiles,
5198
- recommendedValidationCommands,
5199
- diagnostics,
5200
- recommendations,
5201
- nextActions,
5202
- plan
5203
- });
5204
- }
5205
- recommendedValidationCommands.push(...workflowCommandsForPlan(plan));
5206
- nextActions.push(...workflowCommandsForPlan(plan));
5207
- for (const step of plan.steps) {
5208
- const runner = this.registry[workflowStepKey(plan.workflow, step.id)] ?? this.registry[step.tool] ?? this.registry[step.id];
5209
- if (!runner) {
5210
- diagnostics.push({
5211
- severity: "error",
5212
- code: "workflow-step-unsupported",
5213
- message: `Workflow ${plan.workflow} step "${step.id}" is not supported by workflow apply yet.`
5214
- });
5215
- nextActions.push({
5216
- command: `akan workflow explain ${plan.workflow}`,
5217
- reason: "Review the unsupported workflow step before retrying apply."
5218
- });
5219
- break;
5220
- }
5221
- const result = await runner(step, plan);
5222
- if (!result)
5223
- continue;
5224
- changedFiles.push(...result.changedFiles ?? []);
5225
- generatedFiles.push(...result.generatedFiles ?? []);
5226
- recommendedValidationCommands.push(...result.commands ?? []);
5227
- diagnostics.push(...result.diagnostics ?? []);
5228
- recommendations.push(...result.recommendations ?? []);
5229
- nextActions.push(...result.nextActions ?? []);
5230
- if (diagnostics.some((diagnostic) => diagnostic.severity === "error"))
5231
- break;
5232
- }
5233
- return createWorkflowApplyReport({
5234
- workflow: plan.workflow,
5235
- mode: "apply",
5236
- changedFiles,
5237
- generatedFiles,
5238
- recommendedValidationCommands,
5239
- diagnostics,
5240
- recommendations,
5241
- nextActions,
5242
- plan
5243
- });
5244
- }
5245
- }
5246
- // pkgs/@akanjs/devkit/workflow/plan.ts
5247
- import { capitalize as capitalize3 } from "akanjs/common";
5056
+ import ts4 from "typescript";
5248
5057
 
5249
5058
  // pkgs/@akanjs/devkit/workflow/primitive.ts
5250
5059
  var createPrimitiveWriteReport = ({
@@ -5296,6 +5105,23 @@ var sourceFile = (sys2, path10, action, reason) => ({
5296
5105
  action,
5297
5106
  reason
5298
5107
  });
5108
+ var moduleComponentName = (moduleName) => `${moduleName.slice(0, 1).toUpperCase()}${moduleName.slice(1)}`;
5109
+ var moduleSourcePaths = (moduleName) => {
5110
+ const componentName = moduleComponentName(moduleName);
5111
+ return {
5112
+ abstract: `lib/${moduleName}/${moduleName}.abstract.md`,
5113
+ constant: `lib/${moduleName}/${moduleName}.constant.ts`,
5114
+ dictionary: `lib/${moduleName}/${moduleName}.dictionary.ts`,
5115
+ service: `lib/${moduleName}/${moduleName}.service.ts`,
5116
+ signal: `lib/${moduleName}/${moduleName}.signal.ts`,
5117
+ store: `lib/${moduleName}/${moduleName}.store.ts`,
5118
+ template: `lib/${moduleName}/${componentName}.Template.tsx`,
5119
+ unit: `lib/${moduleName}/${componentName}.Unit.tsx`,
5120
+ util: `lib/${moduleName}/${componentName}.Util.tsx`,
5121
+ view: `lib/${moduleName}/${componentName}.View.tsx`,
5122
+ zone: `lib/${moduleName}/${componentName}.Zone.tsx`
5123
+ };
5124
+ };
5299
5125
  var generatedFilesForSync = (sys2, reason = "Generated files may change after sync.") => generatedFilePathsForTarget(getSysRoot(sys2), reason);
5300
5126
  var validationCommandsForTarget = (target) => [
5301
5127
  { command: `akan sync ${target}`, reason: "Refresh generated Akan files from source conventions." },
@@ -5322,6 +5148,50 @@ var createPassedPrimitiveReport = ({
5322
5148
  var scalarChangedFiles = (sys2, scalarName, files) => Object.values(files).map((file) => sourceFile(sys2, `lib/__scalar/${scalarName}/${file.filename}`, "create", "Scalar source file was created."));
5323
5149
  var titleize = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
5324
5150
  var lowerlize = (value) => `${value.slice(0, 1).toLowerCase()}${value.slice(1)}`;
5151
+ var koLabels = {
5152
+ amount: "\uAE08\uC561",
5153
+ budget: "\uC608\uC0B0",
5154
+ category: "\uCE74\uD14C\uACE0\uB9AC",
5155
+ content: "\uB0B4\uC6A9",
5156
+ count: "\uAC1C\uC218",
5157
+ createdAt: "\uC0DD\uC131\uC77C",
5158
+ date: "\uB0A0\uC9DC",
5159
+ description: "\uC124\uBA85",
5160
+ due: "\uB9C8\uAC10\uC77C",
5161
+ dueAt: "\uB9C8\uAC10\uC77C",
5162
+ email: "\uC774\uBA54\uC77C",
5163
+ enabled: "\uD65C\uC131\uD654",
5164
+ endAt: "\uC885\uB8CC\uC77C",
5165
+ id: "ID",
5166
+ name: "\uC774\uB984",
5167
+ owner: "\uB2F4\uB2F9\uC790",
5168
+ priority: "\uC6B0\uC120\uC21C\uC704",
5169
+ project: "\uD504\uB85C\uC81D\uD2B8",
5170
+ rating: "\uD3C9\uC810",
5171
+ startAt: "\uC2DC\uC791\uC77C",
5172
+ status: "\uC0C1\uD0DC",
5173
+ title: "\uC81C\uBAA9",
5174
+ updatedAt: "\uC218\uC815\uC77C"
5175
+ };
5176
+ var splitFieldWords = (fieldName) => fieldName.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").split(/\s+/).map((word) => word.trim()).filter(Boolean);
5177
+ var koLabelForField = (fieldName) => {
5178
+ if (koLabels[fieldName])
5179
+ return koLabels[fieldName];
5180
+ const words = splitFieldWords(fieldName);
5181
+ const translated = words.map((word) => koLabels[word] ?? koLabels[lowerlize(word)] ?? null);
5182
+ return translated.every(Boolean) ? translated.join(" ") : null;
5183
+ };
5184
+ var bilingualLabelForField = (fieldName) => {
5185
+ const en = titleize(fieldName);
5186
+ return { en, ko: koLabelForField(fieldName) ?? en };
5187
+ };
5188
+ var bilingualDescriptionForField = (fieldName) => {
5189
+ const label = bilingualLabelForField(fieldName);
5190
+ return {
5191
+ en: `Enter ${label.en.toLowerCase()}.`,
5192
+ ko: `${label.ko} \uAC12\uC744 \uC785\uB825\uD569\uB2C8\uB2E4.`
5193
+ };
5194
+ };
5325
5195
  var normalizeFieldType = (typeName) => {
5326
5196
  const normalizedTypes = {
5327
5197
  string: "String",
@@ -5349,6 +5219,15 @@ var ensureBaseTypeImport = (content, typeName) => {
5349
5219
  ${content}`;
5350
5220
  };
5351
5221
  var numericDefault = (typeName, rawDefault) => {
5222
+ if (typeof rawDefault === "number") {
5223
+ if (!Number.isFinite(rawDefault))
5224
+ return null;
5225
+ if (typeName === "Int" && !Number.isInteger(rawDefault))
5226
+ return null;
5227
+ return String(rawDefault);
5228
+ }
5229
+ if (typeof rawDefault !== "string")
5230
+ return null;
5352
5231
  const trimmed = rawDefault.trim();
5353
5232
  if (!trimmed || !Number.isFinite(Number(trimmed)))
5354
5233
  return null;
@@ -5356,16 +5235,41 @@ var numericDefault = (typeName, rawDefault) => {
5356
5235
  return null;
5357
5236
  return trimmed;
5358
5237
  };
5359
- var coerceFieldDefault = (typeName, defaultValue) => {
5360
- if (defaultValue === undefined || defaultValue === null || defaultValue === "")
5361
- return { expression: null };
5362
- const normalizedType = normalizeFieldType(typeName);
5238
+ var booleanDefault = (rawDefault) => {
5239
+ if (typeof rawDefault === "boolean")
5240
+ return String(rawDefault);
5241
+ if (typeof rawDefault !== "string")
5242
+ return null;
5243
+ const lowered = rawDefault.trim().toLowerCase();
5244
+ return lowered === "true" || lowered === "false" ? lowered : null;
5245
+ };
5246
+ var dateDefault = (rawDefault) => {
5247
+ if (typeof rawDefault === "number" && Number.isFinite(rawDefault))
5248
+ return `new Date(${rawDefault})`;
5249
+ if (typeof rawDefault !== "string")
5250
+ return null;
5251
+ const trimmed = rawDefault.trim();
5252
+ if (!trimmed)
5253
+ return null;
5254
+ if (trimmed === "now")
5255
+ return "new Date()";
5256
+ if (!Number.isNaN(Date.parse(trimmed)))
5257
+ return `new Date(${JSON.stringify(trimmed)})`;
5258
+ return null;
5259
+ };
5260
+ var coerceFieldDefault = (typeName, defaultValue, options = {}) => {
5261
+ const normalizedType = typeName.toLowerCase() === "enum" ? "enum" : normalizeFieldType(typeName);
5262
+ if (defaultValue === undefined || defaultValue === null || defaultValue === "") {
5263
+ return { expression: null, normalized: false, normalizedType };
5264
+ }
5363
5265
  if (normalizedType === "Int" || normalizedType === "Float") {
5364
5266
  const expression = numericDefault(normalizedType, defaultValue);
5365
5267
  if (expression !== null)
5366
- return { expression };
5268
+ return { expression, normalized: typeof defaultValue === "string", normalizedType };
5367
5269
  return {
5368
5270
  expression: null,
5271
+ normalized: false,
5272
+ normalizedType,
5369
5273
  diagnostic: {
5370
5274
  severity: "error",
5371
5275
  code: "primitive-default-value-invalid",
@@ -5376,11 +5280,13 @@ var coerceFieldDefault = (typeName, defaultValue) => {
5376
5280
  };
5377
5281
  }
5378
5282
  if (normalizedType === "Boolean") {
5379
- const lowered = defaultValue.trim().toLowerCase();
5380
- if (lowered === "true" || lowered === "false")
5381
- return { expression: lowered };
5283
+ const expression = booleanDefault(defaultValue);
5284
+ if (expression !== null)
5285
+ return { expression, normalized: typeof defaultValue === "string", normalizedType };
5382
5286
  return {
5383
5287
  expression: null,
5288
+ normalized: false,
5289
+ normalizedType,
5384
5290
  diagnostic: {
5385
5291
  severity: "error",
5386
5292
  code: "primitive-default-value-invalid",
@@ -5390,13 +5296,58 @@ var coerceFieldDefault = (typeName, defaultValue) => {
5390
5296
  }
5391
5297
  };
5392
5298
  }
5393
- return { expression: JSON.stringify(defaultValue) };
5299
+ if (normalizedType === "Date") {
5300
+ const expression = dateDefault(defaultValue);
5301
+ if (expression !== null)
5302
+ return { expression, normalized: true, normalizedType };
5303
+ return {
5304
+ expression: null,
5305
+ normalized: false,
5306
+ normalizedType,
5307
+ diagnostic: {
5308
+ severity: "error",
5309
+ code: "primitive-default-value-invalid",
5310
+ input: "default",
5311
+ failureScope: "source-change",
5312
+ message: `Default value for Date must be "now", a timestamp, or a parseable date string. Received: ${JSON.stringify(defaultValue)}.`
5313
+ }
5314
+ };
5315
+ }
5316
+ if (normalizedType === "enum") {
5317
+ if (typeof defaultValue === "string" && options.enumValues?.includes(defaultValue)) {
5318
+ return { expression: JSON.stringify(defaultValue), normalized: false, normalizedType };
5319
+ }
5320
+ return {
5321
+ expression: null,
5322
+ normalized: false,
5323
+ normalizedType,
5324
+ diagnostic: {
5325
+ severity: "error",
5326
+ code: "primitive-default-value-invalid",
5327
+ input: "default",
5328
+ failureScope: "source-change",
5329
+ message: `Default value for enum must be one of: ${(options.enumValues ?? []).join(", ")}. Received: ${JSON.stringify(defaultValue)}.`
5330
+ }
5331
+ };
5332
+ }
5333
+ return {
5334
+ expression: JSON.stringify(String(defaultValue)),
5335
+ normalized: typeof defaultValue !== "string",
5336
+ normalizedType
5337
+ };
5394
5338
  };
5395
- var fieldExpression = (typeName, defaultValue) => {
5339
+ var fieldExpression = (typeName, defaultValue, options = {}) => {
5396
5340
  const typeExpression = normalizeFieldType(typeName);
5397
- const defaultExpression = coerceFieldDefault(typeExpression, defaultValue).expression;
5341
+ const defaultExpression = coerceFieldDefault(options.enumValues ? "enum" : typeExpression, defaultValue, options).expression;
5398
5342
  const defaultOption = defaultExpression ? `, { default: ${defaultExpression} }` : "";
5399
- return `field(${typeExpression}${defaultOption})`;
5343
+ return `${options.builderName ?? "field"}(${typeExpression}${defaultOption})`;
5344
+ };
5345
+ var viaBuilderParameterName = (content, className) => {
5346
+ const classIndex = content.indexOf(`export class ${className} extends via`);
5347
+ if (classIndex < 0)
5348
+ return null;
5349
+ const signature = content.slice(classIndex, content.indexOf("=>", classIndex));
5350
+ return /via\(\(\s*([A-Za-z_$][\w$]*)\s*\)/.exec(signature)?.[1] ?? null;
5400
5351
  };
5401
5352
  var insertIntoObject = (content, className, line) => {
5402
5353
  const classIndex = content.indexOf(`export class ${className} extends via`);
@@ -5414,6 +5365,50 @@ var insertIntoObject = (content, className, line) => {
5414
5365
  `;
5415
5366
  return `${prefix}${insertion}${suffix}`;
5416
5367
  };
5368
+ var insertLightProjectionField = (content, moduleClassName, fieldName) => {
5369
+ const classIndex = content.indexOf(`export class Light${moduleClassName} extends via`);
5370
+ if (classIndex < 0)
5371
+ return null;
5372
+ const arrayMatch = /\[([\s\S]*?)\]\s+as const/.exec(content.slice(classIndex));
5373
+ if (!arrayMatch || arrayMatch.index === undefined)
5374
+ return null;
5375
+ const arrayStart = classIndex + arrayMatch.index;
5376
+ const arrayEnd = arrayStart + arrayMatch[0].length;
5377
+ const fields = [...arrayMatch[1].matchAll(/"([^"]+)"/g)].map((match) => match[1]).filter(Boolean);
5378
+ if (fields.includes(fieldName))
5379
+ return content;
5380
+ const nextFields = [...fields, fieldName];
5381
+ const nextArray = nextFields.length === 0 ? "[] as const" : `[
5382
+ ${nextFields.map((field) => ` ${JSON.stringify(field)},`).join(`
5383
+ `)}
5384
+ ] as const`;
5385
+ return `${content.slice(0, arrayStart)}${nextArray}${content.slice(arrayEnd)}`;
5386
+ };
5387
+ var insertTemplateField = ({
5388
+ content,
5389
+ moduleName,
5390
+ moduleClassName,
5391
+ fieldName,
5392
+ component
5393
+ }) => {
5394
+ if (content.includes(`l("${moduleName}.${fieldName}")`) || content.includes(`.${fieldName}`))
5395
+ return content;
5396
+ const layoutEndIndex = content.indexOf(" </Layout.Template>");
5397
+ if (layoutEndIndex < 0)
5398
+ return null;
5399
+ const formName = `${moduleName}Form`;
5400
+ if (!content.includes(`const ${formName} = st.use.${moduleName}Form();`))
5401
+ return null;
5402
+ const fieldSetter = `st.do.set${moduleComponentName(fieldName)}On${moduleClassName}`;
5403
+ const fieldBlock = ` <${component}
5404
+ label={l("${moduleName}.${fieldName}")}
5405
+ desc={l("${moduleName}.${fieldName}.desc")}
5406
+ value={${formName}.${fieldName}}
5407
+ onChange={${fieldSetter}}
5408
+ />
5409
+ `;
5410
+ return `${content.slice(0, layoutEndIndex)}${fieldBlock}${content.slice(layoutEndIndex)}`;
5411
+ };
5417
5412
  var ensureEnumImport = (content) => {
5418
5413
  if (content.includes("enumOf"))
5419
5414
  return content;
@@ -5440,48 +5435,468 @@ ${values.map((value) => ` ${JSON.stringify(value)},`).join(`
5440
5435
  ${enumClass}`;
5441
5436
  return `${content.slice(0, firstClassIndex)}${enumClass}${content.slice(firstClassIndex)}`;
5442
5437
  };
5438
+ var findMatchingBrace = (content, openIndex) => {
5439
+ let depth = 0;
5440
+ let quote = null;
5441
+ let escaped = false;
5442
+ for (let index = openIndex;index < content.length; index++) {
5443
+ const char = content[index];
5444
+ if (quote) {
5445
+ if (escaped) {
5446
+ escaped = false;
5447
+ continue;
5448
+ }
5449
+ if (char === "\\") {
5450
+ escaped = true;
5451
+ continue;
5452
+ }
5453
+ if (char === quote)
5454
+ quote = null;
5455
+ continue;
5456
+ }
5457
+ if (char === '"' || char === "'" || char === "`") {
5458
+ quote = char;
5459
+ continue;
5460
+ }
5461
+ if (char === "{")
5462
+ depth += 1;
5463
+ if (char === "}") {
5464
+ depth -= 1;
5465
+ if (depth === 0)
5466
+ return index;
5467
+ }
5468
+ }
5469
+ return -1;
5470
+ };
5471
+ var dictionaryModelFieldLine = (fieldName) => {
5472
+ const label = bilingualLabelForField(fieldName);
5473
+ const desc = bilingualDescriptionForField(fieldName);
5474
+ return `${fieldName}: t([${JSON.stringify(label.en)}, ${JSON.stringify(label.ko)}]).desc([${JSON.stringify(desc.en)}, ${JSON.stringify(desc.ko)}]),`;
5475
+ };
5443
5476
  var insertDictionaryModelField = (content, moduleClassName, fieldName) => {
5444
5477
  if (new RegExp(`\\b${fieldName}\\s*:`).test(content))
5445
5478
  return content;
5446
- const label = titleize(fieldName);
5447
- const modelIndex = content.indexOf(`.model<${moduleClassName}>((t) => ({`);
5479
+ const modelIndex = content.indexOf(`.model<${moduleClassName}>((t) => (`);
5448
5480
  if (modelIndex < 0)
5449
5481
  return null;
5450
- const objectEndIndex = content.indexOf(" }))", modelIndex);
5482
+ const objectStartIndex = content.indexOf("{", modelIndex);
5483
+ if (objectStartIndex < 0)
5484
+ return null;
5485
+ const objectEndIndex = findMatchingBrace(content, objectStartIndex);
5451
5486
  if (objectEndIndex < 0)
5452
5487
  return null;
5453
- return `${content.slice(0, objectEndIndex)} ${fieldName}: t([${JSON.stringify(label)}, ${JSON.stringify(label)}]).desc([${JSON.stringify(label)}, ${JSON.stringify(label)}]),
5454
- ${content.slice(objectEndIndex)}`;
5488
+ const fieldLine = dictionaryModelFieldLine(fieldName);
5489
+ const body = content.slice(objectStartIndex + 1, objectEndIndex);
5490
+ if (body.trim().length === 0) {
5491
+ return `${content.slice(0, objectStartIndex + 1)}
5492
+ ${fieldLine}
5493
+ ${content.slice(objectEndIndex)}`;
5494
+ }
5495
+ const insertion = body.endsWith(`
5496
+ `) ? ` ${fieldLine}
5497
+ ` : `
5498
+ ${fieldLine}
5499
+ `;
5500
+ return `${content.slice(0, objectEndIndex)}${insertion}${content.slice(objectEndIndex)}`;
5501
+ };
5502
+ var ensureConstantTypeImport = (content, constantPath, typeName) => {
5503
+ if (new RegExp(`import type \\{[^}]*\\b${typeName}\\b[^}]*\\} from "${constantPath}";`).test(content))
5504
+ return content;
5505
+ const importPattern = new RegExp(`import type \\{ ([^}]+) \\} from "${constantPath}";`);
5506
+ const existingImport = content.match(importPattern);
5507
+ if (existingImport !== null) {
5508
+ const names = existingImport[1]?.split(",").map((name) => name.trim()) ?? [];
5509
+ return content.replace(existingImport[0], `import type { ${[...names, typeName].sort().join(", ")} } from "${constantPath}";`);
5510
+ }
5511
+ return `import type { ${typeName} } from "${constantPath}";
5512
+ ${content}`;
5513
+ };
5514
+ var insertDictionaryEnum = (content, enumClassName, enumName, values) => {
5515
+ if (content.includes(`.enum<${enumClassName}>("${enumName}"`))
5516
+ return content;
5517
+ const enumBlock = ` .enum<${enumClassName}>("${enumName}", (t) => ({
5518
+ ${values.map((value) => ` ${value}: t([${JSON.stringify(titleize(value))}, ${JSON.stringify(titleize(value))}]),`).join(`
5519
+ `)}
5520
+ }))
5521
+ `;
5522
+ const chainEndIndex = content.lastIndexOf(";");
5523
+ const insertBeforeIndex = [content.indexOf(".error("), content.indexOf(".translate("), chainEndIndex].filter((index) => index >= 0).sort((a, b) => a - b)[0];
5524
+ if (insertBeforeIndex === undefined)
5525
+ return null;
5526
+ return `${content.slice(0, insertBeforeIndex)}${enumBlock}${content.slice(insertBeforeIndex)}`;
5527
+ };
5528
+ var parseValues = (value) => value?.split(",").map((item) => item.trim()).filter(Boolean) ?? [];
5529
+
5530
+ // pkgs/@akanjs/devkit/workflow/uiPolicy.ts
5531
+ var addFieldUiPolicyForType = (typeName) => {
5532
+ const normalizedType = typeName.toLowerCase() === "enum" ? "enum" : normalizeFieldType(typeName);
5533
+ if (normalizedType === "Int" || normalizedType === "Float") {
5534
+ return {
5535
+ normalizedType,
5536
+ component: "Field.Number",
5537
+ confidence: "high",
5538
+ autoTemplateSupported: true
5539
+ };
5540
+ }
5541
+ if (normalizedType === "Date") {
5542
+ return {
5543
+ normalizedType,
5544
+ component: "Field.Date",
5545
+ confidence: "high",
5546
+ autoTemplateSupported: true
5547
+ };
5548
+ }
5549
+ if (normalizedType === "Boolean" || normalizedType === "enum") {
5550
+ return {
5551
+ normalizedType,
5552
+ component: "Field.ToggleSelect",
5553
+ confidence: "medium",
5554
+ autoTemplateSupported: false
5555
+ };
5556
+ }
5557
+ if (normalizedType === "String") {
5558
+ return {
5559
+ normalizedType,
5560
+ component: "Field.Text",
5561
+ confidence: "high",
5562
+ autoTemplateSupported: true
5563
+ };
5564
+ }
5565
+ return {
5566
+ normalizedType,
5567
+ component: "Field.Text",
5568
+ confidence: "low",
5569
+ autoTemplateSupported: false
5570
+ };
5571
+ };
5572
+
5573
+ // pkgs/@akanjs/devkit/workflow/executor.ts
5574
+ var workflowStepKey = (workflow, stepId) => `${workflow}:${stepId}`;
5575
+ var primitiveReportToWorkflowStepResult = (report) => ({
5576
+ changedFiles: report.changedFiles,
5577
+ generatedFiles: report.generatedFiles,
5578
+ commands: report.validationCommands,
5579
+ diagnostics: report.diagnostics,
5580
+ recommendations: [],
5581
+ nextActions: report.nextActions
5582
+ });
5583
+ var workflowStringInput = (value) => typeof value === "string" ? value : null;
5584
+ var workflowStringListInput = (value) => Array.isArray(value) ? value.join(",") : null;
5585
+ var workflowStringArrayInput = (value) => Array.isArray(value) ? value : null;
5586
+ var workflowBooleanInput = (value) => typeof value === "boolean" ? value : null;
5587
+ var postApplyDiagnostic = (code, message, target) => ({
5588
+ severity: "error",
5589
+ code,
5590
+ message,
5591
+ failureScope: "source-change",
5592
+ context: { target }
5593
+ });
5594
+ var sourceKindForPath = (filePath) => {
5595
+ if (filePath.endsWith(".tsx"))
5596
+ return ts4.ScriptKind.TSX;
5597
+ if (filePath.endsWith(".ts"))
5598
+ return ts4.ScriptKind.TS;
5599
+ return null;
5600
+ };
5601
+ var checkPathCasing = async (workspace, filePath) => {
5602
+ const segments = filePath.split("/").filter(Boolean);
5603
+ let current = ".";
5604
+ for (const segment of segments) {
5605
+ const entries = await workspace.readdir(current);
5606
+ if (entries.includes(segment)) {
5607
+ current = current === "." ? segment : `${current}/${segment}`;
5608
+ continue;
5609
+ }
5610
+ const caseInsensitiveMatch = entries.find((entry) => entry.toLowerCase() === segment.toLowerCase());
5611
+ return caseInsensitiveMatch ? {
5612
+ code: "workflow-path-casing-mismatch",
5613
+ message: `Reported path segment "${segment}" does not match actual casing "${caseInsensitiveMatch}" in ${filePath}.`
5614
+ } : {
5615
+ code: "workflow-path-missing",
5616
+ message: `Reported path does not exist: ${filePath}.`
5617
+ };
5618
+ }
5619
+ return null;
5620
+ };
5621
+ var checkTypeScriptSyntax = async (workspace, filePath) => {
5622
+ const scriptKind = sourceKindForPath(filePath);
5623
+ if (!scriptKind)
5624
+ return null;
5625
+ const content = await workspace.readFile(filePath);
5626
+ const source = ts4.createSourceFile(filePath, content, ts4.ScriptTarget.Latest, true, scriptKind);
5627
+ const diagnostic = source.parseDiagnostics[0];
5628
+ if (!diagnostic)
5629
+ return null;
5630
+ const position = diagnostic.file?.getLineAndCharacterOfPosition(diagnostic.start ?? 0);
5631
+ const location = position ? `:${position.line + 1}:${position.character + 1}` : "";
5632
+ return {
5633
+ code: "workflow-post-apply-syntax-error",
5634
+ message: `Generated source has a TypeScript syntax error at ${filePath}${location}: ${ts4.flattenDiagnosticMessageText(diagnostic.messageText, " ")}`
5635
+ };
5636
+ };
5637
+ var checkChangedFile = async (workspace, file) => {
5638
+ if (file.action === "remove")
5639
+ return { postApplyChecks: [] };
5640
+ const diagnostics = [];
5641
+ const checks = [];
5642
+ const pathIssue = await checkPathCasing(workspace, file.path);
5643
+ if (pathIssue) {
5644
+ diagnostics.push(postApplyDiagnostic(pathIssue.code, pathIssue.message, file.path));
5645
+ checks.push({ ...pathIssue, target: file.path, status: "failed" });
5646
+ return { diagnostics, postApplyChecks: checks };
5647
+ }
5648
+ const syntaxIssue = await checkTypeScriptSyntax(workspace, file.path);
5649
+ if (syntaxIssue) {
5650
+ diagnostics.push(postApplyDiagnostic(syntaxIssue.code, syntaxIssue.message, file.path));
5651
+ checks.push({ ...syntaxIssue, target: file.path, status: "failed" });
5652
+ return { diagnostics, postApplyChecks: checks };
5653
+ }
5654
+ checks.push({
5655
+ code: "workflow-post-apply-file-valid",
5656
+ target: file.path,
5657
+ status: "passed",
5658
+ message: "Changed file exists with exact casing and parses as source when applicable."
5659
+ });
5660
+ return { postApplyChecks: checks };
5661
+ };
5662
+ var checkRecommendationPath = async (workspace, recommendation) => {
5663
+ if (!recommendation.target || recommendation.target.includes("*") || recommendation.target.startsWith("<"))
5664
+ return null;
5665
+ const pathIssue = await checkPathCasing(workspace, recommendation.target);
5666
+ if (!pathIssue)
5667
+ return null;
5668
+ return {
5669
+ severity: "warning",
5670
+ code: "workflow-recommendation-path-unverified",
5671
+ message: `${pathIssue.message} Recommendation "${recommendation.code}" should not be treated as an exact file target until the path is corrected.`,
5672
+ failureScope: "source-change",
5673
+ context: { target: recommendation.target }
5674
+ };
5675
+ };
5676
+ var resolveWorkflowSys = async (workspace, target) => {
5677
+ if (!target)
5678
+ return null;
5679
+ const [apps, libs] = await workspace.getSyss();
5680
+ if (apps.includes(target))
5681
+ return AppExecutor.from(workspace, target);
5682
+ if (libs.includes(target))
5683
+ return LibExecutor.from(workspace, target);
5684
+ return null;
5685
+ };
5686
+ var targetMissing = (input2 = "app") => ({
5687
+ severity: "error",
5688
+ code: "workflow-target-missing",
5689
+ input: input2,
5690
+ message: "Workflow target app or library was not found."
5691
+ });
5692
+ var inputMissing = (input2) => ({
5693
+ severity: "error",
5694
+ code: "workflow-input-missing",
5695
+ input: input2,
5696
+ message: `Workflow input "${input2}" is required for apply.`
5697
+ });
5698
+ var unsupportedInput = (input2, message) => ({
5699
+ severity: "error",
5700
+ code: "workflow-input-unsupported",
5701
+ input: input2,
5702
+ message
5703
+ });
5704
+ var addFieldUiSurfaceInspection = (plan) => {
5705
+ const app = workflowStringInput(plan.inputs.app);
5706
+ const module = workflowStringInput(plan.inputs.module) ?? "<module>";
5707
+ const field = workflowStringInput(plan.inputs.field) ?? "<field>";
5708
+ const typeName = workflowStringInput(plan.inputs.type);
5709
+ const policy = addFieldUiPolicyForType(typeName ?? "String");
5710
+ const surfaces = workflowStringArrayInput(plan.inputs.surfaces);
5711
+ const templateRequested = surfaces?.includes("template") ?? false;
5712
+ const moduleClassName = capitalize2(module);
5713
+ const target = `${app ? `apps/${app}` : "*"}/${moduleSourcePaths(module).template}`;
5714
+ return {
5715
+ recommendations: [
5716
+ {
5717
+ code: "add-field-ui-surface-review",
5718
+ kind: "manual-action",
5719
+ target,
5720
+ 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.`,
5721
+ confidence: "medium",
5722
+ message: `Review UI surfaces for ${module}.${field}; recommended component is ${policy.component}, with manual reasons and candidate positions in action.`
5723
+ }
5724
+ ],
5725
+ nextActions: [
5726
+ {
5727
+ command: `akan workflow explain ${plan.workflow}`,
5728
+ reason: "Review UI surface guidance before manually editing ambiguous UI files."
5729
+ }
5730
+ ]
5731
+ };
5732
+ };
5733
+ var createWorkflowStepRegistry = ({
5734
+ workspace,
5735
+ createModule,
5736
+ createScalar,
5737
+ createUi,
5738
+ addField,
5739
+ addEnumField
5740
+ }) => {
5741
+ const inspect = async () => {
5742
+ return;
5743
+ };
5744
+ const commandOnly = async () => {
5745
+ return;
5746
+ };
5747
+ return {
5748
+ inspectSystem: inspect,
5749
+ inspectModule: inspect,
5750
+ syncTarget: commandOnly,
5751
+ lintTarget: commandOnly,
5752
+ [workflowStepKey("create-module", "create-module")]: async (_step, plan) => {
5753
+ const app = workflowStringInput(plan.inputs.app);
5754
+ const module = workflowStringInput(plan.inputs.module);
5755
+ const sys2 = await resolveWorkflowSys(workspace, app);
5756
+ if (!sys2 || !module)
5757
+ return { diagnostics: [!sys2 ? targetMissing() : inputMissing("module")] };
5758
+ return primitiveReportToWorkflowStepResult(await createModule(sys2, module));
5759
+ },
5760
+ [workflowStepKey("create-scalar", "create-scalar")]: async (_step, plan) => {
5761
+ const app = workflowStringInput(plan.inputs.app);
5762
+ const scalar = workflowStringInput(plan.inputs.scalar);
5763
+ const sys2 = await resolveWorkflowSys(workspace, app);
5764
+ if (!sys2 || !scalar)
5765
+ return { diagnostics: [!sys2 ? targetMissing() : inputMissing("scalar")] };
5766
+ return primitiveReportToWorkflowStepResult(await createScalar(sys2, scalar));
5767
+ },
5768
+ [workflowStepKey("create-ui", "create-ui")]: async (_step, plan) => {
5769
+ const surface = workflowStringInput(plan.inputs.surface);
5770
+ if (surface !== "view" && surface !== "unit" && surface !== "template") {
5771
+ return {
5772
+ diagnostics: [
5773
+ unsupportedInput("surface", "Workflow apply currently supports create-ui surfaces: view, unit, template.")
5774
+ ]
5775
+ };
5776
+ }
5777
+ return primitiveReportToWorkflowStepResult(await createUi({
5778
+ app: workflowStringInput(plan.inputs.app),
5779
+ module: workflowStringInput(plan.inputs.module),
5780
+ surface
5781
+ }));
5782
+ },
5783
+ [workflowStepKey("add-field", "update-constant")]: async (_step, plan) => {
5784
+ if (workflowStringInput(plan.inputs.type)?.toLowerCase() === "enum") {
5785
+ return primitiveReportToWorkflowStepResult(await addEnumField({
5786
+ app: workflowStringInput(plan.inputs.app),
5787
+ module: workflowStringInput(plan.inputs.module),
5788
+ field: workflowStringInput(plan.inputs.field),
5789
+ values: workflowStringListInput(plan.inputs.values),
5790
+ defaultValue: workflowStringInput(plan.inputs.default),
5791
+ surfaces: workflowStringArrayInput(plan.inputs.surfaces),
5792
+ includeInLight: workflowBooleanInput(plan.inputs.includeInLight)
5793
+ }));
5794
+ }
5795
+ return primitiveReportToWorkflowStepResult(await addField({
5796
+ app: workflowStringInput(plan.inputs.app),
5797
+ module: workflowStringInput(plan.inputs.module),
5798
+ field: workflowStringInput(plan.inputs.field),
5799
+ type: workflowStringInput(plan.inputs.type),
5800
+ defaultValue: workflowStringInput(plan.inputs.default),
5801
+ surfaces: workflowStringArrayInput(plan.inputs.surfaces),
5802
+ includeInLight: workflowBooleanInput(plan.inputs.includeInLight)
5803
+ }));
5804
+ },
5805
+ [workflowStepKey("add-field", "update-dictionary")]: inspect,
5806
+ [workflowStepKey("add-field", "update-ui-surfaces")]: async (_step, plan) => addFieldUiSurfaceInspection(plan),
5807
+ [workflowStepKey("add-enum-field", "update-constant")]: async (_step, plan) => primitiveReportToWorkflowStepResult(await addEnumField({
5808
+ app: workflowStringInput(plan.inputs.app),
5809
+ module: workflowStringInput(plan.inputs.module),
5810
+ field: workflowStringInput(plan.inputs.field),
5811
+ values: workflowStringListInput(plan.inputs.values),
5812
+ defaultValue: workflowStringInput(plan.inputs.default)
5813
+ })),
5814
+ [workflowStepKey("add-enum-field", "update-dictionary")]: inspect,
5815
+ [workflowStepKey("add-enum-field", "update-option")]: inspect
5816
+ };
5455
5817
  };
5456
- var ensureConstantTypeImport = (content, constantPath, typeName) => {
5457
- if (new RegExp(`import type \\{[^}]*\\b${typeName}\\b[^}]*\\} from "${constantPath}";`).test(content))
5458
- return content;
5459
- const importPattern = new RegExp(`import type \\{ ([^}]+) \\} from "${constantPath}";`);
5460
- const existingImport = content.match(importPattern);
5461
- if (existingImport !== null) {
5462
- const names = existingImport[1]?.split(",").map((name) => name.trim()) ?? [];
5463
- return content.replace(existingImport[0], `import type { ${[...names, typeName].sort().join(", ")} } from "${constantPath}";`);
5818
+ class WorkflowExecutor {
5819
+ registry;
5820
+ workspace;
5821
+ constructor(registry, workspace) {
5822
+ this.registry = registry;
5823
+ this.workspace = workspace;
5464
5824
  }
5465
- return `import type { ${typeName} } from "${constantPath}";
5466
- ${content}`;
5467
- };
5468
- var insertDictionaryEnum = (content, enumClassName, enumName, values) => {
5469
- if (content.includes(`.enum<${enumClassName}>("${enumName}"`))
5470
- return content;
5471
- const enumBlock = ` .enum<${enumClassName}>("${enumName}", (t) => ({
5472
- ${values.map((value) => ` ${value}: t([${JSON.stringify(titleize(value))}, ${JSON.stringify(titleize(value))}]),`).join(`
5473
- `)}
5474
- }))
5475
- `;
5476
- const chainEndIndex = content.lastIndexOf(";");
5477
- const insertBeforeIndex = [content.indexOf(".error("), content.indexOf(".translate("), chainEndIndex].filter((index) => index >= 0).sort((a, b) => a - b)[0];
5478
- if (insertBeforeIndex === undefined)
5479
- return null;
5480
- return `${content.slice(0, insertBeforeIndex)}${enumBlock}${content.slice(insertBeforeIndex)}`;
5481
- };
5482
- var parseValues = (value) => value?.split(",").map((item) => item.trim()).filter(Boolean) ?? [];
5483
-
5825
+ async apply(plan) {
5826
+ const changedFiles = [];
5827
+ const generatedFiles = [];
5828
+ const recommendedValidationCommands = [];
5829
+ const diagnostics = [...plan.diagnostics];
5830
+ const postApplyChecks = [];
5831
+ const recommendations = [...plan.recommendations];
5832
+ const nextActions = [];
5833
+ if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
5834
+ return createWorkflowApplyReport({
5835
+ workflow: plan.workflow,
5836
+ mode: "apply",
5837
+ changedFiles,
5838
+ generatedFiles,
5839
+ recommendedValidationCommands,
5840
+ diagnostics,
5841
+ postApplyChecks,
5842
+ recommendations,
5843
+ nextActions,
5844
+ plan
5845
+ });
5846
+ }
5847
+ recommendedValidationCommands.push(...workflowCommandsForPlan(plan));
5848
+ nextActions.push(...workflowCommandsForPlan(plan));
5849
+ for (const step of plan.steps) {
5850
+ const runner = this.registry[workflowStepKey(plan.workflow, step.id)] ?? this.registry[step.tool] ?? this.registry[step.id];
5851
+ if (!runner) {
5852
+ diagnostics.push({
5853
+ severity: "error",
5854
+ code: "workflow-step-unsupported",
5855
+ message: `Workflow ${plan.workflow} step "${step.id}" is not supported by workflow apply yet.`
5856
+ });
5857
+ nextActions.push({
5858
+ command: `akan workflow explain ${plan.workflow}`,
5859
+ reason: "Review the unsupported workflow step before retrying apply."
5860
+ });
5861
+ break;
5862
+ }
5863
+ const result = await runner(step, plan);
5864
+ if (!result)
5865
+ continue;
5866
+ changedFiles.push(...result.changedFiles ?? []);
5867
+ generatedFiles.push(...result.generatedFiles ?? []);
5868
+ recommendedValidationCommands.push(...result.commands ?? []);
5869
+ diagnostics.push(...result.diagnostics ?? []);
5870
+ recommendations.push(...result.recommendations ?? []);
5871
+ nextActions.push(...result.nextActions ?? []);
5872
+ if (diagnostics.some((diagnostic) => diagnostic.severity === "error"))
5873
+ break;
5874
+ }
5875
+ if (this.workspace && !diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
5876
+ for (const file of changedFiles) {
5877
+ const result = await checkChangedFile(this.workspace, file);
5878
+ postApplyChecks.push(...result.postApplyChecks ?? []);
5879
+ diagnostics.push(...result.diagnostics ?? []);
5880
+ }
5881
+ const recommendationDiagnostics = await Promise.all(recommendations.map((recommendation) => checkRecommendationPath(this.workspace, recommendation)));
5882
+ diagnostics.push(...recommendationDiagnostics.filter((diagnostic) => !!diagnostic));
5883
+ }
5884
+ return createWorkflowApplyReport({
5885
+ workflow: plan.workflow,
5886
+ mode: "apply",
5887
+ changedFiles,
5888
+ generatedFiles,
5889
+ recommendedValidationCommands,
5890
+ diagnostics,
5891
+ postApplyChecks,
5892
+ recommendations,
5893
+ nextActions,
5894
+ plan
5895
+ });
5896
+ }
5897
+ }
5484
5898
  // pkgs/@akanjs/devkit/workflow/plan.ts
5899
+ import { capitalize as capitalize3 } from "akanjs/common";
5485
5900
  var surfaceModes = new Set(["infer", "include", "skip"]);
5486
5901
  var parseStringList = (value) => {
5487
5902
  if (Array.isArray(value)) {
@@ -5499,6 +5914,18 @@ var normalizeInputValue = (name, spec, value) => {
5499
5914
  const values = parseStringList(value);
5500
5915
  return values && values.length > 0 ? values : null;
5501
5916
  }
5917
+ if (spec.type === "boolean") {
5918
+ if (typeof value === "boolean")
5919
+ return value;
5920
+ if (typeof value !== "string")
5921
+ return null;
5922
+ const lowered = value.trim().toLowerCase();
5923
+ if (lowered === "true")
5924
+ return true;
5925
+ if (lowered === "false")
5926
+ return false;
5927
+ return null;
5928
+ }
5502
5929
  if (typeof value === "string" && surfaceModes.has(value))
5503
5930
  return value;
5504
5931
  throw new Error(`Unsupported workflow input value for ${name}`);
@@ -5506,17 +5933,45 @@ var normalizeInputValue = (name, spec, value) => {
5506
5933
  var listWorkflowSpecs = (specs) => [...specs].sort((a, b) => a.name.localeCompare(b.name));
5507
5934
  var getWorkflowSpec = (specs, name) => specs.find((spec) => spec.name === name) ?? null;
5508
5935
  var compactWorkflowInputs = (inputs) => Object.fromEntries(Object.entries(inputs).filter(([, value]) => value !== null && value !== ""));
5509
- var addFieldComponentForType = (typeName) => {
5510
- const normalizedType = normalizeFieldType(typeName);
5511
- if (normalizedType === "Boolean")
5512
- return "Field.ToggleSelect";
5513
- if (normalizedType === "Date")
5514
- return "Field.Date";
5515
- if (normalizedType === "Int" || normalizedType === "Float")
5516
- return "manual numeric input review";
5517
- if (typeName.toLowerCase() === "enum")
5518
- return "Field.ToggleSelect";
5519
- return "Field.Text";
5936
+ var addFieldTargetRoot = (app) => app ? `apps/${app}` : "*";
5937
+ var addFieldPaths = (inputs) => {
5938
+ const app = typeof inputs.app === "string" ? inputs.app : null;
5939
+ const module = typeof inputs.module === "string" ? inputs.module : "<module>";
5940
+ const paths = moduleSourcePaths(module);
5941
+ const root = addFieldTargetRoot(app);
5942
+ return {
5943
+ constant: `${root}/${paths.constant}`,
5944
+ dictionary: `${root}/${paths.dictionary}`,
5945
+ template: `${root}/${paths.template}`,
5946
+ unit: `${root}/${paths.unit}`,
5947
+ view: `${root}/${paths.view}`
5948
+ };
5949
+ };
5950
+ var selectedAddFieldSurfaces = (inputs) => Array.isArray(inputs.surfaces) ? new Set(inputs.surfaces) : null;
5951
+ var addFieldDefaultCoercion = (inputs) => {
5952
+ const typeName = typeof inputs.type === "string" ? inputs.type : null;
5953
+ const defaultValue = typeof inputs.default === "string" ? inputs.default : null;
5954
+ if (!typeName || defaultValue === null)
5955
+ return null;
5956
+ if (typeName.toLowerCase() === "number" || typeName.toLowerCase() === "numeric")
5957
+ return null;
5958
+ const enumValues = Array.isArray(inputs.values) ? inputs.values : null;
5959
+ return coerceFieldDefault(typeName.toLowerCase() === "enum" ? "enum" : typeName, defaultValue, { enumValues });
5960
+ };
5961
+ var createAddFieldDefaultRecommendations = (inputs) => {
5962
+ const field = typeof inputs.field === "string" ? inputs.field : "<field>";
5963
+ const coercion = addFieldDefaultCoercion(inputs);
5964
+ if (!coercion?.normalized || !coercion.expression)
5965
+ return [];
5966
+ return [
5967
+ {
5968
+ code: "add-field-default-normalized",
5969
+ kind: "manual-action",
5970
+ confidence: "high",
5971
+ message: `Default for ${field} will be normalized to ${coercion.normalizedType} literal ${coercion.expression}.`,
5972
+ action: "Review the normalized default in the apply report if this value should stay unset."
5973
+ }
5974
+ ];
5520
5975
  };
5521
5976
  var createAddFieldRecommendations = (inputs) => {
5522
5977
  const app = typeof inputs.app === "string" ? inputs.app : "<app-or-lib>";
@@ -5525,16 +5980,18 @@ var createAddFieldRecommendations = (inputs) => {
5525
5980
  const typeName = typeof inputs.type === "string" ? inputs.type : null;
5526
5981
  if (!typeName)
5527
5982
  return [];
5528
- const normalizedType = typeName.toLowerCase() === "enum" ? "enum" : normalizeFieldType(typeName);
5529
- const constantPath = `*/lib/${module}/${module}.constant.ts`;
5530
- const dictionaryPath = `*/lib/${module}/${module}.dictionary.ts`;
5531
- const templatePath = `*/lib/${module}/${capitalize3(module)}.Template.tsx`;
5983
+ const policy = addFieldUiPolicyForType(typeName);
5984
+ const normalizedType = policy.normalizedType;
5985
+ const paths = addFieldPaths(inputs);
5986
+ const surfaces = selectedAddFieldSurfaces(inputs);
5987
+ const templateRequested = surfaces?.has("template") ?? false;
5988
+ const includeInLight = inputs.includeInLight === true;
5532
5989
  return [
5533
5990
  ...normalizedType === "Int" || normalizedType === "Float" ? [
5534
5991
  {
5535
5992
  code: "add-field-import",
5536
5993
  kind: "import",
5537
- target: constantPath,
5994
+ target: paths.constant,
5538
5995
  confidence: "high",
5539
5996
  message: `Import ${normalizedType} from "akanjs/base" before writing field(${normalizedType}).`
5540
5997
  }
@@ -5542,35 +5999,122 @@ var createAddFieldRecommendations = (inputs) => {
5542
5999
  {
5543
6000
  code: "add-field-placement-constant",
5544
6001
  kind: "placement",
5545
- target: constantPath,
6002
+ target: paths.constant,
5546
6003
  confidence: "high",
5547
6004
  message: `Insert ${field}: field(${normalizedType}) in ${capitalize3(module)}Input.`
5548
6005
  },
5549
6006
  {
5550
6007
  code: "add-field-placement-dictionary",
5551
6008
  kind: "placement",
5552
- target: dictionaryPath,
6009
+ target: paths.dictionary,
5553
6010
  confidence: "high",
5554
6011
  message: `Add dictionary labels for ${module}.${field}.`
5555
6012
  },
5556
6013
  {
5557
6014
  code: "add-field-component",
5558
6015
  kind: "ui-component",
5559
- target: templatePath,
5560
- confidence: normalizedType === "Int" || normalizedType === "Float" ? "low" : "high",
5561
- action: normalizedType === "Int" || normalizedType === "Float" ? "Do not auto-edit UI. Check local Template/Unit/View patterns for Field.Text parsing, formatting, validation rules, and dictionary labels before adding a numeric input." : undefined,
5562
- message: normalizedType === "Int" || normalizedType === "Float" ? `Numeric UI for ${field} (${normalizedType}) requires manual review; a safe numeric input component pattern is not yet detected.` : `Recommended UI component for ${field} (${normalizedType}): ${addFieldComponentForType(typeName)}.`
6016
+ target: paths.template,
6017
+ confidence: policy.confidence,
6018
+ 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.`,
6019
+ message: `Recommended UI component for ${field} (${normalizedType}): ${policy.component}.`
5563
6020
  },
6021
+ ...!surfaces ? [
6022
+ {
6023
+ code: "add-field-template-surface-choice",
6024
+ kind: "manual-action",
6025
+ target: paths.template,
6026
+ action: `Choose whether to expose ${field} in Template by passing surfaces=["template"].`,
6027
+ confidence: "medium",
6028
+ message: `Template exposure for ${module}.${field} is not selected yet.`
6029
+ }
6030
+ ] : [],
6031
+ ...!includeInLight ? [
6032
+ {
6033
+ code: "add-field-light-projection-choice",
6034
+ kind: "manual-action",
6035
+ target: paths.constant,
6036
+ action: `Pass includeInLight=true when ${field} should appear in Light${capitalize3(module)} list/card projections.`,
6037
+ confidence: "medium",
6038
+ message: `Light projection exposure for ${module}.${field} is not selected yet.`
6039
+ }
6040
+ ] : [],
6041
+ ...includeInLight ? [
6042
+ {
6043
+ code: "add-field-light-projection",
6044
+ kind: "placement",
6045
+ target: paths.constant,
6046
+ confidence: "high",
6047
+ message: `Add ${field} to Light${capitalize3(module)} projection fields.`
6048
+ }
6049
+ ] : [],
6050
+ ...surfaces && !templateRequested ? [
6051
+ {
6052
+ code: "add-field-ui-surface-skip",
6053
+ kind: "manual-action",
6054
+ target: paths.template,
6055
+ confidence: "medium",
6056
+ message: `Template is not included in surfaces, so ${module}.${field} will not be auto-rendered there.`
6057
+ }
6058
+ ] : [],
5564
6059
  {
5565
6060
  code: "add-field-ui-manual-review",
5566
6061
  kind: "manual-action",
5567
- target: templatePath,
5568
- action: `Review ${app}:${module} Template/Unit/View/Store surfaces and add ${field} only where the existing pattern is clear. For numeric fields, confirm parser/formatter behavior and validation before using Field.Text.`,
6062
+ target: paths.template,
6063
+ 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.`,
5569
6064
  confidence: "medium",
5570
- message: normalizedType === "Int" || normalizedType === "Float" ? "UI surface edits stay manual because a safe numeric input component pattern is not yet detected." : "UI surface edits are planned as manual-review unless a safe existing field-list pattern is detected."
6065
+ message: "UI manual review includes the reason auto-edit may be skipped and candidate insertion positions."
6066
+ },
6067
+ ...createAddFieldDefaultRecommendations(inputs)
6068
+ ];
6069
+ };
6070
+ var createWorkflowPlanPredictedChanges = (spec, inputs) => {
6071
+ if (spec.name !== "add-field")
6072
+ return spec.predictedChanges;
6073
+ const paths = addFieldPaths(inputs);
6074
+ const surfaces = selectedAddFieldSurfaces(inputs);
6075
+ const includeInLight = inputs.includeInLight === true;
6076
+ return [
6077
+ {
6078
+ target: paths.constant,
6079
+ action: "modify",
6080
+ applyScope: "auto",
6081
+ reason: includeInLight ? "Field shape and Light projection are added." : "Field shape is added."
6082
+ },
6083
+ {
6084
+ target: paths.dictionary,
6085
+ action: "modify",
6086
+ applyScope: "auto",
6087
+ reason: "Field label is added."
6088
+ },
6089
+ ...!surfaces || surfaces.has("template") ? [
6090
+ {
6091
+ target: paths.template,
6092
+ action: "modify",
6093
+ applyScope: surfaces?.has("template") ? "auto" : "manual-review",
6094
+ reason: surfaces?.has("template") ? "Template form is selected for safe-pattern auto insertion." : "Form surface may include the field."
6095
+ }
6096
+ ] : [],
6097
+ {
6098
+ target: `${addFieldTargetRoot(typeof inputs.app === "string" ? inputs.app : null)}/lib/cnst.ts`,
6099
+ action: "sync",
6100
+ applyScope: "generated-sync",
6101
+ reason: "Generated constants may change after sync."
6102
+ },
6103
+ {
6104
+ target: `${addFieldTargetRoot(typeof inputs.app === "string" ? inputs.app : null)}/lib/dict.ts`,
6105
+ action: "sync",
6106
+ applyScope: "generated-sync",
6107
+ reason: "Generated dictionary barrel may change after sync."
5571
6108
  }
5572
6109
  ];
5573
6110
  };
6111
+ var createWorkflowPlanOptionalSurfaces = (spec, inputs) => {
6112
+ const optionalSurfaces = spec.optionalSurfaces ?? {};
6113
+ const surfaces = selectedAddFieldSurfaces(inputs);
6114
+ if (spec.name !== "add-field" || !surfaces)
6115
+ return optionalSurfaces;
6116
+ return Object.fromEntries(Object.entries(optionalSurfaces).map(([surface, mode]) => [surface, surfaces.has(surface) ? "include" : mode]));
6117
+ };
5574
6118
  var createWorkflowPlanRecommendations = (spec, inputs) => [
5575
6119
  {
5576
6120
  code: "workflow-apply-first",
@@ -5634,14 +6178,32 @@ var createWorkflowPlan = (spec, rawInputs) => {
5634
6178
  message: `Field type "${fieldType}" is ambiguous in Akan. Use Int for integer fields or Float for decimal fields.`
5635
6179
  });
5636
6180
  }
6181
+ if (spec.name === "add-field") {
6182
+ const defaultCoercion = addFieldDefaultCoercion(inputs);
6183
+ if (defaultCoercion?.diagnostic) {
6184
+ diagnostics.push({
6185
+ ...defaultCoercion.diagnostic,
6186
+ code: "workflow-default-value-invalid",
6187
+ message: `Plan input default is not valid for ${defaultCoercion.normalizedType}: ${defaultCoercion.diagnostic.message}`
6188
+ });
6189
+ } else if (defaultCoercion?.normalized && defaultCoercion.expression) {
6190
+ diagnostics.push({
6191
+ severity: "warning",
6192
+ code: "workflow-default-value-normalized",
6193
+ input: "default",
6194
+ failureScope: "source-change",
6195
+ message: `Plan input default will be written as ${defaultCoercion.normalizedType} literal ${defaultCoercion.expression}.`
6196
+ });
6197
+ }
6198
+ }
5637
6199
  return {
5638
6200
  schemaVersion: 1,
5639
6201
  workflow: spec.name,
5640
6202
  mode: "plan",
5641
6203
  inputs,
5642
- optionalSurfaces: spec.optionalSurfaces ?? {},
6204
+ optionalSurfaces: createWorkflowPlanOptionalSurfaces(spec, inputs),
5643
6205
  steps: spec.steps,
5644
- predictedChanges: spec.predictedChanges,
6206
+ predictedChanges: createWorkflowPlanPredictedChanges(spec, inputs),
5645
6207
  validation: spec.validation,
5646
6208
  diagnostics,
5647
6209
  recommendations: createWorkflowPlanRecommendations(spec, inputs),
@@ -5710,35 +6272,65 @@ var renderWorkflowPlan = (plan) => [
5710
6272
  ""
5711
6273
  ].join(`
5712
6274
  `);
5713
- var renderWorkflowApplyReport = (report) => [
5714
- `# Workflow Apply: ${report.workflow}`,
5715
- "",
5716
- `- Mode: ${report.mode}`,
5717
- `- Status: ${report.status}`,
5718
- "",
5719
- "## Changed Files",
5720
- ...report.changedFiles.length ? report.changedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
5721
- "",
5722
- "## Generated Files",
5723
- ...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
5724
- "",
5725
- "## Applied Commands",
5726
- ...report.appliedCommands.length ? report.appliedCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
5727
- "",
5728
- "## Recommended Validation Commands",
5729
- ...report.recommendedValidationCommands.length ? report.recommendedValidationCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
5730
- "",
5731
- "## Diagnostics",
5732
- ...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
5733
- "",
5734
- "## Recommendations",
5735
- ...report.recommendations.length ? report.recommendations.map((recommendation) => `- [${recommendation.kind}] ${recommendation.message}`) : ["- none"],
5736
- "",
5737
- "## Next Actions",
5738
- ...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
5739
- ""
5740
- ].join(`
6275
+ var renderRecommendation = (recommendation) => {
6276
+ const target = recommendation.target ? ` ${recommendation.target}` : "";
6277
+ const action = recommendation.action ? ` Action: ${recommendation.action}` : "";
6278
+ return `- [${recommendation.kind}]${target} ${recommendation.message}${action}`;
6279
+ };
6280
+ var renderDiagnostic = (diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`;
6281
+ var applySourceStatus = (report) => report.diagnostics.some((diagnostic) => diagnostic.severity === "error" && (diagnostic.failureScope === "source-change" || !diagnostic.failureScope || diagnostic.failureScope === "unknown")) ? "failed" : "passed";
6282
+ var renderWorkflowApplyReport = (report) => {
6283
+ const manualReviewItems = [
6284
+ ...report.diagnostics.filter((diagnostic) => diagnostic.severity === "warning").map(renderDiagnostic),
6285
+ ...report.recommendations.filter((recommendation) => recommendation.kind === "manual-action").map(renderRecommendation)
6286
+ ];
6287
+ const validationBlockers = report.diagnostics.filter((diagnostic) => diagnostic.severity === "error" && (diagnostic.failureScope === "workspace-config" || diagnostic.failureScope === "environment")).map(renderDiagnostic);
6288
+ const sourceBlockers = report.diagnostics.filter((diagnostic) => diagnostic.severity === "error" && (diagnostic.failureScope === "source-change" || !diagnostic.failureScope || diagnostic.failureScope === "unknown")).map(renderDiagnostic);
6289
+ return [
6290
+ `# Workflow Apply: ${report.workflow}`,
6291
+ "",
6292
+ `- Mode: ${report.mode}`,
6293
+ `- Status: ${report.status}`,
6294
+ `- Source-change status: ${applySourceStatus(report)}`,
6295
+ `- Workspace status: ${validationBlockers.length ? "blocked" : "not blocked during apply"}`,
6296
+ ...report.validationTarget ? [`- Validation target: ${report.validationTarget}`] : [],
6297
+ "",
6298
+ "## Apply Checks",
6299
+ ...report.postApplyChecks?.length ? report.postApplyChecks.map((check) => `- [${check.status}] ${check.code} ${check.target}: ${check.message}`) : ["- not run"],
6300
+ "",
6301
+ "## Source Change Blockers",
6302
+ ...sourceBlockers.length ? sourceBlockers : ["- none"],
6303
+ "",
6304
+ "## Automatically Modified",
6305
+ ...report.changedFiles.length ? report.changedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
6306
+ "",
6307
+ "## Generated Sync",
6308
+ ...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
6309
+ "",
6310
+ "## Applied Commands",
6311
+ ...report.appliedCommands.length ? report.appliedCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
6312
+ "",
6313
+ "## Recommended Validation Commands",
6314
+ ...report.recommendedValidationCommands.length ? report.recommendedValidationCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
6315
+ "",
6316
+ "## User Review Required",
6317
+ ...manualReviewItems.length ? manualReviewItems : ["- none"],
6318
+ "",
6319
+ "## Validation Blockers",
6320
+ ...validationBlockers.length ? validationBlockers : report.recommendedValidationCommands.length ? ["- none detected during apply; run validation with the validation target for command-level blockers."] : ["- none"],
6321
+ "",
6322
+ "## Diagnostics",
6323
+ ...report.diagnostics.length ? report.diagnostics.map(renderDiagnostic) : ["- none"],
6324
+ "",
6325
+ "## Recommendations",
6326
+ ...report.recommendations.length ? report.recommendations.slice(0, 3).map(renderRecommendation) : ["- none"],
6327
+ "",
6328
+ "## Next Actions",
6329
+ ...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
6330
+ ""
6331
+ ].join(`
5741
6332
  `);
6333
+ };
5742
6334
  var renderWorkflowApply = (report, format = "markdown") => format === "json" ? jsonText(report) : renderWorkflowApplyReport(report);
5743
6335
  var renderWorkflowValidationRunReport = (report) => [
5744
6336
  `# Workflow Validation: ${report.workflow}`,
@@ -5749,11 +6341,24 @@ var renderWorkflowValidationRunReport = (report) => [
5749
6341
  `- Workspace status: ${report.workspaceStatus}`,
5750
6342
  `- Overall status: ${report.overallStatus}`,
5751
6343
  "",
6344
+ "## Status Summary",
6345
+ `- Source-change: ${report.summary.sourceChange}`,
6346
+ `- Generated sync: ${report.summary.generatedSync}`,
6347
+ `- Workspace config: ${report.summary.workspaceConfig}`,
6348
+ `- Environment: ${report.summary.environment}`,
6349
+ "",
6350
+ "## Source Change Diagnostics",
6351
+ ...report.workflowDiagnostics?.length ? report.workflowDiagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
6352
+ "",
6353
+ "## Existing Workspace Blockers",
6354
+ ...report.baselineDiagnostics?.length ? report.baselineDiagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
6355
+ "",
5752
6356
  "## Known Blockers",
5753
6357
  ...report.knownBlockers.length ? report.knownBlockers.map((blocker) => {
5754
6358
  const command = blocker.command ? ` \`${blocker.command}\`` : "";
5755
6359
  const count = blocker.count > 1 ? ` (${blocker.count}x)` : "";
5756
- return `- [${blocker.failureScope}]${command}${count}: ${blocker.message}`;
6360
+ const known = blocker.known ? " known" : "";
6361
+ return `- [${blocker.failureScope}${known}]${command}${count}: ${blocker.message}`;
5757
6362
  }) : ["- none"],
5758
6363
  "",
5759
6364
  "## Commands",
@@ -6822,7 +7427,7 @@ var collapseIndex = (relPathNoExt) => {
6822
7427
 
6823
7428
  // pkgs/@akanjs/devkit/transforms/barrelImportsPlugin.ts
6824
7429
  import path14 from "path";
6825
- import ts4 from "typescript";
7430
+ import ts5 from "typescript";
6826
7431
  var createBarrelImportsPlugin = async (app, { skipPath = defaultSkipPath, pipeAfter } = {}) => {
6827
7432
  const akanConfig2 = await app.getConfig();
6828
7433
  const barrels = [...new Set(akanConfig2.barrelImports)].filter(Boolean);
@@ -7063,11 +7668,11 @@ var rewriteBarrelImports = async (source2, barrels, analyzer) => {
7063
7668
  };
7064
7669
  var findImportStatements = (source2) => {
7065
7670
  const statements = [];
7066
- const sourceFile2 = ts4.createSourceFile("barrel-imports.tsx", source2, ts4.ScriptTarget.Latest, true, ts4.ScriptKind.TSX);
7671
+ const sourceFile2 = ts5.createSourceFile("barrel-imports.tsx", source2, ts5.ScriptTarget.Latest, true, ts5.ScriptKind.TSX);
7067
7672
  for (const statement of sourceFile2.statements) {
7068
- if (!ts4.isImportDeclaration(statement))
7673
+ if (!ts5.isImportDeclaration(statement))
7069
7674
  continue;
7070
- if (!ts4.isStringLiteral(statement.moduleSpecifier))
7675
+ if (!ts5.isStringLiteral(statement.moduleSpecifier))
7071
7676
  continue;
7072
7677
  const importClause = statement.importClause;
7073
7678
  if (!importClause)
@@ -8062,7 +8667,7 @@ import path22 from "path";
8062
8667
  // pkgs/@akanjs/devkit/frontendBuild/pagesEntrySourceGenerator.ts
8063
8668
  import fs3 from "fs";
8064
8669
  import path21 from "path";
8065
- import ts5 from "typescript";
8670
+ import ts6 from "typescript";
8066
8671
 
8067
8672
  class PagesEntrySourceGenerator {
8068
8673
  #pageEntries;
@@ -8106,7 +8711,7 @@ ${entries.join(`
8106
8711
  static #hasAsyncDefaultExport(moduleAbsPath) {
8107
8712
  try {
8108
8713
  const source2 = fs3.readFileSync(path21.resolve(moduleAbsPath), "utf8");
8109
- const sourceFile2 = ts5.createSourceFile(moduleAbsPath, source2, ts5.ScriptTarget.Latest, true, PagesEntrySourceGenerator.#scriptKind(moduleAbsPath));
8714
+ const sourceFile2 = ts6.createSourceFile(moduleAbsPath, source2, ts6.ScriptTarget.Latest, true, PagesEntrySourceGenerator.#scriptKind(moduleAbsPath));
8110
8715
  return PagesEntrySourceGenerator.#sourceFileHasAsyncDefaultExport(sourceFile2);
8111
8716
  } catch {
8112
8717
  return false;
@@ -8116,31 +8721,31 @@ ${entries.join(`
8116
8721
  const asyncBindings = new Map;
8117
8722
  let defaultIdentifier = null;
8118
8723
  for (const statement of sourceFile2.statements) {
8119
- if (ts5.isFunctionDeclaration(statement)) {
8120
- if (PagesEntrySourceGenerator.#hasModifier(statement, ts5.SyntaxKind.DefaultKeyword)) {
8121
- return PagesEntrySourceGenerator.#hasModifier(statement, ts5.SyntaxKind.AsyncKeyword);
8724
+ if (ts6.isFunctionDeclaration(statement)) {
8725
+ if (PagesEntrySourceGenerator.#hasModifier(statement, ts6.SyntaxKind.DefaultKeyword)) {
8726
+ return PagesEntrySourceGenerator.#hasModifier(statement, ts6.SyntaxKind.AsyncKeyword);
8122
8727
  }
8123
8728
  if (statement.name) {
8124
- asyncBindings.set(statement.name.text, PagesEntrySourceGenerator.#hasModifier(statement, ts5.SyntaxKind.AsyncKeyword));
8729
+ asyncBindings.set(statement.name.text, PagesEntrySourceGenerator.#hasModifier(statement, ts6.SyntaxKind.AsyncKeyword));
8125
8730
  }
8126
8731
  continue;
8127
8732
  }
8128
- if (ts5.isVariableStatement(statement)) {
8733
+ if (ts6.isVariableStatement(statement)) {
8129
8734
  for (const declaration of statement.declarationList.declarations) {
8130
- if (!ts5.isIdentifier(declaration.name))
8735
+ if (!ts6.isIdentifier(declaration.name))
8131
8736
  continue;
8132
8737
  asyncBindings.set(declaration.name.text, PagesEntrySourceGenerator.#isAsyncFunctionExpression(declaration.initializer));
8133
8738
  }
8134
8739
  continue;
8135
8740
  }
8136
- if (ts5.isExportAssignment(statement)) {
8741
+ if (ts6.isExportAssignment(statement)) {
8137
8742
  if (PagesEntrySourceGenerator.#isAsyncFunctionExpression(statement.expression))
8138
8743
  return true;
8139
- if (ts5.isIdentifier(statement.expression))
8744
+ if (ts6.isIdentifier(statement.expression))
8140
8745
  defaultIdentifier = statement.expression.text;
8141
8746
  continue;
8142
8747
  }
8143
- if (ts5.isExportDeclaration(statement) && statement.exportClause && ts5.isNamedExports(statement.exportClause)) {
8748
+ if (ts6.isExportDeclaration(statement) && statement.exportClause && ts6.isNamedExports(statement.exportClause)) {
8144
8749
  const exportClause = statement.exportClause;
8145
8750
  for (const specifier of exportClause.elements) {
8146
8751
  if (specifier.name.text !== "default")
@@ -8152,13 +8757,13 @@ ${entries.join(`
8152
8757
  return defaultIdentifier ? asyncBindings.get(defaultIdentifier) === true : false;
8153
8758
  }
8154
8759
  static #hasModifier(node, kind) {
8155
- return ts5.canHaveModifiers(node) && (ts5.getModifiers(node)?.some((modifier) => modifier.kind === kind) ?? false);
8760
+ return ts6.canHaveModifiers(node) && (ts6.getModifiers(node)?.some((modifier) => modifier.kind === kind) ?? false);
8156
8761
  }
8157
8762
  static #isAsyncFunctionExpression(node) {
8158
- return Boolean(node && (ts5.isArrowFunction(node) || ts5.isFunctionExpression(node)) && PagesEntrySourceGenerator.#hasModifier(node, ts5.SyntaxKind.AsyncKeyword));
8763
+ return Boolean(node && (ts6.isArrowFunction(node) || ts6.isFunctionExpression(node)) && PagesEntrySourceGenerator.#hasModifier(node, ts6.SyntaxKind.AsyncKeyword));
8159
8764
  }
8160
8765
  static #scriptKind(moduleAbsPath) {
8161
- return moduleAbsPath.endsWith(".tsx") || moduleAbsPath.endsWith(".jsx") ? ts5.ScriptKind.TSX : ts5.ScriptKind.TS;
8766
+ return moduleAbsPath.endsWith(".tsx") || moduleAbsPath.endsWith(".jsx") ? ts6.ScriptKind.TSX : ts6.ScriptKind.TS;
8162
8767
  }
8163
8768
  }
8164
8769
 
@@ -9077,7 +9682,7 @@ import {
9077
9682
  } from "fontaine";
9078
9683
  import { createFont, woff2 } from "fonteditor-core";
9079
9684
  import subsetFont from "subset-font";
9080
- import ts6 from "typescript";
9685
+ import ts7 from "typescript";
9081
9686
  var FONT_URL_PREFIX = "/_akan/fonts";
9082
9687
  var DEFAULT_FONT_SUBSETS = ["latin"];
9083
9688
 
@@ -9142,17 +9747,17 @@ class FontOptimizer {
9142
9747
  this.#cssParts.push(...faceCss, this.#buildRootVariableRule(font));
9143
9748
  }
9144
9749
  #extractFontsExport(source2, filePath) {
9145
- const sourceFile2 = ts6.createSourceFile(filePath, source2, ts6.ScriptTarget.Latest, true, ts6.ScriptKind.TSX);
9750
+ const sourceFile2 = ts7.createSourceFile(filePath, source2, ts7.ScriptTarget.Latest, true, ts7.ScriptKind.TSX);
9146
9751
  const fonts = [];
9147
9752
  for (const statement of sourceFile2.statements) {
9148
- if (!ts6.isVariableStatement(statement))
9753
+ if (!ts7.isVariableStatement(statement))
9149
9754
  continue;
9150
- const modifiers = ts6.canHaveModifiers(statement) ? ts6.getModifiers(statement) : undefined;
9151
- const isExported = modifiers?.some((modifier) => modifier.kind === ts6.SyntaxKind.ExportKeyword) ?? false;
9755
+ const modifiers = ts7.canHaveModifiers(statement) ? ts7.getModifiers(statement) : undefined;
9756
+ const isExported = modifiers?.some((modifier) => modifier.kind === ts7.SyntaxKind.ExportKeyword) ?? false;
9152
9757
  if (!isExported)
9153
9758
  continue;
9154
9759
  for (const declaration of statement.declarationList.declarations) {
9155
- if (!ts6.isIdentifier(declaration.name) || declaration.name.text !== "fonts")
9760
+ if (!ts7.isIdentifier(declaration.name) || declaration.name.text !== "fonts")
9156
9761
  continue;
9157
9762
  const value = declaration.initializer ? this.#literalToValue(declaration.initializer) : null;
9158
9763
  if (Array.isArray(value)) {
@@ -9163,20 +9768,20 @@ class FontOptimizer {
9163
9768
  return fonts;
9164
9769
  }
9165
9770
  #literalToValue(node) {
9166
- if (ts6.isStringLiteralLike(node))
9771
+ if (ts7.isStringLiteralLike(node))
9167
9772
  return node.text;
9168
- if (ts6.isNumericLiteral(node))
9773
+ if (ts7.isNumericLiteral(node))
9169
9774
  return Number(node.text);
9170
- if (node.kind === ts6.SyntaxKind.TrueKeyword)
9775
+ if (node.kind === ts7.SyntaxKind.TrueKeyword)
9171
9776
  return true;
9172
- if (node.kind === ts6.SyntaxKind.FalseKeyword)
9777
+ if (node.kind === ts7.SyntaxKind.FalseKeyword)
9173
9778
  return false;
9174
- if (ts6.isArrayLiteralExpression(node))
9779
+ if (ts7.isArrayLiteralExpression(node))
9175
9780
  return node.elements.map((element) => this.#literalToValue(element));
9176
- if (ts6.isObjectLiteralExpression(node)) {
9781
+ if (ts7.isObjectLiteralExpression(node)) {
9177
9782
  const obj = {};
9178
9783
  for (const prop of node.properties) {
9179
- if (!ts6.isPropertyAssignment(prop))
9784
+ if (!ts7.isPropertyAssignment(prop))
9180
9785
  continue;
9181
9786
  const name = this.#getPropertyName(prop.name);
9182
9787
  if (!name)
@@ -9185,13 +9790,13 @@ class FontOptimizer {
9185
9790
  }
9186
9791
  return obj;
9187
9792
  }
9188
- if (ts6.isAsExpression(node) || ts6.isSatisfiesExpression(node) || ts6.isParenthesizedExpression(node)) {
9793
+ if (ts7.isAsExpression(node) || ts7.isSatisfiesExpression(node) || ts7.isParenthesizedExpression(node)) {
9189
9794
  return this.#literalToValue(node.expression);
9190
9795
  }
9191
9796
  return;
9192
9797
  }
9193
9798
  #getPropertyName(name) {
9194
- if (ts6.isIdentifier(name) || ts6.isStringLiteral(name) || ts6.isNumericLiteral(name))
9799
+ if (ts7.isIdentifier(name) || ts7.isStringLiteral(name) || ts7.isNumericLiteral(name))
9195
9800
  return name.text;
9196
9801
  return null;
9197
9802
  }
@@ -12661,10 +13266,12 @@ var NODE_NATIVE_MODULE_SET = new Set([
12661
13266
  ]);
12662
13267
  // pkgs/@akanjs/devkit/getCredentials.ts
12663
13268
  import yaml from "js-yaml";
13269
+ // pkgs/@akanjs/devkit/getModelFileData.ts
13270
+ import { capitalize as capitalize8 } from "akanjs/common";
12664
13271
  // pkgs/@akanjs/devkit/getRelatedCnsts.ts
12665
13272
  import { readFileSync as readFileSync4, realpathSync } from "fs";
12666
13273
  import ora2 from "ora";
12667
- import * as ts7 from "typescript";
13274
+ import * as ts8 from "typescript";
12668
13275
  var tsTranspiler = new Bun.Transpiler({ loader: "ts" });
12669
13276
  var tsxTranspiler = new Bun.Transpiler({ loader: "tsx" });
12670
13277
  var getTranspiler = (filePath) => filePath.endsWith(".tsx") ? tsxTranspiler : tsTranspiler;
@@ -12697,10 +13304,10 @@ var scanModuleSpecifiers = (source2, filePath, includeExports) => {
12697
13304
  return importSpecifiers;
12698
13305
  };
12699
13306
  var parseTsConfig = (tsConfigPath = "./tsconfig.json") => {
12700
- const configFile = ts7.readConfigFile(tsConfigPath, (path40) => {
12701
- return ts7.sys.readFile(path40);
13307
+ const configFile = ts8.readConfigFile(tsConfigPath, (path40) => {
13308
+ return ts8.sys.readFile(path40);
12702
13309
  });
12703
- return ts7.parseJsonConfigFileContent(configFile.config, ts7.sys, realpathSync(tsConfigPath).replace(/[^/\\]+$/, ""));
13310
+ return ts8.parseJsonConfigFileContent(configFile.config, ts8.sys, realpathSync(tsConfigPath).replace(/[^/\\]+$/, ""));
12704
13311
  };
12705
13312
  var collectImportedFiles = (constantFilePath, parsedConfig) => {
12706
13313
  const allFilesToAnalyze = new Set([constantFilePath]);
@@ -12715,7 +13322,7 @@ var collectImportedFiles = (constantFilePath, parsedConfig) => {
12715
13322
  for (const importPath of scanModuleSpecifiers(source2, filePath, false)) {
12716
13323
  if (!importPath.startsWith("."))
12717
13324
  continue;
12718
- const resolved = ts7.resolveModuleName(importPath, filePath, parsedConfig.options, ts7.sys).resolvedModule?.resolvedFileName;
13325
+ const resolved = ts8.resolveModuleName(importPath, filePath, parsedConfig.options, ts8.sys).resolvedModule?.resolvedFileName;
12719
13326
  if (resolved && !allFilesToAnalyze.has(resolved)) {
12720
13327
  allFilesToAnalyze.add(resolved);
12721
13328
  collectImported(resolved);
@@ -12732,7 +13339,7 @@ var collectImportedFiles = (constantFilePath, parsedConfig) => {
12732
13339
  var createTsProgram = (filePaths, options) => {
12733
13340
  const spinner = ora2("Creating TypeScript program for all files...");
12734
13341
  spinner.start();
12735
- const program2 = ts7.createProgram(Array.from(filePaths), options);
13342
+ const program2 = ts8.createProgram(Array.from(filePaths), options);
12736
13343
  const checker = program2.getTypeChecker();
12737
13344
  spinner.succeed("TypeScript program created.");
12738
13345
  return {
@@ -12772,17 +13379,17 @@ var analyzeProperties = (filesToAnalyze, program2, checker) => {
12772
13379
  function visit(node) {
12773
13380
  if (!source2)
12774
13381
  return;
12775
- if (ts7.isPropertyAccessExpression(node)) {
13382
+ if (ts8.isPropertyAccessExpression(node)) {
12776
13383
  const left = node.expression;
12777
13384
  const right = node.name;
12778
- const { line } = ts7.getLineAndCharacterOfPosition(source2, node.getStart());
12779
- if (ts7.isIdentifier(left) && sourceLines && sourceLines.length > line && sourceLines[line] && (sourceLines[line]?.includes(`@Field.Prop(() => ${left.text}.${right.text}`) || sourceLines[line].includes(`base.Filter(${left.text}.${right.text},`))) {
13385
+ const { line } = ts8.getLineAndCharacterOfPosition(source2, node.getStart());
13386
+ if (ts8.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},`))) {
12780
13387
  const symbol = getCachedSymbol(right);
12781
13388
  if (symbol?.declarations && symbol.declarations.length > 0) {
12782
13389
  const key = symbol.declarations[0]?.getSourceFile().fileName.split("/").pop()?.split(".")[0] ?? "";
12783
13390
  const property = propertyMap.get(key);
12784
13391
  const isScalar = symbol.declarations[0]?.getSourceFile().fileName.includes("_") ?? false;
12785
- const symbolFilePath = symbol.declarations[0]?.getSourceFile().fileName.replace(`${ts7.sys.getCurrentDirectory()}/`, "");
13392
+ const symbolFilePath = symbol.declarations[0]?.getSourceFile().fileName.replace(`${ts8.sys.getCurrentDirectory()}/`, "");
12786
13393
  if (!symbolFilePath)
12787
13394
  throw new Error(`No symbol file path found for ${left.text}.${right.text}`);
12788
13395
  if (property) {
@@ -12806,10 +13413,10 @@ var analyzeProperties = (filesToAnalyze, program2, checker) => {
12806
13413
  }
12807
13414
  }
12808
13415
  }
12809
- } else if (ts7.isImportDeclaration(node) && ts7.isStringLiteral(node.moduleSpecifier)) {
13416
+ } else if (ts8.isImportDeclaration(node) && ts8.isStringLiteral(node.moduleSpecifier)) {
12810
13417
  const importPath = node.moduleSpecifier.text;
12811
13418
  if (importPath.startsWith(".")) {
12812
- const resolved = ts7.resolveModuleName(importPath, filePath, program2.getCompilerOptions(), ts7.sys).resolvedModule?.resolvedFileName;
13419
+ const resolved = ts8.resolveModuleName(importPath, filePath, program2.getCompilerOptions(), ts8.sys).resolvedModule?.resolvedFileName;
12813
13420
  const moduleName = importPath.split("/").pop()?.split(".")[0] ?? "";
12814
13421
  const property = propertyMap.get(moduleName);
12815
13422
  const isScalar = importPath.includes("_");
@@ -12824,7 +13431,7 @@ var analyzeProperties = (filesToAnalyze, program2, checker) => {
12824
13431
  }
12825
13432
  }
12826
13433
  }
12827
- ts7.forEachChild(node, visit);
13434
+ ts8.forEachChild(node, visit);
12828
13435
  }
12829
13436
  visit(source2);
12830
13437
  }
@@ -14611,7 +15218,7 @@ import path43 from "path";
14611
15218
 
14612
15219
  // pkgs/@akanjs/cli/module/module.script.ts
14613
15220
  import { input as input6 } from "@inquirer/prompts";
14614
- import { capitalize as capitalize10, randomPicks } from "akanjs/common";
15221
+ import { capitalize as capitalize11, randomPicks } from "akanjs/common";
14615
15222
 
14616
15223
  // pkgs/@akanjs/cli/page/page.runner.ts
14617
15224
  class PageRunner extends runner("page") {
@@ -14641,7 +15248,7 @@ function pluralizeName(name) {
14641
15248
  }
14642
15249
 
14643
15250
  // pkgs/@akanjs/cli/module/module.request.ts
14644
- import { capitalize as capitalize8 } from "akanjs/common";
15251
+ import { capitalize as capitalize9 } from "akanjs/common";
14645
15252
 
14646
15253
  // pkgs/@akanjs/cli/module/module.prompt.ts
14647
15254
  var componentDefaultDescription = ({
@@ -14766,7 +15373,7 @@ var requestTemplate = ({
14766
15373
  ${componentDefaultDescription({
14767
15374
  sysName,
14768
15375
  modelName,
14769
- ModelName: ModelName ?? capitalize8(modelName),
15376
+ ModelName: ModelName ?? capitalize9(modelName),
14770
15377
  exampleFiles,
14771
15378
  constant,
14772
15379
  properties
@@ -14823,7 +15430,7 @@ var requestView = ({
14823
15430
  ${componentDefaultDescription({
14824
15431
  sysName,
14825
15432
  modelName,
14826
- ModelName: ModelName ?? capitalize8(modelName),
15433
+ ModelName: ModelName ?? capitalize9(modelName),
14827
15434
  exampleFiles,
14828
15435
  constant,
14829
15436
  properties
@@ -14884,7 +15491,7 @@ var requestUnit = ({
14884
15491
  ${componentDefaultDescription({
14885
15492
  sysName,
14886
15493
  modelName,
14887
- ModelName: ModelName ?? capitalize8(modelName),
15494
+ ModelName: ModelName ?? capitalize9(modelName),
14888
15495
  exampleFiles,
14889
15496
  constant,
14890
15497
  properties
@@ -14933,22 +15540,29 @@ var requestUnit = ({
14933
15540
  `;
14934
15541
 
14935
15542
  // pkgs/@akanjs/cli/module/module.runner.ts
14936
- import { capitalize as capitalize9 } from "akanjs/common";
15543
+ import { capitalize as capitalize10 } from "akanjs/common";
15544
+ var purposeByModule = {
15545
+ budget: "Budget represents planned or actual money allocated inside the app.",
15546
+ project: "Project represents a project workspace or business initiative managed by the app.",
15547
+ task: "Task represents work items that move through the app workflow."
15548
+ };
14937
15549
  var moduleAbstractContent = (moduleName) => {
14938
- const title = capitalize9(moduleName);
15550
+ const title = capitalize10(moduleName);
15551
+ const label = bilingualLabelForField(moduleName);
15552
+ const purpose = purposeByModule[moduleName] ?? `${title} represents ${label.en.toLowerCase()} records managed by the app.`;
14939
15553
  return `# ${title} Module Abstract
14940
15554
 
14941
15555
  ## Purpose
14942
15556
 
14943
- ${title} owns persisted ${moduleName} records.
15557
+ ${purpose}
14944
15558
 
14945
15559
  ## Domain Rules
14946
15560
 
14947
- - No durable business invariants yet.
15561
+ - Keep ${label.en.toLowerCase()} data consistent with user-facing dictionary labels.
14948
15562
 
14949
15563
  ## Data Meaning
14950
15564
 
14951
- No additional data meaning has been documented yet.
15565
+ ${label.en} (${label.ko}) is the primary business concept for this module.
14952
15566
 
14953
15567
  ## Workflows
14954
15568
 
@@ -14965,6 +15579,7 @@ No lifecycle workflow yet.
14965
15579
  - None yet.
14966
15580
  `;
14967
15581
  };
15582
+ var localModuleFilename = (moduleName, pathKey2) => moduleSourcePaths(moduleName)[pathKey2].replace(`lib/${moduleName}/`, "");
14968
15583
 
14969
15584
  class ModuleRunner extends runner("module") {
14970
15585
  async createService(module) {
@@ -14995,24 +15610,47 @@ class ModuleRunner extends runner("module") {
14995
15610
  async createComponentTemplate(module, type) {
14996
15611
  await module.sys.applyTemplate({
14997
15612
  basePath: `./lib/${module.name}`,
14998
- template: `module/__Model__.${capitalize9(type)}.tsx`,
15613
+ template: `module/__Model__.${capitalize10(type)}.tsx`,
14999
15614
  dict: { model: module.name, appName: module.sys.name }
15000
15615
  });
15001
15616
  return {
15002
15617
  component: {
15003
- filename: `${module.name}.${capitalize9(type)}.tsx`,
15004
- content: await module.sys.readFile(`lib/${module.name}/${capitalize9(module.name)}.${capitalize9(type)}.tsx`)
15618
+ filename: `${capitalize10(module.name)}.${capitalize10(type)}.tsx`,
15619
+ content: await module.sys.readFile(`lib/${module.name}/${capitalize10(module.name)}.${capitalize10(type)}.tsx`)
15005
15620
  }
15006
15621
  };
15007
15622
  }
15008
15623
  async createModuleTemplate(module) {
15009
15624
  const names = pluralizeName(module.name);
15625
+ const modelLabel = bilingualLabelForField(module.name);
15626
+ const modelDescription = bilingualDescriptionForField(module.name);
15627
+ const filenames = {
15628
+ abstract: localModuleFilename(module.name, "abstract"),
15629
+ constant: localModuleFilename(module.name, "constant"),
15630
+ dictionary: localModuleFilename(module.name, "dictionary"),
15631
+ service: localModuleFilename(module.name, "service"),
15632
+ store: localModuleFilename(module.name, "store"),
15633
+ signal: localModuleFilename(module.name, "signal"),
15634
+ unit: localModuleFilename(module.name, "unit"),
15635
+ view: localModuleFilename(module.name, "view"),
15636
+ template: localModuleFilename(module.name, "template"),
15637
+ zone: localModuleFilename(module.name, "zone"),
15638
+ util: localModuleFilename(module.name, "util")
15639
+ };
15010
15640
  await module.applyTemplate({
15011
15641
  basePath: `.`,
15012
15642
  template: "module",
15013
- dict: { model: module.name, models: names, sysName: module.sys.name }
15643
+ dict: {
15644
+ model: module.name,
15645
+ models: names,
15646
+ sysName: module.sys.name,
15647
+ modelLabelEn: modelLabel.en,
15648
+ modelLabelKo: modelLabel.ko,
15649
+ modelDescEn: modelDescription.en,
15650
+ modelDescKo: modelDescription.ko
15651
+ }
15014
15652
  });
15015
- await module.writeFile(`${module.name}.abstract.md`, moduleAbstractContent(module.name));
15653
+ await module.writeFile(filenames.abstract, moduleAbstractContent(module.name));
15016
15654
  const [
15017
15655
  abstractContent,
15018
15656
  constantContent,
@@ -15026,30 +15664,30 @@ class ModuleRunner extends runner("module") {
15026
15664
  zoneContent,
15027
15665
  utilContent
15028
15666
  ] = await Promise.all([
15029
- module.readFile(`${module.name}.abstract.md`),
15030
- module.readFile(`${module.name}.constant.ts`),
15031
- module.readFile(`${module.name}.dictionary.ts`),
15032
- module.readFile(`${module.name}.service.ts`),
15033
- module.readFile(`${module.name}.store.ts`),
15034
- module.readFile(`${module.name}.signal.ts`),
15035
- module.readFile(`${module.name}.Unit.tsx`),
15036
- module.readFile(`${module.name}.View.tsx`),
15037
- module.readFile(`${module.name}.Template.tsx`),
15038
- module.readFile(`${module.name}.Zone.tsx`),
15039
- module.readFile(`${module.name}.Util.tsx`)
15667
+ module.readFile(filenames.abstract),
15668
+ module.readFile(filenames.constant),
15669
+ module.readFile(filenames.dictionary),
15670
+ module.readFile(filenames.service),
15671
+ module.readFile(filenames.store),
15672
+ module.readFile(filenames.signal),
15673
+ module.readFile(filenames.unit),
15674
+ module.readFile(filenames.view),
15675
+ module.readFile(filenames.template),
15676
+ module.readFile(filenames.zone),
15677
+ module.readFile(filenames.util)
15040
15678
  ]);
15041
15679
  return {
15042
- abstract: { filename: `${module.name}.abstract.md`, content: abstractContent },
15043
- constant: { filename: `${module.name}.constant.ts`, content: constantContent },
15044
- dictionary: { filename: `${module.name}.dictionary.ts`, content: dictionaryContent },
15045
- service: { filename: `${module.name}.service.ts`, content: serviceContent },
15046
- store: { filename: `${module.name}.store.ts`, content: storeContent },
15047
- signal: { filename: `${module.name}.signal.ts`, content: signalContent },
15048
- unit: { filename: `${module.name}.Unit.tsx`, content: unitContent },
15049
- view: { filename: `${module.name}.View.tsx`, content: viewContent },
15050
- template: { filename: `${module.name}.Template.tsx`, content: templateContent },
15051
- zone: { filename: `${module.name}.Zone.tsx`, content: zoneContent },
15052
- util: { filename: `${module.name}.Util.tsx`, content: utilContent }
15680
+ abstract: { filename: filenames.abstract, content: abstractContent },
15681
+ constant: { filename: filenames.constant, content: constantContent },
15682
+ dictionary: { filename: filenames.dictionary, content: dictionaryContent },
15683
+ service: { filename: filenames.service, content: serviceContent },
15684
+ store: { filename: filenames.store, content: storeContent },
15685
+ signal: { filename: filenames.signal, content: signalContent },
15686
+ unit: { filename: filenames.unit, content: unitContent },
15687
+ view: { filename: filenames.view, content: viewContent },
15688
+ template: { filename: filenames.template, content: templateContent },
15689
+ zone: { filename: filenames.zone, content: zoneContent },
15690
+ util: { filename: filenames.util, content: utilContent }
15053
15691
  };
15054
15692
  }
15055
15693
  }
@@ -15151,8 +15789,9 @@ class ModuleScript extends script("module", [ModuleRunner, PageScript]) {
15151
15789
  async createTest(workspace, name) {}
15152
15790
  async createTemplate(mod) {
15153
15791
  const { component: template } = await this.moduleRunner.createComponentTemplate(mod, "template");
15154
- const templateExampleFiles = (await mod.sys.getTemplatesSourceCode()).filter((f) => !f.filePath.includes(`${mod.name}.Template.tsx`));
15155
- const Name = capitalize10(mod.name);
15792
+ const paths = moduleSourcePaths(mod.name);
15793
+ const templateExampleFiles = (await mod.sys.getTemplatesSourceCode()).filter((f) => !f.filePath.includes(paths.template));
15794
+ const Name = capitalize11(mod.name);
15156
15795
  const relatedCnsts = getRelatedCnsts(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
15157
15796
  const constant = await FileSys.readText(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
15158
15797
  const session = new AiSession("createTemplate", { workspace: mod.sys.workspace, cacheKey: mod.name });
@@ -15178,8 +15817,9 @@ class ModuleScript extends script("module", [ModuleRunner, PageScript]) {
15178
15817
  }
15179
15818
  async createUnit(mod) {
15180
15819
  const { component: unit } = await this.moduleRunner.createComponentTemplate(mod, "unit");
15181
- const Name = capitalize10(mod.name);
15182
- const unitExampleFiles = (await mod.sys.getUnitsSourceCode()).filter((f) => !f.filePath.includes(`${mod.name}.Unit.tsx`));
15820
+ const paths = moduleSourcePaths(mod.name);
15821
+ const Name = capitalize11(mod.name);
15822
+ const unitExampleFiles = (await mod.sys.getUnitsSourceCode()).filter((f) => !f.filePath.includes(paths.unit));
15183
15823
  const relatedCnsts = getRelatedCnsts(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
15184
15824
  const constant = await FileSys.readText(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
15185
15825
  const session = new AiSession("createUnit", { workspace: mod.sys.workspace, cacheKey: mod.name });
@@ -15203,8 +15843,9 @@ class ModuleScript extends script("module", [ModuleRunner, PageScript]) {
15203
15843
  }
15204
15844
  async createView(mod) {
15205
15845
  const { component: view } = await this.moduleRunner.createComponentTemplate(mod, "view");
15206
- const viewExampleFiles = (await mod.sys.getViewsSourceCode()).filter((f) => !f.filePath.includes(`${mod.name}.View.tsx`));
15207
- const Name = capitalize10(mod.name);
15846
+ const paths = moduleSourcePaths(mod.name);
15847
+ const viewExampleFiles = (await mod.sys.getViewsSourceCode()).filter((f) => !f.filePath.includes(paths.view));
15848
+ const Name = capitalize11(mod.name);
15208
15849
  const relatedCnsts = getRelatedCnsts(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
15209
15850
  const constant = await FileSys.readText(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
15210
15851
  const session = new AiSession("createView", { workspace: mod.sys.workspace, cacheKey: mod.name });
@@ -15229,7 +15870,7 @@ class ModuleScript extends script("module", [ModuleRunner, PageScript]) {
15229
15870
  }
15230
15871
 
15231
15872
  // pkgs/@akanjs/cli/primitive/primitive.script.ts
15232
- import { capitalize as capitalize11 } from "akanjs/common";
15873
+ import { capitalize as capitalize12 } from "akanjs/common";
15233
15874
  class PrimitiveScript extends script("primitive", [ModuleScript]) {
15234
15875
  async resolveSys(workspace, target) {
15235
15876
  if (!target)
@@ -15277,7 +15918,7 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
15277
15918
  }
15278
15919
  async addEnumField(workspace, input7) {
15279
15920
  const values = parseValues(input7.values);
15280
- return await this.addFieldToSources(workspace, { ...input7, type: `${capitalize11(input7.field ?? "")}` }, { enumValues: values });
15921
+ return await this.addFieldToSources(workspace, { ...input7, type: `${capitalize12(input7.field ?? "")}` }, { enumValues: values });
15281
15922
  }
15282
15923
  async addFieldToSources(workspace, input7, { enumValues }) {
15283
15924
  const sys3 = await this.resolveSys(workspace, input7.app);
@@ -15321,10 +15962,12 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
15321
15962
  nextActions: []
15322
15963
  });
15323
15964
  }
15324
- const moduleClassName = capitalize11(input7.module);
15965
+ const moduleClassName = capitalize12(input7.module);
15325
15966
  const inputClassName = `${moduleClassName}Input`;
15326
- const constantPath = `lib/${input7.module}/${input7.module}.constant.ts`;
15327
- const dictionaryPath = `lib/${input7.module}/${input7.module}.dictionary.ts`;
15967
+ const paths = moduleSourcePaths(input7.module);
15968
+ const constantPath = paths.constant;
15969
+ const dictionaryPath = paths.dictionary;
15970
+ const templatePath = paths.template;
15328
15971
  const changedFiles = [];
15329
15972
  const generatedFiles2 = generatedFilesForSync(sys3);
15330
15973
  const [hasConstant, hasDictionary] = await Promise.all([sys3.exists(constantPath), sys3.exists(dictionaryPath)]);
@@ -15354,6 +15997,7 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
15354
15997
  }
15355
15998
  let constantContent = await sys3.readFile(constantPath);
15356
15999
  let dictionaryContent = await sys3.readFile(dictionaryPath);
16000
+ const fieldBuilderName = viaBuilderParameterName(constantContent, inputClassName) ?? "field";
15357
16001
  if (new RegExp(`\\b${input7.field}\\s*:`).test(constantContent)) {
15358
16002
  diagnostics.push({
15359
16003
  severity: "error",
@@ -15363,8 +16007,8 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
15363
16007
  });
15364
16008
  }
15365
16009
  if (enumValues) {
15366
- const enumClassName = `${moduleClassName}${capitalize11(input7.field)}`;
15367
- const enumName = `${lowerlize(moduleClassName)}${capitalize11(input7.field)}`;
16010
+ const enumClassName = `${moduleClassName}${capitalize12(input7.field)}`;
16011
+ const enumName = `${lowerlize(moduleClassName)}${capitalize12(input7.field)}`;
15368
16012
  constantContent = insertEnumClass(ensureEnumImport(constantContent), enumClassName, enumName, enumValues);
15369
16013
  dictionaryContent = ensureConstantTypeImport(dictionaryContent, `./${input7.module}.constant`, enumClassName);
15370
16014
  input7.type = enumClassName;
@@ -15386,9 +16030,15 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
15386
16030
  diagnostics.push(defaultCoercion.diagnostic);
15387
16031
  constantContent = ensureBaseTypeImport(constantContent, input7.type);
15388
16032
  }
15389
- const nextConstantContent = insertIntoObject(constantContent, inputClassName, `${input7.field}: ${fieldExpression(input7.type, input7.defaultValue)},`);
16033
+ if (enumValues) {
16034
+ const defaultCoercion = coerceFieldDefault("enum", input7.defaultValue, { enumValues });
16035
+ if (defaultCoercion.diagnostic)
16036
+ diagnostics.push(defaultCoercion.diagnostic);
16037
+ }
16038
+ const nextConstantContent = insertIntoObject(constantContent, inputClassName, `${input7.field}: ${fieldExpression(input7.type, input7.defaultValue, { enumValues, builderName: fieldBuilderName })},`);
16039
+ const nextConstantContentWithLight = nextConstantContent && input7.includeInLight ? insertLightProjectionField(nextConstantContent, moduleClassName, input7.field) : nextConstantContent;
15390
16040
  const nextDictionaryContent = insertDictionaryModelField(dictionaryContent, moduleClassName, input7.field);
15391
- if (nextConstantContent && (input7.type === "Int" || input7.type === "Float") && new RegExp(`\\b${input7.field}\\s*:\\s*field\\(${input7.type}, \\{ default: "`, "m").test(nextConstantContent)) {
16041
+ if (nextConstantContentWithLight && (input7.type === "Int" || input7.type === "Float") && new RegExp(`\\b${input7.field}\\s*:\\s*field\\(${input7.type}, \\{ default: "`, "m").test(nextConstantContentWithLight)) {
15392
16042
  diagnostics.push({
15393
16043
  severity: "error",
15394
16044
  code: "primitive-default-value-invalid",
@@ -15397,7 +16047,7 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
15397
16047
  message: `Generated ${input7.type} default for "${input7.field}" would be a string literal; refusing to write source.`
15398
16048
  });
15399
16049
  }
15400
- if (nextConstantContent && (input7.type === "Int" || input7.type === "Float") && !new RegExp(`import \\{[^}]*\\b${input7.type}\\b[^}]*\\} from "akanjs/base";`).test(nextConstantContent)) {
16050
+ if (nextConstantContentWithLight && (input7.type === "Int" || input7.type === "Float") && !new RegExp(`import \\{[^}]*\\b${input7.type}\\b[^}]*\\} from "akanjs/base";`).test(nextConstantContentWithLight)) {
15401
16051
  diagnostics.push({
15402
16052
  severity: "error",
15403
16053
  code: "primitive-base-type-import-missing",
@@ -15412,6 +16062,14 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
15412
16062
  message: `Could not find ${inputClassName} object shape in ${constantPath}.`
15413
16063
  });
15414
16064
  }
16065
+ if (nextConstantContent && input7.includeInLight && !nextConstantContentWithLight) {
16066
+ diagnostics.push({
16067
+ severity: "warning",
16068
+ code: "primitive-light-projection-shape-unsupported",
16069
+ failureScope: "source-change",
16070
+ message: `Could not find a safe Light${moduleClassName} projection insertion point in ${constantPath}.`
16071
+ });
16072
+ }
15415
16073
  if (!nextDictionaryContent) {
15416
16074
  diagnostics.push({
15417
16075
  severity: "error",
@@ -15419,10 +16077,51 @@ class PrimitiveScript extends script("primitive", [ModuleScript]) {
15419
16077
  message: `Could not find ${moduleClassName} dictionary model shape in ${dictionaryPath}.`
15420
16078
  });
15421
16079
  }
15422
- if (!diagnostics.some((diagnostic) => diagnostic.severity === "error") && nextConstantContent && nextDictionaryContent) {
15423
- await sys3.writeFile(constantPath, nextConstantContent);
16080
+ let nextTemplateContent;
16081
+ if (input7.surfaces?.includes("template")) {
16082
+ const policy = addFieldUiPolicyForType(enumValues ? "enum" : input7.type);
16083
+ const hasTemplate = await sys3.exists(templatePath);
16084
+ if (!hasTemplate) {
16085
+ diagnostics.push({
16086
+ severity: "warning",
16087
+ code: "primitive-template-missing",
16088
+ failureScope: "source-change",
16089
+ message: `Template source file was not found: ${templatePath}. Auto-edit skipped; add the field to the Template form manually if this module renders one.`
16090
+ });
16091
+ } else if (!policy.autoTemplateSupported || policy.component === "Field.ToggleSelect") {
16092
+ diagnostics.push({
16093
+ severity: "warning",
16094
+ code: "primitive-template-component-manual",
16095
+ failureScope: "source-change",
16096
+ message: `Template auto insertion for ${policy.component} is not supported because option binding must be confirmed. Candidate position: inside Layout.Template near existing Field components in ${templatePath}.`
16097
+ });
16098
+ } else {
16099
+ const templateContent = await sys3.readFile(templatePath);
16100
+ nextTemplateContent = insertTemplateField({
16101
+ content: templateContent,
16102
+ moduleName: input7.module,
16103
+ moduleClassName,
16104
+ fieldName: input7.field,
16105
+ component: policy.component
16106
+ });
16107
+ if (!nextTemplateContent) {
16108
+ diagnostics.push({
16109
+ severity: "warning",
16110
+ code: "primitive-template-shape-unsupported",
16111
+ failureScope: "source-change",
16112
+ message: `Could not find a safe Template insertion point in ${templatePath}. Expected generated ${input7.module}Form hook and Layout.Template closing tag; candidate position is the existing field list before </Layout.Template>.`
16113
+ });
16114
+ }
16115
+ }
16116
+ }
16117
+ if (!diagnostics.some((diagnostic) => diagnostic.severity === "error") && nextConstantContentWithLight && nextDictionaryContent) {
16118
+ await sys3.writeFile(constantPath, nextConstantContentWithLight);
15424
16119
  await sys3.writeFile(dictionaryPath, nextDictionaryContent);
15425
16120
  changedFiles.push(sourceFile(sys3, constantPath, "modify", "Field source shape was updated."), sourceFile(sys3, dictionaryPath, "modify", "Field dictionary labels were updated."));
16121
+ if (nextTemplateContent !== undefined && nextTemplateContent !== null) {
16122
+ await sys3.writeFile(templatePath, nextTemplateContent);
16123
+ changedFiles.push(sourceFile(sys3, templatePath, "modify", "Template field surface was updated."));
16124
+ }
15426
16125
  }
15427
16126
  return createPrimitiveWriteReport({
15428
16127
  command: enumValues ? "add-enum-field" : "add-field",
@@ -15791,7 +16490,7 @@ class ScalarScript extends script("scalar", [ScalarRunner]) {
15791
16490
  // pkgs/@akanjs/cli/workflow/workflow.runner.ts
15792
16491
  import { mkdir as mkdir12, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
15793
16492
  import path42 from "path";
15794
- import { capitalize as capitalize12 } from "akanjs/common";
16493
+ import { capitalize as capitalize13 } from "akanjs/common";
15795
16494
 
15796
16495
  // pkgs/@akanjs/cli/workflows/shared.ts
15797
16496
  var sysInputs = {
@@ -15907,7 +16606,15 @@ var addFieldWorkflowSpec = {
15907
16606
  values: { type: "string-list", description: "Comma-separated enum values when type is enum." },
15908
16607
  default: {
15909
16608
  type: "string",
15910
- description: "Optional default value. apply_workflow coerces by field type: Int/Float to numeric literals, Boolean to true/false, String/enum/scalar to string literals; invalid numeric or boolean defaults fail before source is written."
16609
+ description: "Optional default value. plan/apply coerce by field type: Int/Float to numeric literals, Boolean to true/false, Date to new Date(...), String/scalar to string literals, and enum only when the value is in values."
16610
+ },
16611
+ surfaces: {
16612
+ type: "string-list",
16613
+ description: "Optional UI surfaces to update, comma-separated. Use template to auto-update simple generated Template forms; View/Unit stay planned as manual review."
16614
+ },
16615
+ includeInLight: {
16616
+ type: "boolean",
16617
+ description: "Whether to add the field to the Light<Model> projection used by list/card displays."
15911
16618
  }
15912
16619
  },
15913
16620
  optionalSurfaces: {
@@ -16430,7 +17137,7 @@ var planInputString2 = (plan2, key) => {
16430
17137
  var workflowPathsForPlanLike = (plan2) => {
16431
17138
  const app = planInputString2(plan2, "app");
16432
17139
  const module = planInputString2(plan2, "module");
16433
- const moduleClass = module ? capitalize12(module) : "<Module>";
17140
+ const moduleClass = module ? capitalize13(module) : "<Module>";
16434
17141
  return plan2.predictedChanges.map((change) => change.target.replace(/^\*\//, app ? `apps/${app}/` : "").replaceAll("<module>", module || "<module>").replaceAll("<Module>", moduleClass));
16435
17142
  };
16436
17143
  var workflowDiagnosticFromDoctor = (diagnostic, fallbackScope) => ({
@@ -16441,6 +17148,43 @@ var workflowDiagnosticFromDoctor = (diagnostic, fallbackScope) => ({
16441
17148
  failureScope: (diagnostic.scope ?? fallbackScope) === "baseline" ? "workspace-config" : (diagnostic.scope ?? fallbackScope) === "workflow" ? "source-change" : "unknown",
16442
17149
  context: diagnostic.context
16443
17150
  });
17151
+ var baselineBlockerCachePath = (workflow2) => `.akan/workflows/baseline/${workflow2}.json`;
17152
+ var blockerFingerprint = (blocker) => [blocker.failureScope, blocker.code, blocker.command ?? "", blocker.kind ?? "", blocker.message].join("|");
17153
+ var applyBaselineBlockerCache = async (workspace, report) => {
17154
+ const cacheableBlockers = report.knownBlockers.filter((blocker) => blocker.failureScope === "workspace-config" || blocker.failureScope === "environment");
17155
+ if (cacheableBlockers.length === 0)
17156
+ return report;
17157
+ const cachePath = baselineBlockerCachePath(report.workflow);
17158
+ const cached = (await workspace.exists(cachePath) ? await workspace.readJson(cachePath) : null) ?? { schemaVersion: 1, workflow: report.workflow, blockers: [] };
17159
+ const knownFingerprints = new Set(cached.blockers.map((blocker) => blocker.fingerprint));
17160
+ const now = new Date().toISOString();
17161
+ const blockers = new Map(cached.blockers.map((blocker) => [blocker.fingerprint, blocker]));
17162
+ for (const blocker of cacheableBlockers) {
17163
+ const fingerprint = blockerFingerprint(blocker);
17164
+ blockers.set(fingerprint, {
17165
+ fingerprint,
17166
+ code: blocker.code,
17167
+ message: blocker.message,
17168
+ failureScope: blocker.failureScope,
17169
+ command: blocker.command,
17170
+ kind: blocker.kind,
17171
+ lastSeenAt: now
17172
+ });
17173
+ }
17174
+ await workspace.writeFile(cachePath, jsonText({ schemaVersion: 1, workflow: report.workflow, blockers: [...blockers.values()] }), { silent: true });
17175
+ return {
17176
+ ...report,
17177
+ knownBlockers: report.knownBlockers.map((blocker) => {
17178
+ if (!knownFingerprints.has(blockerFingerprint(blocker)))
17179
+ return blocker;
17180
+ return {
17181
+ ...blocker,
17182
+ known: true,
17183
+ message: `Known baseline blocker, unrelated to this source change: ${blocker.message}`
17184
+ };
17185
+ })
17186
+ };
17187
+ };
16444
17188
 
16445
17189
  class WorkflowRunner extends runner("workflow") {
16446
17190
  list({ format = "markdown" } = {}) {
@@ -16529,7 +17273,7 @@ class WorkflowRunner extends runner("workflow") {
16529
17273
  ], plan2);
16530
17274
  return await renderApplyReport(report);
16531
17275
  }
16532
- return await renderApplyReport(await new WorkflowExecutor(registry).apply(plan2));
17276
+ return await renderApplyReport(await new WorkflowExecutor(registry, workspace).apply(plan2));
16533
17277
  }
16534
17278
  async validate(runIdOrPlan, {
16535
17279
  format = "markdown",
@@ -16542,7 +17286,7 @@ class WorkflowRunner extends runner("workflow") {
16542
17286
  runIdOrPlan,
16543
17287
  changedFiles: loaded.changedFiles
16544
17288
  });
16545
- const report = await createWorkflowValidationRunReport({
17289
+ const report = await applyBaselineBlockerCache(workspace, await createWorkflowValidationRunReport({
16546
17290
  workflow: loaded.plan?.workflow ?? loaded.workflow,
16547
17291
  source: loaded.source,
16548
17292
  plan: loaded.plan,
@@ -16552,7 +17296,7 @@ class WorkflowRunner extends runner("workflow") {
16552
17296
  baselineDiagnostics: (doctor.baselineDiagnostics ?? []).map((diagnostic) => workflowDiagnosticFromDoctor(diagnostic, "baseline")),
16553
17297
  workflowDiagnostics: (doctor.workflowDiagnostics ?? []).map((diagnostic) => workflowDiagnosticFromDoctor(diagnostic, "workflow")),
16554
17298
  repairActions: loaded.repairActions
16555
- });
17299
+ }));
16556
17300
  await writeWorkflowRunArtifact(workspace, report);
16557
17301
  return renderWorkflowValidation(report, format);
16558
17302
  }