voratiq 0.1.0-beta.21 → 0.1.0-beta.23

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.
Files changed (222) hide show
  1. package/README.md +19 -23
  2. package/dist/agents/launch/chat.d.ts +3 -1
  3. package/dist/agents/launch/chat.js +2 -0
  4. package/dist/agents/runtime/policy.js +2 -1
  5. package/dist/auth/providers/utils.js +1 -1
  6. package/dist/bin.js +28 -7
  7. package/dist/cli/auto.js +4 -16
  8. package/dist/cli/contract.d.ts +26 -17
  9. package/dist/cli/contract.js +3 -1
  10. package/dist/cli/doctor.d.ts +12 -0
  11. package/dist/cli/doctor.js +115 -0
  12. package/dist/cli/list.js +5 -1
  13. package/dist/cli/message.js +16 -11
  14. package/dist/cli/operator-envelope.d.ts +27 -7
  15. package/dist/cli/operator-envelope.js +95 -3
  16. package/dist/cli/option-parsers.d.ts +2 -0
  17. package/dist/cli/option-parsers.js +7 -0
  18. package/dist/cli/output.d.ts +1 -5
  19. package/dist/cli/reduce.js +5 -13
  20. package/dist/cli/run.js +5 -12
  21. package/dist/cli/spec.js +4 -10
  22. package/dist/cli/verify.d.ts +1 -1
  23. package/dist/cli/verify.js +51 -22
  24. package/dist/commands/auto/command.d.ts +1 -0
  25. package/dist/commands/auto/command.js +22 -15
  26. package/dist/commands/auto/errors.js +1 -1
  27. package/dist/commands/auto/validation.js +3 -1
  28. package/dist/commands/doctor/agents.d.ts +5 -0
  29. package/dist/commands/{init → doctor}/agents.js +40 -20
  30. package/dist/commands/doctor/command.d.ts +22 -0
  31. package/dist/commands/doctor/command.js +100 -0
  32. package/dist/commands/doctor/environment.d.ts +2 -0
  33. package/dist/commands/{init → doctor}/environment.js +41 -7
  34. package/dist/commands/{init/types.d.ts → doctor/fix-types.d.ts} +30 -9
  35. package/dist/commands/doctor/fix.d.ts +2 -0
  36. package/dist/commands/{init/command.js → doctor/fix.js} +109 -11
  37. package/dist/commands/doctor/reconcile.d.ts +2 -0
  38. package/dist/commands/doctor/reconcile.js +103 -0
  39. package/dist/commands/interactive/lifecycle.d.ts +2 -0
  40. package/dist/commands/interactive/lifecycle.js +16 -0
  41. package/dist/commands/list/command.d.ts +1 -0
  42. package/dist/commands/list/command.js +241 -361
  43. package/dist/commands/list/normalization.d.ts +51 -0
  44. package/dist/commands/list/normalization.js +309 -0
  45. package/dist/commands/list/records.d.ts +9 -0
  46. package/dist/commands/list/records.js +6 -0
  47. package/dist/commands/message/command.d.ts +2 -2
  48. package/dist/commands/message/command.js +29 -16
  49. package/dist/commands/message/errors.d.ts +12 -3
  50. package/dist/commands/message/errors.js +19 -3
  51. package/dist/commands/prune/command.js +4 -1
  52. package/dist/commands/reduce/command.js +16 -17
  53. package/dist/commands/reduce/errors.d.ts +2 -2
  54. package/dist/commands/reduce/errors.js +3 -3
  55. package/dist/commands/reduce/targets.js +12 -3
  56. package/dist/commands/root-launcher/command.js +12 -6
  57. package/dist/commands/run/command.d.ts +1 -0
  58. package/dist/commands/run/command.js +4 -1
  59. package/dist/commands/run/record-init.d.ts +2 -0
  60. package/dist/commands/run/record-init.js +8 -1
  61. package/dist/commands/run/spec-provenance.d.ts +37 -0
  62. package/dist/commands/run/spec-provenance.js +384 -0
  63. package/dist/commands/run/validation.d.ts +4 -0
  64. package/dist/commands/run/validation.js +25 -62
  65. package/dist/commands/shared/resolve-stage-competitors.js +2 -1
  66. package/dist/commands/spec/command.js +64 -138
  67. package/dist/commands/spec/errors.d.ts +5 -0
  68. package/dist/commands/spec/errors.js +9 -0
  69. package/dist/commands/verify/agents.d.ts +4 -2
  70. package/dist/commands/verify/agents.js +4 -11
  71. package/dist/commands/verify/command.js +23 -6
  72. package/dist/commands/verify/errors.d.ts +12 -0
  73. package/dist/commands/verify/errors.js +22 -0
  74. package/dist/commands/verify/lifecycle.js +1 -1
  75. package/dist/commands/verify/targets.js +108 -12
  76. package/dist/competition/shared/preflight.d.ts +1 -1
  77. package/dist/competition/shared/preflight.js +15 -2
  78. package/dist/competition/shared/teardown.d.ts +7 -0
  79. package/dist/competition/shared/teardown.js +6 -0
  80. package/dist/configs/agents/defaults.js +13 -44
  81. package/dist/configs/agents/loader.js +1 -1
  82. package/dist/configs/environment/loader.js +2 -1
  83. package/dist/configs/orchestration/loader.js +2 -1
  84. package/dist/configs/sandbox/loader.js +2 -1
  85. package/dist/configs/settings/loader.js +1 -1
  86. package/dist/configs/verification/loader.js +2 -1
  87. package/dist/contracts/list.d.ts +129 -149
  88. package/dist/contracts/list.js +47 -99
  89. package/dist/domain/interactive/model/types.d.ts +2 -0
  90. package/dist/domain/interactive/model/types.js +16 -1
  91. package/dist/domain/interactive/persistence/adapter.d.ts +23 -0
  92. package/dist/domain/interactive/persistence/adapter.js +67 -5
  93. package/dist/domain/interactive/prompt.d.ts +1 -1
  94. package/dist/domain/interactive/prompt.js +1 -1
  95. package/dist/domain/interactive/session-env.d.ts +10 -0
  96. package/dist/domain/interactive/session-env.js +25 -0
  97. package/dist/domain/message/competition/adapter.js +3 -9
  98. package/dist/domain/message/model/types.d.ts +32 -1
  99. package/dist/domain/message/model/types.js +25 -1
  100. package/dist/domain/reduce/competition/adapter.js +30 -16
  101. package/dist/domain/reduce/competition/finalize.d.ts +7 -0
  102. package/dist/domain/reduce/competition/finalize.js +19 -0
  103. package/dist/domain/reduce/model/types.d.ts +3 -3
  104. package/dist/domain/run/competition/agents/artifacts.js +4 -2
  105. package/dist/domain/run/competition/agents/lifecycle.js +1 -1
  106. package/dist/domain/run/competition/agents/workspace.js +2 -1
  107. package/dist/domain/run/competition/errors.d.ts +1 -1
  108. package/dist/domain/run/competition/errors.js +4 -7
  109. package/dist/domain/run/competition/reports.js +2 -1
  110. package/dist/domain/run/model/enhanced.d.ts +1 -1
  111. package/dist/domain/run/model/enhanced.js +2 -1
  112. package/dist/domain/run/model/types.d.ts +384 -0
  113. package/dist/domain/run/model/types.js +87 -0
  114. package/dist/domain/spec/competition/adapter.d.ts +1 -0
  115. package/dist/domain/spec/competition/adapter.js +9 -10
  116. package/dist/domain/spec/model/mutators.d.ts +20 -0
  117. package/dist/domain/spec/model/mutators.js +146 -0
  118. package/dist/domain/spec/model/types.d.ts +3 -0
  119. package/dist/domain/spec/model/types.js +5 -0
  120. package/dist/domain/spec/persistence/adapter.d.ts +1 -0
  121. package/dist/domain/spec/persistence/adapter.js +7 -2
  122. package/dist/domain/verify/competition/adapter.d.ts +1 -1
  123. package/dist/domain/verify/competition/adapter.js +4 -9
  124. package/dist/domain/verify/competition/finalize.d.ts +9 -0
  125. package/dist/domain/verify/competition/finalize.js +22 -3
  126. package/dist/domain/verify/competition/programmatic.js +2 -1
  127. package/dist/domain/verify/competition/prompt.js +1 -1
  128. package/dist/domain/verify/competition/shared-layout.js +1 -1
  129. package/dist/domain/verify/model/types.d.ts +2 -2
  130. package/dist/interactive/providers/launch.d.ts +2 -0
  131. package/dist/interactive/providers/launch.js +19 -2
  132. package/dist/interactive/providers/mcp.d.ts +1 -0
  133. package/dist/interactive/providers/mcp.js +45 -7
  134. package/dist/interactive/substrate.js +32 -5
  135. package/dist/mcp/server.d.ts +1 -0
  136. package/dist/mcp/server.js +337 -44
  137. package/dist/policy/auto.d.ts +0 -1
  138. package/dist/policy/auto.js +3 -14
  139. package/dist/policy/verification.js +18 -1
  140. package/dist/preflight/agents.d.ts +24 -0
  141. package/dist/preflight/agents.js +71 -0
  142. package/dist/preflight/environment.d.ts +6 -0
  143. package/dist/preflight/environment.js +17 -0
  144. package/dist/preflight/formatting.d.ts +5 -0
  145. package/dist/preflight/formatting.js +20 -0
  146. package/dist/preflight/index.d.ts +2 -0
  147. package/dist/preflight/index.js +6 -9
  148. package/dist/preflight/operator.d.ts +32 -0
  149. package/dist/preflight/operator.js +40 -0
  150. package/dist/preflight/settings.d.ts +2 -0
  151. package/dist/preflight/settings.js +17 -0
  152. package/dist/render/transcripts/apply.js +2 -1
  153. package/dist/render/transcripts/interactive.d.ts +16 -0
  154. package/dist/render/transcripts/interactive.js +42 -0
  155. package/dist/render/transcripts/list.d.ts +41 -0
  156. package/dist/render/transcripts/list.js +152 -3
  157. package/dist/render/transcripts/message.d.ts +3 -5
  158. package/dist/render/transcripts/message.js +29 -74
  159. package/dist/render/transcripts/reduce.d.ts +2 -4
  160. package/dist/render/transcripts/reduce.js +31 -75
  161. package/dist/render/transcripts/root-launcher.js +2 -12
  162. package/dist/render/transcripts/run.d.ts +4 -4
  163. package/dist/render/transcripts/run.js +36 -47
  164. package/dist/render/transcripts/spec.d.ts +1 -4
  165. package/dist/render/transcripts/spec.js +15 -62
  166. package/dist/render/transcripts/verify.d.ts +6 -7
  167. package/dist/render/transcripts/verify.js +54 -85
  168. package/dist/render/utils/cli-writer.d.ts +4 -0
  169. package/dist/render/utils/cli-writer.js +1 -0
  170. package/dist/render/utils/duration.d.ts +5 -0
  171. package/dist/render/utils/duration.js +6 -0
  172. package/dist/render/utils/progressive-render.d.ts +3 -0
  173. package/dist/render/utils/progressive-render.js +44 -0
  174. package/dist/render/utils/runs.d.ts +1 -0
  175. package/dist/render/utils/runs.js +1 -0
  176. package/dist/render/utils/transcript-shell.d.ts +2 -1
  177. package/dist/render/utils/transcript-shell.js +19 -6
  178. package/dist/utils/diff.d.ts +2 -0
  179. package/dist/utils/diff.js +1 -0
  180. package/dist/utils/errors.d.ts +2 -1
  181. package/dist/utils/errors.js +3 -1
  182. package/dist/utils/git.d.ts +1 -1
  183. package/dist/utils/git.js +25 -2
  184. package/dist/utils/list-target.d.ts +4 -0
  185. package/dist/utils/list-target.js +35 -0
  186. package/dist/utils/swarm-session-ack.d.ts +9 -0
  187. package/dist/utils/swarm-session-ack.js +17 -0
  188. package/dist/utils/terminal.d.ts +1 -0
  189. package/dist/utils/terminal.js +11 -0
  190. package/dist/workspace/artifact-paths.d.ts +55 -0
  191. package/dist/workspace/artifact-paths.js +106 -0
  192. package/dist/workspace/chat/artifacts.d.ts +7 -0
  193. package/dist/workspace/chat/artifacts.js +95 -4
  194. package/dist/workspace/chat/native-usage.js +1 -1
  195. package/dist/workspace/chat/sources.d.ts +2 -5
  196. package/dist/workspace/chat/sources.js +4 -4
  197. package/dist/workspace/constants.d.ts +47 -0
  198. package/dist/workspace/constants.js +47 -0
  199. package/dist/workspace/errors.js +2 -2
  200. package/dist/workspace/layout.js +3 -1
  201. package/dist/workspace/managed-state.d.ts +32 -0
  202. package/dist/workspace/managed-state.js +104 -0
  203. package/dist/workspace/path-formatters.d.ts +9 -0
  204. package/dist/workspace/path-formatters.js +46 -0
  205. package/dist/workspace/path-resolvers.d.ts +1 -0
  206. package/dist/workspace/path-resolvers.js +5 -0
  207. package/dist/workspace/session-paths.d.ts +16 -0
  208. package/dist/workspace/session-paths.js +59 -0
  209. package/dist/workspace/setup.js +67 -2
  210. package/dist/workspace/shim.d.ts +1 -0
  211. package/dist/workspace/shim.js +3 -3
  212. package/package.json +2 -2
  213. package/dist/cli/init.d.ts +0 -15
  214. package/dist/cli/init.js +0 -70
  215. package/dist/commands/init/agents.d.ts +0 -4
  216. package/dist/commands/init/command.d.ts +0 -2
  217. package/dist/commands/init/environment.d.ts +0 -2
  218. package/dist/render/transcripts/init.d.ts +0 -7
  219. package/dist/render/transcripts/init.js +0 -83
  220. package/dist/workspace/structure.d.ts +0 -143
  221. package/dist/workspace/structure.js +0 -319
  222. /package/dist/commands/{init/types.js → doctor/fix-types.js} +0 -0
