thoth-agents 0.2.9 → 0.2.11

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.
@@ -425,7 +425,27 @@ function writeConfig(configPath, config) {
425
425
  writeFileSync(tmpPath, content);
426
426
  renameSync(tmpPath, configPath);
427
427
  }
428
- async function addPluginToOpenCodeConfig() {
428
+ function mergeOpenCodeMainConfig(config, update) {
429
+ if (update.ensurePlugin) {
430
+ const plugins = Array.isArray(config.plugin) ? config.plugin : [];
431
+ config.plugin = [
432
+ ...plugins.filter(
433
+ (plugin) => plugin !== PACKAGE_NAME && !plugin.startsWith(`${PACKAGE_NAME}@`)
434
+ ),
435
+ `${PACKAGE_NAME}@latest`
436
+ ];
437
+ }
438
+ if (update.disableDefaults) {
439
+ const agent = config.agent && typeof config.agent === "object" ? { ...config.agent } : {};
440
+ const explore = agent.explore && typeof agent.explore === "object" ? { ...agent.explore } : {};
441
+ const general = agent.general && typeof agent.general === "object" ? { ...agent.general } : {};
442
+ agent.explore = { ...explore, disable: true };
443
+ agent.general = { ...general, disable: true };
444
+ config.agent = agent;
445
+ }
446
+ return config;
447
+ }
448
+ function updateOpenCodeMainConfig(update) {
429
449
  const configPath = getExistingConfigPath();
430
450
  try {
431
451
  ensureOpenCodeConfigDir();
@@ -445,14 +465,10 @@ async function addPluginToOpenCodeConfig() {
445
465
  error: `Failed to parse config: ${error}`
446
466
  };
447
467
  }
448
- const config = parsedConfig ?? {};
449
- const plugins = config.plugin ?? [];
450
- const filteredPlugins = plugins.filter(
451
- (p) => p !== PACKAGE_NAME && !p.startsWith(`${PACKAGE_NAME}@`)
468
+ writeConfig(
469
+ configPath,
470
+ mergeOpenCodeMainConfig(parsedConfig ?? {}, update)
452
471
  );
453
- filteredPlugins.push(`${PACKAGE_NAME}@latest`);
454
- config.plugin = filteredPlugins;
455
- writeConfig(configPath, config);
456
472
  return { success: true, configPath };
457
473
  } catch (err) {
458
474
  return {
@@ -485,33 +501,6 @@ function writeLiteConfig(installConfig, targetPath) {
485
501
  };
486
502
  }
487
503
  }
488
- function disableDefaultAgents() {
489
- const configPath = getExistingConfigPath();
490
- try {
491
- ensureOpenCodeConfigDir();
492
- const { config: parsedConfig, error } = parseConfig(configPath);
493
- if (error) {
494
- return {
495
- success: false,
496
- configPath,
497
- error: `Failed to parse config: ${error}`
498
- };
499
- }
500
- const config = parsedConfig ?? {};
501
- const agent = config.agent ?? {};
502
- agent.explore = { disable: true };
503
- agent.general = { disable: true };
504
- config.agent = agent;
505
- writeConfig(configPath, config);
506
- return { success: true, configPath };
507
- } catch (err) {
508
- return {
509
- success: false,
510
- configPath,
511
- error: `Failed to disable default agents: ${err}`
512
- };
513
- }
514
- }
515
504
  function detectCurrentConfig() {
516
505
  const result = {
517
506
  isInstalled: false,
@@ -710,8 +699,8 @@ var OPENCODE_CAPABILITIES = {
710
699
  };
711
700
  var CODEX_PROMPT_CAPABILITIES = {
712
701
  agentDefinitions: "supported",
713
- delegatedExecution: "instruction-only",
714
- parallelDelegation: "instruction-only",
702
+ delegatedExecution: "supported",
703
+ parallelDelegation: "supported",
715
704
  runtimeHooks: "unknown",
716
705
  mcpConfiguration: "supported",
717
706
  skillPackaging: "supported",
@@ -755,6 +744,13 @@ var OPENCODE_PROMPT_DIALECT = {
755
744
  userQuestionTool: "question",
756
745
  progressTool: "todowrite",
757
746
  hostStatusSurface: "task_status",
747
+ lifecycle: {
748
+ statusAction: "wait, poll, and collect",
749
+ terminalState: "terminal task_status result",
750
+ nonterminalState: "nonterminal task_status result",
751
+ sameSessionProbe: "task_status on the same task session",
752
+ enforcement: "runtime-supported"
753
+ },
758
754
  roleReference: (role) => `@${role}`
759
755
  },
760
756
  capabilities: supportedCapabilityProfile(OPENCODE_CAPABILITIES),
@@ -775,12 +771,19 @@ var OPENCODE_PROMPT_DIALECT = {
775
771
  var CODEX_PROMPT_DIALECT = {
776
772
  harness: "codex",
777
773
  tools: {
778
- delegationTool: "multi_agent_v1.spawn_agent",
779
- backgroundDelegationTool: "multi_agent_v1.spawn_agent",
780
- backgroundStatusTool: "multi_agent_v1.wait_agent",
774
+ delegationTool: "collaboration.spawn_agent",
775
+ backgroundDelegationTool: "collaboration.spawn_agent",
776
+ backgroundStatusTool: "collaboration.wait_agent",
781
777
  userQuestionTool: "request_user_input",
782
778
  progressTool: "functions.update_plan",
783
- hostStatusSurface: "multi_agent_v1.wait_agent",
779
+ hostStatusSurface: "collaboration.list_agents",
780
+ lifecycle: {
781
+ statusAction: "wait and inspect status",
782
+ terminalState: "terminal mailbox completion or failure update",
783
+ nonterminalState: "collaboration.wait_agent timeout or silence",
784
+ sameSessionProbe: "collaboration.list_agents on the same task path",
785
+ enforcement: "runtime-supported"
786
+ },
784
787
  roleReference: (role) => `${role} role agent`
785
788
  },
786
789
  capabilities: {
@@ -792,9 +795,9 @@ var CODEX_PROMPT_DIALECT = {
792
795
  case "root-coordinator":
793
796
  return "ambient Codex root session coordinator";
794
797
  case "task":
795
- return "multi_agent_v1.spawn_agent";
798
+ return "collaboration.spawn_agent";
796
799
  case "synchronous-task-only":
797
- return "synchronous multi_agent_v1.spawn_agent only";
800
+ return "synchronous collaboration.spawn_agent only";
798
801
  }
799
802
  },
800
803
  renderRoleInvocation(role) {
@@ -814,6 +817,13 @@ var CLAUDE_CODE_PROMPT_DIALECT = {
814
817
  userQuestionTool: "AskUserQuestion",
815
818
  progressTool: "TodoWrite",
816
819
  hostStatusSurface: "TodoWrite",
820
+ lifecycle: {
821
+ statusAction: "wait, poll, and collect",
822
+ terminalState: "terminal TaskOutput result",
823
+ nonterminalState: "nonterminal TaskOutput result",
824
+ sameSessionProbe: "TaskOutput on the same task session",
825
+ enforcement: "runtime-supported"
826
+ },
817
827
  roleReference: (role) => role === "orchestrator" ? "the main-thread orchestrator" : `Task(subagent_type: ${claudeCodeSubagentType(role)})`
818
828
  },
819
829
  capabilities: supportedCapabilityProfile(CLAUDE_CODE_PROMPT_CAPABILITIES),
@@ -943,6 +953,7 @@ var SDD_PHASES = [
943
953
  artifactSkill: "sdd-tasks",
944
954
  defaultAgentRole: "quick",
945
955
  alternateAgentRoles: ["deep"],
956
+ alternateAgentCondition: "Only when the task plan is complex.",
946
957
  delegationReason: "Mechanical conversion of settled design into dependency-ordered execution tasks."
947
958
  },
948
959
  {
@@ -1131,6 +1142,10 @@ function alternateSddRoles(id) {
1131
1142
  const phase = getDelegatedSddPhase(id);
1132
1143
  return phase.alternateAgentRoles?.length ? renderRoleList(phase.alternateAgentRoles) : "";
1133
1144
  }
1145
+ function alternateSddCondition(id) {
1146
+ const condition = getDelegatedSddPhase(id).alternateAgentCondition;
1147
+ return condition ? condition.replace(/^Only/, "only").replace(/\.$/, "") : "";
1148
+ }
1134
1149
  function supportSddRoles(id) {
1135
1150
  const phase = getDelegatedSddPhase(id);
1136
1151
  return phase.supportingAgentRoles?.length ? renderRoleList(phase.supportingAgentRoles) : "";
@@ -1142,7 +1157,7 @@ function persistenceSddRole(id) {
1142
1157
  function renderSddDelegationMatrix() {
1143
1158
  return `<sdd-delegation-matrix>
1144
1159
  - sdd-init -> ${primarySddRole("init")} (+${supportSddRoles("init")}, if openspec/ missing); sdd-explore -> ${primarySddRole("explore")} (+${supportSddRoles("explore")})
1145
- - sdd-propose/sdd-spec/sdd-design -> ${primarySddRole("proposal")}; sdd-clarify -> ${primarySddRole("clarify")}; sdd-tasks -> ${primarySddRole("tasks")} (fallback: ${alternateSddRoles("tasks")})
1160
+ - sdd-propose/sdd-spec/sdd-design -> ${primarySddRole("proposal")}; sdd-clarify -> ${primarySddRole("clarify")}; sdd-tasks -> ${primarySddRole("tasks")} (fallback: ${alternateSddRoles("tasks")} ${alternateSddCondition("tasks")})
1146
1161
  - plan-reviewer -> ${primarySddRole("plan-review")}; sdd-apply -> ${primarySddRole("apply")} (fallback: ${alternateSddRoles("apply")}); sdd-verify -> ${primarySddRole("verify")} (persistence: ${persistenceSddRole("verify")}); sdd-archive -> ${primarySddRole("archive")}
1147
1162
  </sdd-delegation-matrix>`;
1148
1163
  }
@@ -1280,11 +1295,9 @@ For {{role.explorer}}/{{role.librarian}}, ask narrow fact-finding questions for
1280
1295
  - Experimental background \`{{backgroundDelegationTool}}\` is allowed only for {{role.explorer}} and {{role.librarian}} for asynchronous delegation.
1281
1296
  - {{role.oracle}}, {{role.designer}}, {{role.quick}}, and {{role.deep}} always use normal synchronous \`{{delegationTool}}\` execution.
1282
1297
  - When using background \`{{delegationTool}}\`, treat it as conditional and non-portable: if the host does not expose the experimental path, fall back to normal synchronous \`{{delegationTool}}\`.
1283
- - Use \`{{backgroundStatusTool}}\` to wait, poll, and collect background task results before synthesizing or reporting completion.
1284
- - If a result is empty, contradictory, or low-confidence, retry once with a materially sharper prompt; then escalate with evidence via \`{{userQuestionTool}}\`.
1285
- - If a named subagent hits capacity, retry that same role up to 3 attempts.
1286
- - Never switch to \`default\`, \`worker\`, or any other role.
1287
- - After 3 failures, stay on the same role; if a same-role model override exists, use it. Otherwise report a capacity blocker.
1298
+ - Use \`{{backgroundStatusTool}}\` to {{lifecycleStatusAction}}: treat \`{{lifecycleNonterminalState}}\` as in progress and probe the same session via \`{{lifecycleSameSessionProbe}}\`; no retry/reroute/interruption before \`{{lifecycleTerminalState}}\`, an explicit user deadline, user cancellation, or a superseding request.
1299
+ - Terminal result-quality and required-artifact checks share one sharpened retry; nonterminal probes use none.
1300
+ - Capacity is separate: retry the named role up to 3 times; never switch to \`default\`, \`worker\`, or another role; then use a same-role model override or report a blocker.
1288
1301
  - Write-capable dispatches must include task instructions and handoff retrieval instructions when a root-owned handoff summary exists, so implementers can recover context without rediscovering settled scope.
1289
1302
  - Never tell sub-agents to discard working-tree changes.
1290
1303
  </dispatch>
@@ -1311,7 +1324,6 @@ Hard gates:
1311
1324
  - Group consecutive ready SDD tasks for the same execution agent into one dispatch when dependencies, scope, and verification can be handled together. Keep per-task tracking and evidence; do not split a compatible {{role.designer}}/{{role.quick}}/{{role.deep}} run into one delegation per checkbox.
1312
1325
 
1313
1326
  SDD dispatch envelope must include: skill name, persistence mode, pipeline type, change name, project name, needed prior artifact context, verification expectation, and return envelope.
1314
- After each phase, verify the sub-agent reported the openspec path and/or thoth-mem topic_key. Retry once if missing.
1315
1327
 
1316
1328
  Artifact governance handoff:
1317
1329
  - After \`sdd-tasks\`, you may surface report-only artifact governance findings before execution preparation starts.
@@ -1614,7 +1626,19 @@ function renderRoleText(section, dialect) {
1614
1626
  ).replaceAll(
1615
1627
  "{{backgroundStatusTool}}",
1616
1628
  dialect.tools.backgroundStatusTool ?? dialect.tools.hostStatusSurface ?? ""
1617
- ).replaceAll("{{userQuestionTool}}", dialect.tools.userQuestionTool).replaceAll("{{progressTool}}", dialect.tools.progressTool).replaceAll("{{dispatch.task}}", dialect.dispatchLabel("task")).replaceAll(
1629
+ ).replaceAll("{{userQuestionTool}}", dialect.tools.userQuestionTool).replaceAll("{{progressTool}}", dialect.tools.progressTool).replaceAll(
1630
+ "{{lifecycleStatusAction}}",
1631
+ dialect.tools.lifecycle.statusAction
1632
+ ).replaceAll(
1633
+ "{{lifecycleTerminalState}}",
1634
+ dialect.tools.lifecycle.terminalState
1635
+ ).replaceAll(
1636
+ "{{lifecycleNonterminalState}}",
1637
+ dialect.tools.lifecycle.nonterminalState
1638
+ ).replaceAll(
1639
+ "{{lifecycleSameSessionProbe}}",
1640
+ dialect.tools.lifecycle.sameSessionProbe
1641
+ ).replaceAll("{{dispatch.task}}", dialect.dispatchLabel("task")).replaceAll(
1618
1642
  "{{dispatch.synchronous-task-only}}",
1619
1643
  dialect.dispatchLabel("synchronous-task-only")
1620
1644
  ).replace(
@@ -2079,6 +2103,9 @@ function resolvePackageRoot(packageRoot) {
2079
2103
  const moduleDir = fileURLToPath(new URL(".", import.meta.url));
2080
2104
  return findPackageRoot(moduleDir) ?? fileURLToPath(new URL("../..", import.meta.url));
2081
2105
  }
2106
+ function checkCustomSkillsNeedUpdate(packageRoot) {
2107
+ return checkSkillsNeedUpdate(resolvePackageRoot(packageRoot));
2108
+ }
2082
2109
  function installCustomSkillFiles(skill, packageRoot) {
2083
2110
  const sourcePath = join4(packageRoot, skill.sourcePath);
2084
2111
  const targetPath = join4(getCustomSkillsDir(), skill.name);
@@ -2275,14 +2302,12 @@ export {
2275
2302
  getExistingLiteConfigPath,
2276
2303
  getExistingConfigPath,
2277
2304
  ensureConfigDir,
2278
- ensureOpenCodeConfigDir,
2279
2305
  generateLiteConfig,
2280
2306
  stripJsonComments,
2281
2307
  parseConfig,
2282
2308
  writeConfig,
2283
- addPluginToOpenCodeConfig,
2309
+ updateOpenCodeMainConfig,
2284
2310
  writeLiteConfig,
2285
- disableDefaultAgents,
2286
2311
  detectCurrentConfig,
2287
2312
  loadPluginConfig,
2288
2313
  loadAgentPrompt,
@@ -2309,6 +2334,7 @@ export {
2309
2334
  CUSTOM_SKILLS,
2310
2335
  getCustomSkillsDir,
2311
2336
  findPackageRoot,
2337
+ checkCustomSkillsNeedUpdate,
2312
2338
  installCustomSkills,
2313
2339
  CONTEXT7_MCP_URL,
2314
2340
  context7,
@@ -16,6 +16,11 @@ export declare function parseConfig(path: string): {
16
16
  */
17
17
  export declare function writeConfig(configPath: string, config: OpenCodeConfig): void;
18
18
  export declare function addPluginToOpenCodeConfig(): Promise<ConfigMergeResult>;
19
+ export interface OpenCodeMainConfigUpdate {
20
+ ensurePlugin?: boolean;
21
+ disableDefaults?: boolean;
22
+ }
23
+ export declare function updateOpenCodeMainConfig(update: OpenCodeMainConfigUpdate): ConfigMergeResult;
19
24
  export declare function writeLiteConfig(installConfig: InstallConfig, targetPath?: string): ConfigMergeResult;
20
25
  export declare function disableDefaultAgents(): ConfigMergeResult;
21
26
  export declare function canModifyOpenCodeConfig(): boolean;
@@ -1,4 +1,4 @@
1
- import { type SkillUpdateReason } from './skill-manifest';
1
+ import { type SkillUpdateCheck, type SkillUpdateReason } from './skill-manifest';
2
2
  /**
3
3
  * A custom skill bundled in this repository.
4
4
  * Unlike npx-installed skills, these are copied from src/skills/ to the OpenCode skills directory
@@ -37,6 +37,7 @@ export interface InstallCustomSkillsReport {
37
37
  */
38
38
  export declare function getCustomSkillsDir(): string;
39
39
  export declare function findPackageRoot(startDir: string): string | null;
40
+ export declare function checkCustomSkillsNeedUpdate(packageRoot?: string): SkillUpdateCheck;
40
41
  export declare function removeObsoleteSkills(removedSkillNames: string[]): string[];
41
42
  export declare function installCustomSkills(packageRoot?: string): InstallCustomSkillsReport;
42
43
  /**
package/dist/cli/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  getOpenCodeVersion,
5
5
  isOpenCodeInstalled,
6
6
  opencodeAdapter
7
- } from "../chunk-75JOF37E.js";
7
+ } from "../chunk-6BNAV534.js";
8
8
  import {
9
9
  CODEX_ROLE_NAMES,
10
10
  RECOMMENDED_SKILLS,
@@ -41,19 +41,18 @@ import {
41
41
  installRecommendedSkill,
42
42
  listOperationHarnesses,
43
43
  normalizeEffortSelection
44
- } from "../chunk-6NMPKYWY.js";
44
+ } from "../chunk-ASKXWU6E.js";
45
45
  import {
46
46
  ALL_AGENT_NAMES,
47
47
  CUSTOM_SKILLS,
48
48
  DEFAULT_MODELS,
49
- addPluginToOpenCodeConfig,
50
49
  detectCurrentConfig,
51
- disableDefaultAgents,
52
50
  generateLiteConfig,
53
51
  getExistingLiteConfigPath,
54
52
  installCustomSkills,
53
+ updateOpenCodeMainConfig,
55
54
  writeLiteConfig
56
- } from "../chunk-YLRPDTU7.js";
55
+ } from "../chunk-OZE7JVGS.js";
57
56
 
58
57
  // src/cli/index.ts
59
58
  import { pathToFileURL } from "url";
@@ -167,7 +166,7 @@ async function runInstall(config) {
167
166
  const detected = detectCurrentConfig();
168
167
  const isUpdate = detected.isInstalled;
169
168
  printHeader(isUpdate);
170
- let totalSteps = 4;
169
+ let totalSteps = 3;
171
170
  if (config.installSkills) totalSteps += 1;
172
171
  if (config.installCustomSkills) totalSteps += 1;
173
172
  let step = 1;
@@ -178,19 +177,19 @@ async function runInstall(config) {
178
177
  const { ok } = await checkOpenCodeInstalled();
179
178
  if (!ok) return 1;
180
179
  }
181
- printStep(step++, totalSteps, "Adding thoth-agents plugin...");
182
- if (config.dryRun) {
183
- printInfo("Dry run mode - skipping plugin installation");
184
- } else {
185
- const pluginResult = await addPluginToOpenCodeConfig();
186
- if (!handleStepResult(pluginResult, "Plugin added")) return 1;
187
- }
188
- printStep(step++, totalSteps, "Disabling OpenCode default agents...");
180
+ printStep(
181
+ step++,
182
+ totalSteps,
183
+ "Configuring the thoth-agents plugin and OpenCode default agents..."
184
+ );
189
185
  if (config.dryRun) {
190
- printInfo("Dry run mode - skipping agent disabling");
186
+ printInfo("Dry run mode - skipping main config update");
191
187
  } else {
192
- const agentResult = disableDefaultAgents();
193
- if (!handleStepResult(agentResult, "Default agents disabled")) return 1;
188
+ const mainConfigResult = updateOpenCodeMainConfig({
189
+ ensurePlugin: true,
190
+ disableDefaults: true
191
+ });
192
+ if (!handleStepResult(mainConfigResult, "Main config updated")) return 1;
194
193
  }
195
194
  printStep(step++, totalSteps, "Writing thoth-agents configuration...");
196
195
  if (config.dryRun) {
@@ -382,7 +381,7 @@ function formatPaths(paths) {
382
381
  }
383
382
  function formatWarnings(warnings) {
384
383
  return warnings.map(
385
- (warning) => `- [${warning.severity}] ${warning.message}`
384
+ (warning) => `- [${warning.severity}]${warning.code ? ` [${warning.code}]` : ""} ${warning.message}`
386
385
  );
387
386
  }
388
387
  function formatDisclaimers(disclaimers) {
@@ -447,6 +446,7 @@ function formatOperationPlan(plan) {
447
446
  `Summary: ${plan.summary}`
448
447
  ],
449
448
  plan.targets.length > 0 ? ["Targets:", ...plan.targets.map(formatTarget)] : [],
449
+ plan.blockerTargets?.length ? ["Blocking targets:", ...plan.blockerTargets.map(formatTarget)] : [],
450
450
  plan.surfaces.length > 0 ? [
451
451
  "Managed surfaces:",
452
452
  ...plan.surfaces.map(
@@ -476,6 +476,7 @@ function formatOperationApplyResult(result) {
476
476
  `Summary: ${result.summary}`
477
477
  ],
478
478
  result.changedTargets.length > 0 ? ["Changed targets:", ...result.changedTargets.map(formatTarget)] : [],
479
+ result.diagnosticTargets?.length ? ["Diagnostic targets:", ...result.diagnosticTargets.map(formatTarget)] : [],
479
480
  result.backups.length > 0 ? ["Backups:", ...formatPaths(result.backups)] : [],
480
481
  result.warnings.length > 0 ? ["Warnings:", ...formatWarnings(result.warnings)] : [],
481
482
  result.disclaimers.length > 0 ? ["Disclaimers:", ...formatDisclaimers(result.disclaimers)] : []
@@ -80,6 +80,7 @@ export interface OperationPlan {
80
80
  dryRun: boolean;
81
81
  canApply: boolean;
82
82
  targets: ManagedTarget[];
83
+ blockerTargets?: ManagedTarget[];
83
84
  surfaces: ManagedSurface[];
84
85
  backup: BackupExpectation;
85
86
  items: OperationPlanItem[];
@@ -108,6 +109,7 @@ export interface OperationApplyResult {
108
109
  applied: boolean;
109
110
  summary: string;
110
111
  changedTargets: ManagedTarget[];
112
+ diagnosticTargets?: ManagedTarget[];
111
113
  backups: OperationPath[];
112
114
  warnings: OperationWarning[];
113
115
  disclaimers: OperationDisclaimer[];
@@ -2,8 +2,8 @@ import {
2
2
  defaultTuiOperations,
3
3
  effortChoicesForModel,
4
4
  listOperationHarnesses
5
- } from "../../chunk-6NMPKYWY.js";
6
- import "../../chunk-YLRPDTU7.js";
5
+ } from "../../chunk-ASKXWU6E.js";
6
+ import "../../chunk-OZE7JVGS.js";
7
7
 
8
8
  // src/cli/tui/index.tsx
9
9
  import { render } from "ink";
@@ -255,11 +255,21 @@ function PathLine({ label, value, width }) {
255
255
 
256
256
  // src/cli/tui/components/PlanPreview.tsx
257
257
  import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
258
+ function targetText(target) {
259
+ const label = target.label ?? target.path ?? target.kind;
260
+ const state = target.state ? `: [${target.state}]` : "";
261
+ const observed = target.observed ? ` - ${target.observed}` : "";
262
+ return `${label}${state}${observed}`;
263
+ }
264
+ function warningText(warning) {
265
+ return `[${warning.severity}]${warning.code ? ` [${warning.code}]` : ""} ${warning.message}`;
266
+ }
258
267
  function PlanPreview({
259
268
  plan,
260
269
  selectedAction,
261
270
  result
262
271
  }) {
272
+ const blockerTargets = plan.blockerTargets ?? [];
263
273
  return /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
264
274
  /* @__PURE__ */ jsx6(Text6, { bold: true, children: plan.title }),
265
275
  /* @__PURE__ */ jsx6(Text6, { children: plan.summary }),
@@ -275,6 +285,19 @@ function PlanPreview({
275
285
  "Can apply: ",
276
286
  plan.canApply ? "yes" : "no"
277
287
  ] }),
288
+ !plan.canApply && blockerTargets.length > 0 ? /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
289
+ /* @__PURE__ */ jsx6(Text6, { color: theme.warning, children: "Blocker targets" }),
290
+ blockerTargets.map((target, index) => /* @__PURE__ */ jsxs5(
291
+ Text6,
292
+ {
293
+ children: [
294
+ "- ",
295
+ targetText(target)
296
+ ]
297
+ },
298
+ `${target.label ?? target.path ?? target.kind}-${index}`
299
+ ))
300
+ ] }) : null,
278
301
  plan.surfaces.length > 0 ? /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "Managed surfaces" }) : null,
279
302
  plan.surfaces.map((surface) => /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
280
303
  /* @__PURE__ */ jsxs5(Text6, { children: [
@@ -306,10 +329,8 @@ function PlanPreview({
306
329
  " more items"
307
330
  ] }) : null,
308
331
  plan.warnings.map((warning) => /* @__PURE__ */ jsxs5(Text6, { color: theme.warning, children: [
309
- "- [",
310
- warning.severity,
311
- "] ",
312
- warning.message
332
+ "- ",
333
+ warningText(warning)
313
334
  ] }, warning.code ?? warning.message)),
314
335
  plan.disclaimers.map((disclaimer) => /* @__PURE__ */ jsxs5(Text6, { color: theme.dim, children: [
315
336
  "- ",
@@ -321,7 +342,33 @@ function PlanPreview({
321
342
  /* @__PURE__ */ jsx6(Text6, { color: selectedAction === "cancel" ? theme.accent : void 0, children: "[Cancel]" })
322
343
  ] }),
323
344
  /* @__PURE__ */ jsx6(Text6, { color: theme.dim, children: "Enter selects. a applies. c cancels." }),
324
- result ? /* @__PURE__ */ jsx6(Text6, { color: result.applied ? theme.ok : theme.warning, children: result.summary }) : null
345
+ result ? /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
346
+ /* @__PURE__ */ jsx6(Text6, { color: result.applied ? theme.ok : theme.warning, children: result.summary }),
347
+ !result.applied && ((result.diagnosticTargets?.length ?? 0) > 0 || result.warnings.length > 0) ? /* @__PURE__ */ jsxs5(Box6, { flexDirection: "column", children: [
348
+ /* @__PURE__ */ jsx6(Text6, { color: theme.warning, children: "Apply diagnostics" }),
349
+ result.diagnosticTargets?.map((target, index) => /* @__PURE__ */ jsxs5(
350
+ Text6,
351
+ {
352
+ children: [
353
+ "- ",
354
+ targetText(target)
355
+ ]
356
+ },
357
+ `${target.label ?? target.path ?? target.kind}-${index}`
358
+ )),
359
+ result.warnings.map((warning) => /* @__PURE__ */ jsxs5(
360
+ Text6,
361
+ {
362
+ color: theme.warning,
363
+ children: [
364
+ "- ",
365
+ warningText(warning)
366
+ ]
367
+ },
368
+ warning.code ?? warning.message
369
+ ))
370
+ ] }) : null
371
+ ] }) : null
325
372
  ] });
326
373
  }
327
374
 
@@ -348,10 +395,23 @@ var categoryOrder = [
348
395
  "Model state",
349
396
  "Other"
350
397
  ];
398
+ function titleCasePart(part) {
399
+ const normalized = part.toLowerCase();
400
+ switch (normalized) {
401
+ case "sdd":
402
+ return "SDD";
403
+ case "cli":
404
+ return "CLI";
405
+ case "mcp":
406
+ return "MCP";
407
+ case "opencode":
408
+ return "OpenCode";
409
+ default:
410
+ return `${part.charAt(0).toUpperCase()}${part.slice(1).toLowerCase()}`;
411
+ }
412
+ }
351
413
  function titleCase(value, separator = " ") {
352
- return value.split(/[-_\s]+/).filter(Boolean).map(
353
- (part) => part.toLowerCase() === "sdd" ? "SDD" : part.toLowerCase() === "cli" ? "CLI" : part.toLowerCase() === "mcp" ? "MCP" : part.toLowerCase() === "opencode" ? "OpenCode" : `${part.charAt(0).toUpperCase()}${part.slice(1).toLowerCase()}`
354
- ).join(separator);
414
+ return value.split(/[-_\s]+/).filter(Boolean).map(titleCasePart).join(separator);
355
415
  }
356
416
  function compactPath(path) {
357
417
  return path.replaceAll("\\", "/");
@@ -359,45 +419,56 @@ function compactPath(path) {
359
419
  function categorizeTarget(target) {
360
420
  const path = target.path ? compactPath(target.path) : "";
361
421
  const label = target.label ?? target.kind;
422
+ const detail = target.state !== "installed" ? target.observed : void 0;
362
423
  const agent = path.match(/thoth-agents-([a-z0-9_-]+)\.toml$/i);
363
424
  const skill = path.match(/skills\/([^/]+)\/SKILL\.md$/i);
364
425
  if (agent?.[1]) {
365
426
  return {
366
427
  category: "Agents",
367
428
  label: titleCase(agent[1]),
368
- state: target.state
429
+ state: target.state,
430
+ detail
369
431
  };
370
432
  }
371
433
  if (skill?.[1] || target.kind === "skill") {
372
434
  return {
373
435
  category: "Skills",
374
- label: titleCase(skill?.[1] ?? label, "-"),
375
- state: target.state
436
+ label: skill?.[1] ? titleCase(skill[1], "-") : label,
437
+ state: target.state,
438
+ detail
376
439
  };
377
440
  }
378
441
  if (/marketplace\.json$/i.test(path)) {
379
442
  return {
380
443
  category: "Marketplace",
381
444
  label: "Marketplace",
382
- state: target.state
445
+ state: target.state,
446
+ detail
383
447
  };
384
448
  }
385
449
  if (/\.mcp\.json$/i.test(path) || /plugin\.json$/i.test(path) || /plugin-assets/i.test(path) || /manifest/i.test(path)) {
386
450
  return {
387
451
  category: "Plugin/MCP",
388
452
  label: titleCase(label.replace(/^codex\s+/i, "")),
389
- state: target.state
453
+ state: target.state,
454
+ detail
390
455
  };
391
456
  }
392
457
  if (/AGENTS\.md$/i.test(path) || /root instructions/i.test(label)) {
393
458
  return {
394
459
  category: "Root instructions",
395
460
  label: "AGENTS.md",
396
- state: target.state
461
+ state: target.state,
462
+ detail
397
463
  };
398
464
  }
399
465
  if (/config\.toml$/i.test(path) || target.kind === "config") {
400
- return { category: "Config", label: titleCase(label), state: target.state };
466
+ return {
467
+ category: "Config",
468
+ label: titleCase(label),
469
+ state: target.state,
470
+ detail
471
+ };
401
472
  }
402
473
  if (target.kind === "memory-state" || /model/i.test(label)) {
403
474
  return {
@@ -487,7 +558,9 @@ function StatusView({ report }) {
487
558
  warnings.map((warning) => /* @__PURE__ */ jsxs6(Text7, { color: theme.warning, children: [
488
559
  "- [",
489
560
  warning.severity,
490
- "] ",
561
+ "]",
562
+ warning.code ? ` [${warning.code}]` : "",
563
+ " ",
491
564
  warning.message
492
565
  ] }, warning.code ?? warning.message)),
493
566
  hiddenWarnings > 0 ? /* @__PURE__ */ jsxs6(Text7, { color: theme.dim, children: [
@@ -170,20 +170,19 @@ export declare const CODEX_SURFACES: readonly [{
170
170
  readonly id: "per-agent-runtime-permissions";
171
171
  readonly target: "permission-control";
172
172
  readonly status: "unsupported";
173
- readonly fields: ["per-agent tool allow/deny equivalent to OpenCode permissions"];
173
+ readonly fields: ["named installed-role selection", "per-role permission enforcement", "per-agent tool allow/deny equivalent to OpenCode permissions"];
174
174
  readonly diagnosticCode: "codex.permission.memory.enforcement_gap";
175
- readonly summary: "Codex sandbox and approval policy can constrain sessions, but OpenCode-style per-agent MCP/tool deny maps are not validated.";
175
+ readonly summary: "Named installed-role selection and per-role permission enforcement are instruction-only because collaboration.spawn_agent has no role selector; OpenCode-style per-agent MCP/tool deny maps are not validated.";
176
176
  readonly evidence: "Codex approvals/security docs cover sandbox and approval policies, not exact OpenCode per-agent permission maps.";
177
177
  readonly fallback: "instruction-only";
178
178
  }, {
179
179
  readonly id: "programmatic-delegation-runtime";
180
180
  readonly target: "delegation-runtime";
181
- readonly status: "unsupported";
182
- readonly fields: ["task API", "background task sessions", "tmux lifecycle hooks", "automatic subagent session close"];
183
- readonly diagnosticCode: "codex.delegation.runtime.unsupported";
184
- readonly summary: "Codex subagents are user/instruction-triggered; no OpenCode plugin task API or automatic subagent session close parity is validated.";
185
- readonly evidence: "Codex subagents docs describe manual spawning and agent threads, not this plugin runtime task API or a validated runtime close hook.";
186
- readonly fallback: "instruction-only";
181
+ readonly status: "validated";
182
+ readonly fields: ["collaboration.spawn_agent", "collaboration.wait_agent", "collaboration.list_agents", "collaboration.send_message", "collaboration.followup_task", "collaboration.interrupt_agent"];
183
+ readonly diagnosticCode: "codex.delegation.runtime.validated";
184
+ readonly summary: "The current Codex collaboration surface supports generic programmatic delegation, mailbox waits, live task-tree inspection, messages, follow-up turns, and explicit interruption.";
185
+ readonly evidence: "The callable runtime exposes collaboration.spawn_agent with task_name, message, and optional fork_turns plus wait_agent, list_agents, send_message, followup_task, and interrupt_agent.";
187
186
  }, {
188
187
  readonly id: "parent-context-injection";
189
188
  readonly target: "parent-context-injection";
@@ -15,6 +15,7 @@ export interface SddPhaseContract {
15
15
  condition?: string;
16
16
  defaultAgentRole?: AgentRoleName;
17
17
  alternateAgentRoles?: AgentRoleName[];
18
+ alternateAgentCondition?: string;
18
19
  supportingAgentRoles?: AgentRoleName[];
19
20
  persistenceAgentRole?: AgentRoleName;
20
21
  delegationReason?: string;
@@ -117,6 +118,7 @@ export declare const SDD_PHASES: readonly [{
117
118
  readonly artifactSkill: "sdd-tasks";
118
119
  readonly defaultAgentRole: "quick";
119
120
  readonly alternateAgentRoles: ["deep"];
121
+ readonly alternateAgentCondition: "Only when the task plan is complex.";
120
122
  readonly delegationReason: "Mechanical conversion of settled design into dependency-ordered execution tasks.";
121
123
  }, {
122
124
  readonly id: "plan-review";
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  renderOpenCodeAgentConfigs,
4
4
  spawn,
5
5
  spawnSync
6
- } from "./chunk-75JOF37E.js";
6
+ } from "./chunk-6BNAV534.js";
7
7
  import {
8
8
  DEFAULT_THOTH_COMMAND,
9
9
  POLL_INTERVAL_BACKGROUND_MS,
@@ -14,7 +14,7 @@ import {
14
14
  installCustomSkills,
15
15
  loadPluginConfig,
16
16
  stripJsonComments
17
- } from "./chunk-YLRPDTU7.js";
17
+ } from "./chunk-OZE7JVGS.js";
18
18
 
19
19
  // src/index.ts
20
20
  import path4 from "path";