@drisp/cli 0.5.23 → 0.5.26

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.
@@ -952,18 +952,9 @@ function readConfigFile(configPath, baseDir) {
952
952
  );
953
953
  }
954
954
  const plugins = (raw.plugins ?? []).map((p) => {
955
- if (isMarketplaceRef(p)) {
956
- try {
957
- return resolveMarketplacePlugin(p);
958
- } catch (error) {
959
- console.error(
960
- `Warning: skipping plugin "${p}": ${error.message}`
961
- );
962
- return null;
963
- }
964
- }
955
+ if (isMarketplaceRef(p)) return p;
965
956
  return path4.isAbsolute(p) ? p : path4.resolve(baseDir, p);
966
- }).filter((p) => p !== null);
957
+ });
967
958
  const additionalDirectories = (raw.additionalDirectories ?? []).map(
968
959
  (dir) => path4.isAbsolute(dir) ? dir : path4.resolve(baseDir, dir)
969
960
  );
@@ -980,7 +971,12 @@ function readConfigFile(configPath, baseDir) {
980
971
  telemetry: raw.telemetry,
981
972
  telemetryDiagnostics: raw.telemetryDiagnostics,
982
973
  deviceId: raw.deviceId,
983
- channels: raw.channels
974
+ channels: raw.channels,
975
+ // Personal capabilities are stored opaque. In particular skill `path`
976
+ // is NOT relative-resolved against baseDir (R1) — Issue 3 resolves it
977
+ // to an absolute path at install time.
978
+ mcpServers: raw.mcpServers,
979
+ skills: raw.skills
984
980
  };
985
981
  }
