syntaur 0.17.0 → 0.17.2
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/.claude-plugin/plugin.json +2 -1
- package/dashboard/dist/assets/{_basePickBy-Dek5-ACb.js → _basePickBy-DiPrdxAJ.js} +1 -1
- package/dashboard/dist/assets/{_baseUniq-BeLddDKU.js → _baseUniq-CChFfAnf.js} +1 -1
- package/dashboard/dist/assets/{arc-kotdj4Uh.js → arc-CfkGktFw.js} +1 -1
- package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-BaqFXQ-q.js → architectureDiagram-2XIMDMQ5-hFcMu5eO.js} +1 -1
- package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-BSyGne6h.js → blockDiagram-WCTKOSBZ-GZDth9sZ.js} +1 -1
- package/dashboard/dist/assets/{c4Diagram-IC4MRINW-iYjZFKOW.js → c4Diagram-IC4MRINW-BVajqiYz.js} +1 -1
- package/dashboard/dist/assets/channel-C3CI1Xiu.js +1 -0
- package/dashboard/dist/assets/{chunk-4BX2VUAB-DvY9Y7jB.js → chunk-4BX2VUAB-L7ajwHyw.js} +1 -1
- package/dashboard/dist/assets/{chunk-55IACEB6-2vCDm2cJ.js → chunk-55IACEB6-BACmeqT4.js} +1 -1
- package/dashboard/dist/assets/{chunk-FMBD7UC4-BVPaoRtV.js → chunk-FMBD7UC4-BYQ2kuuH.js} +1 -1
- package/dashboard/dist/assets/{chunk-JSJVCQXG-B9uJpMzd.js → chunk-JSJVCQXG-akulVsXk.js} +1 -1
- package/dashboard/dist/assets/{chunk-KX2RTZJC-CHcjjGaq.js → chunk-KX2RTZJC-4ibFxLB_.js} +1 -1
- package/dashboard/dist/assets/{chunk-NQ4KR5QH-B8n19pSs.js → chunk-NQ4KR5QH-DU4iJf0Y.js} +1 -1
- package/dashboard/dist/assets/{chunk-QZHKN3VN-CF9ZYFGK.js → chunk-QZHKN3VN-ouyGBhQB.js} +1 -1
- package/dashboard/dist/assets/{chunk-WL4C6EOR-mY_1Vmdf.js → chunk-WL4C6EOR-Pq-ySwIY.js} +1 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-Ck2tXWRf.js +1 -0
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-Ck2tXWRf.js +1 -0
- package/dashboard/dist/assets/clone-BEaO4u3F.js +1 -0
- package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-DOYoG3gB.js → cose-bilkent-S5V4N54A-BfkMWgQb.js} +1 -1
- package/dashboard/dist/assets/{dagre-KLK3FWXG-CZFrp-Yq.js → dagre-KLK3FWXG-0yhY8ZsO.js} +1 -1
- package/dashboard/dist/assets/{diagram-E7M64L7V-F3rF5YwM.js → diagram-E7M64L7V-DBkanpFi.js} +1 -1
- package/dashboard/dist/assets/{diagram-IFDJBPK2-BynWv-Vo.js → diagram-IFDJBPK2-CrucCpxV.js} +1 -1
- package/dashboard/dist/assets/{diagram-P4PSJMXO-NIyzhm_M.js → diagram-P4PSJMXO-BJrQ6lK3.js} +1 -1
- package/dashboard/dist/assets/{erDiagram-INFDFZHY-Ca9NbihE.js → erDiagram-INFDFZHY--joDpxAm.js} +1 -1
- package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-YhS0MOu4.js → flowDiagram-PKNHOUZH-BUXiVwju.js} +1 -1
- package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-G7P3qq_2.js → ganttDiagram-A5KZAMGK-u2XuIMRA.js} +1 -1
- package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-BJAK7M7E.js → gitGraphDiagram-K3NZZRJ6-Ayf0-gwT.js} +1 -1
- package/dashboard/dist/assets/{graph-11geMy3f.js → graph-BUGVXq3c.js} +1 -1
- package/dashboard/dist/assets/index-Bv6vn4_x.js +535 -0
- package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-COcQYcLf.js → infoDiagram-LFFYTUFH-DYehx6g7.js} +1 -1
- package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-B7Jjyw8J.js → ishikawaDiagram-PHBUUO56-Bo8-BClf.js} +1 -1
- package/dashboard/dist/assets/{journeyDiagram-4ABVD52K--snU-QAX.js → journeyDiagram-4ABVD52K-DLm-bRYd.js} +1 -1
- package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-BucLoc89.js → kanban-definition-K7BYSVSG-stPdEvds.js} +1 -1
- package/dashboard/dist/assets/{layout-Bcgxdz8A.js → layout-B7aPbDvN.js} +1 -1
- package/dashboard/dist/assets/{linear-CRF8xwvk.js → linear-CtQ_RMAp.js} +1 -1
- package/dashboard/dist/assets/{mermaid.core-D5JD5uYg.js → mermaid.core-CcJoQ5At.js} +4 -4
- package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-BqGcTcbk.js → mindmap-definition-YRQLILUH-DahXO6Ui.js} +1 -1
- package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-CnQ2EkyX.js → pieDiagram-SKSYHLDU-DXv973_0.js} +1 -1
- package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-CKCfmTRc.js → quadrantDiagram-337W2JSQ-5U0ryvjr.js} +1 -1
- package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-B9J4bvUK.js → requirementDiagram-Z7DCOOCP-CvGflSyl.js} +1 -1
- package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-CMSzI5rg.js → sankeyDiagram-WA2Y5GQK-AeetdpCi.js} +1 -1
- package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-BFQDtxd0.js → sequenceDiagram-2WXFIKYE-BhRR4qzn.js} +1 -1
- package/dashboard/dist/assets/{stateDiagram-RAJIS63D-DgWYKT02.js → stateDiagram-RAJIS63D-CxxF58nH.js} +1 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-DHCNHbs7.js +1 -0
- package/dashboard/dist/assets/{timeline-definition-YZTLITO2-jANu0e7M.js → timeline-definition-YZTLITO2-BEQIeygp.js} +1 -1
- package/dashboard/dist/assets/{treemap-KZPCXAKY-BFjWMkyM.js → treemap-KZPCXAKY-BCWNTFfj.js} +1 -1
- package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-DGwrAJYj.js → vennDiagram-LZ73GAT5-BcuC_UIz.js} +1 -1
- package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-BLCPfoFM.js → xychartDiagram-JWTSCODW-CWGez3_f.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/dist/dashboard/server.js +379 -217
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.js +512 -349
- package/dist/index.js.map +1 -1
- package/dist/launch/index.js +20 -1
- package/dist/launch/index.js.map +1 -1
- package/package.json +1 -1
- package/platforms/claude-code/.claude-plugin/plugin.json +2 -1
- package/platforms/claude-code/commands/run-playbook/run-playbook.md +16 -0
- package/platforms/claude-code/skills/run-playbook/SKILL.md +87 -0
- package/platforms/codex/skills/run-playbook/SKILL.md +87 -0
- package/skills/run-playbook/SKILL.md +87 -0
- package/dashboard/dist/assets/channel-eXdj7ayV.js +0 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-B-bBQACr.js +0 -1
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-B-bBQACr.js +0 -1
- package/dashboard/dist/assets/clone-CL04pv9J.js +0 -1
- package/dashboard/dist/assets/index--qwybK3W.js +0 -535
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-Nmwft4cR.js +0 -1
package/dist/index.js
CHANGED
|
@@ -5042,8 +5042,8 @@ async function migrateFromMarkdown(projectsDir2) {
|
|
|
5042
5042
|
return allSessions.length;
|
|
5043
5043
|
}
|
|
5044
5044
|
async function parseMarkdownSessionsIndex(filePath, projectSlug) {
|
|
5045
|
-
const { readFile:
|
|
5046
|
-
const raw = await
|
|
5045
|
+
const { readFile: readFile53 } = await import("fs/promises");
|
|
5046
|
+
const raw = await readFile53(filePath, "utf-8");
|
|
5047
5047
|
const sessions = [];
|
|
5048
5048
|
const lines = raw.split("\n");
|
|
5049
5049
|
let inTable = false;
|
|
@@ -5441,13 +5441,16 @@ __export(scanner_exports, {
|
|
|
5441
5441
|
autoLinkPane: () => autoLinkPane,
|
|
5442
5442
|
checkTmuxAvailable: () => checkTmuxAvailable,
|
|
5443
5443
|
clearScanCache: () => clearScanCache,
|
|
5444
|
+
descendantPidsFromSnapshot: () => descendantPidsFromSnapshot,
|
|
5444
5445
|
execQuiet: () => execQuiet,
|
|
5445
5446
|
findListeningPorts: () => findListeningPorts,
|
|
5446
5447
|
getDescendantPids: () => getDescendantPids,
|
|
5447
5448
|
getGitInfo: () => getGitInfo,
|
|
5448
5449
|
getLsofOutput: () => getLsofOutput,
|
|
5450
|
+
getProcessSnapshot: () => getProcessSnapshot,
|
|
5449
5451
|
listTmuxPanes: () => listTmuxPanes,
|
|
5450
5452
|
loadWorkspaceRecords: () => loadWorkspaceRecords,
|
|
5453
|
+
parseProcessSnapshot: () => parseProcessSnapshot,
|
|
5451
5454
|
parseTmuxPaneOutput: () => parseTmuxPaneOutput,
|
|
5452
5455
|
resolveAndNormalize: () => resolveAndNormalize,
|
|
5453
5456
|
scanAllSessions: () => scanAllSessions,
|
|
@@ -5457,9 +5460,27 @@ __export(scanner_exports, {
|
|
|
5457
5460
|
import { execFile } from "child_process";
|
|
5458
5461
|
import { promisify } from "util";
|
|
5459
5462
|
import { resolve as resolve15 } from "path";
|
|
5460
|
-
import { realpath
|
|
5463
|
+
import { realpath } from "fs/promises";
|
|
5461
5464
|
function clearScanCache() {
|
|
5462
5465
|
cache = null;
|
|
5466
|
+
forceFreshNext = true;
|
|
5467
|
+
scanEpoch++;
|
|
5468
|
+
inFlight = null;
|
|
5469
|
+
tmuxAvailableCache = null;
|
|
5470
|
+
}
|
|
5471
|
+
function emptyScan() {
|
|
5472
|
+
return { sessions: [], tmuxAvailable: false };
|
|
5473
|
+
}
|
|
5474
|
+
function scanKey(serversDir2, projectsDir2, assignmentsDir2) {
|
|
5475
|
+
return `${serversDir2}\0${projectsDir2}\0${assignmentsDir2 ?? ""}`;
|
|
5476
|
+
}
|
|
5477
|
+
function delay(ms) {
|
|
5478
|
+
return new Promise((resolve78) => {
|
|
5479
|
+
const timer2 = setTimeout(resolve78, ms);
|
|
5480
|
+
if (typeof timer2.unref === "function") {
|
|
5481
|
+
timer2.unref();
|
|
5482
|
+
}
|
|
5483
|
+
});
|
|
5463
5484
|
}
|
|
5464
5485
|
function parseTmuxPaneOutput(output2) {
|
|
5465
5486
|
return output2.trim().split("\n").filter((line) => line.length > 0).map((line) => {
|
|
@@ -5493,15 +5514,20 @@ function findListeningPorts(lsofOutput, pids) {
|
|
|
5493
5514
|
}
|
|
5494
5515
|
async function execQuiet(cmd, args) {
|
|
5495
5516
|
try {
|
|
5496
|
-
const { stdout } = await exec(cmd, args
|
|
5517
|
+
const { stdout } = await exec(cmd, args, {
|
|
5518
|
+
timeout: PROBE_TIMEOUT_MS,
|
|
5519
|
+
maxBuffer: PROBE_MAX_BUFFER
|
|
5520
|
+
});
|
|
5497
5521
|
return stdout.trim();
|
|
5498
5522
|
} catch {
|
|
5499
5523
|
return "";
|
|
5500
5524
|
}
|
|
5501
5525
|
}
|
|
5502
5526
|
async function checkTmuxAvailable() {
|
|
5503
|
-
|
|
5504
|
-
|
|
5527
|
+
if (!tmuxAvailableCache) {
|
|
5528
|
+
tmuxAvailableCache = execQuiet("which", ["tmux"]).then((result) => result.length > 0);
|
|
5529
|
+
}
|
|
5530
|
+
return tmuxAvailableCache;
|
|
5505
5531
|
}
|
|
5506
5532
|
async function sessionAlive(name) {
|
|
5507
5533
|
try {
|
|
@@ -5541,6 +5567,40 @@ async function getDescendantPids(rootPid, maxDepth = 4) {
|
|
|
5541
5567
|
}
|
|
5542
5568
|
return all;
|
|
5543
5569
|
}
|
|
5570
|
+
function parseProcessSnapshot(psOutput) {
|
|
5571
|
+
const childrenOf = /* @__PURE__ */ new Map();
|
|
5572
|
+
for (const line of psOutput.split("\n")) {
|
|
5573
|
+
const parts = line.trim().split(/\s+/);
|
|
5574
|
+
if (parts.length < 2) continue;
|
|
5575
|
+
const pid = parseInt(parts[0], 10);
|
|
5576
|
+
const ppid = parseInt(parts[1], 10);
|
|
5577
|
+
if (isNaN(pid) || isNaN(ppid)) continue;
|
|
5578
|
+
const siblings = childrenOf.get(ppid);
|
|
5579
|
+
if (siblings) siblings.push(pid);
|
|
5580
|
+
else childrenOf.set(ppid, [pid]);
|
|
5581
|
+
}
|
|
5582
|
+
return { childrenOf };
|
|
5583
|
+
}
|
|
5584
|
+
async function getProcessSnapshot() {
|
|
5585
|
+
return parseProcessSnapshot(await execQuiet("ps", ["-axo", "pid=,ppid="]));
|
|
5586
|
+
}
|
|
5587
|
+
function descendantPidsFromSnapshot(rootPid, snapshot, maxDepth = 4) {
|
|
5588
|
+
const all = /* @__PURE__ */ new Set([rootPid]);
|
|
5589
|
+
let frontier = [rootPid];
|
|
5590
|
+
for (let depth = 0; depth < maxDepth && frontier.length > 0; depth++) {
|
|
5591
|
+
const nextFrontier = [];
|
|
5592
|
+
for (const pid of frontier) {
|
|
5593
|
+
for (const child of snapshot.childrenOf.get(pid) ?? []) {
|
|
5594
|
+
if (!all.has(child)) {
|
|
5595
|
+
all.add(child);
|
|
5596
|
+
nextFrontier.push(child);
|
|
5597
|
+
}
|
|
5598
|
+
}
|
|
5599
|
+
}
|
|
5600
|
+
frontier = nextFrontier;
|
|
5601
|
+
}
|
|
5602
|
+
return all;
|
|
5603
|
+
}
|
|
5544
5604
|
async function getLsofOutput() {
|
|
5545
5605
|
return execQuiet("lsof", ["-i", "-P", "-n", "-sTCP:LISTEN"]);
|
|
5546
5606
|
}
|
|
@@ -5562,62 +5622,11 @@ async function getGitInfo(cwd) {
|
|
|
5562
5622
|
return { branch: branch || null, worktree: isWorktree };
|
|
5563
5623
|
}
|
|
5564
5624
|
async function loadWorkspaceRecords(projectsDir2, assignmentsDir2) {
|
|
5565
|
-
const records = [];
|
|
5566
5625
|
try {
|
|
5567
|
-
|
|
5568
|
-
for (const project of projects) {
|
|
5569
|
-
const projectAssignmentsDir = resolve15(projectsDir2, project.slug, "assignments");
|
|
5570
|
-
let slugs;
|
|
5571
|
-
try {
|
|
5572
|
-
slugs = await readdir8(projectAssignmentsDir);
|
|
5573
|
-
} catch {
|
|
5574
|
-
continue;
|
|
5575
|
-
}
|
|
5576
|
-
for (const aslug of slugs) {
|
|
5577
|
-
const aFile = resolve15(projectAssignmentsDir, aslug, "assignment.md");
|
|
5578
|
-
try {
|
|
5579
|
-
const raw = await readFile10(aFile, "utf-8");
|
|
5580
|
-
const [fm] = extractFrontmatter(raw);
|
|
5581
|
-
if (!fm) continue;
|
|
5582
|
-
records.push({
|
|
5583
|
-
projectSlug: project.slug,
|
|
5584
|
-
assignmentSlug: aslug,
|
|
5585
|
-
assignmentTitle: getField(fm, "title") ?? aslug,
|
|
5586
|
-
worktreePath: getNestedField(fm, "workspace", "worktreePath") ?? null,
|
|
5587
|
-
branch: getNestedField(fm, "workspace", "branch") ?? null
|
|
5588
|
-
});
|
|
5589
|
-
} catch {
|
|
5590
|
-
continue;
|
|
5591
|
-
}
|
|
5592
|
-
}
|
|
5593
|
-
}
|
|
5626
|
+
return await listWorkspaceRecords(projectsDir2, assignmentsDir2);
|
|
5594
5627
|
} catch {
|
|
5628
|
+
return [];
|
|
5595
5629
|
}
|
|
5596
|
-
if (assignmentsDir2) {
|
|
5597
|
-
try {
|
|
5598
|
-
const entries = await readdir8(assignmentsDir2);
|
|
5599
|
-
for (const id of entries) {
|
|
5600
|
-
if (id.startsWith(".") || id.startsWith("_")) continue;
|
|
5601
|
-
const aFile = resolve15(assignmentsDir2, id, "assignment.md");
|
|
5602
|
-
try {
|
|
5603
|
-
const raw = await readFile10(aFile, "utf-8");
|
|
5604
|
-
const [fm] = extractFrontmatter(raw);
|
|
5605
|
-
if (!fm) continue;
|
|
5606
|
-
records.push({
|
|
5607
|
-
projectSlug: null,
|
|
5608
|
-
assignmentSlug: id,
|
|
5609
|
-
assignmentTitle: getField(fm, "title") ?? id,
|
|
5610
|
-
worktreePath: getNestedField(fm, "workspace", "worktreePath") ?? null,
|
|
5611
|
-
branch: getNestedField(fm, "workspace", "branch") ?? null
|
|
5612
|
-
});
|
|
5613
|
-
} catch {
|
|
5614
|
-
continue;
|
|
5615
|
-
}
|
|
5616
|
-
}
|
|
5617
|
-
} catch {
|
|
5618
|
-
}
|
|
5619
|
-
}
|
|
5620
|
-
return records;
|
|
5621
5630
|
}
|
|
5622
5631
|
async function resolveAndNormalize(p) {
|
|
5623
5632
|
try {
|
|
@@ -5646,7 +5655,7 @@ async function autoLinkPane(cwd, branch, records) {
|
|
|
5646
5655
|
}
|
|
5647
5656
|
return null;
|
|
5648
5657
|
}
|
|
5649
|
-
async function scanSession(sessionData, lsofOutput, workspaceRecords) {
|
|
5658
|
+
async function scanSession(sessionData, lsofOutput, workspaceRecords, procSnapshot) {
|
|
5650
5659
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
5651
5660
|
const alive = await sessionAlive(sessionData.session);
|
|
5652
5661
|
if (!alive) {
|
|
@@ -5669,13 +5678,13 @@ async function scanSession(sessionData, lsofOutput, workspaceRecords) {
|
|
|
5669
5678
|
windowMap.get(rp.windowIndex).panes.push(rp);
|
|
5670
5679
|
}
|
|
5671
5680
|
const cwdSet = new Set(rawPanes.map((p) => p.cwd));
|
|
5672
|
-
const
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5681
|
+
const gitInfoEntries = await Promise.all(
|
|
5682
|
+
[...cwdSet].map(async (cwd) => [cwd, await getGitInfo(cwd)])
|
|
5683
|
+
);
|
|
5684
|
+
const gitInfoCache = new Map(gitInfoEntries);
|
|
5676
5685
|
const pidToPaneKey = /* @__PURE__ */ new Map();
|
|
5677
5686
|
for (const rp of rawPanes) {
|
|
5678
|
-
const descendants =
|
|
5687
|
+
const descendants = descendantPidsFromSnapshot(rp.pid, procSnapshot);
|
|
5679
5688
|
const key = `${rp.windowIndex}:${rp.paneIndex}`;
|
|
5680
5689
|
for (const pid of descendants) {
|
|
5681
5690
|
pidToPaneKey.set(pid, key);
|
|
@@ -5700,37 +5709,38 @@ async function scanSession(sessionData, lsofOutput, workspaceRecords) {
|
|
|
5700
5709
|
}
|
|
5701
5710
|
const windows = [];
|
|
5702
5711
|
for (const [windowIndex, { name, panes: rawPanesInWindow }] of windowMap) {
|
|
5703
|
-
const panes =
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5712
|
+
const panes = await Promise.all(
|
|
5713
|
+
rawPanesInWindow.map(async (rp) => {
|
|
5714
|
+
const key = `${rp.windowIndex}:${rp.paneIndex}`;
|
|
5715
|
+
const gitInfo = gitInfoCache.get(rp.cwd) ?? { branch: null, worktree: false };
|
|
5716
|
+
const ports = panePorts.get(key) ?? [];
|
|
5717
|
+
const urls = ports.map((p) => `http://localhost:${p}`);
|
|
5718
|
+
const override = sessionData.overrides[key];
|
|
5719
|
+
let assignment = null;
|
|
5720
|
+
if (override) {
|
|
5721
|
+
const rec = workspaceRecords.find(
|
|
5722
|
+
(r) => r.projectSlug === override.project && r.assignmentSlug === override.assignment
|
|
5723
|
+
);
|
|
5724
|
+
assignment = {
|
|
5725
|
+
project: override.project,
|
|
5726
|
+
slug: override.assignment,
|
|
5727
|
+
title: rec?.assignmentTitle ?? override.assignment
|
|
5728
|
+
};
|
|
5729
|
+
} else {
|
|
5730
|
+
assignment = await autoLinkPane(rp.cwd, gitInfo.branch, workspaceRecords);
|
|
5731
|
+
}
|
|
5732
|
+
return {
|
|
5733
|
+
index: rp.paneIndex,
|
|
5734
|
+
command: rp.command,
|
|
5735
|
+
cwd: rp.cwd,
|
|
5736
|
+
branch: gitInfo.branch,
|
|
5737
|
+
worktree: gitInfo.worktree,
|
|
5738
|
+
ports,
|
|
5739
|
+
urls,
|
|
5740
|
+
assignment
|
|
5719
5741
|
};
|
|
5720
|
-
}
|
|
5721
|
-
|
|
5722
|
-
}
|
|
5723
|
-
panes.push({
|
|
5724
|
-
index: rp.paneIndex,
|
|
5725
|
-
command: rp.command,
|
|
5726
|
-
cwd: rp.cwd,
|
|
5727
|
-
branch: gitInfo.branch,
|
|
5728
|
-
worktree: gitInfo.worktree,
|
|
5729
|
-
ports,
|
|
5730
|
-
urls,
|
|
5731
|
-
assignment
|
|
5732
|
-
});
|
|
5733
|
-
}
|
|
5742
|
+
})
|
|
5743
|
+
);
|
|
5734
5744
|
windows.push({ index: windowIndex, name, panes });
|
|
5735
5745
|
}
|
|
5736
5746
|
windows.sort((a, b) => a.index - b.index);
|
|
@@ -5802,53 +5812,114 @@ async function scanProcessSession(sessionData, lsofOutput, workspaceRecords) {
|
|
|
5802
5812
|
windows: [{ index: 0, name: "process", panes: [pane] }]
|
|
5803
5813
|
};
|
|
5804
5814
|
}
|
|
5815
|
+
async function performScan(serversDir2, projectsDir2, options) {
|
|
5816
|
+
const epoch = scanEpoch;
|
|
5817
|
+
const [tmuxAvailable, names, lsofOutput, workspaceRecords, procSnapshot] = await Promise.all([
|
|
5818
|
+
checkTmuxAvailable(),
|
|
5819
|
+
listSessionFiles(serversDir2),
|
|
5820
|
+
getLsofOutput(),
|
|
5821
|
+
loadWorkspaceRecords(projectsDir2, options?.assignmentsDir),
|
|
5822
|
+
getProcessSnapshot()
|
|
5823
|
+
]);
|
|
5824
|
+
const datas = await Promise.all(names.map((name) => readSessionFile(serversDir2, name)));
|
|
5825
|
+
const scanned = await Promise.all(
|
|
5826
|
+
datas.map(async (data) => {
|
|
5827
|
+
if (!data) return null;
|
|
5828
|
+
if (data.kind === "process") {
|
|
5829
|
+
return scanProcessSession(data, lsofOutput, workspaceRecords);
|
|
5830
|
+
}
|
|
5831
|
+
if (tmuxAvailable) {
|
|
5832
|
+
return scanSession(data, lsofOutput, workspaceRecords, procSnapshot);
|
|
5833
|
+
}
|
|
5834
|
+
return null;
|
|
5835
|
+
})
|
|
5836
|
+
);
|
|
5837
|
+
const result = {
|
|
5838
|
+
sessions: scanned.filter((s) => s !== null),
|
|
5839
|
+
tmuxAvailable
|
|
5840
|
+
};
|
|
5841
|
+
if (epoch === scanEpoch) {
|
|
5842
|
+
cache = { data: result, expiry: Date.now() + CACHE_TTL_MS };
|
|
5843
|
+
lastKnown = result;
|
|
5844
|
+
forceFreshNext = false;
|
|
5845
|
+
}
|
|
5846
|
+
return result;
|
|
5847
|
+
}
|
|
5848
|
+
function refreshInBackground(serversDir2, projectsDir2, options) {
|
|
5849
|
+
if (!inFlight) {
|
|
5850
|
+
const p = performScan(serversDir2, projectsDir2, options);
|
|
5851
|
+
p.finally(() => {
|
|
5852
|
+
if (inFlight === p) inFlight = null;
|
|
5853
|
+
}).catch(() => {
|
|
5854
|
+
});
|
|
5855
|
+
inFlight = p;
|
|
5856
|
+
}
|
|
5857
|
+
return inFlight;
|
|
5858
|
+
}
|
|
5805
5859
|
async function scanAllSessions(serversDir2, projectsDir2, options) {
|
|
5860
|
+
const key = scanKey(serversDir2, projectsDir2, options?.assignmentsDir);
|
|
5861
|
+
if (key !== cacheKey) {
|
|
5862
|
+
cache = null;
|
|
5863
|
+
lastKnown = null;
|
|
5864
|
+
inFlight = null;
|
|
5865
|
+
forceFreshNext = false;
|
|
5866
|
+
scanEpoch++;
|
|
5867
|
+
cacheKey = key;
|
|
5868
|
+
}
|
|
5806
5869
|
if (!options?.bypassCache && cache && Date.now() < cache.expiry) {
|
|
5807
5870
|
return cache.data;
|
|
5808
5871
|
}
|
|
5809
|
-
const
|
|
5810
|
-
const
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
const data = await readSessionFile(serversDir2, name);
|
|
5816
|
-
if (!data) continue;
|
|
5817
|
-
if (data.kind === "process") {
|
|
5818
|
-
sessions.push(await scanProcessSession(data, lsofOutput, workspaceRecords));
|
|
5819
|
-
} else if (tmuxAvailable) {
|
|
5820
|
-
sessions.push(await scanSession(data, lsofOutput, workspaceRecords));
|
|
5821
|
-
}
|
|
5872
|
+
const refresh = refreshInBackground(serversDir2, projectsDir2, options);
|
|
5873
|
+
const canServeStale = lastKnown !== null && !options?.bypassCache && !forceFreshNext;
|
|
5874
|
+
if (canServeStale) {
|
|
5875
|
+
void refresh.catch(() => {
|
|
5876
|
+
});
|
|
5877
|
+
return lastKnown;
|
|
5822
5878
|
}
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5879
|
+
if (options?.nonBlocking) {
|
|
5880
|
+
return Promise.race([
|
|
5881
|
+
refresh.catch(() => emptyScan()),
|
|
5882
|
+
delay(COLD_WAIT_BUDGET_MS).then(() => emptyScan())
|
|
5883
|
+
]);
|
|
5884
|
+
}
|
|
5885
|
+
return refresh;
|
|
5826
5886
|
}
|
|
5827
5887
|
async function scanSingleSession(serversDir2, projectsDir2, name, options) {
|
|
5828
5888
|
const data = await readSessionFile(serversDir2, name);
|
|
5829
5889
|
if (!data) return null;
|
|
5830
|
-
const lsofOutput = await
|
|
5831
|
-
|
|
5890
|
+
const [lsofOutput, workspaceRecords, procSnapshot] = await Promise.all([
|
|
5891
|
+
getLsofOutput(),
|
|
5892
|
+
loadWorkspaceRecords(projectsDir2, options?.assignmentsDir),
|
|
5893
|
+
getProcessSnapshot()
|
|
5894
|
+
]);
|
|
5832
5895
|
if (data.kind === "process") {
|
|
5833
5896
|
return scanProcessSession(data, lsofOutput, workspaceRecords);
|
|
5834
5897
|
}
|
|
5835
|
-
return scanSession(data, lsofOutput, workspaceRecords);
|
|
5898
|
+
return scanSession(data, lsofOutput, workspaceRecords, procSnapshot);
|
|
5836
5899
|
}
|
|
5837
|
-
var exec, cache, CACHE_TTL_MS;
|
|
5900
|
+
var exec, cache, lastKnown, inFlight, forceFreshNext, scanEpoch, cacheKey, CACHE_TTL_MS, COLD_WAIT_BUDGET_MS, PROBE_TIMEOUT_MS, PROBE_MAX_BUFFER, tmuxAvailableCache;
|
|
5838
5901
|
var init_scanner = __esm({
|
|
5839
5902
|
"src/dashboard/scanner.ts"() {
|
|
5840
5903
|
"use strict";
|
|
5841
5904
|
init_api();
|
|
5842
5905
|
init_servers();
|
|
5843
|
-
init_parser();
|
|
5844
5906
|
exec = promisify(execFile);
|
|
5845
5907
|
cache = null;
|
|
5908
|
+
lastKnown = null;
|
|
5909
|
+
inFlight = null;
|
|
5910
|
+
forceFreshNext = false;
|
|
5911
|
+
scanEpoch = 0;
|
|
5912
|
+
cacheKey = null;
|
|
5846
5913
|
CACHE_TTL_MS = 1e4;
|
|
5914
|
+
COLD_WAIT_BUDGET_MS = 2500;
|
|
5915
|
+
PROBE_TIMEOUT_MS = 15e3;
|
|
5916
|
+
PROBE_MAX_BUFFER = 32 * 1024 * 1024;
|
|
5917
|
+
tmuxAvailableCache = null;
|
|
5847
5918
|
}
|
|
5848
5919
|
});
|
|
5849
5920
|
|
|
5850
5921
|
// src/dashboard/api.ts
|
|
5851
|
-
import { readdir as
|
|
5922
|
+
import { readdir as readdir8, readFile as readFile10, writeFile as writeFile3 } from "fs/promises";
|
|
5852
5923
|
import { resolve as resolve16, dirname as dirname3, basename } from "path";
|
|
5853
5924
|
function clearFrontmatterField(content, key) {
|
|
5854
5925
|
const fieldRegex = new RegExp(`^(${escapeRegExp2(key)}:)\\s*.*$`, "m");
|
|
@@ -5888,10 +5959,41 @@ function emitTrace(traces, meta) {
|
|
|
5888
5959
|
JSON.stringify({ kind: "overview-trace", totalMs, phases: traces.entries, subPhases, ...meta })
|
|
5889
5960
|
);
|
|
5890
5961
|
}
|
|
5962
|
+
function invalidateRecordsCache() {
|
|
5963
|
+
projectRecordsCache.clear();
|
|
5964
|
+
standaloneRecordsCache.clear();
|
|
5965
|
+
}
|
|
5966
|
+
function installRecordsInvalidation(router) {
|
|
5967
|
+
for (const method of ["post", "put", "patch", "delete"]) {
|
|
5968
|
+
const original = router[method].bind(router);
|
|
5969
|
+
router[method] = (path, ...handlers) => {
|
|
5970
|
+
if (handlers.length > 0) {
|
|
5971
|
+
const last = handlers[handlers.length - 1];
|
|
5972
|
+
handlers[handlers.length - 1] = async (req, res, next) => {
|
|
5973
|
+
try {
|
|
5974
|
+
return await last(req, res, next);
|
|
5975
|
+
} finally {
|
|
5976
|
+
invalidateRecordsCache();
|
|
5977
|
+
}
|
|
5978
|
+
};
|
|
5979
|
+
}
|
|
5980
|
+
return original(path, ...handlers);
|
|
5981
|
+
};
|
|
5982
|
+
}
|
|
5983
|
+
}
|
|
5891
5984
|
async function listStandaloneRecords(assignmentsDir2) {
|
|
5985
|
+
const key = assignmentsDir2 ?? "";
|
|
5986
|
+
const cached = standaloneRecordsCache.get(key);
|
|
5987
|
+
if (cached) return cached;
|
|
5988
|
+
const promise = computeStandaloneRecords(assignmentsDir2);
|
|
5989
|
+
standaloneRecordsCache.set(key, promise);
|
|
5990
|
+
promise.catch(() => standaloneRecordsCache.delete(key));
|
|
5991
|
+
return promise;
|
|
5992
|
+
}
|
|
5993
|
+
async function computeStandaloneRecords(assignmentsDir2) {
|
|
5892
5994
|
if (!assignmentsDir2) return [];
|
|
5893
5995
|
if (!await fileExists(assignmentsDir2)) return [];
|
|
5894
|
-
const entries = await
|
|
5996
|
+
const entries = await readdir8(assignmentsDir2, { withFileTypes: true });
|
|
5895
5997
|
const records = [];
|
|
5896
5998
|
for (const entry of entries) {
|
|
5897
5999
|
if (!entry.isDirectory() || entry.name.startsWith(".") || entry.name.startsWith("_")) continue;
|
|
@@ -5899,7 +6001,7 @@ async function listStandaloneRecords(assignmentsDir2) {
|
|
|
5899
6001
|
const assignmentMdPath2 = resolve16(assignmentDir, "assignment.md");
|
|
5900
6002
|
if (!await fileExists(assignmentMdPath2)) continue;
|
|
5901
6003
|
try {
|
|
5902
|
-
const content = await
|
|
6004
|
+
const content = await readFile10(assignmentMdPath2, "utf-8");
|
|
5903
6005
|
const record = parseAssignmentFull(content);
|
|
5904
6006
|
records.push({ assignmentDir, id: entry.name, record });
|
|
5905
6007
|
} catch {
|
|
@@ -5975,7 +6077,7 @@ async function listProjects(projectsDir2) {
|
|
|
5975
6077
|
async function readWorkspaceRegistry(projectsDir2) {
|
|
5976
6078
|
const registryPath = resolve16(dirname3(projectsDir2), "workspaces.json");
|
|
5977
6079
|
try {
|
|
5978
|
-
const raw = await
|
|
6080
|
+
const raw = await readFile10(registryPath, "utf-8");
|
|
5979
6081
|
const parsed = JSON.parse(raw);
|
|
5980
6082
|
return Array.isArray(parsed) ? parsed.filter((w) => typeof w === "string") : [];
|
|
5981
6083
|
} catch {
|
|
@@ -6011,6 +6113,34 @@ async function listWorkspaces(projectsDir2, assignmentsDir2) {
|
|
|
6011
6113
|
const workspaces = Array.from(workspaceSet).sort();
|
|
6012
6114
|
return { workspaces, hasUngrouped };
|
|
6013
6115
|
}
|
|
6116
|
+
async function listWorkspaceRecords(projectsDir2, assignmentsDir2) {
|
|
6117
|
+
const [projectRecords, standaloneRecords] = await Promise.all([
|
|
6118
|
+
listProjectRecords(projectsDir2),
|
|
6119
|
+
listStandaloneRecords(assignmentsDir2)
|
|
6120
|
+
]);
|
|
6121
|
+
const records = [];
|
|
6122
|
+
for (const project of projectRecords) {
|
|
6123
|
+
for (const assignment of project.assignments) {
|
|
6124
|
+
records.push({
|
|
6125
|
+
projectSlug: project.summary.slug,
|
|
6126
|
+
assignmentSlug: assignment.slug,
|
|
6127
|
+
assignmentTitle: assignment.title || assignment.slug,
|
|
6128
|
+
worktreePath: assignment.workspace.worktreePath ?? null,
|
|
6129
|
+
branch: assignment.workspace.branch ?? null
|
|
6130
|
+
});
|
|
6131
|
+
}
|
|
6132
|
+
}
|
|
6133
|
+
for (const standalone of standaloneRecords) {
|
|
6134
|
+
records.push({
|
|
6135
|
+
projectSlug: null,
|
|
6136
|
+
assignmentSlug: standalone.id,
|
|
6137
|
+
assignmentTitle: standalone.record.title || standalone.id,
|
|
6138
|
+
worktreePath: standalone.record.workspace.worktreePath ?? null,
|
|
6139
|
+
branch: standalone.record.workspace.branch ?? null
|
|
6140
|
+
});
|
|
6141
|
+
}
|
|
6142
|
+
return records;
|
|
6143
|
+
}
|
|
6014
6144
|
async function createWorkspace(projectsDir2, name) {
|
|
6015
6145
|
const registered = await readWorkspaceRegistry(projectsDir2);
|
|
6016
6146
|
if (!registered.includes(name)) {
|
|
@@ -6036,7 +6166,7 @@ async function deleteWorkspace(projectsDir2, name, opts = {}) {
|
|
|
6036
6166
|
const timestamp = nowTimestamp();
|
|
6037
6167
|
for (const slug of projectsReferencing) {
|
|
6038
6168
|
const path = resolve16(projectsDir2, slug, "project.md");
|
|
6039
|
-
const raw = await
|
|
6169
|
+
const raw = await readFile10(path, "utf-8");
|
|
6040
6170
|
let next = clearFrontmatterField(raw, "workspace");
|
|
6041
6171
|
next = setUpdatedField(next, timestamp);
|
|
6042
6172
|
await writeFileForce(path, next);
|
|
@@ -6045,7 +6175,7 @@ async function deleteWorkspace(projectsDir2, name, opts = {}) {
|
|
|
6045
6175
|
for (const id of standalonesReferencing) {
|
|
6046
6176
|
if (!opts.assignmentsDir) break;
|
|
6047
6177
|
const path = resolve16(opts.assignmentsDir, id, "assignment.md");
|
|
6048
|
-
const raw = await
|
|
6178
|
+
const raw = await readFile10(path, "utf-8");
|
|
6049
6179
|
let next = clearFrontmatterField(raw, "workspaceGroup");
|
|
6050
6180
|
next = setUpdatedField(next, timestamp);
|
|
6051
6181
|
await writeFileForce(path, next);
|
|
@@ -6055,6 +6185,9 @@ async function deleteWorkspace(projectsDir2, name, opts = {}) {
|
|
|
6055
6185
|
const registered = await readWorkspaceRegistry(projectsDir2);
|
|
6056
6186
|
const filtered = registered.filter((w) => w !== name);
|
|
6057
6187
|
await writeWorkspaceRegistry(projectsDir2, filtered);
|
|
6188
|
+
if (rewroteFiles) {
|
|
6189
|
+
invalidateRecordsCache();
|
|
6190
|
+
}
|
|
6058
6191
|
return { rewroteFiles };
|
|
6059
6192
|
}
|
|
6060
6193
|
async function getOverview(projectsDir2, serversDir2, assignmentsDir2, options = {}) {
|
|
@@ -6098,7 +6231,12 @@ async function getOverview(projectsDir2, serversDir2, assignmentsDir2, options =
|
|
|
6098
6231
|
const servers = await timed(
|
|
6099
6232
|
traces,
|
|
6100
6233
|
"scan-tmux-sessions",
|
|
6101
|
-
() =>
|
|
6234
|
+
() => (
|
|
6235
|
+
// Overview only needs aggregate counts — never block its render on a
|
|
6236
|
+
// live scan; serve last-known stats and let the scan refresh in the
|
|
6237
|
+
// background (stale-while-revalidate).
|
|
6238
|
+
scanAllSessions2(serversDir2, projectsDir2, { assignmentsDir: assignmentsDir2, nonBlocking: true })
|
|
6239
|
+
)
|
|
6102
6240
|
);
|
|
6103
6241
|
if (servers.tmuxAvailable) {
|
|
6104
6242
|
const alive = servers.sessions.filter((s) => s.alive).length;
|
|
@@ -6217,7 +6355,7 @@ async function getEditableDocument(projectsDir2, documentType, projectSlug, assi
|
|
|
6217
6355
|
if (!filePath || !await fileExists(filePath)) {
|
|
6218
6356
|
return null;
|
|
6219
6357
|
}
|
|
6220
|
-
const content = await
|
|
6358
|
+
const content = await readFile10(filePath, "utf-8");
|
|
6221
6359
|
const title = getEditableDocumentTitle(documentType, projectSlug, assignmentSlug);
|
|
6222
6360
|
return {
|
|
6223
6361
|
documentType,
|
|
@@ -6243,7 +6381,7 @@ async function getEditableDocumentById(projectsDir2, assignmentsDir2, documentTy
|
|
|
6243
6381
|
if (!fileName) return null;
|
|
6244
6382
|
const filePath = resolve16(resolved.assignmentDir, fileName);
|
|
6245
6383
|
if (!await fileExists(filePath)) return null;
|
|
6246
|
-
const content = await
|
|
6384
|
+
const content = await readFile10(filePath, "utf-8");
|
|
6247
6385
|
const label = resolved.id;
|
|
6248
6386
|
const title = documentType === "assignment" ? `Edit Assignment: ${label}` : documentType === "plan" ? `Edit Plan: ${label}` : documentType === "scratchpad" ? `Edit Scratchpad: ${label}` : documentType === "handoff" ? `Append Handoff: ${label}` : `Append Decision: ${label}`;
|
|
6249
6387
|
return {
|
|
@@ -6262,7 +6400,7 @@ async function getProjectDetail(projectsDir2, slug) {
|
|
|
6262
6400
|
if (!await fileExists(projectMdPath)) {
|
|
6263
6401
|
return null;
|
|
6264
6402
|
}
|
|
6265
|
-
const projectContent = await
|
|
6403
|
+
const projectContent = await readFile10(projectMdPath, "utf-8");
|
|
6266
6404
|
const project = parseProject(projectContent);
|
|
6267
6405
|
const assignments = await listAssignmentRecords(projectPath);
|
|
6268
6406
|
const rollup = await buildProjectRollup(projectPath, project, assignments);
|
|
@@ -6299,18 +6437,18 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
|
|
|
6299
6437
|
if (!await fileExists(assignmentMdPath2)) {
|
|
6300
6438
|
return null;
|
|
6301
6439
|
}
|
|
6302
|
-
const assignmentContent = await
|
|
6440
|
+
const assignmentContent = await readFile10(assignmentMdPath2, "utf-8");
|
|
6303
6441
|
const assignment = parseAssignmentFull(assignmentContent);
|
|
6304
6442
|
let projectWorkspace = null;
|
|
6305
6443
|
const projectMdPath = resolve16(projectsDir2, projectSlug, "project.md");
|
|
6306
6444
|
if (await fileExists(projectMdPath)) {
|
|
6307
|
-
const projectContent = await
|
|
6445
|
+
const projectContent = await readFile10(projectMdPath, "utf-8");
|
|
6308
6446
|
projectWorkspace = parseProject(projectContent).workspace;
|
|
6309
6447
|
}
|
|
6310
6448
|
let plan = null;
|
|
6311
6449
|
const planPath = resolve16(assignmentDir, "plan.md");
|
|
6312
6450
|
if (await fileExists(planPath)) {
|
|
6313
|
-
const planContent = await
|
|
6451
|
+
const planContent = await readFile10(planPath, "utf-8");
|
|
6314
6452
|
const parsed = parsePlan(planContent);
|
|
6315
6453
|
plan = {
|
|
6316
6454
|
status: parsed.status,
|
|
@@ -6321,7 +6459,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
|
|
|
6321
6459
|
let scratchpad = null;
|
|
6322
6460
|
const scratchpadPath = resolve16(assignmentDir, "scratchpad.md");
|
|
6323
6461
|
if (await fileExists(scratchpadPath)) {
|
|
6324
|
-
const scratchpadContent = await
|
|
6462
|
+
const scratchpadContent = await readFile10(scratchpadPath, "utf-8");
|
|
6325
6463
|
const parsed = parseScratchpad(scratchpadContent);
|
|
6326
6464
|
scratchpad = {
|
|
6327
6465
|
updated: parsed.updated,
|
|
@@ -6331,7 +6469,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
|
|
|
6331
6469
|
let handoff = null;
|
|
6332
6470
|
const handoffPath = resolve16(assignmentDir, "handoff.md");
|
|
6333
6471
|
if (await fileExists(handoffPath)) {
|
|
6334
|
-
const handoffContent = await
|
|
6472
|
+
const handoffContent = await readFile10(handoffPath, "utf-8");
|
|
6335
6473
|
const parsed = parseHandoff(handoffContent);
|
|
6336
6474
|
handoff = {
|
|
6337
6475
|
updated: parsed.updated,
|
|
@@ -6342,7 +6480,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
|
|
|
6342
6480
|
let decisionRecord = null;
|
|
6343
6481
|
const decisionRecordPath = resolve16(assignmentDir, "decision-record.md");
|
|
6344
6482
|
if (await fileExists(decisionRecordPath)) {
|
|
6345
|
-
const decisionRecordContent = await
|
|
6483
|
+
const decisionRecordContent = await readFile10(decisionRecordPath, "utf-8");
|
|
6346
6484
|
const parsed = parseDecisionRecord(decisionRecordContent);
|
|
6347
6485
|
decisionRecord = {
|
|
6348
6486
|
updated: parsed.updated,
|
|
@@ -6353,7 +6491,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
|
|
|
6353
6491
|
let progress = null;
|
|
6354
6492
|
const progressPath = resolve16(assignmentDir, "progress.md");
|
|
6355
6493
|
if (await fileExists(progressPath)) {
|
|
6356
|
-
const progressContent = await
|
|
6494
|
+
const progressContent = await readFile10(progressPath, "utf-8");
|
|
6357
6495
|
const parsed = parseProgress(progressContent);
|
|
6358
6496
|
progress = {
|
|
6359
6497
|
updated: parsed.updated,
|
|
@@ -6364,7 +6502,7 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
|
|
|
6364
6502
|
let comments = null;
|
|
6365
6503
|
const commentsPath = resolve16(assignmentDir, "comments.md");
|
|
6366
6504
|
if (await fileExists(commentsPath)) {
|
|
6367
|
-
const commentsContent = await
|
|
6505
|
+
const commentsContent = await readFile10(commentsPath, "utf-8");
|
|
6368
6506
|
const parsed = parseComments(commentsContent);
|
|
6369
6507
|
comments = {
|
|
6370
6508
|
updated: parsed.updated,
|
|
@@ -6515,7 +6653,7 @@ async function countMentionsInAssignment(sourceDir, target) {
|
|
|
6515
6653
|
const bodies = [];
|
|
6516
6654
|
const assignmentMd = resolve16(sourceDir, "assignment.md");
|
|
6517
6655
|
if (await fileExists(assignmentMd)) {
|
|
6518
|
-
const content = await
|
|
6656
|
+
const content = await readFile10(assignmentMd, "utf-8");
|
|
6519
6657
|
const todosMatch = content.match(/^## Todos\s*$([\s\S]*?)(?=^## |$(?![\r\n]))/m);
|
|
6520
6658
|
if (todosMatch) bodies.push(todosMatch[1]);
|
|
6521
6659
|
}
|
|
@@ -6523,7 +6661,7 @@ async function countMentionsInAssignment(sourceDir, target) {
|
|
|
6523
6661
|
const path = resolve16(sourceDir, filename);
|
|
6524
6662
|
if (await fileExists(path)) {
|
|
6525
6663
|
try {
|
|
6526
|
-
bodies.push(await
|
|
6664
|
+
bodies.push(await readFile10(path, "utf-8"));
|
|
6527
6665
|
} catch {
|
|
6528
6666
|
}
|
|
6529
6667
|
}
|
|
@@ -6583,42 +6721,42 @@ async function buildStandaloneAssignmentDetail(resolved) {
|
|
|
6583
6721
|
const assignmentDir = resolved.assignmentDir;
|
|
6584
6722
|
const assignmentMdPath2 = resolve16(assignmentDir, "assignment.md");
|
|
6585
6723
|
if (!await fileExists(assignmentMdPath2)) return null;
|
|
6586
|
-
const assignmentContent = await
|
|
6724
|
+
const assignmentContent = await readFile10(assignmentMdPath2, "utf-8");
|
|
6587
6725
|
const assignment = parseAssignmentFull(assignmentContent);
|
|
6588
6726
|
let plan = null;
|
|
6589
6727
|
const planPath = resolve16(assignmentDir, "plan.md");
|
|
6590
6728
|
if (await fileExists(planPath)) {
|
|
6591
|
-
const parsed = parsePlan(await
|
|
6729
|
+
const parsed = parsePlan(await readFile10(planPath, "utf-8"));
|
|
6592
6730
|
plan = { status: parsed.status, updated: parsed.updated, body: parsed.body };
|
|
6593
6731
|
}
|
|
6594
6732
|
let scratchpad = null;
|
|
6595
6733
|
const scratchpadPath = resolve16(assignmentDir, "scratchpad.md");
|
|
6596
6734
|
if (await fileExists(scratchpadPath)) {
|
|
6597
|
-
const parsed = parseScratchpad(await
|
|
6735
|
+
const parsed = parseScratchpad(await readFile10(scratchpadPath, "utf-8"));
|
|
6598
6736
|
scratchpad = { updated: parsed.updated, body: parsed.body };
|
|
6599
6737
|
}
|
|
6600
6738
|
let handoff = null;
|
|
6601
6739
|
const handoffPath = resolve16(assignmentDir, "handoff.md");
|
|
6602
6740
|
if (await fileExists(handoffPath)) {
|
|
6603
|
-
const parsed = parseHandoff(await
|
|
6741
|
+
const parsed = parseHandoff(await readFile10(handoffPath, "utf-8"));
|
|
6604
6742
|
handoff = { updated: parsed.updated, handoffCount: parsed.handoffCount, body: parsed.body };
|
|
6605
6743
|
}
|
|
6606
6744
|
let decisionRecord = null;
|
|
6607
6745
|
const decisionRecordPath = resolve16(assignmentDir, "decision-record.md");
|
|
6608
6746
|
if (await fileExists(decisionRecordPath)) {
|
|
6609
|
-
const parsed = parseDecisionRecord(await
|
|
6747
|
+
const parsed = parseDecisionRecord(await readFile10(decisionRecordPath, "utf-8"));
|
|
6610
6748
|
decisionRecord = { updated: parsed.updated, decisionCount: parsed.decisionCount, body: parsed.body };
|
|
6611
6749
|
}
|
|
6612
6750
|
let progress = null;
|
|
6613
6751
|
const progressPath = resolve16(assignmentDir, "progress.md");
|
|
6614
6752
|
if (await fileExists(progressPath)) {
|
|
6615
|
-
const parsed = parseProgress(await
|
|
6753
|
+
const parsed = parseProgress(await readFile10(progressPath, "utf-8"));
|
|
6616
6754
|
progress = { updated: parsed.updated, entryCount: parsed.entryCount, entries: parsed.entries };
|
|
6617
6755
|
}
|
|
6618
6756
|
let comments = null;
|
|
6619
6757
|
const commentsPath = resolve16(assignmentDir, "comments.md");
|
|
6620
6758
|
if (await fileExists(commentsPath)) {
|
|
6621
|
-
const parsed = parseComments(await
|
|
6759
|
+
const parsed = parseComments(await readFile10(commentsPath, "utf-8"));
|
|
6622
6760
|
comments = { updated: parsed.updated, entryCount: parsed.entryCount, entries: parsed.entries };
|
|
6623
6761
|
}
|
|
6624
6762
|
const detail = {
|
|
@@ -6655,6 +6793,14 @@ async function buildStandaloneAssignmentDetail(resolved) {
|
|
|
6655
6793
|
return detail;
|
|
6656
6794
|
}
|
|
6657
6795
|
async function listProjectRecords(projectsDir2, traces) {
|
|
6796
|
+
const cached = projectRecordsCache.get(projectsDir2);
|
|
6797
|
+
if (cached) return cached;
|
|
6798
|
+
const promise = computeProjectRecords(projectsDir2, traces);
|
|
6799
|
+
projectRecordsCache.set(projectsDir2, promise);
|
|
6800
|
+
promise.catch(() => projectRecordsCache.delete(projectsDir2));
|
|
6801
|
+
return promise;
|
|
6802
|
+
}
|
|
6803
|
+
async function computeProjectRecords(projectsDir2, traces) {
|
|
6658
6804
|
if (!await fileExists(projectsDir2)) {
|
|
6659
6805
|
return [];
|
|
6660
6806
|
}
|
|
@@ -6662,7 +6808,7 @@ async function listProjectRecords(projectsDir2, traces) {
|
|
|
6662
6808
|
migratedProjectsDirs.add(projectsDir2);
|
|
6663
6809
|
await migrateLegacyProjectFiles(projectsDir2);
|
|
6664
6810
|
}
|
|
6665
|
-
const entries = await
|
|
6811
|
+
const entries = await readdir8(projectsDir2, { withFileTypes: true });
|
|
6666
6812
|
const projectDirs = entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
|
|
6667
6813
|
const maybeRecords = await Promise.all(
|
|
6668
6814
|
projectDirs.map(async (entry) => {
|
|
@@ -6672,7 +6818,7 @@ async function listProjectRecords(projectsDir2, traces) {
|
|
|
6672
6818
|
return null;
|
|
6673
6819
|
}
|
|
6674
6820
|
const t0 = traces ? performance.now() : 0;
|
|
6675
|
-
const projectContent = await
|
|
6821
|
+
const projectContent = await readFile10(projectMdPath, "utf-8");
|
|
6676
6822
|
const project = parseProject(projectContent);
|
|
6677
6823
|
if (traces) accumulatePhase(traces, "parse-project-md", performance.now() - t0);
|
|
6678
6824
|
const t1 = traces ? performance.now() : 0;
|
|
@@ -6717,7 +6863,7 @@ async function listAssignmentRecords(projectPath, traces) {
|
|
|
6717
6863
|
if (!await fileExists(assignmentsDir2)) {
|
|
6718
6864
|
return [];
|
|
6719
6865
|
}
|
|
6720
|
-
const entries = await
|
|
6866
|
+
const entries = await readdir8(assignmentsDir2, { withFileTypes: true });
|
|
6721
6867
|
const dirEntries = entries.filter((entry) => entry.isDirectory());
|
|
6722
6868
|
const maybeRecords = await Promise.all(
|
|
6723
6869
|
dirEntries.map(async (entry) => {
|
|
@@ -6726,7 +6872,7 @@ async function listAssignmentRecords(projectPath, traces) {
|
|
|
6726
6872
|
return null;
|
|
6727
6873
|
}
|
|
6728
6874
|
const t0 = traces ? performance.now() : 0;
|
|
6729
|
-
const content = await
|
|
6875
|
+
const content = await readFile10(assignmentMd, "utf-8");
|
|
6730
6876
|
const parsed = parseAssignmentFull(content);
|
|
6731
6877
|
if (traces) accumulatePhase(traces, "read-assignment-md", performance.now() - t0);
|
|
6732
6878
|
return parsed;
|
|
@@ -6741,14 +6887,14 @@ async function listResources(projectPath) {
|
|
|
6741
6887
|
if (!await fileExists(resourcesDir)) {
|
|
6742
6888
|
return [];
|
|
6743
6889
|
}
|
|
6744
|
-
const entries = await
|
|
6890
|
+
const entries = await readdir8(resourcesDir, { withFileTypes: true });
|
|
6745
6891
|
const results = [];
|
|
6746
6892
|
for (const entry of entries) {
|
|
6747
6893
|
if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name.startsWith("_")) {
|
|
6748
6894
|
continue;
|
|
6749
6895
|
}
|
|
6750
6896
|
const filePath = resolve16(resourcesDir, entry.name);
|
|
6751
|
-
const content = await
|
|
6897
|
+
const content = await readFile10(filePath, "utf-8");
|
|
6752
6898
|
const parsed = parseResource(content);
|
|
6753
6899
|
results.push({
|
|
6754
6900
|
name: parsed.name,
|
|
@@ -6767,14 +6913,14 @@ async function listMemories(projectPath) {
|
|
|
6767
6913
|
if (!await fileExists(memoriesDir)) {
|
|
6768
6914
|
return [];
|
|
6769
6915
|
}
|
|
6770
|
-
const entries = await
|
|
6916
|
+
const entries = await readdir8(memoriesDir, { withFileTypes: true });
|
|
6771
6917
|
const results = [];
|
|
6772
6918
|
for (const entry of entries) {
|
|
6773
6919
|
if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name.startsWith("_")) {
|
|
6774
6920
|
continue;
|
|
6775
6921
|
}
|
|
6776
6922
|
const filePath = resolve16(memoriesDir, entry.name);
|
|
6777
|
-
const content = await
|
|
6923
|
+
const content = await readFile10(filePath, "utf-8");
|
|
6778
6924
|
const parsed = parseMemory(content);
|
|
6779
6925
|
results.push({
|
|
6780
6926
|
name: parsed.name,
|
|
@@ -6837,7 +6983,7 @@ async function getMemoryDetail(projectsDir2, projectSlug, itemSlug) {
|
|
|
6837
6983
|
if (!projectRecord) return null;
|
|
6838
6984
|
const filePath = resolve16(projectRecord.projectPath, "memories", `${itemSlug}.md`);
|
|
6839
6985
|
if (!await fileExists(filePath)) return null;
|
|
6840
|
-
const content = await
|
|
6986
|
+
const content = await readFile10(filePath, "utf-8");
|
|
6841
6987
|
const parsed = parseMemory(content);
|
|
6842
6988
|
return {
|
|
6843
6989
|
name: parsed.name,
|
|
@@ -6863,7 +7009,7 @@ async function getResourceDetail(projectsDir2, projectSlug, itemSlug) {
|
|
|
6863
7009
|
if (!projectRecord) return null;
|
|
6864
7010
|
const filePath = resolve16(projectRecord.projectPath, "resources", `${itemSlug}.md`);
|
|
6865
7011
|
if (!await fileExists(filePath)) return null;
|
|
6866
|
-
const content = await
|
|
7012
|
+
const content = await readFile10(filePath, "utf-8");
|
|
6867
7013
|
const parsed = parseResource(content);
|
|
6868
7014
|
return {
|
|
6869
7015
|
name: parsed.name,
|
|
@@ -6881,7 +7027,7 @@ async function getResourceDetail(projectsDir2, projectSlug, itemSlug) {
|
|
|
6881
7027
|
async function loadDependencyGraph(projectPath, assignments) {
|
|
6882
7028
|
const statusPath = resolve16(projectPath, "_status.md");
|
|
6883
7029
|
if (await fileExists(statusPath)) {
|
|
6884
|
-
const statusContent = await
|
|
7030
|
+
const statusContent = await readFile10(statusPath, "utf-8");
|
|
6885
7031
|
const parsed = parseStatus(statusContent);
|
|
6886
7032
|
const derivedGraph = extractMermaidGraph(parsed.body);
|
|
6887
7033
|
if (derivedGraph) {
|
|
@@ -7042,7 +7188,7 @@ async function getUnmetDependencies(projectPath, dependsOn, terminalStatuses3, d
|
|
|
7042
7188
|
unmet.push(`${dependency} (missing)`);
|
|
7043
7189
|
continue;
|
|
7044
7190
|
}
|
|
7045
|
-
const content = await
|
|
7191
|
+
const content = await readFile10(dependencyPath, "utf-8");
|
|
7046
7192
|
const parsed = parseAssignmentFull(content);
|
|
7047
7193
|
if (!terminals.has(parsed.status)) {
|
|
7048
7194
|
unmet.push(`${dependency} (${parsed.status})`);
|
|
@@ -7335,7 +7481,7 @@ async function countOpenQuestions(projectPath, assignmentSlug) {
|
|
|
7335
7481
|
return 0;
|
|
7336
7482
|
}
|
|
7337
7483
|
try {
|
|
7338
|
-
const content = await
|
|
7484
|
+
const content = await readFile10(commentsPath, "utf-8");
|
|
7339
7485
|
const parsed = parseComments(content);
|
|
7340
7486
|
return parsed.entries.filter(
|
|
7341
7487
|
(e) => e.type === "question" && e.resolved !== true
|
|
@@ -7403,12 +7549,12 @@ async function listPlaybooks(playbooksDir3) {
|
|
|
7403
7549
|
if (!await fileExists(playbooksDir3)) return [];
|
|
7404
7550
|
const config = await readConfig();
|
|
7405
7551
|
const disabledSet = new Set(config.playbooks.disabled);
|
|
7406
|
-
const entries = await
|
|
7552
|
+
const entries = await readdir8(playbooksDir3, { withFileTypes: true });
|
|
7407
7553
|
const playbooks = [];
|
|
7408
7554
|
for (const entry of entries) {
|
|
7409
7555
|
if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name.startsWith("_") || entry.name === "manifest.md") continue;
|
|
7410
7556
|
const filePath = resolve16(playbooksDir3, entry.name);
|
|
7411
|
-
const raw = await
|
|
7557
|
+
const raw = await readFile10(filePath, "utf-8");
|
|
7412
7558
|
const parsed = parsePlaybook(raw);
|
|
7413
7559
|
const slug = parsed.slug || entry.name.replace(/\.md$/, "");
|
|
7414
7560
|
playbooks.push({
|
|
@@ -7442,7 +7588,7 @@ async function getPlaybookDetail(playbooksDir3, slug) {
|
|
|
7442
7588
|
enabled
|
|
7443
7589
|
};
|
|
7444
7590
|
}
|
|
7445
|
-
var WorkspaceBlockedError, STALE_ASSIGNMENT_MS, RECENT_PROJECTS_LIMIT, RECENT_ACTIVITY_LIMIT, RECENT_SESSIONS_LIMIT, NEWEST_CREATED_LIMIT, SEGMENT_DISPLAY_CAP, STALE_LIMIT_DEFAULT, STALE_LIMIT_MAX, TERMINAL_STATUSES2, STATUS_TO_SEGMENT, HERO_PRIORITY, DEFAULT_TRANSITION_DEFINITIONS, DEFAULT_STATUS_COLORS, _cachedConfig, REFERENCED_BY_LIMIT, migratedProjectsDirs, DEFAULT_GRAPH_COLORS;
|
|
7591
|
+
var WorkspaceBlockedError, STALE_ASSIGNMENT_MS, RECENT_PROJECTS_LIMIT, RECENT_ACTIVITY_LIMIT, RECENT_SESSIONS_LIMIT, NEWEST_CREATED_LIMIT, SEGMENT_DISPLAY_CAP, STALE_LIMIT_DEFAULT, STALE_LIMIT_MAX, TERMINAL_STATUSES2, STATUS_TO_SEGMENT, HERO_PRIORITY, projectRecordsCache, standaloneRecordsCache, DEFAULT_TRANSITION_DEFINITIONS, DEFAULT_STATUS_COLORS, _cachedConfig, REFERENCED_BY_LIMIT, migratedProjectsDirs, DEFAULT_GRAPH_COLORS;
|
|
7446
7592
|
var init_api = __esm({
|
|
7447
7593
|
"src/dashboard/api.ts"() {
|
|
7448
7594
|
"use strict";
|
|
@@ -7493,6 +7639,8 @@ var init_api = __esm({
|
|
|
7493
7639
|
["blocked", "blocked"],
|
|
7494
7640
|
["stale", "stale"]
|
|
7495
7641
|
];
|
|
7642
|
+
projectRecordsCache = /* @__PURE__ */ new Map();
|
|
7643
|
+
standaloneRecordsCache = /* @__PURE__ */ new Map();
|
|
7496
7644
|
DEFAULT_TRANSITION_DEFINITIONS = [
|
|
7497
7645
|
{
|
|
7498
7646
|
command: "start",
|
|
@@ -7589,7 +7737,7 @@ __export(git_worktree_exports, {
|
|
|
7589
7737
|
removeWorktree: () => removeWorktree
|
|
7590
7738
|
});
|
|
7591
7739
|
import { spawn } from "child_process";
|
|
7592
|
-
import { readFile as
|
|
7740
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
7593
7741
|
function run(command, args, cwd) {
|
|
7594
7742
|
return new Promise((resolvePromise) => {
|
|
7595
7743
|
const child = spawn(command, args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -7633,7 +7781,7 @@ async function createWorktreeAndRecord(opts) {
|
|
|
7633
7781
|
const { assignmentPath, repository, branch, worktreePath, parentBranch } = opts;
|
|
7634
7782
|
await createWorktree({ repository, branch, worktreePath, parentBranch });
|
|
7635
7783
|
try {
|
|
7636
|
-
const content = await
|
|
7784
|
+
const content = await readFile13(assignmentPath, "utf-8");
|
|
7637
7785
|
const updated = updateAssignmentWorkspace(content, {
|
|
7638
7786
|
repository,
|
|
7639
7787
|
worktreePath,
|
|
@@ -9480,7 +9628,7 @@ function mergePatch(current, patch) {
|
|
|
9480
9628
|
// src/utils/view-prefs.ts
|
|
9481
9629
|
init_paths();
|
|
9482
9630
|
init_fs();
|
|
9483
|
-
import { readFile as
|
|
9631
|
+
import { readFile as readFile11, rename as rename3, unlink as unlink3 } from "fs/promises";
|
|
9484
9632
|
import { resolve as resolve17 } from "path";
|
|
9485
9633
|
function corruptFilePath() {
|
|
9486
9634
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -9503,7 +9651,7 @@ async function readViewPrefsFile() {
|
|
|
9503
9651
|
}
|
|
9504
9652
|
let raw;
|
|
9505
9653
|
try {
|
|
9506
|
-
raw = await
|
|
9654
|
+
raw = await readFile11(path, "utf-8");
|
|
9507
9655
|
} catch {
|
|
9508
9656
|
return { ...DEFAULT_VIEW_PREFS_FILE };
|
|
9509
9657
|
}
|
|
@@ -9686,7 +9834,7 @@ function isDashboardSlot(value) {
|
|
|
9686
9834
|
// src/utils/saved-views.ts
|
|
9687
9835
|
init_paths();
|
|
9688
9836
|
init_fs();
|
|
9689
|
-
import { readFile as
|
|
9837
|
+
import { readFile as readFile12, rename as rename4, unlink as unlink4 } from "fs/promises";
|
|
9690
9838
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
9691
9839
|
import { resolve as resolve18 } from "path";
|
|
9692
9840
|
function corruptFilePath2() {
|
|
@@ -9716,7 +9864,7 @@ async function readSavedViewsFile() {
|
|
|
9716
9864
|
}
|
|
9717
9865
|
let raw;
|
|
9718
9866
|
try {
|
|
9719
|
-
raw = await
|
|
9867
|
+
raw = await readFile12(path, "utf-8");
|
|
9720
9868
|
} catch {
|
|
9721
9869
|
return cloneDefault();
|
|
9722
9870
|
}
|
|
@@ -10036,7 +10184,7 @@ init_fs();
|
|
|
10036
10184
|
init_git_worktree();
|
|
10037
10185
|
import { Router as Router2 } from "express";
|
|
10038
10186
|
import { resolve as resolve21, basename as basename3, isAbsolute as isAbsolute2 } from "path";
|
|
10039
|
-
import { rm, readFile as
|
|
10187
|
+
import { rm, readFile as readFile15, open as fsOpen, stat as fsStat, realpath as fsRealpath } from "fs/promises";
|
|
10040
10188
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
10041
10189
|
|
|
10042
10190
|
// src/utils/worktree-defaults.ts
|
|
@@ -10083,14 +10231,14 @@ function detectCurrentBranch(cwd) {
|
|
|
10083
10231
|
// src/dashboard/repository-candidates.ts
|
|
10084
10232
|
init_fs();
|
|
10085
10233
|
init_parser();
|
|
10086
|
-
import { readdir as
|
|
10234
|
+
import { readdir as readdir9, readFile as readFile14 } from "fs/promises";
|
|
10087
10235
|
import { resolve as resolve20 } from "path";
|
|
10088
10236
|
async function getProjectRepositoryCandidates(projectsDir2, projectSlug) {
|
|
10089
10237
|
const seen = /* @__PURE__ */ new Set();
|
|
10090
10238
|
const out = [];
|
|
10091
10239
|
const projectPath = resolve20(projectsDir2, projectSlug, "project.md");
|
|
10092
10240
|
if (await fileExists(projectPath)) {
|
|
10093
|
-
const project = parseProject(await
|
|
10241
|
+
const project = parseProject(await readFile14(projectPath, "utf-8"));
|
|
10094
10242
|
for (const raw of project.repositories) {
|
|
10095
10243
|
const path = raw.trim();
|
|
10096
10244
|
if (!path) continue;
|
|
@@ -10102,12 +10250,12 @@ async function getProjectRepositoryCandidates(projectsDir2, projectSlug) {
|
|
|
10102
10250
|
}
|
|
10103
10251
|
const assignmentsDir2 = resolve20(projectsDir2, projectSlug, "assignments");
|
|
10104
10252
|
if (await fileExists(assignmentsDir2)) {
|
|
10105
|
-
const entries = await
|
|
10253
|
+
const entries = await readdir9(assignmentsDir2, { withFileTypes: true });
|
|
10106
10254
|
for (const entry of entries) {
|
|
10107
10255
|
if (!entry.isDirectory()) continue;
|
|
10108
10256
|
const assignmentMd = resolve20(assignmentsDir2, entry.name, "assignment.md");
|
|
10109
10257
|
if (!await fileExists(assignmentMd)) continue;
|
|
10110
|
-
const parsed = parseAssignmentFull(await
|
|
10258
|
+
const parsed = parseAssignmentFull(await readFile14(assignmentMd, "utf-8"));
|
|
10111
10259
|
const repo = parsed.workspace.repository?.trim();
|
|
10112
10260
|
if (!repo) continue;
|
|
10113
10261
|
const abs = resolve20(repo);
|
|
@@ -10124,13 +10272,13 @@ async function getStandaloneRepositoryCandidates(assignmentsDir2, excludeAssignm
|
|
|
10124
10272
|
}
|
|
10125
10273
|
const seen = /* @__PURE__ */ new Set();
|
|
10126
10274
|
const out = [];
|
|
10127
|
-
const entries = await
|
|
10275
|
+
const entries = await readdir9(assignmentsDir2, { withFileTypes: true });
|
|
10128
10276
|
for (const entry of entries) {
|
|
10129
10277
|
if (!entry.isDirectory()) continue;
|
|
10130
10278
|
if (entry.name === excludeAssignmentId) continue;
|
|
10131
10279
|
const assignmentMd = resolve20(assignmentsDir2, entry.name, "assignment.md");
|
|
10132
10280
|
if (!await fileExists(assignmentMd)) continue;
|
|
10133
|
-
const parsed = parseAssignmentFull(await
|
|
10281
|
+
const parsed = parseAssignmentFull(await readFile14(assignmentMd, "utf-8"));
|
|
10134
10282
|
const repo = parsed.workspace.repository?.trim();
|
|
10135
10283
|
if (!repo) continue;
|
|
10136
10284
|
const abs = resolve20(repo);
|
|
@@ -10295,14 +10443,14 @@ async function readCurrentDocument(filePath) {
|
|
|
10295
10443
|
if (!await fileExists(filePath)) {
|
|
10296
10444
|
return null;
|
|
10297
10445
|
}
|
|
10298
|
-
return
|
|
10446
|
+
return readFile15(filePath, "utf-8");
|
|
10299
10447
|
}
|
|
10300
10448
|
async function handleWorktreeCreate(req, res, ctx) {
|
|
10301
10449
|
if (!await fileExists(ctx.assignmentPath)) {
|
|
10302
10450
|
res.status(404).json({ error: "Assignment not found" });
|
|
10303
10451
|
return;
|
|
10304
10452
|
}
|
|
10305
|
-
const parsed = parseAssignmentFull(await
|
|
10453
|
+
const parsed = parseAssignmentFull(await readFile15(ctx.assignmentPath, "utf-8"));
|
|
10306
10454
|
if (parsed.workspace.worktreePath) {
|
|
10307
10455
|
res.status(409).json({ error: "Worktree already configured for this assignment" });
|
|
10308
10456
|
return;
|
|
@@ -10394,6 +10542,7 @@ async function handleWorktreeCreate(req, res, ctx) {
|
|
|
10394
10542
|
function createWriteRouter(projectsDir2, assignmentsDir2, todosDir2) {
|
|
10395
10543
|
const linkedTodosLookup = todosDir2 ? { todosDir: todosDir2, projectsDir: projectsDir2 } : void 0;
|
|
10396
10544
|
const router = Router2();
|
|
10545
|
+
installRecordsInvalidation(router);
|
|
10397
10546
|
router.get("/api/templates/project", (_req, res) => {
|
|
10398
10547
|
const content = renderProject({
|
|
10399
10548
|
id: generateId(),
|
|
@@ -10671,7 +10820,7 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
|
|
|
10671
10820
|
}
|
|
10672
10821
|
const nextContentRaw = requireContent(req, res);
|
|
10673
10822
|
if (!nextContentRaw) return;
|
|
10674
|
-
const currentContent = await
|
|
10823
|
+
const currentContent = await readFile15(filePath, "utf-8");
|
|
10675
10824
|
const frontmatterBlock = extractFrontmatterBlock(currentContent);
|
|
10676
10825
|
if (!frontmatterBlock) {
|
|
10677
10826
|
res.status(500).json({ error: `${kind} file is malformed (no frontmatter)` });
|
|
@@ -11122,7 +11271,7 @@ ${nextBody}${nextBody.endsWith("\n") ? "" : "\n"}`;
|
|
|
11122
11271
|
let currentContent;
|
|
11123
11272
|
let currentCount = 0;
|
|
11124
11273
|
if (await fileExists(commentsPath)) {
|
|
11125
|
-
currentContent = await
|
|
11274
|
+
currentContent = await readFile15(commentsPath, "utf-8");
|
|
11126
11275
|
const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
|
|
11127
11276
|
if (countMatch) currentCount = parseInt(countMatch[1], 10);
|
|
11128
11277
|
} else {
|
|
@@ -11179,7 +11328,7 @@ ${entry}`;
|
|
|
11179
11328
|
res.status(400).json({ error: "resolved (boolean) is required" });
|
|
11180
11329
|
return;
|
|
11181
11330
|
}
|
|
11182
|
-
const content = await
|
|
11331
|
+
const content = await readFile15(commentsPath, "utf-8");
|
|
11183
11332
|
const parsed = parseComments(content);
|
|
11184
11333
|
const target = parsed.entries.find((e) => e.id === commentId);
|
|
11185
11334
|
if (!target) {
|
|
@@ -11226,7 +11375,7 @@ ${entry}`;
|
|
|
11226
11375
|
});
|
|
11227
11376
|
return;
|
|
11228
11377
|
}
|
|
11229
|
-
let content = await
|
|
11378
|
+
let content = await readFile15(projectPath, "utf-8");
|
|
11230
11379
|
content = setTopLevelField(content, "workspace", workspace ?? null);
|
|
11231
11380
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
11232
11381
|
await writeFileForce(projectPath, content);
|
|
@@ -11263,7 +11412,7 @@ ${entry}`;
|
|
|
11263
11412
|
return;
|
|
11264
11413
|
}
|
|
11265
11414
|
const assignmentPath = resolve21(resolved.assignmentDir, "assignment.md");
|
|
11266
|
-
let content = await
|
|
11415
|
+
let content = await readFile15(assignmentPath, "utf-8");
|
|
11267
11416
|
content = setTopLevelField(content, "workspaceGroup", workspaceGroup ?? null);
|
|
11268
11417
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
11269
11418
|
await writeFileForce(assignmentPath, content);
|
|
@@ -11358,7 +11507,7 @@ ${entry}`;
|
|
|
11358
11507
|
return;
|
|
11359
11508
|
}
|
|
11360
11509
|
const assignmentPath = resolve21(resolved.assignmentDir, "assignment.md");
|
|
11361
|
-
const parsedForSlug = parseAssignmentFull(await
|
|
11510
|
+
const parsedForSlug = parseAssignmentFull(await readFile15(assignmentPath, "utf-8"));
|
|
11362
11511
|
const assignmentSlugForBranch = parsedForSlug.slug || resolved.id;
|
|
11363
11512
|
await handleWorktreeCreate(req, res, {
|
|
11364
11513
|
assignmentPath,
|
|
@@ -11387,7 +11536,7 @@ ${entry}`;
|
|
|
11387
11536
|
res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}, or null to clear.` });
|
|
11388
11537
|
return;
|
|
11389
11538
|
}
|
|
11390
|
-
let content = await
|
|
11539
|
+
let content = await readFile15(projectPath, "utf-8");
|
|
11391
11540
|
content = setTopLevelField(content, "statusOverride", status ?? null);
|
|
11392
11541
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
11393
11542
|
await writeFileForce(projectPath, content);
|
|
@@ -11420,7 +11569,7 @@ ${entry}`;
|
|
|
11420
11569
|
res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}.` });
|
|
11421
11570
|
return;
|
|
11422
11571
|
}
|
|
11423
|
-
let content = await
|
|
11572
|
+
let content = await readFile15(assignmentPath, "utf-8");
|
|
11424
11573
|
content = setTopLevelField(content, "status", status);
|
|
11425
11574
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
11426
11575
|
if (status !== "blocked") {
|
|
@@ -11454,7 +11603,7 @@ ${entry}`;
|
|
|
11454
11603
|
res.status(400).json({ error: validation.error });
|
|
11455
11604
|
return;
|
|
11456
11605
|
}
|
|
11457
|
-
let content = await
|
|
11606
|
+
let content = await readFile15(assignmentPath, "utf-8");
|
|
11458
11607
|
content = setTopLevelField(content, "assignee", validation.value);
|
|
11459
11608
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
11460
11609
|
await writeFileForce(assignmentPath, content);
|
|
@@ -11485,7 +11634,7 @@ ${entry}`;
|
|
|
11485
11634
|
res.status(400).json({ error: validation.error });
|
|
11486
11635
|
return;
|
|
11487
11636
|
}
|
|
11488
|
-
let content = await
|
|
11637
|
+
let content = await readFile15(assignmentPath, "utf-8");
|
|
11489
11638
|
content = setTopLevelField(content, "title", validation.value);
|
|
11490
11639
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
11491
11640
|
await writeFileForce(assignmentPath, content);
|
|
@@ -12003,7 +12152,7 @@ ${entry}`;
|
|
|
12003
12152
|
res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}.` });
|
|
12004
12153
|
return;
|
|
12005
12154
|
}
|
|
12006
|
-
let content = await
|
|
12155
|
+
let content = await readFile15(assignmentPath, "utf-8");
|
|
12007
12156
|
content = setTopLevelField(content, "status", status);
|
|
12008
12157
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
12009
12158
|
if (status !== "blocked") {
|
|
@@ -12039,7 +12188,7 @@ ${entry}`;
|
|
|
12039
12188
|
res.status(400).json({ error: validation.error });
|
|
12040
12189
|
return;
|
|
12041
12190
|
}
|
|
12042
|
-
let content = await
|
|
12191
|
+
let content = await readFile15(assignmentPath, "utf-8");
|
|
12043
12192
|
content = setTopLevelField(content, "assignee", validation.value);
|
|
12044
12193
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
12045
12194
|
await writeFileForce(assignmentPath, content);
|
|
@@ -12072,7 +12221,7 @@ ${entry}`;
|
|
|
12072
12221
|
res.status(400).json({ error: validation.error });
|
|
12073
12222
|
return;
|
|
12074
12223
|
}
|
|
12075
|
-
let content = await
|
|
12224
|
+
let content = await readFile15(assignmentPath, "utf-8");
|
|
12076
12225
|
content = setTopLevelField(content, "title", validation.value);
|
|
12077
12226
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
12078
12227
|
await writeFileForce(assignmentPath, content);
|
|
@@ -12141,7 +12290,7 @@ ${entry}`;
|
|
|
12141
12290
|
if (!await fileExists(assignmentPath)) {
|
|
12142
12291
|
throw new Error("assignment file not found");
|
|
12143
12292
|
}
|
|
12144
|
-
let content = await
|
|
12293
|
+
let content = await readFile15(assignmentPath, "utf-8");
|
|
12145
12294
|
content = setTopLevelField(content, "status", status);
|
|
12146
12295
|
content = setTopLevelField(content, "updated", timestamp);
|
|
12147
12296
|
if (status !== "blocked") {
|
|
@@ -12297,7 +12446,7 @@ async function appendCommentTo(assignmentDir, assignmentRef, req, res, reloadDet
|
|
|
12297
12446
|
let currentContent;
|
|
12298
12447
|
let currentCount = 0;
|
|
12299
12448
|
if (await fileExists(commentsPath)) {
|
|
12300
|
-
currentContent = await
|
|
12449
|
+
currentContent = await readFile15(commentsPath, "utf-8");
|
|
12301
12450
|
const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
|
|
12302
12451
|
if (countMatch) currentCount = parseInt(countMatch[1], 10);
|
|
12303
12452
|
} else {
|
|
@@ -12337,7 +12486,7 @@ async function toggleCommentResolvedAt(assignmentDir, commentId, req, res, reloa
|
|
|
12337
12486
|
res.status(400).json({ error: "resolved (boolean) is required" });
|
|
12338
12487
|
return;
|
|
12339
12488
|
}
|
|
12340
|
-
const content = await
|
|
12489
|
+
const content = await readFile15(commentsPath, "utf-8");
|
|
12341
12490
|
const parsed = parseComments(content);
|
|
12342
12491
|
const target = parsed.entries.find((e) => e.id === commentId);
|
|
12343
12492
|
if (!target) {
|
|
@@ -13065,26 +13214,26 @@ import { Router as Router8 } from "express";
|
|
|
13065
13214
|
|
|
13066
13215
|
// src/utils/status-config-resolution.ts
|
|
13067
13216
|
init_frontmatter();
|
|
13068
|
-
import { readFile as
|
|
13217
|
+
import { readFile as readFile16, writeFile as writeFile4, rm as rm2 } from "fs/promises";
|
|
13069
13218
|
import { dirname as dirname5 } from "path";
|
|
13070
13219
|
|
|
13071
13220
|
// src/utils/assignment-walk.ts
|
|
13072
13221
|
init_fs();
|
|
13073
13222
|
import { resolve as resolve23 } from "path";
|
|
13074
|
-
import { readdir as
|
|
13223
|
+
import { readdir as readdir10 } from "fs/promises";
|
|
13075
13224
|
async function listAssignmentsByProject(projectsDir2, standaloneDir) {
|
|
13076
13225
|
const result = {
|
|
13077
13226
|
withAssignmentMd: [],
|
|
13078
13227
|
orphanFolders: []
|
|
13079
13228
|
};
|
|
13080
13229
|
if (await fileExists(projectsDir2)) {
|
|
13081
|
-
const projects = await
|
|
13230
|
+
const projects = await readdir10(projectsDir2, { withFileTypes: true });
|
|
13082
13231
|
for (const m of projects) {
|
|
13083
13232
|
if (!m.isDirectory()) continue;
|
|
13084
13233
|
if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
|
|
13085
13234
|
const assignmentsDir2 = resolve23(projectsDir2, m.name, "assignments");
|
|
13086
13235
|
if (!await fileExists(assignmentsDir2)) continue;
|
|
13087
|
-
const entries = await
|
|
13236
|
+
const entries = await readdir10(assignmentsDir2, { withFileTypes: true });
|
|
13088
13237
|
for (const a of entries) {
|
|
13089
13238
|
if (!a.isDirectory()) continue;
|
|
13090
13239
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
@@ -13106,7 +13255,7 @@ async function listAssignmentsByProject(projectsDir2, standaloneDir) {
|
|
|
13106
13255
|
}
|
|
13107
13256
|
}
|
|
13108
13257
|
if (standaloneDir !== null && await fileExists(standaloneDir)) {
|
|
13109
|
-
const entries = await
|
|
13258
|
+
const entries = await readdir10(standaloneDir, { withFileTypes: true });
|
|
13110
13259
|
for (const a of entries) {
|
|
13111
13260
|
if (!a.isDirectory()) continue;
|
|
13112
13261
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
@@ -13148,7 +13297,7 @@ async function scanAssignmentsByStatus(projectsDir2, standaloneDir, ids) {
|
|
|
13148
13297
|
const assignmentPath = `${entry.assignmentDir}/assignment.md`;
|
|
13149
13298
|
let content;
|
|
13150
13299
|
try {
|
|
13151
|
-
content = await
|
|
13300
|
+
content = await readFile16(assignmentPath, "utf-8");
|
|
13152
13301
|
} catch (err2) {
|
|
13153
13302
|
const code = err2?.code;
|
|
13154
13303
|
if (code === "ENOENT") {
|
|
@@ -13211,7 +13360,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
|
|
|
13211
13360
|
const list = affected.get(r.id) ?? [];
|
|
13212
13361
|
for (const a of list) {
|
|
13213
13362
|
try {
|
|
13214
|
-
const content = await
|
|
13363
|
+
const content = await readFile16(a.path, "utf-8");
|
|
13215
13364
|
buffer.set(a.path, content);
|
|
13216
13365
|
} catch (err2) {
|
|
13217
13366
|
const code = err2?.code;
|
|
@@ -13239,7 +13388,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
|
|
|
13239
13388
|
for (const a of list) {
|
|
13240
13389
|
let current;
|
|
13241
13390
|
try {
|
|
13242
|
-
current = await
|
|
13391
|
+
current = await readFile16(a.path, "utf-8");
|
|
13243
13392
|
} catch (err2) {
|
|
13244
13393
|
const code = err2?.code;
|
|
13245
13394
|
if (code === "ENOENT") {
|
|
@@ -13288,7 +13437,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
|
|
|
13288
13437
|
const list = affected.get(r.id) ?? [];
|
|
13289
13438
|
for (const a of list) {
|
|
13290
13439
|
try {
|
|
13291
|
-
const current = await
|
|
13440
|
+
const current = await readFile16(a.path, "utf-8");
|
|
13292
13441
|
const fm = parseAssignmentFrontmatter(current);
|
|
13293
13442
|
if (fm.status !== r.id) {
|
|
13294
13443
|
console.warn(
|
|
@@ -13424,6 +13573,7 @@ function mapResolutionErrorToHttp(err2, applied) {
|
|
|
13424
13573
|
}
|
|
13425
13574
|
function createStatusConfigRouter(projectsDir2, assignmentsDir2) {
|
|
13426
13575
|
const router = Router8();
|
|
13576
|
+
installRecordsInvalidation(router);
|
|
13427
13577
|
router.get("/", async (_req, res) => {
|
|
13428
13578
|
try {
|
|
13429
13579
|
const config = await getStatusConfig();
|
|
@@ -14648,7 +14798,7 @@ init_playbook();
|
|
|
14648
14798
|
init_playbooks();
|
|
14649
14799
|
import { Router as Router11 } from "express";
|
|
14650
14800
|
import { resolve as resolve26 } from "path";
|
|
14651
|
-
import { readFile as
|
|
14801
|
+
import { readFile as readFile17 } from "fs/promises";
|
|
14652
14802
|
function statusForPlaybookError(code) {
|
|
14653
14803
|
switch (code) {
|
|
14654
14804
|
case "manifest":
|
|
@@ -14730,7 +14880,7 @@ function createPlaybooksRouter(playbooksDir3) {
|
|
|
14730
14880
|
return;
|
|
14731
14881
|
}
|
|
14732
14882
|
const filePath = resolve26(playbooksDir3, resolved.filename);
|
|
14733
|
-
const content = await
|
|
14883
|
+
const content = await readFile17(filePath, "utf-8");
|
|
14734
14884
|
res.json({
|
|
14735
14885
|
documentType: "playbook",
|
|
14736
14886
|
title: `Edit Playbook: ${resolved.slug}`,
|
|
@@ -14827,11 +14977,12 @@ init_parser2();
|
|
|
14827
14977
|
init_fs();
|
|
14828
14978
|
init_paths();
|
|
14829
14979
|
import { Router as Router12 } from "express";
|
|
14830
|
-
import { readdir as
|
|
14980
|
+
import { readdir as readdir11 } from "fs/promises";
|
|
14831
14981
|
import { resolve as resolvePath, dirname as dirname6 } from "path";
|
|
14832
14982
|
import { rename as rename5, mkdir as mkdir2 } from "fs/promises";
|
|
14833
14983
|
init_slug();
|
|
14834
14984
|
init_promote_todos();
|
|
14985
|
+
init_api();
|
|
14835
14986
|
var WORKSPACE_REGEX = /^[a-z0-9_][a-z0-9-]*$/;
|
|
14836
14987
|
function getWorkspaceParam(value) {
|
|
14837
14988
|
if (Array.isArray(value)) {
|
|
@@ -14846,6 +14997,7 @@ function touchItem3(item) {
|
|
|
14846
14997
|
}
|
|
14847
14998
|
function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
14848
14999
|
const router = Router12();
|
|
15000
|
+
installRecordsInvalidation(router);
|
|
14849
15001
|
function broadcastUpdate() {
|
|
14850
15002
|
broadcast({ type: "todos-updated", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
14851
15003
|
}
|
|
@@ -14965,7 +15117,7 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
|
14965
15117
|
router.get("/", async (_req, res) => {
|
|
14966
15118
|
try {
|
|
14967
15119
|
await ensureDir(todosDir2);
|
|
14968
|
-
const files = await
|
|
15120
|
+
const files = await readdir11(todosDir2).catch(() => []);
|
|
14969
15121
|
const workspaces = [];
|
|
14970
15122
|
for (const file of files) {
|
|
14971
15123
|
if (typeof file !== "string") continue;
|
|
@@ -15080,7 +15232,7 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
|
15080
15232
|
try {
|
|
15081
15233
|
const { archivePath: archivePath2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports));
|
|
15082
15234
|
const { resolve: resolve78 } = await import("path");
|
|
15083
|
-
const { readFile:
|
|
15235
|
+
const { readFile: readFile53 } = await import("fs/promises");
|
|
15084
15236
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
15085
15237
|
const workspace = getWorkspaceParam(req.params.workspace);
|
|
15086
15238
|
const checklist = await readChecklist(todosDir2, workspace);
|
|
@@ -15099,7 +15251,7 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
|
15099
15251
|
await ensureDir(resolve78(todosDir2, "archive"));
|
|
15100
15252
|
let archContent = "";
|
|
15101
15253
|
if (await fileExists(archFile)) {
|
|
15102
|
-
archContent = await
|
|
15254
|
+
archContent = await readFile53(archFile, "utf-8");
|
|
15103
15255
|
archContent = archContent.trimEnd() + "\n\n";
|
|
15104
15256
|
} else {
|
|
15105
15257
|
archContent = `---
|
|
@@ -15381,7 +15533,7 @@ workspace: ${workspace}
|
|
|
15381
15533
|
const { readConfig: readConfig3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
15382
15534
|
const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
15383
15535
|
const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
15384
|
-
const { readFile:
|
|
15536
|
+
const { readFile: readFile53 } = await import("fs/promises");
|
|
15385
15537
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
15386
15538
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
15387
15539
|
let assignmentRef;
|
|
@@ -15402,7 +15554,7 @@ workspace: ${workspace}
|
|
|
15402
15554
|
}
|
|
15403
15555
|
const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
|
|
15404
15556
|
if (!await fileExists2(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
|
|
15405
|
-
let content = await
|
|
15557
|
+
let content = await readFile53(assignmentMdPath2, "utf-8");
|
|
15406
15558
|
content = appendTodosToAssignmentBody2(
|
|
15407
15559
|
content,
|
|
15408
15560
|
items.map((it) => ({
|
|
@@ -15590,9 +15742,10 @@ init_fs();
|
|
|
15590
15742
|
init_paths();
|
|
15591
15743
|
init_slug();
|
|
15592
15744
|
import { Router as Router13 } from "express";
|
|
15593
|
-
import { mkdir as mkdir3, readFile as
|
|
15745
|
+
import { mkdir as mkdir3, readFile as readFile18, rename as rename6 } from "fs/promises";
|
|
15594
15746
|
import { resolve as resolve27, dirname as dirname7 } from "path";
|
|
15595
15747
|
init_promote_todos();
|
|
15748
|
+
init_api();
|
|
15596
15749
|
var WORKSPACE_REGEX2 = /^[a-z0-9_][a-z0-9-]*$/;
|
|
15597
15750
|
function touchItem4(item) {
|
|
15598
15751
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -15641,6 +15794,7 @@ function notFound(res, slug) {
|
|
|
15641
15794
|
}
|
|
15642
15795
|
function createProjectTodosRouter(projectsDir2, broadcast, workspaceTodosDir) {
|
|
15643
15796
|
const router = Router13({ mergeParams: true });
|
|
15797
|
+
installRecordsInvalidation(router);
|
|
15644
15798
|
function broadcastUpdate(projectSlug) {
|
|
15645
15799
|
broadcast({ type: "todos-updated", projectSlug, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
15646
15800
|
}
|
|
@@ -15813,7 +15967,7 @@ function createProjectTodosRouter(projectsDir2, broadcast, workspaceTodosDir) {
|
|
|
15813
15967
|
const archFile = archivePath(todosDir2, slug, checklist.archiveInterval);
|
|
15814
15968
|
let archContent = "";
|
|
15815
15969
|
if (await fileExists(archFile)) {
|
|
15816
|
-
archContent = await
|
|
15970
|
+
archContent = await readFile18(archFile, "utf-8");
|
|
15817
15971
|
archContent = archContent.trimEnd() + "\n\n";
|
|
15818
15972
|
} else {
|
|
15819
15973
|
archContent = `---
|
|
@@ -16270,7 +16424,7 @@ workspace: ${slug}
|
|
|
16270
16424
|
}
|
|
16271
16425
|
const assignmentMdPath2 = resolve27(assignmentDir, "assignment.md");
|
|
16272
16426
|
if (!await fileExists(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
|
|
16273
|
-
let content = await
|
|
16427
|
+
let content = await readFile18(assignmentMdPath2, "utf-8");
|
|
16274
16428
|
content = appendTodosToAssignmentBody2(
|
|
16275
16429
|
content,
|
|
16276
16430
|
items.map((it) => ({
|
|
@@ -16466,7 +16620,7 @@ workspace: ${slug}
|
|
|
16466
16620
|
|
|
16467
16621
|
// src/dashboard/api-bundles.ts
|
|
16468
16622
|
import { Router as Router14 } from "express";
|
|
16469
|
-
import { readdir as
|
|
16623
|
+
import { readdir as readdir12 } from "fs/promises";
|
|
16470
16624
|
|
|
16471
16625
|
// src/todos/bundle-parser.ts
|
|
16472
16626
|
init_parser();
|
|
@@ -16474,7 +16628,7 @@ init_fs();
|
|
|
16474
16628
|
init_paths();
|
|
16475
16629
|
init_parser2();
|
|
16476
16630
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
16477
|
-
import { readFile as
|
|
16631
|
+
import { readFile as readFile19 } from "fs/promises";
|
|
16478
16632
|
var BUNDLE_ID_REGEX = /^[a-f0-9]{4}$/;
|
|
16479
16633
|
var SCOPE_VALUES = /* @__PURE__ */ new Set(["workspace", "project", "global"]);
|
|
16480
16634
|
var SCOPE_ID_REGEX = /^[a-z0-9_][a-z0-9_-]*$/;
|
|
@@ -16577,7 +16731,7 @@ ${lines}
|
|
|
16577
16731
|
async function readBundles(todosDir2) {
|
|
16578
16732
|
const path = bundlesPath(todosDir2);
|
|
16579
16733
|
if (!await fileExists(path)) return [];
|
|
16580
|
-
const content = await
|
|
16734
|
+
const content = await readFile19(path, "utf-8");
|
|
16581
16735
|
return parseBundles(content).bundles;
|
|
16582
16736
|
}
|
|
16583
16737
|
async function writeBundles(todosDir2, bundles) {
|
|
@@ -16630,7 +16784,7 @@ function createBundlesRouter(todosDir2, broadcast) {
|
|
|
16630
16784
|
try {
|
|
16631
16785
|
await ensureDir(todosDir2);
|
|
16632
16786
|
const bundles = await readBundles(todosDir2);
|
|
16633
|
-
const workspaceFiles = await
|
|
16787
|
+
const workspaceFiles = await readdir12(todosDir2).catch(() => []);
|
|
16634
16788
|
const itemsByKey = /* @__PURE__ */ new Map();
|
|
16635
16789
|
for (const f of workspaceFiles) {
|
|
16636
16790
|
if (typeof f !== "string") continue;
|
|
@@ -16744,6 +16898,8 @@ function createProjectBundlesRouter(projectsDir2, broadcast) {
|
|
|
16744
16898
|
|
|
16745
16899
|
// src/dashboard/api-backup.ts
|
|
16746
16900
|
init_config2();
|
|
16901
|
+
init_api();
|
|
16902
|
+
init_scanner();
|
|
16747
16903
|
import { Router as Router16 } from "express";
|
|
16748
16904
|
|
|
16749
16905
|
// src/utils/github-backup.ts
|
|
@@ -16752,7 +16908,7 @@ init_fs();
|
|
|
16752
16908
|
init_config2();
|
|
16753
16909
|
import { execFile as execFile2 } from "child_process";
|
|
16754
16910
|
import { promisify as promisify2 } from "util";
|
|
16755
|
-
import { cp, mkdtemp, rm as rm3, readFile as
|
|
16911
|
+
import { cp, mkdtemp, rm as rm3, readFile as readFile20, writeFile as writeFile5, unlink as unlink5, stat, open as open2, rename as rename7 } from "fs/promises";
|
|
16756
16912
|
import { resolve as resolve29, join as join2 } from "path";
|
|
16757
16913
|
import { tmpdir } from "os";
|
|
16758
16914
|
var exec2 = promisify2(execFile2);
|
|
@@ -16813,7 +16969,7 @@ async function acquireLock() {
|
|
|
16813
16969
|
return lockPath;
|
|
16814
16970
|
} catch (err2) {
|
|
16815
16971
|
if (err2.code === "EEXIST") {
|
|
16816
|
-
const pid = await
|
|
16972
|
+
const pid = await readFile20(lockPath, "utf-8").catch(() => "");
|
|
16817
16973
|
throw new Error(
|
|
16818
16974
|
`Backup operation already in progress (lock file at ${lockPath}, pid ${pid.trim() || "unknown"}). If stale, delete the file and retry.`
|
|
16819
16975
|
);
|
|
@@ -16860,7 +17016,7 @@ function resolveCategoriesStrict(csv) {
|
|
|
16860
17016
|
return parseCategoriesStrict(parts);
|
|
16861
17017
|
}
|
|
16862
17018
|
async function readSanitizedConfig(configPath) {
|
|
16863
|
-
const content = await
|
|
17019
|
+
const content = await readFile20(configPath, "utf-8");
|
|
16864
17020
|
return content.replace(/^(\s*lastBackup:\s*).*$/m, "$1null").replace(/^(\s*lastRestore:\s*).*$/m, "$1null");
|
|
16865
17021
|
}
|
|
16866
17022
|
async function backupToGithub(overrides) {
|
|
@@ -17142,6 +17298,9 @@ function createBackupRouter() {
|
|
|
17142
17298
|
success: false,
|
|
17143
17299
|
error: error instanceof Error ? error.message : String(error)
|
|
17144
17300
|
});
|
|
17301
|
+
} finally {
|
|
17302
|
+
invalidateRecordsCache();
|
|
17303
|
+
clearScanCache();
|
|
17145
17304
|
}
|
|
17146
17305
|
});
|
|
17147
17306
|
return router;
|
|
@@ -17380,6 +17539,9 @@ function createDashboardServer(options) {
|
|
|
17380
17539
|
});
|
|
17381
17540
|
});
|
|
17382
17541
|
function broadcast(message) {
|
|
17542
|
+
if (message.type === "project-updated" || message.type === "assignment-updated") {
|
|
17543
|
+
invalidateRecordsCache();
|
|
17544
|
+
}
|
|
17383
17545
|
const data = JSON.stringify(message);
|
|
17384
17546
|
for (const client of clients) {
|
|
17385
17547
|
if (client.readyState === WebSocket.OPEN) {
|
|
@@ -18248,7 +18410,7 @@ init_config2();
|
|
|
18248
18410
|
init_frontmatter();
|
|
18249
18411
|
init_timestamp();
|
|
18250
18412
|
import { resolve as resolve33 } from "path";
|
|
18251
|
-
import { readdir as
|
|
18413
|
+
import { readdir as readdir13, readFile as readFile21 } from "fs/promises";
|
|
18252
18414
|
var PROMOTABLE_STATUSES = /* @__PURE__ */ new Set(["pending"]);
|
|
18253
18415
|
function objectiveIsFleshedOut(content) {
|
|
18254
18416
|
const match = content.match(/##\s+Objective\s*\n([\s\S]*?)(?=\n##\s+|$)/);
|
|
@@ -18266,7 +18428,7 @@ async function collectCandidates(baseDirs) {
|
|
|
18266
18428
|
const candidates = [];
|
|
18267
18429
|
for (const baseDir of baseDirs) {
|
|
18268
18430
|
if (!await fileExists(baseDir)) continue;
|
|
18269
|
-
const projects = await
|
|
18431
|
+
const projects = await readdir13(baseDir, { withFileTypes: true });
|
|
18270
18432
|
for (const m of projects) {
|
|
18271
18433
|
if (!m.isDirectory()) continue;
|
|
18272
18434
|
if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
|
|
@@ -18274,7 +18436,7 @@ async function collectCandidates(baseDirs) {
|
|
|
18274
18436
|
if (await fileExists(directAssignmentMd)) {
|
|
18275
18437
|
const fm = await parseSafe(directAssignmentMd);
|
|
18276
18438
|
if (fm && PROMOTABLE_STATUSES.has(fm.status)) {
|
|
18277
|
-
const content = await
|
|
18439
|
+
const content = await readFile21(directAssignmentMd, "utf-8");
|
|
18278
18440
|
if (objectiveIsFleshedOut(content) && hasAcceptanceCriteria(content)) {
|
|
18279
18441
|
candidates.push({
|
|
18280
18442
|
projectSlug: null,
|
|
@@ -18289,7 +18451,7 @@ async function collectCandidates(baseDirs) {
|
|
|
18289
18451
|
}
|
|
18290
18452
|
const assignmentsDir2 = resolve33(baseDir, m.name, "assignments");
|
|
18291
18453
|
if (!await fileExists(assignmentsDir2)) continue;
|
|
18292
|
-
const entries = await
|
|
18454
|
+
const entries = await readdir13(assignmentsDir2, { withFileTypes: true });
|
|
18293
18455
|
for (const a of entries) {
|
|
18294
18456
|
if (!a.isDirectory()) continue;
|
|
18295
18457
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
@@ -18297,7 +18459,7 @@ async function collectCandidates(baseDirs) {
|
|
|
18297
18459
|
if (!await fileExists(assignmentMd)) continue;
|
|
18298
18460
|
const fm = await parseSafe(assignmentMd);
|
|
18299
18461
|
if (!fm || !PROMOTABLE_STATUSES.has(fm.status)) continue;
|
|
18300
|
-
const content = await
|
|
18462
|
+
const content = await readFile21(assignmentMd, "utf-8");
|
|
18301
18463
|
if (!objectiveIsFleshedOut(content)) continue;
|
|
18302
18464
|
if (!hasAcceptanceCriteria(content)) continue;
|
|
18303
18465
|
candidates.push({
|
|
@@ -18314,7 +18476,7 @@ async function collectCandidates(baseDirs) {
|
|
|
18314
18476
|
}
|
|
18315
18477
|
async function parseSafe(path) {
|
|
18316
18478
|
try {
|
|
18317
|
-
const content = await
|
|
18479
|
+
const content = await readFile21(path, "utf-8");
|
|
18318
18480
|
return parseAssignmentFrontmatter(content);
|
|
18319
18481
|
} catch {
|
|
18320
18482
|
return null;
|
|
@@ -18343,7 +18505,7 @@ async function migrateStatusesCommand(options) {
|
|
|
18343
18505
|
const now = nowTimestamp();
|
|
18344
18506
|
let migrated = 0;
|
|
18345
18507
|
for (const c2 of candidates) {
|
|
18346
|
-
const content = await
|
|
18508
|
+
const content = await readFile21(c2.assignmentMd, "utf-8");
|
|
18347
18509
|
const updated = updateAssignmentFile(content, {
|
|
18348
18510
|
status: c2.toStatus,
|
|
18349
18511
|
updated: now
|
|
@@ -18398,10 +18560,10 @@ init_config2();
|
|
|
18398
18560
|
init_fs();
|
|
18399
18561
|
import {
|
|
18400
18562
|
cp as cp2,
|
|
18401
|
-
readdir as
|
|
18563
|
+
readdir as readdir14,
|
|
18402
18564
|
symlink,
|
|
18403
18565
|
lstat,
|
|
18404
|
-
readFile as
|
|
18566
|
+
readFile as readFile22,
|
|
18405
18567
|
readlink,
|
|
18406
18568
|
rename as rename8,
|
|
18407
18569
|
rm as rm4,
|
|
@@ -18462,7 +18624,7 @@ function getInstallMarkerPath(targetDir) {
|
|
|
18462
18624
|
return resolve35(targetDir, INSTALL_MARKER_FILENAME);
|
|
18463
18625
|
}
|
|
18464
18626
|
async function readPackageManifest(packageRoot) {
|
|
18465
|
-
const raw = await
|
|
18627
|
+
const raw = await readFile22(resolve35(packageRoot, "package.json"), "utf-8");
|
|
18466
18628
|
return JSON.parse(raw);
|
|
18467
18629
|
}
|
|
18468
18630
|
async function readJsonFileIfExists(pathValue) {
|
|
@@ -18470,7 +18632,7 @@ async function readJsonFileIfExists(pathValue) {
|
|
|
18470
18632
|
return null;
|
|
18471
18633
|
}
|
|
18472
18634
|
try {
|
|
18473
|
-
const raw = await
|
|
18635
|
+
const raw = await readFile22(pathValue, "utf-8");
|
|
18474
18636
|
return JSON.parse(raw);
|
|
18475
18637
|
} catch {
|
|
18476
18638
|
return null;
|
|
@@ -18486,7 +18648,7 @@ async function readPluginManifestName(targetDir, pluginKind) {
|
|
|
18486
18648
|
if (!await fileExists(manifestPath)) {
|
|
18487
18649
|
return void 0;
|
|
18488
18650
|
}
|
|
18489
|
-
const raw = await
|
|
18651
|
+
const raw = await readFile22(manifestPath, "utf-8");
|
|
18490
18652
|
const parsed = JSON.parse(raw);
|
|
18491
18653
|
return parsed.name;
|
|
18492
18654
|
}
|
|
@@ -18496,7 +18658,7 @@ async function readInstallMetadata(targetDir) {
|
|
|
18496
18658
|
return null;
|
|
18497
18659
|
}
|
|
18498
18660
|
try {
|
|
18499
|
-
const raw = await
|
|
18661
|
+
const raw = await readFile22(markerPath, "utf-8");
|
|
18500
18662
|
return JSON.parse(raw);
|
|
18501
18663
|
} catch {
|
|
18502
18664
|
return null;
|
|
@@ -18619,7 +18781,7 @@ async function writeClaudeMarketplaceFile(manifestPath, marketplace) {
|
|
|
18619
18781
|
}
|
|
18620
18782
|
if (await fileExists(manifestPath)) {
|
|
18621
18783
|
try {
|
|
18622
|
-
const prev = await
|
|
18784
|
+
const prev = await readFile22(manifestPath, "utf-8");
|
|
18623
18785
|
JSON.parse(prev);
|
|
18624
18786
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
18625
18787
|
await writeFile7(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
|
|
@@ -18692,7 +18854,7 @@ async function listClaudeMarketplaceCandidates() {
|
|
|
18692
18854
|
const [knownMarketplaces, activeMarketplaceNames, entries] = await Promise.all([
|
|
18693
18855
|
readKnownClaudeMarketplaceRecords(),
|
|
18694
18856
|
readInstalledClaudeMarketplaceNames(),
|
|
18695
|
-
|
|
18857
|
+
readdir14(rootDir, { withFileTypes: true })
|
|
18696
18858
|
]);
|
|
18697
18859
|
const candidates = [];
|
|
18698
18860
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -18767,7 +18929,7 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
|
|
|
18767
18929
|
const manifestPath = getClaudeKnownMarketplacesPath();
|
|
18768
18930
|
let existing = {};
|
|
18769
18931
|
if (await fileExists(manifestPath)) {
|
|
18770
|
-
const raw = await
|
|
18932
|
+
const raw = await readFile22(manifestPath, "utf-8");
|
|
18771
18933
|
try {
|
|
18772
18934
|
const parsed = JSON.parse(raw);
|
|
18773
18935
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
@@ -18794,7 +18956,7 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
|
|
|
18794
18956
|
existing[name].autoUpdate = true;
|
|
18795
18957
|
await ensureDir(dirname10(manifestPath));
|
|
18796
18958
|
if (await fileExists(manifestPath)) {
|
|
18797
|
-
const prev = await
|
|
18959
|
+
const prev = await readFile22(manifestPath, "utf-8");
|
|
18798
18960
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
18799
18961
|
await writeFile7(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
|
|
18800
18962
|
}
|
|
@@ -18812,7 +18974,7 @@ async function setSyntaurPluginEnabled(options) {
|
|
|
18812
18974
|
const key = `syntaur@${options.marketplaceName}`;
|
|
18813
18975
|
let parsed = {};
|
|
18814
18976
|
if (await fileExists(settingsPath)) {
|
|
18815
|
-
const raw = await
|
|
18977
|
+
const raw = await readFile22(settingsPath, "utf-8");
|
|
18816
18978
|
try {
|
|
18817
18979
|
parsed = JSON.parse(raw);
|
|
18818
18980
|
} catch {
|
|
@@ -18831,7 +18993,7 @@ async function setSyntaurPluginEnabled(options) {
|
|
|
18831
18993
|
await ensureDir(dirname10(settingsPath));
|
|
18832
18994
|
if (await fileExists(settingsPath)) {
|
|
18833
18995
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
18834
|
-
const prev = await
|
|
18996
|
+
const prev = await readFile22(settingsPath, "utf-8");
|
|
18835
18997
|
await writeFile7(`${settingsPath}.bak-${stamp}`, prev, "utf-8");
|
|
18836
18998
|
}
|
|
18837
18999
|
const tmpPath = `${settingsPath}.tmp`;
|
|
@@ -19069,7 +19231,7 @@ async function readMarketplaceFile(marketplacePath) {
|
|
|
19069
19231
|
plugins: []
|
|
19070
19232
|
};
|
|
19071
19233
|
}
|
|
19072
|
-
const raw = await
|
|
19234
|
+
const raw = await readFile22(marketplacePath, "utf-8");
|
|
19073
19235
|
const parsed = JSON.parse(raw);
|
|
19074
19236
|
return {
|
|
19075
19237
|
name: parsed.name ?? "local",
|
|
@@ -19305,13 +19467,13 @@ async function textPrompt(question, defaultValue) {
|
|
|
19305
19467
|
|
|
19306
19468
|
// src/utils/install-skills.ts
|
|
19307
19469
|
init_fs();
|
|
19308
|
-
import { readFile as
|
|
19470
|
+
import { readFile as readFile24, readdir as readdir15, mkdir as mkdir4, copyFile, rm as rm5, lstat as lstat2 } from "fs/promises";
|
|
19309
19471
|
import { resolve as resolve37, relative as relative3, join as join3 } from "path";
|
|
19310
19472
|
import { homedir as homedir4 } from "os";
|
|
19311
19473
|
|
|
19312
19474
|
// src/utils/plugin-state.ts
|
|
19313
19475
|
init_fs();
|
|
19314
|
-
import { readFile as
|
|
19476
|
+
import { readFile as readFile23 } from "fs/promises";
|
|
19315
19477
|
import { resolve as resolve36 } from "path";
|
|
19316
19478
|
import { homedir as homedir3 } from "os";
|
|
19317
19479
|
function settingsPathFor(agent) {
|
|
@@ -19322,7 +19484,7 @@ async function readJsonOrNull(path) {
|
|
|
19322
19484
|
if (!path) return null;
|
|
19323
19485
|
if (!await fileExists(path)) return null;
|
|
19324
19486
|
try {
|
|
19325
|
-
const raw = await
|
|
19487
|
+
const raw = await readFile23(path, "utf-8");
|
|
19326
19488
|
return JSON.parse(raw);
|
|
19327
19489
|
} catch {
|
|
19328
19490
|
return null;
|
|
@@ -19365,7 +19527,8 @@ var KNOWN_SKILL_NAMES = [
|
|
|
19365
19527
|
"claim-resource",
|
|
19366
19528
|
"release-resource",
|
|
19367
19529
|
"extend-resource",
|
|
19368
|
-
"list-resources"
|
|
19530
|
+
"list-resources",
|
|
19531
|
+
"run-playbook"
|
|
19369
19532
|
];
|
|
19370
19533
|
var KNOWN_SKILLS = KNOWN_SKILL_NAMES;
|
|
19371
19534
|
async function getSkillsDir() {
|
|
@@ -19379,7 +19542,7 @@ function defaultSkillTargetDir(target) {
|
|
|
19379
19542
|
async function walkFiles(root) {
|
|
19380
19543
|
const out = [];
|
|
19381
19544
|
async function walk(dir) {
|
|
19382
|
-
const entries = await
|
|
19545
|
+
const entries = await readdir15(dir, { withFileTypes: true });
|
|
19383
19546
|
for (const entry of entries) {
|
|
19384
19547
|
const full = join3(dir, entry.name);
|
|
19385
19548
|
if (entry.isDirectory()) {
|
|
@@ -19394,7 +19557,7 @@ async function walkFiles(root) {
|
|
|
19394
19557
|
}
|
|
19395
19558
|
async function filesEqual(a, b) {
|
|
19396
19559
|
try {
|
|
19397
|
-
const [ba, bb] = await Promise.all([
|
|
19560
|
+
const [ba, bb] = await Promise.all([readFile24(a), readFile24(b)]);
|
|
19398
19561
|
if (ba.length !== bb.length) return false;
|
|
19399
19562
|
return ba.equals(bb);
|
|
19400
19563
|
} catch {
|
|
@@ -19403,7 +19566,7 @@ async function filesEqual(a, b) {
|
|
|
19403
19566
|
}
|
|
19404
19567
|
async function copyDir(srcDir, destDir) {
|
|
19405
19568
|
await mkdir4(destDir, { recursive: true });
|
|
19406
|
-
const entries = await
|
|
19569
|
+
const entries = await readdir15(srcDir, { withFileTypes: true });
|
|
19407
19570
|
for (const entry of entries) {
|
|
19408
19571
|
const src = join3(srcDir, entry.name);
|
|
19409
19572
|
const dest = join3(destDir, entry.name);
|
|
@@ -19457,7 +19620,7 @@ async function installSkillDir(srcDir, destDir, skillName, force) {
|
|
|
19457
19620
|
return { skill: skillName, status: "differs-preserved", targetPath: destDir };
|
|
19458
19621
|
}
|
|
19459
19622
|
async function discoverSkillNames(sourceDir) {
|
|
19460
|
-
const entries = await
|
|
19623
|
+
const entries = await readdir15(sourceDir, { withFileTypes: true });
|
|
19461
19624
|
const names = [];
|
|
19462
19625
|
for (const entry of entries) {
|
|
19463
19626
|
if (!entry.isDirectory()) continue;
|
|
@@ -19516,7 +19679,7 @@ async function uninstallSkills(options) {
|
|
|
19516
19679
|
if (await isSymlink(destDir)) continue;
|
|
19517
19680
|
const skillMd = join3(destDir, "SKILL.md");
|
|
19518
19681
|
if (!await fileExists(skillMd)) continue;
|
|
19519
|
-
const content = await
|
|
19682
|
+
const content = await readFile24(skillMd, "utf-8").catch(() => "");
|
|
19520
19683
|
const match = content.match(/^name:\s*(\S+)\s*$/m);
|
|
19521
19684
|
if (!match || match[1] !== skill) continue;
|
|
19522
19685
|
await rm5(destDir, { recursive: true, force: true });
|
|
@@ -19705,7 +19868,7 @@ async function installPluginCommand(options) {
|
|
|
19705
19868
|
// src/commands/install-statusline.ts
|
|
19706
19869
|
init_paths();
|
|
19707
19870
|
init_fs();
|
|
19708
|
-
import { readFile as
|
|
19871
|
+
import { readFile as readFile26, writeFile as writeFile9, copyFile as copyFile2, rm as rm6, stat as stat2, symlink as symlink2, unlink as unlink8, lstat as lstat3 } from "fs/promises";
|
|
19709
19872
|
import { resolve as resolve39, dirname as dirname12 } from "path";
|
|
19710
19873
|
import { homedir as homedir5 } from "os";
|
|
19711
19874
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
@@ -19713,7 +19876,7 @@ import { fileURLToPath as fileURLToPath4 } from "url";
|
|
|
19713
19876
|
// src/commands/configure-statusline.ts
|
|
19714
19877
|
init_paths();
|
|
19715
19878
|
init_fs();
|
|
19716
|
-
import { readFile as
|
|
19879
|
+
import { readFile as readFile25, writeFile as writeFile8 } from "fs/promises";
|
|
19717
19880
|
import { resolve as resolve38, dirname as dirname11 } from "path";
|
|
19718
19881
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
19719
19882
|
import { checkbox, input as input2, confirm } from "@inquirer/prompts";
|
|
@@ -19740,7 +19903,7 @@ function getConfigPath(installRoot) {
|
|
|
19740
19903
|
async function readConfig2(path) {
|
|
19741
19904
|
if (!await fileExists(path)) return null;
|
|
19742
19905
|
try {
|
|
19743
|
-
const raw = await
|
|
19906
|
+
const raw = await readFile25(path, "utf-8");
|
|
19744
19907
|
const parsed = JSON.parse(raw);
|
|
19745
19908
|
if (!parsed || typeof parsed !== "object") return null;
|
|
19746
19909
|
const segments = Array.isArray(parsed.segments) ? parsed.segments.filter(isSegmentName) : [];
|
|
@@ -19928,7 +20091,7 @@ function getPackageStatuslineSource() {
|
|
|
19928
20091
|
}
|
|
19929
20092
|
async function readSettingsJson(settingsPath) {
|
|
19930
20093
|
if (!await fileExists(settingsPath)) return {};
|
|
19931
|
-
const raw = await
|
|
20094
|
+
const raw = await readFile26(settingsPath, "utf-8");
|
|
19932
20095
|
if (raw.trim() === "") return {};
|
|
19933
20096
|
try {
|
|
19934
20097
|
const parsed = JSON.parse(raw);
|
|
@@ -20116,7 +20279,7 @@ async function uninstallStatuslineCommand(options = {}) {
|
|
|
20116
20279
|
let restored = null;
|
|
20117
20280
|
if (await fileExists(backupPath)) {
|
|
20118
20281
|
try {
|
|
20119
|
-
const raw = await
|
|
20282
|
+
const raw = await readFile26(backupPath, "utf-8");
|
|
20120
20283
|
const parsed = JSON.parse(raw);
|
|
20121
20284
|
const prev = parsed?.previousStatusLine;
|
|
20122
20285
|
if (prev && typeof prev === "object" && typeof prev.command === "string") {
|
|
@@ -21149,7 +21312,7 @@ function normalizeSlashes(p) {
|
|
|
21149
21312
|
}
|
|
21150
21313
|
function detectInstallKind(scriptUrl, opts = {}) {
|
|
21151
21314
|
const realpath3 = opts.realpath ?? realpathSync.native;
|
|
21152
|
-
const
|
|
21315
|
+
const readFile53 = opts.readFile ?? ((p) => readFileSync(p, "utf-8"));
|
|
21153
21316
|
const ua = opts.envUserAgent !== void 0 ? opts.envUserAgent : process.env.npm_config_user_agent ?? "";
|
|
21154
21317
|
const resolved = resolveScriptPath(scriptUrl, realpath3);
|
|
21155
21318
|
if (resolved === null) {
|
|
@@ -21170,7 +21333,7 @@ function detectInstallKind(scriptUrl, opts = {}) {
|
|
|
21170
21333
|
const pkgJsonPath = join4(dir, "package.json");
|
|
21171
21334
|
let raw;
|
|
21172
21335
|
try {
|
|
21173
|
-
raw =
|
|
21336
|
+
raw = readFile53(pkgJsonPath);
|
|
21174
21337
|
} catch {
|
|
21175
21338
|
const parent2 = dirname13(dir);
|
|
21176
21339
|
if (parent2 === dir) break;
|
|
@@ -21344,7 +21507,7 @@ init_config2();
|
|
|
21344
21507
|
init_paths();
|
|
21345
21508
|
init_fs();
|
|
21346
21509
|
import { resolve as resolve46, isAbsolute as isAbsolute7 } from "path";
|
|
21347
|
-
import { readFile as
|
|
21510
|
+
import { readFile as readFile27 } from "fs/promises";
|
|
21348
21511
|
import { select, confirm as confirm2, input as input3 } from "@inquirer/prompts";
|
|
21349
21512
|
async function browseCommand(options) {
|
|
21350
21513
|
const config = await readConfig();
|
|
@@ -21423,7 +21586,7 @@ async function ensureWorktree(opts) {
|
|
|
21423
21586
|
if (!await fileExists(assignmentPath)) {
|
|
21424
21587
|
return void 0;
|
|
21425
21588
|
}
|
|
21426
|
-
const content = await
|
|
21589
|
+
const content = await readFile27(assignmentPath, "utf-8");
|
|
21427
21590
|
const { parseAssignmentFrontmatter: parseAssignmentFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
|
|
21428
21591
|
const fm = parseAssignmentFrontmatter2(content);
|
|
21429
21592
|
const { workspace } = fm;
|
|
@@ -21557,7 +21720,7 @@ init_paths();
|
|
|
21557
21720
|
init_fs();
|
|
21558
21721
|
init_parser();
|
|
21559
21722
|
init_config2();
|
|
21560
|
-
import { readdir as
|
|
21723
|
+
import { readdir as readdir16, readFile as readFile28 } from "fs/promises";
|
|
21561
21724
|
import { resolve as resolve48 } from "path";
|
|
21562
21725
|
async function listPlaybooksCommand(options = {}) {
|
|
21563
21726
|
const dir = playbooksDir();
|
|
@@ -21567,14 +21730,14 @@ async function listPlaybooksCommand(options = {}) {
|
|
|
21567
21730
|
}
|
|
21568
21731
|
const config = await readConfig();
|
|
21569
21732
|
const disabledSet = new Set(config.playbooks.disabled);
|
|
21570
|
-
const entries = await
|
|
21733
|
+
const entries = await readdir16(dir, { withFileTypes: true });
|
|
21571
21734
|
const mdFiles = entries.filter(
|
|
21572
21735
|
(e) => e.isFile() && e.name.endsWith(".md") && !e.name.startsWith("_") && e.name !== "manifest.md"
|
|
21573
21736
|
);
|
|
21574
21737
|
const rows = [];
|
|
21575
21738
|
for (const entry of mdFiles) {
|
|
21576
21739
|
const filePath = resolve48(dir, entry.name);
|
|
21577
|
-
const raw = await
|
|
21740
|
+
const raw = await readFile28(filePath, "utf-8");
|
|
21578
21741
|
const parsed = parsePlaybook(raw);
|
|
21579
21742
|
const slug = parsed.slug || entry.name.replace(/\.md$/, "");
|
|
21580
21743
|
const disabled = disabledSet.has(slug);
|
|
@@ -21697,13 +21860,13 @@ init_fs();
|
|
|
21697
21860
|
init_config2();
|
|
21698
21861
|
init_slug();
|
|
21699
21862
|
import { Command as Command2 } from "commander";
|
|
21700
|
-
import { readFile as
|
|
21863
|
+
import { readFile as readFile30 } from "fs/promises";
|
|
21701
21864
|
import { resolve as resolve50 } from "path";
|
|
21702
21865
|
|
|
21703
21866
|
// src/commands/bundle.ts
|
|
21704
21867
|
init_paths();
|
|
21705
21868
|
import { Command } from "commander";
|
|
21706
|
-
import { mkdir as mkdir7, readFile as
|
|
21869
|
+
import { mkdir as mkdir7, readFile as readFile29, readdir as readdir17, rm as rm7, writeFile as writeFile11 } from "fs/promises";
|
|
21707
21870
|
import { resolve as resolve49 } from "path";
|
|
21708
21871
|
init_parser2();
|
|
21709
21872
|
init_fs();
|
|
@@ -21996,7 +22159,7 @@ bundleCommand.command("plan").description("Create or open the bundle's shared pl
|
|
|
21996
22159
|
}
|
|
21997
22160
|
const planDir = bundlePlanDir(sc.todosPath, sc.scopeId, id);
|
|
21998
22161
|
await ensureDir(planDir);
|
|
21999
|
-
const existing = (await
|
|
22162
|
+
const existing = (await readdir17(planDir).catch(() => [])).filter((f) => /^plan(?:-v\d+)?\.md$/.test(f));
|
|
22000
22163
|
const { target } = pickNextPlanFile(planDir, existing);
|
|
22001
22164
|
const checklist = await readChecklist(sc.todosPath, sc.checklistKey);
|
|
22002
22165
|
if (!await fileExists(target)) {
|
|
@@ -22065,8 +22228,8 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
22065
22228
|
}
|
|
22066
22229
|
const bundlesFilePath = bundlesPath(sc.todosPath);
|
|
22067
22230
|
const checklistFilePath = checklistPath(sc.todosPath, sc.checklistKey);
|
|
22068
|
-
const bundlesSnapshot = await fileExists(bundlesFilePath) ? await
|
|
22069
|
-
const checklistSnapshot = await fileExists(checklistFilePath) ? await
|
|
22231
|
+
const bundlesSnapshot = await fileExists(bundlesFilePath) ? await readFile29(bundlesFilePath, "utf-8") : null;
|
|
22232
|
+
const checklistSnapshot = await fileExists(checklistFilePath) ? await readFile29(checklistFilePath, "utf-8") : null;
|
|
22070
22233
|
const record = async () => {
|
|
22071
22234
|
bundle.branch = options.branch;
|
|
22072
22235
|
bundle.worktreePath = worktreePath;
|
|
@@ -22612,7 +22775,7 @@ todoCommand.command("archive").description("Archive completed todos and their lo
|
|
|
22612
22775
|
await ensureDir(resolve50(todosPath, "archive"));
|
|
22613
22776
|
let archContent = "";
|
|
22614
22777
|
if (await fileExists(archFile)) {
|
|
22615
|
-
archContent = await
|
|
22778
|
+
archContent = await readFile30(archFile, "utf-8");
|
|
22616
22779
|
archContent = archContent.trimEnd() + "\n\n";
|
|
22617
22780
|
} else {
|
|
22618
22781
|
archContent = `---
|
|
@@ -22789,12 +22952,12 @@ function describeScope(scope) {
|
|
|
22789
22952
|
}
|
|
22790
22953
|
async function injectPromotedTodos(assignmentDir, todos, scopeLabel) {
|
|
22791
22954
|
const { resolve: resolvePath2 } = await import("path");
|
|
22792
|
-
const { readFile:
|
|
22955
|
+
const { readFile: readFile53 } = await import("fs/promises");
|
|
22793
22956
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
22794
22957
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
22795
22958
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
22796
22959
|
const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
|
|
22797
|
-
let content = await
|
|
22960
|
+
let content = await readFile53(assignmentMdPath2, "utf-8");
|
|
22798
22961
|
content = appendTodosToAssignmentBody2(
|
|
22799
22962
|
content,
|
|
22800
22963
|
todos.map((t) => ({
|
|
@@ -22839,8 +23002,8 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
|
|
|
22839
23002
|
}
|
|
22840
23003
|
const planDir = todoPlanDir(todosPath, workspace, id);
|
|
22841
23004
|
await ensureDir(planDir);
|
|
22842
|
-
const { readdir:
|
|
22843
|
-
const existingFiles = (await
|
|
23005
|
+
const { readdir: readdir29 } = await import("fs/promises");
|
|
23006
|
+
const existingFiles = (await readdir29(planDir).catch(() => [])).filter(
|
|
22844
23007
|
(f) => /^plan(?:-v\d+)?\.md$/.test(f)
|
|
22845
23008
|
);
|
|
22846
23009
|
let target;
|
|
@@ -23044,12 +23207,12 @@ backupCommand.command("config").description("Show or update backup configuration
|
|
|
23044
23207
|
|
|
23045
23208
|
// src/commands/doctor.ts
|
|
23046
23209
|
import { Command as Command4 } from "commander";
|
|
23047
|
-
import { readFile as
|
|
23210
|
+
import { readFile as readFile38 } from "fs/promises";
|
|
23048
23211
|
import { isAbsolute as isAbsolute10, resolve as resolve62 } from "path";
|
|
23049
23212
|
|
|
23050
23213
|
// src/utils/doctor/index.ts
|
|
23051
23214
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
23052
|
-
import { readFile as
|
|
23215
|
+
import { readFile as readFile37 } from "fs/promises";
|
|
23053
23216
|
import { dirname as dirname17, join as join8 } from "path";
|
|
23054
23217
|
|
|
23055
23218
|
// src/utils/doctor/context.ts
|
|
@@ -23096,7 +23259,7 @@ function closeCheckContext(ctx) {
|
|
|
23096
23259
|
init_fs();
|
|
23097
23260
|
init_paths();
|
|
23098
23261
|
import { resolve as resolve52, isAbsolute as isAbsolute8 } from "path";
|
|
23099
|
-
import { readFile as
|
|
23262
|
+
import { readFile as readFile31, stat as stat3 } from "fs/promises";
|
|
23100
23263
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
23101
23264
|
import { dirname as dirname15, join as join6 } from "path";
|
|
23102
23265
|
var CATEGORY = "env";
|
|
@@ -23153,7 +23316,7 @@ var configValid = {
|
|
|
23153
23316
|
autoFixable: false
|
|
23154
23317
|
};
|
|
23155
23318
|
}
|
|
23156
|
-
const content = await
|
|
23319
|
+
const content = await readFile31(configPath, "utf-8");
|
|
23157
23320
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
23158
23321
|
if (!fmMatch || fmMatch[1].trim() === "") {
|
|
23159
23322
|
return {
|
|
@@ -23433,7 +23596,7 @@ async function readLocalPkg() {
|
|
|
23433
23596
|
for (let i = 0; i < 6; i++) {
|
|
23434
23597
|
const candidate = join6(dir, "package.json");
|
|
23435
23598
|
try {
|
|
23436
|
-
const text = await
|
|
23599
|
+
const text = await readFile31(candidate, "utf-8");
|
|
23437
23600
|
return JSON.parse(text);
|
|
23438
23601
|
} catch {
|
|
23439
23602
|
dir = dirname15(dir);
|
|
@@ -23486,7 +23649,7 @@ function versionGte(a, b) {
|
|
|
23486
23649
|
// src/utils/doctor/checks/structure.ts
|
|
23487
23650
|
init_fs();
|
|
23488
23651
|
import { resolve as resolve53 } from "path";
|
|
23489
|
-
import { readdir as
|
|
23652
|
+
import { readdir as readdir18, stat as stat4 } from "fs/promises";
|
|
23490
23653
|
var CATEGORY2 = "structure";
|
|
23491
23654
|
var KNOWN_TOP_LEVEL = /* @__PURE__ */ new Set([
|
|
23492
23655
|
"projects",
|
|
@@ -23617,7 +23780,7 @@ var knownFilesRecognized = {
|
|
|
23617
23780
|
category: CATEGORY2,
|
|
23618
23781
|
title: "No unexpected top-level entries under ~/.syntaur/",
|
|
23619
23782
|
async run(ctx) {
|
|
23620
|
-
const entries = await
|
|
23783
|
+
const entries = await readdir18(ctx.syntaurRoot, { withFileTypes: true });
|
|
23621
23784
|
const unexpected = [];
|
|
23622
23785
|
for (const e of entries) {
|
|
23623
23786
|
if (e.name.startsWith(".")) continue;
|
|
@@ -23661,7 +23824,7 @@ function pass2(check) {
|
|
|
23661
23824
|
// src/utils/doctor/checks/project.ts
|
|
23662
23825
|
init_fs();
|
|
23663
23826
|
import { resolve as resolve54 } from "path";
|
|
23664
|
-
import { readdir as
|
|
23827
|
+
import { readdir as readdir19, stat as stat5 } from "fs/promises";
|
|
23665
23828
|
var CATEGORY3 = "project";
|
|
23666
23829
|
var REQUIRED_PROJECT_FILES = [
|
|
23667
23830
|
"project.md",
|
|
@@ -23685,7 +23848,7 @@ var PROJECT_MARKERS = ["project.md", "manifest.md", "assignments"];
|
|
|
23685
23848
|
async function listProjects2(ctx) {
|
|
23686
23849
|
const dir = ctx.config.defaultProjectDir;
|
|
23687
23850
|
if (!await fileExists(dir)) return [];
|
|
23688
|
-
const entries = await
|
|
23851
|
+
const entries = await readdir19(dir, { withFileTypes: true });
|
|
23689
23852
|
const result = [];
|
|
23690
23853
|
for (const e of entries) {
|
|
23691
23854
|
if (!e.isDirectory()) continue;
|
|
@@ -23779,7 +23942,7 @@ var orphanFiles = {
|
|
|
23779
23942
|
const projects = await listProjects2(ctx);
|
|
23780
23943
|
const results = [];
|
|
23781
23944
|
for (const projectDir of projects) {
|
|
23782
|
-
const entries = await
|
|
23945
|
+
const entries = await readdir19(projectDir, { withFileTypes: true });
|
|
23783
23946
|
const orphans = [];
|
|
23784
23947
|
for (const e of entries) {
|
|
23785
23948
|
if (e.name.startsWith(".")) continue;
|
|
@@ -23809,7 +23972,7 @@ async function newestAssignmentMtime(projectDir) {
|
|
|
23809
23972
|
let newest = 0;
|
|
23810
23973
|
let entries;
|
|
23811
23974
|
try {
|
|
23812
|
-
entries = await
|
|
23975
|
+
entries = await readdir19(assignmentsRoot, { withFileTypes: true });
|
|
23813
23976
|
} catch {
|
|
23814
23977
|
return 0;
|
|
23815
23978
|
}
|
|
@@ -23840,7 +24003,7 @@ init_parser();
|
|
|
23840
24003
|
init_types();
|
|
23841
24004
|
init_paths();
|
|
23842
24005
|
import { resolve as resolve55 } from "path";
|
|
23843
|
-
import { readFile as
|
|
24006
|
+
import { readFile as readFile32, readdir as readdir20 } from "fs/promises";
|
|
23844
24007
|
var CATEGORY4 = "assignment";
|
|
23845
24008
|
var STATUSES_REQUIRING_HANDOFF = /* @__PURE__ */ new Set(["review", "completed"]);
|
|
23846
24009
|
var PRE_WORKSPACE_STATUSES = /* @__PURE__ */ new Set([
|
|
@@ -24188,7 +24351,7 @@ var draftMissingObjective = {
|
|
|
24188
24351
|
if (parsed.status !== "draft") continue;
|
|
24189
24352
|
let raw;
|
|
24190
24353
|
try {
|
|
24191
|
-
raw = await
|
|
24354
|
+
raw = await readFile32(path, "utf-8");
|
|
24192
24355
|
} catch {
|
|
24193
24356
|
continue;
|
|
24194
24357
|
}
|
|
@@ -24225,12 +24388,12 @@ var readyToImplementMissingPlan = {
|
|
|
24225
24388
|
const parsed = await parseSafe2(path);
|
|
24226
24389
|
if (!parsed) continue;
|
|
24227
24390
|
if (parsed.status !== "ready_to_implement") continue;
|
|
24228
|
-
const entries = await
|
|
24391
|
+
const entries = await readdir20(a.assignmentDir).catch(() => []);
|
|
24229
24392
|
const planFiles = entries.filter((f) => /^plan(?:-v\d+)?\.md$/i.test(f));
|
|
24230
24393
|
let hasPlanContent = false;
|
|
24231
24394
|
for (const f of planFiles) {
|
|
24232
24395
|
try {
|
|
24233
|
-
const c2 = await
|
|
24396
|
+
const c2 = await readFile32(resolve55(a.assignmentDir, f), "utf-8");
|
|
24234
24397
|
if (c2.trim().length > 0) {
|
|
24235
24398
|
hasPlanContent = true;
|
|
24236
24399
|
break;
|
|
@@ -24273,7 +24436,7 @@ var assignmentChecks = [
|
|
|
24273
24436
|
];
|
|
24274
24437
|
async function parseSafe2(path) {
|
|
24275
24438
|
try {
|
|
24276
|
-
const content = await
|
|
24439
|
+
const content = await readFile32(path, "utf-8");
|
|
24277
24440
|
return parseAssignmentFull(content);
|
|
24278
24441
|
} catch {
|
|
24279
24442
|
return null;
|
|
@@ -24434,7 +24597,7 @@ function skipped(check, reason) {
|
|
|
24434
24597
|
// src/utils/doctor/checks/integrations.ts
|
|
24435
24598
|
init_fs();
|
|
24436
24599
|
import { resolve as resolve57, dirname as dirname16, basename as basename5 } from "path";
|
|
24437
|
-
import { readdir as
|
|
24600
|
+
import { readdir as readdir21, readFile as readFile33 } from "fs/promises";
|
|
24438
24601
|
import { homedir as homedir7 } from "os";
|
|
24439
24602
|
var CATEGORY6 = "integrations";
|
|
24440
24603
|
var claudePluginLinked = {
|
|
@@ -24497,7 +24660,7 @@ var backupConfigured = {
|
|
|
24497
24660
|
if (ctx.config.backup?.repo) return pass6(this);
|
|
24498
24661
|
const projectsDir2 = ctx.config.defaultProjectDir;
|
|
24499
24662
|
if (!await fileExists(projectsDir2)) return skipped2(this, "no projects dir");
|
|
24500
|
-
const entries = await
|
|
24663
|
+
const entries = await readdir21(projectsDir2, { withFileTypes: true });
|
|
24501
24664
|
const hasProjects = entries.some((e) => e.isDirectory() && !e.name.startsWith(".") && !e.name.startsWith("_"));
|
|
24502
24665
|
if (!hasProjects) return skipped2(this, "no projects yet");
|
|
24503
24666
|
return {
|
|
@@ -24519,7 +24682,7 @@ async function readKnownMarketplaces() {
|
|
|
24519
24682
|
const path = resolve57(homedir7(), ".claude", "plugins", "known_marketplaces.json");
|
|
24520
24683
|
if (!await fileExists(path)) return {};
|
|
24521
24684
|
try {
|
|
24522
|
-
const raw = await
|
|
24685
|
+
const raw = await readFile33(path, "utf-8");
|
|
24523
24686
|
return JSON.parse(raw);
|
|
24524
24687
|
} catch {
|
|
24525
24688
|
return {};
|
|
@@ -24572,7 +24735,7 @@ var claudeMarketplaceRegistered = {
|
|
|
24572
24735
|
}
|
|
24573
24736
|
let parsed = {};
|
|
24574
24737
|
try {
|
|
24575
|
-
parsed = JSON.parse(await
|
|
24738
|
+
parsed = JSON.parse(await readFile33(marketplaceManifest, "utf-8"));
|
|
24576
24739
|
} catch {
|
|
24577
24740
|
return {
|
|
24578
24741
|
id: this.id,
|
|
@@ -24645,7 +24808,7 @@ init_fs();
|
|
|
24645
24808
|
init_parser();
|
|
24646
24809
|
init_types();
|
|
24647
24810
|
import { resolve as resolve58 } from "path";
|
|
24648
|
-
import { readFile as
|
|
24811
|
+
import { readFile as readFile34 } from "fs/promises";
|
|
24649
24812
|
var CATEGORY7 = "workspace";
|
|
24650
24813
|
var ASSIGNMENT_FIELDS = ["projectSlug", "assignmentSlug", "projectDir", "assignmentDir"];
|
|
24651
24814
|
var BUNDLE_FIELDS = ["bundleId", "bundleScope", "bundleScopeId"];
|
|
@@ -24671,7 +24834,7 @@ async function loadContext(ctx) {
|
|
|
24671
24834
|
return { data: null, path, exists: false, parseError: null };
|
|
24672
24835
|
}
|
|
24673
24836
|
try {
|
|
24674
|
-
const raw = await
|
|
24837
|
+
const raw = await readFile34(path, "utf-8");
|
|
24675
24838
|
return { data: JSON.parse(raw), path, exists: true, parseError: null };
|
|
24676
24839
|
} catch (err2) {
|
|
24677
24840
|
return {
|
|
@@ -24798,7 +24961,7 @@ var contextTerminal = {
|
|
|
24798
24961
|
const assignmentMd = resolve58(data.assignmentDir, "assignment.md");
|
|
24799
24962
|
if (!await fileExists(assignmentMd)) return skipped3(this, "assignment file missing");
|
|
24800
24963
|
try {
|
|
24801
|
-
const content = await
|
|
24964
|
+
const content = await readFile34(assignmentMd, "utf-8");
|
|
24802
24965
|
const parsed = parseAssignmentFull(content);
|
|
24803
24966
|
const terminal = terminalStatuses2(ctx);
|
|
24804
24967
|
if (terminal.has(parsed.status)) {
|
|
@@ -24944,7 +25107,7 @@ var agentChecks = [agentsResolvable];
|
|
|
24944
25107
|
// src/utils/doctor/checks/terminal.ts
|
|
24945
25108
|
init_config2();
|
|
24946
25109
|
import { spawnSync as spawnSync8 } from "child_process";
|
|
24947
|
-
import { readFile as
|
|
25110
|
+
import { readFile as readFile35 } from "fs/promises";
|
|
24948
25111
|
import { resolve as resolve59 } from "path";
|
|
24949
25112
|
init_paths();
|
|
24950
25113
|
init_fs();
|
|
@@ -24952,7 +25115,7 @@ var CATEGORY9 = "terminal";
|
|
|
24952
25115
|
async function readRawTerminalKey() {
|
|
24953
25116
|
const configPath = resolve59(syntaurRoot(), "config.md");
|
|
24954
25117
|
if (!await fileExists(configPath)) return null;
|
|
24955
|
-
const content = await
|
|
25118
|
+
const content = await readFile35(configPath, "utf-8");
|
|
24956
25119
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
24957
25120
|
if (!fmMatch) return null;
|
|
24958
25121
|
const line = fmMatch[1].split("\n").find((l) => /^terminal:\s*/.test(l));
|
|
@@ -25103,7 +25266,7 @@ var terminalChecks = [
|
|
|
25103
25266
|
// src/utils/doctor/checks/skills.ts
|
|
25104
25267
|
init_fs();
|
|
25105
25268
|
import { resolve as resolve60, join as join7 } from "path";
|
|
25106
|
-
import { readdir as
|
|
25269
|
+
import { readdir as readdir22, readFile as readFile36, lstat as lstat4 } from "fs/promises";
|
|
25107
25270
|
import { homedir as homedir8 } from "os";
|
|
25108
25271
|
var CATEGORY10 = "skills";
|
|
25109
25272
|
var skillTargets = [
|
|
@@ -25123,7 +25286,7 @@ var skillsDedupCheck = {
|
|
|
25123
25286
|
const present = [];
|
|
25124
25287
|
let entries;
|
|
25125
25288
|
try {
|
|
25126
|
-
entries = await
|
|
25289
|
+
entries = await readdir22(dir, { withFileTypes: true });
|
|
25127
25290
|
} catch {
|
|
25128
25291
|
continue;
|
|
25129
25292
|
}
|
|
@@ -25132,7 +25295,7 @@ var skillsDedupCheck = {
|
|
|
25132
25295
|
if (!KNOWN_SKILLS.includes(entry.name)) continue;
|
|
25133
25296
|
const skillMd = join7(dir, entry.name, "SKILL.md");
|
|
25134
25297
|
if (!await fileExists(skillMd)) continue;
|
|
25135
|
-
const content = await
|
|
25298
|
+
const content = await readFile36(skillMd, "utf-8").catch(() => "");
|
|
25136
25299
|
const match = content.match(/^name:\s*(\S+)\s*$/m);
|
|
25137
25300
|
if (!match || match[1] !== entry.name) continue;
|
|
25138
25301
|
let isSymlink2 = false;
|
|
@@ -25184,7 +25347,7 @@ var skillsChecks = [skillsDedupCheck];
|
|
|
25184
25347
|
init_fs();
|
|
25185
25348
|
init_paths();
|
|
25186
25349
|
import { resolve as resolve61 } from "path";
|
|
25187
|
-
import { readdir as
|
|
25350
|
+
import { readdir as readdir23 } from "fs/promises";
|
|
25188
25351
|
import { spawnSync as spawnSync9 } from "child_process";
|
|
25189
25352
|
init_parser2();
|
|
25190
25353
|
var CATEGORY11 = "bundles";
|
|
@@ -25192,7 +25355,7 @@ async function listScopes(ctx) {
|
|
|
25192
25355
|
const out = [];
|
|
25193
25356
|
const td = todosDir();
|
|
25194
25357
|
if (await fileExists(td)) {
|
|
25195
|
-
const entries = await
|
|
25358
|
+
const entries = await readdir23(td).catch(() => []);
|
|
25196
25359
|
for (const f of entries) {
|
|
25197
25360
|
if (typeof f !== "string") continue;
|
|
25198
25361
|
if (!f.endsWith(".md") || f.endsWith("-log.md")) continue;
|
|
@@ -25208,7 +25371,7 @@ async function listScopes(ctx) {
|
|
|
25208
25371
|
}
|
|
25209
25372
|
}
|
|
25210
25373
|
if (await fileExists(ctx.config.defaultProjectDir)) {
|
|
25211
|
-
const projectEntries = await
|
|
25374
|
+
const projectEntries = await readdir23(ctx.config.defaultProjectDir, { withFileTypes: true }).catch(() => []);
|
|
25212
25375
|
for (const e of projectEntries) {
|
|
25213
25376
|
if (!e.isDirectory()) continue;
|
|
25214
25377
|
const slug = e.name;
|
|
@@ -25504,7 +25667,7 @@ async function readVersion() {
|
|
|
25504
25667
|
let dir = dirname17(here);
|
|
25505
25668
|
for (let i = 0; i < 6; i++) {
|
|
25506
25669
|
try {
|
|
25507
|
-
const raw = await
|
|
25670
|
+
const raw = await readFile37(join8(dir, "package.json"), "utf-8");
|
|
25508
25671
|
const parsed = JSON.parse(raw);
|
|
25509
25672
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
25510
25673
|
} catch {
|
|
@@ -25614,7 +25777,7 @@ async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
|
|
|
25614
25777
|
}
|
|
25615
25778
|
let content;
|
|
25616
25779
|
try {
|
|
25617
|
-
content = await
|
|
25780
|
+
content = await readFile38(absolute, "utf-8");
|
|
25618
25781
|
} catch (err2) {
|
|
25619
25782
|
return {
|
|
25620
25783
|
ok: false,
|
|
@@ -25939,7 +26102,7 @@ init_timestamp();
|
|
|
25939
26102
|
init_assignment_resolver();
|
|
25940
26103
|
init_templates();
|
|
25941
26104
|
import { resolve as resolve63 } from "path";
|
|
25942
|
-
import { readFile as
|
|
26105
|
+
import { readFile as readFile39 } from "fs/promises";
|
|
25943
26106
|
function shortId() {
|
|
25944
26107
|
return generateId().split("-")[0];
|
|
25945
26108
|
}
|
|
@@ -25985,7 +26148,7 @@ async function commentCommand(target, text, options = {}) {
|
|
|
25985
26148
|
let currentContent;
|
|
25986
26149
|
let currentCount = 0;
|
|
25987
26150
|
if (await fileExists(commentsPath)) {
|
|
25988
|
-
currentContent = await
|
|
26151
|
+
currentContent = await readFile39(commentsPath, "utf-8");
|
|
25989
26152
|
const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
|
|
25990
26153
|
if (countMatch) currentCount = parseInt(countMatch[1], 10);
|
|
25991
26154
|
} else {
|
|
@@ -26031,7 +26194,7 @@ init_slug();
|
|
|
26031
26194
|
init_assignment_resolver();
|
|
26032
26195
|
init_parser();
|
|
26033
26196
|
import { resolve as resolve64 } from "path";
|
|
26034
|
-
import { readFile as
|
|
26197
|
+
import { readFile as readFile40 } from "fs/promises";
|
|
26035
26198
|
var AssignmentTargetError = class extends Error {
|
|
26036
26199
|
};
|
|
26037
26200
|
function classifyContext(ctx) {
|
|
@@ -26046,7 +26209,7 @@ async function readAssignmentFrontmatterId(assignmentDir) {
|
|
|
26046
26209
|
const path = resolve64(assignmentDir, "assignment.md");
|
|
26047
26210
|
if (!await fileExists(path)) return null;
|
|
26048
26211
|
try {
|
|
26049
|
-
const content = await
|
|
26212
|
+
const content = await readFile40(path, "utf-8");
|
|
26050
26213
|
const [fm] = extractFrontmatter(content);
|
|
26051
26214
|
return getField(fm, "id");
|
|
26052
26215
|
} catch {
|
|
@@ -26057,7 +26220,7 @@ async function readContextJson(cwd) {
|
|
|
26057
26220
|
const path = resolve64(cwd, ".syntaur", "context.json");
|
|
26058
26221
|
if (!await fileExists(path)) return null;
|
|
26059
26222
|
try {
|
|
26060
|
-
const raw = await
|
|
26223
|
+
const raw = await readFile40(path, "utf-8");
|
|
26061
26224
|
return JSON.parse(raw);
|
|
26062
26225
|
} catch {
|
|
26063
26226
|
return null;
|
|
@@ -26290,7 +26453,7 @@ async function captureScreenshot(mode) {
|
|
|
26290
26453
|
|
|
26291
26454
|
// src/utils/asciinema.ts
|
|
26292
26455
|
import { spawn as spawn6 } from "child_process";
|
|
26293
|
-
import { mkdtemp as mkdtemp3, readFile as
|
|
26456
|
+
import { mkdtemp as mkdtemp3, readFile as readFile41, rm as rm9 } from "fs/promises";
|
|
26294
26457
|
import { tmpdir as tmpdir4 } from "os";
|
|
26295
26458
|
import { join as join10 } from "path";
|
|
26296
26459
|
var SAFE_RE = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
@@ -26353,7 +26516,7 @@ async function captureAsciinema(opts) {
|
|
|
26353
26516
|
}
|
|
26354
26517
|
throw err2;
|
|
26355
26518
|
}
|
|
26356
|
-
const text = await
|
|
26519
|
+
const text = await readFile41(castPath, "utf8").catch(() => null);
|
|
26357
26520
|
if (text === null) {
|
|
26358
26521
|
throw new Error(
|
|
26359
26522
|
`asciinema produced no cast file at ${castPath} (exit ${exitCode}). Try running 'asciinema rec ${castPath}' directly to diagnose.`
|
|
@@ -26381,7 +26544,7 @@ async function captureAsciinema(opts) {
|
|
|
26381
26544
|
// src/utils/recording.ts
|
|
26382
26545
|
init_paths();
|
|
26383
26546
|
import { spawn as spawn7 } from "child_process";
|
|
26384
|
-
import { mkdir as mkdir8, mkdtemp as mkdtemp4, open as open3, readFile as
|
|
26547
|
+
import { mkdir as mkdir8, mkdtemp as mkdtemp4, open as open3, readFile as readFile42, rm as rm10, stat as stat7, unlink as unlink9, writeFile as writeFile12 } from "fs/promises";
|
|
26385
26548
|
import { tmpdir as tmpdir5 } from "os";
|
|
26386
26549
|
import { join as join11, resolve as resolve65 } from "path";
|
|
26387
26550
|
import { setTimeout as sleep } from "timers/promises";
|
|
@@ -26455,7 +26618,7 @@ async function acquirePidfile(pidfile) {
|
|
|
26455
26618
|
} catch (err2) {
|
|
26456
26619
|
if (err2.code !== "EEXIST") throw err2;
|
|
26457
26620
|
if (attempt === 1) throw err2;
|
|
26458
|
-
const existing = (await
|
|
26621
|
+
const existing = (await readFile42(pidfile, "utf-8").catch(() => "")).trim();
|
|
26459
26622
|
if (existing.startsWith(STARTING_SENTINEL_PREFIX)) {
|
|
26460
26623
|
const parentPidRaw = existing.slice(STARTING_SENTINEL_PREFIX.length);
|
|
26461
26624
|
const parentPid = Number.parseInt(parentPidRaw, 10);
|
|
@@ -26557,7 +26720,7 @@ async function startRecording(input4) {
|
|
|
26557
26720
|
logHandle = null;
|
|
26558
26721
|
if (warmupMs > 0) await sleep(warmupMs);
|
|
26559
26722
|
if (!await isProcessAlive(pid)) {
|
|
26560
|
-
const tail = await
|
|
26723
|
+
const tail = await readFile42(log, "utf-8").then((s) => s.split("\n").slice(-20).join("\n")).catch(() => "");
|
|
26561
26724
|
acquiredPid = null;
|
|
26562
26725
|
throw new Error(
|
|
26563
26726
|
`ffmpeg exited during startup \u2014 likely macOS Screen Recording permission missing. Grant access to your terminal in System Settings \u2192 Privacy & Security \u2192 Screen Recording, then retry. Log: ${log}
|
|
@@ -26614,7 +26777,7 @@ ${tail}`
|
|
|
26614
26777
|
async function stopRecording() {
|
|
26615
26778
|
const pidfile = pidfilePath();
|
|
26616
26779
|
const sidecar = sidecarPath();
|
|
26617
|
-
const pidRaw = await
|
|
26780
|
+
const pidRaw = await readFile42(pidfile, "utf-8").catch(() => null);
|
|
26618
26781
|
if (pidRaw === null) {
|
|
26619
26782
|
throw new Error(
|
|
26620
26783
|
`No active recording found (no pidfile at ${pidfile}). Did you run --start?`
|
|
@@ -26624,7 +26787,7 @@ async function stopRecording() {
|
|
|
26624
26787
|
if (!Number.isInteger(pid) || pid <= 0) {
|
|
26625
26788
|
throw new Error(`Pidfile at ${pidfile} is corrupt (got "${pidRaw}").`);
|
|
26626
26789
|
}
|
|
26627
|
-
const sidecarRaw = await
|
|
26790
|
+
const sidecarRaw = await readFile42(sidecar, "utf-8").catch(() => null);
|
|
26628
26791
|
if (sidecarRaw === null) {
|
|
26629
26792
|
throw new Error(
|
|
26630
26793
|
`No recording sidecar at ${sidecar}. The recording state is inconsistent \u2014 delete ${pidfile} and re-run --start.`
|
|
@@ -26757,7 +26920,7 @@ function listArtifactsByAssignment(assignmentId) {
|
|
|
26757
26920
|
|
|
26758
26921
|
// src/utils/transcribers/elevenlabs.ts
|
|
26759
26922
|
import { spawn as spawn8 } from "child_process";
|
|
26760
|
-
import { mkdtemp as mkdtemp5, readFile as
|
|
26923
|
+
import { mkdtemp as mkdtemp5, readFile as readFile43, rm as rm11 } from "fs/promises";
|
|
26761
26924
|
import { tmpdir as tmpdir6 } from "os";
|
|
26762
26925
|
import { join as join12 } from "path";
|
|
26763
26926
|
var SCRIBE_URL = "https://api.elevenlabs.io/v1/speech-to-text";
|
|
@@ -26821,7 +26984,7 @@ async function extractAudio(videoAbsPath, wavOut) {
|
|
|
26821
26984
|
throw new TranscribeFfmpegError(`ffmpeg failed (exit ${result.code}): ${tail}`);
|
|
26822
26985
|
}
|
|
26823
26986
|
async function callScribe(wavPath, apiKey, opts) {
|
|
26824
|
-
const audio = await
|
|
26987
|
+
const audio = await readFile43(wavPath);
|
|
26825
26988
|
const form = new FormData();
|
|
26826
26989
|
form.set("file", new Blob([new Uint8Array(audio)], { type: "audio/wav" }), "audio.wav");
|
|
26827
26990
|
form.set("model_id", "scribe_v1");
|
|
@@ -27286,7 +27449,7 @@ async function captureCommand(target, options = {}) {
|
|
|
27286
27449
|
|
|
27287
27450
|
// src/commands/proof.ts
|
|
27288
27451
|
import { Command as Command6 } from "commander";
|
|
27289
|
-
import { readFile as
|
|
27452
|
+
import { readFile as readFile44, writeFile as writeFile14, rename as rename9, stat as stat9 } from "fs/promises";
|
|
27290
27453
|
import { resolve as resolve68, relative as relative5, isAbsolute as isAbsolute11, dirname as dirname19 } from "path";
|
|
27291
27454
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
27292
27455
|
|
|
@@ -27531,7 +27694,7 @@ async function readAssignmentMeta(assignmentDir) {
|
|
|
27531
27694
|
if (!await fileExists(path)) {
|
|
27532
27695
|
return { title: "", body: "" };
|
|
27533
27696
|
}
|
|
27534
|
-
const content = await
|
|
27697
|
+
const content = await readFile44(path, "utf-8");
|
|
27535
27698
|
const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
27536
27699
|
let title = "";
|
|
27537
27700
|
if (fmMatch) {
|
|
@@ -27593,7 +27756,7 @@ async function loadInlineFiles(rows, assignmentDir) {
|
|
|
27593
27756
|
continue;
|
|
27594
27757
|
}
|
|
27595
27758
|
try {
|
|
27596
|
-
out.set(r.file_path, await
|
|
27759
|
+
out.set(r.file_path, await readFile44(abs, "utf-8"));
|
|
27597
27760
|
} catch {
|
|
27598
27761
|
out.set(r.file_path, null);
|
|
27599
27762
|
}
|
|
@@ -27612,7 +27775,7 @@ async function loadTranscriptSidecars(rows, assignmentDir) {
|
|
|
27612
27775
|
const st = await stat9(sidecar);
|
|
27613
27776
|
if (st.size > INLINE_TEXT_LIMIT_BYTES) continue;
|
|
27614
27777
|
try {
|
|
27615
|
-
out.set(r.id, await
|
|
27778
|
+
out.set(r.id, await readFile44(sidecar, "utf-8"));
|
|
27616
27779
|
} catch {
|
|
27617
27780
|
}
|
|
27618
27781
|
}
|
|
@@ -27791,13 +27954,13 @@ leaseCommand.command("claim").description("Claim an idle member of an inventory.
|
|
|
27791
27954
|
process.exit(1);
|
|
27792
27955
|
return;
|
|
27793
27956
|
}
|
|
27794
|
-
const
|
|
27957
|
+
const delay2 = Math.min(
|
|
27795
27958
|
backoffSchedule[Math.min(attempt, backoffSchedule.length - 1)] ?? 1e3,
|
|
27796
27959
|
1e3,
|
|
27797
27960
|
remaining
|
|
27798
27961
|
);
|
|
27799
27962
|
attempt += 1;
|
|
27800
|
-
await new Promise((r) => setTimeout(r,
|
|
27963
|
+
await new Promise((r) => setTimeout(r, delay2));
|
|
27801
27964
|
}
|
|
27802
27965
|
}
|
|
27803
27966
|
}
|
|
@@ -28370,7 +28533,7 @@ function isoToCcusageDate(iso) {
|
|
|
28370
28533
|
|
|
28371
28534
|
// src/usage/cwd-extractor.ts
|
|
28372
28535
|
init_paths();
|
|
28373
|
-
import { open as open4, readdir as
|
|
28536
|
+
import { open as open4, readdir as readdir24, stat as stat10 } from "fs/promises";
|
|
28374
28537
|
import { join as join13 } from "path";
|
|
28375
28538
|
import { homedir as homedir9 } from "os";
|
|
28376
28539
|
var SCAN_LINE_CAP = 50;
|
|
@@ -28493,7 +28656,7 @@ function resolveCodexSessionsRoot(override) {
|
|
|
28493
28656
|
}
|
|
28494
28657
|
async function listDirSafe(path) {
|
|
28495
28658
|
try {
|
|
28496
|
-
const entries = await
|
|
28659
|
+
const entries = await readdir24(path, { withFileTypes: true });
|
|
28497
28660
|
return entries.map((e) => ({
|
|
28498
28661
|
name: e.name,
|
|
28499
28662
|
isFile: e.isFile(),
|
|
@@ -28849,7 +29012,7 @@ init_timestamp();
|
|
|
28849
29012
|
init_assignment_resolver();
|
|
28850
29013
|
init_assignment_todos();
|
|
28851
29014
|
import { resolve as resolve69 } from "path";
|
|
28852
|
-
import { readFile as
|
|
29015
|
+
import { readFile as readFile45 } from "fs/promises";
|
|
28853
29016
|
async function requestCommand(target, text, options = {}) {
|
|
28854
29017
|
if (!text || !text.trim()) {
|
|
28855
29018
|
throw new Error("Request text cannot be empty.");
|
|
@@ -28880,7 +29043,7 @@ async function requestCommand(target, text, options = {}) {
|
|
|
28880
29043
|
throw new Error(`assignment.md not found at ${assignmentMdPath2}`);
|
|
28881
29044
|
}
|
|
28882
29045
|
const source = options.from ?? process.env.SYNTAUR_ASSIGNMENT ?? "unknown";
|
|
28883
|
-
let content = await
|
|
29046
|
+
let content = await readFile45(assignmentMdPath2, "utf-8");
|
|
28884
29047
|
content = appendTodosToAssignmentBody(content, [
|
|
28885
29048
|
{ description: `${text.trim()} (from: ${source})` }
|
|
28886
29049
|
]);
|
|
@@ -28893,13 +29056,13 @@ async function requestCommand(target, text, options = {}) {
|
|
|
28893
29056
|
init_fs();
|
|
28894
29057
|
init_paths();
|
|
28895
29058
|
import { Command as Command9 } from "commander";
|
|
28896
|
-
import { readFile as
|
|
29059
|
+
import { readFile as readFile46, readdir as readdir25 } from "fs/promises";
|
|
28897
29060
|
import { resolve as resolve70 } from "path";
|
|
28898
29061
|
async function readContextAssignmentDir(cwd) {
|
|
28899
29062
|
const path = resolve70(cwd, ".syntaur", "context.json");
|
|
28900
29063
|
if (!await fileExists(path)) return null;
|
|
28901
29064
|
try {
|
|
28902
|
-
const raw = await
|
|
29065
|
+
const raw = await readFile46(path, "utf-8");
|
|
28903
29066
|
const ctx = JSON.parse(raw);
|
|
28904
29067
|
if (typeof ctx.assignmentDir === "string" && ctx.assignmentDir.length > 0) {
|
|
28905
29068
|
return ctx.assignmentDir;
|
|
@@ -28926,7 +29089,7 @@ async function resolveAssignmentDir(opts) {
|
|
|
28926
29089
|
var PLAN_PATTERN = /^plan(?:-v(\d+))?\.md$/;
|
|
28927
29090
|
async function listPlanFiles(assignmentDir) {
|
|
28928
29091
|
if (!await fileExists(assignmentDir)) return [];
|
|
28929
|
-
const entries = await
|
|
29092
|
+
const entries = await readdir25(assignmentDir, { withFileTypes: true });
|
|
28930
29093
|
const out = [];
|
|
28931
29094
|
for (const e of entries) {
|
|
28932
29095
|
if (!e.isFile()) continue;
|
|
@@ -29074,11 +29237,11 @@ async function runPlanVersion(options) {
|
|
|
29074
29237
|
if (await fileExists(newPath) && !options.force) {
|
|
29075
29238
|
throw new Error(`${next.fileName} already exists. Use --force to overwrite.`);
|
|
29076
29239
|
}
|
|
29077
|
-
const assignmentMd = await
|
|
29240
|
+
const assignmentMd = await readFile46(assignmentMdPath2, "utf-8");
|
|
29078
29241
|
const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
|
|
29079
29242
|
const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
|
|
29080
29243
|
const oldPlanPath = resolve70(assignmentDir, current.fileName);
|
|
29081
|
-
const oldPlanContent = await
|
|
29244
|
+
const oldPlanContent = await readFile46(oldPlanPath, "utf-8");
|
|
29082
29245
|
const oldBody = oldPlanContent.replace(/^---[\s\S]*?\n---\n?/, "");
|
|
29083
29246
|
const carriedTodos = extractUncheckedTodos(oldBody);
|
|
29084
29247
|
const stub = buildNewPlanStub({
|
|
@@ -29115,13 +29278,13 @@ planCommand.command("version").description(
|
|
|
29115
29278
|
// src/commands/session.ts
|
|
29116
29279
|
init_fs();
|
|
29117
29280
|
import { Command as Command10 } from "commander";
|
|
29118
|
-
import { readFile as
|
|
29281
|
+
import { readFile as readFile47, readdir as readdir26, stat as stat11 } from "fs/promises";
|
|
29119
29282
|
import { resolve as resolve71 } from "path";
|
|
29120
29283
|
async function readContext(cwd) {
|
|
29121
29284
|
const path = resolve71(cwd, ".syntaur", "context.json");
|
|
29122
29285
|
if (!await fileExists(path)) return null;
|
|
29123
29286
|
try {
|
|
29124
|
-
const raw = await
|
|
29287
|
+
const raw = await readFile47(path, "utf-8");
|
|
29125
29288
|
return JSON.parse(raw);
|
|
29126
29289
|
} catch {
|
|
29127
29290
|
return null;
|
|
@@ -29130,7 +29293,7 @@ async function readContext(cwd) {
|
|
|
29130
29293
|
async function findLatestSessionSummary(assignmentDir) {
|
|
29131
29294
|
const sessionsRoot = resolve71(assignmentDir, "sessions");
|
|
29132
29295
|
if (!await fileExists(sessionsRoot)) return null;
|
|
29133
|
-
const entries = await
|
|
29296
|
+
const entries = await readdir26(sessionsRoot, { withFileTypes: true });
|
|
29134
29297
|
let best = null;
|
|
29135
29298
|
for (const entry of entries) {
|
|
29136
29299
|
if (!entry.isDirectory()) continue;
|
|
@@ -29146,7 +29309,7 @@ async function findLatestSessionSummary(assignmentDir) {
|
|
|
29146
29309
|
async function findOpenHandoff(assignmentDir) {
|
|
29147
29310
|
const handoffPath = resolve71(assignmentDir, "handoff.md");
|
|
29148
29311
|
if (!await fileExists(handoffPath)) return null;
|
|
29149
|
-
const content = await
|
|
29312
|
+
const content = await readFile47(handoffPath, "utf-8");
|
|
29150
29313
|
const body = content.replace(/^---[\s\S]*?\n---\n?/, "").trim();
|
|
29151
29314
|
if (body.length === 0) return null;
|
|
29152
29315
|
if (/^<!--[\s\S]*-->$/.test(body)) return null;
|
|
@@ -29254,13 +29417,13 @@ init_git_worktree();
|
|
|
29254
29417
|
init_fs();
|
|
29255
29418
|
init_paths();
|
|
29256
29419
|
import { Command as Command11 } from "commander";
|
|
29257
|
-
import { readFile as
|
|
29420
|
+
import { readFile as readFile48 } from "fs/promises";
|
|
29258
29421
|
import { resolve as resolve72 } from "path";
|
|
29259
29422
|
async function readContext2(cwd) {
|
|
29260
29423
|
const path = resolve72(cwd, ".syntaur", "context.json");
|
|
29261
29424
|
if (!await fileExists(path)) return null;
|
|
29262
29425
|
try {
|
|
29263
|
-
return JSON.parse(await
|
|
29426
|
+
return JSON.parse(await readFile48(path, "utf-8"));
|
|
29264
29427
|
} catch {
|
|
29265
29428
|
return null;
|
|
29266
29429
|
}
|
|
@@ -29332,7 +29495,7 @@ import { resolve as resolve74 } from "path";
|
|
|
29332
29495
|
// src/utils/project-indexes.ts
|
|
29333
29496
|
init_parser();
|
|
29334
29497
|
init_fs();
|
|
29335
|
-
import { readdir as
|
|
29498
|
+
import { readdir as readdir27, readFile as readFile49 } from "fs/promises";
|
|
29336
29499
|
import { resolve as resolve73 } from "path";
|
|
29337
29500
|
function nowIso3() {
|
|
29338
29501
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
@@ -29342,7 +29505,7 @@ function readProjectSlug(projectDir) {
|
|
|
29342
29505
|
}
|
|
29343
29506
|
async function listSlugFiles(dir) {
|
|
29344
29507
|
if (!await fileExists(dir)) return [];
|
|
29345
|
-
const entries = await
|
|
29508
|
+
const entries = await readdir27(dir, { withFileTypes: true });
|
|
29346
29509
|
return entries.filter((e) => e.isFile() && e.name.endsWith(".md") && !e.name.startsWith("_")).map((e) => e.name).sort();
|
|
29347
29510
|
}
|
|
29348
29511
|
function escapeCell(value) {
|
|
@@ -29368,7 +29531,7 @@ async function rebuildResourcesIndex(projectDir) {
|
|
|
29368
29531
|
lines.push("| Name | Category | Source | Related Assignments | Updated |");
|
|
29369
29532
|
lines.push("|------|----------|--------|---------------------|---------|");
|
|
29370
29533
|
for (const fileName of files) {
|
|
29371
|
-
const content = await
|
|
29534
|
+
const content = await readFile49(resolve73(dir, fileName), "utf-8");
|
|
29372
29535
|
const parsed = parseResource(content);
|
|
29373
29536
|
const slugBase = fileName.replace(/\.md$/, "");
|
|
29374
29537
|
const name = parsed.name || slugBase;
|
|
@@ -29399,7 +29562,7 @@ async function rebuildMemoriesIndex(projectDir) {
|
|
|
29399
29562
|
lines.push("| Name | Source | Scope | Source Assignment | Updated |");
|
|
29400
29563
|
lines.push("|------|--------|-------|-------------------|---------|");
|
|
29401
29564
|
for (const fileName of files) {
|
|
29402
|
-
const content = await
|
|
29565
|
+
const content = await readFile49(resolve73(dir, fileName), "utf-8");
|
|
29403
29566
|
const parsed = parseMemory(content);
|
|
29404
29567
|
const slugBase = fileName.replace(/\.md$/, "");
|
|
29405
29568
|
const name = parsed.name || slugBase;
|
|
@@ -29574,7 +29737,7 @@ init_paths();
|
|
|
29574
29737
|
init_fs();
|
|
29575
29738
|
init_frontmatter();
|
|
29576
29739
|
import { Command as Command14 } from "commander";
|
|
29577
|
-
import { readFile as
|
|
29740
|
+
import { readFile as readFile50 } from "fs/promises";
|
|
29578
29741
|
import { resolve as resolve76 } from "path";
|
|
29579
29742
|
var AGE_PATTERN = /^(\d+)([dhwm])$/i;
|
|
29580
29743
|
function parseAgeToCutoff(age) {
|
|
@@ -29608,7 +29771,7 @@ async function loadTags(item) {
|
|
|
29608
29771
|
const path = assignmentMdPath(item);
|
|
29609
29772
|
if (!await fileExists(path)) return [];
|
|
29610
29773
|
try {
|
|
29611
|
-
const content = await
|
|
29774
|
+
const content = await readFile50(path, "utf-8");
|
|
29612
29775
|
return parseAssignmentFrontmatter(content).tags;
|
|
29613
29776
|
} catch {
|
|
29614
29777
|
return [];
|
|
@@ -29686,10 +29849,10 @@ var lsCommand = new Command14("ls").description(
|
|
|
29686
29849
|
|
|
29687
29850
|
// src/cli-default-command.ts
|
|
29688
29851
|
init_config2();
|
|
29689
|
-
import { readdir as
|
|
29852
|
+
import { readdir as readdir28 } from "fs/promises";
|
|
29690
29853
|
async function hasAnyProjectContent(projectsDir2) {
|
|
29691
29854
|
try {
|
|
29692
|
-
const entries = await
|
|
29855
|
+
const entries = await readdir28(projectsDir2, { withFileTypes: true });
|
|
29693
29856
|
return entries.some((entry) => entry.isDirectory());
|
|
29694
29857
|
} catch {
|
|
29695
29858
|
return false;
|
|
@@ -29726,20 +29889,20 @@ async function getDefaultCommandName() {
|
|
|
29726
29889
|
init_paths();
|
|
29727
29890
|
init_fs();
|
|
29728
29891
|
import { fileURLToPath as fileURLToPath10 } from "url";
|
|
29729
|
-
import { readFile as
|
|
29892
|
+
import { readFile as readFile52 } from "fs/promises";
|
|
29730
29893
|
import { dirname as dirname21, join as join15, resolve as resolve77 } from "path";
|
|
29731
29894
|
import { spawn as spawn10 } from "child_process";
|
|
29732
29895
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
29733
29896
|
|
|
29734
29897
|
// src/utils/version.ts
|
|
29735
29898
|
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
29736
|
-
import { readFile as
|
|
29899
|
+
import { readFile as readFile51 } from "fs/promises";
|
|
29737
29900
|
import { dirname as dirname20, join as join14 } from "path";
|
|
29738
29901
|
async function readPackageVersion(scriptUrl) {
|
|
29739
29902
|
try {
|
|
29740
29903
|
const scriptPath = fileURLToPath9(scriptUrl);
|
|
29741
29904
|
const pkgRoot = dirname20(dirname20(scriptPath));
|
|
29742
|
-
const raw = await
|
|
29905
|
+
const raw = await readFile51(join14(pkgRoot, "package.json"), "utf-8");
|
|
29743
29906
|
const parsed = JSON.parse(raw);
|
|
29744
29907
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
29745
29908
|
} catch {
|
|
@@ -29769,7 +29932,7 @@ function isRunningViaNpx(scriptUrl) {
|
|
|
29769
29932
|
async function readState() {
|
|
29770
29933
|
if (!await fileExists(STATE_FILE)) return null;
|
|
29771
29934
|
try {
|
|
29772
|
-
const raw = await
|
|
29935
|
+
const raw = await readFile52(STATE_FILE, "utf-8");
|
|
29773
29936
|
return JSON.parse(raw);
|
|
29774
29937
|
} catch {
|
|
29775
29938
|
return null;
|
|
@@ -29828,7 +29991,7 @@ async function readGlobalVersion() {
|
|
|
29828
29991
|
try {
|
|
29829
29992
|
const manifestPath = join15(rootPath, "syntaur", "package.json");
|
|
29830
29993
|
if (!await fileExists(manifestPath)) return null;
|
|
29831
|
-
const raw = await
|
|
29994
|
+
const raw = await readFile52(manifestPath, "utf-8");
|
|
29832
29995
|
const parsed = JSON.parse(raw);
|
|
29833
29996
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
29834
29997
|
} catch {
|