@drisp/cli 0.5.2 → 0.5.6

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.
@@ -206,14 +206,6 @@ function ensureRepo(owner, repo) {
206
206
  `Failed to clone marketplace repo ${owner}/${repo}: ${error.message}`
207
207
  );
208
208
  }
209
- } else {
210
- try {
211
- execFileSync("git", ["pull", "--ff-only"], {
212
- cwd: repoDir,
213
- stdio: "ignore"
214
- });
215
- } catch {
216
- }
217
209
  }
218
210
  return repoDir;
219
211
  }
@@ -734,15 +726,41 @@ function refreshVersionedMarketplacePluginTarget(ref, version, sourceRepoDir) {
734
726
  // src/infra/plugins/marketplace.ts
735
727
  function pullMarketplaceRepo(owner, repo) {
736
728
  const repoDir = marketplaceRepoCacheDir(owner, repo);
729
+ const repoUrl = `https://github.com/${owner}/${repo}.git`;
737
730
  if (!fs4.existsSync(repoDir)) {
731
+ cloneMarketplaceRepo(owner, repo, repoUrl, repoDir);
732
+ return;
733
+ }
734
+ try {
735
+ execFileSync3("git", ["pull", "--ff-only"], {
736
+ cwd: repoDir,
737
+ stdio: "ignore"
738
+ });
739
+ return;
740
+ } catch (pullError) {
741
+ const backupDir = `${repoDir}.backup-${Date.now()}`;
742
+ try {
743
+ fs4.renameSync(repoDir, backupDir);
744
+ cloneMarketplaceRepo(owner, repo, repoUrl, repoDir);
745
+ } catch (recoveryError) {
746
+ throw new Error(
747
+ `Failed to refresh marketplace repo ${owner}/${repo}: ${pullError.message}. Recovery clone failed: ${recoveryError.message}. Preserved previous cache at ${backupDir}.`
748
+ );
749
+ }
750
+ }
751
+ }
752
+ function cloneMarketplaceRepo(owner, repo, repoUrl, repoDir) {
753
+ fs4.mkdirSync(repoDir, { recursive: true });
754
+ try {
755
+ execFileSync3("git", ["clone", "--depth", "1", repoUrl, repoDir], {
756
+ stdio: "ignore"
757
+ });
758
+ } catch (error) {
759
+ fs4.rmSync(repoDir, { recursive: true, force: true });
738
760
  throw new Error(
739
- `Marketplace repo ${owner}/${repo} is not cached. It will be cloned on first use.`
761
+ `Failed to clone marketplace repo ${owner}/${repo}: ${error.message}`
740
762
  );
741
763
  }
742
- execFileSync3("git", ["pull", "--ff-only"], {
743
- cwd: repoDir,
744
- stdio: "ignore"
745
- });
746
764
  }
747
765
  function resolveMarketplacePlugin(ref) {
748
766
  requireGitForMarketplace("plugins");
@@ -934,31 +952,6 @@ function hasProjectWorkflow(projectDir) {
934
952
  return hasActiveWorkflow(projectConfigPath(projectDir));
935
953
  }
936
954
 
937
- // src/infra/plugins/mcpOptions.ts
938
- import fs6 from "fs";
939
- import path5 from "path";
940
- function collectMcpServersWithOptions(pluginDirs) {
941
- const result = [];
942
- const seen = /* @__PURE__ */ new Set();
943
- for (const dir of pluginDirs) {
944
- const mcpPath = path5.join(dir, ".mcp.json");
945
- if (!fs6.existsSync(mcpPath)) {
946
- continue;
947
- }
948
- const config = JSON.parse(fs6.readFileSync(mcpPath, "utf-8"));
949
- for (const [serverName, serverConfig] of Object.entries(
950
- config.mcpServers ?? {}
951
- )) {
952
- if (seen.has(serverName) || !Array.isArray(serverConfig.options) || serverConfig.options.length === 0) {
953
- continue;
954
- }
955
- seen.add(serverName);
956
- result.push({ serverName, options: serverConfig.options });
957
- }
958
- }
959
- return result;
960
- }
961
-
962
955
  // src/core/workflows/types.ts
963
956
  function pluginSpecRef(spec) {
964
957
  return typeof spec === "string" ? spec : spec.ref;
@@ -1052,17 +1045,17 @@ function refreshPinnedWorkflowPlugins(workflow) {
1052
1045
  }
1053
1046
 
1054
1047
  // src/core/workflows/registry.ts
1055
- import fs10 from "fs";
1048
+ import fs9 from "fs";
1056
1049
  import os5 from "os";
1057
- import path8 from "path";
1050
+ import path7 from "path";
1058
1051
 
1059
1052
  // src/core/workflows/builtins/index.ts
1060
- import fs8 from "fs";
1053
+ import fs7 from "fs";
1061
1054
  import os4 from "os";
1062
- import path6 from "path";
1055
+ import path5 from "path";
1063
1056
 
1064
1057
  // src/core/workflows/loopManager.ts
1065
- import fs7 from "fs";
1058
+ import fs6 from "fs";
1066
1059
 
1067
1060
  // src/core/workflows/templateVars.ts
1068
1061
  function substituteVariables(text, ctx) {
@@ -1093,7 +1086,7 @@ function createLoopManager(trackerPath, config) {
1093
1086
  const blockedMarker = config.blockedMarker ?? DEFAULT_BLOCKED_MARKER;
1094
1087
  function readTracker() {
1095
1088
  try {
1096
- return fs7.readFileSync(trackerPath, "utf-8");
1089
+ return fs6.readFileSync(trackerPath, "utf-8");
1097
1090
  } catch {
1098
1091
  return "";
1099
1092
  }
@@ -1205,17 +1198,17 @@ If you are blocked and cannot make further progress:
1205
1198
  3. Explain what needs to happen to unblock the task whenever possible
1206
1199
  `;
1207
1200
  function ensureSystemPromptFile() {
1208
- const dir = path6.join(
1201
+ const dir = path5.join(
1209
1202
  os4.homedir(),
1210
1203
  ".config",
1211
1204
  "athena",
1212
1205
  "builtins",
1213
1206
  "default"
1214
1207
  );
1215
- const filePath = path6.join(dir, "system_prompt.md");
1216
- if (!fs8.existsSync(filePath) || fs8.readFileSync(filePath, "utf-8") !== SYSTEM_PROMPT) {
1217
- fs8.mkdirSync(dir, { recursive: true });
1218
- fs8.writeFileSync(filePath, SYSTEM_PROMPT, "utf-8");
1208
+ const filePath = path5.join(dir, "system_prompt.md");
1209
+ if (!fs7.existsSync(filePath) || fs7.readFileSync(filePath, "utf-8") !== SYSTEM_PROMPT) {
1210
+ fs7.mkdirSync(dir, { recursive: true });
1211
+ fs7.writeFileSync(filePath, SYSTEM_PROMPT, "utf-8");
1219
1212
  }
1220
1213
  return filePath;
1221
1214
  }
@@ -1242,19 +1235,19 @@ function listBuiltinWorkflows() {
1242
1235
  }
1243
1236
 
1244
1237
  // src/core/workflows/sourceMetadata.ts
1245
- import fs9 from "fs";
1246
- import path7 from "path";
1238
+ import fs8 from "fs";
1239
+ import path6 from "path";
1247
1240
  function legacyLocalToNew(legacyPath, legacyRepoDir) {
1248
1241
  const repoDir = legacyRepoDir ?? findMarketplaceRepoDir(legacyPath);
1249
1242
  if (!repoDir) {
1250
1243
  return { kind: "filesystem", path: legacyPath };
1251
1244
  }
1252
1245
  try {
1253
- const canonicalRepoDir = fs9.realpathSync(repoDir);
1246
+ const canonicalRepoDir = fs8.realpathSync(repoDir);
1254
1247
  const listings = listMarketplaceWorkflowsFromRepo(canonicalRepoDir);
1255
- const absolutePath = fs9.realpathSync(legacyPath);
1248
+ const absolutePath = fs8.realpathSync(legacyPath);
1256
1249
  const match = listings.find(
1257
- (l) => fs9.realpathSync(l.workflowPath) === absolutePath
1250
+ (l) => fs8.realpathSync(l.workflowPath) === absolutePath
1258
1251
  );
1259
1252
  if (match) {
1260
1253
  return {
@@ -1269,11 +1262,11 @@ function legacyLocalToNew(legacyPath, legacyRepoDir) {
1269
1262
  return { kind: "filesystem", path: legacyPath };
1270
1263
  }
1271
1264
  function readWorkflowSourceMetadata(workflowDir) {
1272
- const sourceFile = path7.join(workflowDir, "source.json");
1273
- if (!fs9.existsSync(sourceFile)) return void 0;
1265
+ const sourceFile = path6.join(workflowDir, "source.json");
1266
+ if (!fs8.existsSync(sourceFile)) return void 0;
1274
1267
  let raw;
1275
1268
  try {
1276
- raw = JSON.parse(fs9.readFileSync(sourceFile, "utf-8"));
1269
+ raw = JSON.parse(fs8.readFileSync(sourceFile, "utf-8"));
1277
1270
  } catch {
1278
1271
  throw new Error(`Invalid source.json: ${sourceFile} is not valid JSON`);
1279
1272
  }
@@ -1315,9 +1308,9 @@ function readWorkflowSourceMetadata(workflowDir) {
1315
1308
  throw new Error(`Invalid source.json: ${sourceFile} kind is not supported`);
1316
1309
  }
1317
1310
  function writeWorkflowSourceMetadata(workflowDir, metadata) {
1318
- fs9.mkdirSync(workflowDir, { recursive: true });
1319
- fs9.writeFileSync(
1320
- path7.join(workflowDir, "source.json"),
1311
+ fs8.mkdirSync(workflowDir, { recursive: true });
1312
+ fs8.writeFileSync(
1313
+ path6.join(workflowDir, "source.json"),
1321
1314
  JSON.stringify({ v: 2, ...metadata }),
1322
1315
  "utf-8"
1323
1316
  );
@@ -1325,19 +1318,19 @@ function writeWorkflowSourceMetadata(workflowDir, metadata) {
1325
1318
 
1326
1319
  // src/core/workflows/registry.ts
1327
1320
  function registryDir() {
1328
- return path8.join(os5.homedir(), ".config", "athena", "workflows");
1321
+ return path7.join(os5.homedir(), ".config", "athena", "workflows");
1329
1322
  }
1330
1323
  function ensurePathWithinRoot(rootDir, targetPath, label) {
1331
- const relative = path8.relative(rootDir, targetPath);
1332
- if (relative === "" || !relative.startsWith("..") && !path8.isAbsolute(relative)) {
1324
+ const relative = path7.relative(rootDir, targetPath);
1325
+ if (relative === "" || !relative.startsWith("..") && !path7.isAbsolute(relative)) {
1333
1326
  return;
1334
1327
  }
1335
1328
  throw new Error(`${label} resolves outside the workflow root: ${targetPath}`);
1336
1329
  }
1337
1330
  function resolveWorkflow(name) {
1338
- const workflowDir = path8.join(registryDir(), name);
1339
- const workflowPath = path8.join(workflowDir, "workflow.json");
1340
- if (!fs10.existsSync(workflowPath)) {
1331
+ const workflowDir = path7.join(registryDir(), name);
1332
+ const workflowPath = path7.join(workflowDir, "workflow.json");
1333
+ if (!fs9.existsSync(workflowPath)) {
1341
1334
  const builtin = resolveBuiltinWorkflow(name);
1342
1335
  if (builtin) {
1343
1336
  return builtin;
@@ -1347,7 +1340,7 @@ function resolveWorkflow(name) {
1347
1340
  );
1348
1341
  }
1349
1342
  const source = readWorkflowSourceMetadata(workflowDir);
1350
- const raw = JSON.parse(fs10.readFileSync(workflowPath, "utf-8"));
1343
+ const raw = JSON.parse(fs9.readFileSync(workflowPath, "utf-8"));
1351
1344
  if (!Array.isArray(raw["plugins"])) {
1352
1345
  throw new Error(
1353
1346
  `Invalid workflow.json: "plugins" must be an array (got ${typeof raw["plugins"]})`
@@ -1387,15 +1380,15 @@ function resolveWorkflow(name) {
1387
1380
  );
1388
1381
  }
1389
1382
  const workflowFile = raw["workflowFile"];
1390
- if (typeof workflowFile === "string" && !path8.isAbsolute(workflowFile)) {
1391
- const workflowFilePath = path8.resolve(workflowDir, workflowFile);
1392
- if (!fs10.existsSync(workflowFilePath)) {
1383
+ if (typeof workflowFile === "string" && !path7.isAbsolute(workflowFile)) {
1384
+ const workflowFilePath = path7.resolve(workflowDir, workflowFile);
1385
+ if (!fs9.existsSync(workflowFilePath)) {
1393
1386
  throw new Error(
1394
1387
  `Invalid workflow.json: workflowFile "${workflowFile}" not found at ${workflowFilePath}`
1395
1388
  );
1396
1389
  }
1397
1390
  raw["workflowFile"] = workflowFilePath;
1398
- } else if (typeof workflowFile === "string" && !fs10.existsSync(workflowFile)) {
1391
+ } else if (typeof workflowFile === "string" && !fs9.existsSync(workflowFile)) {
1399
1392
  throw new Error(
1400
1393
  `Invalid workflow.json: workflowFile "${workflowFile}" not found`
1401
1394
  );
@@ -1406,29 +1399,29 @@ function resolveWorkflow(name) {
1406
1399
  };
1407
1400
  }
1408
1401
  function readWorkflowSource(sourcePath) {
1409
- const content = fs10.readFileSync(sourcePath, "utf-8");
1402
+ const content = fs9.readFileSync(sourcePath, "utf-8");
1410
1403
  return { content, workflow: JSON.parse(content) };
1411
1404
  }
1412
1405
  function copyWorkflowFiles(sourcePath, destDir) {
1413
1406
  const { content, workflow } = readWorkflowSource(sourcePath);
1414
- const absoluteSourcePath = path8.resolve(sourcePath);
1415
- const sourceDir = path8.dirname(absoluteSourcePath);
1416
- const absoluteDestDir = path8.resolve(destDir);
1417
- fs10.mkdirSync(absoluteDestDir, { recursive: true });
1418
- fs10.writeFileSync(
1419
- path8.join(absoluteDestDir, "workflow.json"),
1407
+ const absoluteSourcePath = path7.resolve(sourcePath);
1408
+ const sourceDir = path7.dirname(absoluteSourcePath);
1409
+ const absoluteDestDir = path7.resolve(destDir);
1410
+ fs9.mkdirSync(absoluteDestDir, { recursive: true });
1411
+ fs9.writeFileSync(
1412
+ path7.join(absoluteDestDir, "workflow.json"),
1420
1413
  content,
1421
1414
  "utf-8"
1422
1415
  );
1423
1416
  const copyRelativeAsset = (assetPath) => {
1424
- if (!assetPath || path8.isAbsolute(assetPath)) return;
1425
- const sourceAssetPath = path8.resolve(sourceDir, assetPath);
1417
+ if (!assetPath || path7.isAbsolute(assetPath)) return;
1418
+ const sourceAssetPath = path7.resolve(sourceDir, assetPath);
1426
1419
  ensurePathWithinRoot(sourceDir, sourceAssetPath, "Workflow asset");
1427
- if (!fs10.existsSync(sourceAssetPath)) return;
1428
- const destAssetPath = path8.resolve(absoluteDestDir, assetPath);
1420
+ if (!fs9.existsSync(sourceAssetPath)) return;
1421
+ const destAssetPath = path7.resolve(absoluteDestDir, assetPath);
1429
1422
  ensurePathWithinRoot(absoluteDestDir, destAssetPath, "Workflow asset");
1430
- fs10.mkdirSync(path8.dirname(destAssetPath), { recursive: true });
1431
- fs10.copyFileSync(sourceAssetPath, destAssetPath);
1423
+ fs9.mkdirSync(path7.dirname(destAssetPath), { recursive: true });
1424
+ fs9.copyFileSync(sourceAssetPath, destAssetPath);
1432
1425
  };
1433
1426
  const rawWorkflow = workflow;
1434
1427
  const promptAsset = rawWorkflow["workflowFile"];
@@ -1465,7 +1458,7 @@ function installWorkflowFromSource(source, name) {
1465
1458
  'Workflow has no "name" field. Provide --name to specify one.'
1466
1459
  );
1467
1460
  }
1468
- const destDir = path8.join(registryDir(), workflowName);
1461
+ const destDir = path7.join(registryDir(), workflowName);
1469
1462
  copyWorkflowFiles(source.workflowPath, destDir);
1470
1463
  const metadata = toStoredMetadata(source);
1471
1464
  writeWorkflowSourceMetadata(destDir, metadata);
@@ -1477,13 +1470,7 @@ function reResolveFromMetadata(metadata) {
1477
1470
  const slashIdx = slug.indexOf("/");
1478
1471
  const owner = slug.slice(0, slashIdx);
1479
1472
  const repo = slug.slice(slashIdx + 1);
1480
- try {
1481
- pullMarketplaceRepo(owner, repo);
1482
- } catch (error) {
1483
- if (!(error instanceof Error) || !error.message.includes("is not cached")) {
1484
- throw error;
1485
- }
1486
- }
1473
+ pullMarketplaceRepo(owner, repo);
1487
1474
  return resolveWorkflowInstall(metadata.ref, []);
1488
1475
  }
1489
1476
  if (metadata.kind === "marketplace-local") {
@@ -1507,7 +1494,7 @@ function reResolveFromMetadata(metadata) {
1507
1494
  return { kind: "filesystem", workflowPath: metadata.path };
1508
1495
  }
1509
1496
  function updateWorkflow(name) {
1510
- const workflowDir = path8.join(registryDir(), name);
1497
+ const workflowDir = path7.join(registryDir(), name);
1511
1498
  const metadata = readWorkflowSourceMetadata(workflowDir);
1512
1499
  if (!metadata) {
1513
1500
  throw new Error(
@@ -1521,8 +1508,8 @@ function updateWorkflow(name) {
1521
1508
  }
1522
1509
  function listWorkflows() {
1523
1510
  const dir = registryDir();
1524
- const installed = fs10.existsSync(dir) ? fs10.readdirSync(dir, { withFileTypes: true }).filter(
1525
- (entry) => entry.isDirectory() && fs10.existsSync(path8.join(dir, entry.name, "workflow.json"))
1511
+ const installed = fs9.existsSync(dir) ? fs9.readdirSync(dir, { withFileTypes: true }).filter(
1512
+ (entry) => entry.isDirectory() && fs9.existsSync(path7.join(dir, entry.name, "workflow.json"))
1526
1513
  ).map((entry) => entry.name) : [];
1527
1514
  const builtins = listBuiltinWorkflows().filter(
1528
1515
  (name) => !installed.includes(name)
@@ -1530,11 +1517,11 @@ function listWorkflows() {
1530
1517
  return [...builtins, ...installed];
1531
1518
  }
1532
1519
  function removeWorkflow(name) {
1533
- const dir = path8.join(registryDir(), name);
1534
- if (!fs10.existsSync(dir)) {
1520
+ const dir = path7.join(registryDir(), name);
1521
+ if (!fs9.existsSync(dir)) {
1535
1522
  throw new Error(`Workflow "${name}" not found.`);
1536
1523
  }
1537
- fs10.rmSync(dir, { recursive: true, force: true });
1524
+ fs9.rmSync(dir, { recursive: true, force: true });
1538
1525
  }
1539
1526
 
1540
1527
  // src/core/workflows/applyWorkflow.ts
@@ -1568,8 +1555,8 @@ function compileWorkflowPlan(input) {
1568
1555
  }
1569
1556
 
1570
1557
  // src/core/workflows/sessionPlan.ts
1571
- import fs11 from "fs";
1572
- import path9 from "path";
1558
+ import fs10 from "fs";
1559
+ import path8 from "path";
1573
1560
 
1574
1561
  // src/core/workflows/stateMachine.md
1575
1562
  var stateMachine_default = "# Stateless Session Protocol\n\nYou run in a stateless loop. Each session is a fresh process with no memory of prior sessions. **The tracker file is your only continuity** \u2014 read it, work, write it. Assume interruption: the runner may kill a long session, your context may collapse under tool output, you may hit token limits mid-task. Anything not in the tracker is gone.\n\n## First action, every session\n\n1. Read the tracker at the configured path (default: `.athena/<session_id>/tracker.md`). The runner provides the session ID \u2014 do not invent one.\n2. If the tracker contains `<!-- TRACKER_SKELETON -->` \u2192 this is session 1, run [**Orient**](#orient-session-1).\n3. Otherwise \u2192 this is a continuation, run [**Execute**](#execute-session-2) from where the tracker says, not from the start of the flow.\n\nReading first prevents two failure modes that waste whole sessions: redoing work already done, or contradicting decisions a prior session made.\n\n## Tracker contract\n\nThe tracker must always answer four questions:\n\n1. What are we trying to accomplish?\n2. What has been done?\n3. What's left?\n4. What should the next session do first?\n\nA future session has no other context. If something isn't here, it doesn't exist. Section headings may vary by workflow, but these four answers must be explicit and easy to find.\n\n### Terminal markers\n\nDefault markers (workflows may override \u2014 use the markers configured for the active workflow):\n\n- `<!-- WORKFLOW_COMPLETE -->` \u2014 all work done and verified\n- `<!-- WORKFLOW_BLOCKED -->` or `<!-- WORKFLOW_BLOCKED: reason -->` \u2014 cannot proceed without external intervention\n\nRules:\n\n- Only the last non-empty line of the tracker is authoritative. Marker-like text in notes, examples, or quoted instructions earlier in the file is ignored.\n- The runner trusts markers unconditionally. A premature marker ends the loop with no automatic recovery \u2014 write one only when its criteria are fully met.\n- Include a concrete reason after `WORKFLOW_BLOCKED:` whenever possible; the bare form is still valid.\n\n## Phases\n\n### Orient (session 1)\n\n1. **Replace the skeleton immediately**, before any domain work. Even a three-line tracker (goal + \"orienting\") protects you if the session dies during setup.\n2. Run the workflow's orientation steps. These vary by domain \u2014 a test-writing workflow explores the product in a browser; a migration workflow audits the schema. The workflow defines what orientation means.\n3. Refine the tracker into a granular plan. Each task a concrete, verifiable unit of work, including verification steps (running checks, reviewing output) \u2014 not just implementation. Vague tasks (\"write tests\") cannot be meaningfully resumed by a future session that has no idea what they mean here.\n4. Record concrete observations \u2014 what you actually saw, not what you assumed. Wrong assumptions burn entire future sessions on rework.\n5. **Single-turn requests still go through this phase.** If the entire request is satisfied in one turn, write a minimal tracker (what was asked, what was done, the outcome) and append `<!-- WORKFLOW_COMPLETE -->`. Leaving the skeleton in place causes the runner to classify the session as a failure.\n\n### Execute (session 2+)\n\n- Work from where the tracker says, in the workflow's prescribed sequence. Not every session covers every step.\n- If the workflow defines a skill table, **load the relevant skill before each activity**. Skills carry the implementation detail (scaffolding steps, locator rules, anti-patterns, code templates) that this protocol intentionally doesn't repeat.\n- Delegate heavy exploration or generation to subagents via the Task tool. Pass file paths, conventions, and concrete output expectations; tell them which skill to load. Respect the workflow's **delegation constraints** \u2014 some operations must run in the main agent because their output is proof, or because the main agent needs to interpret results in context.\n- Run quality gates in order. Do not skip \u2014 they exist because skipping cascades into rework. On a failing verdict, address the issues and re-run before proceeding. Respect the workflow's **retry limits**: repeated failure usually signals a deeper issue another retry won't fix.\n\n### End\n\n1. Tracker reflects all progress, discoveries, and blockers.\n2. Tracker says clearly what the next session should do first.\n3. If all work is verified: append the completion marker.\n4. If an unrecoverable blocker prevents progress: append the blocked marker, with a reason if you have one.\n\n## When to write the tracker\n\nWrite on **concrete triggers**, not on a vague sense of \"meaningful progress.\" The right cadence sits between every-tool-call (noisy log, wastes tokens) and end-of-session (everything lost if you die mid-task).\n\n- **Discrete unit done** \u2014 file written, fix applied, test run, gate passed. Reflect the new reality before starting the next unit.\n- **Insight learned** \u2014 API quirk, config field that turned out to matter, dead end ruled out, decision between two approaches. Insights are tracker-worthy even when no code changed; rediscovering them costs the next session a full re-exploration. The tracker is a knowledge ledger, not just a task log.\n- **About to do something risky or long-running** \u2014 subagent dispatch, long build, flaky external call, large refactor. Write _first_, then act. If the operation kills your session, only what's on disk survives.\n- **Plan changed** \u2014 task resequenced, new task surfaced, planned task no longer needed. Stale plans poison continuation sessions.\n- **You haven't written in a while** \u2014 if you can't remember the last update, you've gone too long. A short defensive update (\"doing X, last completed Y, next is Z\") beats nothing.\n\nEach update covers: what changed (work or knowledge), what's now next, and any caveat the next session needs. Don't transcribe tool calls \u2014 the tracker is a contract with your future self, not a replay log.\n\nThe cost of one extra tracker update is a few tokens. The cost of dying without one is a whole wasted session. Bias toward writing.\n\n## Task UI projection\n\nThe tracker is the durable source of truth. Your harness's task tools are a session-scoped UI projection of the same plan, shown to the user in their CLI widget. They do not survive process exit.\n\n{{TASK_TOOL_INSTRUCTIONS}}\n\n- **Session 1, after orientation:** project the tracker's task plan into the task tools.\n- **Session 2+, after reading the tracker:** recreate the projection from the tracker; do not assume task IDs from prior sessions still exist.\n- **During work:** update both \u2014 the task tools for immediate UI feedback, the tracker for persistence \u2014 in the same working phase.\n\n## Session bounding\n\nEach fresh session starts with a clean context window and a compact tracker \u2014 effectively self-compaction. As you work, context fills with tool outputs and intermediate state. The longer you run, the more attention is spread across tokens that are no longer relevant, degrading precision on the work that matters now.\n\nWork a bounded chunk per session. Ending early and letting the next session pick up from a clean tracker is almost always better than pushing through with a heavy context. Natural checkpoints:\n\n- After a quality gate\n- After crossing multiple phases (explored \u2192 planned \u2192 wrote specs) \u2014 stop before pushing into the next\n- When your context is visibly heavy with tool output from earlier work\n\n## Quick reference\n\n- [ ] Read the tracker before doing anything else\n- [ ] Replace the skeleton immediately, even for single-turn requests\n- [ ] Update on concrete triggers \u2014 unit done, insight learned, risky op pending, plan changed\n- [ ] Project the tracker plan into task tools at session start; keep both in sync as work lands\n- [ ] Load the workflow's skill before each activity\n- [ ] Run quality gates in order; respect delegation constraints and retry limits\n- [ ] Write the completion marker only when all work is verified\n- [ ] Checkpoint and end before context goes stale\n";
@@ -1612,10 +1599,10 @@ function readWorkflowOverride(projectDir, workflow, sessionId, trackerPath, harn
1612
1599
  if (!workflow?.workflowFile) {
1613
1600
  return { workflowOverride: void 0, warnings: [] };
1614
1601
  }
1615
- const resolvedPath = path9.isAbsolute(workflow.workflowFile) ? workflow.workflowFile : path9.resolve(projectDir, workflow.workflowFile);
1602
+ const resolvedPath = path8.isAbsolute(workflow.workflowFile) ? workflow.workflowFile : path8.resolve(projectDir, workflow.workflowFile);
1616
1603
  let workflowContent;
1617
1604
  try {
1618
- workflowContent = fs11.readFileSync(resolvedPath, "utf-8");
1605
+ workflowContent = fs10.readFileSync(resolvedPath, "utf-8");
1619
1606
  } catch {
1620
1607
  return {
1621
1608
  workflowOverride: void 0,
@@ -1629,9 +1616,9 @@ function readWorkflowOverride(projectDir, workflow, sessionId, trackerPath, harn
1629
1616
  sessionId,
1630
1617
  trackerPath: trackerPath ?? void 0
1631
1618
  });
1632
- const workflowDir = path9.dirname(resolvedPath);
1633
- const composedPath = path9.join(workflowDir, ".composed-system-prompt.md");
1634
- fs11.writeFileSync(composedPath, composed, "utf-8");
1619
+ const workflowDir = path8.dirname(resolvedPath);
1620
+ const composedPath = path8.join(workflowDir, ".composed-system-prompt.md");
1621
+ fs10.writeFileSync(composedPath, composed, "utf-8");
1635
1622
  return {
1636
1623
  workflowOverride: {
1637
1624
  appendSystemPromptFile: composedPath,
@@ -1658,7 +1645,7 @@ function resolveTrackerPath(input) {
1658
1645
  return null;
1659
1646
  }
1660
1647
  const promptPath = input.sessionId ? rawPath.replaceAll("{sessionId}", input.sessionId) : rawPath;
1661
- const absolutePath = path9.isAbsolute(promptPath) ? promptPath : path9.resolve(input.projectDir, promptPath);
1648
+ const absolutePath = path8.isAbsolute(promptPath) ? promptPath : path8.resolve(input.projectDir, promptPath);
1662
1649
  return {
1663
1650
  absolutePath,
1664
1651
  promptPath
@@ -1704,7 +1691,7 @@ function shouldContinueWorkflowRun(state) {
1704
1691
  return null;
1705
1692
  }
1706
1693
  const loopState = loopManager.getState();
1707
- if (!fs11.existsSync(loopManager.trackerPath)) {
1694
+ if (!fs10.existsSync(loopManager.trackerPath)) {
1708
1695
  cleanupWorkflowRun(state);
1709
1696
  return {
1710
1697
  reason: "missing_tracker",
@@ -1750,8 +1737,8 @@ import { useCallback, useEffect, useRef, useState } from "react";
1750
1737
 
1751
1738
  // src/core/workflows/workflowRunner.ts
1752
1739
  import crypto from "crypto";
1753
- import fs12 from "fs";
1754
- import path10 from "path";
1740
+ import fs11 from "fs";
1741
+ import path9 from "path";
1755
1742
  var NULL_TOKENS = {
1756
1743
  input: null,
1757
1744
  output: null,
@@ -1808,9 +1795,9 @@ function mergeTokens(base, next) {
1808
1795
  };
1809
1796
  }
1810
1797
  function defaultCreateTracker(trackerPath, content) {
1811
- fs12.mkdirSync(path10.dirname(trackerPath), { recursive: true });
1798
+ fs11.mkdirSync(path9.dirname(trackerPath), { recursive: true });
1812
1799
  try {
1813
- fs12.writeFileSync(trackerPath, content, { encoding: "utf-8", flag: "wx" });
1800
+ fs11.writeFileSync(trackerPath, content, { encoding: "utf-8", flag: "wx" });
1814
1801
  } catch (e) {
1815
1802
  if (e.code !== "EEXIST") throw e;
1816
1803
  }
@@ -2067,6 +2054,31 @@ function useWorkflowSessionController(base, input) {
2067
2054
  };
2068
2055
  }
2069
2056
 
2057
+ // src/infra/plugins/mcpOptions.ts
2058
+ import fs12 from "fs";
2059
+ import path10 from "path";
2060
+ function collectMcpServersWithOptions(pluginDirs) {
2061
+ const result = [];
2062
+ const seen = /* @__PURE__ */ new Set();
2063
+ for (const dir of pluginDirs) {
2064
+ const mcpPath = path10.join(dir, ".mcp.json");
2065
+ if (!fs12.existsSync(mcpPath)) {
2066
+ continue;
2067
+ }
2068
+ const config = JSON.parse(fs12.readFileSync(mcpPath, "utf-8"));
2069
+ for (const [serverName, serverConfig] of Object.entries(
2070
+ config.mcpServers ?? {}
2071
+ )) {
2072
+ if (seen.has(serverName) || !Array.isArray(serverConfig.options) || serverConfig.options.length === 0) {
2073
+ continue;
2074
+ }
2075
+ seen.add(serverName);
2076
+ result.push({ serverName, options: serverConfig.options });
2077
+ }
2078
+ }
2079
+ return result;
2080
+ }
2081
+
2070
2082
  export {
2071
2083
  createWorkflowRunner,
2072
2084
  useWorkflowSessionController,
@@ -2085,7 +2097,6 @@ export {
2085
2097
  writeGlobalConfig,
2086
2098
  writeProjectConfig,
2087
2099
  hasProjectWorkflow,
2088
- collectMcpServersWithOptions,
2089
2100
  installWorkflowPlugins,
2090
2101
  resolveWorkflowPlugins,
2091
2102
  listBuiltinWorkflows,
@@ -2094,6 +2105,7 @@ export {
2094
2105
  updateWorkflow,
2095
2106
  listWorkflows,
2096
2107
  removeWorkflow,
2097
- compileWorkflowPlan
2108
+ compileWorkflowPlan,
2109
+ collectMcpServersWithOptions
2098
2110
  };
2099
- //# sourceMappingURL=chunk-A54HGVML.js.map
2111
+ //# sourceMappingURL=chunk-7E54JMXH.js.map