@akanjs/cli 2.3.9-rc.8 → 2.3.9

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
@@ -4797,6 +4797,11 @@ var compactDiagnostics = (diagnostics) => diagnostics.filter((diagnostic) => !!d
4797
4797
 
4798
4798
  // pkgs/@akanjs/devkit/workflow/artifacts.ts
4799
4799
  var sourceChangeBlocked = (diagnostics) => diagnostics.some((diagnostic) => diagnostic.severity === "error" && (diagnostic.failureScope === "source-change" || !diagnostic.failureScope || diagnostic.failureScope === "unknown"));
4800
+ var workflowPlanApproval = {
4801
+ required: true,
4802
+ meaning: "Review this read-only plan before apply_workflow mutates files.",
4803
+ applyTool: "apply_workflow"
4804
+ };
4800
4805
  var inferNextActionCode = (action, diagnostics) => {
4801
4806
  if (action.action)
4802
4807
  return action.action;
@@ -4841,13 +4846,19 @@ var createWorkflowApplyReport = ({
4841
4846
  });
4842
4847
  }
4843
4848
  const orderedNextActions = uniqueBy(nextActionsWithIntent, (action) => action.command).sort((left, right) => nextActionPriority(left, diagnostics) - nextActionPriority(right, diagnostics));
4849
+ const sourceFilesChanged = uniqueBy(changedFiles, (file) => `${file.action}:${file.path}:${file.reason}`);
4850
+ const generatedFilesSynced = uniqueBy(generatedFiles, (file) => `${file.action}:${file.path}:${file.reason}`);
4844
4851
  return {
4845
4852
  schemaVersion: 1,
4846
4853
  workflow,
4847
4854
  mode,
4848
4855
  status: workflowStatus(diagnostics),
4849
- changedFiles: uniqueBy(changedFiles, (file) => `${file.action}:${file.path}:${file.reason}`),
4850
- generatedFiles: uniqueBy(generatedFiles, (file) => `${file.action}:${file.path}:${file.reason}`),
4856
+ summary: {
4857
+ sourceFilesChanged,
4858
+ generatedFilesSynced
4859
+ },
4860
+ changedFiles: sourceFilesChanged,
4861
+ generatedFiles: generatedFilesSynced,
4851
4862
  appliedCommands: uniqueBy(appliedCommands, (command) => command.command),
4852
4863
  recommendedValidationCommands: uniqueBy(validationCommands, (command) => command.command),
4853
4864
  commands: uniqueBy(validationCommands, (command) => command.command),
@@ -4925,6 +4936,52 @@ var statusForValidationKind = (commands, kind) => {
4925
4936
  return "unknown";
4926
4937
  return matching.some((command) => command.status === "failed") ? "failed" : "passed";
4927
4938
  };
4939
+ var statusForCommands = (commands) => {
4940
+ if (commands.length === 0)
4941
+ return "unknown";
4942
+ return commandStatus(commands) === "failed" ? "failed" : "passed";
4943
+ };
4944
+ var statusForDiagnostics = (diagnostics) => {
4945
+ if (diagnostics.length === 0)
4946
+ return "unknown";
4947
+ return workflowStatus(diagnostics) === "failed" ? "failed" : "passed";
4948
+ };
4949
+ var workflowDiagnosticContextPaths = (diagnostics) => uniqueBy(diagnostics.flatMap((diagnostic) => diagnostic.context?.paths ?? []), (filePath) => filePath);
4950
+ var createWorkflowBaselineSummary = (diagnostics, { detailsIncluded = true, knownBlockerCount = 0 } = {}) => {
4951
+ const grouped = new Map;
4952
+ let totalErrors = 0;
4953
+ let totalWarnings = 0;
4954
+ for (const diagnostic of diagnostics) {
4955
+ if (diagnostic.severity === "error")
4956
+ totalErrors += 1;
4957
+ else
4958
+ totalWarnings += 1;
4959
+ const existing = grouped.get(diagnostic.code);
4960
+ if (existing) {
4961
+ existing.count += 1;
4962
+ if (existing.severity !== diagnostic.severity)
4963
+ existing.severity = "mixed";
4964
+ } else {
4965
+ grouped.set(diagnostic.code, {
4966
+ code: diagnostic.code,
4967
+ severity: diagnostic.severity,
4968
+ count: 1,
4969
+ sampleMessage: diagnostic.message
4970
+ });
4971
+ }
4972
+ }
4973
+ const contextPaths = workflowDiagnosticContextPaths(diagnostics);
4974
+ return {
4975
+ status: totalErrors > 0 ? "failed" : diagnostics.length > 0 ? "passed" : "unknown",
4976
+ total: diagnostics.length,
4977
+ totalErrors,
4978
+ totalWarnings,
4979
+ detailsIncluded,
4980
+ knownBlockerCount,
4981
+ byCode: [...grouped.values()],
4982
+ ...contextPaths.length ? { contextPaths } : {}
4983
+ };
4984
+ };
4928
4985
  var createKnownBlockers = (commands, diagnostics) => {
4929
4986
  const commandBlockers = commands.filter((command) => command.status === "failed" && (command.failureScope === "workspace-config" || command.failureScope === "environment")).map((command) => ({
4930
4987
  code: `workflow-validation-${command.failureScope}`,
@@ -4953,18 +5010,28 @@ var createKnownBlockers = (commands, diagnostics) => {
4953
5010
  }
4954
5011
  return [...grouped.values()];
4955
5012
  };
4956
- var createValidationStatuses = (commands, diagnostics) => {
5013
+ var createValidationStatuses = (commands, reportDiagnostics, baselineDiagnostics, workflowDiagnostics) => {
5014
+ const diagnostics = [...reportDiagnostics, ...baselineDiagnostics, ...workflowDiagnostics];
4957
5015
  const scopes = [...failedCommandScopes(commands), ...errorDiagnosticScopes(diagnostics)];
4958
- const sourceStatus = statusForScope(commands, diagnostics, scopes, "source-change");
5016
+ const sourceDiagnostics = [...reportDiagnostics, ...workflowDiagnostics].filter((diagnostic) => diagnostic.failureScope === "source-change" || diagnostic.scope === "workflow" || !diagnostic.failureScope && diagnostic.scope !== "baseline");
5017
+ const sourceScopes = [...failedCommandScopes(commands), ...errorDiagnosticScopes(sourceDiagnostics)];
5018
+ const sourceStatus = statusForScope(commands, sourceDiagnostics, sourceScopes, "source-change");
5019
+ const validationCommandsStatus = statusForCommands(commands);
5020
+ const baselineStatus = statusForDiagnostics(baselineDiagnostics);
5021
+ const nonBaselineDiagnostics = [...reportDiagnostics, ...workflowDiagnostics];
4959
5022
  const workspaceStatus = hasScopeFailure(scopes, "workspace-config", diagnostics) || hasScopeFailure(scopes, "environment", diagnostics) ? "failed" : commands.length || diagnostics.length ? "passed" : "unknown";
4960
- 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";
5023
+ const overallStatus = hasScopeFailure(scopes, "source-change", diagnostics) ? "failed" : validationCommandsStatus === "passed" && baselineStatus === "failed" && workflowStatus(nonBaselineDiagnostics) !== "failed" ? "passed-with-baseline-blockers" : hasScopeFailure(scopes, "workspace-config", diagnostics) ? "blocked-by-workspace-config" : hasScopeFailure(scopes, "environment", diagnostics) ? "blocked-by-environment" : workflowStatus(diagnostics) === "failed" || commandStatus(commands) === "failed" ? "failed" : "passed";
4961
5024
  return {
4962
5025
  sourceStatus,
4963
5026
  workspaceStatus,
5027
+ validationCommandsStatus,
5028
+ baselineStatus,
4964
5029
  overallStatus,
4965
5030
  summary: {
4966
5031
  sourceChange: sourceStatus,
4967
5032
  generatedSync: statusForValidationKind(commands, "sync"),
5033
+ validationCommands: validationCommandsStatus,
5034
+ baseline: baselineStatus,
4968
5035
  workspaceConfig: statusForScope(commands, diagnostics, scopes, "workspace-config"),
4969
5036
  environment: statusForScope(commands, diagnostics, scopes, "environment")
4970
5037
  }
@@ -4998,7 +5065,8 @@ var createWorkflowValidationRunReport = async ({
4998
5065
  ] : []);
4999
5066
  const reportDiagnostics = [...diagnostics, ...commandDiagnostics];
5000
5067
  const scopedDiagnostics = [...reportDiagnostics, ...baselineDiagnostics, ...workflowDiagnostics];
5001
- const statuses = createValidationStatuses(results, scopedDiagnostics);
5068
+ const knownBlockers = createKnownBlockers(results, scopedDiagnostics);
5069
+ const statuses = createValidationStatuses(results, reportDiagnostics, baselineDiagnostics, workflowDiagnostics);
5002
5070
  return {
5003
5071
  schemaVersion: 1,
5004
5072
  runId,
@@ -5007,9 +5075,12 @@ var createWorkflowValidationRunReport = async ({
5007
5075
  source,
5008
5076
  status: statuses.overallStatus === "passed" ? "passed" : "failed",
5009
5077
  ...statuses,
5010
- knownBlockers: createKnownBlockers(results, scopedDiagnostics),
5078
+ knownBlockers,
5011
5079
  commands: results,
5012
5080
  diagnostics: reportDiagnostics,
5081
+ baselineSummary: createWorkflowBaselineSummary(baselineDiagnostics, {
5082
+ knownBlockerCount: knownBlockers.filter((blocker) => blocker.failureScope === "workspace-config").length
5083
+ }),
5013
5084
  baselineDiagnostics,
5014
5085
  workflowDiagnostics,
5015
5086
  repairActions: uniqueBy(repairActions, (action) => action.command),
@@ -6325,7 +6396,7 @@ var checkTypeScriptSyntax = async (workspace, filePath) => {
6325
6396
  return null;
6326
6397
  const content = await workspace.readFile(filePath);
6327
6398
  const source = ts6.createSourceFile(filePath, content, ts6.ScriptTarget.Latest, true, scriptKind);
6328
- const diagnostic = source.parseDiagnostics[0];
6399
+ const diagnostic = (source.parseDiagnostics ?? [])[0];
6329
6400
  if (!diagnostic)
6330
6401
  return null;
6331
6402
  const position = diagnostic.file?.getLineAndCharacterOfPosition(diagnostic.start ?? 0);
@@ -6523,9 +6594,9 @@ var addFieldUiSurfaceInspection = (plan) => {
6523
6594
  code: "add-field-ui-surface-review",
6524
6595
  kind: "manual-action",
6525
6596
  target,
6526
- 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.`,
6597
+ action: templateRequested ? `Template was requested for ${field}. If no Template file changed, users will not see the field in the form yet because the file was missing, the generated ${module}Form/Layout.Template pattern was not found, or ${policy.component} needs option binding. Add it inside Layout.Template near existing Field components.` : `Template was not selected, so users will not see ${field} in the form from this apply. If list/card display is needed, include ${field} in Light${moduleClassName} projection data and place it in the local Unit/View card layout.`,
6527
6598
  confidence: "medium",
6528
- message: `Review UI surfaces for ${module}.${field}; recommended component is ${policy.component}, with manual reasons and candidate positions in action.`
6599
+ message: `Review user-visible UI for ${module}.${field}; recommended component is ${policy.component}.`
6529
6600
  }
6530
6601
  ],
6531
6602
  nextActions: [
@@ -6780,10 +6851,48 @@ var createAddFieldDefaultRecommendations = (inputs) => {
6780
6851
  kind: "manual-action",
6781
6852
  confidence: "high",
6782
6853
  message: `Default for ${field} will be normalized to ${coercion.normalizedType} literal ${coercion.expression}.`,
6783
- action: "Review the normalized default in the apply report if this value should stay unset."
6854
+ action: "Review the normalized default in the apply report. To leave the field without a default, omit the default input."
6784
6855
  }
6785
6856
  ];
6786
6857
  };
6858
+ var moneyLikeFieldPattern = /(budget|price|amount|cost|rate|fee|salary|balance)/i;
6859
+ var wholeNumberFieldPattern = /(count|quantity|total|number|index|rank|order|age)/i;
6860
+ var createAddFieldInputRecommendations = (inputs) => {
6861
+ const field = typeof inputs.field === "string" ? inputs.field : "<field>";
6862
+ const typeName = typeof inputs.type === "string" ? inputs.type : null;
6863
+ const recommendations = [];
6864
+ if (!typeName)
6865
+ return recommendations;
6866
+ if (typeName.toLowerCase() === "number" || typeName.toLowerCase() === "numeric") {
6867
+ const recommendedType = moneyLikeFieldPattern.test(field) ? "Float" : wholeNumberFieldPattern.test(field) ? "Int" : null;
6868
+ recommendations.push({
6869
+ code: "add-field-type-choice",
6870
+ kind: "input-guidance",
6871
+ confidence: recommendedType ? "high" : "medium",
6872
+ message: recommendedType ? `Use ${recommendedType} for ${field}; Float is recommended for money-like decimal values and Int for whole-number values.` : `Choose Int for whole-number ${field} values or Float for decimal ${field} values.`,
6873
+ action: recommendedType ? `Re-run plan_workflow with type="${recommendedType}".` : 'Re-run plan_workflow with type="Int" or type="Float".'
6874
+ });
6875
+ }
6876
+ if (typeName.toLowerCase() === "enum" && !Array.isArray(inputs.values)) {
6877
+ recommendations.push({
6878
+ code: "add-field-enum-values",
6879
+ kind: "input-guidance",
6880
+ confidence: "high",
6881
+ message: `Enum field ${field} needs values before apply can choose valid dictionary and default behavior.`,
6882
+ action: 'Pass values as an array or comma-separated string, for example values=["draft","done"].'
6883
+ });
6884
+ }
6885
+ if (inputs.default === undefined) {
6886
+ recommendations.push({
6887
+ code: "add-field-default-optional",
6888
+ kind: "input-guidance",
6889
+ confidence: "medium",
6890
+ message: `No default will be written for ${field} because default is omitted.`,
6891
+ action: "Keep default omitted when the field should have no default value."
6892
+ });
6893
+ }
6894
+ return recommendations;
6895
+ };
6787
6896
  var createAddFieldRecommendations = (inputs) => {
6788
6897
  const app = typeof inputs.app === "string" ? inputs.app : "<app-or-lib>";
6789
6898
  const module = typeof inputs.module === "string" ? inputs.module : "<module>";
@@ -6844,9 +6953,9 @@ var createAddFieldRecommendations = (inputs) => {
6844
6953
  code: "add-field-light-projection-choice",
6845
6954
  kind: "manual-action",
6846
6955
  target: paths.constant,
6847
- action: `Pass includeInLight=true when ${field} should appear in Light${capitalize2(module)} list/card projections.`,
6956
+ action: `Pass includeInLight=true when users should see ${field} in list/card data. Without it, the field can exist on ${capitalize2(module)}Input but stay absent from Light${capitalize2(module)} projections.`,
6848
6957
  confidence: "medium",
6849
- message: `Light projection exposure for ${module}.${field} is not selected yet.`
6958
+ message: `${module}.${field} is not selected for list/card projection data.`
6850
6959
  }
6851
6960
  ] : [],
6852
6961
  ...includeInLight ? [
@@ -6864,18 +6973,20 @@ var createAddFieldRecommendations = (inputs) => {
6864
6973
  kind: "manual-action",
6865
6974
  target: paths.template,
6866
6975
  confidence: "medium",
6867
- message: `Template is not included in surfaces, so ${module}.${field} will not be auto-rendered there.`
6976
+ message: `Template form auto-edit is skipped for ${module}.${field} because surfaces does not include "template".`,
6977
+ action: `Pass surfaces=["template"] when users should enter ${field} in the Template form.`
6868
6978
  }
6869
6979
  ] : [],
6870
6980
  {
6871
6981
  code: "add-field-ui-manual-review",
6872
6982
  kind: "manual-action",
6873
6983
  target: paths.template,
6874
- 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${capitalize2(module)} projection array for list data, and Unit/View card sections for display.`,
6984
+ action: `After apply, verify what users can see: Template form auto-edit only runs when a generated ${module}Form hook and Layout.Template field list are present. Unit/View cards are not auto-edited, so ${field} may be stored on ${capitalize2(module)} but not shown in list/card UI until you place it in the local card layout.`,
6875
6985
  confidence: "medium",
6876
- message: "UI manual review includes the reason auto-edit may be skipped and candidate insertion positions."
6986
+ message: `${app}:${module}.${field} may need UI review for user-visible Template, Unit, and View behavior.`
6877
6987
  },
6878
- ...createAddFieldDefaultRecommendations(inputs)
6988
+ ...createAddFieldDefaultRecommendations(inputs),
6989
+ ...createAddFieldInputRecommendations(inputs)
6879
6990
  ];
6880
6991
  };
6881
6992
  var createWorkflowPlanPredictedChanges = (spec, inputs) => {
@@ -7018,7 +7129,8 @@ var createWorkflowPlan = (spec, rawInputs) => {
7018
7129
  validation: spec.validation,
7019
7130
  diagnostics,
7020
7131
  recommendations: createWorkflowPlanRecommendations(spec, inputs),
7021
- requiresApproval: true
7132
+ requiresApproval: true,
7133
+ approval: workflowPlanApproval
7022
7134
  };
7023
7135
  };
7024
7136
  // pkgs/@akanjs/devkit/workflow/render.ts
@@ -7056,6 +7168,7 @@ var renderWorkflowPlan = (plan) => [
7056
7168
  "",
7057
7169
  `- Mode: ${plan.mode}`,
7058
7170
  `- Requires approval: ${plan.requiresApproval}`,
7171
+ `- Approval: ${plan.approval?.meaning ?? "Review this read-only plan before applying workflow mutations."}`,
7059
7172
  "",
7060
7173
  "## Inputs",
7061
7174
  ...Object.entries(plan.inputs).map(([name, value]) => `- \`${name}\`: ${Array.isArray(value) ? value.join(", ") : value}`),
@@ -7092,6 +7205,10 @@ var renderDiagnostic = (diagnostic) => `- [${diagnostic.severity}] ${diagnostic.
7092
7205
  var renderNextAction = (action) => `- \`${action.command}\`: ${action.reason}`;
7093
7206
  var applySourceStatus = (report) => report.diagnostics.some((diagnostic) => diagnostic.severity === "error" && (diagnostic.failureScope === "source-change" || !diagnostic.failureScope || diagnostic.failureScope === "unknown")) ? "failed" : "passed";
7094
7207
  var renderWorkflowApplyReport = (report) => {
7208
+ const applySummary = {
7209
+ sourceFilesChanged: report.summary?.sourceFilesChanged ?? report.changedFiles,
7210
+ generatedFilesSynced: report.summary?.generatedFilesSynced ?? report.generatedFiles
7211
+ };
7095
7212
  const manualReviewItems = [
7096
7213
  ...report.diagnostics.filter((diagnostic) => diagnostic.severity === "warning").map(renderDiagnostic),
7097
7214
  ...report.recommendations.filter((recommendation) => recommendation.kind === "manual-action").map(renderRecommendation)
@@ -7105,6 +7222,8 @@ var renderWorkflowApplyReport = (report) => {
7105
7222
  `- Status: ${report.status}`,
7106
7223
  `- Source-change status: ${applySourceStatus(report)}`,
7107
7224
  `- Workspace status: ${validationBlockers.length ? "blocked" : "not blocked during apply"}`,
7225
+ `- Source files changed: ${applySummary.sourceFilesChanged.length}`,
7226
+ `- Generated files queued for sync: ${applySummary.generatedFilesSynced.length}`,
7108
7227
  ...report.validationTarget ? [`- Validation target: ${report.validationTarget}`] : [],
7109
7228
  "",
7110
7229
  "## Apply Checks",
@@ -7114,10 +7233,10 @@ var renderWorkflowApplyReport = (report) => {
7114
7233
  ...sourceBlockers.length ? sourceBlockers : ["- none"],
7115
7234
  "",
7116
7235
  "## Automatically Modified",
7117
- ...report.changedFiles.length ? report.changedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
7236
+ ...applySummary.sourceFilesChanged.length ? applySummary.sourceFilesChanged.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
7118
7237
  "",
7119
7238
  "## Generated Sync",
7120
- ...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
7239
+ ...applySummary.generatedFilesSynced.length ? applySummary.generatedFilesSynced.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
7121
7240
  "",
7122
7241
  "## Applied Commands",
7123
7242
  ...report.appliedCommands.length ? report.appliedCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
@@ -7144,52 +7263,76 @@ var renderWorkflowApplyReport = (report) => {
7144
7263
  `);
7145
7264
  };
7146
7265
  var renderWorkflowApply = (report, format = "markdown") => format === "json" ? jsonText(report) : renderWorkflowApplyReport(report);
7147
- var renderWorkflowValidationRunReport = (report) => [
7148
- `# Workflow Validation: ${report.workflow}`,
7149
- "",
7150
- `- Run: ${report.runId}`,
7151
- `- Status: ${report.status}`,
7152
- `- Source status: ${report.sourceStatus}`,
7153
- `- Workspace status: ${report.workspaceStatus}`,
7154
- `- Overall status: ${report.overallStatus}`,
7155
- "",
7156
- "## Status Summary",
7157
- `- Source-change: ${report.summary.sourceChange}`,
7158
- `- Generated sync: ${report.summary.generatedSync}`,
7159
- `- Workspace config: ${report.summary.workspaceConfig}`,
7160
- `- Environment: ${report.summary.environment}`,
7161
- "",
7162
- "## Source Change Diagnostics",
7163
- ...report.workflowDiagnostics?.length ? report.workflowDiagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
7164
- "",
7165
- "## Existing Workspace Blockers",
7166
- ...report.baselineDiagnostics?.length ? report.baselineDiagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
7167
- "",
7168
- "## Known Blockers",
7169
- ...report.knownBlockers.length ? report.knownBlockers.map((blocker) => {
7170
- const command = blocker.command ? ` \`${blocker.command}\`` : "";
7171
- const count = blocker.count > 1 ? ` (${blocker.count}x)` : "";
7172
- const known = blocker.known ? " known" : "";
7173
- return `- [${blocker.failureScope}${known}]${command}${count}: ${blocker.message}`;
7174
- }) : ["- none"],
7175
- "",
7176
- "## Commands",
7177
- ...report.commands.length ? report.commands.map((command) => {
7178
- const scope = command.failureScope ? ` (${command.failureScope})` : "";
7179
- return `- [${command.status}] \`${command.command}\`${scope}: ${command.reason}`;
7180
- }) : ["- none"],
7181
- "",
7182
- "## Diagnostics",
7183
- ...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
7184
- "",
7185
- "## Repair Actions",
7186
- ...report.repairActions.length ? report.repairActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
7187
- "",
7188
- "## Next Actions",
7189
- ...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
7190
- ""
7191
- ].join(`
7266
+ var renderWorkflowValidationRunReport = (report) => {
7267
+ const baselineSummary = report.baselineSummary ?? {
7268
+ status: report.baselineDiagnostics?.some((diagnostic) => diagnostic.severity === "error") ? "failed" : "unknown",
7269
+ total: report.baselineDiagnostics?.length ?? 0,
7270
+ totalErrors: report.baselineDiagnostics?.filter((diagnostic) => diagnostic.severity === "error").length ?? 0,
7271
+ totalWarnings: report.baselineDiagnostics?.filter((diagnostic) => diagnostic.severity === "warning").length ?? 0,
7272
+ detailsIncluded: true,
7273
+ knownBlockerCount: report.knownBlockers.filter((blocker) => blocker.known).length,
7274
+ byCode: []
7275
+ };
7276
+ return [
7277
+ `# Workflow Validation: ${report.workflow}`,
7278
+ "",
7279
+ `- Run: ${report.runId}`,
7280
+ `- Status: ${report.status}`,
7281
+ `- Source status: ${report.sourceStatus}`,
7282
+ `- Workspace status: ${report.workspaceStatus}`,
7283
+ `- Validation commands status: ${report.validationCommandsStatus ?? "unknown"}`,
7284
+ `- Baseline status: ${report.baselineStatus ?? baselineSummary.status}`,
7285
+ `- Overall status: ${report.overallStatus}`,
7286
+ "",
7287
+ "## Status Summary",
7288
+ `- Source-change: ${report.summary.sourceChange}`,
7289
+ `- Generated sync: ${report.summary.generatedSync}`,
7290
+ `- Validation commands: ${report.summary.validationCommands ?? "unknown"}`,
7291
+ `- Baseline: ${report.summary.baseline ?? baselineSummary.status}`,
7292
+ `- Workspace config: ${report.summary.workspaceConfig}`,
7293
+ `- Environment: ${report.summary.environment}`,
7294
+ "",
7295
+ "## Source Change Diagnostics",
7296
+ ...report.workflowDiagnostics?.length ? report.workflowDiagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
7297
+ "",
7298
+ "## Existing Workspace Blockers",
7299
+ `- Status: ${baselineSummary.status}`,
7300
+ `- Errors: ${baselineSummary.totalErrors}`,
7301
+ `- Warnings: ${baselineSummary.totalWarnings}`,
7302
+ ...baselineSummary.byCode.length ? baselineSummary.byCode.map((item) => `- [${item.severity}] ${item.code} (${item.count}x): ${item.sampleMessage}`) : ["- none"],
7303
+ ...baselineSummary.detailsIncluded ? [] : [
7304
+ "- Baseline details are summarized by default. Re-run validation with includeBaselineDetails=true for full baselineDiagnostics."
7305
+ ],
7306
+ "",
7307
+ "## Existing Workspace Blocker Details",
7308
+ ...report.baselineDiagnostics?.length ? report.baselineDiagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : baselineSummary.detailsIncluded ? ["- none"] : ["- omitted"],
7309
+ "",
7310
+ "## Known Blockers",
7311
+ ...report.knownBlockers.length ? report.knownBlockers.map((blocker) => {
7312
+ const command = blocker.command ? ` \`${blocker.command}\`` : "";
7313
+ const count = blocker.count > 1 ? ` (${blocker.count}x)` : "";
7314
+ const known = blocker.known ? " known" : "";
7315
+ return `- [${blocker.failureScope}${known}]${command}${count}: ${blocker.message}`;
7316
+ }) : ["- none"],
7317
+ "",
7318
+ "## Commands",
7319
+ ...report.commands.length ? report.commands.map((command) => {
7320
+ const scope = command.failureScope ? ` (${command.failureScope})` : "";
7321
+ return `- [${command.status}] \`${command.command}\`${scope}: ${command.reason}`;
7322
+ }) : ["- none"],
7323
+ "",
7324
+ "## Diagnostics",
7325
+ ...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
7326
+ "",
7327
+ "## Repair Actions",
7328
+ ...report.repairActions.length ? report.repairActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
7329
+ "",
7330
+ "## Next Actions",
7331
+ ...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
7332
+ ""
7333
+ ].join(`
7192
7334
  `);
7335
+ };
7193
7336
  var renderWorkflowValidation = (report, format = "markdown") => format === "json" ? jsonText(report) : renderWorkflowValidationRunReport(report);
7194
7337
  var renderRepairReportMarkdown = (report) => [
7195
7338
  `# Akan Repair: ${report.kind}`,
@@ -7278,7 +7421,11 @@ var toolingRolloutGate = {
7278
7421
  candidates: toolingRolloutCandidates
7279
7422
  };
7280
7423
  var isRestrictedCandidate = (candidate) => candidate.status !== "allowed";
7281
- var restrictedCandidates = new Map(toolingRolloutCandidates.filter(isRestrictedCandidate).map((candidate) => [candidate.packageName, candidate]));
7424
+ var restrictedCandidates = new Map;
7425
+ for (const candidate of toolingRolloutCandidates) {
7426
+ if (isRestrictedCandidate(candidate))
7427
+ restrictedCandidates.set(candidate.packageName, candidate);
7428
+ }
7282
7429
  // pkgs/@akanjs/devkit/akanContext.ts
7283
7430
  var resourceList = [
7284
7431
  { uri: "akan://docs/framework", name: "Akan framework guide", mimeType: "text/markdown" },
@@ -7847,6 +7994,7 @@ var applyFirstPolicy = {
7847
7994
  mode: "apply-first",
7848
7995
  directSourceEdits: "fallback-only",
7849
7996
  applyRequiredWhen: ["plan_workflow returns planPath", "plan_workflow returns next.tool=apply_workflow"],
7997
+ approvalMeaning: "requiresApproval is an agent/user review signal before apply_workflow mutates files; it is not a separate MCP permission gate.",
7850
7998
  validationRequiredAfterApply: ["validationTarget", "applyReportPath"],
7851
7999
  fallbackAllowedWhen: [
7852
8000
  "list_workflows and explain_workflow show no matching workflow",
@@ -7982,7 +8130,12 @@ var readonlyMcpTools = [
7982
8130
  description: "Report workspace diagnostics, optionally split into baseline and workflow scopes.",
7983
8131
  inputSchema: {
7984
8132
  type: "object",
7985
- properties: { strict: booleanProperty, runIdOrPlan: stringProperty, changedFiles: stringArrayProperty }
8133
+ properties: {
8134
+ strict: booleanProperty,
8135
+ runIdOrPlan: stringProperty,
8136
+ changedFiles: stringArrayProperty,
8137
+ includeBaselineDetails: booleanProperty
8138
+ }
7986
8139
  }
7987
8140
  },
7988
8141
  {
@@ -8027,7 +8180,7 @@ var applyMcpTools = [
8027
8180
  description: "Validate a plan, apply report, validationTarget, or run artifact.",
8028
8181
  inputSchema: {
8029
8182
  type: "object",
8030
- properties: { runIdOrPlan: stringProperty },
8183
+ properties: { runIdOrPlan: stringProperty, includeBaselineDetails: booleanProperty },
8031
8184
  required: ["runIdOrPlan"]
8032
8185
  }
8033
8186
  },
@@ -8077,7 +8230,16 @@ var createAkanValidationContract = (listTools = listAkanMcpTools) => ({
8077
8230
  validationStatuses: {
8078
8231
  sourceStatus: ["passed", "failed", "unknown"],
8079
8232
  workspaceStatus: ["passed", "failed", "unknown"],
8080
- overallStatus: ["passed", "failed", "blocked-by-workspace-config", "blocked-by-environment"],
8233
+ validationCommandsStatus: ["passed", "failed", "unknown"],
8234
+ baselineStatus: ["passed", "failed", "unknown"],
8235
+ overallStatus: [
8236
+ "passed",
8237
+ "failed",
8238
+ "passed-with-baseline-blockers",
8239
+ "blocked-by-workspace-config",
8240
+ "blocked-by-environment"
8241
+ ],
8242
+ baselineSummary: "Default MCP validation output summarizes baseline diagnostics; pass includeBaselineDetails=true for full baselineDiagnostics.",
8081
8243
  knownBlockers: "Repeated workspace-config/environment failures are summarized before command output."
8082
8244
  },
8083
8245
  moduleContextInputs: {
@@ -8088,6 +8250,7 @@ var createAkanValidationContract = (listTools = listAkanMcpTools) => ({
8088
8250
  toolingRolloutGate,
8089
8251
  directEditFallbackPolicy: applyFirstPolicy,
8090
8252
  applyReportFields: [
8253
+ "summary",
8091
8254
  "appliedCommands",
8092
8255
  "recommendedValidationCommands",
8093
8256
  "commands",
@@ -8129,7 +8292,14 @@ var inspectAkanContext = async (workspace, args) => {
8129
8292
  strict: true,
8130
8293
  runIdOrPlan: workspacePath(workspace, props.request.runIdOrPlan)
8131
8294
  });
8132
- diagnostics.push(...doctor.diagnostics.map((diagnostic) => ({
8295
+ const baselineSummary = createWorkflowBaselineSummary((doctor.baselineDiagnostics ?? []).map((diagnostic) => ({
8296
+ severity: diagnostic.severity,
8297
+ code: diagnostic.code,
8298
+ message: diagnostic.message,
8299
+ scope: diagnostic.scope,
8300
+ context: diagnostic.context
8301
+ })), { detailsIncluded: false });
8302
+ diagnostics.push(...(doctor.workflowDiagnostics ?? doctor.diagnostics).map((diagnostic) => ({
8133
8303
  severity: diagnostic.severity,
8134
8304
  code: diagnostic.code,
8135
8305
  message: diagnostic.message,
@@ -8150,7 +8320,8 @@ var inspectAkanContext = async (workspace, args) => {
8150
8320
  reason: doctor.status === "passed" ? "No strict workspace diagnostics were found for the workflow context." : "Review diagnostics before applying or manually editing source files."
8151
8321
  }, {
8152
8322
  status: doctor.status,
8153
- baselineDiagnostics: doctor.baselineDiagnostics?.length ?? 0,
8323
+ baselineSummary,
8324
+ baselineDiagnostics: 0,
8154
8325
  workflowDiagnostics: doctor.workflowDiagnostics?.length ?? 0
8155
8326
  });
8156
8327
  }
@@ -12331,6 +12502,7 @@ void run().catch((error) => {
12331
12502
  }
12332
12503
  // pkgs/@akanjs/devkit/applicationReleasePackager.ts
12333
12504
  import { cp, mkdir as mkdir9, rm as rm4 } from "fs/promises";
12505
+ import path38 from "path";
12334
12506
 
12335
12507
  // pkgs/@akanjs/devkit/uploadRelease.ts
12336
12508
  import { HttpClient as HttpClient2, Logger as Logger9 } from "akanjs/common";
@@ -12446,13 +12618,9 @@ class ApplicationReleasePackager {
12446
12618
  await cp(`${this.#app.dist.cwdPath}/backend`, `${buildPath}/backend`, { recursive: true });
12447
12619
  await cp(this.#app.dist.cwdPath, buildRoot, { recursive: true });
12448
12620
  await rm4(`${buildRoot}/frontend/.next`, { recursive: true, force: true });
12449
- await this.#app.workspace.spawn("tar", [
12450
- "-zcf",
12451
- `${this.#app.workspace.workspaceRoot}/releases/builds/${this.#app.name}-release.tar.gz`,
12452
- "-C",
12453
- buildRoot,
12454
- "./"
12455
- ]);
12621
+ const releaseRoot = this.#app.workspace.workspaceRoot;
12622
+ const releaseArchivePath = path38.relative(releaseRoot, `${releaseRoot}/releases/builds/${this.#app.name}-release.tar.gz`).split(path38.sep).join("/");
12623
+ await this.#app.workspace.spawn("tar", ["-zcf", releaseArchivePath, "-C", buildRoot, "./"], { cwd: releaseRoot });
12456
12624
  await this.#writeCsrZipIfPresent();
12457
12625
  return { platformVersion };
12458
12626
  }
@@ -12473,22 +12641,18 @@ class ApplicationReleasePackager {
12473
12641
  await cp(this.#app.dist.cwdPath, `${sourceRoot}/apps/${this.#app.name}`, { recursive: true });
12474
12642
  const libDeps = ["social", "shared", "platform", "util"];
12475
12643
  await Promise.all(libDeps.map((lib) => cp(`${this.#app.workspace.cwdPath}/libs/${lib}`, `${sourceRoot}/libs/${lib}`, { recursive: true })));
12476
- await Promise.all([".next", "ios", "android", "public/libs"].map(async (path38) => {
12477
- const targetPath = `${sourceRoot}/apps/${this.#app.name}/${path38}`;
12644
+ await Promise.all([".next", "ios", "android", "public/libs"].map(async (path39) => {
12645
+ const targetPath = `${sourceRoot}/apps/${this.#app.name}/${path39}`;
12478
12646
  if (await FileSys.dirExists(targetPath))
12479
12647
  await rm4(targetPath, { recursive: true, force: true });
12480
12648
  }));
12481
12649
  const syncPaths = [".husky", ".gitignore", "package.json"];
12482
- await Promise.all(syncPaths.map((path38) => cp(`${this.#app.workspace.cwdPath}/${path38}`, `${sourceRoot}/${path38}`, { recursive: true })));
12650
+ await Promise.all(syncPaths.map((path39) => cp(`${this.#app.workspace.cwdPath}/${path39}`, `${sourceRoot}/${path39}`, { recursive: true })));
12483
12651
  await this.#writeSourceTsconfig(sourceRoot, libDeps);
12484
12652
  await Bun.write(`${sourceRoot}/README.md`, readme);
12485
- await this.#app.workspace.spawn("tar", [
12486
- "-zcf",
12487
- `${this.#app.workspace.cwdPath}/releases/sources/${this.#app.name}-source.tar.gz`,
12488
- "-C",
12489
- sourceRoot,
12490
- "./"
12491
- ]);
12653
+ const sourceCwd = this.#app.workspace.cwdPath;
12654
+ const sourceArchivePath = path38.relative(sourceCwd, `${sourceCwd}/releases/sources/${this.#app.name}-source.tar.gz`).split(path38.sep).join("/");
12655
+ await this.#app.workspace.spawn("tar", ["-zcf", sourceArchivePath, "-C", sourceRoot, "./"], { cwd: sourceCwd });
12492
12656
  }
12493
12657
  async#resetSourceRoot(sourceRoot) {
12494
12658
  if (await FileSys.dirExists(sourceRoot)) {
@@ -12570,20 +12734,20 @@ class ApplicationReleasePackager {
12570
12734
  }
12571
12735
  }
12572
12736
  // pkgs/@akanjs/devkit/applicationTestPreload.ts
12573
- import path38 from "path";
12737
+ import path39 from "path";
12574
12738
  var SIGNAL_TEST_PRELOAD_PATH = "test/signalTest.preload.ts";
12575
12739
  async function resolveSignalTestPreloadPath(target) {
12576
12740
  const candidates = [];
12577
12741
  const addResolvedPackageCandidate = (basePath2) => {
12578
12742
  try {
12579
- candidates.push(path38.join(path38.dirname(Bun.resolveSync("akanjs/package.json", basePath2)), SIGNAL_TEST_PRELOAD_PATH));
12743
+ candidates.push(path39.join(path39.dirname(Bun.resolveSync("akanjs/package.json", basePath2)), SIGNAL_TEST_PRELOAD_PATH));
12580
12744
  } catch {}
12581
12745
  };
12582
12746
  addResolvedPackageCandidate(target.cwdPath);
12583
12747
  addResolvedPackageCandidate(process.cwd());
12584
- addResolvedPackageCandidate(path38.dirname(Bun.main));
12748
+ addResolvedPackageCandidate(path39.dirname(Bun.main));
12585
12749
  addResolvedPackageCandidate(import.meta.dir);
12586
- candidates.push(path38.join(target.cwdPath, "../../node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path38.join(target.cwdPath, "../../pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path38.join(process.cwd(), "node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path38.join(process.cwd(), "pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path38.join(path38.dirname(Bun.main), "../../akanjs", SIGNAL_TEST_PRELOAD_PATH), path38.resolve(import.meta.dir, "../../akanjs", SIGNAL_TEST_PRELOAD_PATH));
12750
+ candidates.push(path39.join(target.cwdPath, "../../node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.join(target.cwdPath, "../../pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.join(process.cwd(), "node_modules/akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.join(process.cwd(), "pkgs/akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.join(path39.dirname(Bun.main), "../../akanjs", SIGNAL_TEST_PRELOAD_PATH), path39.resolve(import.meta.dir, "../../akanjs", SIGNAL_TEST_PRELOAD_PATH));
12587
12751
  for (const candidate of [...new Set(candidates)]) {
12588
12752
  if (await Bun.file(candidate).exists())
12589
12753
  return candidate;
@@ -12593,7 +12757,7 @@ async function resolveSignalTestPreloadPath(target) {
12593
12757
  // pkgs/@akanjs/devkit/builder.ts
12594
12758
  import { existsSync as existsSync2 } from "fs";
12595
12759
  import { mkdir as mkdir10 } from "fs/promises";
12596
- import path39 from "path";
12760
+ import path40 from "path";
12597
12761
  var SKIP_ENTRY_DIR_SET = new Set(["node_modules", "dist", "build", ".git", ".next"]);
12598
12762
  var assetExtensions = [".css", ".md", ".js", ".png", ".ico", ".svg", ".json", ".template"];
12599
12763
  var assetLoader = Object.fromEntries(assetExtensions.map((ext) => [ext, "file"]));
@@ -12610,14 +12774,14 @@ class Builder {
12610
12774
  #globEntrypoints(cwd, pattern) {
12611
12775
  const glob = new Bun.Glob(pattern);
12612
12776
  return Array.from(glob.scanSync({ cwd, onlyFiles: true })).filter((relativePath) => {
12613
- const segments = relativePath.split(path39.sep);
12777
+ const segments = relativePath.split(path40.sep);
12614
12778
  return !segments.some((segment) => SKIP_ENTRY_DIR_SET.has(segment));
12615
- }).map((rel) => path39.join(cwd, rel));
12779
+ }).map((rel) => path40.join(cwd, rel));
12616
12780
  }
12617
12781
  #globFiles(cwd, pattern = "**/*.*") {
12618
12782
  const glob = new Bun.Glob(pattern);
12619
12783
  return Array.from(glob.scanSync({ cwd, onlyFiles: true })).filter((relativePath) => {
12620
- const segments = relativePath.split(path39.sep);
12784
+ const segments = relativePath.split(path40.sep);
12621
12785
  return !segments.some((segment) => SKIP_ENTRY_DIR_SET.has(segment));
12622
12786
  });
12623
12787
  }
@@ -12625,17 +12789,17 @@ class Builder {
12625
12789
  const out = [];
12626
12790
  for (const p of additionalEntryPoints) {
12627
12791
  if (p.includes("*")) {
12628
- const rel = p.startsWith(`${cwd}/`) || p.startsWith(`${cwd}${path39.sep}`) ? p.slice(cwd.length + 1) : p;
12792
+ const rel = p.startsWith(`${cwd}/`) || p.startsWith(`${cwd}${path40.sep}`) ? p.slice(cwd.length + 1) : p;
12629
12793
  out.push(...this.#globEntrypoints(cwd, rel));
12630
12794
  } else
12631
- out.push(path39.isAbsolute(p) ? p : path39.join(cwd, p));
12795
+ out.push(path40.isAbsolute(p) ? p : path40.join(cwd, p));
12632
12796
  }
12633
12797
  return out;
12634
12798
  }
12635
12799
  #getBuildOptions({ bundle = false, additionalEntryPoints = [] } = {}) {
12636
12800
  const cwd = this.#executor.cwdPath;
12637
12801
  const entrypoints = [
12638
- ...bundle ? [path39.join(cwd, "index.ts")] : this.#globEntrypoints(cwd, "**/*.{ts,tsx}"),
12802
+ ...bundle ? [path40.join(cwd, "index.ts")] : this.#globEntrypoints(cwd, "**/*.{ts,tsx}"),
12639
12803
  ...this.#resolveAdditionalEntrypoints(cwd, additionalEntryPoints)
12640
12804
  ];
12641
12805
  return {
@@ -12656,9 +12820,9 @@ class Builder {
12656
12820
  for (const relativePath of this.#globFiles(cwd)) {
12657
12821
  if (relativePath === "package.json")
12658
12822
  continue;
12659
- const sourcePath = path39.join(cwd, relativePath);
12660
- const targetPath = path39.join(this.#distExecutor.cwdPath, relativePath);
12661
- await mkdir10(path39.dirname(targetPath), { recursive: true });
12823
+ const sourcePath = path40.join(cwd, relativePath);
12824
+ const targetPath = path40.join(this.#distExecutor.cwdPath, relativePath);
12825
+ await mkdir10(path40.dirname(targetPath), { recursive: true });
12662
12826
  await Bun.write(targetPath, Bun.file(sourcePath));
12663
12827
  }
12664
12828
  }
@@ -12669,13 +12833,13 @@ class Builder {
12669
12833
  return withoutFormatDir;
12670
12834
  if (!hasDotSlash && withoutFormatDir === publishedPath)
12671
12835
  return publishedPath;
12672
- const parsed = path39.posix.parse(withoutFormatDir);
12836
+ const parsed = path40.posix.parse(withoutFormatDir);
12673
12837
  if (![".js", ".mjs", ".cjs"].includes(parsed.ext))
12674
12838
  return withoutFormatDir;
12675
- const withoutExt = path39.posix.join(parsed.dir, parsed.name);
12839
+ const withoutExt = path40.posix.join(parsed.dir, parsed.name);
12676
12840
  const sourcePath = withoutExt.startsWith("./") ? withoutExt.slice(2) : withoutExt;
12677
12841
  const sourceCandidates = [`${sourcePath}.ts`, `${sourcePath}.tsx`];
12678
- const matchedSource = sourceCandidates.find((candidate) => existsSync2(path39.join(this.#executor.cwdPath, candidate)));
12842
+ const matchedSource = sourceCandidates.find((candidate) => existsSync2(path40.join(this.#executor.cwdPath, candidate)));
12679
12843
  if (!matchedSource)
12680
12844
  return withoutFormatDir;
12681
12845
  return hasDotSlash ? `./${matchedSource}` : matchedSource;
@@ -12726,7 +12890,7 @@ class Builder {
12726
12890
  // pkgs/@akanjs/devkit/capacitorApp.ts
12727
12891
  import { cp as cp2, mkdir as mkdir11, rm as rm5 } from "fs/promises";
12728
12892
  import os from "os";
12729
- import path40 from "path";
12893
+ import path41 from "path";
12730
12894
  import { select as select2 } from "@inquirer/prompts";
12731
12895
  import { MobileProject } from "@trapezedev/project";
12732
12896
  import { capitalize as capitalize5 } from "akanjs/common";
@@ -12933,13 +13097,13 @@ var rootCapacitorConfigFilenames = [
12933
13097
  "capacitor.config.js",
12934
13098
  "capacitor.config.json"
12935
13099
  ];
12936
- var rootCapacitorConfigPaths = (appRoot) => rootCapacitorConfigFilenames.map((file) => path40.join(appRoot, file));
13100
+ var rootCapacitorConfigPaths = (appRoot) => rootCapacitorConfigFilenames.map((file) => path41.join(appRoot, file));
12937
13101
  async function clearRootCapacitorConfigs(appRoot) {
12938
13102
  await Promise.all(rootCapacitorConfigPaths(appRoot).map((file) => rm5(file, { force: true })));
12939
13103
  }
12940
13104
  async function writeRootCapacitorConfig(appRoot, content) {
12941
13105
  await clearRootCapacitorConfigs(appRoot);
12942
- await Bun.write(path40.join(appRoot, "capacitor.config.json"), content);
13106
+ await Bun.write(path41.join(appRoot, "capacitor.config.json"), content);
12943
13107
  }
12944
13108
  var getLocalIP = () => {
12945
13109
  const interfaces = os.networkInterfaces();
@@ -13048,13 +13212,13 @@ function buildIosNativeRunCommand({
13048
13212
  scheme = "App",
13049
13213
  configuration = "Debug"
13050
13214
  }) {
13051
- const derivedDataPath = path40.join(appRoot, "ios/DerivedData", device.id);
13215
+ const derivedDataPath = path41.join(appRoot, "ios/DerivedData", device.id);
13052
13216
  const productPlatform = device.kind === "device" ? "iphoneos" : "iphonesimulator";
13053
13217
  const destination = device.kind === "device" ? `id=${device.xcodebuildId ?? device.id}` : `platform=iOS Simulator,id=${device.id}`;
13054
13218
  return {
13055
13219
  configuration,
13056
13220
  derivedDataPath,
13057
- appPath: path40.join(derivedDataPath, "Build/Products", `${configuration}-${productPlatform}`, "App.app"),
13221
+ appPath: path41.join(derivedDataPath, "Build/Products", `${configuration}-${productPlatform}`, "App.app"),
13058
13222
  xcodebuildArgs: [
13059
13223
  "-project",
13060
13224
  "App.xcodeproj",
@@ -13264,7 +13428,7 @@ function materializeCapacitorConfig(target, { operation, localServerUrl, localIp
13264
13428
  ...capacitorConfig,
13265
13429
  appId,
13266
13430
  appName,
13267
- webDir: path40.posix.join(".akan", "mobile", name, "www"),
13431
+ webDir: path41.posix.join(".akan", "mobile", name, "www"),
13268
13432
  plugins: {
13269
13433
  CapacitorCookies: { enabled: true },
13270
13434
  ...pluginsConfig,
@@ -13321,10 +13485,10 @@ class CapacitorApp {
13321
13485
  constructor(app, target) {
13322
13486
  this.app = app;
13323
13487
  this.target = target;
13324
- this.targetRootPath = path40.posix.join(".akan", "mobile", this.target.name);
13325
- this.targetRoot = path40.join(this.app.cwdPath, this.targetRootPath);
13326
- this.targetWebRoot = path40.join(this.targetRoot, "www");
13327
- this.targetAssetRoot = path40.join(this.targetRoot, "assets");
13488
+ this.targetRootPath = path41.posix.join(".akan", "mobile", this.target.name);
13489
+ this.targetRoot = path41.join(this.app.cwdPath, this.targetRootPath);
13490
+ this.targetWebRoot = path41.join(this.targetRoot, "www");
13491
+ this.targetAssetRoot = path41.join(this.targetRoot, "assets");
13328
13492
  this.project = new MobileProject(this.app.cwdPath, {
13329
13493
  android: { path: this.androidRootPath },
13330
13494
  ios: { path: this.iosProjectPath }
@@ -13339,9 +13503,9 @@ class CapacitorApp {
13339
13503
  await mkdir11(this.targetRoot, { recursive: true });
13340
13504
  if (regenerate) {
13341
13505
  if (!platform || platform === "ios")
13342
- await rm5(path40.join(this.app.cwdPath, this.iosRootPath), { recursive: true, force: true });
13506
+ await rm5(path41.join(this.app.cwdPath, this.iosRootPath), { recursive: true, force: true });
13343
13507
  if (!platform || platform === "android")
13344
- await rm5(path40.join(this.app.cwdPath, this.androidRootPath), { recursive: true, force: true });
13508
+ await rm5(path41.join(this.app.cwdPath, this.androidRootPath), { recursive: true, force: true });
13345
13509
  }
13346
13510
  const project = this.project;
13347
13511
  await this.project.load();
@@ -13464,7 +13628,7 @@ ${textError instanceof Error ? textError.message : ""}`);
13464
13628
  const xcodebuildArgs = noAllowProvisioningUpdates ? command.xcodebuildArgs : [...command.xcodebuildArgs.slice(0, -1), "-allowProvisioningUpdates", ...command.xcodebuildArgs.slice(-1)];
13465
13629
  try {
13466
13630
  await this.#spawn("xcodebuild", xcodebuildArgs, {
13467
- cwd: path40.join(this.app.cwdPath, this.iosProjectPath),
13631
+ cwd: path41.join(this.app.cwdPath, this.iosProjectPath),
13468
13632
  env: mobileEnv
13469
13633
  });
13470
13634
  const devicectlId = runTarget.devicectlId ?? runTarget.id;
@@ -13489,7 +13653,7 @@ ${textError instanceof Error ? textError.message : ""}`);
13489
13653
  return isRecord2(this.target.ios) && typeof this.target.ios.scheme === "string" ? this.target.ios.scheme : "App";
13490
13654
  }
13491
13655
  async#getIosDevelopmentTeam() {
13492
- const pbxprojPath = path40.join(this.app.cwdPath, this.iosProjectPath, "App.xcodeproj/project.pbxproj");
13656
+ const pbxprojPath = path41.join(this.app.cwdPath, this.iosProjectPath, "App.xcodeproj/project.pbxproj");
13493
13657
  if (!await Bun.file(pbxprojPath).exists())
13494
13658
  return;
13495
13659
  return (await Bun.file(pbxprojPath).text()).match(/DEVELOPMENT_TEAM = ([^;]+);/)?.[1]?.trim();
@@ -13515,7 +13679,7 @@ ${error.message}`;
13515
13679
  await this.#spawnMobile("npx", ["cap", "sync", "android"], { operation, env });
13516
13680
  }
13517
13681
  async#updateAndroidBuildTypes() {
13518
- const appGradle = await FileEditor.create(path40.join(this.app.cwdPath, this.androidRootPath, "app/build.gradle"));
13682
+ const appGradle = await FileEditor.create(path41.join(this.app.cwdPath, this.androidRootPath, "app/build.gradle"));
13519
13683
  const buildTypesBlock = `
13520
13684
  debug {
13521
13685
  applicationIdSuffix ".debug"
@@ -13559,7 +13723,7 @@ ${error.message}`;
13559
13723
  const gradleCommand = isWindows ? "gradlew.bat" : "./gradlew";
13560
13724
  await this.app.spawn(gradleCommand, [assembleType === "apk" ? "assembleRelease" : "bundleRelease"], {
13561
13725
  stdio: "inherit",
13562
- cwd: path40.join(this.app.cwdPath, this.androidRootPath),
13726
+ cwd: path41.join(this.app.cwdPath, this.androidRootPath),
13563
13727
  env: await this.#commandEnv("release", env)
13564
13728
  });
13565
13729
  }
@@ -13567,10 +13731,10 @@ ${error.message}`;
13567
13731
  await this.#spawnMobile("npx", ["cap", "open", "android"], { operation: "local", env: "local" });
13568
13732
  }
13569
13733
  async#ensureAndroidAssetsDir() {
13570
- await mkdir11(path40.join(this.app.cwdPath, this.androidAssetsPath), { recursive: true });
13734
+ await mkdir11(path41.join(this.app.cwdPath, this.androidAssetsPath), { recursive: true });
13571
13735
  }
13572
13736
  async#ensureAndroidDebugKeystore() {
13573
- const keystorePath = path40.join(this.app.cwdPath, this.androidRootPath, "app/debug.keystore");
13737
+ const keystorePath = path41.join(this.app.cwdPath, this.androidRootPath, "app/debug.keystore");
13574
13738
  if (await Bun.file(keystorePath).exists())
13575
13739
  return;
13576
13740
  await this.#spawn("keytool", [
@@ -13609,7 +13773,7 @@ ${error.message}`;
13609
13773
  await this.#spawnMobile("npx", args, { operation, env }, { stdio: "inherit" });
13610
13774
  }
13611
13775
  async#assertAndroidReleaseSigningConfig() {
13612
- const gradlePropertiesPath = path40.join(this.app.cwdPath, this.androidRootPath, "gradle.properties");
13776
+ const gradlePropertiesPath = path41.join(this.app.cwdPath, this.androidRootPath, "gradle.properties");
13613
13777
  const gradleProperties = await Bun.file(gradlePropertiesPath).exists() ? await Bun.file(gradlePropertiesPath).text() : "";
13614
13778
  const missingKeys = getMissingAndroidReleaseSigningKeys({ gradleProperties });
13615
13779
  if (missingKeys.length > 0)
@@ -13636,12 +13800,12 @@ ${error.message}`;
13636
13800
  await this.#prepareAndroid({ operation: "release", env: "main" });
13637
13801
  }
13638
13802
  async prepareWww() {
13639
- const htmlSource = path40.join(this.app.dist.cwdPath, "csr", targetHtmlFilename(this.target));
13803
+ const htmlSource = path41.join(this.app.dist.cwdPath, "csr", targetHtmlFilename(this.target));
13640
13804
  if (!await Bun.file(htmlSource).exists())
13641
13805
  throw new Error(`CSR html for mobile target '${this.target.name}' not found: ${htmlSource}`);
13642
13806
  await rm5(this.targetWebRoot, { recursive: true, force: true });
13643
13807
  await mkdir11(this.targetWebRoot, { recursive: true });
13644
- await Bun.write(path40.join(this.targetWebRoot, "index.html"), this.#injectMobileTargetMeta(await Bun.file(htmlSource).text()));
13808
+ await Bun.write(path41.join(this.targetWebRoot, "index.html"), this.#injectMobileTargetMeta(await Bun.file(htmlSource).text()));
13645
13809
  }
13646
13810
  #injectMobileTargetMeta(html) {
13647
13811
  const basePath2 = this.target.basePath?.replace(/^\/+|\/+$/g, "") ?? "";
@@ -13661,7 +13825,7 @@ ${error.message}`;
13661
13825
  });
13662
13826
  const content = `${JSON.stringify(config, null, 2)}
13663
13827
  `;
13664
- await Bun.write(path40.join(this.targetRoot, "capacitor.config.json"), content);
13828
+ await Bun.write(path41.join(this.targetRoot, "capacitor.config.json"), content);
13665
13829
  return content;
13666
13830
  }
13667
13831
  async#prepareTargetAssets() {
@@ -13669,11 +13833,11 @@ ${error.message}`;
13669
13833
  return;
13670
13834
  await mkdir11(this.targetAssetRoot, { recursive: true });
13671
13835
  if (this.target.assets.icon)
13672
- await cp2(path40.join(this.app.cwdPath, this.target.assets.icon), path40.join(this.targetAssetRoot, "icon.png"), {
13836
+ await cp2(path41.join(this.app.cwdPath, this.target.assets.icon), path41.join(this.targetAssetRoot, "icon.png"), {
13673
13837
  force: true
13674
13838
  });
13675
13839
  if (this.target.assets.splash)
13676
- await cp2(path40.join(this.app.cwdPath, this.target.assets.splash), path40.join(this.targetAssetRoot, "splash.png"), {
13840
+ await cp2(path41.join(this.app.cwdPath, this.target.assets.splash), path41.join(this.targetAssetRoot, "splash.png"), {
13677
13841
  force: true
13678
13842
  });
13679
13843
  }
@@ -13681,11 +13845,11 @@ ${error.message}`;
13681
13845
  const files = this.target.files?.[platform];
13682
13846
  if (!files)
13683
13847
  return;
13684
- const platformRoot = path40.join(this.app.cwdPath, platform === "ios" ? this.iosRootPath : this.androidRootPath);
13848
+ const platformRoot = path41.join(this.app.cwdPath, platform === "ios" ? this.iosRootPath : this.androidRootPath);
13685
13849
  await Promise.all(Object.entries(files).map(async ([to, from]) => {
13686
- const targetPath = path40.join(platformRoot, to);
13687
- await mkdir11(path40.dirname(targetPath), { recursive: true });
13688
- await cp2(path40.join(this.app.cwdPath, from), targetPath, { force: true });
13850
+ const targetPath = path41.join(platformRoot, to);
13851
+ await mkdir11(path41.dirname(targetPath), { recursive: true });
13852
+ await cp2(path41.join(this.app.cwdPath, from), targetPath, { force: true });
13689
13853
  }));
13690
13854
  }
13691
13855
  async#generateAssets({ operation, env }) {
@@ -13695,7 +13859,7 @@ ${error.message}`;
13695
13859
  "@capacitor/assets",
13696
13860
  "generate",
13697
13861
  "--assetPath",
13698
- path40.posix.join(this.targetRootPath, "assets"),
13862
+ path41.posix.join(this.targetRootPath, "assets"),
13699
13863
  "--iosProject",
13700
13864
  this.iosProjectPath,
13701
13865
  "--androidProject",
@@ -13905,7 +14069,7 @@ var Pkg = createInternalArgToken("Pkg");
13905
14069
  var Module = createInternalArgToken("Module");
13906
14070
  var Workspace = createInternalArgToken("Workspace");
13907
14071
  // pkgs/@akanjs/devkit/commandDecorators/command.ts
13908
- import path41 from "path";
14072
+ import path42 from "path";
13909
14073
  import { confirm, input as input2, select as select3 } from "@inquirer/prompts";
13910
14074
  import { Logger as Logger10 } from "akanjs/common";
13911
14075
  import chalk6 from "chalk";
@@ -14409,7 +14573,7 @@ var runCommands = async (...commands) => {
14409
14573
  process.exit(1);
14410
14574
  });
14411
14575
  const __dirname2 = getDirname(import.meta.url);
14412
- const packageJsonCandidates = [`${path41.dirname(Bun.main)}/package.json`, `${__dirname2}/../package.json`];
14576
+ const packageJsonCandidates = [`${path42.dirname(Bun.main)}/package.json`, `${__dirname2}/../package.json`];
14413
14577
  let cliPackageJson = null;
14414
14578
  for (const packageJsonPath of packageJsonCandidates) {
14415
14579
  if (!await FileSys.fileExists(packageJsonPath))
@@ -14689,8 +14853,8 @@ var scanModuleSpecifiers = (source2, filePath, includeExports) => {
14689
14853
  return importSpecifiers;
14690
14854
  };
14691
14855
  var parseTsConfig = (tsConfigPath = "./tsconfig.json") => {
14692
- const configFile = ts10.readConfigFile(tsConfigPath, (path42) => {
14693
- return ts10.sys.readFile(path42);
14856
+ const configFile = ts10.readConfigFile(tsConfigPath, (path43) => {
14857
+ return ts10.sys.readFile(path43);
14694
14858
  });
14695
14859
  return ts10.parseJsonConfigFileContent(configFile.config, ts10.sys, realpathSync(tsConfigPath).replace(/[^/\\]+$/, ""));
14696
14860
  };
@@ -14906,6 +15070,571 @@ ${document}
14906
15070
  `;
14907
15071
  }
14908
15072
  }
15073
+ // pkgs/@akanjs/devkit/qualityScanner.ts
15074
+ import { createHash } from "crypto";
15075
+ import { readdir as readdir3, readFile as readFile2, stat as stat4 } from "fs/promises";
15076
+ import path43 from "path";
15077
+ import ignore2 from "ignore";
15078
+ import ts11 from "typescript";
15079
+ var MAX_FILE_LINES = 2000;
15080
+ var PLACEHOLDER_EXPORT_NAMES = new Set([
15081
+ "aa",
15082
+ "dumb",
15083
+ "dumb2",
15084
+ "someBaseLogic",
15085
+ "someCommonLogic",
15086
+ "someFrontendLogic",
15087
+ "someBackendLogic"
15088
+ ]);
15089
+ var SUGGESTED_RULES = [
15090
+ "Keep generated scanSync index files out of hand-written changes; generated indexes should only contain one-depth export statements.",
15091
+ "Generated Akan index files should not contain placeholder exports such as aa, dumb, dumb2, or some*Logic.",
15092
+ "Dictionary text should not contain scaffold wording, misspellings, or stale copied domain nouns.",
15093
+ "Warn earlier on very long Akan files: 500 lines for services, 800 lines for Template/Zone files, and 1000 lines for Util files.",
15094
+ "Global declarations, Window augmentation, and prototype mutation should stay in explicitly approved low-level integration files.",
15095
+ "Keep app root folders small and conventional: application code belongs under common, env, lib, page, private, public, script, srvkit, ui, or webkit.",
15096
+ "Keep apps/*/lib and libs/*/lib root files limited to generated support facets such as cnst.ts, db.ts, dict.ts, sig.ts, srv.ts, st.ts, useClient.ts, and useServer.ts.",
15097
+ "Use domain module folders consistently: lib/<model> for database modules, lib/_<service> for service modules, and lib/__scalar/<scalar> for scalar modules.",
15098
+ "Keep module UI filenames predictable: database modules use <Model>.Template/Unit/Util/View/Zone.tsx, service modules use <Service>.Util/Zone.tsx, and scalar modules use <Scalar>.Template/Unit.tsx.",
15099
+ "Move shared app utilities to apps/*/common instead of creating apps/*/base.",
15100
+ "Avoid large mixed-purpose class files; class export files should import helpers from neighboring utility files instead of declaring them inline."
15101
+ ];
15102
+ var APP_ROOT_FILES = new Set([
15103
+ "akan.app.json",
15104
+ "akan.config.ts",
15105
+ "capacitor.config.ts",
15106
+ "client.ts",
15107
+ "main.ts",
15108
+ "package.json",
15109
+ "server.ts",
15110
+ "tsconfig.json"
15111
+ ]);
15112
+ var LIB_ROOT_FILES = new Set([
15113
+ "cnst.ts",
15114
+ "db.ts",
15115
+ "dict.ts",
15116
+ "option.ts",
15117
+ "sig.ts",
15118
+ "srv.ts",
15119
+ "st.ts",
15120
+ "useClient.ts",
15121
+ "useServer.ts"
15122
+ ]);
15123
+ var CONVENTION_SUFFIXES = [
15124
+ ".constant.ts",
15125
+ ".dictionary.ts",
15126
+ ".document.ts",
15127
+ ".service.ts",
15128
+ ".signal.ts",
15129
+ ".store.ts"
15130
+ ];
15131
+
15132
+ class AkanQualityScanner {
15133
+ async scan(workspaceRoot) {
15134
+ const targetFiles = await this.#collectTargetFiles(workspaceRoot);
15135
+ const sourceFiles = await Promise.all(targetFiles.map((file) => this.#readSourceFile(workspaceRoot, file)));
15136
+ const warnings = [
15137
+ ...this.#scanGlobalQuality(sourceFiles),
15138
+ ...sourceFiles.flatMap((sourceFile2) => this.#scanSingleFileQuality(sourceFile2)),
15139
+ ...sourceFiles.flatMap((sourceFile2) => this.#scanConventionQuality(sourceFile2)),
15140
+ ...sourceFiles.flatMap((sourceFile2) => this.#scanLayoutQuality(sourceFile2))
15141
+ ];
15142
+ return {
15143
+ workspaceRoot,
15144
+ scannedFiles: sourceFiles.length,
15145
+ warnings: warnings.sort(compareWarnings),
15146
+ suggestedRules: SUGGESTED_RULES
15147
+ };
15148
+ }
15149
+ async#collectTargetFiles(workspaceRoot) {
15150
+ const ignoreFilter = ignore2().add(await this.#readGitIgnore(workspaceRoot));
15151
+ const files = [];
15152
+ for (const targetRoot of ["apps", "libs"]) {
15153
+ const absoluteTargetRoot = path43.join(workspaceRoot, targetRoot);
15154
+ if (!await isDirectory(absoluteTargetRoot))
15155
+ continue;
15156
+ await this.#walkTargetFiles(workspaceRoot, absoluteTargetRoot, ignoreFilter, files);
15157
+ }
15158
+ return files.sort();
15159
+ }
15160
+ async#readGitIgnore(workspaceRoot) {
15161
+ const gitIgnorePath = path43.join(workspaceRoot, ".gitignore");
15162
+ if (!await Bun.file(gitIgnorePath).exists())
15163
+ return [];
15164
+ return (await readFile2(gitIgnorePath, "utf8")).split(/\r?\n/);
15165
+ }
15166
+ async#walkTargetFiles(workspaceRoot, currentPath, ignoreFilter, files) {
15167
+ const entries = await readdir3(currentPath, { withFileTypes: true });
15168
+ for (const entry of entries) {
15169
+ const absolutePath = path43.join(currentPath, entry.name);
15170
+ const relativePath = toPosix(path43.relative(workspaceRoot, absolutePath));
15171
+ if (shouldSkipPath(relativePath, entry.isDirectory(), ignoreFilter))
15172
+ continue;
15173
+ if (entry.isDirectory()) {
15174
+ await this.#walkTargetFiles(workspaceRoot, absolutePath, ignoreFilter, files);
15175
+ continue;
15176
+ }
15177
+ if ((relativePath.endsWith(".ts") || relativePath.endsWith(".tsx")) && !relativePath.endsWith(".d.ts")) {
15178
+ files.push(relativePath);
15179
+ }
15180
+ }
15181
+ }
15182
+ async#readSourceFile(workspaceRoot, file) {
15183
+ const absolutePath = path43.join(workspaceRoot, file);
15184
+ const content = await readFile2(absolutePath, "utf8");
15185
+ return {
15186
+ file,
15187
+ absolutePath,
15188
+ content,
15189
+ sourceFile: ts11.createSourceFile(file, content, ts11.ScriptTarget.Latest, true, getScriptKind(file))
15190
+ };
15191
+ }
15192
+ #scanGlobalQuality(sourceFiles) {
15193
+ const exportedFunctionLikes = sourceFiles.flatMap((sourceFile2) => getExportedFunctionLikes(sourceFile2));
15194
+ const warnings = [];
15195
+ for (const [name, declarations] of groupBy(exportedFunctionLikes, (declaration) => declaration.name)) {
15196
+ if (declarations.length < 2)
15197
+ continue;
15198
+ warnings.push({
15199
+ rule: "akan.global.duplicate-exported-function-name",
15200
+ scope: "global",
15201
+ severity: "warning",
15202
+ message: `Exported function or class name "${name}" is declared in ${declarations.length} files.`,
15203
+ locations: declarations.map(({ file, line }) => ({ file, line }))
15204
+ });
15205
+ }
15206
+ const declarationsWithBody = exportedFunctionLikes.filter((declaration) => declaration.bodyFingerprint);
15207
+ for (const [fingerprint, declarations] of groupBy(declarationsWithBody, (declaration) => declaration.bodyFingerprint ?? "")) {
15208
+ const uniqueNames = new Set(declarations.map((declaration) => declaration.name));
15209
+ if (declarations.length < 2 || uniqueNames.size < 2 || fingerprint === "")
15210
+ continue;
15211
+ warnings.push({
15212
+ rule: "akan.global.duplicate-exported-function-body",
15213
+ scope: "global",
15214
+ severity: "warning",
15215
+ message: `Exported functions/classes share the same implementation body: ${declarations.map((declaration) => declaration.name).join(", ")}.`,
15216
+ locations: declarations.map(({ file, line }) => ({ file, line }))
15217
+ });
15218
+ }
15219
+ return warnings;
15220
+ }
15221
+ #scanSingleFileQuality(sourceFile2) {
15222
+ const warnings = [];
15223
+ const lineCount = sourceFile2.content.split(/\r?\n/).length;
15224
+ const recommendedLineLimit = getRecommendedLineLimit(sourceFile2.file);
15225
+ if (recommendedLineLimit && lineCount > recommendedLineLimit) {
15226
+ warnings.push({
15227
+ rule: "akan.file.recommended-max-lines",
15228
+ scope: "file",
15229
+ severity: "warning",
15230
+ file: sourceFile2.file,
15231
+ message: `File has ${lineCount} lines. Recommended limit for this file type is ${recommendedLineLimit} lines.`
15232
+ });
15233
+ }
15234
+ if (lineCount > MAX_FILE_LINES) {
15235
+ warnings.push({
15236
+ rule: "akan.file.max-lines",
15237
+ scope: "file",
15238
+ severity: "warning",
15239
+ file: sourceFile2.file,
15240
+ message: `File has ${lineCount} lines. Keep single files under ${MAX_FILE_LINES} lines.`
15241
+ });
15242
+ }
15243
+ warnings.push(...getPlaceholderExportWarnings(sourceFile2));
15244
+ warnings.push(...getDictionaryTextWarnings(sourceFile2));
15245
+ warnings.push(...getGlobalMutationWarnings(sourceFile2));
15246
+ const exportedClassNames = getExportedClassNames(sourceFile2.sourceFile);
15247
+ if (exportedClassNames.length === 0)
15248
+ return warnings;
15249
+ const allowedInterfaceNames = new Set(exportedClassNames.map((name) => `${name}Options`));
15250
+ for (const declaration of getTopLevelDeclarations(sourceFile2)) {
15251
+ if (declaration.kind === "class" && exportedClassNames.includes(declaration.name))
15252
+ continue;
15253
+ if (declaration.kind === "interface" && allowedInterfaceNames.has(declaration.name))
15254
+ continue;
15255
+ warnings.push({
15256
+ rule: "akan.file.class-export-global-declaration",
15257
+ scope: "file",
15258
+ severity: "warning",
15259
+ file: sourceFile2.file,
15260
+ line: declaration.line,
15261
+ message: `Class export files should not declare top-level ${declaration.kind} "${declaration.name}". Move helpers to another file and import them.`
15262
+ });
15263
+ }
15264
+ return warnings;
15265
+ }
15266
+ #scanConventionQuality(sourceFile2) {
15267
+ const suffix = CONVENTION_SUFFIXES.find((candidate) => sourceFile2.file.endsWith(candidate));
15268
+ if (!suffix)
15269
+ return [];
15270
+ const modelName = toPascalCase(path43.basename(sourceFile2.file, suffix));
15271
+ const warnings = [];
15272
+ for (const declaration of getTopLevelDeclarations(sourceFile2)) {
15273
+ if (isAllowedConventionDeclaration(suffix, modelName, declaration))
15274
+ continue;
15275
+ warnings.push({
15276
+ rule: `akan.convention${suffix.replace(".ts", "")}`,
15277
+ scope: "convention",
15278
+ severity: "warning",
15279
+ file: sourceFile2.file,
15280
+ line: declaration.line,
15281
+ message: `${path43.basename(sourceFile2.file)} should not declare top-level ${declaration.kind} "${declaration.name}". Allowed declarations: ${getConventionDescription(suffix, modelName)}.`
15282
+ });
15283
+ }
15284
+ return warnings;
15285
+ }
15286
+ #scanLayoutQuality(sourceFile2) {
15287
+ const segments = sourceFile2.file.split("/");
15288
+ const warnings = [];
15289
+ if (segments[0] === "apps" && segments.length === 3 && !APP_ROOT_FILES.has(segments[2])) {
15290
+ warnings.push({
15291
+ rule: "akan.layout.app-root-file",
15292
+ scope: "layout",
15293
+ severity: "warning",
15294
+ file: sourceFile2.file,
15295
+ message: `Unexpected app root file "${segments[2]}". Keep application code in conventional app folders.`
15296
+ });
15297
+ }
15298
+ const libRootFile = getLibRootFile(sourceFile2.file);
15299
+ if (libRootFile && !LIB_ROOT_FILES.has(libRootFile)) {
15300
+ warnings.push({
15301
+ rule: "akan.layout.lib-root-file",
15302
+ scope: "layout",
15303
+ severity: "warning",
15304
+ file: sourceFile2.file,
15305
+ message: `Unexpected lib root file "${libRootFile}". Keep direct lib root files limited to generated support facets.`
15306
+ });
15307
+ }
15308
+ const moduleUiWarning = getModuleUiWarning(sourceFile2.file);
15309
+ if (moduleUiWarning)
15310
+ warnings.push(moduleUiWarning);
15311
+ return warnings;
15312
+ }
15313
+ }
15314
+ function formatQualityScanResult(result) {
15315
+ const sections = [
15316
+ "Akan Code Quality Scan",
15317
+ `workspace: ${result.workspaceRoot}`,
15318
+ `scanned files: ${result.scannedFiles}`,
15319
+ `warnings: ${result.warnings.length}`,
15320
+ "",
15321
+ "Warnings:",
15322
+ "",
15323
+ ...formatQualityWarnings(result.warnings),
15324
+ "",
15325
+ "Suggested quality rules:",
15326
+ "",
15327
+ ...result.suggestedRules.map((rule) => ` - ${rule}`)
15328
+ ];
15329
+ return sections.join(`
15330
+ `);
15331
+ }
15332
+ function formatQualityWarnings(warnings) {
15333
+ if (warnings.length === 0)
15334
+ return ["No warnings found."];
15335
+ return warnings.flatMap((warning) => {
15336
+ const location = formatQualityLocation(warning.file, warning.line);
15337
+ const lines = [`${location} - warning ${warning.rule}: ${warning.message}`];
15338
+ if (warning.locations?.length) {
15339
+ lines.push(...warning.locations.map(({ file, line }) => ` note: related location ${formatQualityLocation(file, line)}`));
15340
+ }
15341
+ return lines;
15342
+ });
15343
+ }
15344
+ function formatQualityLocation(file, line) {
15345
+ return `${file ?? "<global>"}:${line ?? 1}:1`;
15346
+ }
15347
+ function getExportedFunctionLikes(sourceFile2) {
15348
+ const declarations = [];
15349
+ for (const statement of sourceFile2.sourceFile.statements) {
15350
+ if (ts11.isFunctionDeclaration(statement) && statement.name && isExported(statement)) {
15351
+ declarations.push({
15352
+ name: statement.name.text,
15353
+ kind: "function",
15354
+ file: sourceFile2.file,
15355
+ line: getLine(sourceFile2.sourceFile, statement),
15356
+ bodyFingerprint: getBodyFingerprint(sourceFile2.sourceFile, statement.body)
15357
+ });
15358
+ }
15359
+ if (ts11.isClassDeclaration(statement) && statement.name && isExported(statement)) {
15360
+ declarations.push({
15361
+ name: statement.name.text,
15362
+ kind: "class",
15363
+ file: sourceFile2.file,
15364
+ line: getLine(sourceFile2.sourceFile, statement),
15365
+ bodyFingerprint: getBodyFingerprint(sourceFile2.sourceFile, statement)
15366
+ });
15367
+ }
15368
+ if (ts11.isVariableStatement(statement) && isExported(statement)) {
15369
+ for (const declaration of statement.declarationList.declarations) {
15370
+ if (!ts11.isIdentifier(declaration.name) || !isFunctionLikeInitializer(declaration.initializer))
15371
+ continue;
15372
+ declarations.push({
15373
+ name: declaration.name.text,
15374
+ kind: "function-variable",
15375
+ file: sourceFile2.file,
15376
+ line: getLine(sourceFile2.sourceFile, declaration),
15377
+ bodyFingerprint: getBodyFingerprint(sourceFile2.sourceFile, declaration.initializer)
15378
+ });
15379
+ }
15380
+ }
15381
+ }
15382
+ return declarations;
15383
+ }
15384
+ function getExportedClassNames(sourceFile2) {
15385
+ return sourceFile2.statements.filter((statement) => ts11.isClassDeclaration(statement) && !!statement.name).filter((statement) => isExported(statement)).map((statement) => statement.name.text);
15386
+ }
15387
+ function getPlaceholderExportWarnings(sourceFile2) {
15388
+ if (!sourceFile2.file.endsWith("/index.ts") && !sourceFile2.file.endsWith("/index.tsx"))
15389
+ return [];
15390
+ return getTopLevelDeclarations(sourceFile2).filter((declaration) => declaration.exported && PLACEHOLDER_EXPORT_NAMES.has(declaration.name)).map((declaration) => ({
15391
+ rule: "akan.file.placeholder-export",
15392
+ scope: "file",
15393
+ severity: "warning",
15394
+ file: sourceFile2.file,
15395
+ line: declaration.line,
15396
+ message: `Generated or barrel index file should not export placeholder "${declaration.name}".`
15397
+ }));
15398
+ }
15399
+ function getDictionaryTextWarnings(sourceFile2) {
15400
+ if (!sourceFile2.file.endsWith(".dictionary.ts"))
15401
+ return [];
15402
+ const stalePatterns = [
15403
+ { pattern: /\b[A-Z][A-Za-z0-9]* description\b/, label: "scaffold description text" },
15404
+ { pattern: /settting/, label: "misspelling: settting" },
15405
+ { pattern: /\uBC30\uB108 \uC218/, label: "stale copied Korean domain noun: \uBC30\uB108 \uC218" }
15406
+ ];
15407
+ return stalePatterns.flatMap(({ pattern, label }) => findPatternLines(sourceFile2.content, pattern).map((line) => ({
15408
+ rule: "akan.file.dictionary-stale-text",
15409
+ scope: "file",
15410
+ severity: "warning",
15411
+ file: sourceFile2.file,
15412
+ line,
15413
+ message: `Dictionary text appears to contain ${label}.`
15414
+ })));
15415
+ }
15416
+ function getGlobalMutationWarnings(sourceFile2) {
15417
+ const warnings = [];
15418
+ for (const statement of sourceFile2.sourceFile.statements) {
15419
+ if (ts11.isModuleDeclaration(statement) && statement.name.getText(sourceFile2.sourceFile) === "global") {
15420
+ warnings.push({
15421
+ rule: "akan.file.global-declaration",
15422
+ scope: "file",
15423
+ severity: "warning",
15424
+ file: sourceFile2.file,
15425
+ line: getLine(sourceFile2.sourceFile, statement),
15426
+ message: "Global declarations require an explicit low-level integration allowlist."
15427
+ });
15428
+ }
15429
+ if (ts11.isInterfaceDeclaration(statement) && statement.name.text === "Window") {
15430
+ warnings.push({
15431
+ rule: "akan.file.window-augmentation",
15432
+ scope: "file",
15433
+ severity: "warning",
15434
+ file: sourceFile2.file,
15435
+ line: getLine(sourceFile2.sourceFile, statement),
15436
+ message: "Window augmentation should be isolated to approved browser integration files."
15437
+ });
15438
+ }
15439
+ if (ts11.isExpressionStatement(statement) && statement.expression.getText(sourceFile2.sourceFile).includes(".prototype.")) {
15440
+ warnings.push({
15441
+ rule: "akan.file.prototype-mutation",
15442
+ scope: "file",
15443
+ severity: "warning",
15444
+ file: sourceFile2.file,
15445
+ line: getLine(sourceFile2.sourceFile, statement),
15446
+ message: "Prototype mutation should be avoided or isolated to approved low-level integration files."
15447
+ });
15448
+ }
15449
+ }
15450
+ return warnings;
15451
+ }
15452
+ function getTopLevelDeclarations(sourceFile2) {
15453
+ return sourceFile2.sourceFile.statements.flatMap((statement) => getTopLevelDeclaration(sourceFile2.sourceFile, statement));
15454
+ }
15455
+ function getTopLevelDeclaration(sourceFile2, statement) {
15456
+ const line = getLine(sourceFile2, statement);
15457
+ if (ts11.isClassDeclaration(statement) && statement.name) {
15458
+ return [{ name: statement.name.text, kind: "class", line, exported: isExported(statement), node: statement }];
15459
+ }
15460
+ if (ts11.isFunctionDeclaration(statement) && statement.name) {
15461
+ return [{ name: statement.name.text, kind: "function", line, exported: isExported(statement), node: statement }];
15462
+ }
15463
+ if (ts11.isInterfaceDeclaration(statement)) {
15464
+ return [{ name: statement.name.text, kind: "interface", line, exported: isExported(statement), node: statement }];
15465
+ }
15466
+ if (ts11.isTypeAliasDeclaration(statement)) {
15467
+ return [{ name: statement.name.text, kind: "type", line, exported: isExported(statement), node: statement }];
15468
+ }
15469
+ if (ts11.isEnumDeclaration(statement)) {
15470
+ return [{ name: statement.name.text, kind: "enum", line, exported: isExported(statement), node: statement }];
15471
+ }
15472
+ if (ts11.isVariableStatement(statement)) {
15473
+ return statement.declarationList.declarations.filter((declaration) => ts11.isIdentifier(declaration.name)).map((declaration) => ({
15474
+ name: declaration.name.text,
15475
+ kind: "variable",
15476
+ line: getLine(sourceFile2, declaration),
15477
+ exported: isExported(statement),
15478
+ node: statement
15479
+ }));
15480
+ }
15481
+ if (ts11.isExportDeclaration(statement)) {
15482
+ return [{ name: "export declaration", kind: "export", line, exported: true, node: statement }];
15483
+ }
15484
+ return [];
15485
+ }
15486
+ function isAllowedConventionDeclaration(suffix, modelName, declaration) {
15487
+ if (suffix === ".dictionary.ts")
15488
+ return isExportedConst(declaration) && declaration.name === "dictionary";
15489
+ if (suffix === ".constant.ts")
15490
+ return isAllowedConstantDeclaration(modelName, declaration);
15491
+ if (suffix === ".document.ts")
15492
+ return declaration.kind === "class" && [`${modelName}Filter`, modelName, `${modelName}Model`].includes(declaration.name);
15493
+ if (suffix === ".service.ts")
15494
+ return declaration.kind === "class" && declaration.name === `${modelName}Service`;
15495
+ if (suffix === ".signal.ts")
15496
+ return declaration.kind === "class" && [`${modelName}Internal`, `${modelName}Slice`, `${modelName}Endpoint`].includes(declaration.name);
15497
+ if (suffix === ".store.ts")
15498
+ return declaration.kind === "class" && declaration.name === `${modelName}Store`;
15499
+ return false;
15500
+ }
15501
+ function isAllowedConstantDeclaration(modelName, declaration) {
15502
+ if (declaration.kind !== "class")
15503
+ return false;
15504
+ if ([`${modelName}Input`, `${modelName}Object`, modelName, `Light${modelName}`, `${modelName}Insight`].includes(declaration.name))
15505
+ return true;
15506
+ if (!ts11.isClassDeclaration(declaration.node))
15507
+ return false;
15508
+ const heritageClause = declaration.node.heritageClauses?.find((clause) => clause.token === ts11.SyntaxKind.ExtendsKeyword);
15509
+ const expression = heritageClause?.types[0]?.expression;
15510
+ return !!expression && expression.getText().startsWith("enumOf(");
15511
+ }
15512
+ function getConventionDescription(suffix, modelName) {
15513
+ if (suffix === ".dictionary.ts")
15514
+ return "export const dictionary";
15515
+ if (suffix === ".constant.ts")
15516
+ return `${modelName}Input, ${modelName}Object, ${modelName}, Light${modelName}, ${modelName}Insight, or enumOf classes`;
15517
+ if (suffix === ".document.ts")
15518
+ return `${modelName}Filter, ${modelName}, or ${modelName}Model`;
15519
+ if (suffix === ".service.ts")
15520
+ return `${modelName}Service`;
15521
+ if (suffix === ".signal.ts")
15522
+ return `${modelName}Internal, ${modelName}Slice, or ${modelName}Endpoint`;
15523
+ return `${modelName}Store`;
15524
+ }
15525
+ function getLibRootFile(file) {
15526
+ const segments = file.split("/");
15527
+ if (segments[0] === "libs" && segments.length === 4 && segments[2] === "lib")
15528
+ return segments[3];
15529
+ if (segments[0] === "apps" && segments.length === 5 && segments[2] === "lib")
15530
+ return segments[4];
15531
+ return null;
15532
+ }
15533
+ function getModuleUiWarning(file) {
15534
+ if (!file.endsWith(".tsx"))
15535
+ return null;
15536
+ const moduleInfo = getModuleInfo(file);
15537
+ if (!moduleInfo)
15538
+ return null;
15539
+ const { moduleName, fileName, kind } = moduleInfo;
15540
+ const pascalName = toPascalCase(moduleName.replace(/^_+/, ""));
15541
+ const allowedSuffixes = kind === "database" ? ["Template", "Unit", "Util", "View", "Zone"] : kind === "service" ? ["Util", "Zone"] : ["Template", "Unit"];
15542
+ const allowedFileNames = new Set(allowedSuffixes.map((suffix) => `${pascalName}.${suffix}.tsx`));
15543
+ if (allowedFileNames.has(fileName) || fileName.endsWith(".test.tsx") || fileName.endsWith(".spec.tsx"))
15544
+ return null;
15545
+ return {
15546
+ rule: "akan.layout.module-ui-file",
15547
+ scope: "layout",
15548
+ severity: "warning",
15549
+ file,
15550
+ message: `Unexpected ${kind} module UI filename "${fileName}". Expected one of: ${[...allowedFileNames].join(", ")}.`
15551
+ };
15552
+ }
15553
+ function getModuleInfo(file) {
15554
+ const segments = file.split("/");
15555
+ const libIndex = segments.indexOf("lib");
15556
+ if (libIndex < 0)
15557
+ return null;
15558
+ const moduleName = segments[libIndex + 1];
15559
+ if (moduleName === "__scalar") {
15560
+ if (segments.length !== libIndex + 4)
15561
+ return null;
15562
+ return { moduleName: segments[libIndex + 2], fileName: segments[libIndex + 3], kind: "scalar" };
15563
+ }
15564
+ if (segments.length !== libIndex + 3)
15565
+ return null;
15566
+ const fileName = segments[libIndex + 2];
15567
+ if (moduleName.startsWith("__")) {
15568
+ const scalarName = moduleName === "__scalar" ? segments[libIndex + 2] : moduleName;
15569
+ return { moduleName: scalarName, fileName, kind: "scalar" };
15570
+ }
15571
+ if (moduleName.startsWith("_"))
15572
+ return { moduleName, fileName, kind: "service" };
15573
+ return { moduleName, fileName, kind: "database" };
15574
+ }
15575
+ function isExportedConst(declaration) {
15576
+ return declaration.exported && ts11.isVariableStatement(declaration.node) && (declaration.node.declarationList.flags & ts11.NodeFlags.Const) !== 0;
15577
+ }
15578
+ function isExported(node) {
15579
+ return !!ts11.getCombinedModifierFlags(node) && !!(ts11.getCombinedModifierFlags(node) & ts11.ModifierFlags.Export);
15580
+ }
15581
+ function isFunctionLikeInitializer(node) {
15582
+ return !!node && (ts11.isArrowFunction(node) || ts11.isFunctionExpression(node));
15583
+ }
15584
+ function getBodyFingerprint(sourceFile2, node) {
15585
+ if (!node)
15586
+ return;
15587
+ const normalizedBody = node.getText(sourceFile2).replace(/\s+/g, " ").trim();
15588
+ if (normalizedBody.length < 80)
15589
+ return;
15590
+ return createHash("sha256").update(normalizedBody).digest("hex");
15591
+ }
15592
+ function shouldSkipPath(relativePath, isDirectory, ignoreFilter) {
15593
+ const ignorePath = isDirectory ? `${relativePath}/` : relativePath;
15594
+ return relativePath === ".git" || relativePath.includes("/.git/") || relativePath.includes("/node_modules/") || ignoreFilter.ignores(relativePath) || ignoreFilter.ignores(ignorePath);
15595
+ }
15596
+ async function isDirectory(absolutePath) {
15597
+ try {
15598
+ return (await stat4(absolutePath)).isDirectory();
15599
+ } catch {
15600
+ return false;
15601
+ }
15602
+ }
15603
+ function getScriptKind(file) {
15604
+ return file.endsWith(".tsx") ? ts11.ScriptKind.TSX : ts11.ScriptKind.TS;
15605
+ }
15606
+ function getLine(sourceFile2, node) {
15607
+ return sourceFile2.getLineAndCharacterOfPosition(node.getStart(sourceFile2)).line + 1;
15608
+ }
15609
+ function getRecommendedLineLimit(file) {
15610
+ if (file.endsWith(".service.ts"))
15611
+ return 500;
15612
+ if (file.endsWith(".Template.tsx") || file.endsWith(".Zone.tsx"))
15613
+ return 800;
15614
+ if (file.endsWith(".Util.tsx"))
15615
+ return 1000;
15616
+ return null;
15617
+ }
15618
+ function findPatternLines(content, pattern) {
15619
+ return content.split(/\r?\n/).flatMap((line, index) => pattern.test(line) ? [index + 1] : []);
15620
+ }
15621
+ function toPascalCase(value) {
15622
+ return value.replace(/(^|[-_./])([a-zA-Z0-9])/g, (_, __, char) => char.toUpperCase()).replace(/[-_./]/g, "");
15623
+ }
15624
+ function toPosix(value) {
15625
+ return value.split(path43.sep).join("/");
15626
+ }
15627
+ function groupBy(items, getKey) {
15628
+ const grouped = new Map;
15629
+ for (const item of items) {
15630
+ const key = getKey(item);
15631
+ grouped.set(key, [...grouped.get(key) ?? [], item]);
15632
+ }
15633
+ return grouped;
15634
+ }
15635
+ function compareWarnings(a, b) {
15636
+ return a.scope.localeCompare(b.scope) || (a.file ?? "").localeCompare(b.file ?? "") || (a.line ?? 0) - (b.line ?? 0) || a.rule.localeCompare(b.rule);
15637
+ }
14909
15638
  // pkgs/@akanjs/devkit/selectModel.ts
14910
15639
  import { select as select5 } from "@inquirer/prompts";
14911
15640
  // pkgs/@akanjs/devkit/streamAi.ts
@@ -15867,7 +16596,7 @@ class ApplicationCommand extends command("application", [ApplicationScript], ({
15867
16596
  import { Logger as Logger16 } from "akanjs/common";
15868
16597
 
15869
16598
  // pkgs/@akanjs/cli/package/package.runner.ts
15870
- import path42 from "path";
16599
+ import path44 from "path";
15871
16600
  import { Logger as Logger14 } from "akanjs/common";
15872
16601
  var {$: $2 } = globalThis.Bun;
15873
16602
 
@@ -15887,11 +16616,11 @@ class PackageRunner extends runner("package") {
15887
16616
  }
15888
16617
  async#getInstalledPackageJson() {
15889
16618
  const packageJsonCandidates = [
15890
- `${path42.dirname(Bun.main)}/package.json`,
16619
+ `${path44.dirname(Bun.main)}/package.json`,
15891
16620
  `${process.cwd()}/node_modules/akanjs/package.json`
15892
16621
  ];
15893
16622
  try {
15894
- packageJsonCandidates.unshift(Bun.resolveSync("akanjs/package.json", path42.dirname(Bun.main)));
16623
+ packageJsonCandidates.unshift(Bun.resolveSync("akanjs/package.json", path44.dirname(Bun.main)));
15895
16624
  } catch {}
15896
16625
  for (const packageJsonPath of packageJsonCandidates) {
15897
16626
  if (!await Bun.file(packageJsonPath).exists())
@@ -15900,7 +16629,7 @@ class PackageRunner extends runner("package") {
15900
16629
  if (packageJson.name === "akanjs" || packageJson.name === "@akanjs/cli")
15901
16630
  return packageJson;
15902
16631
  }
15903
- throw new Error(`[package] failed to locate akanjs package.json from ${path42.dirname(Bun.main)}`);
16632
+ throw new Error(`[package] failed to locate akanjs package.json from ${path44.dirname(Bun.main)}`);
15904
16633
  }
15905
16634
  async createPackage(workspace, pkgName) {
15906
16635
  await workspace.applyTemplate({ basePath: `pkgs/${pkgName}`, template: "pkgRoot", dict: { pkgName } });
@@ -16075,7 +16804,7 @@ class PackageScript extends script("package", [PackageRunner]) {
16075
16804
  }
16076
16805
 
16077
16806
  // pkgs/@akanjs/cli/cloud/cloud.runner.ts
16078
- import path43 from "path";
16807
+ import path45 from "path";
16079
16808
  import { confirm as confirm4, input as input5, select as select8 } from "@inquirer/prompts";
16080
16809
  import { Logger as Logger15, sleep } from "akanjs/common";
16081
16810
  import chalk7 from "chalk";
@@ -16346,7 +17075,7 @@ ${chalk7.green("\u27A4")} Authentication Required`));
16346
17075
  await Promise.all(akanPkgs.map(async (library) => {
16347
17076
  Logger15.info(`Publishing ${library}@${nextVersion} to ${registry ?? "npm"}...`);
16348
17077
  await workspace.spawn("npm", ["publish", "--tag", tag, ...this.#getRegistryArgs(registry), ...this.#getLocalRegistryAuthArgs(registry)], {
16349
- cwd: path43.join(workspace.workspaceRoot, "dist/pkgs", library),
17078
+ cwd: path45.join(workspace.workspaceRoot, "dist/pkgs", library),
16350
17079
  env: this.#getRegistryEnv(registry),
16351
17080
  stdio: "inherit"
16352
17081
  });
@@ -16400,11 +17129,12 @@ ${chalk7.green("\u27A4")} Authentication Required`));
16400
17129
  async downloadEnv(cloudApi2, workspace, workspaceId) {
16401
17130
  await workspace.mkdir("local");
16402
17131
  const localPath = await cloudApi2.downloadEnv(workspaceId);
16403
- await workspace.spawn("tar", ["-xf", localPath], { cwd: workspace.workspaceRoot });
17132
+ const relativePath = path45.relative(workspace.workspaceRoot, localPath).split(path45.sep).join("/");
17133
+ await workspace.spawn("tar", ["-xf", relativePath], { cwd: workspace.workspaceRoot });
16404
17134
  await workspace.remove(localPath);
16405
17135
  }
16406
17136
  async uploadEnv(cloudApi2, workspaceId, filePath) {
16407
- const file = new File([Bun.file(filePath)], path43.basename(filePath));
17137
+ const file = new File([Bun.file(filePath)], path45.basename(filePath));
16408
17138
  await cloudApi2.uploadEnv(workspaceId, file);
16409
17139
  }
16410
17140
  async downloadEnvByScp(workspace) {
@@ -16496,14 +17226,14 @@ class CloudScript extends script("cloud", [CloudRunner, ApplicationScript, Packa
16496
17226
  }
16497
17227
  async uploadEnv(workspace) {
16498
17228
  const workspaceId = workspace.getWorkspaceId({ allowEmpty: true });
16499
- const { path: path44 } = await this.cloudRunner.gatherEnvFiles(workspace);
17229
+ const { path: path46 } = await this.cloudRunner.gatherEnvFiles(workspace);
16500
17230
  if (workspaceId) {
16501
17231
  await this.login(workspace);
16502
17232
  const cloudApi2 = await CloudApi.fromHost(workspace);
16503
- await this.cloudRunner.uploadEnv(cloudApi2, workspaceId, path44);
17233
+ await this.cloudRunner.uploadEnv(cloudApi2, workspaceId, path46);
16504
17234
  return;
16505
17235
  }
16506
- await this.cloudRunner.uploadEnvByScp(workspace, path44);
17236
+ await this.cloudRunner.uploadEnvByScp(workspace, path46);
16507
17237
  }
16508
17238
  async deployAkan(workspace, { test = true, registryUrl } = {}) {
16509
17239
  const akanPkgs = await this.cloudRunner.getAkanPkgs(workspace);
@@ -16744,8 +17474,8 @@ class RepairRunner extends runner("repair") {
16744
17474
  }
16745
17475
 
16746
17476
  // pkgs/@akanjs/cli/workflow/workflow.runner.ts
16747
- import { mkdir as mkdir12, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
16748
- import path44 from "path";
17477
+ import { mkdir as mkdir12, readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
17478
+ import path46 from "path";
16749
17479
  import { capitalize as capitalize8 } from "akanjs/common";
16750
17480
 
16751
17481
  // pkgs/@akanjs/cli/workflows/shared.ts
@@ -17288,7 +18018,7 @@ var workflowSpecs = [
17288
18018
  ];
17289
18019
 
17290
18020
  // pkgs/@akanjs/cli/workflow/workflow.runner.ts
17291
- var resolvePath = (filePath) => path44.isAbsolute(filePath) ? filePath : path44.join(process.cwd(), filePath);
18021
+ var resolvePath = (filePath) => path46.isAbsolute(filePath) ? filePath : path46.join(process.cwd(), filePath);
17292
18022
  var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
17293
18023
  var isWorkflowPlan2 = (value) => {
17294
18024
  if (!isRecord3(value))
@@ -17316,7 +18046,8 @@ var failedPlan = (workflow2, diagnostics) => ({
17316
18046
  validation: [],
17317
18047
  diagnostics,
17318
18048
  recommendations: [],
17319
- requiresApproval: true
18049
+ requiresApproval: true,
18050
+ approval: workflowPlanApproval
17320
18051
  });
17321
18052
  var failedApplyReport = (workflow2, diagnostics, plan2) => createWorkflowApplyReport({
17322
18053
  workflow: workflow2,
@@ -17385,7 +18116,7 @@ var defaultValidationExecutor = (workspace) => async (command3) => {
17385
18116
  };
17386
18117
  }
17387
18118
  };
17388
- var readJsonFile = async (filePath) => JSON.parse(await readFile2(resolvePath(filePath), "utf8"));
18119
+ var readJsonFile = async (filePath) => JSON.parse(await readFile3(resolvePath(filePath), "utf8"));
17389
18120
  var planInputString2 = (plan2, key) => {
17390
18121
  const value = plan2.inputs[key];
17391
18122
  return typeof value === "string" ? value : "";
@@ -17428,17 +18159,33 @@ var applyBaselineBlockerCache = async (workspace, report) => {
17428
18159
  });
17429
18160
  }
17430
18161
  await workspace.writeFile(cachePath, jsonText({ schemaVersion: 1, workflow: report.workflow, blockers: [...blockers.values()] }), { silent: true });
18162
+ const knownBlockers = report.knownBlockers.map((blocker) => {
18163
+ if (!knownFingerprints.has(blockerFingerprint(blocker)))
18164
+ return blocker;
18165
+ return {
18166
+ ...blocker,
18167
+ known: true,
18168
+ message: `Known baseline blocker, unrelated to this source change: ${blocker.message}`
18169
+ };
18170
+ });
17431
18171
  return {
17432
18172
  ...report,
17433
- knownBlockers: report.knownBlockers.map((blocker) => {
17434
- if (!knownFingerprints.has(blockerFingerprint(blocker)))
17435
- return blocker;
17436
- return {
17437
- ...blocker,
17438
- known: true,
17439
- message: `Known baseline blocker, unrelated to this source change: ${blocker.message}`
17440
- };
17441
- })
18173
+ knownBlockers,
18174
+ baselineSummary: {
18175
+ ...report.baselineSummary,
18176
+ knownBlockerCount: knownBlockers.filter((blocker) => blocker.known).length
18177
+ }
18178
+ };
18179
+ };
18180
+ var withBaselineDetailsPolicy = (report, includeBaselineDetails) => {
18181
+ const baselineDiagnostics = report.baselineDiagnostics ?? [];
18182
+ return {
18183
+ ...report,
18184
+ baselineSummary: createWorkflowBaselineSummary(baselineDiagnostics, {
18185
+ detailsIncluded: includeBaselineDetails,
18186
+ knownBlockerCount: report.knownBlockers.filter((blocker) => blocker.known).length
18187
+ }),
18188
+ baselineDiagnostics: includeBaselineDetails ? baselineDiagnostics : []
17442
18189
  };
17443
18190
  };
17444
18191
 
@@ -17465,7 +18212,7 @@ class WorkflowRunner extends runner("workflow") {
17465
18212
  const plan2 = createWorkflowPlan(spec, compactWorkflowInputs(inputs));
17466
18213
  if (out) {
17467
18214
  const outPath = resolvePath(out);
17468
- await mkdir12(path44.dirname(outPath), { recursive: true });
18215
+ await mkdir12(path46.dirname(outPath), { recursive: true });
17469
18216
  await writeFile2(outPath, jsonText(plan2));
17470
18217
  }
17471
18218
  return format === "json" ? jsonText(plan2) : renderWorkflowPlan(plan2);
@@ -17484,7 +18231,7 @@ class WorkflowRunner extends runner("workflow") {
17484
18231
  };
17485
18232
  let plan2;
17486
18233
  try {
17487
- const parsed = JSON.parse(await readFile2(resolvePath(planPath), "utf8"));
18234
+ const parsed = JSON.parse(await readFile3(resolvePath(planPath), "utf8"));
17488
18235
  if (!isWorkflowPlan2(parsed)) {
17489
18236
  const report = failedApplyReport("unknown", [
17490
18237
  {
@@ -17534,7 +18281,8 @@ class WorkflowRunner extends runner("workflow") {
17534
18281
  async validate(runIdOrPlan, {
17535
18282
  format = "markdown",
17536
18283
  workspace,
17537
- execute
18284
+ execute,
18285
+ includeBaselineDetails = false
17538
18286
  }) {
17539
18287
  const loaded = await this.loadValidationTarget(runIdOrPlan, workspace);
17540
18288
  const doctor = await AkanContextAnalyzer.doctor(workspace, {
@@ -17542,7 +18290,7 @@ class WorkflowRunner extends runner("workflow") {
17542
18290
  runIdOrPlan,
17543
18291
  changedFiles: loaded.changedFiles
17544
18292
  });
17545
- const report = await applyBaselineBlockerCache(workspace, await createWorkflowValidationRunReport({
18293
+ const report = withBaselineDetailsPolicy(await applyBaselineBlockerCache(workspace, await createWorkflowValidationRunReport({
17546
18294
  workflow: loaded.plan?.workflow ?? loaded.workflow,
17547
18295
  source: loaded.source,
17548
18296
  plan: loaded.plan,
@@ -17552,7 +18300,7 @@ class WorkflowRunner extends runner("workflow") {
17552
18300
  baselineDiagnostics: (doctor.baselineDiagnostics ?? []).map((diagnostic) => workflowDiagnosticFromDoctor(diagnostic, "baseline")),
17553
18301
  workflowDiagnostics: (doctor.workflowDiagnostics ?? []).map((diagnostic) => workflowDiagnosticFromDoctor(diagnostic, "workflow")),
17554
18302
  repairActions: loaded.repairActions
17555
- }));
18303
+ })), includeBaselineDetails);
17556
18304
  await writeWorkflowRunArtifact(workspace, report);
17557
18305
  return renderWorkflowValidation(report, format);
17558
18306
  }
@@ -18814,6 +19562,32 @@ var createCliWorkflowStepRegistry = (workspace) => createWorkflowStepRegistry({
18814
19562
  });
18815
19563
 
18816
19564
  // pkgs/@akanjs/cli/context/context.runner.ts
19565
+ var workflowDiagnosticFromContext = (diagnostic) => ({
19566
+ severity: diagnostic.severity,
19567
+ code: diagnostic.code,
19568
+ message: diagnostic.message,
19569
+ scope: diagnostic.scope,
19570
+ context: diagnostic.context
19571
+ });
19572
+ var compactDoctorWorkspaceResult = (result, includeBaselineDetails) => {
19573
+ const baselineDiagnostics = result.baselineDiagnostics ?? [];
19574
+ if (baselineDiagnostics.length === 0) {
19575
+ return {
19576
+ ...result,
19577
+ baselineSummary: createWorkflowBaselineSummary([], { detailsIncluded: includeBaselineDetails })
19578
+ };
19579
+ }
19580
+ const baselineSummary = createWorkflowBaselineSummary(baselineDiagnostics.map(workflowDiagnosticFromContext), {
19581
+ detailsIncluded: includeBaselineDetails
19582
+ });
19583
+ return {
19584
+ ...result,
19585
+ baselineSummary,
19586
+ diagnostics: includeBaselineDetails ? result.diagnostics : result.diagnostics.filter((diagnostic) => diagnostic.scope !== "baseline"),
19587
+ baselineDiagnostics: includeBaselineDetails ? baselineDiagnostics : []
19588
+ };
19589
+ };
19590
+
18817
19591
  class ContextRunner extends runner("context") {
18818
19592
  async getContext(workspace, {
18819
19593
  format = "markdown",
@@ -18903,11 +19677,11 @@ class ContextRunner extends runner("context") {
18903
19677
  if (name === "get_guideline")
18904
19678
  return await Prompter.getInstruction(stringArg(args, "name"));
18905
19679
  if (name === "doctor_workspace")
18906
- return await AkanContextAnalyzer.doctor(workspace, {
19680
+ return compactDoctorWorkspaceResult(await AkanContextAnalyzer.doctor(workspace, {
18907
19681
  strict: !!args.strict,
18908
19682
  runIdOrPlan: typeof args.runIdOrPlan === "string" ? workspacePath(workspace, args.runIdOrPlan) : null,
18909
19683
  changedFiles: Array.isArray(args.changedFiles) ? args.changedFiles.filter((file) => typeof file === "string") : []
18910
- });
19684
+ }), !!args.includeBaselineDetails);
18911
19685
  if (name === "explain_command")
18912
19686
  return this.explainCommand(stringArg(args, "command"));
18913
19687
  if (name === "get_validation_contract")
@@ -18927,6 +19701,7 @@ class ContextRunner extends runner("context") {
18927
19701
  return {
18928
19702
  ...plan2,
18929
19703
  planPath,
19704
+ approval: typeof plan2 === "object" && plan2 && "approval" in plan2 ? { ...plan2.approval, canApplyWith: { planPath } } : undefined,
18930
19705
  next: { tool: "apply_workflow", args: { planPath } },
18931
19706
  policy: applyFirstPolicy
18932
19707
  };
@@ -18949,7 +19724,8 @@ class ContextRunner extends runner("context") {
18949
19724
  if (name === "run_validation")
18950
19725
  return parseJsonOutput(await new WorkflowRunner().validate(workspacePath(workspace, stringArg(args, "runIdOrPlan")), {
18951
19726
  format: "json",
18952
- workspace
19727
+ workspace,
19728
+ includeBaselineDetails: !!args.includeBaselineDetails
18953
19729
  }));
18954
19730
  if (name === "repair_generated") {
18955
19731
  const report = parseJsonOutput(await new RepairRunner().repair("generated", { workspace, app: stringArg(args, "app"), format: "json" }));
@@ -19260,14 +20036,14 @@ class GuidelinePrompt extends Prompter {
19260
20036
  async#getScanFilePaths(matchPattern, { avoidDirs = ["node_modules", ".next"], filterText } = {}) {
19261
20037
  const glob = new Bun.Glob(matchPattern);
19262
20038
  const paths = [];
19263
- for await (const path45 of glob.scan({ cwd: this.workspace.workspaceRoot, absolute: true })) {
19264
- if (avoidDirs.some((dir) => path45.includes(dir)))
20039
+ for await (const path47 of glob.scan({ cwd: this.workspace.workspaceRoot, absolute: true })) {
20040
+ if (avoidDirs.some((dir) => path47.includes(dir)))
19265
20041
  continue;
19266
- const fileContent = await FileSys.readText(path45);
20042
+ const fileContent = await FileSys.readText(path47);
19267
20043
  const textFilter = filterText ? new RegExp(filterText) : null;
19268
20044
  if (filterText && !textFilter?.test(fileContent))
19269
20045
  continue;
19270
- paths.push(path45);
20046
+ paths.push(path47);
19271
20047
  }
19272
20048
  return paths;
19273
20049
  }
@@ -19587,7 +20363,7 @@ class LibraryCommand extends command("library", [LibraryScript], ({ public: targ
19587
20363
 
19588
20364
  // pkgs/@akanjs/cli/localRegistry/localRegistry.runner.ts
19589
20365
  import { mkdir as mkdir13, rm as rm6 } from "fs/promises";
19590
- import path45 from "path";
20366
+ import path47 from "path";
19591
20367
  import { Logger as Logger19 } from "akanjs/common";
19592
20368
  var defaultLocalRegistryUrl = "http://127.0.0.1:4873";
19593
20369
  var containerName = "akan-verdaccio";
@@ -19605,8 +20381,8 @@ class LocalRegistryRunner extends runner("localRegistry") {
19605
20381
  Logger19.info(`Local registry is already running at ${registry}`);
19606
20382
  return registry;
19607
20383
  } catch {}
19608
- const configPath2 = path45.join(workspace.workspaceRoot, "pkgs/@akanjs/cli/localRegistry/verdaccio.yaml");
19609
- const storagePath = path45.join(workspace.workspaceRoot, ".akan/verdaccio/storage");
20384
+ const configPath2 = path47.join(workspace.workspaceRoot, "pkgs/@akanjs/cli/localRegistry/verdaccio.yaml");
20385
+ const storagePath = path47.join(workspace.workspaceRoot, ".akan/verdaccio/storage");
19610
20386
  await mkdir13(storagePath, { recursive: true });
19611
20387
  await workspace.spawn("docker", [
19612
20388
  "run",
@@ -19629,13 +20405,13 @@ class LocalRegistryRunner extends runner("localRegistry") {
19629
20405
  try {
19630
20406
  await workspace.spawn("docker", ["rm", "-f", containerName], { stdio: "inherit" });
19631
20407
  } catch {}
19632
- await rm6(path45.join(workspace.workspaceRoot, ".akan/verdaccio"), { recursive: true, force: true });
20408
+ await rm6(path47.join(workspace.workspaceRoot, ".akan/verdaccio"), { recursive: true, force: true });
19633
20409
  Logger19.info("Local registry storage has been reset");
19634
20410
  }
19635
20411
  async smoke(workspace, { registryUrl } = {}) {
19636
20412
  const registry = this.getRegistryUrl(registryUrl);
19637
- const smokeRoot = path45.join(workspace.workspaceRoot, ".akan/e2e");
19638
- await rm6(path45.join(smokeRoot, smokeRepoName), { recursive: true, force: true });
20413
+ const smokeRoot = path47.join(workspace.workspaceRoot, ".akan/e2e");
20414
+ await rm6(path47.join(smokeRoot, smokeRepoName), { recursive: true, force: true });
19639
20415
  await workspace.spawn(process.execPath, [
19640
20416
  "dist/pkgs/create-akan-workspace/index.js",
19641
20417
  smokeRepoName,
@@ -19652,12 +20428,12 @@ class LocalRegistryRunner extends runner("localRegistry") {
19652
20428
  stdio: "inherit"
19653
20429
  });
19654
20430
  await workspace.spawn("akan", ["typecheck", smokeAppName], {
19655
- cwd: path45.join(smokeRoot, smokeRepoName),
20431
+ cwd: path47.join(smokeRoot, smokeRepoName),
19656
20432
  env: { ...process.env, AKAN_NPM_REGISTRY: registry, NPM_CONFIG_REGISTRY: registry },
19657
20433
  stdio: "inherit"
19658
20434
  });
19659
20435
  await workspace.spawn("akan", ["build", smokeAppName], {
19660
- cwd: path45.join(smokeRoot, smokeRepoName),
20436
+ cwd: path47.join(smokeRoot, smokeRepoName),
19661
20437
  env: { ...process.env, AKAN_NPM_REGISTRY: registry, NPM_CONFIG_REGISTRY: registry },
19662
20438
  stdio: "inherit"
19663
20439
  });
@@ -19836,8 +20612,46 @@ class PrimitiveCommand extends command("primitive", [PrimitiveScript], ({ public
19836
20612
  })) {
19837
20613
  }
19838
20614
 
19839
- // pkgs/@akanjs/cli/repair/repair.script.ts
20615
+ // pkgs/@akanjs/cli/quality/quality.script.ts
19840
20616
  import { Logger as Logger22 } from "akanjs/common";
20617
+
20618
+ // pkgs/@akanjs/cli/quality/quality.runner.ts
20619
+ class QualityRunner extends runner("quality") {
20620
+ async scan(workspace) {
20621
+ return await new AkanQualityScanner().scan(workspace.workspaceRoot);
20622
+ }
20623
+ }
20624
+
20625
+ // pkgs/@akanjs/cli/quality/quality.script.ts
20626
+ class QualityScript extends script("quality", [QualityRunner]) {
20627
+ async scan(workspace, format = "text") {
20628
+ const spinner2 = workspace.spinning("Scanning Akan code quality...");
20629
+ const result = await this.qualityRunner.scan(workspace);
20630
+ spinner2.succeed(`Quality scan completed (${result.scannedFiles} files, ${result.warnings.length} warnings)`);
20631
+ Logger22.rawLog(format === "json" ? JSON.stringify(result, null, 2) : formatQualityScanResult(result));
20632
+ }
20633
+ }
20634
+
20635
+ // pkgs/@akanjs/cli/quality/quality.command.ts
20636
+ class QualityCommand extends command("quality", [QualityScript], ({ public: target }) => ({
20637
+ quality: target({ desc: "Scan apps and libs for Akan code quality warnings" }).arg("action", String, {
20638
+ desc: "quality action",
20639
+ default: "scan",
20640
+ enum: ["scan"]
20641
+ }).option("format", String, {
20642
+ desc: "output format",
20643
+ default: "text",
20644
+ enum: ["text", "json"]
20645
+ }).with(Workspace).exec(async function(action, format, workspace) {
20646
+ if (action !== "scan")
20647
+ throw new Error(`Unknown quality action: ${action}. Use "scan".`);
20648
+ await this.qualityScript.scan(workspace, format);
20649
+ })
20650
+ })) {
20651
+ }
20652
+
20653
+ // pkgs/@akanjs/cli/repair/repair.script.ts
20654
+ import { Logger as Logger23 } from "akanjs/common";
19841
20655
  class RepairScript extends script("repair", [RepairRunner]) {
19842
20656
  async repair(kind, {
19843
20657
  workspace,
@@ -19846,7 +20660,7 @@ class RepairScript extends script("repair", [RepairRunner]) {
19846
20660
  target = null,
19847
20661
  format = "markdown"
19848
20662
  }) {
19849
- Logger22.rawLog(await this.repairRunner.repair(kind, {
20663
+ Logger23.rawLog(await this.repairRunner.repair(kind, {
19850
20664
  workspace,
19851
20665
  app,
19852
20666
  module,
@@ -19886,12 +20700,12 @@ class RepairCommand extends command("repair", [RepairScript], ({ public: target
19886
20700
  }
19887
20701
 
19888
20702
  // pkgs/@akanjs/cli/scalar/scalar.command.ts
19889
- import { Logger as Logger23, lowerlize as lowerlize3 } from "akanjs/common";
20703
+ import { Logger as Logger24, lowerlize as lowerlize3 } from "akanjs/common";
19890
20704
  class ScalarCommand extends command("scalar", [ScalarScript], ({ public: target }) => ({
19891
20705
  createScalar: target({ desc: "Create a new scalar type (simple data model without DB)" }).arg("scalarName", String, { desc: "name of scalar" }).with(Sys).option("ai", Boolean, { default: false, desc: "use ai to create scalar" }).option("format", String, { flag: "o", desc: "output format", default: "markdown", enum: ["markdown", "json"] }).exec(async function(scalarName, sys3, ai, format) {
19892
20706
  const name = lowerlize3(scalarName.replace(/ /g, ""));
19893
20707
  const report = ai ? await this.scalarScript.createScalarWithAi(sys3, name) : await this.scalarScript.createScalar(sys3, name);
19894
- Logger23.rawLog(renderPrimitiveReport(report, format));
20708
+ Logger24.rawLog(renderPrimitiveReport(report, format));
19895
20709
  }),
19896
20710
  removeScalar: target({ desc: "Remove a scalar type from an app or library" }).arg("scalarName", String, { desc: "name of scalar" }).with(Sys).exec(async function(scalarName, sys3) {
19897
20711
  await this.scalarScript.removeScalar(sys3, scalarName);
@@ -19900,7 +20714,7 @@ class ScalarCommand extends command("scalar", [ScalarScript], ({ public: target
19900
20714
  }
19901
20715
 
19902
20716
  // pkgs/@akanjs/cli/workflow/workflow.script.ts
19903
- import { Logger as Logger24 } from "akanjs/common";
20717
+ import { Logger as Logger25 } from "akanjs/common";
19904
20718
  class WorkflowScript extends script("workflow", [WorkflowRunner, ModuleScript, ScalarScript, PrimitiveScript]) {
19905
20719
  async workflow(action, workflow2, inputs, {
19906
20720
  format = "markdown",
@@ -19909,23 +20723,23 @@ class WorkflowScript extends script("workflow", [WorkflowRunner, ModuleScript, S
19909
20723
  workspace
19910
20724
  } = {}) {
19911
20725
  if (action === "list") {
19912
- Logger24.rawLog(this.workflowRunner.list({ format }));
20726
+ Logger25.rawLog(this.workflowRunner.list({ format }));
19913
20727
  return;
19914
20728
  }
19915
20729
  if (!workflow2)
19916
20730
  throw new Error(`Workflow name is required for "${action}".`);
19917
20731
  if (action === "explain") {
19918
- Logger24.rawLog(this.workflowRunner.explain(workflow2, { format }));
20732
+ Logger25.rawLog(this.workflowRunner.explain(workflow2, { format }));
19919
20733
  return;
19920
20734
  }
19921
20735
  if (action === "plan") {
19922
- Logger24.rawLog(await this.workflowRunner.plan(workflow2, inputs, { format, out }));
20736
+ Logger25.rawLog(await this.workflowRunner.plan(workflow2, inputs, { format, out }));
19923
20737
  return;
19924
20738
  }
19925
20739
  if (action === "apply") {
19926
20740
  if (!workspace)
19927
20741
  throw new Error("Workspace is required for workflow apply.");
19928
- Logger24.rawLog(await this.workflowRunner.apply(workflow2, {
20742
+ Logger25.rawLog(await this.workflowRunner.apply(workflow2, {
19929
20743
  dryRun,
19930
20744
  format,
19931
20745
  workspace,
@@ -19943,13 +20757,13 @@ class WorkflowScript extends script("workflow", [WorkflowRunner, ModuleScript, S
19943
20757
  if (action === "validate") {
19944
20758
  if (!workspace)
19945
20759
  throw new Error("Workspace is required for workflow validate.");
19946
- Logger24.rawLog(await this.workflowRunner.validate(workflow2, { format, workspace }));
20760
+ Logger25.rawLog(await this.workflowRunner.validate(workflow2, { format, workspace }));
19947
20761
  return;
19948
20762
  }
19949
20763
  if (action === "report") {
19950
20764
  if (!workspace)
19951
20765
  throw new Error("Workspace is required for workflow report.");
19952
- Logger24.rawLog(await this.workflowRunner.report(workflow2, { format, workspace }));
20766
+ Logger25.rawLog(await this.workflowRunner.report(workflow2, { format, workspace }));
19953
20767
  return;
19954
20768
  }
19955
20769
  throw new Error(`Unknown workflow action: ${action}. Use list, explain, plan, apply, validate, or report.`);
@@ -19973,11 +20787,11 @@ class WorkflowCommand extends command("workflow", [WorkflowScript], ({ public: t
19973
20787
  }
19974
20788
 
19975
20789
  // pkgs/@akanjs/cli/workspace/workspace.script.ts
19976
- import path47 from "path";
19977
- import { Logger as Logger25 } from "akanjs/common";
20790
+ import path49 from "path";
20791
+ import { Logger as Logger26 } from "akanjs/common";
19978
20792
 
19979
20793
  // pkgs/@akanjs/cli/workspace/workspace.runner.ts
19980
- import path46 from "path";
20794
+ import path48 from "path";
19981
20795
  var defaultWorkspacePeerDependencies = new Set([
19982
20796
  "@react-spring/web",
19983
20797
  "@use-gesture/react",
@@ -20028,7 +20842,7 @@ class WorkspaceRunner extends runner("workspace") {
20028
20842
  owner = ""
20029
20843
  }) {
20030
20844
  const cwdPath = process.cwd();
20031
- const workspaceRoot = path46.join(cwdPath, dirname2, repoName);
20845
+ const workspaceRoot = path48.join(cwdPath, dirname2, repoName);
20032
20846
  const normalizedRegistryUrl = registryUrl ? getNpmRegistryUrl(registryUrl) : undefined;
20033
20847
  const workspace = WorkspaceExecutor.fromRoot({ workspaceRoot, repoName });
20034
20848
  const templateSpinner = workspace.spinning(`Creating workspace template files in ${dirname2}/${repoName}...`);
@@ -20084,9 +20898,9 @@ class WorkspaceRunner extends runner("workspace") {
20084
20898
  }
20085
20899
  async#getCliPackageJson() {
20086
20900
  const packageJsonCandidates = [
20087
- path46.join(import.meta.dir, "../package.json"),
20088
- path46.join(import.meta.dir, "package.json"),
20089
- path46.join(path46.dirname(Bun.main), "package.json")
20901
+ path48.join(import.meta.dir, "../package.json"),
20902
+ path48.join(import.meta.dir, "package.json"),
20903
+ path48.join(path48.dirname(Bun.main), "package.json")
20090
20904
  ];
20091
20905
  try {
20092
20906
  packageJsonCandidates.unshift(Bun.resolveSync("@akanjs/cli/package.json", import.meta.dir));
@@ -20102,9 +20916,9 @@ class WorkspaceRunner extends runner("workspace") {
20102
20916
  }
20103
20917
  async#getAkanPackageJson() {
20104
20918
  const packageJsonCandidates = [
20105
- path46.join(import.meta.dir, "../../../akanjs/package.json"),
20106
- path46.join(process.cwd(), "pkgs/akanjs/package.json"),
20107
- path46.join(path46.dirname(Bun.main), "node_modules/akanjs/package.json")
20919
+ path48.join(import.meta.dir, "../../../akanjs/package.json"),
20920
+ path48.join(process.cwd(), "pkgs/akanjs/package.json"),
20921
+ path48.join(path48.dirname(Bun.main), "node_modules/akanjs/package.json")
20108
20922
  ];
20109
20923
  try {
20110
20924
  packageJsonCandidates.unshift(Bun.resolveSync("akanjs/package.json", import.meta.dir));
@@ -20118,13 +20932,13 @@ class WorkspaceRunner extends runner("workspace") {
20118
20932
  }
20119
20933
  let current = import.meta.dir;
20120
20934
  for (let depth = 0;depth < 6; depth++) {
20121
- const packageJsonPath = path46.join(current, "package.json");
20935
+ const packageJsonPath = path48.join(current, "package.json");
20122
20936
  if (await Bun.file(packageJsonPath).exists()) {
20123
20937
  const packageJson = await FileSys.readJson(packageJsonPath);
20124
20938
  if (packageJson.name === "akanjs")
20125
20939
  return packageJson;
20126
20940
  }
20127
- const parent = path46.dirname(current);
20941
+ const parent = path48.dirname(current);
20128
20942
  if (parent === current)
20129
20943
  break;
20130
20944
  current = parent;
@@ -20201,11 +21015,11 @@ class WorkspaceScript extends script("workspace", [
20201
21015
  } catch (_) {
20202
21016
  gitSpinner.fail("Git repository initialization failed. It's not fatal, you can commit manually");
20203
21017
  }
20204
- const workspacePath2 = path47.join(dirname2, repoName);
20205
- Logger25.rawLog(`
21018
+ const workspacePath2 = path49.join(dirname2, repoName);
21019
+ Logger26.rawLog(`
20206
21020
  \uD83C\uDF89 Welcome aboard! Workspace created in ${dirname2}/${repoName}`);
20207
- Logger25.rawLog(`\uD83D\uDE80 Run \`cd ${workspacePath2} && akan start ${appName}\` to start the development server.`);
20208
- Logger25.rawLog(`
21021
+ Logger26.rawLog(`\uD83D\uDE80 Run \`cd ${workspacePath2} && akan start ${appName}\` to start the development server.`);
21022
+ Logger26.rawLog(`
20209
21023
  \uD83D\uDC4B Happy coding!`);
20210
21024
  }
20211
21025
  async generateAgentRules(workspace, { overwrite = false, cursorRules = true } = {}) {
@@ -20321,4 +21135,4 @@ class WorkspaceCommand extends command("workspace", [WorkspaceScript], ({ public
20321
21135
  }
20322
21136
 
20323
21137
  // pkgs/@akanjs/cli/index.ts
20324
- runCommands(WorkspaceCommand, AgentCommand, ApplicationCommand, LibraryCommand, LocalRegistryCommand, PackageCommand, ModuleCommand, PageCommand, ContextCommand, CloudCommand, GuidelineCommand, ScalarCommand, PrimitiveCommand, RepairCommand, WorkflowCommand);
21138
+ runCommands(WorkspaceCommand, AgentCommand, ApplicationCommand, LibraryCommand, LocalRegistryCommand, PackageCommand, ModuleCommand, PageCommand, ContextCommand, CloudCommand, GuidelineCommand, ScalarCommand, PrimitiveCommand, QualityCommand, RepairCommand, WorkflowCommand);