@@ -69,6 +69,7 @@ export interface AutoVerifyStageResult {
69
69
  selectedSpecPath?: string;
70
70
  selection?: SelectionDecision;
71
71
  selectionWarnings?: readonly string[];
72
+ warningMessage?: string;
72
73
  }
73
74
  export interface AutoApplyStageInput {
74
75
  runId: string;
@@ -101,12 +101,7 @@ export async function executeAutoCommand(options, dependencies) {
101
101
  stderr: specVerifyResult.stderr,
102
102
  exitCode: specVerifyResult.exitCode,
103
103
  });
104
- if (specVerifyResult.exitCode === 1) {
105
- specStatus = "failed";
106
- specDetail = "One or more spec verifiers failed.";
107
- hardFailure = true;
108
- }
109
- else if (typeof specVerifyResult.selectedSpecPath === "string" &&
104
+ if (typeof specVerifyResult.selectedSpecPath === "string" &&
110
105
  specVerifyResult.selectedSpecPath.trim().length > 0) {
111
106
  resolvedSpecPath = specVerifyResult.selectedSpecPath;
112
107
  specPath = specVerifyResult.selectedSpecPath;
@@ -117,9 +112,8 @@ export async function executeAutoCommand(options, dependencies) {
117
112
  "Spec verification returned a resolvable selection without a selected spec path.";
118
113
  hardFailure = true;
119
114
  }
120
- else {
115
+ else if (specVerifyResult.selection) {
121
116
  const specSelectionDisposition = classifyAutoVerificationSelection({
122
- targetKind: "spec",
123
117
  selection: specVerifyResult.selection,
124
118
  });
125
119
  if (specSelectionDisposition.kind !== "action_required") {
@@ -128,6 +122,13 @@ export async function executeAutoCommand(options, dependencies) {
128
122
  specDetail = specSelectionDisposition.detail;
129
123
  markActionRequired(specSelectionDisposition.detail);
130
124
  }
125
+ else if (specVerifyResult.exitCode === 1) {
126
+ specStatus = "failed";
127
+ specDetail =
128
+ specVerifyResult.warningMessage?.trim() ||
129
+ "Spec verification did not produce any successful verifier results.";
130
+ hardFailure = true;
131
+ }
131
132
  }
132
133
  catch (error) {
133
134
  specStatus = "failed";
@@ -215,11 +216,6 @@ export async function executeAutoCommand(options, dependencies) {
215
216
  });
216
217
  verifyStatus = "succeeded";
217
218
  verifySelection = verifyResult.selection;
218
- if (verifyResult.exitCode === 1) {
219
- verifyStatus = "failed";
220
- verifyDetail = "One or more verifiers failed.";
221
- hardFailure = true;
222
- }
223
219
  recordEvent({
224
220
  kind: "body",
225
221
  body: verifyResult.body,
@@ -234,9 +230,14 @@ export async function executeAutoCommand(options, dependencies) {
234
230
  separateWithDivider: bodyOutputEmitted,
235
231
  });
236
232
  }
233
+ if (options.apply === true &&
234
+ (verifyResult.selectionWarnings?.length ?? 0) > 0) {
235
+ const warningDetail = "Verification reported warnings for the selected candidate; automatic apply halted. Review the verify output and apply manually if appropriate.";
236
+ verifyDetail = warningDetail;
237
+ markActionRequired(warningDetail);
238
+ }
237
239
  if (verifySelection?.state === "unresolved") {
238
240
  const verifySelectionDisposition = classifyAutoVerificationSelection({
239
- targetKind: "run",
240
241
  selection: verifySelection,
241
242
  });
242
243
  applyAutoVerificationSelectionDisposition({
@@ -247,6 +248,13 @@ export async function executeAutoCommand(options, dependencies) {
247
248
  },
248
249
  });
249
250
  }
251
+ else if (verifyResult.exitCode === 1) {
252
+ verifyStatus = "failed";
253
+ verifyDetail =
254
+ verifyResult.warningMessage?.trim() ||
255
+ "Verification did not produce any successful verifier results.";
256
+ hardFailure = true;
257
+ }
250
258
  }
251
259
  catch (error) {
252
260
  verifyStatus = "failed";
@@ -270,7 +278,6 @@ export async function executeAutoCommand(options, dependencies) {
270
278
  });
271
279
  }
272
280
  classifyAutoVerificationSelection({
273
- targetKind: "run",
274
281
  selection: verifySelection,
275
282
  });
276
283
  if (verifySelection.state !== "resolvable") {
@@ -12,7 +12,7 @@ export class AutoPreflightError extends CliError {
12
12
  }
13
13
  });
14
14
  super("Preflight failed. Aborting auto.", detailLines, [
15
- "Run `voratiq init` from the repository root, then retry.",
15
+ "Run `voratiq doctor --fix` to repair workspace setup.",
16
16
  ]);
17
17
  this.name = "AutoPreflightError";
18
18
  }
