strikethroo 3.12.0 → 3.13.0

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 (57) hide show
  1. package/dist-web/assets/{arc-BVtIjHfG.js → arc-BgbdqiWZ.js} +1 -1
  2. package/dist-web/assets/{architectureDiagram-3BPJPVTR-6n6Kx_Nw.js → architectureDiagram-3BPJPVTR-C11ra1og.js} +1 -1
  3. package/dist-web/assets/{blockDiagram-GPEHLZMM-DVQrCUNW.js → blockDiagram-GPEHLZMM-sneTcKTc.js} +1 -1
  4. package/dist-web/assets/{c4Diagram-AAUBKEIU-BmsJKunV.js → c4Diagram-AAUBKEIU-rcZrBkXz.js} +1 -1
  5. package/dist-web/assets/channel-DhL4ovjN.js +1 -0
  6. package/dist-web/assets/{chunk-2J33WTMH-CLmshK_w.js → chunk-2J33WTMH-BMATkNbT.js} +1 -1
  7. package/dist-web/assets/{chunk-4BX2VUAB-DFWJI57N.js → chunk-4BX2VUAB-BSz2O6Ft.js} +1 -1
  8. package/dist-web/assets/{chunk-55IACEB6-Cw5bYv7_.js → chunk-55IACEB6-MumFzZkZ.js} +1 -1
  9. package/dist-web/assets/{chunk-727SXJPM-cGYMQHSz.js → chunk-727SXJPM-4dQkVcIM.js} +1 -1
  10. package/dist-web/assets/{chunk-AQP2D5EJ-CQjDykhJ.js → chunk-AQP2D5EJ-v5TVK_fa.js} +1 -1
  11. package/dist-web/assets/{chunk-FMBD7UC4-x3hzV0ms.js → chunk-FMBD7UC4-BArw-yD6.js} +1 -1
  12. package/dist-web/assets/{chunk-ND2GUHAM-B2mXRbNo.js → chunk-ND2GUHAM-jmMu7-6G.js} +1 -1
  13. package/dist-web/assets/{chunk-QZHKN3VN-CvnkhP9r.js → chunk-QZHKN3VN-CoFZRE9S.js} +1 -1
  14. package/dist-web/assets/classDiagram-4FO5ZUOK-DNoS6CPC.js +1 -0
  15. package/dist-web/assets/classDiagram-v2-Q7XG4LA2-DNoS6CPC.js +1 -0
  16. package/dist-web/assets/{cose-bilkent-S5V4N54A-BZpF-dzH.js → cose-bilkent-S5V4N54A-DfE6Yc6V.js} +1 -1
  17. package/dist-web/assets/{dagre-BM42HDAG-DFMuZyrG.js → dagre-BM42HDAG-GLQxrEea.js} +1 -1
  18. package/dist-web/assets/{diagram-2AECGRRQ-BvprRz2I.js → diagram-2AECGRRQ-CPF-j3jI.js} +1 -1
  19. package/dist-web/assets/{diagram-5GNKFQAL-DbHN8zOP.js → diagram-5GNKFQAL-DX8N0bhf.js} +1 -1
  20. package/dist-web/assets/{diagram-KO2AKTUF-2xhIBvDF.js → diagram-KO2AKTUF-D4wM_Xrg.js} +1 -1
  21. package/dist-web/assets/{diagram-LMA3HP47-BZSMb8wm.js → diagram-LMA3HP47-uVt4DExc.js} +1 -1
  22. package/dist-web/assets/{diagram-OG6HWLK6-DZxBiAwf.js → diagram-OG6HWLK6-DzfLImY-.js} +1 -1
  23. package/dist-web/assets/{erDiagram-TEJ5UH35-DOc66mWB.js → erDiagram-TEJ5UH35-BDOB8bJk.js} +1 -1
  24. package/dist-web/assets/{flowDiagram-I6XJVG4X-rEwEgwKf.js → flowDiagram-I6XJVG4X-DsofFC7H.js} +1 -1
  25. package/dist-web/assets/{ganttDiagram-6RSMTGT7-BAStzA-L.js → ganttDiagram-6RSMTGT7-DYCTEBUe.js} +1 -1
  26. package/dist-web/assets/{gitGraphDiagram-PVQCEYII-C0gTHRDa.js → gitGraphDiagram-PVQCEYII-B373xEyt.js} +1 -1
  27. package/dist-web/assets/{index-DBUy3IfQ.js → index-BrXKePvp.js} +1 -1
  28. package/dist-web/assets/{index-CNj2Qj_m.js → index-BxjDhqax.js} +1 -1
  29. package/dist-web/assets/{index-CGjoHidG.js → index-CvZkEGgs.js} +4 -4
  30. package/dist-web/assets/{infoDiagram-5YYISTIA-Dh2D2DOL.js → infoDiagram-5YYISTIA-BTdfbRG3.js} +1 -1
  31. package/dist-web/assets/{ishikawaDiagram-YF4QCWOH-4Fb9ugBL.js → ishikawaDiagram-YF4QCWOH-By0jpBpG.js} +1 -1
  32. package/dist-web/assets/{journeyDiagram-JHISSGLW-BBjMcjNc.js → journeyDiagram-JHISSGLW-CN4ddpRF.js} +1 -1
  33. package/dist-web/assets/{kanban-definition-UN3LZRKU-eyj4aSf_.js → kanban-definition-UN3LZRKU-CSzp97xY.js} +1 -1
  34. package/dist-web/assets/{linear-KJUWAP7h.js → linear-Bo45a3LX.js} +1 -1
  35. package/dist-web/assets/{mermaid.core-B9tzk4XH.js → mermaid.core-fEMv_ThD.js} +4 -4
  36. package/dist-web/assets/{mindmap-definition-RKZ34NQL-pTCX9_8k.js → mindmap-definition-RKZ34NQL-lcelYnaU.js} +1 -1
  37. package/dist-web/assets/{pieDiagram-4H26LBE5-CV1GWPlT.js → pieDiagram-4H26LBE5-DqrHeOSK.js} +1 -1
  38. package/dist-web/assets/{quadrantDiagram-W4KKPZXB-C60DVKh9.js → quadrantDiagram-W4KKPZXB-lKQsolLl.js} +1 -1
  39. package/dist-web/assets/{requirementDiagram-4Y6WPE33-D5GCyDny.js → requirementDiagram-4Y6WPE33-D3GerVWN.js} +1 -1
  40. package/dist-web/assets/{sankeyDiagram-5OEKKPKP-L_jgOwN_.js → sankeyDiagram-5OEKKPKP-Dn0Rn4jy.js} +1 -1
  41. package/dist-web/assets/{sequenceDiagram-3UESZ5HK-ek4VA0bX.js → sequenceDiagram-3UESZ5HK-BMZb9HkP.js} +1 -1
  42. package/dist-web/assets/{stateDiagram-AJRCARHV-DaTz-gXI.js → stateDiagram-AJRCARHV-mHX_sHdp.js} +1 -1
  43. package/dist-web/assets/stateDiagram-v2-BHNVJYJU-BwuUXdDR.js +1 -0
  44. package/dist-web/assets/{timeline-definition-PNZ67QCA-BSZIQpRw.js → timeline-definition-PNZ67QCA-CrVF3pNw.js} +1 -1
  45. package/dist-web/assets/{vennDiagram-CIIHVFJN-0qDOzqt8.js → vennDiagram-CIIHVFJN-Dfw6e8s7.js} +1 -1
  46. package/dist-web/assets/{wardley-L42UT6IY-BOcRObI0.js → wardley-L42UT6IY-HlLGXiR6.js} +1 -1
  47. package/dist-web/assets/{wardleyDiagram-YWT4CUSO-KyyBf4a6.js → wardleyDiagram-YWT4CUSO-DPL-Ch-8.js} +1 -1
  48. package/dist-web/assets/{xychartDiagram-2RQKCTM6-SCVN95Cn.js → xychartDiagram-2RQKCTM6-Cd6IimVj.js} +1 -1
  49. package/dist-web/index.html +1 -1
  50. package/package.json +1 -1
  51. package/templates/harness/skills/st-full-workflow/scripts/route-task-execution.cjs +133 -26
  52. package/templates/harness/skills/st-generate-tasks/scripts/route-task-execution.cjs +133 -26
  53. package/templates/strikethroo/config/config.yaml +8 -6
  54. package/dist-web/assets/channel-C1JVeaAF.js +0 -1
  55. package/dist-web/assets/classDiagram-4FO5ZUOK-D1UIhNJl.js +0 -1
  56. package/dist-web/assets/classDiagram-v2-Q7XG4LA2-D1UIhNJl.js +0 -1
  57. package/dist-web/assets/stateDiagram-v2-BHNVJYJU-CgQxtWKp.js +0 -1
