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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3396,28 +3396,28 @@ class SysExecutor extends Executor {
3396
3396
  return scalarModules;
3397
3397
  }
3398
3398
  async getViewComponents() {
3399
- const viewComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.View.tsx`).exists());
3399
+ const viewComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${capitalize(name)}.View.tsx`).exists());
3400
3400
  return viewComponents;
3401
3401
  }
3402
3402
  async getUnitComponents() {
3403
- const unitComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.Unit.tsx`).exists());
3403
+ const unitComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${capitalize(name)}.Unit.tsx`).exists());
3404
3404
  return unitComponents;
3405
3405
  }
3406
3406
  async getTemplateComponents() {
3407
- const templateComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.Template.tsx`).exists());
3407
+ const templateComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${capitalize(name)}.Template.tsx`).exists());
3408
3408
  return templateComponents;
3409
3409
  }
3410
3410
  async getViewsSourceCode() {
3411
3411
  const viewComponents = await this.getViewComponents();
3412
- return Promise.all(viewComponents.map((viewComponent) => this.getLocalFile(`lib/${viewComponent}/${viewComponent}.View.tsx`)));
3412
+ return Promise.all(viewComponents.map((viewComponent) => this.getLocalFile(`lib/${viewComponent}/${capitalize(viewComponent)}.View.tsx`)));
3413
3413
  }
3414
3414
  async getUnitsSourceCode() {
3415
3415
  const unitComponents = await this.getUnitComponents();
3416
- return Promise.all(unitComponents.map((unitComponent) => this.getLocalFile(`lib/${unitComponent}/${unitComponent}.Unit.tsx`)));
3416
+ return Promise.all(unitComponents.map((unitComponent) => this.getLocalFile(`lib/${unitComponent}/${capitalize(unitComponent)}.Unit.tsx`)));
3417
3417
  }
3418
3418
  async getTemplatesSourceCode() {
3419
3419
  const templateComponents = await this.getTemplateComponents();
3420
- return Promise.all(templateComponents.map((templateComponent) => this.getLocalFile(`lib/${templateComponent}/${templateComponent}.Template.tsx`)));
3420
+ return Promise.all(templateComponents.map((templateComponent) => this.getLocalFile(`lib/${templateComponent}/${capitalize(templateComponent)}.Template.tsx`)));
3421
3421
  }