@@ -1,7 +1,9 @@
1
1
  import { join } from "node:path";
2
2
  import { HintedError } from "../../utils/errors.js";
3
3
  import { isDirectory, isFile } from "../../utils/fs.js";
4
- import { formatWorkspacePath, resolveWorkspacePath, VORATIQ_VERIFICATION_TEMPLATES_DIR, } from "../../workspace/structure.js";
4
+ import { VORATIQ_VERIFICATION_TEMPLATES_DIR } from "../../workspace/constants.js";
5
+ import { formatWorkspacePath } from "../../workspace/path-formatters.js";
6
+ import { resolveWorkspacePath } from "../../workspace/path-resolvers.js";
5
7
  import { AutoPreflightError } from "./errors.js";
6
8
  export async function validateAutoVerificationConfig(options) {
7
9
  const requiredSelectors = resolveRequiredAutoSelectors(options.command);
@@ -0,0 +1,5 @@
1
+ import { type AgentPreset } from "../../workspace/templates.js";
2
+ import type { AgentInitSummary, DoctorBootstrapConfigureOptions } from "./fix-types.js";
3
+ export declare const AGENTS_CONFIG_DISPLAY_PATH: string;
4
+ export declare function bootstrapDoctorAgents(root: string, preset: AgentPreset, options: DoctorBootstrapConfigureOptions): Promise<AgentInitSummary>;
5
+ export declare function reconcileManagedDoctorAgents(root: string): Promise<AgentInitSummary>;
@@ -1,21 +1,37 @@
1
1
  import { getAgentDefaultId, getSupportedAgentDefaults, } from "../../configs/agents/defaults.js";
2
2
  import { readAgentsConfig } from "../../configs/agents/loader.js";
3
3
  import { detectBinary } from "../../utils/binaries.js";
4
- import { isDefaultYamlTemplate, loadYamlConfig, persistYamlConfig, } from "../../utils/yaml.js";
5
- import { formatWorkspacePath, resolveWorkspacePath, VORATIQ_AGENTS_FILE, } from "../../workspace/structure.js";
4
+ import { isDefaultYamlTemplate, loadYamlConfig, writeConfigIfChanged, } from "../../utils/yaml.js";
5
+ import { VORATIQ_AGENTS_FILE } from "../../workspace/constants.js";
6
+ import { isManagedAgentsFingerprintMatch, readManagedState, } from "../../workspace/managed-state.js";
7
+ import { formatWorkspacePath } from "../../workspace/path-formatters.js";
8
+ import { resolveWorkspacePath } from "../../workspace/path-resolvers.js";
6
9
  import { buildDefaultAgentsTemplate, serializeAgentsConfigEntries, } from "../../workspace/templates.js";
7
10
  export const AGENTS_CONFIG_DISPLAY_PATH = formatWorkspacePath(VORATIQ_AGENTS_FILE);
8
- export async function configureAgents(root, preset, options) {
11
+ export async function bootstrapDoctorAgents(root, preset, options) {
9
12
  void preset;
10
13
  void options;
11
- const filePath = resolveWorkspacePath(root, VORATIQ_AGENTS_FILE);
12
14
  const defaultTemplate = buildDefaultAgentsTemplate();
15
+ return configureAgentsWithMode(root, defaultTemplate, "bootstrap");
16
+ }
17
+ export async function reconcileManagedDoctorAgents(root) {
18
+ const defaultTemplate = buildDefaultAgentsTemplate();
19
+ return configureAgentsWithMode(root, defaultTemplate, "reconcile");
20
+ }
21
+ async function configureAgentsWithMode(root, defaultTemplate, mode) {
22
+ const filePath = resolveWorkspacePath(root, VORATIQ_AGENTS_FILE);
13
23
  const loadResult = await loadYamlConfig(filePath, readAgentsConfig);
14
24
  const defaultStatus = isDefaultYamlTemplate(loadResult.snapshot, defaultTemplate);
15
25
  const configCreated = !loadResult.snapshot.exists;
16
- const lifecycle = scanWorkspaceForAgentDefaults(loadResult.config, defaultStatus, getSupportedAgentDefaults());
26
+ const managedState = mode === "reconcile" ? await readManagedState(root) : undefined;
27
+ const managed = mode === "reconcile"
28
+ ? !loadResult.snapshot.exists ||
29
+ isManagedAgentsFingerprintMatch(managedState?.configs.agents, loadResult.snapshot.content) ||
30
+ defaultStatus
31
+ : defaultStatus;
32
+ const lifecycle = scanWorkspaceForAgentDefaults(loadResult.config, mode, getSupportedAgentDefaults());
17
33
  const detectedProviders = collectDetectedProviders(lifecycle.templates);
18
- if (!defaultStatus && loadResult.snapshot.exists) {
34
+ if (!managed && loadResult.snapshot.exists) {
19
35
  return buildAgentSummary({
20
36
  entries: loadResult.config.agents,
21
37
  zeroDetections: detectedProviders.length === 0,
@@ -23,16 +39,14 @@ export async function configureAgents(root, preset, options) {
23
39
  providerEnablementPrompted: false,
24
40
  configCreated,
25
41
  configUpdated: false,
42
+ managed: false,
26
43
  });
27
44
  }
28
45
  const snapshotResult = finalizeAgentConfigSnapshot(lifecycle);
29
- const updated = await persistYamlConfig({
30
- filePath,
31
- serialized: snapshotResult.serialized,
32
- original: loadResult.snapshot,
33
- defaultTemplate,
34
- isDefaultTemplate: defaultStatus,
35
- });
46
+ const previousNormalized = loadResult.snapshot.exists
47
+ ? loadResult.snapshot.normalized
48
+ : "__missing__";
49
+ const updated = await writeConfigIfChanged(filePath, snapshotResult.serialized, previousNormalized);
36
50
  return buildAgentSummary({
37
51
  entries: snapshotResult.entries,
38
52
  zeroDetections: lifecycle.zeroDetections,
@@ -40,9 +54,10 @@ export async function configureAgents(root, preset, options) {
40
54
  providerEnablementPrompted: false,
41
55
  configCreated,
42
56
  configUpdated: updated,
57
+ managed: true,
43
58
  });
44
59
  }
45
- function scanWorkspaceForAgentDefaults(config, isDefaultTemplate, templates) {
60
+ function scanWorkspaceForAgentDefaults(config, mode, templates) {
46
61
  const templatesById = new Map();
47
62
  for (const entry of config.agents) {
48
63
  templatesById.set(entry.id, entry);
@@ -56,9 +71,7 @@ function scanWorkspaceForAgentDefaults(config, isDefaultTemplate, templates) {
56
71
  }));
57
72
  for (const { template, templateId } of templateEntries) {
58
73
  templateIds.add(templateId);
59
- const existing = isDefaultTemplate
60
- ? undefined
61
- : templatesById.get(templateId);
74
+ const existing = templatesById.get(templateId);
62
75
  let detectedBinary = detectedBinaryByProvider.get(template.provider);
63
76
  if (!detectedBinaryByProvider.has(template.provider)) {
64
77
  detectedBinary = detectBinary(template.provider);
@@ -66,8 +79,14 @@ function scanWorkspaceForAgentDefaults(config, isDefaultTemplate, templates) {
66
79
  }
67
80
  const baseEntry = existing ?? buildEntryFromTemplate(template, templateId);
68
81
  const entry = cloneAgentEntry(baseEntry);
69
- entry.binary = detectedBinary ?? "";
70
- entry.enabled = entry.enabled !== false;
82
+ if (hasBinary(detectedBinary)) {
83
+ entry.binary = detectedBinary ?? "";
84
+ entry.enabled = true;
85
+ }
86
+ else if (!existing || mode === "bootstrap") {
87
+ entry.binary = "";
88
+ entry.enabled = false;
89
+ }
71
90
  templateStates.push({
72
91
  templateId,
73
92
  template,
@@ -142,7 +161,7 @@ function finalizeAgentConfigSnapshot(state) {
142
161
  return { entries, serialized };
143
162
  }
144
163
  function buildAgentSummary(options) {
145
- const { entries, zeroDetections, detectedProviders, providerEnablementPrompted, configCreated, configUpdated, } = options;
164
+ const { entries, zeroDetections, detectedProviders, providerEnablementPrompted, configCreated, configUpdated, managed, } = options;
146
165
  const enabledAgents = entries
147
166
  .filter((entry) => entry.enabled !== false)
148
167
  .map((entry) => entry.id);
@@ -155,6 +174,7 @@ function buildAgentSummary(options) {
155
174
  providerEnablementPrompted,
156
175
  configCreated,
157
176
  configUpdated,
177
+ managed,
158
178
  };
159
179
  }
160
180
  function hasBinary(binary) {
@@ -0,0 +1,22 @@
1
+ import { executeDoctorBootstrap } from "./fix.js";
2
+ import type { DoctorReconcileResult } from "./fix-types.js";
3
+ export interface DoctorDiagnosisResult {
4
+ readonly healthy: boolean;
5
+ readonly issueLines: readonly string[];
6
+ }
7
+ export type DoctorFixMode = "bootstrap-workspace" | "repair-and-reconcile";
8
+ export interface DoctorFixResult {
9
+ readonly mode: DoctorFixMode;
10
+ readonly reconcileResult?: DoctorReconcileResult;
11
+ }
12
+ export interface ExecuteDoctorDiagnosisInput {
13
+ readonly root: string;
14
+ }
15
+ export interface ExecuteDoctorFixInput {
16
+ readonly root: string;
17
+ readonly mode?: DoctorFixMode;
18
+ readonly bootstrapOptions?: Pick<Parameters<typeof executeDoctorBootstrap>[0], "preset" | "interactive" | "assumeYes" | "confirm" | "prompt">;
19
+ }
20
+ export declare function executeDoctorDiagnosis(input: ExecuteDoctorDiagnosisInput): Promise<DoctorDiagnosisResult>;
21
+ export declare function resolveDoctorFixMode(root: string): Promise<DoctorFixMode>;
22
+ export declare function executeDoctorFix(input: ExecuteDoctorFixInput): Promise<DoctorFixResult>;
@@ -0,0 +1,100 @@
1
+ import { formatPreflightIssueLines } from "../../competition/shared/preflight.js";
2
+ import { EnvironmentConfigParseError, MissingEnvironmentConfigError, } from "../../configs/environment/errors.js";
3
+ import { loadEnvironmentConfig } from "../../configs/environment/loader.js";
4
+ import { prepareConfiguredOperatorReadiness } from "../../preflight/operator.js";
5
+ import { toErrorMessage } from "../../utils/errors.js";
6
+ import { pathExists } from "../../utils/fs.js";
7
+ import { WorkspaceError, WorkspaceMissingEntryError, } from "../../workspace/errors.js";
8
+ import { formatWorkspacePath } from "../../workspace/path-formatters.js";
9
+ import { resolveWorkspacePath } from "../../workspace/path-resolvers.js";
10
+ import { repairWorkspaceStructure, validateWorkspace, } from "../../workspace/setup.js";
11
+ import { executeDoctorBootstrap } from "./fix.js";
12
+ import { executeDoctorReconcile } from "./reconcile.js";
13
+ const DOCTOR_PREFLIGHT_UNLABELED_AGENT_IDS = ["settings"];
14
+ export async function executeDoctorDiagnosis(input) {
15
+ const { root } = input;
16
+ const issueLines = [];
17
+ const workspacePresent = await pathExists(resolveWorkspacePath(root));
18
+ if (!workspacePresent) {
19
+ const missingWorkspaceIssue = new WorkspaceMissingEntryError(`${formatWorkspacePath()}/`);
20
+ issueLines.push(`- ${missingWorkspaceIssue.headline}`);
21
+ return {
22
+ healthy: false,
23
+ issueLines,
24
+ };
25
+ }
26
+ try {
27
+ await validateWorkspace(root);
28
+ }
29
+ catch (error) {
30
+ issueLines.push(...formatDoctorIssueLines(error));
31
+ return {
32
+ healthy: false,
33
+ issueLines,
34
+ };
35
+ }
36
+ try {
37
+ const diagnostics = await prepareConfiguredOperatorReadiness({ root });
38
+ if (diagnostics.noAgentsEnabled) {
39
+ issueLines.push("- No agents are enabled in `agents.yaml`.");
40
+ }
41
+ issueLines.push(...formatPreflightIssueLines(diagnostics.issues, {
42
+ unlabeledAgentIds: DOCTOR_PREFLIGHT_UNLABELED_AGENT_IDS,
43
+ }));
44
+ }
45
+ catch (error) {
46
+ issueLines.push(...formatDoctorIssueLines(error));
47
+ }
48
+ try {
49
+ loadEnvironmentConfig({ root });
50
+ }
51
+ catch (error) {
52
+ if (error instanceof MissingEnvironmentConfigError) {
53
+ issueLines.push(`- ${error.headline}`);
54
+ }
55
+ else if (error instanceof EnvironmentConfigParseError) {
56
+ issueLines.push(`- ${error.headline}`);
57
+ }
58
+ else {
59
+ issueLines.push(`- ${toErrorMessage(error)}`);
60
+ }
61
+ }
62
+ return {
63
+ healthy: issueLines.length === 0,
64
+ issueLines,
65
+ };
66
+ }
67
+ export async function resolveDoctorFixMode(root) {
68
+ const workspacePresent = await pathExists(resolveWorkspacePath(root));
69
+ return workspacePresent ? "repair-and-reconcile" : "bootstrap-workspace";
70
+ }
71
+ export async function executeDoctorFix(input) {
72
+ const mode = input.mode ?? (await resolveDoctorFixMode(input.root));
73
+ if (mode === "bootstrap-workspace") {
74
+ const bootstrapOptions = input.bootstrapOptions;
75
+ await executeDoctorBootstrap({
76
+ root: input.root,
77
+ preset: bootstrapOptions?.preset ?? "pro",
78
+ interactive: bootstrapOptions?.interactive ?? false,
79
+ assumeYes: bootstrapOptions?.assumeYes,
80
+ confirm: bootstrapOptions?.confirm,
81
+ prompt: bootstrapOptions?.prompt,
82
+ });
83
+ return { mode };
84
+ }
85
+ await repairWorkspaceStructure(input.root);
86
+ const reconcileResult = await executeDoctorReconcile({ root: input.root });
87
+ return {
88
+ mode,
89
+ reconcileResult,
90
+ };
91
+ }
92
+ function formatDoctorIssueLines(error) {
93
+ if (error instanceof WorkspaceError) {
94
+ return [`- ${error.headline}`];
95
+ }
96
+ if (error instanceof Error && error.message.length > 0) {
97
+ return [`- ${error.message}`];
98
+ }
99
+ return [`- ${toErrorMessage(error)}`];
100
+ }
@@ -0,0 +1,2 @@
1
+ import type { DoctorBootstrapConfigureOptions, EnvironmentInitSummary } from "./fix-types.js";
2
+ export declare function reconcileDoctorEnvironment(root: string, options: DoctorBootstrapConfigureOptions): Promise<EnvironmentInitSummary>;
@@ -1,15 +1,18 @@
1
1
  import { detectEnvironmentConfig } from "../../configs/environment/detect.js";
2
+ import { EnvironmentConfigParseError } from "../../configs/environment/errors.js";
2
3
  import { readEnvironmentConfig, serializeEnvironmentConfig, } from "../../configs/environment/loader.js";
3
4
  import { getNodeDependencyRoots, getPythonEnvironmentPath, isNodeEnvironmentDisabled, isPythonEnvironmentDisabled, } from "../../configs/environment/types.js";
4
- import { loadYamlConfig, persistYamlConfig } from "../../utils/yaml.js";
5
- import { formatWorkspacePath, resolveWorkspacePath, VORATIQ_ENVIRONMENT_FILE, } from "../../workspace/structure.js";
5
+ import { persistYamlConfig, readConfigSnapshot } from "../../utils/yaml.js";
6
+ import { VORATIQ_ENVIRONMENT_FILE } from "../../workspace/constants.js";
7
+ import { formatWorkspacePath } from "../../workspace/path-formatters.js";
8
+ import { resolveWorkspacePath } from "../../workspace/path-resolvers.js";
6
9
  import { buildDefaultEnvironmentTemplate } from "../../workspace/templates.js";
7
10
  const ENVIRONMENT_CONFIG_DISPLAY_PATH = formatWorkspacePath(VORATIQ_ENVIRONMENT_FILE);
8
- export async function configureEnvironment(root, options) {
11
+ export async function reconcileDoctorEnvironment(root, options) {
9
12
  const filePath = resolveWorkspacePath(root, VORATIQ_ENVIRONMENT_FILE);
10
13
  const defaultTemplate = buildDefaultEnvironmentTemplate();
11
- const loadResult = await loadYamlConfig(filePath, readEnvironmentConfig);
12
- const existingConfig = loadResult.config;
14
+ const originalSnapshot = await readConfigSnapshot(filePath);
15
+ const existingConfig = resolveExistingConfig(originalSnapshot);
13
16
  const detection = await detectEnvironmentConfig({
14
17
  root,
15
18
  interactive: options.interactive,
@@ -20,17 +23,48 @@ export async function configureEnvironment(root, options) {
20
23
  const configUpdated = await persistYamlConfig({
21
24
  filePath,
22
25
  serialized: finalSerialized,
23
- original: loadResult.snapshot,
26
+ original: shouldRewriteFromScratch(originalSnapshot)
27
+ ? { content: "", normalized: "", exists: false }
28
+ : originalSnapshot,
24
29
  defaultTemplate,
25
30
  });
26
31
  return {
27
32
  configPath: ENVIRONMENT_CONFIG_DISPLAY_PATH,
28
33
  detectedEntries: describeEnvironmentEntries(mergedConfig),
29
- configCreated: !loadResult.snapshot.exists,
34
+ configCreated: !originalSnapshot.exists,
30
35
  configUpdated,
31
36
  config: mergedConfig,
32
37
  };
33
38
  }
39
+ function resolveExistingConfig(snapshot) {
40
+ if (!snapshot.exists) {
41
+ return {};
42
+ }
43
+ try {
44
+ return readEnvironmentConfig(snapshot.content);
45
+ }
46
+ catch (error) {
47
+ if (error instanceof EnvironmentConfigParseError) {
48
+ return {};
49
+ }
50
+ throw error;
51
+ }
52
+ }
53
+ function shouldRewriteFromScratch(snapshot) {
54
+ if (!snapshot.exists) {
55
+ return false;
56
+ }
57
+ try {
58
+ readEnvironmentConfig(snapshot.content);
59
+ return false;
60
+ }
61
+ catch (error) {
62
+ if (error instanceof EnvironmentConfigParseError) {
63
+ return true;
64
+ }
65
+ throw error;
66
+ }
67
+ }
34
68
  function mergeEnvironmentConfig(existing, detected) {
35
69
  const merged = { ...existing };
36
70
  if (!isNodeEnvironmentDisabled(merged)) {
@@ -2,17 +2,18 @@ import type { EnvironmentConfig } from "../../configs/environment/types.js";
2
2
  import type { ConfirmationOptions, PromptOptions } from "../../render/interactions/confirmation.js";
3
3
  import type { AgentPreset } from "../../workspace/templates.js";
4
4
  import type { CreateWorkspaceResult } from "../../workspace/types.js";
5
- export interface InitCommandInput {
5
+ export interface DoctorBootstrapInput {
6
6
  root: string;
7
7
  preset: AgentPreset;
8
8
  presetProvided?: boolean;
9
9
  onPresetResolved?: (preset: AgentPreset) => void;
10
10
  assumeYes?: boolean;
11
11
  interactive: boolean;
12
- confirm?: InitConfirmationHandler;
13
- prompt?: InitPromptHandler;
12
+ confirm?: DoctorConfirmationHandler;
13
+ prompt?: DoctorPromptHandler;
14
14
  }
15
- export interface InitCommandResult {
15
+ export interface DoctorBootstrapResult {
16
+ mode: "bootstrap" | "repair";
16
17
  preset: AgentPreset;
17
18
  workspaceResult: CreateWorkspaceResult;
18
19
  agentSummary: AgentInitSummary;
@@ -29,6 +30,7 @@ export interface AgentInitSummary {
29
30
  providerEnablementPrompted: boolean;
30
31
  configCreated: boolean;
31
32
  configUpdated: boolean;
33
+ managed: boolean;
32
34
  }
33
35
  export interface DetectedProviderSummary {
34
36
  provider: string;
@@ -48,12 +50,31 @@ export interface SandboxInitSummary {
48
50
  export interface OrchestrationInitSummary {
49
51
  configPath: string;
50
52
  configCreated: boolean;
53
+ configUpdated?: boolean;
51
54
  }
52
- export type InitConfirmationHandler = (options: ConfirmationOptions) => Promise<boolean>;
53
- export type InitPromptHandler = (options: PromptOptions) => Promise<string>;
54
- export interface InitConfigureOptions {
55
+ export type DoctorConfirmationHandler = (options: ConfirmationOptions) => Promise<boolean>;
56
+ export type DoctorPromptHandler = (options: PromptOptions) => Promise<string>;
57
+ export interface DoctorBootstrapConfigureOptions {
55
58
  interactive: boolean;
56
59
  assumeYes?: boolean;
57
- confirm?: InitConfirmationHandler;
58
- prompt?: InitPromptHandler;
60
+ confirm?: DoctorConfirmationHandler;
61
+ prompt?: DoctorPromptHandler;
62
+ }
63
+ export interface DoctorReconcileInput {
64
+ root: string;
65
+ }
66
+ export interface DoctorReconcileOrchestrationSummary {
67
+ configPath: string;
68
+ configCreated: boolean;
69
+ configUpdated: boolean;
70
+ skippedCustomized: boolean;
71
+ managed: boolean;
72
+ preset: AgentPreset;
73
+ }
74
+ export interface DoctorReconcileResult {
75
+ workspaceBootstrapped: boolean;
76
+ workspaceResult?: CreateWorkspaceResult;
77
+ agentSummary: AgentInitSummary;
78
+ environmentSummary: EnvironmentInitSummary;
79
+ orchestrationSummary: DoctorReconcileOrchestrationSummary;
59
80
  }
@@ -0,0 +1,2 @@
1
+ import type { DoctorBootstrapInput, DoctorBootstrapResult } from "./fix-types.js";
2
+ export declare function executeDoctorBootstrap(input: DoctorBootstrapInput): Promise<DoctorBootstrapResult>;