@@ -3337,8 +3337,8 @@ var require_excerpt = __commonJS({
3337
3337
  if (sep == null && (opts.excerpt === false || opts.excerpt == null)) {
3338
3338
  return file;
3339
3339
  }
3340
- const delimiter = typeof opts.excerpt === "string" ? opts.excerpt : sep || opts.delimiters[0];
3341
- const idx = file.content.indexOf(delimiter);
3340
+ const delimiter2 = typeof opts.excerpt === "string" ? opts.excerpt : sep || opts.delimiters[0];
3341
+ const idx = file.content.indexOf(delimiter2);
3342
3342
  if (idx !== -1) {
3343
3343
  file.excerpt = file.content.slice(0, idx);
3344
3344
  }
@@ -3402,7 +3402,7 @@ var require_parse = __commonJS({
3402
3402
  var require_gray_matter = __commonJS({
3403
3403
  "node_modules/gray-matter/index.js"(exports2, module2) {
3404
3404
  "use strict";
3405
- var fs6 = require("fs");
3405
+ var fs7 = require("fs");
3406
3406
  var sections = require_section_matter();
3407
3407
  var defaults = require_defaults();
3408
3408
  var stringify = require_stringify();
@@ -3486,7 +3486,7 @@ var require_gray_matter = __commonJS({
3486
3486
  return stringify(file, data, options2);
3487
3487
  };
3488
3488
  matter2.read = function(filepath, options2) {
3489
- const str2 = fs6.readFileSync(filepath, "utf8");
3489
+ const str2 = fs7.readFileSync(filepath, "utf8");
3490
3490
  const file = matter2(str2, options2);
3491
3491
  file.path = filepath;
3492
3492
  return file;
@@ -3515,8 +3515,8 @@ var require_gray_matter = __commonJS({
3515
3515
  });
3516
3516
 
3517
3517
  // src/skill-scripts/route-task-execution.ts
3518
- var fs5 = __toESM(require("fs"));
3519
- var path5 = __toESM(require("path"));
3518
+ var fs6 = __toESM(require("fs"));
3519
+ var path6 = __toESM(require("path"));
3520
3520
 
3521
3521
  // src/types.ts
3522
3522
  var SUPPORTED_HARNESSES = [
@@ -3797,8 +3797,8 @@ var readTaskExecutionPolicy = (taskMarkdown, context) => {
3797
3797
  };
3798
3798
 
3799
3799
  // src/skill-scripts/shared/execution-routing.ts
3800
- var fs4 = __toESM(require("fs"));
3801
- var path4 = __toESM(require("path"));
3800
+ var fs5 = __toESM(require("fs"));
3801
+ var path5 = __toESM(require("path"));
3802
3802
  var import_child_process = require("child_process");
3803
3803
 
3804
3804
  // node_modules/js-yaml/dist/js-yaml.mjs
@@ -6023,8 +6023,108 @@ var DEFAULT_DUMP_OPTIONS = {
6023
6023
  }
6024
6024
  };
6025
6025
 
6026
+ // src/skill-scripts/shared/external-dispatch.ts
6027
+ var fs4 = __toESM(require("fs"));
6028
+ var path4 = __toESM(require("path"));
6029
+ var taskPrompt = (request) => `Strikethroo external task dispatch \u2014 Plan ${request.planId}, Task ${request.taskId}.
6030
+ Workspace: ${request.workspace}
6031
+ Task file: ${request.taskFile}
6032
+ Before implementation, read and execute ${path4.join(
6033
+ request.workspace,
6034
+ ".ai/strikethroo/config/hooks/PRE_TASK_EXECUTION.md"
6035
+ )}. Halt if that hook fails.
6036
+
6037
+ Read and implement this task. Preserve dependency validation, status transitions, evidence reporting, and error-hook handling. Report failures clearly.
6038
+
6039
+ ${request.taskMarkdown}`;
6040
+ var command = (executable, argv, request) => ({
6041
+ executable,
6042
+ argv,
6043
+ cwd: request.workspace,
6044
+ stdin: taskPrompt(request)
6045
+ });
6046
+ var EXTERNAL_HARNESS_ADAPTERS = {
6047
+ claude: {
6048
+ executable: "claude",
6049
+ buildCommand: (request) => command(
6050
+ "claude",
6051
+ [
6052
+ "-p",
6053
+ "--model",
6054
+ request.model,
6055
+ ...request.reasoningEffort === void 0 ? [] : ["--effort", request.reasoningEffort]
6056
+ ],
6057
+ request
6058
+ ),
6059
+ authenticationArgv: () => ["auth", "status"]
6060
+ },
6061
+ codex: {
6062
+ executable: "codex",
6063
+ buildCommand: (request) => command(
6064
+ "codex",
6065
+ [
6066
+ "exec",
6067
+ "--model",
6068
+ request.model,
6069
+ ...request.reasoningEffort === void 0 ? [] : ["--config", `model_reasoning_effort=${request.reasoningEffort}`],
6070
+ "-"
6071
+ ],
6072
+ request
6073
+ ),
6074
+ authenticationArgv: () => ["login", "status"]
6075
+ },
6076
+ cursor: {
6077
+ executable: "cursor-agent",
6078
+ buildCommand: (request) => command("cursor-agent", ["--print", "--model", request.model], request),
6079
+ authenticationArgv: () => ["status"]
6080
+ },
6081
+ gemini: {
6082
+ executable: "gemini",
6083
+ buildCommand: (request) => command("gemini", ["--prompt", "", "--model", request.model], request),
6084
+ authenticationArgv: () => ["auth", "status"]
6085
+ },
6086
+ copilot: {
6087
+ executable: "copilot",
6088
+ buildCommand: (request) => command("copilot", ["-p", "", "--model", request.model], request),
6089
+ authenticationArgv: () => ["auth", "status"]
6090
+ },
6091
+ opencode: {
6092
+ executable: "opencode",
6093
+ buildCommand: (request) => command(
6094
+ "opencode",
6095
+ [
6096
+ "run",
6097
+ "--model",
6098
+ request.model,
6099
+ ...request.reasoningEffort === void 0 ? [] : ["--variant", request.reasoningEffort],
6100
+ "-"
6101
+ ],
6102
+ request
6103
+ ),
6104
+ authenticationArgv: () => ["auth", "list"]
6105
+ }
6106
+ };
6107
+ var adapterKeys = Object.keys(EXTERNAL_HARNESS_ADAPTERS).sort();
6108
+ var harnessKeys = [...SUPPORTED_HARNESSES].sort();
6109
+ if (adapterKeys.join("\0") !== harnessKeys.join("\0")) {
6110
+ throw new Error("External harness adapter registry does not cover SUPPORTED_HARNESSES exactly.");
6111
+ }
6112
+ var executableOnPath = (executable) => (process.env.PATH ?? "").split(path4.delimiter).some((directory) => {
6113
+ if (!directory) return false;
6114
+ const candidate = path4.join(directory, executable);
6115
+ try {
6116
+ return fs4.statSync(candidate).isFile();
6117
+ } catch {
6118
+ return false;
6119
+ }
6120
+ });
6121
+ var harnessExecutableAvailable = (harness) => {
6122
+ const adapter = EXTERNAL_HARNESS_ADAPTERS[harness];
6123
+ return adapter ? executableOnPath(adapter.executable) : false;
6124
+ };
6125
+
6026
6126
  // src/skill-scripts/shared/execution-routing.ts
6027
- var WORKSPACE_CONFIG_RELPATH = path4.join("config", "config.yaml");
6127
+ var WORKSPACE_CONFIG_RELPATH = path5.join("config", "config.yaml");
6028
6128
  var EXECUTION_ROUTING_SECTION = "execution_routing";
6029
6129
  var isPlainObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
6030
6130
  var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
@@ -6099,10 +6199,10 @@ var validateProfile = (name, raw, supportedHarnesses, errors) => {
6099
6199
  return { name, description: raw.description.trim(), targets };
6100
6200
  };
6101
6201
  var loadRoutingConfig = (strikethrooRoot, supportedHarnesses) => {
6102
- const configPath = path4.join(strikethrooRoot, WORKSPACE_CONFIG_RELPATH);
6202
+ const configPath = path5.join(strikethrooRoot, WORKSPACE_CONFIG_RELPATH);
6103
6203
  let contents;
6104
6204
  try {
6105
- contents = fs4.readFileSync(configPath, "utf8");
6205
+ contents = fs5.readFileSync(configPath, "utf8");
6106
6206
  } catch {
6107
6207
  return { kind: "no-config" };
6108
6208
  }
@@ -6221,16 +6321,23 @@ var selectTargets = (config, assignments, options2) => {
6221
6321
  return { id, profile: profileName, candidates: profile.targets };
6222
6322
  });
6223
6323
  if (!config.resolverScript) {
6324
+ const isHarnessAvailable = options2.harnessAvailable ?? harnessExecutableAvailable;
6325
+ const random = options2.random ?? Math.random;
6224
6326
  const selections2 = /* @__PURE__ */ new Map();
6225
6327
  for (const task of tasks) {
6226
- const first = task.candidates[0];
6227
- if (!first) throw new Error(`profile "${task.profile}" has no targets`);
6228
- selections2.set(task.id, first);
6328
+ const fallback2 = task.candidates[0];
6329
+ if (!fallback2) throw new Error(`profile "${task.profile}" has no targets`);
6330
+ const available = task.candidates.filter(
6331
+ (candidate) => candidate.harness === void 0 || isHarnessAvailable(candidate.harness)
6332
+ );
6333
+ const pool = available.length > 0 ? available : [fallback2];
6334
+ const chosen = pool[Math.floor(random() * pool.length)] ?? fallback2;
6335
+ selections2.set(task.id, chosen);
6229
6336
  }
6230
6337
  return { kind: "selected", selections: selections2 };
6231
6338
  }
6232
- const scriptPath = path4.resolve(options2.projectRoot, config.resolverScript);
6233
- if (!fs4.existsSync(scriptPath)) {
6339
+ const scriptPath = path5.resolve(options2.projectRoot, config.resolverScript);
6340
+ if (!fs5.existsSync(scriptPath)) {
6234
6341
  return {
6235
6342
  kind: "resolver-failure",
6236
6343
  detail: `custom resolver script not found: ${scriptPath}`
@@ -6344,10 +6451,10 @@ var usage = () => emit(
6344
6451
  2
6345
6452
  );
6346
6453
  var readTaskDocuments = (planDir) => {
6347
- const tasksDir = path5.join(planDir, "tasks");
6454
+ const tasksDir = path6.join(planDir, "tasks");
6348
6455
  let entries;
6349
6456
  try {
6350
- entries = fs5.readdirSync(tasksDir, { withFileTypes: true });
6457
+ entries = fs6.readdirSync(tasksDir, { withFileTypes: true });
6351
6458
  } catch {
6352
6459
  return { kind: "invalid", errors: [`no tasks directory at ${tasksDir}.`] };
6353
6460
  }
@@ -6357,8 +6464,8 @@ var readTaskDocuments = (planDir) => {
6357
6464
  const taskFiles = entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => e.name).sort();
6358
6465
  for (const name of taskFiles) {
6359
6466
  const entry = { name };
6360
- const file = path5.join(tasksDir, entry.name);
6361
- const content = fs5.readFileSync(file, "utf8");
6467
+ const file = path6.join(tasksDir, entry.name);
6468
+ const content = fs6.readFileSync(file, "utf8");
6362
6469
  const id = extractPlanId(content, file);
6363
6470
  if (id === null) {
6364
6471
  errors.push(`task file ${entry.name} has no integer id in its frontmatter.`);
@@ -6431,7 +6538,7 @@ var main = () => {
6431
6538
  }
6432
6539
  let rawMap;
6433
6540
  try {
6434
- rawMap = JSON.parse(fs5.readFileSync(assignmentFile, "utf8"));
6541
+ rawMap = JSON.parse(fs6.readFileSync(assignmentFile, "utf8"));
6435
6542
  } catch (error) {
6436
6543
  emit(
6437
6544
  {
@@ -6453,7 +6560,7 @@ var main = () => {
6453
6560
  emit({ kind: "invalid-assignments", errors: assignmentResult.errors }, 1);
6454
6561
  return;
6455
6562
  }
6456
- const projectRoot = path5.dirname(path5.dirname(resolved.strikethrooRoot));
6563
+ const projectRoot = path6.dirname(path6.dirname(resolved.strikethrooRoot));
6457
6564
  const selection = selectTargets(config, assignmentResult.assignments, {
6458
6565
  planId: resolved.planId,
6459
6566
  projectRoot
@@ -6481,7 +6588,7 @@ var main = () => {
6481
6588
  emit(
6482
6589
  {
6483
6590
  kind: "routing-failure",
6484
- detail: `task ${task.id} (${path5.basename(task.file)}): ${error instanceof Error ? error.message : String(error)}`
6591
+ detail: `task ${task.id} (${path6.basename(task.file)}): ${error instanceof Error ? error.message : String(error)}`
6485
6592
  },
6486
6593
  1
6487
6594
  );
@@ -6503,11 +6610,11 @@ var main = () => {
6503
6610
  const written = [];
6504
6611
  try {
6505
6612
  for (const { task, next } of staged) {
6506
- fs5.writeFileSync(task.file, next);
6613
+ fs6.writeFileSync(task.file, next);
6507
6614
  written.push(task);
6508
6615
  }
6509
6616
  for (const { task, execution } of staged) {
6510
- const reread = fs5.readFileSync(task.file, "utf8");
6617
+ const reread = fs6.readFileSync(task.file, "utf8");
6511
6618
  const policy = readTaskExecutionPolicy(reread, policyContext);
6512
6619
  if (!policyMatchesExecution(policy, execution)) {
6513
6620
  throw new Error(`task ${task.id} failed post-write verification.`);
@@ -6516,7 +6623,7 @@ var main = () => {
6516
6623
  } catch (error) {
6517
6624
  for (const task of written) {
6518
6625
  try {
6519
- fs5.writeFileSync(task.file, task.content);
6626
+ fs6.writeFileSync(task.file, task.content);
6520
6627
  } catch {
6521
6628
  }
6522
6629
  }
@@ -3337,8 +3337,8 @@ var require_excerpt = __commonJS({
3337
3337
  if (sep == null && (opts.excerpt === false || opts.excerpt == null)) {
3338
3338
  return file;
3339
3339
  }
3340
- const delimiter = typeof opts.excerpt === "string" ? opts.excerpt : sep || opts.delimiters[0];
3341
- const idx = file.content.indexOf(delimiter);
3340
+ const delimiter2 = typeof opts.excerpt === "string" ? opts.excerpt : sep || opts.delimiters[0];
3341
+ const idx = file.content.indexOf(delimiter2);
3342
3342
  if (idx !== -1) {
3343
3343
  file.excerpt = file.content.slice(0, idx);
3344
3344
  }
@@ -3402,7 +3402,7 @@ var require_parse = __commonJS({
3402
3402
  var require_gray_matter = __commonJS({
3403
3403
  "node_modules/gray-matter/index.js"(exports2, module2) {
3404
3404
  "use strict";
3405
- var fs6 = require("fs");
3405
+ var fs7 = require("fs");
3406
3406
  var sections = require_section_matter();
3407
3407
  var defaults = require_defaults();
3408
3408
  var stringify = require_stringify();
@@ -3486,7 +3486,7 @@ var require_gray_matter = __commonJS({
3486
3486
  return stringify(file, data, options2);
3487
3487
  };
3488
3488
  matter2.read = function(filepath, options2) {
3489
- const str2 = fs6.readFileSync(filepath, "utf8");
3489
+ const str2 = fs7.readFileSync(filepath, "utf8");
3490
3490
  const file = matter2(str2, options2);
3491
3491
  file.path = filepath;
3492
3492
  return file;
@@ -3515,8 +3515,8 @@ var require_gray_matter = __commonJS({
3515
3515
  });
3516
3516
 
3517
3517
  // src/skill-scripts/route-task-execution.ts
3518
- var fs5 = __toESM(require("fs"));
3519
- var path5 = __toESM(require("path"));
3518
+ var fs6 = __toESM(require("fs"));
3519
+ var path6 = __toESM(require("path"));
3520
3520
 
3521
3521
  // src/types.ts
3522
3522
  var SUPPORTED_HARNESSES = [
@@ -3797,8 +3797,8 @@ var readTaskExecutionPolicy = (taskMarkdown, context) => {
3797
3797
  };
3798
3798
 
3799
3799
  // src/skill-scripts/shared/execution-routing.ts
3800
- var fs4 = __toESM(require("fs"));
3801
- var path4 = __toESM(require("path"));
3800
+ var fs5 = __toESM(require("fs"));
3801
+ var path5 = __toESM(require("path"));
3802
3802
  var import_child_process = require("child_process");
3803
3803
 
3804
3804
  // node_modules/js-yaml/dist/js-yaml.mjs
@@ -6023,8 +6023,108 @@ var DEFAULT_DUMP_OPTIONS = {
6023
6023
  }
6024
6024
  };
6025
6025
 
6026
+ // src/skill-scripts/shared/external-dispatch.ts
6027
+ var fs4 = __toESM(require("fs"));
6028
+ var path4 = __toESM(require("path"));
6029
+ var taskPrompt = (request) => `Strikethroo external task dispatch \u2014 Plan ${request.planId}, Task ${request.taskId}.
6030
+ Workspace: ${request.workspace}
6031
+ Task file: ${request.taskFile}
6032
+ Before implementation, read and execute ${path4.join(
6033
+ request.workspace,
6034
+ ".ai/strikethroo/config/hooks/PRE_TASK_EXECUTION.md"
6035
+ )}. Halt if that hook fails.
6036
+
6037
+ Read and implement this task. Preserve dependency validation, status transitions, evidence reporting, and error-hook handling. Report failures clearly.
6038
+
6039
+ ${request.taskMarkdown}`;
6040
+ var command = (executable, argv, request) => ({
6041
+ executable,
6042
+ argv,
6043
+ cwd: request.workspace,
6044
+ stdin: taskPrompt(request)
6045
+ });
6046
+ var EXTERNAL_HARNESS_ADAPTERS = {
6047
+ claude: {
6048
+ executable: "claude",
6049
+ buildCommand: (request) => command(
6050
+ "claude",
6051
+ [
6052
+ "-p",
6053
+ "--model",
6054
+ request.model,
6055
+ ...request.reasoningEffort === void 0 ? [] : ["--effort", request.reasoningEffort]
6056
+ ],
6057
+ request
6058
+ ),
6059
+ authenticationArgv: () => ["auth", "status"]
6060
+ },
6061
+ codex: {
6062
+ executable: "codex",
6063
+ buildCommand: (request) => command(
6064
+ "codex",
6065
+ [
6066
+ "exec",
6067
+ "--model",
6068
+ request.model,
6069
+ ...request.reasoningEffort === void 0 ? [] : ["--config", `model_reasoning_effort=${request.reasoningEffort}`],
6070
+ "-"
6071
+ ],
6072
+ request
6073
+ ),
6074
+ authenticationArgv: () => ["login", "status"]
6075
+ },
6076
+ cursor: {
6077
+ executable: "cursor-agent",
6078
+ buildCommand: (request) => command("cursor-agent", ["--print", "--model", request.model], request),
6079
+ authenticationArgv: () => ["status"]
6080
+ },
6081
+ gemini: {
6082
+ executable: "gemini",
6083
+ buildCommand: (request) => command("gemini", ["--prompt", "", "--model", request.model], request),
6084
+ authenticationArgv: () => ["auth", "status"]
6085
+ },
6086
+ copilot: {
6087
+ executable: "copilot",
6088
+ buildCommand: (request) => command("copilot", ["-p", "", "--model", request.model], request),
6089
+ authenticationArgv: () => ["auth", "status"]
6090
+ },
6091
+ opencode: {
6092
+ executable: "opencode",
6093
+ buildCommand: (request) => command(
6094
+ "opencode",
6095
+ [
6096
+ "run",
6097
+ "--model",
6098
+ request.model,
6099
+ ...request.reasoningEffort === void 0 ? [] : ["--variant", request.reasoningEffort],
6100
+ "-"
6101
+ ],
6102
+ request
6103
+ ),
6104
+ authenticationArgv: () => ["auth", "list"]
6105
+ }
6106
+ };
6107
+ var adapterKeys = Object.keys(EXTERNAL_HARNESS_ADAPTERS).sort();
6108
+ var harnessKeys = [...SUPPORTED_HARNESSES].sort();
6109
+ if (adapterKeys.join("\0") !== harnessKeys.join("\0")) {
6110
+ throw new Error("External harness adapter registry does not cover SUPPORTED_HARNESSES exactly.");
6111
+ }
6112
+ var executableOnPath = (executable) => (process.env.PATH ?? "").split(path4.delimiter).some((directory) => {
6113
+ if (!directory) return false;
6114
+ const candidate = path4.join(directory, executable);
6115
+ try {
6116
+ return fs4.statSync(candidate).isFile();
6117
+ } catch {
6118
+ return false;
6119
+ }
6120
+ });
6121
+ var harnessExecutableAvailable = (harness) => {
6122
+ const adapter = EXTERNAL_HARNESS_ADAPTERS[harness];
6123
+ return adapter ? executableOnPath(adapter.executable) : false;
6124
+ };
6125
+
6026
6126
  // src/skill-scripts/shared/execution-routing.ts
6027
- var WORKSPACE_CONFIG_RELPATH = path4.join("config", "config.yaml");
6127
+ var WORKSPACE_CONFIG_RELPATH = path5.join("config", "config.yaml");
6028
6128
  var EXECUTION_ROUTING_SECTION = "execution_routing";
6029
6129
  var isPlainObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
6030
6130
  var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
@@ -6099,10 +6199,10 @@ var validateProfile = (name, raw, supportedHarnesses, errors) => {
6099
6199
  return { name, description: raw.description.trim(), targets };
6100
6200
  };
6101
6201
  var loadRoutingConfig = (strikethrooRoot, supportedHarnesses) => {
6102
- const configPath = path4.join(strikethrooRoot, WORKSPACE_CONFIG_RELPATH);
6202
+ const configPath = path5.join(strikethrooRoot, WORKSPACE_CONFIG_RELPATH);
6103
6203
  let contents;
6104
6204
  try {
6105
- contents = fs4.readFileSync(configPath, "utf8");
6205
+ contents = fs5.readFileSync(configPath, "utf8");
6106
6206
  } catch {
6107
6207
  return { kind: "no-config" };
6108
6208
  }
@@ -6221,16 +6321,23 @@ var selectTargets = (config, assignments, options2) => {
6221
6321
  return { id, profile: profileName, candidates: profile.targets };
6222
6322
  });
6223
6323
  if (!config.resolverScript) {
6324
+ const isHarnessAvailable = options2.harnessAvailable ?? harnessExecutableAvailable;
6325
+ const random = options2.random ?? Math.random;
6224
6326
  const selections2 = /* @__PURE__ */ new Map();
6225
6327
  for (const task of tasks) {
6226
- const first = task.candidates[0];
6227
- if (!first) throw new Error(`profile "${task.profile}" has no targets`);
6228
- selections2.set(task.id, first);
6328
+ const fallback2 = task.candidates[0];
6329
+ if (!fallback2) throw new Error(`profile "${task.profile}" has no targets`);
6330
+ const available = task.candidates.filter(
6331
+ (candidate) => candidate.harness === void 0 || isHarnessAvailable(candidate.harness)
6332
+ );
6333
+ const pool = available.length > 0 ? available : [fallback2];
6334
+ const chosen = pool[Math.floor(random() * pool.length)] ?? fallback2;
6335
+ selections2.set(task.id, chosen);
6229
6336
  }
6230
6337
  return { kind: "selected", selections: selections2 };
6231
6338
  }
6232
- const scriptPath = path4.resolve(options2.projectRoot, config.resolverScript);
6233
- if (!fs4.existsSync(scriptPath)) {
6339
+ const scriptPath = path5.resolve(options2.projectRoot, config.resolverScript);
6340
+ if (!fs5.existsSync(scriptPath)) {
6234
6341
  return {
6235
6342
  kind: "resolver-failure",
6236
6343
  detail: `custom resolver script not found: ${scriptPath}`
@@ -6344,10 +6451,10 @@ var usage = () => emit(
6344
6451
  2
6345
6452
  );
6346
6453
  var readTaskDocuments = (planDir) => {
6347
- const tasksDir = path5.join(planDir, "tasks");
6454
+ const tasksDir = path6.join(planDir, "tasks");
6348
6455
  let entries;
6349
6456
  try {
6350
- entries = fs5.readdirSync(tasksDir, { withFileTypes: true });
6457
+ entries = fs6.readdirSync(tasksDir, { withFileTypes: true });
6351
6458
  } catch {
6352
6459
  return { kind: "invalid", errors: [`no tasks directory at ${tasksDir}.`] };
6353
6460
  }
@@ -6357,8 +6464,8 @@ var readTaskDocuments = (planDir) => {
6357
6464
  const taskFiles = entries.filter((e) => e.isFile() && e.name.endsWith(".md")).map((e) => e.name).sort();
6358
6465
  for (const name of taskFiles) {
6359
6466
  const entry = { name };
6360
- const file = path5.join(tasksDir, entry.name);
6361
- const content = fs5.readFileSync(file, "utf8");
6467
+ const file = path6.join(tasksDir, entry.name);
6468
+ const content = fs6.readFileSync(file, "utf8");
6362
6469
  const id = extractPlanId(content, file);
6363
6470
  if (id === null) {
6364
6471
  errors.push(`task file ${entry.name} has no integer id in its frontmatter.`);
@@ -6431,7 +6538,7 @@ var main = () => {
6431
6538
  }
6432
6539
  let rawMap;
6433
6540
  try {
6434
- rawMap = JSON.parse(fs5.readFileSync(assignmentFile, "utf8"));
6541
+ rawMap = JSON.parse(fs6.readFileSync(assignmentFile, "utf8"));
6435
6542
  } catch (error) {
6436
6543
  emit(
6437
6544
  {
@@ -6453,7 +6560,7 @@ var main = () => {
6453
6560
  emit({ kind: "invalid-assignments", errors: assignmentResult.errors }, 1);
6454
6561
  return;
6455
6562
  }
6456
- const projectRoot = path5.dirname(path5.dirname(resolved.strikethrooRoot));
6563
+ const projectRoot = path6.dirname(path6.dirname(resolved.strikethrooRoot));
6457
6564
  const selection = selectTargets(config, assignmentResult.assignments, {
6458
6565
  planId: resolved.planId,
6459
6566
  projectRoot
@@ -6481,7 +6588,7 @@ var main = () => {
6481
6588
  emit(
6482
6589
  {
6483
6590
  kind: "routing-failure",
6484
- detail: `task ${task.id} (${path5.basename(task.file)}): ${error instanceof Error ? error.message : String(error)}`
6591
+ detail: `task ${task.id} (${path6.basename(task.file)}): ${error instanceof Error ? error.message : String(error)}`
6485
6592
  },
6486
6593
  1
6487
6594
  );
@@ -6503,11 +6610,11 @@ var main = () => {
6503
6610
  const written = [];
6504
6611
  try {
6505
6612
  for (const { task, next } of staged) {
6506
- fs5.writeFileSync(task.file, next);
6613
+ fs6.writeFileSync(task.file, next);
6507
6614
  written.push(task);
6508
6615
  }
6509
6616
  for (const { task, execution } of staged) {
6510
- const reread = fs5.readFileSync(task.file, "utf8");
6617
+ const reread = fs6.readFileSync(task.file, "utf8");
6511
6618
  const policy = readTaskExecutionPolicy(reread, policyContext);
6512
6619
  if (!policyMatchesExecution(policy, execution)) {
6513
6620
  throw new Error(`task ${task.id} failed post-write verification.`);
@@ -6516,7 +6623,7 @@ var main = () => {
6516
6623
  } catch (error) {
6517
6624
  for (const task of written) {
6518
6625
  try {
6519
- fs5.writeFileSync(task.file, task.content);
6626
+ fs6.writeFileSync(task.file, task.content);
6520
6627
  } catch {
6521
6628
  }
6522
6629
  }
@@ -10,12 +10,14 @@
10
10
  # execution_routing — automatic task execution routing during task
11
11
  # generation. Profiles are arbitrary, project-defined routing categories;
12
12
  # each `description` tells the task-generation LLM when the profile
13
- # applies, and `models` is an ordered priority list of exact execution
14
- # targets (`model` required; `harness` and `reasoning_effort` optional;
15
- # values are copied verbatim — no aliases, defaults, or translation).
16
- # The first target wins unless one optional global `resolver.script`
17
- # (repository-relative) selects a later candidate. Routing is disabled
18
- # while `profiles` is empty. See the customization guide for the full
13
+ # applies, and `models` is a list of exact execution targets (`model`
14
+ # required; `harness` and `reasoning_effort` optional; values are copied
15
+ # verbatim — no aliases, defaults, or translation). By default one target
16
+ # is chosen at random from those whose harness is available on this machine
17
+ # (a target with no `harness` is native and always available); one optional
18
+ # global `resolver.script` (repository-relative) can override that choice.
19
+ # Routing is disabled while `profiles` is empty. See the customization
20
+ # guide for the full
19
21
  # contract and an illustrative example:
20
22
  # https://strikethroo.canpicasoft.com/customization.html
21
23
 
@@ -1 +0,0 @@
1
- import{U as a,C as n}from"./mermaid.core-B9tzk4XH.js";const t=(r,o)=>a.lang.round(n.parse(r)[o]);export{t as c};
@@ -1 +0,0 @@
1
- import{s as a,a as s,c as e,C as t}from"./chunk-727SXJPM-cGYMQHSz.js";import{b as i}from"./mermaid.core-B9tzk4XH.js";import"./chunk-FMBD7UC4-x3hzV0ms.js";import"./chunk-ND2GUHAM-B2mXRbNo.js";import"./chunk-55IACEB6-Cw5bYv7_.js";import"./chunk-2J33WTMH-CLmshK_w.js";import"./index-CGjoHidG.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
@@ -1 +0,0 @@
1
- import{s as a,a as s,c as e,C as t}from"./chunk-727SXJPM-cGYMQHSz.js";import{b as i}from"./mermaid.core-B9tzk4XH.js";import"./chunk-FMBD7UC4-x3hzV0ms.js";import"./chunk-ND2GUHAM-B2mXRbNo.js";import"./chunk-55IACEB6-Cw5bYv7_.js";import"./chunk-2J33WTMH-CLmshK_w.js";import"./index-CGjoHidG.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
@@ -1 +0,0 @@
1
- import{b as e,a as r,s as a,S as s}from"./chunk-AQP2D5EJ-CQjDykhJ.js";import{b as i}from"./mermaid.core-B9tzk4XH.js";import"./chunk-55IACEB6-Cw5bYv7_.js";import"./chunk-2J33WTMH-CLmshK_w.js";import"./index-CGjoHidG.js";var u={parser:a,get db(){return new s(2)},renderer:r,styles:e,init:i(t=>{t.state||(t.state={}),t.state.arrowMarkerAbsolute=t.arrowMarkerAbsolute},"init")};export{u as diagram};