3422
3422
  async getScalarConstantFiles() {
3423
3423
  const scalarModules = await this.getScalarModules();
@@ -4889,6 +4889,12 @@ var statusForScope = (commands, diagnostics, scopes, expectedScope) => {
4889
4889
  return "unknown";
4890
4890
  return hasScopeFailure(scopes, expectedScope, diagnostics) ? "failed" : "passed";
4891
4891
  };
4892
+ var statusForValidationKind = (commands, kind) => {
4893
+ const matching = commands.filter((command) => command.kind === kind);
4894
+ if (matching.length === 0)
4895
+ return "unknown";
4896
+ return matching.some((command) => command.status === "failed") ? "failed" : "passed";
4897
+ };
4892
4898
  var createKnownBlockers = (commands, diagnostics) => {
4893
4899
  const commandBlockers = commands.filter((command) => command.status === "failed" && (command.failureScope === "workspace-config" || command.failureScope === "environment")).map((command) => ({
4894
4900
  code: `workflow-validation-${command.failureScope}`,
@@ -4922,7 +4928,17 @@ var createValidationStatuses = (commands, diagnostics) => {
4922
4928
  const sourceStatus = statusForScope(commands, diagnostics, scopes, "source-change");
4923
4929
  const workspaceStatus = hasScopeFailure(scopes, "workspace-config", diagnostics) || hasScopeFailure(scopes, "environment", diagnostics) ? "failed" : commands.length || diagnostics.length ? "passed" : "unknown";
4924
4930
  const overallStatus = hasScopeFailure(scopes, "source-change", diagnostics) ? "failed" : hasScopeFailure(scopes, "workspace-config", diagnostics) ? "blocked-by-workspace-config" : hasScopeFailure(scopes, "environment", diagnostics) ? "blocked-by-environment" : workflowStatus(diagnostics) === "failed" || commandStatus(commands) === "failed" ? "failed" : "passed";
4925
- return { sourceStatus, workspaceStatus, overallStatus };
4931
+ return {
4932
+ sourceStatus,
4933
+ workspaceStatus,
4934
+ overallStatus,
4935
+ summary: {
4936
+ sourceChange: sourceStatus,
4937
+ generatedSync: statusForValidationKind(commands, "sync"),
4938
+ workspaceConfig: statusForScope(commands, diagnostics, scopes, "workspace-config"),
4939
+ environment: statusForScope(commands, diagnostics, scopes, "environment")
4940
+ }
4941
+ };
4926
4942
  };
4927
4943
  var createWorkflowValidationRunReport = async ({
4928
4944
  runId = createWorkflowRunId("validation"),
@@ -5032,221 +5048,6 @@ var createRepairReport = ({
5032
5048
  });
5033
5049
  // pkgs/@akanjs/devkit/workflow/executor.ts
5034
5050
  import { capitalize as capitalize2 } from "akanjs/common";
5035
- var workflowStepKey = (workflow, stepId) => `${workflow}:${stepId}`;
5036
- var primitiveReportToWorkflowStepResult = (report) => ({
5037
- changedFiles: report.changedFiles,
5038
- generatedFiles: report.generatedFiles,
5039
- commands: report.validationCommands,
5040
- diagnostics: report.diagnostics,
5041
- recommendations: [],
5042
- nextActions: report.nextActions
5043
- });
5044
- var workflowStringInput = (value) => typeof value === "string" ? value : null;
5045
- var workflowStringListInput = (value) => Array.isArray(value) ? value.join(",") : null;
5046
- var resolveWorkflowSys = async (workspace, target) => {
5047
- if (!target)
5048
- return null;
5049
- const [apps, libs] = await workspace.getSyss();
5050
- if (apps.includes(target))
5051
- return AppExecutor.from(workspace, target);
5052
- if (libs.includes(target))
5053
- return LibExecutor.from(workspace, target);
5054
- return null;
5055
- };
5056
- var targetMissing = (input2 = "app") => ({
5057
- severity: "error",
5058
- code: "workflow-target-missing",
5059
- input: input2,
5060
- message: "Workflow target app or library was not found."
5061
- });
5062
- var inputMissing = (input2) => ({
5063
- severity: "error",
5064
- code: "workflow-input-missing",
5065
- input: input2,
5066
- message: `Workflow input "${input2}" is required for apply.`
5067
- });
5068
- var unsupportedInput = (input2, message) => ({
5069
- severity: "error",
5070
- code: "workflow-input-unsupported",
5071
- input: input2,
5072
- message
5073
- });
5074
- var addFieldUiSurfaceInspection = (plan) => {
5075
- const app = workflowStringInput(plan.inputs.app);
5076
- const module = workflowStringInput(plan.inputs.module) ?? "<module>";
5077
- const field = workflowStringInput(plan.inputs.field) ?? "<field>";
5078
- const typeName = workflowStringInput(plan.inputs.type);
5079
- const isNumeric = typeName === "Int" || typeName === "Float" || typeName === "integer" || typeName === "float";
5080
- const moduleClassName = capitalize2(module);
5081
- const target = `${app ? `apps/${app}` : "*"}/lib/${module}/${moduleClassName}.Template.tsx`;
5082
- return {
5083
- recommendations: [
5084
- {
5085
- code: "add-field-ui-surface-review",
5086
- kind: "manual-action",
5087
- target,
5088
- 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.`,
5089
- confidence: "medium",
5090
- 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.`
5091
- }
5092
- ],
5093
- nextActions: [
5094
- {
5095
- command: `akan workflow explain ${plan.workflow}`,
5096
- reason: "Review UI surface guidance before manually editing ambiguous UI files."
5097
- }
5098
- ]
5099
- };
5100
- };
5101
- var createWorkflowStepRegistry = ({
5102
- workspace,
5103
- createModule,
5104
- createScalar,
5105
- createUi,
5106
- addField,
5107
- addEnumField
5108
- }) => {
5109
- const inspect = async () => {
5110
- return;
5111
- };
5112
- const commandOnly = async () => {
5113
- return;
5114
- };
5115
- return {
5116
- inspectSystem: inspect,
5117
- inspectModule: inspect,
5118
- syncTarget: commandOnly,
5119
- lintTarget: commandOnly,
5120
- [workflowStepKey("create-module", "create-module")]: async (_step, plan) => {
5121
- const app = workflowStringInput(plan.inputs.app);
5122
- const module = workflowStringInput(plan.inputs.module);
5123
- const sys2 = await resolveWorkflowSys(workspace, app);
5124
- if (!sys2 || !module)
5125
- return { diagnostics: [!sys2 ? targetMissing() : inputMissing("module")] };
5126
- return primitiveReportToWorkflowStepResult(await createModule(sys2, module));
5127
- },
5128
- [workflowStepKey("create-scalar", "create-scalar")]: async (_step, plan) => {
5129
- const app = workflowStringInput(plan.inputs.app);
5130
- const scalar = workflowStringInput(plan.inputs.scalar);
5131
- const sys2 = await resolveWorkflowSys(workspace, app);
5132
- if (!sys2 || !scalar)
5133
- return { diagnostics: [!sys2 ? targetMissing() : inputMissing("scalar")] };
5134
- return primitiveReportToWorkflowStepResult(await createScalar(sys2, scalar));
5135
- },
5136
- [workflowStepKey("create-ui", "create-ui")]: async (_step, plan) => {
5137
- const surface = workflowStringInput(plan.inputs.surface);
5138
- if (surface !== "view" && surface !== "unit" && surface !== "template") {
5139
- return {
5140
- diagnostics: [
5141
- unsupportedInput("surface", "Workflow apply currently supports create-ui surfaces: view, unit, template.")
5142
- ]
5143
- };
5144
- }
5145
- return primitiveReportToWorkflowStepResult(await createUi({
5146
- app: workflowStringInput(plan.inputs.app),
5147
- module: workflowStringInput(plan.inputs.module),
5148
- surface
5149
- }));
5150
- },
5151
- [workflowStepKey("add-field", "update-constant")]: async (_step, plan) => {
5152
- if (workflowStringInput(plan.inputs.type)?.toLowerCase() === "enum") {
5153
- return primitiveReportToWorkflowStepResult(await addEnumField({
5154
- app: workflowStringInput(plan.inputs.app),
5155
- module: workflowStringInput(plan.inputs.module),
5156
- field: workflowStringInput(plan.inputs.field),
5157
- values: workflowStringListInput(plan.inputs.values),
5158
- defaultValue: workflowStringInput(plan.inputs.default)
5159
- }));
5160
- }
5161
- return primitiveReportToWorkflowStepResult(await addField({
5162
- app: workflowStringInput(plan.inputs.app),
5163
- module: workflowStringInput(plan.inputs.module),
5164
- field: workflowStringInput(plan.inputs.field),
5165
- type: workflowStringInput(plan.inputs.type),
5166
- defaultValue: workflowStringInput(plan.inputs.default)
5167
- }));
5168
- },
5169
- [workflowStepKey("add-field", "update-dictionary")]: inspect,
5170
- [workflowStepKey("add-field", "update-ui-surfaces")]: async (_step, plan) => addFieldUiSurfaceInspection(plan),
5171
- [workflowStepKey("add-enum-field", "update-constant")]: async (_step, plan) => primitiveReportToWorkflowStepResult(await addEnumField({
5172
- app: workflowStringInput(plan.inputs.app),
5173
- module: workflowStringInput(plan.inputs.module),
5174
- field: workflowStringInput(plan.inputs.field),
5175
- values: workflowStringListInput(plan.inputs.values),
5176
- defaultValue: workflowStringInput(plan.inputs.default)
5177
- })),
5178
- [workflowStepKey("add-enum-field", "update-dictionary")]: inspect,
5179
- [workflowStepKey("add-enum-field", "update-option")]: inspect
5180
- };
5181
- };
5182
- class WorkflowExecutor {
5183
- registry;
5184
- constructor(registry) {
5185
- this.registry = registry;
5186
- }
5187
- async apply(plan) {
5188
- const changedFiles = [];
5189
- const generatedFiles = [];
5190
- const recommendedValidationCommands = [];
5191
- const diagnostics = [...plan.diagnostics];
5192
- const recommendations = [...plan.recommendations];
5193
- const nextActions = [];
5194
- if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
5195
- return createWorkflowApplyReport({
5196
- workflow: plan.workflow,
5197
- mode: "apply",
5198
- changedFiles,
5199
- generatedFiles,
5200
- recommendedValidationCommands,
5201
- diagnostics,
5202
- recommendations,
5203
- nextActions,
5204
- plan
5205
- });
5206
- }
5207
- recommendedValidationCommands.push(...workflowCommandsForPlan(plan));
5208
- nextActions.push(...workflowCommandsForPlan(plan));
5209
- for (const step of plan.steps) {
5210
- const runner = this.registry[workflowStepKey(plan.workflow, step.id)] ?? this.registry[step.tool] ?? this.registry[step.id];
5211
- if (!runner) {
5212
- diagnostics.push({
5213
- severity: "error",
5214
- code: "workflow-step-unsupported",
5215
- message: `Workflow ${plan.workflow} step "${step.id}" is not supported by workflow apply yet.`
5216
- });
5217
- nextActions.push({
5218
- command: `akan workflow explain ${plan.workflow}`,
5219
- reason: "Review the unsupported workflow step before retrying apply."
5220
- });
5221
- break;
5222
- }
5223
- const result = await runner(step, plan);
5224
- if (!result)
5225
- continue;
5226
- changedFiles.push(...result.changedFiles ?? []);
5227
- generatedFiles.push(...result.generatedFiles ?? []);
5228
- recommendedValidationCommands.push(...result.commands ?? []);
5229
- diagnostics.push(...result.diagnostics ?? []);
5230
- recommendations.push(...result.recommendations ?? []);
5231
- nextActions.push(...result.nextActions ?? []);
5232
- if (diagnostics.some((diagnostic) => diagnostic.severity === "error"))
5233
- break;
5234
- }
5235
- return createWorkflowApplyReport({
5236
- workflow: plan.workflow,
5237
- mode: "apply",
5238
- changedFiles,
5239
- generatedFiles,
5240
- recommendedValidationCommands,
5241
- diagnostics,
5242
- recommendations,
5243
- nextActions,
5244
- plan
5245
- });
5246
- }
5247
- }
5248
- // pkgs/@akanjs/devkit/workflow/plan.ts
5249
- import { capitalize as capitalize3 } from "akanjs/common";
5250
5051
 
5251
5052
  // pkgs/@akanjs/devkit/workflow/primitive.ts
5252
5053
  var createPrimitiveWriteReport = ({
@@ -5298,6 +5099,23 @@ var sourceFile = (sys2, path10, action, reason) => ({
5298
5099
  action,
5299
5100
  reason
5300
5101
  });
5102
+ var moduleComponentName = (moduleName) => `${moduleName.slice(0, 1).toUpperCase()}${moduleName.slice(1)}`;
5103
+ var moduleSourcePaths = (moduleName) => {
5104
+ const componentName = moduleComponentName(moduleName);
5105
+ return {
5106
+ abstract: `lib/${moduleName}/${moduleName}.abstract.md`,
5107
+ constant: `lib/${moduleName}/${moduleName}.constant.ts`,
5108
+ dictionary: `lib/${moduleName}/${moduleName}.dictionary.ts`,
5109
+ service: `lib/${moduleName}/${moduleName}.service.ts`,
5110
+ signal: `lib/${moduleName}/${moduleName}.signal.ts`,
5111
+ store: `lib/${moduleName}/${moduleName}.store.ts`,
5112
+ template: `lib/${moduleName}/${componentName}.Template.tsx`,
5113
+ unit: `lib/${moduleName}/${componentName}.Unit.tsx`,
5114
+ util: `lib/${moduleName}/${componentName}.Util.tsx`,
5115
+ view: `lib/${moduleName}/${componentName}.View.tsx`,
5116
+ zone: `lib/${moduleName}/${componentName}.Zone.tsx`
5117
+ };
5118
+ };
5301
5119
  var generatedFilesForSync = (sys2, reason = "Generated files may change after sync.") => generatedFilePathsForTarget(getSysRoot(sys2), reason);
5302
5120
  var validationCommandsForTarget = (target) => [
5303
5121
  { command: `akan sync ${target}`, reason: "Refresh generated Akan files from source conventions." },
@@ -5324,6 +5142,50 @@ var createPassedPrimitiveReport = ({
5324
5142
  var scalarChangedFiles = (sys2, scalarName, files) => Object.values(files).map((file) => sourceFile(sys2, `lib/__scalar/${scalarName}/${file.filename}`, "create", "Scalar source file was created."));
5325
5143
  var titleize = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
5326
5144
  var lowerlize = (value) => `${value.slice(0, 1).toLowerCase()}${value.slice(1)}`;
5145
+ var koLabels = {
5146
+ amount: "\uAE08\uC561",
5147
+ budget: "\uC608\uC0B0",
5148
+ category: "\uCE74\uD14C\uACE0\uB9AC",
5149
+ content: "\uB0B4\uC6A9",
5150
+ count: "\uAC1C\uC218",
5151
+ createdAt: "\uC0DD\uC131\uC77C",
5152
+ date: "\uB0A0\uC9DC",
5153
+ description: "\uC124\uBA85",
5154
+ due: "\uB9C8\uAC10\uC77C",
5155
+ dueAt: "\uB9C8\uAC10\uC77C",
5156
+ email: "\uC774\uBA54\uC77C",
5157
+ enabled: "\uD65C\uC131\uD654",
5158
+ endAt: "\uC885\uB8CC\uC77C",
5159
+ id: "ID",
5160
+ name: "\uC774\uB984",
5161
+ owner: "\uB2F4\uB2F9\uC790",
5162
+ priority: "\uC6B0\uC120\uC21C\uC704",
5163
+ project: "\uD504\uB85C\uC81D\uD2B8",
5164
+ rating: "\uD3C9\uC810",
5165
+ startAt: "\uC2DC\uC791\uC77C",
5166
+ status: "\uC0C1\uD0DC",
5167
+ title: "\uC81C\uBAA9",
5168
+ updatedAt: "\uC218\uC815\uC77C"
5169
+ };
5170
+ var splitFieldWords = (fieldName) => fieldName.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").split(/\s+/).map((word) => word.trim()).filter(Boolean);
5171
+ var koLabelForField = (fieldName) => {
5172
+ if (koLabels[fieldName])
5173
+ return koLabels[fieldName];
5174
+ const words = splitFieldWords(fieldName);
5175
+ const translated = words.map((word) => koLabels[word] ?? koLabels[lowerlize(word)] ?? null);
5176
+ return translated.every(Boolean) ? translated.join(" ") : null;
5177
+ };
5178
+ var bilingualLabelForField = (fieldName) => {
5179
+ const en = titleize(fieldName);
5180
+ return { en, ko: koLabelForField(fieldName) ?? en };
5181
+ };
5182
+ var bilingualDescriptionForField = (fieldName) => {
5183
+ const label = bilingualLabelForField(fieldName);
5184
+ return {
5185
+ en: `Enter ${label.en.toLowerCase()}.`,
5186
+ ko: `${label.ko} \uAC12\uC744 \uC785\uB825\uD569\uB2C8\uB2E4.`
5187
+ };
5188
+ };
5327
5189
  var normalizeFieldType = (typeName) => {
5328
5190
  const normalizedTypes = {
5329
5191
  string: "String",
@@ -5351,6 +5213,15 @@ var ensureBaseTypeImport = (content, typeName) => {
5351
5213
  ${content}`;
5352
5214
  };
5353
5215
  var numericDefault = (typeName, rawDefault) => {
5216
+ if (typeof rawDefault === "number") {
5217
+ if (!Number.isFinite(rawDefault))
5218
+ return null;
5219
+ if (typeName === "Int" && !Number.isInteger(rawDefault))
5220
+ return null;
5221
+ return String(rawDefault);
5222
+ }
5223
+ if (typeof rawDefault !== "string")
5224
+ return null;
5354
5225
  const trimmed = rawDefault.trim();
5355
5226
  if (!trimmed || !Number.isFinite(Number(trimmed)))
5356
5227
  return null;
@@ -5358,16 +5229,41 @@ var numericDefault = (typeName, rawDefault) => {
5358
5229
  return null;
5359
5230
  return trimmed;
5360
5231
  };
5361
- var coerceFieldDefault = (typeName, defaultValue) => {
5362
- if (defaultValue === undefined || defaultValue === null || defaultValue === "")
5363
- return { expression: null };
5364
- const normalizedType = normalizeFieldType(typeName);
5232
+ var booleanDefault = (rawDefault) => {
5233
+ if (typeof rawDefault === "boolean")
5234
+ return String(rawDefault);
5235
+ if (typeof rawDefault !== "string")
5236
+ return null;
5237
+ const lowered = rawDefault.trim().toLowerCase();
5238
+ return lowered === "true" || lowered === "false" ? lowered : null;
5239
+ };
5240
+ var dateDefault = (rawDefault) => {
5241
+ if (typeof rawDefault === "number" && Number.isFinite(rawDefault))
5242
+ return `new Date(${rawDefault})`;
5243
+ if (typeof rawDefault !== "string")
5244
+ return null;
5245
+ const trimmed = rawDefault.trim();
5246
+ if (!trimmed)
5247
+ return null;
5248
+ if (trimmed === "now")
5249
+ return "new Date()";
5250
+ if (!Number.isNaN(Date.parse(trimmed)))
5251
+ return `new Date(${JSON.stringify(trimmed)})`;
5252
+ return null;
5253
+ };
5254
+ var coerceFieldDefault = (typeName, defaultValue, options = {}) => {
5255
+ const normalizedType = typeName.toLowerCase() === "enum" ? "enum" : normalizeFieldType(typeName);
5256
+ if (defaultValue === undefined || defaultValue === null || defaultValue === "") {
5257
+ return { expression: null, normalized: false, normalizedType };
5258
+ }
5365
5259
  if (normalizedType === "Int" || normalizedType === "Float") {
5366
5260
  const expression = numericDefault(normalizedType, defaultValue);
5367
5261
  if (expression !== null)
5368
- return { expression };
5262
+ return { expression, normalized: typeof defaultValue === "string", normalizedType };
5369
5263
  return {
5370
5264
  expression: null,
5265
+ normalized: false,
5266
+ normalizedType,
5371
5267
  diagnostic: {
5372
5268
  severity: "error",
5373
5269
  code: "primitive-default-value-invalid",
@@ -5378,11 +5274,13 @@ var coerceFieldDefault = (typeName, defaultValue) => {
5378
5274
  };
5379
5275
  }
5380
5276
  if (normalizedType === "Boolean") {
5381
- const lowered = defaultValue.trim().toLowerCase();
5382
- if (lowered === "true" || lowered === "false")
5383
- return { expression: lowered };
5277
+ const expression = booleanDefault(defaultValue);
5278
+ if (expression !== null)
5279
+ return { expression, normalized: typeof defaultValue === "string", normalizedType };
5384
5280
  return {
5385
5281
  expression: null,
5282
+ normalized: false,
5283
+ normalizedType,
5386
5284
  diagnostic: {
5387
5285
  severity: "error",
5388
5286
  code: "primitive-default-value-invalid",
@@ -5392,31 +5290,113 @@ var coerceFieldDefault = (typeName, defaultValue) => {
5392
5290
  }
5393
5291
  };
5394
5292
  }
5395
- return { expression: JSON.stringify(defaultValue) };
5396
- };
5397
- var fieldExpression = (typeName, defaultValue) => {
5398
- const typeExpression = normalizeFieldType(typeName);
5399
- const defaultExpression = coerceFieldDefault(typeExpression, defaultValue).expression;
5400
- const defaultOption = defaultExpression ? `, { default: ${defaultExpression} }` : "";
5401
- return `field(${typeExpression}${defaultOption})`;
5402
- };
5403
- var insertIntoObject = (content, className, line) => {
5404
- const classIndex = content.indexOf(`export class ${className} extends via`);
5405
- if (classIndex < 0)
5406
- return null;
5407
- const objectEndIndex = content.indexOf("}))", classIndex);
5408
- if (objectEndIndex < 0)
5409
- return null;
5410
- const prefix = content.slice(0, objectEndIndex);
5411
- const suffix = content.slice(objectEndIndex);
5412
- const insertion = prefix.endsWith(`
5413
- `) ? ` ${line}
5414
- ` : `
5415
- ${line}
5416
- `;
5417
- return `${prefix}${insertion}${suffix}`;
5418
- };
5419
- var ensureEnumImport = (content) => {
5293
+ if (normalizedType === "Date") {
5294
+ const expression = dateDefault(defaultValue);
5295
+ if (expression !== null)
5296
+ return { expression, normalized: true, normalizedType };
5297
+ return {
5298
+ expression: null,
5299
+ normalized: false,
5300
+ normalizedType,
5301
+ diagnostic: {
5302
+ severity: "error",
5303
+ code: "primitive-default-value-invalid",
5304
+ input: "default",
5305
+ failureScope: "source-change",
5306
+ message: `Default value for Date must be "now", a timestamp, or a parseable date string. Received: ${JSON.stringify(defaultValue)}.`
5307
+ }
5308
+ };
5309
+ }
5310
+ if (normalizedType === "enum") {
5311
+ if (typeof defaultValue === "string" && options.enumValues?.includes(defaultValue)) {
5312
+ return { expression: JSON.stringify(defaultValue), normalized: false, normalizedType };
5313
+ }
5314
+ return {
5315
+ expression: null,
5316
+ normalized: false,
5317
+ normalizedType,
5318
+ diagnostic: {
5319
+ severity: "error",
5320
+ code: "primitive-default-value-invalid",
5321
+ input: "default",
5322
+ failureScope: "source-change",
5323
+ message: `Default value for enum must be one of: ${(options.enumValues ?? []).join(", ")}. Received: ${JSON.stringify(defaultValue)}.`
5324
+ }
5325
+ };
5326
+ }
5327
+ return {
5328
+ expression: JSON.stringify(String(defaultValue)),
5329
+ normalized: typeof defaultValue !== "string",
5330
+ normalizedType
5331
+ };
5332
+ };
5333
+ var fieldExpression = (typeName, defaultValue, options = {}) => {
5334
+ const typeExpression = normalizeFieldType(typeName);
5335
+ const defaultExpression = coerceFieldDefault(options.enumValues ? "enum" : typeExpression, defaultValue, options).expression;
5336
+ const defaultOption = defaultExpression ? `, { default: ${defaultExpression} }` : "";
5337
+ return `field(${typeExpression}${defaultOption})`;
5338
+ };
5339
+ var insertIntoObject = (content, className, line) => {
5340
+ const classIndex = content.indexOf(`export class ${className} extends via`);
5341
+ if (classIndex < 0)
5342
+ return null;
5343
+ const objectEndIndex = content.indexOf("}))", classIndex);
5344
+ if (objectEndIndex < 0)
5345
+ return null;
5346
+ const prefix = content.slice(0, objectEndIndex);
5347
+ const suffix = content.slice(objectEndIndex);
5348
+ const insertion = prefix.endsWith(`
5349
+ `) ? ` ${line}
5350
+ ` : `
5351
+ ${line}
5352
+ `;
5353
+ return `${prefix}${insertion}${suffix}`;
5354
+ };
5355
+ var insertLightProjectionField = (content, moduleClassName, fieldName) => {
5356
+ const classIndex = content.indexOf(`export class Light${moduleClassName} extends via`);
5357
+ if (classIndex < 0)
5358
+ return null;
5359
+ const arrayMatch = /\[([\s\S]*?)\]\s+as const/.exec(content.slice(classIndex));
5360
+ if (!arrayMatch || arrayMatch.index === undefined)
5361
+ return null;
5362
+ const arrayStart = classIndex + arrayMatch.index;
5363
+ const arrayEnd = arrayStart + arrayMatch[0].length;
5364
+ const fields = [...arrayMatch[1].matchAll(/"([^"]+)"/g)].map((match) => match[1]).filter(Boolean);
5365
+ if (fields.includes(fieldName))
5366
+ return content;
5367
+ const nextFields = [...fields, fieldName];
5368
+ const nextArray = nextFields.length === 0 ? "[] as const" : `[
5369
+ ${nextFields.map((field) => ` ${JSON.stringify(field)},`).join(`
5370
+ `)}
5371
+ ] as const`;
5372
+ return `${content.slice(0, arrayStart)}${nextArray}${content.slice(arrayEnd)}`;
5373
+ };
5374
+ var insertTemplateField = ({
5375
+ content,
5376
+ moduleName,
5377
+ moduleClassName,
5378
+ fieldName,
5379
+ component
5380
+ }) => {
5381
+ if (content.includes(`l("${moduleName}.${fieldName}")`) || content.includes(`.${fieldName}`))
5382
+ return content;
5383
+ const layoutEndIndex = content.indexOf(" </Layout.Template>");
5384
+ if (layoutEndIndex < 0)
5385
+ return null;
5386
+ const formName = `${moduleName}Form`;
5387
+ if (!content.includes(`const ${formName} = st.use.${moduleName}Form();`))
5388
+ return null;
5389
+ const fieldSetter = `st.do.set${moduleComponentName(fieldName)}On${moduleClassName}`;
5390
+ const fieldBlock = ` <${component}
5391
+ label={l("${moduleName}.${fieldName}")}
5392
+ desc={l("${moduleName}.${fieldName}.desc")}
5393
+ value={${formName}.${fieldName}}
5394
+ onChange={${fieldSetter}}
5395
+ />
5396
+ `;
5397
+ return `${content.slice(0, layoutEndIndex)}${fieldBlock}${content.slice(layoutEndIndex)}`;
5398
+ };
5399
+ var ensureEnumImport = (content) => {
5420
5400
  if (content.includes("enumOf"))
5421
5401
  return content;
5422
5402
  const baseImport = /import \{ ([^}]+) \} from "akanjs\/base";/.exec(content);
@@ -5445,14 +5425,15 @@ ${enumClass}`;
5445
5425
  var insertDictionaryModelField = (content, moduleClassName, fieldName) => {
5446
5426
  if (new RegExp(`\\b${fieldName}\\s*:`).test(content))
5447
5427
  return content;
5448
- const label = titleize(fieldName);
5428
+ const label = bilingualLabelForField(fieldName);
5429
+ const desc = bilingualDescriptionForField(fieldName);
5449
5430
  const modelIndex = content.indexOf(`.model<${moduleClassName}>((t) => ({`);
5450
5431
  if (modelIndex < 0)
5451
5432
  return null;
5452
5433
  const objectEndIndex = content.indexOf(" }))", modelIndex);
5453
5434
  if (objectEndIndex < 0)
5454
5435
  return null;
5455
- return `${content.slice(0, objectEndIndex)} ${fieldName}: t([${JSON.stringify(label)}, ${JSON.stringify(label)}]).desc([${JSON.stringify(label)}, ${JSON.stringify(label)}]),
5436
+ return `${content.slice(0, objectEndIndex)} ${fieldName}: t([${JSON.stringify(label.en)}, ${JSON.stringify(label.ko)}]).desc([${JSON.stringify(desc.en)}, ${JSON.stringify(desc.ko)}]),
5456
5437
  ${content.slice(objectEndIndex)}`;
5457
5438
  };
5458
5439
  var ensureConstantTypeImport = (content, constantPath, typeName) => {
@@ -5483,7 +5464,273 @@ ${values.map((value) => ` ${value}: t([${JSON.stringify(titleize(value))}, ${
5483
5464
  };
5484
5465
  var parseValues = (value) => value?.split(",").map((item) => item.trim()).filter(Boolean) ?? [];
5485
5466
 
5467
+ // pkgs/@akanjs/devkit/workflow/uiPolicy.ts
5468
+ var addFieldUiPolicyForType = (typeName) => {
5469
+ const normalizedType = typeName.toLowerCase() === "enum" ? "enum" : normalizeFieldType(typeName);
5470
+ if (normalizedType === "Int" || normalizedType === "Float") {
5471
+ return {
5472
+ normalizedType,
5473
+ component: "Field.Number",
5474
+ confidence: "high",
5475
+ autoTemplateSupported: true
5476
+ };
5477
+ }
5478
+ if (normalizedType === "Date") {
5479
+ return {
5480
+ normalizedType,
5481
+ component: "Field.Date",
5482
+ confidence: "high",
5483
+ autoTemplateSupported: true
5484
+ };
5485
+ }
5486
+ if (normalizedType === "Boolean" || normalizedType === "enum") {
5487
+ return {
5488
+ normalizedType,
5489
+ component: "Field.ToggleSelect",
5490
+ confidence: "medium",
5491
+ autoTemplateSupported: false
5492
+ };
5493
+ }
5494
+ if (normalizedType === "String") {
5495
+ return {
5496
+ normalizedType,
5497
+ component: "Field.Text",
5498
+ confidence: "high",
5499
+ autoTemplateSupported: true
5500
+ };
5501
+ }
5502
+ return {
5503
+ normalizedType,
5504
+ component: "Field.Text",
5505
+ confidence: "low",
5506
+ autoTemplateSupported: false
5507
+ };
5508
+ };
5509
+
5510
+ // pkgs/@akanjs/devkit/workflow/executor.ts
5511
+ var workflowStepKey = (workflow, stepId) => `${workflow}:${stepId}`;
5512
+ var primitiveReportToWorkflowStepResult = (report) => ({
5513
+ changedFiles: report.changedFiles,
5514
+ generatedFiles: report.generatedFiles,
5515
+ commands: report.validationCommands,
5516
+ diagnostics: report.diagnostics,
5517
+ recommendations: [],
5518
+ nextActions: report.nextActions
5519
+ });
5520
+ var workflowStringInput = (value) => typeof value === "string" ? value : null;
5521
+ var workflowStringListInput = (value) => Array.isArray(value) ? value.join(",") : null;
5522
+ var workflowStringArrayInput = (value) => Array.isArray(value) ? value : null;
5523
+ var workflowBooleanInput = (value) => typeof value === "boolean" ? value : null;
5524
+ var resolveWorkflowSys = async (workspace, target) => {
5525
+ if (!target)
5526
+ return null;
5527
+ const [apps, libs] = await workspace.getSyss();
5528
+ if (apps.includes(target))
5529
+ return AppExecutor.from(workspace, target);
5530
+ if (libs.includes(target))
5531
+ return LibExecutor.from(workspace, target);
5532
+ return null;
5533
+ };
5534
+ var targetMissing = (input2 = "app") => ({
5535
+ severity: "error",
5536
+ code: "workflow-target-missing",
5537
+ input: input2,
5538
+ message: "Workflow target app or library was not found."
5539
+ });
5540
+ var inputMissing = (input2) => ({
5541
+ severity: "error",
5542
+ code: "workflow-input-missing",
5543
+ input: input2,
5544
+ message: `Workflow input "${input2}" is required for apply.`
5545
+ });
5546
+ var unsupportedInput = (input2, message) => ({
5547
+ severity: "error",
5548
+ code: "workflow-input-unsupported",
5549
+ input: input2,
5550
+ message
5551
+ });
5552
+ var addFieldUiSurfaceInspection = (plan) => {
5553
+ const app = workflowStringInput(plan.inputs.app);
5554
+ const module = workflowStringInput(plan.inputs.module) ?? "<module>";
5555
+ const field = workflowStringInput(plan.inputs.field) ?? "<field>";
5556
+ const typeName = workflowStringInput(plan.inputs.type);
5557
+ const policy = addFieldUiPolicyForType(typeName ?? "String");
5558
+ const surfaces = workflowStringArrayInput(plan.inputs.surfaces);
5559
+ const templateRequested = surfaces?.includes("template") ?? false;
5560
+ const moduleClassName = capitalize2(module);
5561
+ const target = `${app ? `apps/${app}` : "*"}/${moduleSourcePaths(module).template}`;
5562
+ return {
5563
+ recommendations: [
5564
+ {
5565
+ code: "add-field-ui-surface-review",
5566
+ kind: "manual-action",
5567
+ target,
5568
+ action: templateRequested ? `Template was requested for ${field}. If no Template file changed, auto-edit was skipped because the file was missing, the generated ${module}Form/Layout.Template pattern was not found, or ${policy.component} needs option binding. Candidate position: inside Layout.Template near the existing Field components.` : `Template was not selected, so UI files are intentionally left unchanged. Candidate positions if you expose it later: Layout.Template field list for editing, Light${moduleClassName} projection for list/card data, and Unit/View card sections for display.`,
5569
+ confidence: "medium",
5570
+ message: `Review UI surfaces for ${module}.${field}; recommended component is ${policy.component}, with manual reasons and candidate positions in action.`
5571
+ }
5572
+ ],
5573
+ nextActions: [
5574
+ {
5575
+ command: `akan workflow explain ${plan.workflow}`,
5576
+ reason: "Review UI surface guidance before manually editing ambiguous UI files."
5577
+ }
5578
+ ]
5579
+ };
5580
+ };
5581
+ var createWorkflowStepRegistry = ({
5582
+ workspace,
5583
+ createModule,
5584
+ createScalar,
5585
+ createUi,
5586
+ addField,
5587
+ addEnumField
5588
+ }) => {
5589
+ const inspect = async () => {
5590
+ return;
5591
+ };
5592
+ const commandOnly = async () => {
5593
+ return;
5594
+ };
5595
+ return {
5596
+ inspectSystem: inspect,
5597
+ inspectModule: inspect,
5598
+ syncTarget: commandOnly,
5599
+ lintTarget: commandOnly,
5600
+ [workflowStepKey("create-module", "create-module")]: async (_step, plan) => {
5601
+ const app = workflowStringInput(plan.inputs.app);
5602
+ const module = workflowStringInput(plan.inputs.module);
5603
+ const sys2 = await resolveWorkflowSys(workspace, app);
5604
+ if (!sys2 || !module)
5605
+ return { diagnostics: [!sys2 ? targetMissing() : inputMissing("module")] };
5606
+ return primitiveReportToWorkflowStepResult(await createModule(sys2, module));
5607
+ },
5608
+ [workflowStepKey("create-scalar", "create-scalar")]: async (_step, plan) => {
5609
+ const app = workflowStringInput(plan.inputs.app);
5610
+ const scalar = workflowStringInput(plan.inputs.scalar);
5611
+ const sys2 = await resolveWorkflowSys(workspace, app);
5612
+ if (!sys2 || !scalar)
5613
+ return { diagnostics: [!sys2 ? targetMissing() : inputMissing("scalar")] };
5614
+ return primitiveReportToWorkflowStepResult(await createScalar(sys2, scalar));
5615
+ },
5616
+ [workflowStepKey("create-ui", "create-ui")]: async (_step, plan) => {
5617
+ const surface = workflowStringInput(plan.inputs.surface);
5618
+ if (surface !== "view" && surface !== "unit" && surface !== "template") {
5619
+ return {
5620
+ diagnostics: [
5621
+ unsupportedInput("surface", "Workflow apply currently supports create-ui surfaces: view, unit, template.")
5622
+ ]
5623
+ };
5624
+ }
5625
+ return primitiveReportToWorkflowStepResult(await createUi({
5626
+ app: workflowStringInput(plan.inputs.app),
5627
+ module: workflowStringInput(plan.inputs.module),
5628
+ surface
5629
+ }));
5630
+ },
5631
+ [workflowStepKey("add-field", "update-constant")]: async (_step, plan) => {
5632
+ if (workflowStringInput(plan.inputs.type)?.toLowerCase() === "enum") {
5633
+ return primitiveReportToWorkflowStepResult(await addEnumField({
5634
+ app: workflowStringInput(plan.inputs.app),
5635
+ module: workflowStringInput(plan.inputs.module),
5636
+ field: workflowStringInput(plan.inputs.field),
5637
+ values: workflowStringListInput(plan.inputs.values),
5638
+ defaultValue: workflowStringInput(plan.inputs.default),
5639
+ surfaces: workflowStringArrayInput(plan.inputs.surfaces),
5640
+ includeInLight: workflowBooleanInput(plan.inputs.includeInLight)
5641
+ }));
5642
+ }
5643
+ return primitiveReportToWorkflowStepResult(await addField({
5644
+ app: workflowStringInput(plan.inputs.app),
5645
+ module: workflowStringInput(plan.inputs.module),
5646
+ field: workflowStringInput(plan.inputs.field),
5647
+ type: workflowStringInput(plan.inputs.type),
5648
+ defaultValue: workflowStringInput(plan.inputs.default),
5649
+ surfaces: workflowStringArrayInput(plan.inputs.surfaces),
5650
+ includeInLight: workflowBooleanInput(plan.inputs.includeInLight)
5651
+ }));
5652
+ },
5653
+ [workflowStepKey("add-field", "update-dictionary")]: inspect,
5654
+ [workflowStepKey("add-field", "update-ui-surfaces")]: async (_step, plan) => addFieldUiSurfaceInspection(plan),
5655
+ [workflowStepKey("add-enum-field", "update-constant")]: async (_step, plan) => primitiveReportToWorkflowStepResult(await addEnumField({
5656
+ app: workflowStringInput(plan.inputs.app),
5657
+ module: workflowStringInput(plan.inputs.module),
5658
+ field: workflowStringInput(plan.inputs.field),
5659
+ values: workflowStringListInput(plan.inputs.values),
5660
+ defaultValue: workflowStringInput(plan.inputs.default)
5661
+ })),
5662
+ [workflowStepKey("add-enum-field", "update-dictionary")]: inspect,
5663
+ [workflowStepKey("add-enum-field", "update-option")]: inspect
5664
+ };
5665
+ };
5666
+ class WorkflowExecutor {
5667
+ registry;
5668
+ constructor(registry) {
5669
+ this.registry = registry;
5670
+ }
5671
+ async apply(plan) {
5672
+ const changedFiles = [];
5673
+ const generatedFiles = [];
5674
+ const recommendedValidationCommands = [];
5675
+ const diagnostics = [...plan.diagnostics];
5676
+ const recommendations = [...plan.recommendations];
5677
+ const nextActions = [];
5678
+ if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
5679
+ return createWorkflowApplyReport({
5680
+ workflow: plan.workflow,
5681
+ mode: "apply",
5682
+ changedFiles,
5683
+ generatedFiles,
5684
+ recommendedValidationCommands,
5685
+ diagnostics,
5686
+ recommendations,
5687
+ nextActions,
5688
+ plan
5689
+ });
5690
+ }
5691
+ recommendedValidationCommands.push(...workflowCommandsForPlan(plan));
5692
+ nextActions.push(...workflowCommandsForPlan(plan));
5693
+ for (const step of plan.steps) {
5694
+ const runner = this.registry[workflowStepKey(plan.workflow, step.id)] ?? this.registry[step.tool] ?? this.registry[step.id];
5695
+ if (!runner) {
5696
+ diagnostics.push({
5697
+ severity: "error",
5698
+ code: "workflow-step-unsupported",
5699
+ message: `Workflow ${plan.workflow} step "${step.id}" is not supported by workflow apply yet.`
5700
+ });
5701
+ nextActions.push({
5702
+ command: `akan workflow explain ${plan.workflow}`,
5703
+ reason: "Review the unsupported workflow step before retrying apply."
5704
+ });
5705
+ break;
5706
+ }
5707
+ const result = await runner(step, plan);
5708
+ if (!result)
5709
+ continue;
5710
+ changedFiles.push(...result.changedFiles ?? []);
5711
+ generatedFiles.push(...result.generatedFiles ?? []);
5712
+ recommendedValidationCommands.push(...result.commands ?? []);
5713
+ diagnostics.push(...result.diagnostics ?? []);
5714
+ recommendations.push(...result.recommendations ?? []);
5715
+ nextActions.push(...result.nextActions ?? []);
5716
+ if (diagnostics.some((diagnostic) => diagnostic.severity === "error"))
5717
+ break;
5718
+ }
5719
+ return createWorkflowApplyReport({
5720
+ workflow: plan.workflow,
5721
+ mode: "apply",
5722
+ changedFiles,
5723
+ generatedFiles,
5724
+ recommendedValidationCommands,
5725
+ diagnostics,
5726
+ recommendations,
5727
+ nextActions,
5728
+ plan
5729
+ });
5730
+ }
5731
+ }
5486
5732
  // pkgs/@akanjs/devkit/workflow/plan.ts
5733
+ import { capitalize as capitalize3 } from "akanjs/common";
5487
5734
  var surfaceModes = new Set(["infer", "include", "skip"]);
5488
5735
  var parseStringList = (value) => {
5489
5736
  if (Array.isArray(value)) {
@@ -5501,6 +5748,18 @@ var normalizeInputValue = (name, spec, value) => {
5501
5748
  const values = parseStringList(value);
5502
5749
  return values && values.length > 0 ? values : null;
5503
5750
  }
5751
+ if (spec.type === "boolean") {
5752
+ if (typeof value === "boolean")
5753
+ return value;
5754
+ if (typeof value !== "string")
5755
+ return null;
5756
+ const lowered = value.trim().toLowerCase();
5757
+ if (lowered === "true")
5758
+ return true;
5759
+ if (lowered === "false")
5760
+ return false;
5761
+ return null;
5762
+ }
5504
5763
  if (typeof value === "string" && surfaceModes.has(value))
5505
5764
  return value;
5506
5765
  throw new Error(`Unsupported workflow input value for ${name}`);
@@ -5508,17 +5767,45 @@ var normalizeInputValue = (name, spec, value) => {
5508
5767
  var listWorkflowSpecs = (specs) => [...specs].sort((a, b) => a.name.localeCompare(b.name));
5509
5768
  var getWorkflowSpec = (specs, name) => specs.find((spec) => spec.name === name) ?? null;
5510
5769
  var compactWorkflowInputs = (inputs) => Object.fromEntries(Object.entries(inputs).filter(([, value]) => value !== null && value !== ""));
5511
- var addFieldComponentForType = (typeName) => {
5512
- const normalizedType = normalizeFieldType(typeName);
5513
- if (normalizedType === "Boolean")
5514
- return "Field.ToggleSelect";
5515
- if (normalizedType === "Date")
5516
- return "Field.Date";
5517
- if (normalizedType === "Int" || normalizedType === "Float")
5518
- return "manual numeric input review";
5519
- if (typeName.toLowerCase() === "enum")
5520
- return "Field.ToggleSelect";
5521
- return "Field.Text";
5770
+ var addFieldTargetRoot = (app) => app ? `apps/${app}` : "*";
5771
+ var addFieldPaths = (inputs) => {
5772
+ const app = typeof inputs.app === "string" ? inputs.app : null;
5773
+ const module = typeof inputs.module === "string" ? inputs.module : "<module>";
5774
+ const paths = moduleSourcePaths(module);
5775
+ const root = addFieldTargetRoot(app);
5776
+ return {
5777
+ constant: `${root}/${paths.constant}`,
5778
+ dictionary: `${root}/${paths.dictionary}`,
5779
+ template: `${root}/${paths.template}`,
5780
+ unit: `${root}/${paths.unit}`,
5781
+ view: `${root}/${paths.view}`
5782
+ };
5783
+ };
5784
+ var selectedAddFieldSurfaces = (inputs) => Array.isArray(inputs.surfaces) ? new Set(inputs.surfaces) : null;
5785
+ var addFieldDefaultCoercion = (inputs) => {
5786
+ const typeName = typeof inputs.type === "string" ? inputs.type : null;
5787
+ const defaultValue = typeof inputs.default === "string" ? inputs.default : null;
5788
+ if (!typeName || defaultValue === null)
5789
+ return null;
5790
+ if (typeName.toLowerCase() === "number" || typeName.toLowerCase() === "numeric")
5791
+ return null;
5792
+ const enumValues = Array.isArray(inputs.values) ? inputs.values : null;
5793
+ return coerceFieldDefault(typeName.toLowerCase() === "enum" ? "enum" : typeName, defaultValue, { enumValues });
5794
+ };
5795
+ var createAddFieldDefaultRecommendations = (inputs) => {
5796
+ const field = typeof inputs.field === "string" ? inputs.field : "<field>";
5797
+ const coercion = addFieldDefaultCoercion(inputs);
5798
+ if (!coercion?.normalized || !coercion.expression)
5799
+ return [];
5800
+ return [
5801
+ {
5802
+ code: "add-field-default-normalized",
5803
+ kind: "manual-action",
5804
+ confidence: "high",
5805
+ message: `Default for ${field} will be normalized to ${coercion.normalizedType} literal ${coercion.expression}.`,
5806
+ action: "Review the normalized default in the apply report if this value should stay unset."
5807
+ }
5808
+ ];
5522
5809
  };
5523
5810
  var createAddFieldRecommendations = (inputs) => {
5524
5811
  const app = typeof inputs.app === "string" ? inputs.app : "<app-or-lib>";
@@ -5527,16 +5814,18 @@ var createAddFieldRecommendations = (inputs) => {
5527
5814
  const typeName = typeof inputs.type === "string" ? inputs.type : null;
5528
5815
  if (!typeName)
5529
5816
  return [];
5530
- const normalizedType = typeName.toLowerCase() === "enum" ? "enum" : normalizeFieldType(typeName);
5531
- const constantPath = `*/lib/${module}/${module}.constant.ts`;
5532
- const dictionaryPath = `*/lib/${module}/${module}.dictionary.ts`;
5533
- const templatePath = `*/lib/${module}/${capitalize3(module)}.Template.tsx`;
5817
+ const policy = addFieldUiPolicyForType(typeName);
5818
+ const normalizedType = policy.normalizedType;
5819
+ const paths = addFieldPaths(inputs);
5820
+ const surfaces = selectedAddFieldSurfaces(inputs);
5821
+ const templateRequested = surfaces?.has("template") ?? false;
5822
+ const includeInLight = inputs.includeInLight === true;
5534
5823
  return [
5535
5824
  ...normalizedType === "Int" || normalizedType === "Float" ? [
5536
5825
  {
5537
5826
  code: "add-field-import",
5538
5827
  kind: "import",
5539
- target: constantPath,
5828
+ target: paths.constant,
5540
5829
  confidence: "high",
5541
5830
  message: `Import ${normalizedType} from "akanjs/base" before writing field(${normalizedType}).`
5542
5831
  }
@@ -5544,35 +5833,122 @@ var createAddFieldRecommendations = (inputs) => {
5544
5833
  {
5545
5834
  code: "add-field-placement-constant",
5546
5835
  kind: "placement",
5547
- target: constantPath,
5836
+ target: paths.constant,
5548
5837
  confidence: "high",
5549
5838
  message: `Insert ${field}: field(${normalizedType}) in ${capitalize3(module)}Input.`
5550
5839
  },
5551
5840
  {
5552
5841
  code: "add-field-placement-dictionary",
5553
5842
  kind: "placement",
5554
- target: dictionaryPath,
5843
+ target: paths.dictionary,
5555
5844
  confidence: "high",
5556
5845
  message: `Add dictionary labels for ${module}.${field}.`
5557
5846
  },
5558
5847
  {
5559
5848
  code: "add-field-component",
5560
5849
  kind: "ui-component",
5561
- target: templatePath,
5562
- confidence: normalizedType === "Int" || normalizedType === "Float" ? "low" : "high",
5563
- 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,
5564
- 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)}.`
5850
+ target: paths.template,
5851
+ confidence: policy.confidence,
5852
+ action: templateRequested ? `apply_workflow will try to add ${policy.component} to Template when the existing Template has a safe generated field-list pattern.` : `Recommended component is ${policy.component}. Pass surfaces=["template"] when this field should be rendered in the Template form.`,
5853
+ message: `Recommended UI component for ${field} (${normalizedType}): ${policy.component}.`
5565
5854
  },
5855
+ ...!surfaces ? [
5856
+ {
5857
+ code: "add-field-template-surface-choice",
5858
+ kind: "manual-action",
5859
+ target: paths.template,
5860
+ action: `Choose whether to expose ${field} in Template by passing surfaces=["template"].`,
5861
+ confidence: "medium",
5862
+ message: `Template exposure for ${module}.${field} is not selected yet.`
5863
+ }
5864
+ ] : [],
5865
+ ...!includeInLight ? [
5866
+ {
5867
+ code: "add-field-light-projection-choice",
5868
+ kind: "manual-action",
5869
+ target: paths.constant,
5870
+ action: `Pass includeInLight=true when ${field} should appear in Light${capitalize3(module)} list/card projections.`,
5871
+ confidence: "medium",
5872
+ message: `Light projection exposure for ${module}.${field} is not selected yet.`
5873
+ }
5874
+ ] : [],
5875
+ ...includeInLight ? [
5876
+ {
5877
+ code: "add-field-light-projection",
5878
+ kind: "placement",
5879
+ target: paths.constant,
5880
+ confidence: "high",
5881
+ message: `Add ${field} to Light${capitalize3(module)} projection fields.`
5882
+ }
5883
+ ] : [],
5884
+ ...surfaces && !templateRequested ? [
5885
+ {
5886
+ code: "add-field-ui-surface-skip",
5887
+ kind: "manual-action",
5888
+ target: paths.template,
5889
+ confidence: "medium",
5890
+ message: `Template is not included in surfaces, so ${module}.${field} will not be auto-rendered there.`
5891
+ }
5892
+ ] : [],
5566
5893
  {
5567
5894
  code: "add-field-ui-manual-review",
5568
5895
  kind: "manual-action",
5569
- target: templatePath,
5570
- 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.`,
5896
+ target: paths.template,
5897
+ action: `Review ${app}:${module} UI after apply. Template auto-edit requires a generated ${module}Form hook and Layout.Template field list; Unit/View are not auto-edited because list/card placement depends on local layout. Candidate positions: Layout.Template before the closing tag, Light${capitalize3(module)} projection array for list data, and Unit/View card sections for display.`,
5571
5898
  confidence: "medium",
5572
- 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."
5899
+ message: "UI manual review includes the reason auto-edit may be skipped and candidate insertion positions."
5900
+ },
5901
+ ...createAddFieldDefaultRecommendations(inputs)
5902
+ ];
5903
+ };
5904
+ var createWorkflowPlanPredictedChanges = (spec, inputs) => {
5905
+ if (spec.name !== "add-field")
5906
+ return spec.predictedChanges;
5907
+ const paths = addFieldPaths(inputs);
5908
+ const surfaces = selectedAddFieldSurfaces(inputs);
5909
+ const includeInLight = inputs.includeInLight === true;
5910
+ return [
5911
+ {
5912
+ target: paths.constant,
5913
+ action: "modify",
5914
+ applyScope: "auto",
5915
+ reason: includeInLight ? "Field shape and Light projection are added." : "Field shape is added."
5916
+ },
5917
+ {
5918
+ target: paths.dictionary,
5919
+ action: "modify",
5920
+ applyScope: "auto",
5921
+ reason: "Field label is added."
5922
+ },
5923
+ ...!surfaces || surfaces.has("template") ? [
5924
+ {
5925
+ target: paths.template,
5926
+ action: "modify",
5927
+ applyScope: surfaces?.has("template") ? "auto" : "manual-review",
5928
+ reason: surfaces?.has("template") ? "Template form is selected for safe-pattern auto insertion." : "Form surface may include the field."
5929
+ }
5930
+ ] : [],
5931
+ {
5932
+ target: `${addFieldTargetRoot(typeof inputs.app === "string" ? inputs.app : null)}/lib/cnst.ts`,
5933
+ action: "sync",
5934
+ applyScope: "generated-sync",
5935
+ reason: "Generated constants may change after sync."
5936
+ },
5937
+ {
5938
+ target: `${addFieldTargetRoot(typeof inputs.app === "string" ? inputs.app : null)}/lib/dict.ts`,
5939
+ action: "sync",
5940
+ applyScope: "generated-sync",
5941
+ reason: "Generated dictionary barrel may change after sync."
5573
5942
  }
5574
5943
  ];
5575
5944
  };
5945
+ var createWorkflowPlanOptionalSurfaces = (spec, inputs) => {
5946
+ const optionalSurfaces = spec.optionalSurfaces ?? {};
5947
+ const surfaces = selectedAddFieldSurfaces(inputs);
5948
+ if (spec.name !== "add-field" || !surfaces)
5949
+ return optionalSurfaces;
5950
+ return Object.fromEntries(Object.entries(optionalSurfaces).map(([surface, mode]) => [surface, surfaces.has(surface) ? "include" : mode]));
5951
+ };
5576
5952
  var createWorkflowPlanRecommendations = (spec, inputs) => [
5577
5953
  {
5578
5954
  code: "workflow-apply-first",
@@ -5636,14 +6012,32 @@ var createWorkflowPlan = (spec, rawInputs) => {
5636
6012
  message: `Field type "${fieldType}" is ambiguous in Akan. Use Int for integer fields or Float for decimal fields.`
5637
6013
  });
5638
6014
  }
6015
+ if (spec.name === "add-field") {
6016
+ const defaultCoercion = addFieldDefaultCoercion(inputs);
6017
+ if (defaultCoercion?.diagnostic) {
6018
+ diagnostics.push({
6019
+ ...defaultCoercion.diagnostic,
6020
+ code: "workflow-default-value-invalid",
6021
+ message: `Plan input default is not valid for ${defaultCoercion.normalizedType}: ${defaultCoercion.diagnostic.message}`
6022
+ });
6023
+ } else if (defaultCoercion?.normalized && defaultCoercion.expression) {
6024
+ diagnostics.push({
6025
+ severity: "warning",
6026
+ code: "workflow-default-value-normalized",
6027
+ input: "default",
6028
+ failureScope: "source-change",
6029
+ message: `Plan input default will be written as ${defaultCoercion.normalizedType} literal ${defaultCoercion.expression}.`
6030
+ });
6031
+ }
6032
+ }
5639
6033
  return {
5640
6034
  schemaVersion: 1,
5641
6035
  workflow: spec.name,
5642
6036
  mode: "plan",
5643
6037
  inputs,
5644
- optionalSurfaces: spec.optionalSurfaces ?? {},
6038
+ optionalSurfaces: createWorkflowPlanOptionalSurfaces(spec, inputs),
5645
6039
  steps: spec.steps,
5646
- predictedChanges: spec.predictedChanges,
6040
+ predictedChanges: createWorkflowPlanPredictedChanges(spec, inputs),
5647
6041
  validation: spec.validation,
5648
6042
  diagnostics,
5649
6043
  recommendations: createWorkflowPlanRecommendations(spec, inputs),
@@ -5712,35 +6106,55 @@ var renderWorkflowPlan = (plan) => [
5712
6106
  ""
5713
6107
  ].join(`
5714
6108
  `);
5715
- var renderWorkflowApplyReport = (report) => [
5716
- `# Workflow Apply: ${report.workflow}`,
5717
- "",
5718
- `- Mode: ${report.mode}`,
5719
- `- Status: ${report.status}`,
5720
- "",
5721
- "## Changed Files",
5722
- ...report.changedFiles.length ? report.changedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
5723
- "",
5724
- "## Generated Files",
5725
- ...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
5726
- "",
5727
- "## Applied Commands",
5728
- ...report.appliedCommands.length ? report.appliedCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
5729
- "",
5730
- "## Recommended Validation Commands",
5731
- ...report.recommendedValidationCommands.length ? report.recommendedValidationCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
5732
- "",
5733
- "## Diagnostics",
5734
- ...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
5735
- "",
5736
- "## Recommendations",
5737
- ...report.recommendations.length ? report.recommendations.map((recommendation) => `- [${recommendation.kind}] ${recommendation.message}`) : ["- none"],
5738
- "",
5739
- "## Next Actions",
5740
- ...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
5741
- ""
5742
- ].join(`
6109
+ var renderRecommendation = (recommendation) => {
6110
+ const target = recommendation.target ? ` ${recommendation.target}` : "";
6111
+ const action = recommendation.action ? ` Action: ${recommendation.action}` : "";
6112
+ return `- [${recommendation.kind}]${target} ${recommendation.message}${action}`;
6113
+ };
6114
+ var renderDiagnostic = (diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`;
6115
+ var renderWorkflowApplyReport = (report) => {
6116
+ const manualReviewItems = [
6117
+ ...report.diagnostics.filter((diagnostic) => diagnostic.severity === "warning").map(renderDiagnostic),
6118
+ ...report.recommendations.filter((recommendation) => recommendation.kind === "manual-action").map(renderRecommendation)
6119
+ ];
6120
+ const validationBlockers = report.diagnostics.filter((diagnostic) => diagnostic.severity === "error" && (diagnostic.failureScope === "workspace-config" || diagnostic.failureScope === "environment")).map(renderDiagnostic);
6121
+ return [
6122
+ `# Workflow Apply: ${report.workflow}`,
6123
+ "",
6124
+ `- Mode: ${report.mode}`,
6125
+ `- Status: ${report.status}`,
6126
+ ...report.validationTarget ? [`- Validation target: ${report.validationTarget}`] : [],
6127
+ "",
6128
+ "## Automatically Modified",
6129
+ ...report.changedFiles.length ? report.changedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
6130
+ "",
6131
+ "## Generated Sync",
6132
+ ...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
6133
+ "",
6134
+ "## Applied Commands",
6135
+ ...report.appliedCommands.length ? report.appliedCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
6136
+ "",
6137
+ "## Recommended Validation Commands",
6138
+ ...report.recommendedValidationCommands.length ? report.recommendedValidationCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
6139
+ "",
6140
+ "## User Review Required",
6141
+ ...manualReviewItems.length ? manualReviewItems : ["- none"],
6142
+ "",
6143
+ "## Validation Blockers",
6144
+ ...validationBlockers.length ? validationBlockers : report.recommendedValidationCommands.length ? ["- none detected during apply; run validation with the validation target for command-level blockers."] : ["- none"],
6145
+ "",
6146
+ "## Diagnostics",
6147
+ ...report.diagnostics.length ? report.diagnostics.map(renderDiagnostic) : ["- none"],
6148
+ "",
6149
+ "## Recommendations",
6150
+ ...report.recommendations.length ? report.recommendations.map(renderRecommendation) : ["- none"],
6151
+ "",
6152
+ "## Next Actions",
6153
+ ...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
6154
+ ""
6155
+ ].join(`
5743
6156
  `);
6157
+ };
5744
6158
  var renderWorkflowApply = (report, format = "markdown") => format === "json" ? jsonText(report) : renderWorkflowApplyReport(report);
5745
6159
  var renderWorkflowValidationRunReport = (report) => [
5746
6160
  `# Workflow Validation: ${report.workflow}`,
@@ -5751,11 +6165,18 @@ var renderWorkflowValidationRunReport = (report) => [
5751
6165
  `- Workspace status: ${report.workspaceStatus}`,
5752
6166
  `- Overall status: ${report.overallStatus}`,
5753
6167
  "",
6168
+ "## Status Summary",
6169
+ `- Required source-change validation: ${report.summary.sourceChange}`,
6170
+ `- Generated sync validation: ${report.summary.generatedSync}`,
6171
+ `- Workspace configuration validation: ${report.summary.workspaceConfig}`,
6172
+ `- Environment validation: ${report.summary.environment}`,
6173
+ "",
5754
6174
  "## Known Blockers",
5755
6175
  ...report.knownBlockers.length ? report.knownBlockers.map((blocker) => {
5756
6176
  const command = blocker.command ? ` \`${blocker.command}\`` : "";
5757
6177
  const count = blocker.count > 1 ? ` (${blocker.count}x)` : "";
5758
- return `- [${blocker.failureScope}]${command}${count}: ${blocker.message}`;
6178
+ const known = blocker.known ? " known" : "";
6179
+ return `- [${blocker.failureScope}${known}]${command}${count}: ${blocker.message}`;
5759
6180
  }) : ["- none"],
5760
6181
  "",
5761
6182
  "## Commands",
@@ -12663,6 +13084,8 @@ var NODE_NATIVE_MODULE_SET = new Set([
12663
13084
  ]);
12664
13085
  // pkgs/@akanjs/devkit/getCredentials.ts
12665
13086
  import yaml from "js-yaml";
13087
+ // pkgs/@akanjs/devkit/getModelFileData.ts
13088
+ import { capitalize as capitalize8 } from "akanjs/common";
12666
13089
  // pkgs/@akanjs/devkit/getRelatedCnsts.ts
12667
13090
  import { readFileSync as readFileSync4, realpathSync } from "fs";
12668
13091
  import ora2 from "ora";