986
982
  function readExistingConfig(configPath) {
@@ -1138,7 +1134,7 @@ import fs7 from "fs";
1138
1134
  import os4 from "os";
1139
1135
  import path5 from "path";
1140
1136
 
1141
- // src/core/workflows/loopManager.ts
1137
+ // src/core/workflows/trackerReader.ts
1142
1138
  import fs6 from "fs";
1143
1139
 
1144
1140
  // src/core/workflows/templateVars.ts
@@ -1157,84 +1153,60 @@ function substituteVariables(text, ctx) {
1157
1153
  return result;
1158
1154
  }
1159
1155
 
1160
- // src/core/workflows/loopManager.ts
1156
+ // src/core/workflows/trackerReader.ts
1161
1157
  var DEFAULT_COMPLETION_MARKER = "<!-- WORKFLOW_COMPLETE -->";
1162
1158
  var DEFAULT_BLOCKED_MARKER = "<!-- WORKFLOW_BLOCKED";
1163
1159
  var DEFAULT_TRACKER_PATH = ".athena/{sessionId}/tracker.md";
1164
1160
  var TRACKER_SKELETON_MARKER = "<!-- TRACKER_SKELETON -->";
1165
1161
  var DEFAULT_CONTINUE_PROMPT = "Continue the task. Read the tracker at {trackerPath} for current progress. If the work is complete or blocked, the terminal marker must be the final non-empty line of the tracker; do not write any prose after it.";
1166
- function createLoopManager(trackerPath, config) {
1167
- let iteration = 0;
1168
- let active = true;
1169
- const completionMarker = config.completionMarker ?? DEFAULT_COMPLETION_MARKER;
1170
- const blockedMarker = config.blockedMarker ?? DEFAULT_BLOCKED_MARKER;
1171
- function readTracker() {
1172
- try {
1173
- return fs6.readFileSync(trackerPath, "utf-8");
1174
- } catch {
1175
- return "";
1176
- }
1177
- }
1178
- function getNonEmptyLines(content) {
1179
- return content.trimEnd().split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
1180
- }
1181
- function isBlockedLine(line) {
1182
- return line === `${blockedMarker} -->` || line.startsWith(`${blockedMarker}:`);
1183
- }
1184
- function isTerminalMarkerLine(line) {
1185
- return line === completionMarker || isBlockedLine(line);
1186
- }
1187
- function getTerminalLine(content) {
1188
- const lines = getNonEmptyLines(content);
1189
- return lines.at(-1);
1190
- }
1191
- function getMisplacedTerminalMarker(content) {
1192
- const lines = getNonEmptyLines(content);
1193
- if (lines.length < 2) return void 0;
1194
- const terminalLine = lines.at(-1);
1195
- if (terminalLine && isTerminalMarkerLine(terminalLine)) return void 0;
1196
- return lines.slice(0, -1).find(isTerminalMarkerLine);
1197
- }
1198
- function extractBlockedReason(line) {
1199
- if (!line.startsWith(blockedMarker)) return void 0;
1200
- const afterMarker = line.slice(blockedMarker.length);
1201
- const match = afterMarker.match(/^:\s*(.+?)(?:\s*-->|$)/);
1202
- return match?.[1]?.trim();
1203
- }
1204
- function getState() {
1205
- const content = readTracker();
1206
- const terminalLine = getTerminalLine(content);
1207
- const completed = terminalLine === completionMarker;
1208
- const blocked = terminalLine !== void 0 && isBlockedLine(terminalLine);
1209
- const blockedReason = blocked && terminalLine ? extractBlockedReason(terminalLine) : void 0;
1210
- const misplacedTerminalMarker = getMisplacedTerminalMarker(content);
1211
- const reachedLimit = iteration >= config.maxIterations;
1212
- const skeletonNotReplaced = content.includes(TRACKER_SKELETON_MARKER);
1213
- return {
1214
- active,
1215
- iteration,
1216
- maxIterations: config.maxIterations,
1217
- completionMarker,
1218
- blockedMarker,
1219
- completed,
1220
- blocked,
1221
- blockedReason,
1222
- misplacedTerminalMarker,
1223
- reachedLimit,
1224
- skeletonNotReplaced
1225
- };
1226
- }
1227
- function incrementIteration() {
1228
- iteration++;
1162
+ function readTracker(trackerPath) {
1163
+ try {
1164
+ return fs6.readFileSync(trackerPath, "utf-8");
1165
+ } catch {
1166
+ return "";
1229
1167
  }
1230
- function deactivate() {
1231
- active = false;
1168
+ }
1169
+ function getNonEmptyLines(content) {
1170
+ return content.trimEnd().split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
1171
+ }
1172
+ function isBlockedLine(line, blockedMarker) {
1173
+ return line === `${blockedMarker} -->` || line.startsWith(`${blockedMarker}:`);
1174
+ }
1175
+ function isTerminalMarkerLine(line, completionMarker, blockedMarker) {
1176
+ return line === completionMarker || isBlockedLine(line, blockedMarker);
1177
+ }
1178
+ function getMisplacedTerminalMarker(lines, completionMarker, blockedMarker) {
1179
+ if (lines.length < 2) return void 0;
1180
+ const terminalLine = lines.at(-1);
1181
+ if (terminalLine && isTerminalMarkerLine(terminalLine, completionMarker, blockedMarker)) {
1182
+ return void 0;
1232
1183
  }
1184
+ return lines.slice(0, -1).find((line) => isTerminalMarkerLine(line, completionMarker, blockedMarker));
1185
+ }
1186
+ function extractBlockedReason(line, blockedMarker) {
1187
+ if (!line.startsWith(blockedMarker)) return void 0;
1188
+ const afterMarker = line.slice(blockedMarker.length);
1189
+ const match = afterMarker.match(/^:\s*(.+?)(?:\s*-->|$)/);
1190
+ return match?.[1]?.trim();
1191
+ }
1192
+ function parseTrackerState(content, markers = {}) {
1193
+ const completionMarker = markers.completionMarker ?? DEFAULT_COMPLETION_MARKER;
1194
+ const blockedMarker = markers.blockedMarker ?? DEFAULT_BLOCKED_MARKER;
1195
+ const lines = getNonEmptyLines(content);
1196
+ const terminalLine = lines.at(-1);
1197
+ const completed = terminalLine === completionMarker;
1198
+ const blocked = terminalLine !== void 0 && isBlockedLine(terminalLine, blockedMarker);
1199
+ const blockedReason = blocked && terminalLine ? extractBlockedReason(terminalLine, blockedMarker) : void 0;
1233
1200
  return {
1234
- getState,
1235
- incrementIteration,
1236
- deactivate,
1237
- trackerPath
1201
+ completed,
1202
+ blocked,
1203
+ blockedReason,
1204
+ misplacedTerminalMarker: getMisplacedTerminalMarker(
1205
+ lines,
1206
+ completionMarker,
1207
+ blockedMarker
1208
+ ),
1209
+ skeletonNotReplaced: content.includes(TRACKER_SKELETON_MARKER)
1238
1210
  };
1239
1211
  }
1240
1212
  function buildContinuePrompt(loop) {
@@ -1808,7 +1780,6 @@ function resolveTrackerPath(input) {
1808
1780
  function createWorkflowRunState(input) {
1809
1781
  const { projectDir, sessionId, workflow, harness } = input;
1810
1782
  const trackerResolved = resolveTrackerPath({ projectDir, sessionId, workflow });
1811
- const loopManager = workflow?.loop?.enabled === true && trackerResolved ? createLoopManager(trackerResolved.absolutePath, workflow.loop) : null;
1812
1783
  const { workflowOverride, warnings } = readWorkflowOverride(
1813
1784
  projectDir,
1814
1785
  workflow,
@@ -1818,15 +1789,16 @@ function createWorkflowRunState(input) {
1818
1789
  );
1819
1790
  return {
1820
1791
  workflow,
1821
- loopManager,
1822
1792
  trackerPathForPrompt: trackerResolved?.promptPath,
1823
1793
  workflowOverride,
1824
1794
  warnings
1825
1795
  };
1826
1796
  }
1827
1797
  function prepareWorkflowTurn(state, input) {
1828
- const { workflow, loopManager } = state;
1829
- const prompt = workflow && loopManager && loopManager.getState().iteration > 0 ? buildContinuePrompt({
1798
+ const { workflow } = state;
1799
+ const iteration = input.iteration ?? 1;
1800
+ const isContinuation = workflow?.loop?.enabled === true && iteration > 1;
1801
+ const prompt = isContinuation ? buildContinuePrompt({
1830
1802
  ...workflow.loop,
1831
1803
  trackerPath: state.trackerPathForPrompt ?? workflow.loop?.trackerPath
1832
1804
  }) : workflow ? applyPromptTemplate(workflow.promptTemplate, input.prompt) : input.prompt;
@@ -1839,67 +1811,57 @@ function prepareWorkflowTurn(state, input) {
1839
1811
  warnings: state.warnings
1840
1812
  };
1841
1813
  }
1842
- function shouldContinueWorkflowRun(state) {
1843
- const { workflow, loopManager } = state;
1844
- if (!workflow?.loop?.enabled || !loopManager) {
1845
- return null;
1846
- }
1847
- const loopState = loopManager.getState();
1848
- if (!fs10.existsSync(loopManager.trackerPath)) {
1849
- cleanupWorkflowRun(state);
1814
+
1815
+ // src/core/workflows/useWorkflowSessionController.ts
1816
+ import { useCallback, useEffect, useRef, useState } from "react";
1817
+
1818
+ // src/core/workflows/workflowRunner.ts
1819
+ import crypto from "crypto";
1820
+ import fs12 from "fs";
1821
+ import path9 from "path";
1822
+
1823
+ // src/core/workflows/terminalOutcome.ts
1824
+ import fs11 from "fs";
1825
+ var MISSING_TRACKER_MESSAGE = "the tracker file went missing during the run \u2014 the workflow can no longer verify progress";
1826
+ var SKELETON_NOT_REPLACED_MESSAGE = "tracker skeleton was never replaced \u2014 Claude did not bootstrap the tracker";
1827
+ var MISPLACED_TERMINAL_MARKER_MESSAGE = "terminal workflow marker is not the final non-empty line of the tracker; move all summary text above the marker";
1828
+ function resolveTurnOutcome(input) {
1829
+ const { trackerPath, loop, iteration } = input;
1830
+ if (!fs11.existsSync(trackerPath)) {
1850
1831
  return {
1851
- reason: "missing_tracker",
1852
- maxIterations: loopState.maxIterations
1832
+ kind: "stop",
1833
+ status: "failed",
1834
+ stopReason: MISSING_TRACKER_MESSAGE
1853
1835
  };
1854
1836
  }
1855
- if (loopState.skeletonNotReplaced) {
1856
- cleanupWorkflowRun(state);
1837
+ const tracker = parseTrackerState(readTracker(trackerPath), loop);
1838
+ if (tracker.skeletonNotReplaced) {
1857
1839
  return {
1858
- reason: "skeleton_not_replaced",
1859
- maxIterations: loopState.maxIterations
1840
+ kind: "stop",
1841
+ status: "failed",
1842
+ stopReason: SKELETON_NOT_REPLACED_MESSAGE
1860
1843
  };
1861
1844
  }
1862
- if (loopState.misplacedTerminalMarker) {
1863
- cleanupWorkflowRun(state);
1845
+ if (tracker.misplacedTerminalMarker) {
1864
1846
  return {
1865
- reason: "misplaced_terminal_marker",
1866
- maxIterations: loopState.maxIterations
1847
+ kind: "stop",
1848
+ status: "failed",
1849
+ stopReason: MISPLACED_TERMINAL_MARKER_MESSAGE
1867
1850
  };
1868
1851
  }
1869
- let reason;
1870
- if (loopState.completed) {
1871
- reason = "completed";
1872
- } else if (loopState.blocked) {
1873
- reason = "blocked";
1874
- } else if (loopState.iteration + 1 >= loopState.maxIterations) {
1875
- reason = "max_iterations";
1852
+ if (tracker.completed) {
1853
+ return { kind: "stop", status: "completed" };
1876
1854
  }
1877
- if (reason) {
1878
- cleanupWorkflowRun(state);
1879
- return {
1880
- reason,
1881
- blockedReason: loopState.blockedReason,
1882
- maxIterations: loopState.maxIterations
1883
- };
1855
+ if (tracker.blocked) {
1856
+ return { kind: "stop", status: "blocked", stopReason: tracker.blockedReason };
1884
1857
  }
1885
- loopManager.incrementIteration();
1886
- return null;
1887
- }
1888
- function cleanupWorkflowRun(state) {
1889
- if (!state.loopManager) {
1890
- return;
1858
+ if (iteration >= loop.maxIterations) {
1859
+ return { kind: "stop", status: "exhausted" };
1891
1860
  }
1892
- state.loopManager.deactivate();
1893
- state.loopManager = null;
1861
+ return { kind: "continue" };
1894
1862
  }
1895
1863
 
1896
- // src/core/workflows/useWorkflowSessionController.ts
1897
- import { useCallback, useEffect, useRef, useState } from "react";
1898
-
1899
1864
  // src/core/workflows/workflowRunner.ts
1900
- import crypto from "crypto";
1901
- import fs11 from "fs";
1902
- import path9 from "path";
1903
1865
  var NULL_TOKENS = {
1904
1866
  input: null,
1905
1867
  output: null,
@@ -1956,9 +1918,9 @@ function mergeTokens(base, next) {
1956
1918
  };
1957
1919
  }
1958
1920
  function defaultCreateTracker(trackerPath, content) {
1959
- fs11.mkdirSync(path9.dirname(trackerPath), { recursive: true });
1921
+ fs12.mkdirSync(path9.dirname(trackerPath), { recursive: true });
1960
1922
  try {
1961
- fs11.writeFileSync(trackerPath, content, { encoding: "utf-8", flag: "wx" });
1923
+ fs12.writeFileSync(trackerPath, content, { encoding: "utf-8", flag: "wx" });
1962
1924
  } catch (e) {
1963
1925
  if (e.code !== "EEXIST") throw e;
1964
1926
  }
@@ -2016,83 +1978,72 @@ function createWorkflowRunner(input) {
2016
1978
  let nextContinuation = input.initialContinuation ?? {
2017
1979
  mode: "fresh"
2018
1980
  };
2019
- try {
2020
- while (!cancelled) {
2021
- iterations++;
2022
- const prepared = prepareWorkflowTurn(workflowState, {
2023
- prompt: input.prompt,
2024
- configOverride: void 0
2025
- });
2026
- const turnResult = await input.startTurn({
2027
- prompt: prepared.prompt,
2028
- continuation: nextContinuation,
2029
- configOverride: prepared.configOverride
2030
- });
2031
- if (cancelled) {
2032
- status = "cancelled";
2033
- persist();
2034
- break;
2035
- }
2036
- cumulativeTokens = mergeTokens(cumulativeTokens, turnResult.tokens);
2037
- if (turnResult.error || turnResult.exitCode !== null && turnResult.exitCode !== 0) {
2038
- status = "failed";
2039
- const parts = [];
2040
- if (turnResult.error?.message) {
2041
- parts.push(turnResult.error.message);
2042
- } else if (turnResult.exitCode !== null) {
2043
- parts.push(`Process exited with code ${turnResult.exitCode}`);
2044
- }
2045
- if (turnResult.lastStderr) {
2046
- parts.push(turnResult.lastStderr);
2047
- }
2048
- stopReason = parts.join(": ") || "Turn failed";
2049
- persist();
2050
- break;
2051
- }
2052
- const transport = turnResult.diagnostics?.transport;
2053
- if (transport && transport.streamToolUses > 0 && transport.preToolUseEvents === 0) {
2054
- status = "failed";
2055
- stopReason = `Hook transport broken: observed ${transport.streamToolUses} tool use(s) in Claude stream but received no PreToolUse events.`;
2056
- persist();
2057
- break;
2058
- }
2059
- if (!input.workflow?.loop?.enabled) {
2060
- status = "completed";
2061
- persist();
2062
- break;
1981
+ const loop = input.workflow?.loop;
1982
+ while (!cancelled) {
1983
+ iterations++;
1984
+ const prepared = prepareWorkflowTurn(workflowState, {
1985
+ prompt: input.prompt,
1986
+ iteration: iterations,
1987
+ configOverride: void 0
1988
+ });
1989
+ const turnResult = await input.startTurn({
1990
+ prompt: prepared.prompt,
1991
+ continuation: nextContinuation,
1992
+ configOverride: prepared.configOverride
1993
+ });
1994
+ if (cancelled) {
1995
+ status = "cancelled";
1996
+ persist();
1997
+ break;
1998
+ }
1999
+ cumulativeTokens = mergeTokens(cumulativeTokens, turnResult.tokens);
2000
+ if (turnResult.error || turnResult.exitCode !== null && turnResult.exitCode !== 0) {
2001
+ status = "failed";
2002
+ const parts = [];
2003
+ if (turnResult.error?.message) {
2004
+ parts.push(turnResult.error.message);
2005
+ } else if (turnResult.exitCode !== null) {
2006
+ parts.push(`Process exited with code ${turnResult.exitCode}`);
2063
2007
  }
2064
- const loopStop = shouldContinueWorkflowRun(workflowState);
2065
- if (loopStop) {
2066
- if (loopStop.reason === "completed") {
2067
- status = "completed";
2068
- } else if (loopStop.reason === "blocked") {
2069
- status = "blocked";
2070
- stopReason = loopStop.blockedReason;
2071
- } else if (loopStop.reason === "max_iterations") {
2072
- status = "exhausted";
2073
- } else if (loopStop.reason === "skeleton_not_replaced") {
2074
- status = "failed";
2075
- stopReason = "tracker skeleton was never replaced \u2014 Claude did not bootstrap the tracker";
2076
- } else if (loopStop.reason === "misplaced_terminal_marker") {
2077
- status = "failed";
2078
- stopReason = "terminal workflow marker is not the final non-empty line of the tracker; move all summary text above the marker";
2079
- } else {
2080
- status = "failed";
2081
- stopReason = `Loop stopped: ${loopStop.reason}`;
2082
- }
2083
- persist();
2084
- break;
2008
+ if (turnResult.lastStderr) {
2009
+ parts.push(turnResult.lastStderr);
2085
2010
  }
2011
+ stopReason = parts.join(": ") || "Turn failed";
2086
2012
  persist();
2087
- input.onIterationComplete?.(snapshot());
2088
- nextContinuation = { mode: "fresh" };
2013
+ break;
2089
2014
  }
2090
- if (cancelled && status === "running") {
2091
- status = "cancelled";
2015
+ const transport = turnResult.diagnostics?.transport;
2016
+ if (transport && transport.streamToolUses > 0 && transport.preToolUseEvents === 0) {
2017
+ status = "failed";
2018
+ stopReason = `Hook transport broken: observed ${transport.streamToolUses} tool use(s) in Claude stream but received no PreToolUse events.`;
2092
2019
  persist();
2020
+ break;
2093
2021
  }
2094
- } finally {
2095
- cleanupWorkflowRun(workflowState);
2022
+ if (!loop?.enabled) {
2023
+ status = "completed";
2024
+ persist();
2025
+ break;
2026
+ }
2027
+ if (trackerAbsPath) {
2028
+ const outcome = resolveTurnOutcome({
2029
+ trackerPath: trackerAbsPath,
2030
+ loop,
2031
+ iteration: iterations
2032
+ });
2033
+ if (outcome.kind === "stop") {
2034
+ status = outcome.status;
2035
+ stopReason = outcome.stopReason;
2036
+ persist();
2037
+ break;
2038
+ }
2039
+ }
2040
+ persist();
2041
+ input.onIterationComplete?.(snapshot());
2042
+ nextContinuation = { mode: "fresh" };
2043
+ }
2044
+ if (cancelled && status === "running") {
2045
+ status = "cancelled";
2046
+ persist();
2096
2047
  }
2097
2048
  return {
2098
2049
  runId,
@@ -2219,17 +2170,17 @@ function useWorkflowSessionController(base, input) {
2219
2170
  }
2220
2171
 
2221
2172
  // src/infra/plugins/mcpOptions.ts
2222
- import fs12 from "fs";
2173
+ import fs13 from "fs";
2223
2174
  import path10 from "path";
2224
2175
  function collectMcpServersWithOptions(pluginDirs) {
2225
2176
  const result = [];
2226
2177
  const seen = /* @__PURE__ */ new Set();
2227
2178
  for (const dir of pluginDirs) {
2228
2179
  const mcpPath = path10.join(dir, ".mcp.json");
2229
- if (!fs12.existsSync(mcpPath)) {
2180
+ if (!fs13.existsSync(mcpPath)) {
2230
2181
  continue;
2231
2182
  }
2232
- const config = JSON.parse(fs12.readFileSync(mcpPath, "utf-8"));
2183
+ const config = JSON.parse(fs13.readFileSync(mcpPath, "utf-8"));
2233
2184
  for (const [serverName, serverConfig] of Object.entries(
2234
2185
  config.mcpServers ?? {}
2235
2186
  )) {
@@ -2255,6 +2206,7 @@ export {
2255
2206
  gatherMarketplaceWorkflowSources,
2256
2207
  resolveWorkflowInstall,
2257
2208
  pullMarketplaceRepo,
2209
+ resolveMarketplacePlugin,
2258
2210
  projectConfigPath,
2259
2211
  readConfig,
2260
2212
  resolveActiveWorkflow,
@@ -2274,4 +2226,4 @@ export {
2274
2226
  compileWorkflowPlan,
2275
2227
  collectMcpServersWithOptions
2276
2228
  };
2277
- //# sourceMappingURL=chunk-JHSADKDJ.js.map
2229
+ //# sourceMappingURL=chunk-73V7GXV6.js.map
@@ -147,4 +147,4 @@ export {
147
147
  createPermissionRequestDenyResult,
148
148
  createStopBlockResult
149
149
  };
150
- //# sourceMappingURL=chunk-BTKQ67RE.js.map
150
+ //# sourceMappingURL=chunk-QYB6N2OT.js.map