syntaur 0.17.1 → 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 +198 -160
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.js +329 -290
- 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/dashboard/server.js
CHANGED
|
@@ -3871,8 +3871,8 @@ async function migrateFromMarkdown(projectsDir) {
|
|
|
3871
3871
|
return allSessions.length;
|
|
3872
3872
|
}
|
|
3873
3873
|
async function parseMarkdownSessionsIndex(filePath, projectSlug) {
|
|
3874
|
-
const { readFile:
|
|
3875
|
-
const raw = await
|
|
3874
|
+
const { readFile: readFile20 } = await import("fs/promises");
|
|
3875
|
+
const raw = await readFile20(filePath, "utf-8");
|
|
3876
3876
|
const sessions = [];
|
|
3877
3877
|
const lines = raw.split("\n");
|
|
3878
3878
|
let inTable = false;
|
|
@@ -4289,12 +4289,13 @@ __export(scanner_exports, {
|
|
|
4289
4289
|
import { execFile } from "child_process";
|
|
4290
4290
|
import { promisify } from "util";
|
|
4291
4291
|
import { resolve as resolve12 } from "path";
|
|
4292
|
-
import { realpath
|
|
4292
|
+
import { realpath } from "fs/promises";
|
|
4293
4293
|
function clearScanCache() {
|
|
4294
4294
|
cache = null;
|
|
4295
4295
|
forceFreshNext = true;
|
|
4296
4296
|
scanEpoch++;
|
|
4297
4297
|
inFlight = null;
|
|
4298
|
+
tmuxAvailableCache = null;
|
|
4298
4299
|
}
|
|
4299
4300
|
function emptyScan() {
|
|
4300
4301
|
return { sessions: [], tmuxAvailable: false };
|
|
@@ -4352,8 +4353,10 @@ async function execQuiet(cmd, args) {
|
|
|
4352
4353
|
}
|
|
4353
4354
|
}
|
|
4354
4355
|
async function checkTmuxAvailable() {
|
|
4355
|
-
|
|
4356
|
-
|
|
4356
|
+
if (!tmuxAvailableCache) {
|
|
4357
|
+
tmuxAvailableCache = execQuiet("which", ["tmux"]).then((result) => result.length > 0);
|
|
4358
|
+
}
|
|
4359
|
+
return tmuxAvailableCache;
|
|
4357
4360
|
}
|
|
4358
4361
|
async function sessionAlive(name) {
|
|
4359
4362
|
try {
|
|
@@ -4448,62 +4451,11 @@ async function getGitInfo(cwd) {
|
|
|
4448
4451
|
return { branch: branch || null, worktree: isWorktree };
|
|
4449
4452
|
}
|
|
4450
4453
|
async function loadWorkspaceRecords(projectsDir, assignmentsDir2) {
|
|
4451
|
-
const records = [];
|
|
4452
4454
|
try {
|
|
4453
|
-
|
|
4454
|
-
for (const project of projects) {
|
|
4455
|
-
const projectAssignmentsDir = resolve12(projectsDir, project.slug, "assignments");
|
|
4456
|
-
let slugs;
|
|
4457
|
-
try {
|
|
4458
|
-
slugs = await readdir7(projectAssignmentsDir);
|
|
4459
|
-
} catch {
|
|
4460
|
-
continue;
|
|
4461
|
-
}
|
|
4462
|
-
for (const aslug of slugs) {
|
|
4463
|
-
const aFile = resolve12(projectAssignmentsDir, aslug, "assignment.md");
|
|
4464
|
-
try {
|
|
4465
|
-
const raw = await readFile9(aFile, "utf-8");
|
|
4466
|
-
const [fm] = extractFrontmatter2(raw);
|
|
4467
|
-
if (!fm) continue;
|
|
4468
|
-
records.push({
|
|
4469
|
-
projectSlug: project.slug,
|
|
4470
|
-
assignmentSlug: aslug,
|
|
4471
|
-
assignmentTitle: getField(fm, "title") ?? aslug,
|
|
4472
|
-
worktreePath: getNestedField(fm, "workspace", "worktreePath") ?? null,
|
|
4473
|
-
branch: getNestedField(fm, "workspace", "branch") ?? null
|
|
4474
|
-
});
|
|
4475
|
-
} catch {
|
|
4476
|
-
continue;
|
|
4477
|
-
}
|
|
4478
|
-
}
|
|
4479
|
-
}
|
|
4455
|
+
return await listWorkspaceRecords(projectsDir, assignmentsDir2);
|
|
4480
4456
|
} catch {
|
|
4457
|
+
return [];
|
|
4481
4458
|
}
|
|
4482
|
-
if (assignmentsDir2) {
|
|
4483
|
-
try {
|
|
4484
|
-
const entries = await readdir7(assignmentsDir2);
|
|
4485
|
-
for (const id of entries) {
|
|
4486
|
-
if (id.startsWith(".") || id.startsWith("_")) continue;
|
|
4487
|
-
const aFile = resolve12(assignmentsDir2, id, "assignment.md");
|
|
4488
|
-
try {
|
|
4489
|
-
const raw = await readFile9(aFile, "utf-8");
|
|
4490
|
-
const [fm] = extractFrontmatter2(raw);
|
|
4491
|
-
if (!fm) continue;
|
|
4492
|
-
records.push({
|
|
4493
|
-
projectSlug: null,
|
|
4494
|
-
assignmentSlug: id,
|
|
4495
|
-
assignmentTitle: getField(fm, "title") ?? id,
|
|
4496
|
-
worktreePath: getNestedField(fm, "workspace", "worktreePath") ?? null,
|
|
4497
|
-
branch: getNestedField(fm, "workspace", "branch") ?? null
|
|
4498
|
-
});
|
|
4499
|
-
} catch {
|
|
4500
|
-
continue;
|
|
4501
|
-
}
|
|
4502
|
-
}
|
|
4503
|
-
} catch {
|
|
4504
|
-
}
|
|
4505
|
-
}
|
|
4506
|
-
return records;
|
|
4507
4459
|
}
|
|
4508
4460
|
async function resolveAndNormalize(p) {
|
|
4509
4461
|
try {
|
|
@@ -4774,13 +4726,12 @@ async function scanSingleSession(serversDir2, projectsDir, name, options) {
|
|
|
4774
4726
|
}
|
|
4775
4727
|
return scanSession(data, lsofOutput, workspaceRecords, procSnapshot);
|
|
4776
4728
|
}
|
|
4777
|
-
var exec, cache, lastKnown, inFlight, forceFreshNext, scanEpoch, cacheKey, CACHE_TTL_MS, COLD_WAIT_BUDGET_MS, PROBE_TIMEOUT_MS, PROBE_MAX_BUFFER;
|
|
4729
|
+
var exec, cache, lastKnown, inFlight, forceFreshNext, scanEpoch, cacheKey, CACHE_TTL_MS, COLD_WAIT_BUDGET_MS, PROBE_TIMEOUT_MS, PROBE_MAX_BUFFER, tmuxAvailableCache;
|
|
4778
4730
|
var init_scanner = __esm({
|
|
4779
4731
|
"src/dashboard/scanner.ts"() {
|
|
4780
4732
|
"use strict";
|
|
4781
4733
|
init_api();
|
|
4782
4734
|
init_servers();
|
|
4783
|
-
init_parser();
|
|
4784
4735
|
exec = promisify(execFile);
|
|
4785
4736
|
cache = null;
|
|
4786
4737
|
lastKnown = null;
|
|
@@ -4792,11 +4743,12 @@ var init_scanner = __esm({
|
|
|
4792
4743
|
COLD_WAIT_BUDGET_MS = 2500;
|
|
4793
4744
|
PROBE_TIMEOUT_MS = 15e3;
|
|
4794
4745
|
PROBE_MAX_BUFFER = 32 * 1024 * 1024;
|
|
4746
|
+
tmuxAvailableCache = null;
|
|
4795
4747
|
}
|
|
4796
4748
|
});
|
|
4797
4749
|
|
|
4798
4750
|
// src/dashboard/api.ts
|
|
4799
|
-
import { readdir as
|
|
4751
|
+
import { readdir as readdir7, readFile as readFile9, writeFile as writeFile3 } from "fs/promises";
|
|
4800
4752
|
import { resolve as resolve13, dirname as dirname2, basename } from "path";
|
|
4801
4753
|
function clearFrontmatterField(content, key) {
|
|
4802
4754
|
const fieldRegex = new RegExp(`^(${escapeRegExp2(key)}:)\\s*.*$`, "m");
|
|
@@ -4836,10 +4788,41 @@ function emitTrace(traces, meta) {
|
|
|
4836
4788
|
JSON.stringify({ kind: "overview-trace", totalMs, phases: traces.entries, subPhases, ...meta })
|
|
4837
4789
|
);
|
|
4838
4790
|
}
|
|
4791
|
+
function invalidateRecordsCache() {
|
|
4792
|
+
projectRecordsCache.clear();
|
|
4793
|
+
standaloneRecordsCache.clear();
|
|
4794
|
+
}
|
|
4795
|
+
function installRecordsInvalidation(router) {
|
|
4796
|
+
for (const method of ["post", "put", "patch", "delete"]) {
|
|
4797
|
+
const original = router[method].bind(router);
|
|
4798
|
+
router[method] = (path, ...handlers) => {
|
|
4799
|
+
if (handlers.length > 0) {
|
|
4800
|
+
const last = handlers[handlers.length - 1];
|
|
4801
|
+
handlers[handlers.length - 1] = async (req, res, next) => {
|
|
4802
|
+
try {
|
|
4803
|
+
return await last(req, res, next);
|
|
4804
|
+
} finally {
|
|
4805
|
+
invalidateRecordsCache();
|
|
4806
|
+
}
|
|
4807
|
+
};
|
|
4808
|
+
}
|
|
4809
|
+
return original(path, ...handlers);
|
|
4810
|
+
};
|
|
4811
|
+
}
|
|
4812
|
+
}
|
|
4839
4813
|
async function listStandaloneRecords(assignmentsDir2) {
|
|
4814
|
+
const key = assignmentsDir2 ?? "";
|
|
4815
|
+
const cached = standaloneRecordsCache.get(key);
|
|
4816
|
+
if (cached) return cached;
|
|
4817
|
+
const promise = computeStandaloneRecords(assignmentsDir2);
|
|
4818
|
+
standaloneRecordsCache.set(key, promise);
|
|
4819
|
+
promise.catch(() => standaloneRecordsCache.delete(key));
|
|
4820
|
+
return promise;
|
|
4821
|
+
}
|
|
4822
|
+
async function computeStandaloneRecords(assignmentsDir2) {
|
|
4840
4823
|
if (!assignmentsDir2) return [];
|
|
4841
4824
|
if (!await fileExists(assignmentsDir2)) return [];
|
|
4842
|
-
const entries = await
|
|
4825
|
+
const entries = await readdir7(assignmentsDir2, { withFileTypes: true });
|
|
4843
4826
|
const records = [];
|
|
4844
4827
|
for (const entry of entries) {
|
|
4845
4828
|
if (!entry.isDirectory() || entry.name.startsWith(".") || entry.name.startsWith("_")) continue;
|
|
@@ -4847,7 +4830,7 @@ async function listStandaloneRecords(assignmentsDir2) {
|
|
|
4847
4830
|
const assignmentMdPath = resolve13(assignmentDir, "assignment.md");
|
|
4848
4831
|
if (!await fileExists(assignmentMdPath)) continue;
|
|
4849
4832
|
try {
|
|
4850
|
-
const content = await
|
|
4833
|
+
const content = await readFile9(assignmentMdPath, "utf-8");
|
|
4851
4834
|
const record = parseAssignmentFull(content);
|
|
4852
4835
|
records.push({ assignmentDir, id: entry.name, record });
|
|
4853
4836
|
} catch {
|
|
@@ -4923,7 +4906,7 @@ async function listProjects(projectsDir) {
|
|
|
4923
4906
|
async function readWorkspaceRegistry(projectsDir) {
|
|
4924
4907
|
const registryPath = resolve13(dirname2(projectsDir), "workspaces.json");
|
|
4925
4908
|
try {
|
|
4926
|
-
const raw = await
|
|
4909
|
+
const raw = await readFile9(registryPath, "utf-8");
|
|
4927
4910
|
const parsed = JSON.parse(raw);
|
|
4928
4911
|
return Array.isArray(parsed) ? parsed.filter((w) => typeof w === "string") : [];
|
|
4929
4912
|
} catch {
|
|
@@ -4959,6 +4942,34 @@ async function listWorkspaces(projectsDir, assignmentsDir2) {
|
|
|
4959
4942
|
const workspaces = Array.from(workspaceSet).sort();
|
|
4960
4943
|
return { workspaces, hasUngrouped };
|
|
4961
4944
|
}
|
|
4945
|
+
async function listWorkspaceRecords(projectsDir, assignmentsDir2) {
|
|
4946
|
+
const [projectRecords, standaloneRecords] = await Promise.all([
|
|
4947
|
+
listProjectRecords(projectsDir),
|
|
4948
|
+
listStandaloneRecords(assignmentsDir2)
|
|
4949
|
+
]);
|
|
4950
|
+
const records = [];
|
|
4951
|
+
for (const project of projectRecords) {
|
|
4952
|
+
for (const assignment of project.assignments) {
|
|
4953
|
+
records.push({
|
|
4954
|
+
projectSlug: project.summary.slug,
|
|
4955
|
+
assignmentSlug: assignment.slug,
|
|
4956
|
+
assignmentTitle: assignment.title || assignment.slug,
|
|
4957
|
+
worktreePath: assignment.workspace.worktreePath ?? null,
|
|
4958
|
+
branch: assignment.workspace.branch ?? null
|
|
4959
|
+
});
|
|
4960
|
+
}
|
|
4961
|
+
}
|
|
4962
|
+
for (const standalone of standaloneRecords) {
|
|
4963
|
+
records.push({
|
|
4964
|
+
projectSlug: null,
|
|
4965
|
+
assignmentSlug: standalone.id,
|
|
4966
|
+
assignmentTitle: standalone.record.title || standalone.id,
|
|
4967
|
+
worktreePath: standalone.record.workspace.worktreePath ?? null,
|
|
4968
|
+
branch: standalone.record.workspace.branch ?? null
|
|
4969
|
+
});
|
|
4970
|
+
}
|
|
4971
|
+
return records;
|
|
4972
|
+
}
|
|
4962
4973
|
async function createWorkspace(projectsDir, name) {
|
|
4963
4974
|
const registered = await readWorkspaceRegistry(projectsDir);
|
|
4964
4975
|
if (!registered.includes(name)) {
|
|
@@ -4984,7 +4995,7 @@ async function deleteWorkspace(projectsDir, name, opts = {}) {
|
|
|
4984
4995
|
const timestamp = nowTimestamp();
|
|
4985
4996
|
for (const slug of projectsReferencing) {
|
|
4986
4997
|
const path = resolve13(projectsDir, slug, "project.md");
|
|
4987
|
-
const raw = await
|
|
4998
|
+
const raw = await readFile9(path, "utf-8");
|
|
4988
4999
|
let next = clearFrontmatterField(raw, "workspace");
|
|
4989
5000
|
next = setUpdatedField(next, timestamp);
|
|
4990
5001
|
await writeFileForce(path, next);
|
|
@@ -4993,7 +5004,7 @@ async function deleteWorkspace(projectsDir, name, opts = {}) {
|
|
|
4993
5004
|
for (const id of standalonesReferencing) {
|
|
4994
5005
|
if (!opts.assignmentsDir) break;
|
|
4995
5006
|
const path = resolve13(opts.assignmentsDir, id, "assignment.md");
|
|
4996
|
-
const raw = await
|
|
5007
|
+
const raw = await readFile9(path, "utf-8");
|
|
4997
5008
|
let next = clearFrontmatterField(raw, "workspaceGroup");
|
|
4998
5009
|
next = setUpdatedField(next, timestamp);
|
|
4999
5010
|
await writeFileForce(path, next);
|
|
@@ -5003,6 +5014,9 @@ async function deleteWorkspace(projectsDir, name, opts = {}) {
|
|
|
5003
5014
|
const registered = await readWorkspaceRegistry(projectsDir);
|
|
5004
5015
|
const filtered = registered.filter((w) => w !== name);
|
|
5005
5016
|
await writeWorkspaceRegistry(projectsDir, filtered);
|
|
5017
|
+
if (rewroteFiles) {
|
|
5018
|
+
invalidateRecordsCache();
|
|
5019
|
+
}
|
|
5006
5020
|
return { rewroteFiles };
|
|
5007
5021
|
}
|
|
5008
5022
|
async function getOverview(projectsDir, serversDir2, assignmentsDir2, options = {}) {
|
|
@@ -5170,7 +5184,7 @@ async function getEditableDocument(projectsDir, documentType, projectSlug, assig
|
|
|
5170
5184
|
if (!filePath || !await fileExists(filePath)) {
|
|
5171
5185
|
return null;
|
|
5172
5186
|
}
|
|
5173
|
-
const content = await
|
|
5187
|
+
const content = await readFile9(filePath, "utf-8");
|
|
5174
5188
|
const title = getEditableDocumentTitle(documentType, projectSlug, assignmentSlug);
|
|
5175
5189
|
return {
|
|
5176
5190
|
documentType,
|
|
@@ -5196,7 +5210,7 @@ async function getEditableDocumentById(projectsDir, assignmentsDir2, documentTyp
|
|
|
5196
5210
|
if (!fileName) return null;
|
|
5197
5211
|
const filePath = resolve13(resolved.assignmentDir, fileName);
|
|
5198
5212
|
if (!await fileExists(filePath)) return null;
|
|
5199
|
-
const content = await
|
|
5213
|
+
const content = await readFile9(filePath, "utf-8");
|
|
5200
5214
|
const label = resolved.id;
|
|
5201
5215
|
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}`;
|
|
5202
5216
|
return {
|
|
@@ -5215,7 +5229,7 @@ async function getProjectDetail(projectsDir, slug) {
|
|
|
5215
5229
|
if (!await fileExists(projectMdPath)) {
|
|
5216
5230
|
return null;
|
|
5217
5231
|
}
|
|
5218
|
-
const projectContent = await
|
|
5232
|
+
const projectContent = await readFile9(projectMdPath, "utf-8");
|
|
5219
5233
|
const project = parseProject(projectContent);
|
|
5220
5234
|
const assignments = await listAssignmentRecords(projectPath);
|
|
5221
5235
|
const rollup = await buildProjectRollup(projectPath, project, assignments);
|
|
@@ -5252,18 +5266,18 @@ async function getAssignmentDetail(projectsDir, projectSlug, assignmentSlug) {
|
|
|
5252
5266
|
if (!await fileExists(assignmentMdPath)) {
|
|
5253
5267
|
return null;
|
|
5254
5268
|
}
|
|
5255
|
-
const assignmentContent = await
|
|
5269
|
+
const assignmentContent = await readFile9(assignmentMdPath, "utf-8");
|
|
5256
5270
|
const assignment = parseAssignmentFull(assignmentContent);
|
|
5257
5271
|
let projectWorkspace = null;
|
|
5258
5272
|
const projectMdPath = resolve13(projectsDir, projectSlug, "project.md");
|
|
5259
5273
|
if (await fileExists(projectMdPath)) {
|
|
5260
|
-
const projectContent = await
|
|
5274
|
+
const projectContent = await readFile9(projectMdPath, "utf-8");
|
|
5261
5275
|
projectWorkspace = parseProject(projectContent).workspace;
|
|
5262
5276
|
}
|
|
5263
5277
|
let plan = null;
|
|
5264
5278
|
const planPath = resolve13(assignmentDir, "plan.md");
|
|
5265
5279
|
if (await fileExists(planPath)) {
|
|
5266
|
-
const planContent = await
|
|
5280
|
+
const planContent = await readFile9(planPath, "utf-8");
|
|
5267
5281
|
const parsed = parsePlan(planContent);
|
|
5268
5282
|
plan = {
|
|
5269
5283
|
status: parsed.status,
|
|
@@ -5274,7 +5288,7 @@ async function getAssignmentDetail(projectsDir, projectSlug, assignmentSlug) {
|
|
|
5274
5288
|
let scratchpad = null;
|
|
5275
5289
|
const scratchpadPath = resolve13(assignmentDir, "scratchpad.md");
|
|
5276
5290
|
if (await fileExists(scratchpadPath)) {
|
|
5277
|
-
const scratchpadContent = await
|
|
5291
|
+
const scratchpadContent = await readFile9(scratchpadPath, "utf-8");
|
|
5278
5292
|
const parsed = parseScratchpad(scratchpadContent);
|
|
5279
5293
|
scratchpad = {
|
|
5280
5294
|
updated: parsed.updated,
|
|
@@ -5284,7 +5298,7 @@ async function getAssignmentDetail(projectsDir, projectSlug, assignmentSlug) {
|
|
|
5284
5298
|
let handoff = null;
|
|
5285
5299
|
const handoffPath = resolve13(assignmentDir, "handoff.md");
|
|
5286
5300
|
if (await fileExists(handoffPath)) {
|
|
5287
|
-
const handoffContent = await
|
|
5301
|
+
const handoffContent = await readFile9(handoffPath, "utf-8");
|
|
5288
5302
|
const parsed = parseHandoff(handoffContent);
|
|
5289
5303
|
handoff = {
|
|
5290
5304
|
updated: parsed.updated,
|
|
@@ -5295,7 +5309,7 @@ async function getAssignmentDetail(projectsDir, projectSlug, assignmentSlug) {
|
|
|
5295
5309
|
let decisionRecord = null;
|
|
5296
5310
|
const decisionRecordPath = resolve13(assignmentDir, "decision-record.md");
|
|
5297
5311
|
if (await fileExists(decisionRecordPath)) {
|
|
5298
|
-
const decisionRecordContent = await
|
|
5312
|
+
const decisionRecordContent = await readFile9(decisionRecordPath, "utf-8");
|
|
5299
5313
|
const parsed = parseDecisionRecord(decisionRecordContent);
|
|
5300
5314
|
decisionRecord = {
|
|
5301
5315
|
updated: parsed.updated,
|
|
@@ -5306,7 +5320,7 @@ async function getAssignmentDetail(projectsDir, projectSlug, assignmentSlug) {
|
|
|
5306
5320
|
let progress = null;
|
|
5307
5321
|
const progressPath = resolve13(assignmentDir, "progress.md");
|
|
5308
5322
|
if (await fileExists(progressPath)) {
|
|
5309
|
-
const progressContent = await
|
|
5323
|
+
const progressContent = await readFile9(progressPath, "utf-8");
|
|
5310
5324
|
const parsed = parseProgress(progressContent);
|
|
5311
5325
|
progress = {
|
|
5312
5326
|
updated: parsed.updated,
|
|
@@ -5317,7 +5331,7 @@ async function getAssignmentDetail(projectsDir, projectSlug, assignmentSlug) {
|
|
|
5317
5331
|
let comments = null;
|
|
5318
5332
|
const commentsPath = resolve13(assignmentDir, "comments.md");
|
|
5319
5333
|
if (await fileExists(commentsPath)) {
|
|
5320
|
-
const commentsContent = await
|
|
5334
|
+
const commentsContent = await readFile9(commentsPath, "utf-8");
|
|
5321
5335
|
const parsed = parseComments(commentsContent);
|
|
5322
5336
|
comments = {
|
|
5323
5337
|
updated: parsed.updated,
|
|
@@ -5468,7 +5482,7 @@ async function countMentionsInAssignment(sourceDir, target) {
|
|
|
5468
5482
|
const bodies = [];
|
|
5469
5483
|
const assignmentMd = resolve13(sourceDir, "assignment.md");
|
|
5470
5484
|
if (await fileExists(assignmentMd)) {
|
|
5471
|
-
const content = await
|
|
5485
|
+
const content = await readFile9(assignmentMd, "utf-8");
|
|
5472
5486
|
const todosMatch = content.match(/^## Todos\s*$([\s\S]*?)(?=^## |$(?![\r\n]))/m);
|
|
5473
5487
|
if (todosMatch) bodies.push(todosMatch[1]);
|
|
5474
5488
|
}
|
|
@@ -5476,7 +5490,7 @@ async function countMentionsInAssignment(sourceDir, target) {
|
|
|
5476
5490
|
const path = resolve13(sourceDir, filename);
|
|
5477
5491
|
if (await fileExists(path)) {
|
|
5478
5492
|
try {
|
|
5479
|
-
bodies.push(await
|
|
5493
|
+
bodies.push(await readFile9(path, "utf-8"));
|
|
5480
5494
|
} catch {
|
|
5481
5495
|
}
|
|
5482
5496
|
}
|
|
@@ -5536,42 +5550,42 @@ async function buildStandaloneAssignmentDetail(resolved) {
|
|
|
5536
5550
|
const assignmentDir = resolved.assignmentDir;
|
|
5537
5551
|
const assignmentMdPath = resolve13(assignmentDir, "assignment.md");
|
|
5538
5552
|
if (!await fileExists(assignmentMdPath)) return null;
|
|
5539
|
-
const assignmentContent = await
|
|
5553
|
+
const assignmentContent = await readFile9(assignmentMdPath, "utf-8");
|
|
5540
5554
|
const assignment = parseAssignmentFull(assignmentContent);
|
|
5541
5555
|
let plan = null;
|
|
5542
5556
|
const planPath = resolve13(assignmentDir, "plan.md");
|
|
5543
5557
|
if (await fileExists(planPath)) {
|
|
5544
|
-
const parsed = parsePlan(await
|
|
5558
|
+
const parsed = parsePlan(await readFile9(planPath, "utf-8"));
|
|
5545
5559
|
plan = { status: parsed.status, updated: parsed.updated, body: parsed.body };
|
|
5546
5560
|
}
|
|
5547
5561
|
let scratchpad = null;
|
|
5548
5562
|
const scratchpadPath = resolve13(assignmentDir, "scratchpad.md");
|
|
5549
5563
|
if (await fileExists(scratchpadPath)) {
|
|
5550
|
-
const parsed = parseScratchpad(await
|
|
5564
|
+
const parsed = parseScratchpad(await readFile9(scratchpadPath, "utf-8"));
|
|
5551
5565
|
scratchpad = { updated: parsed.updated, body: parsed.body };
|
|
5552
5566
|
}
|
|
5553
5567
|
let handoff = null;
|
|
5554
5568
|
const handoffPath = resolve13(assignmentDir, "handoff.md");
|
|
5555
5569
|
if (await fileExists(handoffPath)) {
|
|
5556
|
-
const parsed = parseHandoff(await
|
|
5570
|
+
const parsed = parseHandoff(await readFile9(handoffPath, "utf-8"));
|
|
5557
5571
|
handoff = { updated: parsed.updated, handoffCount: parsed.handoffCount, body: parsed.body };
|
|
5558
5572
|
}
|
|
5559
5573
|
let decisionRecord = null;
|
|
5560
5574
|
const decisionRecordPath = resolve13(assignmentDir, "decision-record.md");
|
|
5561
5575
|
if (await fileExists(decisionRecordPath)) {
|
|
5562
|
-
const parsed = parseDecisionRecord(await
|
|
5576
|
+
const parsed = parseDecisionRecord(await readFile9(decisionRecordPath, "utf-8"));
|
|
5563
5577
|
decisionRecord = { updated: parsed.updated, decisionCount: parsed.decisionCount, body: parsed.body };
|
|
5564
5578
|
}
|
|
5565
5579
|
let progress = null;
|
|
5566
5580
|
const progressPath = resolve13(assignmentDir, "progress.md");
|
|
5567
5581
|
if (await fileExists(progressPath)) {
|
|
5568
|
-
const parsed = parseProgress(await
|
|
5582
|
+
const parsed = parseProgress(await readFile9(progressPath, "utf-8"));
|
|
5569
5583
|
progress = { updated: parsed.updated, entryCount: parsed.entryCount, entries: parsed.entries };
|
|
5570
5584
|
}
|
|
5571
5585
|
let comments = null;
|
|
5572
5586
|
const commentsPath = resolve13(assignmentDir, "comments.md");
|
|
5573
5587
|
if (await fileExists(commentsPath)) {
|
|
5574
|
-
const parsed = parseComments(await
|
|
5588
|
+
const parsed = parseComments(await readFile9(commentsPath, "utf-8"));
|
|
5575
5589
|
comments = { updated: parsed.updated, entryCount: parsed.entryCount, entries: parsed.entries };
|
|
5576
5590
|
}
|
|
5577
5591
|
const detail = {
|
|
@@ -5608,6 +5622,14 @@ async function buildStandaloneAssignmentDetail(resolved) {
|
|
|
5608
5622
|
return detail;
|
|
5609
5623
|
}
|
|
5610
5624
|
async function listProjectRecords(projectsDir, traces) {
|
|
5625
|
+
const cached = projectRecordsCache.get(projectsDir);
|
|
5626
|
+
if (cached) return cached;
|
|
5627
|
+
const promise = computeProjectRecords(projectsDir, traces);
|
|
5628
|
+
projectRecordsCache.set(projectsDir, promise);
|
|
5629
|
+
promise.catch(() => projectRecordsCache.delete(projectsDir));
|
|
5630
|
+
return promise;
|
|
5631
|
+
}
|
|
5632
|
+
async function computeProjectRecords(projectsDir, traces) {
|
|
5611
5633
|
if (!await fileExists(projectsDir)) {
|
|
5612
5634
|
return [];
|
|
5613
5635
|
}
|
|
@@ -5615,7 +5637,7 @@ async function listProjectRecords(projectsDir, traces) {
|
|
|
5615
5637
|
migratedProjectsDirs.add(projectsDir);
|
|
5616
5638
|
await migrateLegacyProjectFiles(projectsDir);
|
|
5617
5639
|
}
|
|
5618
|
-
const entries = await
|
|
5640
|
+
const entries = await readdir7(projectsDir, { withFileTypes: true });
|
|
5619
5641
|
const projectDirs = entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
|
|
5620
5642
|
const maybeRecords = await Promise.all(
|
|
5621
5643
|
projectDirs.map(async (entry) => {
|
|
@@ -5625,7 +5647,7 @@ async function listProjectRecords(projectsDir, traces) {
|
|
|
5625
5647
|
return null;
|
|
5626
5648
|
}
|
|
5627
5649
|
const t0 = traces ? performance.now() : 0;
|
|
5628
|
-
const projectContent = await
|
|
5650
|
+
const projectContent = await readFile9(projectMdPath, "utf-8");
|
|
5629
5651
|
const project = parseProject(projectContent);
|
|
5630
5652
|
if (traces) accumulatePhase(traces, "parse-project-md", performance.now() - t0);
|
|
5631
5653
|
const t1 = traces ? performance.now() : 0;
|
|
@@ -5670,7 +5692,7 @@ async function listAssignmentRecords(projectPath, traces) {
|
|
|
5670
5692
|
if (!await fileExists(assignmentsDir2)) {
|
|
5671
5693
|
return [];
|
|
5672
5694
|
}
|
|
5673
|
-
const entries = await
|
|
5695
|
+
const entries = await readdir7(assignmentsDir2, { withFileTypes: true });
|
|
5674
5696
|
const dirEntries = entries.filter((entry) => entry.isDirectory());
|
|
5675
5697
|
const maybeRecords = await Promise.all(
|
|
5676
5698
|
dirEntries.map(async (entry) => {
|
|
@@ -5679,7 +5701,7 @@ async function listAssignmentRecords(projectPath, traces) {
|
|
|
5679
5701
|
return null;
|
|
5680
5702
|
}
|
|
5681
5703
|
const t0 = traces ? performance.now() : 0;
|
|
5682
|
-
const content = await
|
|
5704
|
+
const content = await readFile9(assignmentMd, "utf-8");
|
|
5683
5705
|
const parsed = parseAssignmentFull(content);
|
|
5684
5706
|
if (traces) accumulatePhase(traces, "read-assignment-md", performance.now() - t0);
|
|
5685
5707
|
return parsed;
|
|
@@ -5694,14 +5716,14 @@ async function listResources(projectPath) {
|
|
|
5694
5716
|
if (!await fileExists(resourcesDir)) {
|
|
5695
5717
|
return [];
|
|
5696
5718
|
}
|
|
5697
|
-
const entries = await
|
|
5719
|
+
const entries = await readdir7(resourcesDir, { withFileTypes: true });
|
|
5698
5720
|
const results = [];
|
|
5699
5721
|
for (const entry of entries) {
|
|
5700
5722
|
if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name.startsWith("_")) {
|
|
5701
5723
|
continue;
|
|
5702
5724
|
}
|
|
5703
5725
|
const filePath = resolve13(resourcesDir, entry.name);
|
|
5704
|
-
const content = await
|
|
5726
|
+
const content = await readFile9(filePath, "utf-8");
|
|
5705
5727
|
const parsed = parseResource(content);
|
|
5706
5728
|
results.push({
|
|
5707
5729
|
name: parsed.name,
|
|
@@ -5720,14 +5742,14 @@ async function listMemories(projectPath) {
|
|
|
5720
5742
|
if (!await fileExists(memoriesDir)) {
|
|
5721
5743
|
return [];
|
|
5722
5744
|
}
|
|
5723
|
-
const entries = await
|
|
5745
|
+
const entries = await readdir7(memoriesDir, { withFileTypes: true });
|
|
5724
5746
|
const results = [];
|
|
5725
5747
|
for (const entry of entries) {
|
|
5726
5748
|
if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name.startsWith("_")) {
|
|
5727
5749
|
continue;
|
|
5728
5750
|
}
|
|
5729
5751
|
const filePath = resolve13(memoriesDir, entry.name);
|
|
5730
|
-
const content = await
|
|
5752
|
+
const content = await readFile9(filePath, "utf-8");
|
|
5731
5753
|
const parsed = parseMemory(content);
|
|
5732
5754
|
results.push({
|
|
5733
5755
|
name: parsed.name,
|
|
@@ -5790,7 +5812,7 @@ async function getMemoryDetail(projectsDir, projectSlug, itemSlug) {
|
|
|
5790
5812
|
if (!projectRecord) return null;
|
|
5791
5813
|
const filePath = resolve13(projectRecord.projectPath, "memories", `${itemSlug}.md`);
|
|
5792
5814
|
if (!await fileExists(filePath)) return null;
|
|
5793
|
-
const content = await
|
|
5815
|
+
const content = await readFile9(filePath, "utf-8");
|
|
5794
5816
|
const parsed = parseMemory(content);
|
|
5795
5817
|
return {
|
|
5796
5818
|
name: parsed.name,
|
|
@@ -5816,7 +5838,7 @@ async function getResourceDetail(projectsDir, projectSlug, itemSlug) {
|
|
|
5816
5838
|
if (!projectRecord) return null;
|
|
5817
5839
|
const filePath = resolve13(projectRecord.projectPath, "resources", `${itemSlug}.md`);
|
|
5818
5840
|
if (!await fileExists(filePath)) return null;
|
|
5819
|
-
const content = await
|
|
5841
|
+
const content = await readFile9(filePath, "utf-8");
|
|
5820
5842
|
const parsed = parseResource(content);
|
|
5821
5843
|
return {
|
|
5822
5844
|
name: parsed.name,
|
|
@@ -5834,7 +5856,7 @@ async function getResourceDetail(projectsDir, projectSlug, itemSlug) {
|
|
|
5834
5856
|
async function loadDependencyGraph(projectPath, assignments) {
|
|
5835
5857
|
const statusPath = resolve13(projectPath, "_status.md");
|
|
5836
5858
|
if (await fileExists(statusPath)) {
|
|
5837
|
-
const statusContent = await
|
|
5859
|
+
const statusContent = await readFile9(statusPath, "utf-8");
|
|
5838
5860
|
const parsed = parseStatus(statusContent);
|
|
5839
5861
|
const derivedGraph = extractMermaidGraph(parsed.body);
|
|
5840
5862
|
if (derivedGraph) {
|
|
@@ -5995,7 +6017,7 @@ async function getUnmetDependencies(projectPath, dependsOn, terminalStatuses, de
|
|
|
5995
6017
|
unmet.push(`${dependency} (missing)`);
|
|
5996
6018
|
continue;
|
|
5997
6019
|
}
|
|
5998
|
-
const content = await
|
|
6020
|
+
const content = await readFile9(dependencyPath, "utf-8");
|
|
5999
6021
|
const parsed = parseAssignmentFull(content);
|
|
6000
6022
|
if (!terminals.has(parsed.status)) {
|
|
6001
6023
|
unmet.push(`${dependency} (${parsed.status})`);
|
|
@@ -6288,7 +6310,7 @@ async function countOpenQuestions(projectPath, assignmentSlug) {
|
|
|
6288
6310
|
return 0;
|
|
6289
6311
|
}
|
|
6290
6312
|
try {
|
|
6291
|
-
const content = await
|
|
6313
|
+
const content = await readFile9(commentsPath, "utf-8");
|
|
6292
6314
|
const parsed = parseComments(content);
|
|
6293
6315
|
return parsed.entries.filter(
|
|
6294
6316
|
(e) => e.type === "question" && e.resolved !== true
|
|
@@ -6356,12 +6378,12 @@ async function listPlaybooks(playbooksDir2) {
|
|
|
6356
6378
|
if (!await fileExists(playbooksDir2)) return [];
|
|
6357
6379
|
const config = await readConfig();
|
|
6358
6380
|
const disabledSet = new Set(config.playbooks.disabled);
|
|
6359
|
-
const entries = await
|
|
6381
|
+
const entries = await readdir7(playbooksDir2, { withFileTypes: true });
|
|
6360
6382
|
const playbooks = [];
|
|
6361
6383
|
for (const entry of entries) {
|
|
6362
6384
|
if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name.startsWith("_") || entry.name === "manifest.md") continue;
|
|
6363
6385
|
const filePath = resolve13(playbooksDir2, entry.name);
|
|
6364
|
-
const raw = await
|
|
6386
|
+
const raw = await readFile9(filePath, "utf-8");
|
|
6365
6387
|
const parsed = parsePlaybook(raw);
|
|
6366
6388
|
const slug = parsed.slug || entry.name.replace(/\.md$/, "");
|
|
6367
6389
|
playbooks.push({
|
|
@@ -6395,7 +6417,7 @@ async function getPlaybookDetail(playbooksDir2, slug) {
|
|
|
6395
6417
|
enabled
|
|
6396
6418
|
};
|
|
6397
6419
|
}
|
|
6398
|
-
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;
|
|
6420
|
+
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;
|
|
6399
6421
|
var init_api = __esm({
|
|
6400
6422
|
"src/dashboard/api.ts"() {
|
|
6401
6423
|
"use strict";
|
|
@@ -6446,6 +6468,8 @@ var init_api = __esm({
|
|
|
6446
6468
|
["blocked", "blocked"],
|
|
6447
6469
|
["stale", "stale"]
|
|
6448
6470
|
];
|
|
6471
|
+
projectRecordsCache = /* @__PURE__ */ new Map();
|
|
6472
|
+
standaloneRecordsCache = /* @__PURE__ */ new Map();
|
|
6449
6473
|
DEFAULT_TRANSITION_DEFINITIONS = [
|
|
6450
6474
|
{
|
|
6451
6475
|
command: "start",
|
|
@@ -6994,7 +7018,7 @@ function mergePatch(current, patch) {
|
|
|
6994
7018
|
// src/utils/view-prefs.ts
|
|
6995
7019
|
init_paths();
|
|
6996
7020
|
init_fs();
|
|
6997
|
-
import { readFile as
|
|
7021
|
+
import { readFile as readFile10, rename as rename3, unlink as unlink3 } from "fs/promises";
|
|
6998
7022
|
import { resolve as resolve14 } from "path";
|
|
6999
7023
|
function corruptFilePath() {
|
|
7000
7024
|
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
@@ -7017,7 +7041,7 @@ async function readViewPrefsFile() {
|
|
|
7017
7041
|
}
|
|
7018
7042
|
let raw;
|
|
7019
7043
|
try {
|
|
7020
|
-
raw = await
|
|
7044
|
+
raw = await readFile10(path, "utf-8");
|
|
7021
7045
|
} catch {
|
|
7022
7046
|
return { ...DEFAULT_VIEW_PREFS_FILE };
|
|
7023
7047
|
}
|
|
@@ -7200,7 +7224,7 @@ function isDashboardSlot(value) {
|
|
|
7200
7224
|
// src/utils/saved-views.ts
|
|
7201
7225
|
init_paths();
|
|
7202
7226
|
init_fs();
|
|
7203
|
-
import { readFile as
|
|
7227
|
+
import { readFile as readFile11, rename as rename4, unlink as unlink4 } from "fs/promises";
|
|
7204
7228
|
import { randomUUID } from "crypto";
|
|
7205
7229
|
import { resolve as resolve15 } from "path";
|
|
7206
7230
|
function corruptFilePath2() {
|
|
@@ -7230,7 +7254,7 @@ async function readSavedViewsFile() {
|
|
|
7230
7254
|
}
|
|
7231
7255
|
let raw;
|
|
7232
7256
|
try {
|
|
7233
|
-
raw = await
|
|
7257
|
+
raw = await readFile11(path, "utf-8");
|
|
7234
7258
|
} catch {
|
|
7235
7259
|
return cloneDefault();
|
|
7236
7260
|
}
|
|
@@ -7546,7 +7570,7 @@ init_lifecycle();
|
|
|
7546
7570
|
init_slug();
|
|
7547
7571
|
import { Router as Router2 } from "express";
|
|
7548
7572
|
import { resolve as resolve18, basename as basename3, isAbsolute as isAbsolute2 } from "path";
|
|
7549
|
-
import { rm, readFile as
|
|
7573
|
+
import { rm, readFile as readFile14, open as fsOpen, stat as fsStat, realpath as fsRealpath } from "fs/promises";
|
|
7550
7574
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
7551
7575
|
|
|
7552
7576
|
// src/utils/uuid.ts
|
|
@@ -7563,7 +7587,7 @@ init_fs();
|
|
|
7563
7587
|
init_frontmatter();
|
|
7564
7588
|
init_fs();
|
|
7565
7589
|
import { spawn } from "child_process";
|
|
7566
|
-
import { readFile as
|
|
7590
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
7567
7591
|
function run(command, args, cwd) {
|
|
7568
7592
|
return new Promise((resolvePromise) => {
|
|
7569
7593
|
const child = spawn(command, args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -7613,7 +7637,7 @@ async function createWorktreeAndRecord(opts) {
|
|
|
7613
7637
|
const { assignmentPath, repository, branch, worktreePath, parentBranch } = opts;
|
|
7614
7638
|
await createWorktree({ repository, branch, worktreePath, parentBranch });
|
|
7615
7639
|
try {
|
|
7616
|
-
const content = await
|
|
7640
|
+
const content = await readFile12(assignmentPath, "utf-8");
|
|
7617
7641
|
const updated = updateAssignmentWorkspace(content, {
|
|
7618
7642
|
repository,
|
|
7619
7643
|
worktreePath,
|
|
@@ -7697,14 +7721,14 @@ function detectCurrentBranch(cwd) {
|
|
|
7697
7721
|
// src/dashboard/repository-candidates.ts
|
|
7698
7722
|
init_fs();
|
|
7699
7723
|
init_parser();
|
|
7700
|
-
import { readdir as
|
|
7724
|
+
import { readdir as readdir8, readFile as readFile13 } from "fs/promises";
|
|
7701
7725
|
import { resolve as resolve17 } from "path";
|
|
7702
7726
|
async function getProjectRepositoryCandidates(projectsDir, projectSlug) {
|
|
7703
7727
|
const seen = /* @__PURE__ */ new Set();
|
|
7704
7728
|
const out = [];
|
|
7705
7729
|
const projectPath = resolve17(projectsDir, projectSlug, "project.md");
|
|
7706
7730
|
if (await fileExists(projectPath)) {
|
|
7707
|
-
const project = parseProject(await
|
|
7731
|
+
const project = parseProject(await readFile13(projectPath, "utf-8"));
|
|
7708
7732
|
for (const raw of project.repositories) {
|
|
7709
7733
|
const path = raw.trim();
|
|
7710
7734
|
if (!path) continue;
|
|
@@ -7716,12 +7740,12 @@ async function getProjectRepositoryCandidates(projectsDir, projectSlug) {
|
|
|
7716
7740
|
}
|
|
7717
7741
|
const assignmentsDir2 = resolve17(projectsDir, projectSlug, "assignments");
|
|
7718
7742
|
if (await fileExists(assignmentsDir2)) {
|
|
7719
|
-
const entries = await
|
|
7743
|
+
const entries = await readdir8(assignmentsDir2, { withFileTypes: true });
|
|
7720
7744
|
for (const entry of entries) {
|
|
7721
7745
|
if (!entry.isDirectory()) continue;
|
|
7722
7746
|
const assignmentMd = resolve17(assignmentsDir2, entry.name, "assignment.md");
|
|
7723
7747
|
if (!await fileExists(assignmentMd)) continue;
|
|
7724
|
-
const parsed = parseAssignmentFull(await
|
|
7748
|
+
const parsed = parseAssignmentFull(await readFile13(assignmentMd, "utf-8"));
|
|
7725
7749
|
const repo = parsed.workspace.repository?.trim();
|
|
7726
7750
|
if (!repo) continue;
|
|
7727
7751
|
const abs = resolve17(repo);
|
|
@@ -7738,13 +7762,13 @@ async function getStandaloneRepositoryCandidates(assignmentsDir2, excludeAssignm
|
|
|
7738
7762
|
}
|
|
7739
7763
|
const seen = /* @__PURE__ */ new Set();
|
|
7740
7764
|
const out = [];
|
|
7741
|
-
const entries = await
|
|
7765
|
+
const entries = await readdir8(assignmentsDir2, { withFileTypes: true });
|
|
7742
7766
|
for (const entry of entries) {
|
|
7743
7767
|
if (!entry.isDirectory()) continue;
|
|
7744
7768
|
if (entry.name === excludeAssignmentId) continue;
|
|
7745
7769
|
const assignmentMd = resolve17(assignmentsDir2, entry.name, "assignment.md");
|
|
7746
7770
|
if (!await fileExists(assignmentMd)) continue;
|
|
7747
|
-
const parsed = parseAssignmentFull(await
|
|
7771
|
+
const parsed = parseAssignmentFull(await readFile13(assignmentMd, "utf-8"));
|
|
7748
7772
|
const repo = parsed.workspace.repository?.trim();
|
|
7749
7773
|
if (!repo) continue;
|
|
7750
7774
|
const abs = resolve17(repo);
|
|
@@ -8310,14 +8334,14 @@ async function readCurrentDocument(filePath) {
|
|
|
8310
8334
|
if (!await fileExists(filePath)) {
|
|
8311
8335
|
return null;
|
|
8312
8336
|
}
|
|
8313
|
-
return
|
|
8337
|
+
return readFile14(filePath, "utf-8");
|
|
8314
8338
|
}
|
|
8315
8339
|
async function handleWorktreeCreate(req, res, ctx) {
|
|
8316
8340
|
if (!await fileExists(ctx.assignmentPath)) {
|
|
8317
8341
|
res.status(404).json({ error: "Assignment not found" });
|
|
8318
8342
|
return;
|
|
8319
8343
|
}
|
|
8320
|
-
const parsed = parseAssignmentFull(await
|
|
8344
|
+
const parsed = parseAssignmentFull(await readFile14(ctx.assignmentPath, "utf-8"));
|
|
8321
8345
|
if (parsed.workspace.worktreePath) {
|
|
8322
8346
|
res.status(409).json({ error: "Worktree already configured for this assignment" });
|
|
8323
8347
|
return;
|
|
@@ -8409,6 +8433,7 @@ async function handleWorktreeCreate(req, res, ctx) {
|
|
|
8409
8433
|
function createWriteRouter(projectsDir, assignmentsDir2, todosDir2) {
|
|
8410
8434
|
const linkedTodosLookup = todosDir2 ? { todosDir: todosDir2, projectsDir } : void 0;
|
|
8411
8435
|
const router = Router2();
|
|
8436
|
+
installRecordsInvalidation(router);
|
|
8412
8437
|
router.get("/api/templates/project", (_req, res) => {
|
|
8413
8438
|
const content = renderProject({
|
|
8414
8439
|
id: generateId(),
|
|
@@ -8686,7 +8711,7 @@ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"
|
|
|
8686
8711
|
}
|
|
8687
8712
|
const nextContentRaw = requireContent(req, res);
|
|
8688
8713
|
if (!nextContentRaw) return;
|
|
8689
|
-
const currentContent = await
|
|
8714
|
+
const currentContent = await readFile14(filePath, "utf-8");
|
|
8690
8715
|
const frontmatterBlock = extractFrontmatterBlock(currentContent);
|
|
8691
8716
|
if (!frontmatterBlock) {
|
|
8692
8717
|
res.status(500).json({ error: `${kind} file is malformed (no frontmatter)` });
|
|
@@ -9137,7 +9162,7 @@ ${nextBody}${nextBody.endsWith("\n") ? "" : "\n"}`;
|
|
|
9137
9162
|
let currentContent;
|
|
9138
9163
|
let currentCount = 0;
|
|
9139
9164
|
if (await fileExists(commentsPath)) {
|
|
9140
|
-
currentContent = await
|
|
9165
|
+
currentContent = await readFile14(commentsPath, "utf-8");
|
|
9141
9166
|
const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
|
|
9142
9167
|
if (countMatch) currentCount = parseInt(countMatch[1], 10);
|
|
9143
9168
|
} else {
|
|
@@ -9194,7 +9219,7 @@ ${entry}`;
|
|
|
9194
9219
|
res.status(400).json({ error: "resolved (boolean) is required" });
|
|
9195
9220
|
return;
|
|
9196
9221
|
}
|
|
9197
|
-
const content = await
|
|
9222
|
+
const content = await readFile14(commentsPath, "utf-8");
|
|
9198
9223
|
const parsed = parseComments(content);
|
|
9199
9224
|
const target = parsed.entries.find((e) => e.id === commentId);
|
|
9200
9225
|
if (!target) {
|
|
@@ -9241,7 +9266,7 @@ ${entry}`;
|
|
|
9241
9266
|
});
|
|
9242
9267
|
return;
|
|
9243
9268
|
}
|
|
9244
|
-
let content = await
|
|
9269
|
+
let content = await readFile14(projectPath, "utf-8");
|
|
9245
9270
|
content = setTopLevelField(content, "workspace", workspace ?? null);
|
|
9246
9271
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
9247
9272
|
await writeFileForce(projectPath, content);
|
|
@@ -9278,7 +9303,7 @@ ${entry}`;
|
|
|
9278
9303
|
return;
|
|
9279
9304
|
}
|
|
9280
9305
|
const assignmentPath = resolve18(resolved.assignmentDir, "assignment.md");
|
|
9281
|
-
let content = await
|
|
9306
|
+
let content = await readFile14(assignmentPath, "utf-8");
|
|
9282
9307
|
content = setTopLevelField(content, "workspaceGroup", workspaceGroup ?? null);
|
|
9283
9308
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
9284
9309
|
await writeFileForce(assignmentPath, content);
|
|
@@ -9373,7 +9398,7 @@ ${entry}`;
|
|
|
9373
9398
|
return;
|
|
9374
9399
|
}
|
|
9375
9400
|
const assignmentPath = resolve18(resolved.assignmentDir, "assignment.md");
|
|
9376
|
-
const parsedForSlug = parseAssignmentFull(await
|
|
9401
|
+
const parsedForSlug = parseAssignmentFull(await readFile14(assignmentPath, "utf-8"));
|
|
9377
9402
|
const assignmentSlugForBranch = parsedForSlug.slug || resolved.id;
|
|
9378
9403
|
await handleWorktreeCreate(req, res, {
|
|
9379
9404
|
assignmentPath,
|
|
@@ -9402,7 +9427,7 @@ ${entry}`;
|
|
|
9402
9427
|
res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}, or null to clear.` });
|
|
9403
9428
|
return;
|
|
9404
9429
|
}
|
|
9405
|
-
let content = await
|
|
9430
|
+
let content = await readFile14(projectPath, "utf-8");
|
|
9406
9431
|
content = setTopLevelField(content, "statusOverride", status ?? null);
|
|
9407
9432
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
9408
9433
|
await writeFileForce(projectPath, content);
|
|
@@ -9435,7 +9460,7 @@ ${entry}`;
|
|
|
9435
9460
|
res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}.` });
|
|
9436
9461
|
return;
|
|
9437
9462
|
}
|
|
9438
|
-
let content = await
|
|
9463
|
+
let content = await readFile14(assignmentPath, "utf-8");
|
|
9439
9464
|
content = setTopLevelField(content, "status", status);
|
|
9440
9465
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
9441
9466
|
if (status !== "blocked") {
|
|
@@ -9469,7 +9494,7 @@ ${entry}`;
|
|
|
9469
9494
|
res.status(400).json({ error: validation.error });
|
|
9470
9495
|
return;
|
|
9471
9496
|
}
|
|
9472
|
-
let content = await
|
|
9497
|
+
let content = await readFile14(assignmentPath, "utf-8");
|
|
9473
9498
|
content = setTopLevelField(content, "assignee", validation.value);
|
|
9474
9499
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
9475
9500
|
await writeFileForce(assignmentPath, content);
|
|
@@ -9500,7 +9525,7 @@ ${entry}`;
|
|
|
9500
9525
|
res.status(400).json({ error: validation.error });
|
|
9501
9526
|
return;
|
|
9502
9527
|
}
|
|
9503
|
-
let content = await
|
|
9528
|
+
let content = await readFile14(assignmentPath, "utf-8");
|
|
9504
9529
|
content = setTopLevelField(content, "title", validation.value);
|
|
9505
9530
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
9506
9531
|
await writeFileForce(assignmentPath, content);
|
|
@@ -10018,7 +10043,7 @@ ${entry}`;
|
|
|
10018
10043
|
res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}.` });
|
|
10019
10044
|
return;
|
|
10020
10045
|
}
|
|
10021
|
-
let content = await
|
|
10046
|
+
let content = await readFile14(assignmentPath, "utf-8");
|
|
10022
10047
|
content = setTopLevelField(content, "status", status);
|
|
10023
10048
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
10024
10049
|
if (status !== "blocked") {
|
|
@@ -10054,7 +10079,7 @@ ${entry}`;
|
|
|
10054
10079
|
res.status(400).json({ error: validation.error });
|
|
10055
10080
|
return;
|
|
10056
10081
|
}
|
|
10057
|
-
let content = await
|
|
10082
|
+
let content = await readFile14(assignmentPath, "utf-8");
|
|
10058
10083
|
content = setTopLevelField(content, "assignee", validation.value);
|
|
10059
10084
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
10060
10085
|
await writeFileForce(assignmentPath, content);
|
|
@@ -10087,7 +10112,7 @@ ${entry}`;
|
|
|
10087
10112
|
res.status(400).json({ error: validation.error });
|
|
10088
10113
|
return;
|
|
10089
10114
|
}
|
|
10090
|
-
let content = await
|
|
10115
|
+
let content = await readFile14(assignmentPath, "utf-8");
|
|
10091
10116
|
content = setTopLevelField(content, "title", validation.value);
|
|
10092
10117
|
content = setTopLevelField(content, "updated", nowTimestamp());
|
|
10093
10118
|
await writeFileForce(assignmentPath, content);
|
|
@@ -10156,7 +10181,7 @@ ${entry}`;
|
|
|
10156
10181
|
if (!await fileExists(assignmentPath)) {
|
|
10157
10182
|
throw new Error("assignment file not found");
|
|
10158
10183
|
}
|
|
10159
|
-
let content = await
|
|
10184
|
+
let content = await readFile14(assignmentPath, "utf-8");
|
|
10160
10185
|
content = setTopLevelField(content, "status", status);
|
|
10161
10186
|
content = setTopLevelField(content, "updated", timestamp);
|
|
10162
10187
|
if (status !== "blocked") {
|
|
@@ -10312,7 +10337,7 @@ async function appendCommentTo(assignmentDir, assignmentRef, req, res, reloadDet
|
|
|
10312
10337
|
let currentContent;
|
|
10313
10338
|
let currentCount = 0;
|
|
10314
10339
|
if (await fileExists(commentsPath)) {
|
|
10315
|
-
currentContent = await
|
|
10340
|
+
currentContent = await readFile14(commentsPath, "utf-8");
|
|
10316
10341
|
const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
|
|
10317
10342
|
if (countMatch) currentCount = parseInt(countMatch[1], 10);
|
|
10318
10343
|
} else {
|
|
@@ -10352,7 +10377,7 @@ async function toggleCommentResolvedAt(assignmentDir, commentId, req, res, reloa
|
|
|
10352
10377
|
res.status(400).json({ error: "resolved (boolean) is required" });
|
|
10353
10378
|
return;
|
|
10354
10379
|
}
|
|
10355
|
-
const content = await
|
|
10380
|
+
const content = await readFile14(commentsPath, "utf-8");
|
|
10356
10381
|
const parsed = parseComments(content);
|
|
10357
10382
|
const target = parsed.entries.find((e) => e.id === commentId);
|
|
10358
10383
|
if (!target) {
|
|
@@ -11080,26 +11105,26 @@ import { Router as Router8 } from "express";
|
|
|
11080
11105
|
|
|
11081
11106
|
// src/utils/status-config-resolution.ts
|
|
11082
11107
|
init_frontmatter();
|
|
11083
|
-
import { readFile as
|
|
11108
|
+
import { readFile as readFile15, writeFile as writeFile4, rm as rm2 } from "fs/promises";
|
|
11084
11109
|
import { dirname as dirname4 } from "path";
|
|
11085
11110
|
|
|
11086
11111
|
// src/utils/assignment-walk.ts
|
|
11087
11112
|
init_fs();
|
|
11088
11113
|
import { resolve as resolve20 } from "path";
|
|
11089
|
-
import { readdir as
|
|
11114
|
+
import { readdir as readdir9 } from "fs/promises";
|
|
11090
11115
|
async function listAssignmentsByProject(projectsDir, standaloneDir) {
|
|
11091
11116
|
const result = {
|
|
11092
11117
|
withAssignmentMd: [],
|
|
11093
11118
|
orphanFolders: []
|
|
11094
11119
|
};
|
|
11095
11120
|
if (await fileExists(projectsDir)) {
|
|
11096
|
-
const projects = await
|
|
11121
|
+
const projects = await readdir9(projectsDir, { withFileTypes: true });
|
|
11097
11122
|
for (const m of projects) {
|
|
11098
11123
|
if (!m.isDirectory()) continue;
|
|
11099
11124
|
if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
|
|
11100
11125
|
const assignmentsDir2 = resolve20(projectsDir, m.name, "assignments");
|
|
11101
11126
|
if (!await fileExists(assignmentsDir2)) continue;
|
|
11102
|
-
const entries = await
|
|
11127
|
+
const entries = await readdir9(assignmentsDir2, { withFileTypes: true });
|
|
11103
11128
|
for (const a of entries) {
|
|
11104
11129
|
if (!a.isDirectory()) continue;
|
|
11105
11130
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
@@ -11121,7 +11146,7 @@ async function listAssignmentsByProject(projectsDir, standaloneDir) {
|
|
|
11121
11146
|
}
|
|
11122
11147
|
}
|
|
11123
11148
|
if (standaloneDir !== null && await fileExists(standaloneDir)) {
|
|
11124
|
-
const entries = await
|
|
11149
|
+
const entries = await readdir9(standaloneDir, { withFileTypes: true });
|
|
11125
11150
|
for (const a of entries) {
|
|
11126
11151
|
if (!a.isDirectory()) continue;
|
|
11127
11152
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
@@ -11163,7 +11188,7 @@ async function scanAssignmentsByStatus(projectsDir, standaloneDir, ids) {
|
|
|
11163
11188
|
const assignmentPath = `${entry.assignmentDir}/assignment.md`;
|
|
11164
11189
|
let content;
|
|
11165
11190
|
try {
|
|
11166
|
-
content = await
|
|
11191
|
+
content = await readFile15(assignmentPath, "utf-8");
|
|
11167
11192
|
} catch (err) {
|
|
11168
11193
|
const code = err?.code;
|
|
11169
11194
|
if (code === "ENOENT") {
|
|
@@ -11226,7 +11251,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
|
|
|
11226
11251
|
const list = affected.get(r.id) ?? [];
|
|
11227
11252
|
for (const a of list) {
|
|
11228
11253
|
try {
|
|
11229
|
-
const content = await
|
|
11254
|
+
const content = await readFile15(a.path, "utf-8");
|
|
11230
11255
|
buffer.set(a.path, content);
|
|
11231
11256
|
} catch (err) {
|
|
11232
11257
|
const code = err?.code;
|
|
@@ -11254,7 +11279,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
|
|
|
11254
11279
|
for (const a of list) {
|
|
11255
11280
|
let current;
|
|
11256
11281
|
try {
|
|
11257
|
-
current = await
|
|
11282
|
+
current = await readFile15(a.path, "utf-8");
|
|
11258
11283
|
} catch (err) {
|
|
11259
11284
|
const code = err?.code;
|
|
11260
11285
|
if (code === "ENOENT") {
|
|
@@ -11303,7 +11328,7 @@ async function applyStatusResolutions(resolutions, affected, validTargets) {
|
|
|
11303
11328
|
const list = affected.get(r.id) ?? [];
|
|
11304
11329
|
for (const a of list) {
|
|
11305
11330
|
try {
|
|
11306
|
-
const current = await
|
|
11331
|
+
const current = await readFile15(a.path, "utf-8");
|
|
11307
11332
|
const fm = parseAssignmentFrontmatter(current);
|
|
11308
11333
|
if (fm.status !== r.id) {
|
|
11309
11334
|
console.warn(
|
|
@@ -11439,6 +11464,7 @@ function mapResolutionErrorToHttp(err, applied) {
|
|
|
11439
11464
|
}
|
|
11440
11465
|
function createStatusConfigRouter(projectsDir, assignmentsDir2) {
|
|
11441
11466
|
const router = Router8();
|
|
11467
|
+
installRecordsInvalidation(router);
|
|
11442
11468
|
router.get("/", async (_req, res) => {
|
|
11443
11469
|
try {
|
|
11444
11470
|
const config = await getStatusConfig();
|
|
@@ -12149,7 +12175,7 @@ init_timestamp();
|
|
|
12149
12175
|
init_fs();
|
|
12150
12176
|
import { Router as Router11 } from "express";
|
|
12151
12177
|
import { resolve as resolve23 } from "path";
|
|
12152
|
-
import { readFile as
|
|
12178
|
+
import { readFile as readFile16 } from "fs/promises";
|
|
12153
12179
|
init_playbooks();
|
|
12154
12180
|
function statusForPlaybookError(code) {
|
|
12155
12181
|
switch (code) {
|
|
@@ -12232,7 +12258,7 @@ function createPlaybooksRouter(playbooksDir2) {
|
|
|
12232
12258
|
return;
|
|
12233
12259
|
}
|
|
12234
12260
|
const filePath = resolve23(playbooksDir2, resolved.filename);
|
|
12235
|
-
const content = await
|
|
12261
|
+
const content = await readFile16(filePath, "utf-8");
|
|
12236
12262
|
res.json({
|
|
12237
12263
|
documentType: "playbook",
|
|
12238
12264
|
title: `Edit Playbook: ${resolved.slug}`,
|
|
@@ -12329,7 +12355,7 @@ init_parser2();
|
|
|
12329
12355
|
init_fs();
|
|
12330
12356
|
init_paths();
|
|
12331
12357
|
import { Router as Router12 } from "express";
|
|
12332
|
-
import { readdir as
|
|
12358
|
+
import { readdir as readdir10 } from "fs/promises";
|
|
12333
12359
|
import { resolve as resolvePath, dirname as dirname5 } from "path";
|
|
12334
12360
|
import { rename as rename5, mkdir as mkdir2 } from "fs/promises";
|
|
12335
12361
|
init_slug();
|
|
@@ -12669,6 +12695,7 @@ async function promoteTodosToNewAssignment(groups, options) {
|
|
|
12669
12695
|
}
|
|
12670
12696
|
|
|
12671
12697
|
// src/dashboard/api-todos.ts
|
|
12698
|
+
init_api();
|
|
12672
12699
|
var WORKSPACE_REGEX = /^[a-z0-9_][a-z0-9-]*$/;
|
|
12673
12700
|
function getWorkspaceParam(value) {
|
|
12674
12701
|
if (Array.isArray(value)) {
|
|
@@ -12683,6 +12710,7 @@ function touchItem3(item) {
|
|
|
12683
12710
|
}
|
|
12684
12711
|
function createTodosRouter(todosDir2, broadcast, projectsDir) {
|
|
12685
12712
|
const router = Router12();
|
|
12713
|
+
installRecordsInvalidation(router);
|
|
12686
12714
|
function broadcastUpdate() {
|
|
12687
12715
|
broadcast({ type: "todos-updated", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
12688
12716
|
}
|
|
@@ -12802,7 +12830,7 @@ function createTodosRouter(todosDir2, broadcast, projectsDir) {
|
|
|
12802
12830
|
router.get("/", async (_req, res) => {
|
|
12803
12831
|
try {
|
|
12804
12832
|
await ensureDir(todosDir2);
|
|
12805
|
-
const files = await
|
|
12833
|
+
const files = await readdir10(todosDir2).catch(() => []);
|
|
12806
12834
|
const workspaces = [];
|
|
12807
12835
|
for (const file of files) {
|
|
12808
12836
|
if (typeof file !== "string") continue;
|
|
@@ -12917,7 +12945,7 @@ function createTodosRouter(todosDir2, broadcast, projectsDir) {
|
|
|
12917
12945
|
try {
|
|
12918
12946
|
const { archivePath: archivePath2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports));
|
|
12919
12947
|
const { resolve: resolve29 } = await import("path");
|
|
12920
|
-
const { readFile:
|
|
12948
|
+
const { readFile: readFile20 } = await import("fs/promises");
|
|
12921
12949
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
12922
12950
|
const workspace = getWorkspaceParam(req.params.workspace);
|
|
12923
12951
|
const checklist = await readChecklist(todosDir2, workspace);
|
|
@@ -12936,7 +12964,7 @@ function createTodosRouter(todosDir2, broadcast, projectsDir) {
|
|
|
12936
12964
|
await ensureDir(resolve29(todosDir2, "archive"));
|
|
12937
12965
|
let archContent = "";
|
|
12938
12966
|
if (await fileExists(archFile)) {
|
|
12939
|
-
archContent = await
|
|
12967
|
+
archContent = await readFile20(archFile, "utf-8");
|
|
12940
12968
|
archContent = archContent.trimEnd() + "\n\n";
|
|
12941
12969
|
} else {
|
|
12942
12970
|
archContent = `---
|
|
@@ -13218,7 +13246,7 @@ workspace: ${workspace}
|
|
|
13218
13246
|
const { readConfig: readConfig2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
13219
13247
|
const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
13220
13248
|
const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
13221
|
-
const { readFile:
|
|
13249
|
+
const { readFile: readFile20 } = await import("fs/promises");
|
|
13222
13250
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
13223
13251
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
13224
13252
|
let assignmentRef;
|
|
@@ -13239,7 +13267,7 @@ workspace: ${workspace}
|
|
|
13239
13267
|
}
|
|
13240
13268
|
const assignmentMdPath = resolvePath2(assignmentDir, "assignment.md");
|
|
13241
13269
|
if (!await fileExists2(assignmentMdPath)) return { error: `Target assignment not found: ${assignmentMdPath}` };
|
|
13242
|
-
let content = await
|
|
13270
|
+
let content = await readFile20(assignmentMdPath, "utf-8");
|
|
13243
13271
|
content = appendTodosToAssignmentBody2(
|
|
13244
13272
|
content,
|
|
13245
13273
|
items.map((it) => ({
|
|
@@ -13427,8 +13455,9 @@ init_fs();
|
|
|
13427
13455
|
init_paths();
|
|
13428
13456
|
init_slug();
|
|
13429
13457
|
import { Router as Router13 } from "express";
|
|
13430
|
-
import { mkdir as mkdir3, readFile as
|
|
13458
|
+
import { mkdir as mkdir3, readFile as readFile17, rename as rename6 } from "fs/promises";
|
|
13431
13459
|
import { resolve as resolve25, dirname as dirname6 } from "path";
|
|
13460
|
+
init_api();
|
|
13432
13461
|
var WORKSPACE_REGEX2 = /^[a-z0-9_][a-z0-9-]*$/;
|
|
13433
13462
|
function touchItem4(item) {
|
|
13434
13463
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -13477,6 +13506,7 @@ function notFound(res, slug) {
|
|
|
13477
13506
|
}
|
|
13478
13507
|
function createProjectTodosRouter(projectsDir, broadcast, workspaceTodosDir) {
|
|
13479
13508
|
const router = Router13({ mergeParams: true });
|
|
13509
|
+
installRecordsInvalidation(router);
|
|
13480
13510
|
function broadcastUpdate(projectSlug) {
|
|
13481
13511
|
broadcast({ type: "todos-updated", projectSlug, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
13482
13512
|
}
|
|
@@ -13649,7 +13679,7 @@ function createProjectTodosRouter(projectsDir, broadcast, workspaceTodosDir) {
|
|
|
13649
13679
|
const archFile = archivePath(todosDir2, slug, checklist.archiveInterval);
|
|
13650
13680
|
let archContent = "";
|
|
13651
13681
|
if (await fileExists(archFile)) {
|
|
13652
|
-
archContent = await
|
|
13682
|
+
archContent = await readFile17(archFile, "utf-8");
|
|
13653
13683
|
archContent = archContent.trimEnd() + "\n\n";
|
|
13654
13684
|
} else {
|
|
13655
13685
|
archContent = `---
|
|
@@ -14106,7 +14136,7 @@ workspace: ${slug}
|
|
|
14106
14136
|
}
|
|
14107
14137
|
const assignmentMdPath = resolve25(assignmentDir, "assignment.md");
|
|
14108
14138
|
if (!await fileExists(assignmentMdPath)) return { error: `Target assignment not found: ${assignmentMdPath}` };
|
|
14109
|
-
let content = await
|
|
14139
|
+
let content = await readFile17(assignmentMdPath, "utf-8");
|
|
14110
14140
|
content = appendTodosToAssignmentBody2(
|
|
14111
14141
|
content,
|
|
14112
14142
|
items.map((it) => ({
|
|
@@ -14302,7 +14332,7 @@ workspace: ${slug}
|
|
|
14302
14332
|
|
|
14303
14333
|
// src/dashboard/api-bundles.ts
|
|
14304
14334
|
import { Router as Router14 } from "express";
|
|
14305
|
-
import { readdir as
|
|
14335
|
+
import { readdir as readdir11 } from "fs/promises";
|
|
14306
14336
|
|
|
14307
14337
|
// src/todos/bundle-parser.ts
|
|
14308
14338
|
init_parser();
|
|
@@ -14310,7 +14340,7 @@ init_fs();
|
|
|
14310
14340
|
init_paths();
|
|
14311
14341
|
init_parser2();
|
|
14312
14342
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
14313
|
-
import { readFile as
|
|
14343
|
+
import { readFile as readFile18 } from "fs/promises";
|
|
14314
14344
|
var BUNDLE_ID_REGEX = /^[a-f0-9]{4}$/;
|
|
14315
14345
|
var SCOPE_VALUES = /* @__PURE__ */ new Set(["workspace", "project", "global"]);
|
|
14316
14346
|
var SCOPE_ID_REGEX = /^[a-z0-9_][a-z0-9_-]*$/;
|
|
@@ -14377,7 +14407,7 @@ function parseBundles(content) {
|
|
|
14377
14407
|
async function readBundles(todosDir2) {
|
|
14378
14408
|
const path = bundlesPath(todosDir2);
|
|
14379
14409
|
if (!await fileExists(path)) return [];
|
|
14380
|
-
const content = await
|
|
14410
|
+
const content = await readFile18(path, "utf-8");
|
|
14381
14411
|
return parseBundles(content).bundles;
|
|
14382
14412
|
}
|
|
14383
14413
|
|
|
@@ -14426,7 +14456,7 @@ function createBundlesRouter(todosDir2, broadcast) {
|
|
|
14426
14456
|
try {
|
|
14427
14457
|
await ensureDir(todosDir2);
|
|
14428
14458
|
const bundles = await readBundles(todosDir2);
|
|
14429
|
-
const workspaceFiles = await
|
|
14459
|
+
const workspaceFiles = await readdir11(todosDir2).catch(() => []);
|
|
14430
14460
|
const itemsByKey = /* @__PURE__ */ new Map();
|
|
14431
14461
|
for (const f of workspaceFiles) {
|
|
14432
14462
|
if (typeof f !== "string") continue;
|
|
@@ -14540,6 +14570,8 @@ function createProjectBundlesRouter(projectsDir, broadcast) {
|
|
|
14540
14570
|
|
|
14541
14571
|
// src/dashboard/api-backup.ts
|
|
14542
14572
|
init_config2();
|
|
14573
|
+
init_api();
|
|
14574
|
+
init_scanner();
|
|
14543
14575
|
import { Router as Router16 } from "express";
|
|
14544
14576
|
|
|
14545
14577
|
// src/utils/github-backup.ts
|
|
@@ -14548,7 +14580,7 @@ init_fs();
|
|
|
14548
14580
|
init_config2();
|
|
14549
14581
|
import { execFile as execFile2 } from "child_process";
|
|
14550
14582
|
import { promisify as promisify2 } from "util";
|
|
14551
|
-
import { cp, mkdtemp, rm as rm3, readFile as
|
|
14583
|
+
import { cp, mkdtemp, rm as rm3, readFile as readFile19, writeFile as writeFile5, unlink as unlink5, stat, open as open2, rename as rename7 } from "fs/promises";
|
|
14552
14584
|
import { resolve as resolve27, join as join2 } from "path";
|
|
14553
14585
|
import { tmpdir } from "os";
|
|
14554
14586
|
var exec2 = promisify2(execFile2);
|
|
@@ -14609,7 +14641,7 @@ async function acquireLock() {
|
|
|
14609
14641
|
return lockPath;
|
|
14610
14642
|
} catch (err) {
|
|
14611
14643
|
if (err.code === "EEXIST") {
|
|
14612
|
-
const pid = await
|
|
14644
|
+
const pid = await readFile19(lockPath, "utf-8").catch(() => "");
|
|
14613
14645
|
throw new Error(
|
|
14614
14646
|
`Backup operation already in progress (lock file at ${lockPath}, pid ${pid.trim() || "unknown"}). If stale, delete the file and retry.`
|
|
14615
14647
|
);
|
|
@@ -14656,7 +14688,7 @@ function resolveCategoriesStrict(csv) {
|
|
|
14656
14688
|
return parseCategoriesStrict(parts);
|
|
14657
14689
|
}
|
|
14658
14690
|
async function readSanitizedConfig(configPath) {
|
|
14659
|
-
const content = await
|
|
14691
|
+
const content = await readFile19(configPath, "utf-8");
|
|
14660
14692
|
return content.replace(/^(\s*lastBackup:\s*).*$/m, "$1null").replace(/^(\s*lastRestore:\s*).*$/m, "$1null");
|
|
14661
14693
|
}
|
|
14662
14694
|
async function backupToGithub(overrides) {
|
|
@@ -14938,6 +14970,9 @@ function createBackupRouter() {
|
|
|
14938
14970
|
success: false,
|
|
14939
14971
|
error: error instanceof Error ? error.message : String(error)
|
|
14940
14972
|
});
|
|
14973
|
+
} finally {
|
|
14974
|
+
invalidateRecordsCache();
|
|
14975
|
+
clearScanCache();
|
|
14941
14976
|
}
|
|
14942
14977
|
});
|
|
14943
14978
|
return router;
|
|
@@ -15176,6 +15211,9 @@ function createDashboardServer(options) {
|
|
|
15176
15211
|
});
|
|
15177
15212
|
});
|
|
15178
15213
|
function broadcast(message) {
|
|
15214
|
+
if (message.type === "project-updated" || message.type === "assignment-updated") {
|
|
15215
|
+
invalidateRecordsCache();
|
|
15216
|
+
}
|
|
15179
15217
|
const data = JSON.stringify(message);
|
|
15180
15218
|
for (const client of clients) {
|
|
15181
15219
|
if (client.readyState === WebSocket.OPEN) {
|