@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.
- package/dist/{WorkflowInstallWizard-X754ND4V.js → WorkflowInstallWizard-7Y5PWAKW.js} +2 -2
- package/dist/athena-gateway.js +4130 -4
- package/dist/{chunk-A54HGVML.js → chunk-7E54JMXH.js} +126 -114
- package/dist/{chunk-PC46SGTK.js → chunk-ZU7M6YZW.js} +2054 -1110
- package/dist/cli.js +21 -380
- package/dist/dashboard-daemon.js +103 -8
- package/package.json +1 -1
- package/dist/chunk-2OJ3GGIP.js +0 -104
- package/dist/chunk-JV7CRNTC.js +0 -406
- package/dist/chunk-LKLNEXTK.js +0 -4124
- package/dist/supervisor.js +0 -692
|
@@ -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
|
-
`
|
|
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
|
|
1048
|
+
import fs9 from "fs";
|
|
1056
1049
|
import os5 from "os";
|
|
1057
|
-
import
|
|
1050
|
+
import path7 from "path";
|
|
1058
1051
|
|
|
1059
1052
|
// src/core/workflows/builtins/index.ts
|
|
1060
|
-
import
|
|
1053
|
+
import fs7 from "fs";
|
|
1061
1054
|
import os4 from "os";
|
|
1062
|
-
import
|
|
1055
|
+
import path5 from "path";
|
|
1063
1056
|
|
|
1064
1057
|
// src/core/workflows/loopManager.ts
|
|
1065
|
-
import
|
|
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
|
|
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 =
|
|
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 =
|
|
1216
|
-
if (!
|
|
1217
|
-
|
|
1218
|
-
|
|
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
|
|
1246
|
-
import
|
|
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 =
|
|
1246
|
+
const canonicalRepoDir = fs8.realpathSync(repoDir);
|
|
1254
1247
|
const listings = listMarketplaceWorkflowsFromRepo(canonicalRepoDir);
|
|
1255
|
-
const absolutePath =
|
|
1248
|
+
const absolutePath = fs8.realpathSync(legacyPath);
|
|
1256
1249
|
const match = listings.find(
|
|
1257
|
-
(l) =>
|
|
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 =
|
|
1273
|
-
if (!
|
|
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(
|
|
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
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
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
|
|
1321
|
+
return path7.join(os5.homedir(), ".config", "athena", "workflows");
|
|
1329
1322
|
}
|
|
1330
1323
|
function ensurePathWithinRoot(rootDir, targetPath, label) {
|
|
1331
|
-
const relative =
|
|
1332
|
-
if (relative === "" || !relative.startsWith("..") && !
|
|
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 =
|
|
1339
|
-
const workflowPath =
|
|
1340
|
-
if (!
|
|
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(
|
|
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" && !
|
|
1391
|
-
const workflowFilePath =
|
|
1392
|
-
if (!
|
|
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" && !
|
|
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 =
|
|
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 =
|
|
1415
|
-
const sourceDir =
|
|
1416
|
-
const absoluteDestDir =
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
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 ||
|
|
1425
|
-
const sourceAssetPath =
|
|
1417
|
+
if (!assetPath || path7.isAbsolute(assetPath)) return;
|
|
1418
|
+
const sourceAssetPath = path7.resolve(sourceDir, assetPath);
|
|
1426
1419
|
ensurePathWithinRoot(sourceDir, sourceAssetPath, "Workflow asset");
|
|
1427
|
-
if (!
|
|
1428
|
-
const destAssetPath =
|
|
1420
|
+
if (!fs9.existsSync(sourceAssetPath)) return;
|
|
1421
|
+
const destAssetPath = path7.resolve(absoluteDestDir, assetPath);
|
|
1429
1422
|
ensurePathWithinRoot(absoluteDestDir, destAssetPath, "Workflow asset");
|
|
1430
|
-
|
|
1431
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
1525
|
-
(entry) => entry.isDirectory() &&
|
|
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 =
|
|
1534
|
-
if (!
|
|
1520
|
+
const dir = path7.join(registryDir(), name);
|
|
1521
|
+
if (!fs9.existsSync(dir)) {
|
|
1535
1522
|
throw new Error(`Workflow "${name}" not found.`);
|
|
1536
1523
|
}
|
|
1537
|
-
|
|
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
|
|
1572
|
-
import
|
|
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 =
|
|
1602
|
+
const resolvedPath = path8.isAbsolute(workflow.workflowFile) ? workflow.workflowFile : path8.resolve(projectDir, workflow.workflowFile);
|
|
1616
1603
|
let workflowContent;
|
|
1617
1604
|
try {
|
|
1618
|
-
workflowContent =
|
|
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 =
|
|
1633
|
-
const composedPath =
|
|
1634
|
-
|
|
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 =
|
|
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 (!
|
|
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
|
|
1754
|
-
import
|
|
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
|
-
|
|
1798
|
+
fs11.mkdirSync(path9.dirname(trackerPath), { recursive: true });
|
|
1812
1799
|
try {
|
|
1813
|
-
|
|
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-
|
|
2111
|
+
//# sourceMappingURL=chunk-7E54JMXH.js.map
|