syntaur 0.18.0 → 0.20.0
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/dashboard/dist/assets/{_basePickBy-Cx0q44iE.js → _basePickBy-CFyquBjL.js} +1 -1
- package/dashboard/dist/assets/{_baseUniq-CfFbkKif.js → _baseUniq-S9hlXGZI.js} +1 -1
- package/dashboard/dist/assets/{arc-XluBomWz.js → arc-DNRWeoWH.js} +1 -1
- package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-CihGybG6.js → architectureDiagram-2XIMDMQ5-BujnKgOd.js} +1 -1
- package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-BPUKO5fj.js → blockDiagram-WCTKOSBZ-DOaDmG_o.js} +1 -1
- package/dashboard/dist/assets/{c4Diagram-IC4MRINW-DKVq_j1f.js → c4Diagram-IC4MRINW-BeBwVhQx.js} +1 -1
- package/dashboard/dist/assets/channel-yUe8n53Y.js +1 -0
- package/dashboard/dist/assets/{chunk-4BX2VUAB-DAaE8oN2.js → chunk-4BX2VUAB-CtzM4Epf.js} +1 -1
- package/dashboard/dist/assets/{chunk-55IACEB6-BZoVpy5G.js → chunk-55IACEB6-D2cd1TFn.js} +1 -1
- package/dashboard/dist/assets/{chunk-FMBD7UC4-Cuo9M0jI.js → chunk-FMBD7UC4-DAqRATzJ.js} +1 -1
- package/dashboard/dist/assets/{chunk-JSJVCQXG-DGzypY5S.js → chunk-JSJVCQXG-CIEi981s.js} +1 -1
- package/dashboard/dist/assets/{chunk-KX2RTZJC-DfxYbrKI.js → chunk-KX2RTZJC-BMv4Xwdm.js} +1 -1
- package/dashboard/dist/assets/{chunk-NQ4KR5QH-BEbwpe5N.js → chunk-NQ4KR5QH-BKTm5ctg.js} +1 -1
- package/dashboard/dist/assets/{chunk-QZHKN3VN-DDjrkJqg.js → chunk-QZHKN3VN-DjmLZcpF.js} +1 -1
- package/dashboard/dist/assets/{chunk-WL4C6EOR-BSMHQ3p4.js → chunk-WL4C6EOR-CphX8dKS.js} +1 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-C0f25bKl.js +1 -0
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-C0f25bKl.js +1 -0
- package/dashboard/dist/assets/clone-D-aj2eAK.js +1 -0
- package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-BgUyyImN.js → cose-bilkent-S5V4N54A-BZ-8TY36.js} +1 -1
- package/dashboard/dist/assets/{dagre-KLK3FWXG-zAe7pq6F.js → dagre-KLK3FWXG-Bx5whsTp.js} +1 -1
- package/dashboard/dist/assets/{diagram-E7M64L7V-B1bVb4ll.js → diagram-E7M64L7V-BVOQiv2e.js} +1 -1
- package/dashboard/dist/assets/{diagram-IFDJBPK2-DZCxJIkJ.js → diagram-IFDJBPK2-D7bQBDL_.js} +1 -1
- package/dashboard/dist/assets/{diagram-P4PSJMXO-BuPgL5q1.js → diagram-P4PSJMXO-CwO6aafO.js} +1 -1
- package/dashboard/dist/assets/{erDiagram-INFDFZHY-l3YvnHT9.js → erDiagram-INFDFZHY-DCNz82wG.js} +1 -1
- package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-CLOLQLdW.js → flowDiagram-PKNHOUZH-Dq6yNscM.js} +1 -1
- package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-D1yLx2Ap.js → ganttDiagram-A5KZAMGK-B2xtuK0X.js} +1 -1
- package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-uC4rY3UQ.js → gitGraphDiagram-K3NZZRJ6-D8mNkQd3.js} +1 -1
- package/dashboard/dist/assets/{graph-DvUnLFOz.js → graph-DMbUUtaR.js} +1 -1
- package/dashboard/dist/assets/index-BlikGvSd.js +550 -0
- package/dashboard/dist/assets/index-CsyJIOqR.css +1 -0
- package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-B6jLLOUk.js → infoDiagram-LFFYTUFH-BsrDYNxL.js} +1 -1
- package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-CDvhubqL.js → ishikawaDiagram-PHBUUO56-DlUJjWQM.js} +1 -1
- package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-BOtMcoBj.js → journeyDiagram-4ABVD52K-DBbVdCE_.js} +1 -1
- package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-DC05q9p4.js → kanban-definition-K7BYSVSG-Cuv3IHJ_.js} +1 -1
- package/dashboard/dist/assets/{layout-B4jiZlj6.js → layout-CgL4rycu.js} +1 -1
- package/dashboard/dist/assets/{linear-DpQXVSyZ.js → linear-CgnhFMi4.js} +1 -1
- package/dashboard/dist/assets/{mermaid.core-aNcWKq-4.js → mermaid.core-C3eCFd8P.js} +4 -4
- package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-Dv4qo7wQ.js → mindmap-definition-YRQLILUH-Bsm6cGkL.js} +1 -1
- package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-ByRY4ahm.js → pieDiagram-SKSYHLDU-DBRLfMtc.js} +1 -1
- package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-Dt5_Xjwp.js → quadrantDiagram-337W2JSQ-CES4NNws.js} +1 -1
- package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-CSRV6VcE.js → requirementDiagram-Z7DCOOCP-B0cUyFW2.js} +1 -1
- package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-DA8gxGEq.js → sankeyDiagram-WA2Y5GQK-LVaVg6wF.js} +1 -1
- package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-CVZr9aKv.js → sequenceDiagram-2WXFIKYE-BISSySYI.js} +1 -1
- package/dashboard/dist/assets/{stateDiagram-RAJIS63D-yIU4X0e8.js → stateDiagram-RAJIS63D-rdMNHAXE.js} +1 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-ti7j33kD.js +1 -0
- package/dashboard/dist/assets/{timeline-definition-YZTLITO2-DnfvRVEJ.js → timeline-definition-YZTLITO2-fVRyntnZ.js} +1 -1
- package/dashboard/dist/assets/{treemap-KZPCXAKY-p5sbmBwK.js → treemap-KZPCXAKY-ECgEamZF.js} +1 -1
- package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-Do2Vp3nv.js → vennDiagram-LZ73GAT5-DkL5XCcJ.js} +1 -1
- package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-BrfAGQWt.js → xychartDiagram-JWTSCODW-DSQCD2gS.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/dashboard/server.js +328 -123
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.js +795 -488
- package/dist/index.js.map +1 -1
- package/dist/launch/index.js +75 -10
- package/dist/launch/index.js.map +1 -1
- package/package.json +1 -1
- package/dashboard/dist/assets/channel-mNClnLVi.js +0 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-Dv6vdcHm.js +0 -1
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-Dv6vdcHm.js +0 -1
- package/dashboard/dist/assets/clone-CRLXHkrd.js +0 -1
- package/dashboard/dist/assets/index-BoVXMp5e.js +0 -535
- package/dashboard/dist/assets/index-XappA8I7.css +0 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-DMBx3hAg.js +0 -1
package/dist/index.js
CHANGED
|
@@ -311,6 +311,9 @@ function parseAssignmentFull(fileContent) {
|
|
|
311
311
|
},
|
|
312
312
|
externalIds: parseExternalIds(fm),
|
|
313
313
|
tags: parseListField(fm, "tags"),
|
|
314
|
+
archived: getField(fm, "archived") === "true",
|
|
315
|
+
archivedAt: getField(fm, "archivedAt"),
|
|
316
|
+
archivedReason: getField(fm, "archivedReason"),
|
|
314
317
|
created: getField(fm, "created") ?? "",
|
|
315
318
|
updated: getField(fm, "updated") ?? "",
|
|
316
319
|
body
|
|
@@ -506,6 +509,53 @@ async function migrateLegacyProjectFiles(projectsDir2) {
|
|
|
506
509
|
}
|
|
507
510
|
return result;
|
|
508
511
|
}
|
|
512
|
+
function setFrontmatterField(content, key, value) {
|
|
513
|
+
const formatted = value === null ? "null" : typeof value === "boolean" ? String(value) : value;
|
|
514
|
+
const fieldRegex = new RegExp(`^(${key}:)\\s*.*$`, "m");
|
|
515
|
+
if (fieldRegex.test(content)) {
|
|
516
|
+
return content.replace(fieldRegex, `$1 ${formatted}`);
|
|
517
|
+
}
|
|
518
|
+
const closingIdx = content.indexOf("\n---", 4);
|
|
519
|
+
if (closingIdx === -1) return content;
|
|
520
|
+
return `${content.slice(0, closingIdx)}
|
|
521
|
+
${key}: ${formatted}${content.slice(closingIdx)}`;
|
|
522
|
+
}
|
|
523
|
+
function readFrontmatterField(content, key) {
|
|
524
|
+
const match = content.match(new RegExp(`^${key}:\\s*(.*)$`, "m"));
|
|
525
|
+
if (!match) return null;
|
|
526
|
+
const raw = match[1].trim().replace(/^['"]|['"]$/g, "");
|
|
527
|
+
return raw === "" || raw === "null" ? null : raw;
|
|
528
|
+
}
|
|
529
|
+
async function migrateLegacyArchivedProjects(projectsDir2) {
|
|
530
|
+
const result = { reconciled: [] };
|
|
531
|
+
if (!await fileExists(projectsDir2)) return result;
|
|
532
|
+
let entries;
|
|
533
|
+
try {
|
|
534
|
+
entries = await readdir(projectsDir2, { withFileTypes: true });
|
|
535
|
+
} catch {
|
|
536
|
+
return result;
|
|
537
|
+
}
|
|
538
|
+
for (const entry of entries) {
|
|
539
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
540
|
+
const projectMd = resolve2(projectsDir2, entry.name, "project.md");
|
|
541
|
+
try {
|
|
542
|
+
if (!await fileExists(projectMd)) continue;
|
|
543
|
+
const content = await readFile(projectMd, "utf-8");
|
|
544
|
+
if (readFrontmatterField(content, "statusOverride") !== "archived") continue;
|
|
545
|
+
let next = setFrontmatterField(content, "archived", true);
|
|
546
|
+
if (readFrontmatterField(content, "archivedAt") === null) {
|
|
547
|
+
next = setFrontmatterField(next, "archivedAt", nowTimestamp());
|
|
548
|
+
}
|
|
549
|
+
next = setFrontmatterField(next, "statusOverride", null);
|
|
550
|
+
next = setFrontmatterField(next, "updated", nowTimestamp());
|
|
551
|
+
await writeFile2(projectMd, next, "utf-8");
|
|
552
|
+
result.reconciled.push(entry.name);
|
|
553
|
+
} catch {
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
return result;
|
|
558
|
+
}
|
|
509
559
|
async function migrateLegacyConfig(configPath) {
|
|
510
560
|
const result = {
|
|
511
561
|
renamedField: false,
|
|
@@ -599,6 +649,7 @@ var init_fs_migration = __esm({
|
|
|
599
649
|
"src/utils/fs-migration.ts"() {
|
|
600
650
|
"use strict";
|
|
601
651
|
init_fs();
|
|
652
|
+
init_timestamp();
|
|
602
653
|
}
|
|
603
654
|
});
|
|
604
655
|
|
|
@@ -2047,7 +2098,7 @@ import { readdir as readdir2, readFile as readFile3, unlink } from "fs/promises"
|
|
|
2047
2098
|
function escapeRegExp(value) {
|
|
2048
2099
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2049
2100
|
}
|
|
2050
|
-
function
|
|
2101
|
+
function setFrontmatterField2(content, key, value) {
|
|
2051
2102
|
const regex = new RegExp(`^(${escapeRegExp(key)}:)\\s*.*$`, "m");
|
|
2052
2103
|
if (regex.test(content)) {
|
|
2053
2104
|
return content.replace(regex, `$1 ${value}`);
|
|
@@ -2196,8 +2247,8 @@ async function renamePlaybook(playbooksDir3, oldSlug, newSlug) {
|
|
|
2196
2247
|
}
|
|
2197
2248
|
}
|
|
2198
2249
|
const raw = await readFile3(oldPath, "utf-8");
|
|
2199
|
-
let next =
|
|
2200
|
-
next =
|
|
2250
|
+
let next = setFrontmatterField2(raw, "slug", newSlug);
|
|
2251
|
+
next = setFrontmatterField2(next, "updated", `"${nowTimestamp()}"`);
|
|
2201
2252
|
await writeFileForce(newPath, next);
|
|
2202
2253
|
if (!renamedInPlace) {
|
|
2203
2254
|
await unlink(oldPath);
|
|
@@ -2375,6 +2426,9 @@ workspace:
|
|
|
2375
2426
|
branch: null
|
|
2376
2427
|
parentBranch: null
|
|
2377
2428
|
tags: []
|
|
2429
|
+
archived: false
|
|
2430
|
+
archivedAt: null
|
|
2431
|
+
archivedReason: null
|
|
2378
2432
|
---
|
|
2379
2433
|
|
|
2380
2434
|
# ${params2.title}
|
|
@@ -3568,10 +3622,14 @@ function parseAssignmentFrontmatter(fileContent) {
|
|
|
3568
3622
|
links: parseLinks(frontmatter),
|
|
3569
3623
|
blockedReason: getField2("blockedReason"),
|
|
3570
3624
|
workspace: parseWorkspace(frontmatter),
|
|
3571
|
-
tags: parseTags(frontmatter)
|
|
3625
|
+
tags: parseTags(frontmatter),
|
|
3626
|
+
archived: getField2("archived") === "true",
|
|
3627
|
+
archivedAt: getField2("archivedAt"),
|
|
3628
|
+
archivedReason: getField2("archivedReason")
|
|
3572
3629
|
};
|
|
3573
3630
|
}
|
|
3574
3631
|
function formatYamlValue(value) {
|
|
3632
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
3575
3633
|
if (value === null) return "null";
|
|
3576
3634
|
if (/^\d{4}-\d{2}-\d{2}T/.test(value)) {
|
|
3577
3635
|
return `"${value}"`;
|
|
@@ -3590,6 +3648,12 @@ function updateAssignmentFile(fileContent, updates) {
|
|
|
3590
3648
|
const fieldRegex = new RegExp(`^(${key}:)\\s*.*$`, "m");
|
|
3591
3649
|
if (fieldRegex.test(result)) {
|
|
3592
3650
|
result = result.replace(fieldRegex, `$1 ${formatted}`);
|
|
3651
|
+
} else {
|
|
3652
|
+
const closeIdx = result.indexOf("\n---", 4);
|
|
3653
|
+
if (closeIdx !== -1) {
|
|
3654
|
+
result = `${result.slice(0, closeIdx)}
|
|
3655
|
+
${key}: ${formatted}${result.slice(closeIdx)}`;
|
|
3656
|
+
}
|
|
3593
3657
|
}
|
|
3594
3658
|
}
|
|
3595
3659
|
return result;
|
|
@@ -5042,8 +5106,8 @@ async function migrateFromMarkdown(projectsDir2) {
|
|
|
5042
5106
|
return allSessions.length;
|
|
5043
5107
|
}
|
|
5044
5108
|
async function parseMarkdownSessionsIndex(filePath, projectSlug) {
|
|
5045
|
-
const { readFile:
|
|
5046
|
-
const raw = await
|
|
5109
|
+
const { readFile: readFile54 } = await import("fs/promises");
|
|
5110
|
+
const raw = await readFile54(filePath, "utf-8");
|
|
5047
5111
|
const sessions = [];
|
|
5048
5112
|
const lines = raw.split("\n");
|
|
5049
5113
|
let inTable = false;
|
|
@@ -5475,8 +5539,8 @@ function scanKey(serversDir2, projectsDir2, assignmentsDir2) {
|
|
|
5475
5539
|
return `${serversDir2}\0${projectsDir2}\0${assignmentsDir2 ?? ""}`;
|
|
5476
5540
|
}
|
|
5477
5541
|
function delay(ms) {
|
|
5478
|
-
return new Promise((
|
|
5479
|
-
const timer2 = setTimeout(
|
|
5542
|
+
return new Promise((resolve79) => {
|
|
5543
|
+
const timer2 = setTimeout(resolve79, ms);
|
|
5480
5544
|
if (typeof timer2.unref === "function") {
|
|
5481
5545
|
timer2.unref();
|
|
5482
5546
|
}
|
|
@@ -5935,6 +5999,12 @@ function setUpdatedField(content, value) {
|
|
|
5935
5999
|
function escapeRegExp2(value) {
|
|
5936
6000
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5937
6001
|
}
|
|
6002
|
+
function isProjectArchived(p) {
|
|
6003
|
+
return p.archived === true;
|
|
6004
|
+
}
|
|
6005
|
+
function activeAssignments(items) {
|
|
6006
|
+
return items.filter((item) => item.archived !== true);
|
|
6007
|
+
}
|
|
5938
6008
|
function createTraces() {
|
|
5939
6009
|
return { entries: [], subPhases: /* @__PURE__ */ new Map() };
|
|
5940
6010
|
}
|
|
@@ -6072,7 +6142,7 @@ function clearStatusConfigCache() {
|
|
|
6072
6142
|
}
|
|
6073
6143
|
async function listProjects(projectsDir2) {
|
|
6074
6144
|
const projectRecords = await listProjectRecords(projectsDir2);
|
|
6075
|
-
return projectRecords.map((record) => record.summary);
|
|
6145
|
+
return projectRecords.filter((record) => !isProjectArchived(record.summary)).map((record) => record.summary);
|
|
6076
6146
|
}
|
|
6077
6147
|
async function readWorkspaceRegistry(projectsDir2) {
|
|
6078
6148
|
const registryPath = resolve16(dirname3(projectsDir2), "workspaces.json");
|
|
@@ -6204,7 +6274,9 @@ async function getOverview(projectsDir2, serversDir2, assignmentsDir2, options =
|
|
|
6204
6274
|
"list-standalone-records",
|
|
6205
6275
|
() => listStandaloneRecords(assignmentsDir2)
|
|
6206
6276
|
);
|
|
6207
|
-
const
|
|
6277
|
+
const activeProjectRecords = projectRecords.filter((record) => !isProjectArchived(record.summary));
|
|
6278
|
+
const activeStandaloneRecords = standaloneRecords.filter((sr) => sr.record.archived !== true);
|
|
6279
|
+
const recentActivity = buildRecentActivity(activeProjectRecords, activeStandaloneRecords);
|
|
6208
6280
|
const staleLimit = clamp(
|
|
6209
6281
|
Number.isFinite(options.staleLimit) ? Number(options.staleLimit) : STALE_LIMIT_DEFAULT,
|
|
6210
6282
|
1,
|
|
@@ -6263,56 +6335,111 @@ async function getOverview(projectsDir2, serversDir2, assignmentsDir2, options =
|
|
|
6263
6335
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6264
6336
|
firstRun: projectRecords.length === 0 && standaloneRecords.length === 0,
|
|
6265
6337
|
stats: {
|
|
6266
|
-
activeProjects:
|
|
6267
|
-
inProgressAssignments:
|
|
6338
|
+
activeProjects: activeProjectRecords.filter((record) => record.summary.status === "active").length,
|
|
6339
|
+
inProgressAssignments: activeProjectRecords.reduce(
|
|
6268
6340
|
(total, record) => total + (record.summary.progress["in_progress"] ?? 0),
|
|
6269
6341
|
0
|
|
6270
6342
|
),
|
|
6271
|
-
blockedAssignments:
|
|
6343
|
+
blockedAssignments: activeProjectRecords.reduce(
|
|
6272
6344
|
(total, record) => total + (record.summary.progress["blocked"] ?? 0),
|
|
6273
6345
|
0
|
|
6274
6346
|
),
|
|
6275
|
-
reviewAssignments:
|
|
6347
|
+
reviewAssignments: activeProjectRecords.reduce(
|
|
6276
6348
|
(total, record) => total + (record.summary.progress["review"] ?? 0),
|
|
6277
6349
|
0
|
|
6278
6350
|
),
|
|
6279
|
-
failedAssignments:
|
|
6351
|
+
failedAssignments: activeProjectRecords.reduce(
|
|
6280
6352
|
(total, record) => total + (record.summary.progress["failed"] ?? 0),
|
|
6281
6353
|
0
|
|
6282
6354
|
),
|
|
6283
|
-
staleAssignments:
|
|
6284
|
-
(total, record) => total + record.assignments.filter((assignment) => isStale(assignment.updated)).length,
|
|
6355
|
+
staleAssignments: activeProjectRecords.reduce(
|
|
6356
|
+
(total, record) => total + activeAssignments(record.assignments).filter((assignment) => isStale(assignment.updated)).length,
|
|
6285
6357
|
0
|
|
6286
|
-
) +
|
|
6358
|
+
) + activeStandaloneRecords.filter((sr) => isStale(sr.record.updated)).length
|
|
6287
6359
|
},
|
|
6288
6360
|
hero,
|
|
6289
6361
|
segments,
|
|
6290
6362
|
recentSessions,
|
|
6291
|
-
recentProjects:
|
|
6363
|
+
recentProjects: activeProjectRecords.map((record) => record.summary).sort((left, right) => compareTimestamps(right.updated, left.updated)).slice(0, RECENT_PROJECTS_LIMIT),
|
|
6292
6364
|
recentActivity: recentActivity.slice(0, RECENT_ACTIVITY_LIMIT),
|
|
6293
6365
|
serverStats
|
|
6294
6366
|
};
|
|
6295
6367
|
}
|
|
6296
|
-
async function listAssignmentsBoard(projectsDir2, assignmentsDir2) {
|
|
6368
|
+
async function listAssignmentsBoard(projectsDir2, assignmentsDir2, options = {}) {
|
|
6369
|
+
const mode = options.archived ?? "exclude";
|
|
6297
6370
|
const projectRecords = await listProjectRecords(projectsDir2);
|
|
6298
6371
|
const projectItems = await Promise.all(
|
|
6299
|
-
projectRecords.flatMap(
|
|
6300
|
-
|
|
6301
|
-
|
|
6372
|
+
projectRecords.flatMap(async (record) => {
|
|
6373
|
+
if (mode === "only") {
|
|
6374
|
+
return Promise.all(
|
|
6375
|
+
record.assignments.filter((assignment) => assignment.archived === true).map(async (assignment) => toAssignmentBoardItem(projectsDir2, record, assignment))
|
|
6376
|
+
);
|
|
6377
|
+
}
|
|
6378
|
+
if (isProjectArchived(record.summary)) return [];
|
|
6379
|
+
return Promise.all(
|
|
6380
|
+
activeAssignments(record.assignments).map(
|
|
6302
6381
|
async (assignment) => toAssignmentBoardItem(projectsDir2, record, assignment)
|
|
6303
6382
|
)
|
|
6304
|
-
)
|
|
6305
|
-
)
|
|
6383
|
+
);
|
|
6384
|
+
})
|
|
6306
6385
|
);
|
|
6307
6386
|
const standaloneRecords = await listStandaloneRecords(assignmentsDir2);
|
|
6387
|
+
const filteredStandalone = mode === "only" ? standaloneRecords.filter((sr) => sr.record.archived === true) : standaloneRecords.filter((sr) => sr.record.archived !== true);
|
|
6308
6388
|
const standaloneItems = await Promise.all(
|
|
6309
|
-
|
|
6389
|
+
filteredStandalone.map(async (sr) => toStandaloneBoardItem(sr))
|
|
6310
6390
|
);
|
|
6311
6391
|
return {
|
|
6312
6392
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6313
6393
|
assignments: [...projectItems.flat(), ...standaloneItems].sort((left, right) => compareTimestamps(right.updated, left.updated))
|
|
6314
6394
|
};
|
|
6315
6395
|
}
|
|
6396
|
+
function toArchivedAssignmentItem(assignment, projectSlug, projectTitle) {
|
|
6397
|
+
return {
|
|
6398
|
+
id: assignment.id,
|
|
6399
|
+
slug: assignment.slug,
|
|
6400
|
+
title: assignment.title,
|
|
6401
|
+
status: assignment.status,
|
|
6402
|
+
type: assignment.type,
|
|
6403
|
+
priority: assignment.priority,
|
|
6404
|
+
projectSlug,
|
|
6405
|
+
projectTitle,
|
|
6406
|
+
archived: assignment.archived,
|
|
6407
|
+
archivedAt: assignment.archivedAt,
|
|
6408
|
+
archivedReason: assignment.archivedReason,
|
|
6409
|
+
updated: assignment.updated
|
|
6410
|
+
};
|
|
6411
|
+
}
|
|
6412
|
+
async function listArchived(projectsDir2, assignmentsDir2) {
|
|
6413
|
+
const projectRecords = await listProjectRecords(projectsDir2);
|
|
6414
|
+
const standaloneRecords = await listStandaloneRecords(assignmentsDir2);
|
|
6415
|
+
const projects = projectRecords.filter((record) => isProjectArchived(record.summary)).map((record) => ({
|
|
6416
|
+
slug: record.summary.slug,
|
|
6417
|
+
title: record.summary.title,
|
|
6418
|
+
archivedAt: record.summary.archivedAt,
|
|
6419
|
+
archivedReason: record.summary.archivedReason,
|
|
6420
|
+
assignments: record.assignments.map(
|
|
6421
|
+
(assignment) => toArchivedAssignmentItem(assignment, record.summary.slug, record.summary.title)
|
|
6422
|
+
).sort((left, right) => compareTimestamps(right.updated, left.updated))
|
|
6423
|
+
})).sort((left, right) => compareTimestamps(right.archivedAt ?? "", left.archivedAt ?? ""));
|
|
6424
|
+
const individuallyArchived = [];
|
|
6425
|
+
for (const record of projectRecords) {
|
|
6426
|
+
if (isProjectArchived(record.summary)) continue;
|
|
6427
|
+
for (const assignment of record.assignments) {
|
|
6428
|
+
if (assignment.archived === true) {
|
|
6429
|
+
individuallyArchived.push(
|
|
6430
|
+
toArchivedAssignmentItem(assignment, record.summary.slug, record.summary.title)
|
|
6431
|
+
);
|
|
6432
|
+
}
|
|
6433
|
+
}
|
|
6434
|
+
}
|
|
6435
|
+
for (const sr of standaloneRecords) {
|
|
6436
|
+
if (sr.record.archived === true) {
|
|
6437
|
+
individuallyArchived.push(toArchivedAssignmentItem(sr.record, null, null));
|
|
6438
|
+
}
|
|
6439
|
+
}
|
|
6440
|
+
individuallyArchived.sort((left, right) => compareTimestamps(right.updated, left.updated));
|
|
6441
|
+
return { projects, assignments: individuallyArchived };
|
|
6442
|
+
}
|
|
6316
6443
|
async function toStandaloneBoardItem(sr) {
|
|
6317
6444
|
return {
|
|
6318
6445
|
...toAssignmentSummary(sr.record),
|
|
@@ -6407,7 +6534,7 @@ async function getProjectDetail(projectsDir2, slug) {
|
|
|
6407
6534
|
const dependencyGraph = await loadDependencyGraph(projectPath, assignments);
|
|
6408
6535
|
const resources = await listResources(projectPath);
|
|
6409
6536
|
const memories = await listMemories(projectPath);
|
|
6410
|
-
const updated = getProjectActivityTimestamp(project.updated, assignments);
|
|
6537
|
+
const updated = getProjectActivityTimestamp(project.updated, activeAssignments(assignments));
|
|
6411
6538
|
return {
|
|
6412
6539
|
slug: project.slug || slug,
|
|
6413
6540
|
title: project.title,
|
|
@@ -6528,6 +6655,9 @@ async function getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug) {
|
|
|
6528
6655
|
projectWorkspace,
|
|
6529
6656
|
externalIds: assignment.externalIds,
|
|
6530
6657
|
tags: assignment.tags,
|
|
6658
|
+
archived: assignment.archived,
|
|
6659
|
+
archivedAt: assignment.archivedAt,
|
|
6660
|
+
archivedReason: assignment.archivedReason,
|
|
6531
6661
|
created: assignment.created,
|
|
6532
6662
|
updated: assignment.updated,
|
|
6533
6663
|
body: assignment.body,
|
|
@@ -6778,6 +6908,9 @@ async function buildStandaloneAssignmentDetail(resolved) {
|
|
|
6778
6908
|
projectWorkspace: assignment.workspaceGroup,
|
|
6779
6909
|
externalIds: assignment.externalIds,
|
|
6780
6910
|
tags: assignment.tags,
|
|
6911
|
+
archived: assignment.archived,
|
|
6912
|
+
archivedAt: assignment.archivedAt,
|
|
6913
|
+
archivedReason: assignment.archivedReason,
|
|
6781
6914
|
created: assignment.created,
|
|
6782
6915
|
updated: assignment.updated,
|
|
6783
6916
|
body: assignment.body,
|
|
@@ -6807,6 +6940,7 @@ async function computeProjectRecords(projectsDir2, traces) {
|
|
|
6807
6940
|
if (!migratedProjectsDirs.has(projectsDir2)) {
|
|
6808
6941
|
migratedProjectsDirs.add(projectsDir2);
|
|
6809
6942
|
await migrateLegacyProjectFiles(projectsDir2);
|
|
6943
|
+
await migrateLegacyArchivedProjects(projectsDir2);
|
|
6810
6944
|
}
|
|
6811
6945
|
const entries = await readdir8(projectsDir2, { withFileTypes: true });
|
|
6812
6946
|
const projectDirs = entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
|
|
@@ -6827,7 +6961,7 @@ async function computeProjectRecords(projectsDir2, traces) {
|
|
|
6827
6961
|
const t2 = traces ? performance.now() : 0;
|
|
6828
6962
|
const rollup = await buildProjectRollup(projectPath, project, assignments, traces);
|
|
6829
6963
|
if (traces) accumulatePhase(traces, "build-rollup", performance.now() - t2);
|
|
6830
|
-
const updated = getProjectActivityTimestamp(project.updated, assignments);
|
|
6964
|
+
const updated = getProjectActivityTimestamp(project.updated, activeAssignments(assignments));
|
|
6831
6965
|
const t3 = traces ? performance.now() : 0;
|
|
6832
6966
|
const dependencyGraph = await loadDependencyGraph(projectPath, assignments);
|
|
6833
6967
|
if (traces) accumulatePhase(traces, "load-dep-graph", performance.now() - t3);
|
|
@@ -7037,9 +7171,10 @@ async function loadDependencyGraph(projectPath, assignments) {
|
|
|
7037
7171
|
return buildDependencyGraph(assignments);
|
|
7038
7172
|
}
|
|
7039
7173
|
async function buildProjectRollup(projectPath, project, assignments, traces) {
|
|
7040
|
-
const
|
|
7174
|
+
const active = activeAssignments(assignments);
|
|
7175
|
+
const progress = { total: active.length };
|
|
7041
7176
|
const perAssignment = await Promise.all(
|
|
7042
|
-
|
|
7177
|
+
active.map(async (assignment) => {
|
|
7043
7178
|
const t0 = traces ? performance.now() : 0;
|
|
7044
7179
|
const openQuestions2 = await countOpenQuestions(projectPath, assignment.slug);
|
|
7045
7180
|
if (traces) accumulatePhase(traces, "count-open-questions", performance.now() - t0);
|
|
@@ -7087,7 +7222,12 @@ function toAssignmentSummary(assignment) {
|
|
|
7087
7222
|
assignee: assignment.assignee,
|
|
7088
7223
|
dependsOn: assignment.dependsOn,
|
|
7089
7224
|
links: assignment.links,
|
|
7090
|
-
|
|
7225
|
+
tags: assignment.tags,
|
|
7226
|
+
created: assignment.created,
|
|
7227
|
+
updated: assignment.updated,
|
|
7228
|
+
archived: assignment.archived,
|
|
7229
|
+
archivedAt: assignment.archivedAt,
|
|
7230
|
+
archivedReason: assignment.archivedReason
|
|
7091
7231
|
};
|
|
7092
7232
|
}
|
|
7093
7233
|
async function toAssignmentBoardItem(projectsDir2, projectRecord, assignment) {
|
|
@@ -7225,12 +7365,14 @@ async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standal
|
|
|
7225
7365
|
const buckets = emptyBuckets();
|
|
7226
7366
|
const newestPool = [];
|
|
7227
7367
|
for (const record of projectRecords) {
|
|
7368
|
+
if (isProjectArchived(record.summary)) continue;
|
|
7228
7369
|
const depMap = /* @__PURE__ */ new Map();
|
|
7229
7370
|
for (const a of record.assignments) {
|
|
7230
7371
|
depMap.set(a.slug, a.status);
|
|
7231
7372
|
}
|
|
7373
|
+
const visibleAssignments = activeAssignments(record.assignments);
|
|
7232
7374
|
const resolvedTransitions = await Promise.all(
|
|
7233
|
-
|
|
7375
|
+
visibleAssignments.map(async (assignment) => {
|
|
7234
7376
|
const t0 = traces ? performance.now() : 0;
|
|
7235
7377
|
const availableTransitions = await getAvailableTransitions(
|
|
7236
7378
|
projectsDir2,
|
|
@@ -7299,7 +7441,7 @@ async function buildOverviewSegmentBuckets(projectsDir2, projectRecords, standal
|
|
|
7299
7441
|
}
|
|
7300
7442
|
}
|
|
7301
7443
|
const resolvedStandaloneTransitions = await Promise.all(
|
|
7302
|
-
standaloneRecords.map(async (sr) => {
|
|
7444
|
+
standaloneRecords.filter((sr) => sr.record.archived !== true).map(async (sr) => {
|
|
7303
7445
|
const t0 = traces ? performance.now() : 0;
|
|
7304
7446
|
const availableTransitions = await getStandaloneAvailableTransitions(sr.record);
|
|
7305
7447
|
if (traces) accumulatePhase(traces, "get-available-transitions", performance.now() - t0);
|
|
@@ -7425,7 +7567,7 @@ function buildRecentActivity(projectRecords, standaloneRecords = []) {
|
|
|
7425
7567
|
assignmentSlug: null,
|
|
7426
7568
|
summary: `Project status is ${record.summary.status}.`
|
|
7427
7569
|
});
|
|
7428
|
-
for (const assignment of record.assignments) {
|
|
7570
|
+
for (const assignment of activeAssignments(record.assignments)) {
|
|
7429
7571
|
activity.push({
|
|
7430
7572
|
id: `assignment:${record.summary.slug}:${assignment.slug}`,
|
|
7431
7573
|
type: "assignment",
|
|
@@ -8128,8 +8270,8 @@ __export(launch_exports, {
|
|
|
8128
8270
|
shellQuote: () => shellQuote
|
|
8129
8271
|
});
|
|
8130
8272
|
import { spawn as spawn3 } from "child_process";
|
|
8131
|
-
import { mkdir as mkdir6, writeFile as
|
|
8132
|
-
import { isAbsolute as isAbsolute5, resolve as
|
|
8273
|
+
import { mkdir as mkdir6, writeFile as writeFile11 } from "fs/promises";
|
|
8274
|
+
import { isAbsolute as isAbsolute5, resolve as resolve44 } from "path";
|
|
8133
8275
|
function shellQuote(arg) {
|
|
8134
8276
|
if (arg === "") return "''";
|
|
8135
8277
|
return `'${arg.replace(/'/g, `'\\''`)}'`;
|
|
@@ -8165,8 +8307,8 @@ async function launchAgent(options) {
|
|
|
8165
8307
|
console.error(`Assignment not found: ${projectSlug}/${assignmentSlug}`);
|
|
8166
8308
|
process.exit(1);
|
|
8167
8309
|
}
|
|
8168
|
-
const projectDir =
|
|
8169
|
-
const assignmentDir =
|
|
8310
|
+
const projectDir = resolve44(projectsDir2, projectSlug);
|
|
8311
|
+
const assignmentDir = resolve44(projectDir, "assignments", assignmentSlug);
|
|
8170
8312
|
let workspaceDir;
|
|
8171
8313
|
if (cwdOverride) {
|
|
8172
8314
|
if (!isExistingDir(cwdOverride)) {
|
|
@@ -8198,7 +8340,7 @@ async function launchAgent(options) {
|
|
|
8198
8340
|
});
|
|
8199
8341
|
if (warning) console.warn(warning);
|
|
8200
8342
|
}
|
|
8201
|
-
const contextDir =
|
|
8343
|
+
const contextDir = resolve44(workspaceDir, ".syntaur");
|
|
8202
8344
|
await mkdir6(contextDir, { recursive: true });
|
|
8203
8345
|
const context = {
|
|
8204
8346
|
projectSlug,
|
|
@@ -8210,8 +8352,8 @@ async function launchAgent(options) {
|
|
|
8210
8352
|
branch: detail.workspace.branch ?? null,
|
|
8211
8353
|
grabbedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
8212
8354
|
};
|
|
8213
|
-
await
|
|
8214
|
-
|
|
8355
|
+
await writeFile11(
|
|
8356
|
+
resolve44(contextDir, "context.json"),
|
|
8215
8357
|
JSON.stringify(context, null, 2) + "\n"
|
|
8216
8358
|
);
|
|
8217
8359
|
const { argv, shellFallbackWarning } = buildAgentArgv(
|
|
@@ -8286,7 +8428,7 @@ import {
|
|
|
8286
8428
|
unlinkSync
|
|
8287
8429
|
} from "fs";
|
|
8288
8430
|
import { fileURLToPath as fileURLToPath6, pathToFileURL } from "url";
|
|
8289
|
-
import { dirname as dirname14, resolve as
|
|
8431
|
+
import { dirname as dirname14, resolve as resolve46, join as join6 } from "path";
|
|
8290
8432
|
import { homedir as homedir7, tmpdir as tmpdir2 } from "os";
|
|
8291
8433
|
import { spawnSync as spawnSync6 } from "child_process";
|
|
8292
8434
|
function syntaurRootMjs() {
|
|
@@ -8308,7 +8450,7 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
|
|
|
8308
8450
|
}
|
|
8309
8451
|
const stateRoot = syntaurRootMjs();
|
|
8310
8452
|
mkdirSync2(stateRoot, { recursive: true });
|
|
8311
|
-
const lockPath =
|
|
8453
|
+
const lockPath = resolve46(stateRoot, "install-url-handler.lock");
|
|
8312
8454
|
let lockFd;
|
|
8313
8455
|
try {
|
|
8314
8456
|
lockFd = openSync(lockPath, "wx");
|
|
@@ -8324,8 +8466,8 @@ async function registerMacosUrlHandler(options = { throwOnFailure: false }) {
|
|
|
8324
8466
|
throw err2;
|
|
8325
8467
|
}
|
|
8326
8468
|
try {
|
|
8327
|
-
const pkgRoot =
|
|
8328
|
-
const cliBin = realpathSync2(
|
|
8469
|
+
const pkgRoot = resolve46(dirname14(fileURLToPath6(import.meta.url)), "..");
|
|
8470
|
+
const cliBin = realpathSync2(resolve46(pkgRoot, "bin/syntaur.js"));
|
|
8329
8471
|
const nodeBin = process.execPath;
|
|
8330
8472
|
const bundleParent = join6(
|
|
8331
8473
|
homedir7(),
|
|
@@ -8675,7 +8817,7 @@ function useProjects(projectsDir2) {
|
|
|
8675
8817
|
if (cancelled) return;
|
|
8676
8818
|
const tree = projects.map((m, i) => {
|
|
8677
8819
|
const detail = details[i];
|
|
8678
|
-
const children = (detail?.assignments ?? []).map((a) => ({
|
|
8820
|
+
const children = (detail?.assignments ?? []).filter((a) => !a.archived).map((a) => ({
|
|
8679
8821
|
id: `a:${m.slug}:${a.slug}`,
|
|
8680
8822
|
kind: "assignment",
|
|
8681
8823
|
label: a.title,
|
|
@@ -9653,6 +9795,7 @@ var SORT_FIELDS = [
|
|
|
9653
9795
|
"priority",
|
|
9654
9796
|
"assignee",
|
|
9655
9797
|
"dependencies",
|
|
9798
|
+
"created",
|
|
9656
9799
|
"updated"
|
|
9657
9800
|
];
|
|
9658
9801
|
var SORT_DIRECTIONS = ["asc", "desc"];
|
|
@@ -9666,6 +9809,37 @@ var GROUPINGS = [
|
|
|
9666
9809
|
"project"
|
|
9667
9810
|
];
|
|
9668
9811
|
var ACTIVITIES = ["all", "stale", "fresh"];
|
|
9812
|
+
var DATE_RANGE_FIELDS = ["created", "updated"];
|
|
9813
|
+
var DATE_RANGE_PRESETS = [
|
|
9814
|
+
"last_24h",
|
|
9815
|
+
"last_7d",
|
|
9816
|
+
"last_30d",
|
|
9817
|
+
"last_90d",
|
|
9818
|
+
"older_7d",
|
|
9819
|
+
"older_30d"
|
|
9820
|
+
];
|
|
9821
|
+
var YMD_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
9822
|
+
function isDateRangeField(v) {
|
|
9823
|
+
return typeof v === "string" && DATE_RANGE_FIELDS.includes(v);
|
|
9824
|
+
}
|
|
9825
|
+
function isDateRangePreset(v) {
|
|
9826
|
+
return typeof v === "string" && DATE_RANGE_PRESETS.includes(v);
|
|
9827
|
+
}
|
|
9828
|
+
function isDateRange(v) {
|
|
9829
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return false;
|
|
9830
|
+
const r = v;
|
|
9831
|
+
if (!isDateRangeField(r.field)) return false;
|
|
9832
|
+
for (const key of Object.keys(r)) {
|
|
9833
|
+
if (key !== "field" && key !== "preset" && key !== "from" && key !== "to") return false;
|
|
9834
|
+
}
|
|
9835
|
+
const hasPreset = r.preset !== void 0;
|
|
9836
|
+
const hasAbsolute = r.from !== void 0 || r.to !== void 0;
|
|
9837
|
+
if (hasPreset && hasAbsolute) return false;
|
|
9838
|
+
if (hasPreset && !isDateRangePreset(r.preset)) return false;
|
|
9839
|
+
if (r.from !== void 0 && (typeof r.from !== "string" || !YMD_RE.test(r.from))) return false;
|
|
9840
|
+
if (r.to !== void 0 && (typeof r.to !== "string" || !YMD_RE.test(r.to))) return false;
|
|
9841
|
+
return true;
|
|
9842
|
+
}
|
|
9669
9843
|
function toFilterValues(v) {
|
|
9670
9844
|
const arr = v === void 0 ? [] : Array.isArray(v) ? v : [v];
|
|
9671
9845
|
const out = [];
|
|
@@ -9841,7 +10015,7 @@ function isViewPrefsDefaults(file) {
|
|
|
9841
10015
|
if (g.density !== d.density) return false;
|
|
9842
10016
|
if (g.grouping !== d.grouping) return false;
|
|
9843
10017
|
const gf = g.filters;
|
|
9844
|
-
for (const key of ["status", "type", "priority", "assignee", "project"]) {
|
|
10018
|
+
for (const key of ["status", "type", "priority", "assignee", "project", "tags"]) {
|
|
9845
10019
|
if (toFilterValues(gf[key]).length > 0) return false;
|
|
9846
10020
|
}
|
|
9847
10021
|
if (gf.activity !== void 0 && gf.activity !== "all") return false;
|
|
@@ -9858,6 +10032,7 @@ var TABLE_COLUMN_IDS = [
|
|
|
9858
10032
|
"priority",
|
|
9859
10033
|
"assignee",
|
|
9860
10034
|
"dependencies",
|
|
10035
|
+
"created",
|
|
9861
10036
|
"updated"
|
|
9862
10037
|
];
|
|
9863
10038
|
function isTableColumnId(value) {
|
|
@@ -9933,10 +10108,12 @@ function isTableColumnVisibility(value) {
|
|
|
9933
10108
|
function isViewFilters(value) {
|
|
9934
10109
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
9935
10110
|
const obj = value;
|
|
9936
|
-
for (const key of ["status", "type", "priority", "assignee", "project"]) {
|
|
10111
|
+
for (const key of ["status", "type", "priority", "assignee", "project", "tags"]) {
|
|
9937
10112
|
if (obj[key] !== void 0 && !isFilterValue(obj[key])) return false;
|
|
9938
10113
|
}
|
|
9939
10114
|
if (obj.activity !== void 0 && !isActivity(obj.activity)) return false;
|
|
10115
|
+
if (obj.dateRange !== void 0 && !isDateRange(obj.dateRange)) return false;
|
|
10116
|
+
if (obj.search !== void 0 && typeof obj.search !== "string") return false;
|
|
9940
10117
|
return true;
|
|
9941
10118
|
}
|
|
9942
10119
|
function isSavedViewConfig(value) {
|
|
@@ -10614,6 +10791,13 @@ ${key}: ${formatted}${content.slice(closingIdx)}`;
|
|
|
10614
10791
|
function escapeRegExp3(value) {
|
|
10615
10792
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
10616
10793
|
}
|
|
10794
|
+
function applyArchiveFields(content, archived, reason) {
|
|
10795
|
+
let next = setTopLevelField(content, "archived", archived);
|
|
10796
|
+
next = setTopLevelField(next, "archivedAt", archived ? nowTimestamp() : null);
|
|
10797
|
+
next = setTopLevelField(next, "archivedReason", archived ? reason : null);
|
|
10798
|
+
next = setTopLevelField(next, "updated", nowTimestamp());
|
|
10799
|
+
return next;
|
|
10800
|
+
}
|
|
10617
10801
|
function appendLogEntry2(existingContent, countField, nextCount, heading, body, emptyPlaceholder) {
|
|
10618
10802
|
const timestamp = nowTimestamp();
|
|
10619
10803
|
let next = setTopLevelField(existingContent, "updated", timestamp);
|
|
@@ -11907,7 +12091,7 @@ ${entry}`;
|
|
|
11907
12091
|
}
|
|
11908
12092
|
const { status } = req.body || {};
|
|
11909
12093
|
const config = await getStatusConfig();
|
|
11910
|
-
const validStatuses = ["active",
|
|
12094
|
+
const validStatuses = ["active", ...config.statuses.map((s) => s.id)];
|
|
11911
12095
|
if (status !== null && (typeof status !== "string" || !validStatuses.includes(status))) {
|
|
11912
12096
|
res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}, or null to clear.` });
|
|
11913
12097
|
return;
|
|
@@ -11959,6 +12143,106 @@ ${entry}`;
|
|
|
11959
12143
|
res.status(500).json({ error: `Failed to override status: ${error.message}` });
|
|
11960
12144
|
}
|
|
11961
12145
|
});
|
|
12146
|
+
function archiveReason(body) {
|
|
12147
|
+
const reason = body?.reason;
|
|
12148
|
+
return typeof reason === "string" && reason.trim() ? reason : null;
|
|
12149
|
+
}
|
|
12150
|
+
router.post("/api/projects/:slug/archive", async (req, res) => {
|
|
12151
|
+
try {
|
|
12152
|
+
const projectSlug = getParam(req.params.slug);
|
|
12153
|
+
const projectPath = resolve21(projectsDir2, projectSlug, "project.md");
|
|
12154
|
+
if (!await fileExists(projectPath)) {
|
|
12155
|
+
res.status(404).json({ error: `Project "${projectSlug}" not found` });
|
|
12156
|
+
return;
|
|
12157
|
+
}
|
|
12158
|
+
const content = await readFile15(projectPath, "utf-8");
|
|
12159
|
+
await writeFileForce(projectPath, applyArchiveFields(content, true, archiveReason(req.body)));
|
|
12160
|
+
const project = await getProjectDetail(projectsDir2, projectSlug);
|
|
12161
|
+
res.json({ project });
|
|
12162
|
+
} catch (error) {
|
|
12163
|
+
console.error("Error archiving project:", error);
|
|
12164
|
+
res.status(500).json({ error: `Failed to archive project: ${error.message}` });
|
|
12165
|
+
}
|
|
12166
|
+
});
|
|
12167
|
+
router.post("/api/projects/:slug/unarchive", async (req, res) => {
|
|
12168
|
+
try {
|
|
12169
|
+
const projectSlug = getParam(req.params.slug);
|
|
12170
|
+
const projectPath = resolve21(projectsDir2, projectSlug, "project.md");
|
|
12171
|
+
if (!await fileExists(projectPath)) {
|
|
12172
|
+
res.status(404).json({ error: `Project "${projectSlug}" not found` });
|
|
12173
|
+
return;
|
|
12174
|
+
}
|
|
12175
|
+
const content = await readFile15(projectPath, "utf-8");
|
|
12176
|
+
await writeFileForce(projectPath, applyArchiveFields(content, false, null));
|
|
12177
|
+
const project = await getProjectDetail(projectsDir2, projectSlug);
|
|
12178
|
+
res.json({ project });
|
|
12179
|
+
} catch (error) {
|
|
12180
|
+
console.error("Error restoring project:", error);
|
|
12181
|
+
res.status(500).json({ error: `Failed to restore project: ${error.message}` });
|
|
12182
|
+
}
|
|
12183
|
+
});
|
|
12184
|
+
async function handleAssignmentArchive(req, res, archived) {
|
|
12185
|
+
const projectSlug = getParam(req.params.slug);
|
|
12186
|
+
const assignmentSlug = getParam(req.params.aslug);
|
|
12187
|
+
const assignmentPath = resolve21(projectsDir2, projectSlug, "assignments", assignmentSlug, "assignment.md");
|
|
12188
|
+
if (!await fileExists(assignmentPath)) {
|
|
12189
|
+
res.status(404).json({ error: "Assignment not found" });
|
|
12190
|
+
return;
|
|
12191
|
+
}
|
|
12192
|
+
const content = await readFile15(assignmentPath, "utf-8");
|
|
12193
|
+
await writeFileForce(assignmentPath, applyArchiveFields(content, archived, archived ? archiveReason(req.body) : null));
|
|
12194
|
+
const assignment = await getAssignmentDetail(projectsDir2, projectSlug, assignmentSlug);
|
|
12195
|
+
res.json({ assignment });
|
|
12196
|
+
}
|
|
12197
|
+
router.post("/api/projects/:slug/assignments/:aslug/archive", async (req, res) => {
|
|
12198
|
+
try {
|
|
12199
|
+
await handleAssignmentArchive(req, res, true);
|
|
12200
|
+
} catch (error) {
|
|
12201
|
+
console.error("Error archiving assignment:", error);
|
|
12202
|
+
res.status(500).json({ error: `Failed to archive assignment: ${error.message}` });
|
|
12203
|
+
}
|
|
12204
|
+
});
|
|
12205
|
+
router.post("/api/projects/:slug/assignments/:aslug/unarchive", async (req, res) => {
|
|
12206
|
+
try {
|
|
12207
|
+
await handleAssignmentArchive(req, res, false);
|
|
12208
|
+
} catch (error) {
|
|
12209
|
+
console.error("Error restoring assignment:", error);
|
|
12210
|
+
res.status(500).json({ error: `Failed to restore assignment: ${error.message}` });
|
|
12211
|
+
}
|
|
12212
|
+
});
|
|
12213
|
+
async function handleStandaloneArchive(req, res, archived) {
|
|
12214
|
+
if (!assignmentsDir2) {
|
|
12215
|
+
res.status(501).json({ error: "Standalone assignments not configured on this server" });
|
|
12216
|
+
return;
|
|
12217
|
+
}
|
|
12218
|
+
const id = getParam(req.params.id);
|
|
12219
|
+
const resolved = await resolveAssignmentById(projectsDir2, assignmentsDir2, id);
|
|
12220
|
+
if (!resolved) {
|
|
12221
|
+
res.status(404).json({ error: `Assignment "${id}" not found` });
|
|
12222
|
+
return;
|
|
12223
|
+
}
|
|
12224
|
+
const assignmentPath = resolve21(resolved.assignmentDir, "assignment.md");
|
|
12225
|
+
const content = await readFile15(assignmentPath, "utf-8");
|
|
12226
|
+
await writeFileForce(assignmentPath, applyArchiveFields(content, archived, archived ? archiveReason(req.body) : null));
|
|
12227
|
+
const assignment = await getAssignmentDetailById(projectsDir2, assignmentsDir2, id);
|
|
12228
|
+
res.json({ assignment });
|
|
12229
|
+
}
|
|
12230
|
+
router.post("/api/assignments/:id/archive", async (req, res) => {
|
|
12231
|
+
try {
|
|
12232
|
+
await handleStandaloneArchive(req, res, true);
|
|
12233
|
+
} catch (error) {
|
|
12234
|
+
console.error("Error archiving assignment:", error);
|
|
12235
|
+
res.status(500).json({ error: `Failed to archive assignment: ${error.message}` });
|
|
12236
|
+
}
|
|
12237
|
+
});
|
|
12238
|
+
router.post("/api/assignments/:id/unarchive", async (req, res) => {
|
|
12239
|
+
try {
|
|
12240
|
+
await handleStandaloneArchive(req, res, false);
|
|
12241
|
+
} catch (error) {
|
|
12242
|
+
console.error("Error restoring assignment:", error);
|
|
12243
|
+
res.status(500).json({ error: `Failed to restore assignment: ${error.message}` });
|
|
12244
|
+
}
|
|
12245
|
+
});
|
|
11962
12246
|
router.patch("/api/projects/:slug/assignments/:aslug/assignee", async (req, res) => {
|
|
11963
12247
|
try {
|
|
11964
12248
|
const projectSlug = getParam(req.params.slug);
|
|
@@ -12608,84 +12892,6 @@ ${entry}`;
|
|
|
12608
12892
|
res.status(500).json({ error: `Failed to update title: ${error.message}` });
|
|
12609
12893
|
}
|
|
12610
12894
|
});
|
|
12611
|
-
router.post("/api/assignments/bulk-status-override", async (req, res) => {
|
|
12612
|
-
try {
|
|
12613
|
-
const body = req.body;
|
|
12614
|
-
if (!body || typeof body !== "object" || !Array.isArray(body.items)) {
|
|
12615
|
-
res.status(400).json({ error: "Request body must include `items` (array)." });
|
|
12616
|
-
return;
|
|
12617
|
-
}
|
|
12618
|
-
const items = body.items;
|
|
12619
|
-
if (items.length === 0) {
|
|
12620
|
-
res.status(400).json({ error: "`items` must contain at least one entry." });
|
|
12621
|
-
return;
|
|
12622
|
-
}
|
|
12623
|
-
if (items.length > 200) {
|
|
12624
|
-
res.status(400).json({ error: "`items` is capped at 200 entries per call." });
|
|
12625
|
-
return;
|
|
12626
|
-
}
|
|
12627
|
-
const config = await getStatusConfig();
|
|
12628
|
-
const validStatuses = new Set(config.statuses.map((s) => s.id));
|
|
12629
|
-
const timestamp = nowTimestamp();
|
|
12630
|
-
const results = [];
|
|
12631
|
-
let succeeded = 0;
|
|
12632
|
-
let failed = 0;
|
|
12633
|
-
for (let index = 0; index < items.length; index += 1) {
|
|
12634
|
-
const raw = items[index];
|
|
12635
|
-
const itemKey = buildBulkItemKey(raw, index);
|
|
12636
|
-
try {
|
|
12637
|
-
if (!raw || typeof raw !== "object") {
|
|
12638
|
-
throw new Error("item must be an object");
|
|
12639
|
-
}
|
|
12640
|
-
const item = raw;
|
|
12641
|
-
const status = typeof item.status === "string" ? item.status : null;
|
|
12642
|
-
if (!status || !validStatuses.has(status)) {
|
|
12643
|
-
throw new Error(`invalid status "${status ?? ""}"`);
|
|
12644
|
-
}
|
|
12645
|
-
let assignmentPath = null;
|
|
12646
|
-
if (typeof item.id === "string" && item.id.trim()) {
|
|
12647
|
-
if (!assignmentsDir2) {
|
|
12648
|
-
throw new Error("standalone assignments are not configured on this server");
|
|
12649
|
-
}
|
|
12650
|
-
const resolved = await resolveAssignmentById(projectsDir2, assignmentsDir2, item.id);
|
|
12651
|
-
if (!resolved) {
|
|
12652
|
-
throw new Error(`assignment "${item.id}" not found`);
|
|
12653
|
-
}
|
|
12654
|
-
assignmentPath = resolve21(resolved.assignmentDir, "assignment.md");
|
|
12655
|
-
} else if (typeof item.projectSlug === "string" && typeof item.assignmentSlug === "string" && item.projectSlug && item.assignmentSlug) {
|
|
12656
|
-
assignmentPath = resolve21(
|
|
12657
|
-
projectsDir2,
|
|
12658
|
-
item.projectSlug,
|
|
12659
|
-
"assignments",
|
|
12660
|
-
item.assignmentSlug,
|
|
12661
|
-
"assignment.md"
|
|
12662
|
-
);
|
|
12663
|
-
} else {
|
|
12664
|
-
throw new Error("must supply either `id` or both `projectSlug` and `assignmentSlug`");
|
|
12665
|
-
}
|
|
12666
|
-
if (!await fileExists(assignmentPath)) {
|
|
12667
|
-
throw new Error("assignment file not found");
|
|
12668
|
-
}
|
|
12669
|
-
let content = await readFile15(assignmentPath, "utf-8");
|
|
12670
|
-
content = setTopLevelField(content, "status", status);
|
|
12671
|
-
content = setTopLevelField(content, "updated", timestamp);
|
|
12672
|
-
if (status !== "blocked") {
|
|
12673
|
-
content = setTopLevelField(content, "blockedReason", null);
|
|
12674
|
-
}
|
|
12675
|
-
await writeFileForce(assignmentPath, content);
|
|
12676
|
-
results.push({ key: itemKey, ok: true });
|
|
12677
|
-
succeeded += 1;
|
|
12678
|
-
} catch (error) {
|
|
12679
|
-
results.push({ key: itemKey, ok: false, error: error.message });
|
|
12680
|
-
failed += 1;
|
|
12681
|
-
}
|
|
12682
|
-
}
|
|
12683
|
-
res.json({ results, succeeded, failed });
|
|
12684
|
-
} catch (error) {
|
|
12685
|
-
console.error("Error in bulk-status-override:", error);
|
|
12686
|
-
res.status(500).json({ error: `Bulk status override failed: ${error.message}` });
|
|
12687
|
-
}
|
|
12688
|
-
});
|
|
12689
12895
|
router.patch("/api/assignments/:id/acceptance-criteria/:index", async (req, res) => {
|
|
12690
12896
|
try {
|
|
12691
12897
|
if (!assignmentsDir2) {
|
|
@@ -12799,16 +13005,6 @@ function validateTitleBody(body) {
|
|
|
12799
13005
|
}
|
|
12800
13006
|
return { ok: true, value: trimmed };
|
|
12801
13007
|
}
|
|
12802
|
-
function buildBulkItemKey(raw, index) {
|
|
12803
|
-
if (raw && typeof raw === "object") {
|
|
12804
|
-
const item = raw;
|
|
12805
|
-
if (typeof item.id === "string" && item.id.trim()) return item.id;
|
|
12806
|
-
if (typeof item.projectSlug === "string" && typeof item.assignmentSlug === "string") {
|
|
12807
|
-
return `${item.projectSlug}/${item.assignmentSlug}`;
|
|
12808
|
-
}
|
|
12809
|
-
}
|
|
12810
|
-
return `#${index}`;
|
|
12811
|
-
}
|
|
12812
13008
|
async function appendCommentTo(assignmentDir, assignmentRef, req, res, reloadDetail) {
|
|
12813
13009
|
const commentsPath = resolve21(assignmentDir, "comments.md");
|
|
12814
13010
|
const { body, author, type, replyTo } = req.body || {};
|
|
@@ -15693,8 +15889,8 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
|
15693
15889
|
router.post("/:workspace/archive", async (req, res) => {
|
|
15694
15890
|
try {
|
|
15695
15891
|
const { archivePath: archivePath2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports));
|
|
15696
|
-
const { resolve:
|
|
15697
|
-
const { readFile:
|
|
15892
|
+
const { resolve: resolve79 } = await import("path");
|
|
15893
|
+
const { readFile: readFile54 } = await import("fs/promises");
|
|
15698
15894
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
15699
15895
|
const workspace = getWorkspaceParam(req.params.workspace);
|
|
15700
15896
|
const checklist = await readChecklist(todosDir2, workspace);
|
|
@@ -15710,10 +15906,10 @@ function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
|
15710
15906
|
(e) => e.itemIds.every((id) => completedIds.has(id))
|
|
15711
15907
|
);
|
|
15712
15908
|
const archFile = archivePath2(todosDir2, workspace, checklist.archiveInterval);
|
|
15713
|
-
await ensureDir(
|
|
15909
|
+
await ensureDir(resolve79(todosDir2, "archive"));
|
|
15714
15910
|
let archContent = "";
|
|
15715
15911
|
if (await fileExists(archFile)) {
|
|
15716
|
-
archContent = await
|
|
15912
|
+
archContent = await readFile54(archFile, "utf-8");
|
|
15717
15913
|
archContent = archContent.trimEnd() + "\n\n";
|
|
15718
15914
|
} else {
|
|
15719
15915
|
archContent = `---
|
|
@@ -15995,7 +16191,7 @@ workspace: ${workspace}
|
|
|
15995
16191
|
const { readConfig: readConfig3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
15996
16192
|
const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
15997
16193
|
const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
15998
|
-
const { readFile:
|
|
16194
|
+
const { readFile: readFile54 } = await import("fs/promises");
|
|
15999
16195
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
16000
16196
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
16001
16197
|
let assignmentRef;
|
|
@@ -16016,7 +16212,7 @@ workspace: ${workspace}
|
|
|
16016
16212
|
}
|
|
16017
16213
|
const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
|
|
16018
16214
|
if (!await fileExists2(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
|
|
16019
|
-
let content = await
|
|
16215
|
+
let content = await readFile54(assignmentMdPath2, "utf-8");
|
|
16020
16216
|
content = appendTodosToAssignmentBody2(
|
|
16021
16217
|
content,
|
|
16022
16218
|
items.map((it) => ({
|
|
@@ -18181,7 +18377,7 @@ function createDashboardServer(options) {
|
|
|
18181
18377
|
}
|
|
18182
18378
|
});
|
|
18183
18379
|
const VIEW_PREFS_LOCK = "vp:global";
|
|
18184
|
-
const FILTER_KEYS = /* @__PURE__ */ new Set(["status", "type", "priority", "assignee", "project", "activity"]);
|
|
18380
|
+
const FILTER_KEYS = /* @__PURE__ */ new Set(["status", "type", "priority", "assignee", "project", "tags", "activity"]);
|
|
18185
18381
|
const GLOBAL_KEYS = /* @__PURE__ */ new Set(["defaultView", "sortField", "sortDirection", "density", "grouping", "filters"]);
|
|
18186
18382
|
const SCOPE_KEYS = /* @__PURE__ */ new Set(["defaultView", "sortField", "sortDirection", "grouping", "filters"]);
|
|
18187
18383
|
const ROOT_KEYS = /* @__PURE__ */ new Set(["global", "projects"]);
|
|
@@ -18200,7 +18396,7 @@ function createDashboardServer(options) {
|
|
|
18200
18396
|
const unknown = unknownKey(obj, FILTER_KEYS, "filters");
|
|
18201
18397
|
if (unknown) return { ok: false, error: unknown };
|
|
18202
18398
|
const out = {};
|
|
18203
|
-
for (const key of ["status", "type", "priority", "assignee", "project"]) {
|
|
18399
|
+
for (const key of ["status", "type", "priority", "assignee", "project", "tags"]) {
|
|
18204
18400
|
if (obj[key] !== void 0) {
|
|
18205
18401
|
if (!isFilterValue(obj[key])) {
|
|
18206
18402
|
return { ok: false, error: `filters.${key} must be a non-empty string or array of non-empty strings` };
|
|
@@ -18425,6 +18621,15 @@ function createDashboardServer(options) {
|
|
|
18425
18621
|
res.status(500).json({ error: "Failed to list assignments" });
|
|
18426
18622
|
}
|
|
18427
18623
|
});
|
|
18624
|
+
app.get("/api/archived", async (_req, res) => {
|
|
18625
|
+
try {
|
|
18626
|
+
const result = await listArchived(projectsDir2, assignmentsDir2);
|
|
18627
|
+
res.json(result);
|
|
18628
|
+
} catch (error) {
|
|
18629
|
+
console.error("Error listing archived content:", error);
|
|
18630
|
+
res.status(500).json({ error: "Failed to list archived content" });
|
|
18631
|
+
}
|
|
18632
|
+
});
|
|
18428
18633
|
app.get("/api/projects/:slug", async (req, res) => {
|
|
18429
18634
|
try {
|
|
18430
18635
|
const detail = await getProjectDetail(projectsDir2, req.params.slug);
|
|
@@ -18852,6 +19057,91 @@ async function startCommand(assignment, options) {
|
|
|
18852
19057
|
reportResult(result);
|
|
18853
19058
|
}
|
|
18854
19059
|
|
|
19060
|
+
// src/commands/_archive-helper.ts
|
|
19061
|
+
init_paths();
|
|
19062
|
+
init_fs();
|
|
19063
|
+
init_config2();
|
|
19064
|
+
init_slug();
|
|
19065
|
+
init_frontmatter();
|
|
19066
|
+
init_timestamp();
|
|
19067
|
+
init_assignment_resolver();
|
|
19068
|
+
import { resolve as resolve33 } from "path";
|
|
19069
|
+
import { readFile as readFile21, writeFile as writeFile7 } from "fs/promises";
|
|
19070
|
+
async function resolveTarget(target, options) {
|
|
19071
|
+
const config = await readConfig();
|
|
19072
|
+
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
19073
|
+
if (options.project) {
|
|
19074
|
+
if (!isValidSlug(options.project)) {
|
|
19075
|
+
throw new Error(`Invalid project slug "${options.project}".`);
|
|
19076
|
+
}
|
|
19077
|
+
if (!isValidSlug(target)) {
|
|
19078
|
+
throw new Error(`Invalid assignment slug "${target}".`);
|
|
19079
|
+
}
|
|
19080
|
+
const assignmentMd = resolve33(baseDir, options.project, "assignments", target, "assignment.md");
|
|
19081
|
+
if (!await fileExists(assignmentMd)) {
|
|
19082
|
+
throw new Error(`Assignment "${target}" not found in project "${options.project}".`);
|
|
19083
|
+
}
|
|
19084
|
+
return { kind: "assignment", filePath: assignmentMd, label: `assignment "${options.project}/${target}"` };
|
|
19085
|
+
}
|
|
19086
|
+
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
|
|
19087
|
+
if (resolved) {
|
|
19088
|
+
return {
|
|
19089
|
+
kind: "assignment",
|
|
19090
|
+
filePath: resolve33(resolved.assignmentDir, "assignment.md"),
|
|
19091
|
+
label: resolved.projectSlug ? `assignment "${resolved.projectSlug}/${resolved.assignmentSlug}"` : `assignment "${target}"`
|
|
19092
|
+
};
|
|
19093
|
+
}
|
|
19094
|
+
const projectMd = resolve33(baseDir, target, "project.md");
|
|
19095
|
+
if (await fileExists(projectMd)) {
|
|
19096
|
+
return { kind: "project", filePath: projectMd, label: `project "${target}"` };
|
|
19097
|
+
}
|
|
19098
|
+
return null;
|
|
19099
|
+
}
|
|
19100
|
+
async function writeArchiveState(filePath, archived, reason) {
|
|
19101
|
+
const content = await readFile21(filePath, "utf-8");
|
|
19102
|
+
const updated = updateAssignmentFile(content, {
|
|
19103
|
+
archived,
|
|
19104
|
+
archivedAt: archived ? nowTimestamp() : null,
|
|
19105
|
+
archivedReason: archived ? reason : null,
|
|
19106
|
+
updated: nowTimestamp()
|
|
19107
|
+
});
|
|
19108
|
+
await writeFile7(filePath, updated, "utf-8");
|
|
19109
|
+
}
|
|
19110
|
+
async function runArchive(target, options = {}) {
|
|
19111
|
+
const resolved = await resolveTarget(target, options);
|
|
19112
|
+
if (!resolved) {
|
|
19113
|
+
return { success: false, message: `No assignment or project matched "${target}".` };
|
|
19114
|
+
}
|
|
19115
|
+
await writeArchiveState(resolved.filePath, true, options.reason ?? null);
|
|
19116
|
+
return { success: true, message: `Archived ${resolved.label}.` };
|
|
19117
|
+
}
|
|
19118
|
+
async function runRestore(target, options = {}) {
|
|
19119
|
+
const resolved = await resolveTarget(target, options);
|
|
19120
|
+
if (!resolved) {
|
|
19121
|
+
return { success: false, message: `No assignment or project matched "${target}".` };
|
|
19122
|
+
}
|
|
19123
|
+
await writeArchiveState(resolved.filePath, false, null);
|
|
19124
|
+
return { success: true, message: `Restored ${resolved.label}.` };
|
|
19125
|
+
}
|
|
19126
|
+
function reportArchiveResult(result) {
|
|
19127
|
+
if (!result.success) {
|
|
19128
|
+
throw new Error(result.message);
|
|
19129
|
+
}
|
|
19130
|
+
console.log(result.message);
|
|
19131
|
+
}
|
|
19132
|
+
|
|
19133
|
+
// src/commands/archive.ts
|
|
19134
|
+
async function archiveCommand(target, options) {
|
|
19135
|
+
const result = await runArchive(target, options);
|
|
19136
|
+
reportArchiveResult(result);
|
|
19137
|
+
}
|
|
19138
|
+
|
|
19139
|
+
// src/commands/restore.ts
|
|
19140
|
+
async function restoreCommand(target, options) {
|
|
19141
|
+
const result = await runRestore(target, options);
|
|
19142
|
+
reportArchiveResult(result);
|
|
19143
|
+
}
|
|
19144
|
+
|
|
18855
19145
|
// src/commands/shape.ts
|
|
18856
19146
|
async function shapeCommand(assignment, options) {
|
|
18857
19147
|
const result = await runTransition(assignment, "shape", options);
|
|
@@ -18876,8 +19166,8 @@ init_fs();
|
|
|
18876
19166
|
init_config2();
|
|
18877
19167
|
init_frontmatter();
|
|
18878
19168
|
init_timestamp();
|
|
18879
|
-
import { resolve as
|
|
18880
|
-
import { readdir as readdir13, readFile as
|
|
19169
|
+
import { resolve as resolve34 } from "path";
|
|
19170
|
+
import { readdir as readdir13, readFile as readFile22 } from "fs/promises";
|
|
18881
19171
|
var PROMOTABLE_STATUSES = /* @__PURE__ */ new Set(["pending"]);
|
|
18882
19172
|
function objectiveIsFleshedOut(content) {
|
|
18883
19173
|
const match = content.match(/##\s+Objective\s*\n([\s\S]*?)(?=\n##\s+|$)/);
|
|
@@ -18899,11 +19189,11 @@ async function collectCandidates(baseDirs) {
|
|
|
18899
19189
|
for (const m of projects) {
|
|
18900
19190
|
if (!m.isDirectory()) continue;
|
|
18901
19191
|
if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
|
|
18902
|
-
const directAssignmentMd =
|
|
19192
|
+
const directAssignmentMd = resolve34(baseDir, m.name, "assignment.md");
|
|
18903
19193
|
if (await fileExists(directAssignmentMd)) {
|
|
18904
19194
|
const fm = await parseSafe(directAssignmentMd);
|
|
18905
19195
|
if (fm && PROMOTABLE_STATUSES.has(fm.status)) {
|
|
18906
|
-
const content = await
|
|
19196
|
+
const content = await readFile22(directAssignmentMd, "utf-8");
|
|
18907
19197
|
if (objectiveIsFleshedOut(content) && hasAcceptanceCriteria(content)) {
|
|
18908
19198
|
candidates.push({
|
|
18909
19199
|
projectSlug: null,
|
|
@@ -18916,17 +19206,17 @@ async function collectCandidates(baseDirs) {
|
|
|
18916
19206
|
}
|
|
18917
19207
|
continue;
|
|
18918
19208
|
}
|
|
18919
|
-
const assignmentsDir2 =
|
|
19209
|
+
const assignmentsDir2 = resolve34(baseDir, m.name, "assignments");
|
|
18920
19210
|
if (!await fileExists(assignmentsDir2)) continue;
|
|
18921
19211
|
const entries = await readdir13(assignmentsDir2, { withFileTypes: true });
|
|
18922
19212
|
for (const a of entries) {
|
|
18923
19213
|
if (!a.isDirectory()) continue;
|
|
18924
19214
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
18925
|
-
const assignmentMd =
|
|
19215
|
+
const assignmentMd = resolve34(assignmentsDir2, a.name, "assignment.md");
|
|
18926
19216
|
if (!await fileExists(assignmentMd)) continue;
|
|
18927
19217
|
const fm = await parseSafe(assignmentMd);
|
|
18928
19218
|
if (!fm || !PROMOTABLE_STATUSES.has(fm.status)) continue;
|
|
18929
|
-
const content = await
|
|
19219
|
+
const content = await readFile22(assignmentMd, "utf-8");
|
|
18930
19220
|
if (!objectiveIsFleshedOut(content)) continue;
|
|
18931
19221
|
if (!hasAcceptanceCriteria(content)) continue;
|
|
18932
19222
|
candidates.push({
|
|
@@ -18943,7 +19233,7 @@ async function collectCandidates(baseDirs) {
|
|
|
18943
19233
|
}
|
|
18944
19234
|
async function parseSafe(path) {
|
|
18945
19235
|
try {
|
|
18946
|
-
const content = await
|
|
19236
|
+
const content = await readFile22(path, "utf-8");
|
|
18947
19237
|
return parseAssignmentFrontmatter(content);
|
|
18948
19238
|
} catch {
|
|
18949
19239
|
return null;
|
|
@@ -18972,7 +19262,7 @@ async function migrateStatusesCommand(options) {
|
|
|
18972
19262
|
const now = nowTimestamp();
|
|
18973
19263
|
let migrated = 0;
|
|
18974
19264
|
for (const c2 of candidates) {
|
|
18975
|
-
const content = await
|
|
19265
|
+
const content = await readFile22(c2.assignmentMd, "utf-8");
|
|
18976
19266
|
const updated = updateAssignmentFile(content, {
|
|
18977
19267
|
status: c2.toStatus,
|
|
18978
19268
|
updated: now
|
|
@@ -19030,29 +19320,29 @@ import {
|
|
|
19030
19320
|
readdir as readdir14,
|
|
19031
19321
|
symlink,
|
|
19032
19322
|
lstat,
|
|
19033
|
-
readFile as
|
|
19323
|
+
readFile as readFile23,
|
|
19034
19324
|
readlink,
|
|
19035
19325
|
rename as rename8,
|
|
19036
19326
|
rm as rm4,
|
|
19037
19327
|
unlink as unlink7,
|
|
19038
|
-
writeFile as
|
|
19328
|
+
writeFile as writeFile8
|
|
19039
19329
|
} from "fs/promises";
|
|
19040
19330
|
import { existsSync as existsSync3 } from "fs";
|
|
19041
19331
|
import { homedir as homedir3 } from "os";
|
|
19042
|
-
import { basename as basename4, dirname as dirname10, isAbsolute as isAbsolute4, relative as relative2, resolve as
|
|
19332
|
+
import { basename as basename4, dirname as dirname10, isAbsolute as isAbsolute4, relative as relative2, resolve as resolve36 } from "path";
|
|
19043
19333
|
|
|
19044
19334
|
// src/utils/package-root.ts
|
|
19045
19335
|
init_fs();
|
|
19046
|
-
import { dirname as dirname9, resolve as
|
|
19336
|
+
import { dirname as dirname9, resolve as resolve35 } from "path";
|
|
19047
19337
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
19048
19338
|
async function findPackageRoot(expectedRelativePath) {
|
|
19049
19339
|
let currentDir = dirname9(fileURLToPath3(import.meta.url));
|
|
19050
19340
|
while (true) {
|
|
19051
|
-
const candidate =
|
|
19341
|
+
const candidate = resolve35(currentDir, expectedRelativePath);
|
|
19052
19342
|
if (await fileExists(candidate)) {
|
|
19053
19343
|
return currentDir;
|
|
19054
19344
|
}
|
|
19055
|
-
const parentDir =
|
|
19345
|
+
const parentDir = resolve35(currentDir, "..");
|
|
19056
19346
|
if (parentDir === currentDir) {
|
|
19057
19347
|
throw new Error(
|
|
19058
19348
|
`Could not locate package root containing ${expectedRelativePath}.`
|
|
@@ -19073,25 +19363,25 @@ function getPluginManifestRelativePath(pluginKind) {
|
|
|
19073
19363
|
}
|
|
19074
19364
|
function getDefaultPluginTargetDir(pluginKind) {
|
|
19075
19365
|
const home = homedir3();
|
|
19076
|
-
return pluginKind === "claude" ?
|
|
19366
|
+
return pluginKind === "claude" ? resolve36(home, ".claude", "plugins", "syntaur") : resolve36(home, "plugins", "syntaur");
|
|
19077
19367
|
}
|
|
19078
19368
|
function getDefaultMarketplacePath() {
|
|
19079
|
-
return
|
|
19369
|
+
return resolve36(homedir3(), ".agents", "plugins", "marketplace.json");
|
|
19080
19370
|
}
|
|
19081
19371
|
function getClaudeMarketplacesRoot() {
|
|
19082
|
-
return
|
|
19372
|
+
return resolve36(homedir3(), ".claude", "plugins", "marketplaces");
|
|
19083
19373
|
}
|
|
19084
19374
|
function getClaudeKnownMarketplacesPath() {
|
|
19085
|
-
return
|
|
19375
|
+
return resolve36(homedir3(), ".claude", "plugins", "known_marketplaces.json");
|
|
19086
19376
|
}
|
|
19087
19377
|
function getClaudeInstalledPluginsPath() {
|
|
19088
|
-
return
|
|
19378
|
+
return resolve36(homedir3(), ".claude", "plugins", "installed_plugins.json");
|
|
19089
19379
|
}
|
|
19090
19380
|
function getInstallMarkerPath(targetDir) {
|
|
19091
|
-
return
|
|
19381
|
+
return resolve36(targetDir, INSTALL_MARKER_FILENAME);
|
|
19092
19382
|
}
|
|
19093
19383
|
async function readPackageManifest(packageRoot) {
|
|
19094
|
-
const raw = await
|
|
19384
|
+
const raw = await readFile23(resolve36(packageRoot, "package.json"), "utf-8");
|
|
19095
19385
|
return JSON.parse(raw);
|
|
19096
19386
|
}
|
|
19097
19387
|
async function readJsonFileIfExists(pathValue) {
|
|
@@ -19099,7 +19389,7 @@ async function readJsonFileIfExists(pathValue) {
|
|
|
19099
19389
|
return null;
|
|
19100
19390
|
}
|
|
19101
19391
|
try {
|
|
19102
|
-
const raw = await
|
|
19392
|
+
const raw = await readFile23(pathValue, "utf-8");
|
|
19103
19393
|
return JSON.parse(raw);
|
|
19104
19394
|
} catch {
|
|
19105
19395
|
return null;
|
|
@@ -19107,15 +19397,15 @@ async function readJsonFileIfExists(pathValue) {
|
|
|
19107
19397
|
}
|
|
19108
19398
|
async function readClaudePluginManifest(pluginDir) {
|
|
19109
19399
|
return await readJsonFileIfExists(
|
|
19110
|
-
|
|
19400
|
+
resolve36(pluginDir, ".claude-plugin", "plugin.json")
|
|
19111
19401
|
) ?? {};
|
|
19112
19402
|
}
|
|
19113
19403
|
async function readPluginManifestName(targetDir, pluginKind) {
|
|
19114
|
-
const manifestPath =
|
|
19404
|
+
const manifestPath = resolve36(targetDir, getPluginManifestRelativePath(pluginKind));
|
|
19115
19405
|
if (!await fileExists(manifestPath)) {
|
|
19116
19406
|
return void 0;
|
|
19117
19407
|
}
|
|
19118
|
-
const raw = await
|
|
19408
|
+
const raw = await readFile23(manifestPath, "utf-8");
|
|
19119
19409
|
const parsed = JSON.parse(raw);
|
|
19120
19410
|
return parsed.name;
|
|
19121
19411
|
}
|
|
@@ -19125,7 +19415,7 @@ async function readInstallMetadata(targetDir) {
|
|
|
19125
19415
|
return null;
|
|
19126
19416
|
}
|
|
19127
19417
|
try {
|
|
19128
|
-
const raw = await
|
|
19418
|
+
const raw = await readFile23(markerPath, "utf-8");
|
|
19129
19419
|
return JSON.parse(raw);
|
|
19130
19420
|
} catch {
|
|
19131
19421
|
return null;
|
|
@@ -19138,7 +19428,7 @@ async function getInstallStatus(targetDir, pluginKind) {
|
|
|
19138
19428
|
const info = await lstat(targetDir);
|
|
19139
19429
|
if (info.isSymbolicLink()) {
|
|
19140
19430
|
const symlinkTarget = await readlink(targetDir);
|
|
19141
|
-
const resolvedTarget =
|
|
19431
|
+
const resolvedTarget = resolve36(dirname10(targetDir), symlinkTarget);
|
|
19142
19432
|
const manifestName2 = await readPluginManifestName(resolvedTarget, pluginKind);
|
|
19143
19433
|
return {
|
|
19144
19434
|
exists: true,
|
|
@@ -19167,7 +19457,7 @@ async function writeInstallMetadata(targetDir, pluginKind, installMode, packageM
|
|
|
19167
19457
|
installMode,
|
|
19168
19458
|
installedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
19169
19459
|
};
|
|
19170
|
-
await
|
|
19460
|
+
await writeFile8(
|
|
19171
19461
|
getInstallMarkerPath(targetDir),
|
|
19172
19462
|
`${JSON.stringify(metadata, null, 2)}
|
|
19173
19463
|
`,
|
|
@@ -19184,7 +19474,7 @@ async function installLink(paths) {
|
|
|
19184
19474
|
await ensureDir(dirname10(paths.targetDir));
|
|
19185
19475
|
await rm4(paths.targetDir, { recursive: true, force: true });
|
|
19186
19476
|
await ensureDir(dirname10(paths.targetDir));
|
|
19187
|
-
await symlink(
|
|
19477
|
+
await symlink(resolve36(paths.sourceDir), paths.targetDir, "dir");
|
|
19188
19478
|
}
|
|
19189
19479
|
async function removeInstallMarker(targetDir) {
|
|
19190
19480
|
const markerPath = getInstallMarkerPath(targetDir);
|
|
@@ -19198,13 +19488,13 @@ function normalizeAbsoluteInstallPath(pathValue, label) {
|
|
|
19198
19488
|
if (!isAbsolute4(expanded)) {
|
|
19199
19489
|
throw new Error(`${label} must be an absolute path.`);
|
|
19200
19490
|
}
|
|
19201
|
-
return
|
|
19491
|
+
return resolve36(expanded);
|
|
19202
19492
|
}
|
|
19203
19493
|
async function resolvePluginPaths(pluginKind, targetDir) {
|
|
19204
19494
|
const packageRoot = await findPackageRoot(getPluginRelativePath(pluginKind));
|
|
19205
19495
|
return {
|
|
19206
19496
|
packageRoot,
|
|
19207
|
-
sourceDir:
|
|
19497
|
+
sourceDir: resolve36(packageRoot, getPluginRelativePath(pluginKind)),
|
|
19208
19498
|
targetDir: targetDir ?? getDefaultPluginTargetDir(pluginKind)
|
|
19209
19499
|
};
|
|
19210
19500
|
}
|
|
@@ -19248,10 +19538,10 @@ async function writeClaudeMarketplaceFile(manifestPath, marketplace) {
|
|
|
19248
19538
|
}
|
|
19249
19539
|
if (await fileExists(manifestPath)) {
|
|
19250
19540
|
try {
|
|
19251
|
-
const prev = await
|
|
19541
|
+
const prev = await readFile23(manifestPath, "utf-8");
|
|
19252
19542
|
JSON.parse(prev);
|
|
19253
19543
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
19254
|
-
await
|
|
19544
|
+
await writeFile8(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
|
|
19255
19545
|
} catch {
|
|
19256
19546
|
throw new Error(
|
|
19257
19547
|
`Refusing to overwrite ${manifestPath}: existing file is not valid JSON. Inspect and remove or repair it manually before re-running.`
|
|
@@ -19259,7 +19549,7 @@ async function writeClaudeMarketplaceFile(manifestPath, marketplace) {
|
|
|
19259
19549
|
}
|
|
19260
19550
|
}
|
|
19261
19551
|
const tmpPath = `${manifestPath}.tmp`;
|
|
19262
|
-
await
|
|
19552
|
+
await writeFile8(tmpPath, `${JSON.stringify(marketplace, null, 2)}
|
|
19263
19553
|
`, "utf-8");
|
|
19264
19554
|
await rename8(tmpPath, manifestPath);
|
|
19265
19555
|
}
|
|
@@ -19329,8 +19619,8 @@ async function listClaudeMarketplaceCandidates() {
|
|
|
19329
19619
|
if (!entry.isDirectory()) {
|
|
19330
19620
|
continue;
|
|
19331
19621
|
}
|
|
19332
|
-
const candidateRoot =
|
|
19333
|
-
const manifestPath =
|
|
19622
|
+
const candidateRoot = resolve36(rootDir, entry.name);
|
|
19623
|
+
const manifestPath = resolve36(candidateRoot, ".claude-plugin", "marketplace.json");
|
|
19334
19624
|
if (!await fileExists(manifestPath)) {
|
|
19335
19625
|
continue;
|
|
19336
19626
|
}
|
|
@@ -19357,11 +19647,11 @@ async function listClaudeMarketplaceCandidates() {
|
|
|
19357
19647
|
if (!installLocation) {
|
|
19358
19648
|
continue;
|
|
19359
19649
|
}
|
|
19360
|
-
const candidateRoot =
|
|
19650
|
+
const candidateRoot = resolve36(expandHome(installLocation));
|
|
19361
19651
|
if (seen.has(candidateRoot)) {
|
|
19362
19652
|
continue;
|
|
19363
19653
|
}
|
|
19364
|
-
const manifestPath =
|
|
19654
|
+
const manifestPath = resolve36(candidateRoot, ".claude-plugin", "marketplace.json");
|
|
19365
19655
|
if (!await fileExists(manifestPath)) {
|
|
19366
19656
|
continue;
|
|
19367
19657
|
}
|
|
@@ -19389,14 +19679,14 @@ async function getPreferredClaudeMarketplace() {
|
|
|
19389
19679
|
name: candidate.name,
|
|
19390
19680
|
rootDir: candidate.rootDir,
|
|
19391
19681
|
manifestPath: candidate.manifestPath,
|
|
19392
|
-
targetDir:
|
|
19682
|
+
targetDir: resolve36(candidate.rootDir, "plugins", "syntaur")
|
|
19393
19683
|
};
|
|
19394
19684
|
}
|
|
19395
19685
|
async function registerKnownClaudeMarketplace(name, rootDir) {
|
|
19396
19686
|
const manifestPath = getClaudeKnownMarketplacesPath();
|
|
19397
19687
|
let existing = {};
|
|
19398
19688
|
if (await fileExists(manifestPath)) {
|
|
19399
|
-
const raw = await
|
|
19689
|
+
const raw = await readFile23(manifestPath, "utf-8");
|
|
19400
19690
|
try {
|
|
19401
19691
|
const parsed = JSON.parse(raw);
|
|
19402
19692
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
@@ -19423,12 +19713,12 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
|
|
|
19423
19713
|
existing[name].autoUpdate = true;
|
|
19424
19714
|
await ensureDir(dirname10(manifestPath));
|
|
19425
19715
|
if (await fileExists(manifestPath)) {
|
|
19426
|
-
const prev = await
|
|
19716
|
+
const prev = await readFile23(manifestPath, "utf-8");
|
|
19427
19717
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
19428
|
-
await
|
|
19718
|
+
await writeFile8(`${manifestPath}.bak-${stamp}`, prev, "utf-8");
|
|
19429
19719
|
}
|
|
19430
19720
|
const tmpPath = `${manifestPath}.tmp`;
|
|
19431
|
-
await
|
|
19721
|
+
await writeFile8(tmpPath, `${JSON.stringify(existing, null, 2)}
|
|
19432
19722
|
`, "utf-8");
|
|
19433
19723
|
await rename8(tmpPath, manifestPath);
|
|
19434
19724
|
return { added: !had, updated: had };
|
|
@@ -19437,11 +19727,11 @@ async function ensureKnownClaudeMarketplaceForRoot(options) {
|
|
|
19437
19727
|
return registerKnownClaudeMarketplace(options.name, options.rootDir);
|
|
19438
19728
|
}
|
|
19439
19729
|
async function setSyntaurPluginEnabled(options) {
|
|
19440
|
-
const settingsPath =
|
|
19730
|
+
const settingsPath = resolve36(homedir3(), ".claude", "settings.json");
|
|
19441
19731
|
const key = `syntaur@${options.marketplaceName}`;
|
|
19442
19732
|
let parsed = {};
|
|
19443
19733
|
if (await fileExists(settingsPath)) {
|
|
19444
|
-
const raw = await
|
|
19734
|
+
const raw = await readFile23(settingsPath, "utf-8");
|
|
19445
19735
|
try {
|
|
19446
19736
|
parsed = JSON.parse(raw);
|
|
19447
19737
|
} catch {
|
|
@@ -19460,11 +19750,11 @@ async function setSyntaurPluginEnabled(options) {
|
|
|
19460
19750
|
await ensureDir(dirname10(settingsPath));
|
|
19461
19751
|
if (await fileExists(settingsPath)) {
|
|
19462
19752
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
19463
|
-
const prev = await
|
|
19464
|
-
await
|
|
19753
|
+
const prev = await readFile23(settingsPath, "utf-8");
|
|
19754
|
+
await writeFile8(`${settingsPath}.bak-${stamp}`, prev, "utf-8");
|
|
19465
19755
|
}
|
|
19466
19756
|
const tmpPath = `${settingsPath}.tmp`;
|
|
19467
|
-
await
|
|
19757
|
+
await writeFile8(tmpPath, `${JSON.stringify(parsed, null, 2)}
|
|
19468
19758
|
`, "utf-8");
|
|
19469
19759
|
await rename8(tmpPath, settingsPath);
|
|
19470
19760
|
return { key, previous, current: options.enabled, changed: true };
|
|
@@ -19474,9 +19764,9 @@ async function ensureClaudeUserMarketplace() {
|
|
|
19474
19764
|
if (existing) {
|
|
19475
19765
|
return existing;
|
|
19476
19766
|
}
|
|
19477
|
-
const rootDir =
|
|
19478
|
-
const manifestPath =
|
|
19479
|
-
await ensureDir(
|
|
19767
|
+
const rootDir = resolve36(getClaudeMarketplacesRoot(), "user-plugins");
|
|
19768
|
+
const manifestPath = resolve36(rootDir, ".claude-plugin", "marketplace.json");
|
|
19769
|
+
await ensureDir(resolve36(rootDir, "plugins"));
|
|
19480
19770
|
if (!await fileExists(manifestPath)) {
|
|
19481
19771
|
const scaffold = {
|
|
19482
19772
|
plugins: []
|
|
@@ -19495,7 +19785,7 @@ async function ensureClaudeUserMarketplace() {
|
|
|
19495
19785
|
name: "user-plugins",
|
|
19496
19786
|
rootDir,
|
|
19497
19787
|
manifestPath,
|
|
19498
|
-
targetDir:
|
|
19788
|
+
targetDir: resolve36(rootDir, "plugins", "syntaur")
|
|
19499
19789
|
};
|
|
19500
19790
|
}
|
|
19501
19791
|
async function detectClaudeMarketplaceForTarget(targetDir) {
|
|
@@ -19505,7 +19795,7 @@ async function detectClaudeMarketplaceForTarget(targetDir) {
|
|
|
19505
19795
|
return null;
|
|
19506
19796
|
}
|
|
19507
19797
|
const rootDir = dirname10(pluginsDir);
|
|
19508
|
-
const manifestPath =
|
|
19798
|
+
const manifestPath = resolve36(rootDir, ".claude-plugin", "marketplace.json");
|
|
19509
19799
|
if (!await fileExists(manifestPath)) {
|
|
19510
19800
|
return null;
|
|
19511
19801
|
}
|
|
@@ -19521,7 +19811,7 @@ async function detectClaudeMarketplaceForTarget(targetDir) {
|
|
|
19521
19811
|
async function findManagedClaudeMarketplacePluginDir() {
|
|
19522
19812
|
const marketplaces = await listClaudeMarketplaceCandidates();
|
|
19523
19813
|
for (const marketplace of marketplaces) {
|
|
19524
|
-
const targetDir =
|
|
19814
|
+
const targetDir = resolve36(marketplace.rootDir, "plugins", "syntaur");
|
|
19525
19815
|
const status = await getInstallStatus(targetDir, "claude");
|
|
19526
19816
|
if (status.exists && status.managed) {
|
|
19527
19817
|
return targetDir;
|
|
@@ -19646,7 +19936,7 @@ async function installManagedPlugin(options) {
|
|
|
19646
19936
|
`${paths.targetDir} already exists and is not a Syntaur-managed install. Remove it manually before installing Syntaur there.`
|
|
19647
19937
|
);
|
|
19648
19938
|
}
|
|
19649
|
-
if (desiredMode === "link" && existing.exists && existing.installMode === "link" && existing.symlinkTarget ===
|
|
19939
|
+
if (desiredMode === "link" && existing.exists && existing.installMode === "link" && existing.symlinkTarget === resolve36(paths.sourceDir) && !force) {
|
|
19650
19940
|
return {
|
|
19651
19941
|
targetDir: paths.targetDir,
|
|
19652
19942
|
sourceDir: paths.sourceDir,
|
|
@@ -19698,7 +19988,7 @@ async function readMarketplaceFile(marketplacePath) {
|
|
|
19698
19988
|
plugins: []
|
|
19699
19989
|
};
|
|
19700
19990
|
}
|
|
19701
|
-
const raw = await
|
|
19991
|
+
const raw = await readFile23(marketplacePath, "utf-8");
|
|
19702
19992
|
const parsed = JSON.parse(raw);
|
|
19703
19993
|
return {
|
|
19704
19994
|
name: parsed.name ?? "local",
|
|
@@ -19708,7 +19998,7 @@ async function readMarketplaceFile(marketplacePath) {
|
|
|
19708
19998
|
}
|
|
19709
19999
|
async function writeMarketplaceFile(marketplacePath, marketplace) {
|
|
19710
20000
|
await ensureDir(dirname10(marketplacePath));
|
|
19711
|
-
await
|
|
20001
|
+
await writeFile8(marketplacePath, `${JSON.stringify(marketplace, null, 2)}
|
|
19712
20002
|
`, "utf-8");
|
|
19713
20003
|
}
|
|
19714
20004
|
async function hasAnySyntaurMarketplaceEntry(marketplacePath) {
|
|
@@ -19859,13 +20149,13 @@ async function recommendMarketplacePath() {
|
|
|
19859
20149
|
return configuredOrManaged ?? getDefaultMarketplacePath();
|
|
19860
20150
|
}
|
|
19861
20151
|
async function isSyntaurDataInstalled() {
|
|
19862
|
-
return fileExists(
|
|
20152
|
+
return fileExists(resolve36(syntaurRoot(), "config.md"));
|
|
19863
20153
|
}
|
|
19864
20154
|
async function removeSyntaurData() {
|
|
19865
20155
|
await rm4(syntaurRoot(), { recursive: true, force: true });
|
|
19866
20156
|
}
|
|
19867
20157
|
async function getConfiguredProjectDir() {
|
|
19868
|
-
if (!await fileExists(
|
|
20158
|
+
if (!await fileExists(resolve36(syntaurRoot(), "config.md"))) {
|
|
19869
20159
|
return null;
|
|
19870
20160
|
}
|
|
19871
20161
|
return (await readConfig()).defaultProjectDir;
|
|
@@ -19934,24 +20224,24 @@ async function textPrompt(question, defaultValue) {
|
|
|
19934
20224
|
|
|
19935
20225
|
// src/utils/install-skills.ts
|
|
19936
20226
|
init_fs();
|
|
19937
|
-
import { readFile as
|
|
19938
|
-
import { resolve as
|
|
20227
|
+
import { readFile as readFile25, readdir as readdir15, mkdir as mkdir4, copyFile, rm as rm5, lstat as lstat2 } from "fs/promises";
|
|
20228
|
+
import { resolve as resolve38, relative as relative3, join as join4 } from "path";
|
|
19939
20229
|
import { homedir as homedir5 } from "os";
|
|
19940
20230
|
|
|
19941
20231
|
// src/utils/plugin-state.ts
|
|
19942
20232
|
init_fs();
|
|
19943
|
-
import { readFile as
|
|
19944
|
-
import { resolve as
|
|
20233
|
+
import { readFile as readFile24 } from "fs/promises";
|
|
20234
|
+
import { resolve as resolve37 } from "path";
|
|
19945
20235
|
import { homedir as homedir4 } from "os";
|
|
19946
20236
|
function settingsPathFor(agent) {
|
|
19947
|
-
if (agent === "claude") return
|
|
20237
|
+
if (agent === "claude") return resolve37(homedir4(), ".claude", "settings.json");
|
|
19948
20238
|
return null;
|
|
19949
20239
|
}
|
|
19950
20240
|
async function readJsonOrNull(path) {
|
|
19951
20241
|
if (!path) return null;
|
|
19952
20242
|
if (!await fileExists(path)) return null;
|
|
19953
20243
|
try {
|
|
19954
|
-
const raw = await
|
|
20244
|
+
const raw = await readFile24(path, "utf-8");
|
|
19955
20245
|
return JSON.parse(raw);
|
|
19956
20246
|
} catch {
|
|
19957
20247
|
return null;
|
|
@@ -20000,11 +20290,11 @@ var KNOWN_SKILL_NAMES = [
|
|
|
20000
20290
|
var KNOWN_SKILLS = KNOWN_SKILL_NAMES;
|
|
20001
20291
|
async function getSkillsDir() {
|
|
20002
20292
|
const packageRoot = await findPackageRoot("skills");
|
|
20003
|
-
return
|
|
20293
|
+
return resolve38(packageRoot, "skills");
|
|
20004
20294
|
}
|
|
20005
20295
|
function defaultSkillTargetDir(target) {
|
|
20006
|
-
if (target === "claude") return
|
|
20007
|
-
return
|
|
20296
|
+
if (target === "claude") return resolve38(homedir5(), ".claude", "skills");
|
|
20297
|
+
return resolve38(homedir5(), ".codex", "skills");
|
|
20008
20298
|
}
|
|
20009
20299
|
async function walkFiles(root) {
|
|
20010
20300
|
const out = [];
|
|
@@ -20024,7 +20314,7 @@ async function walkFiles(root) {
|
|
|
20024
20314
|
}
|
|
20025
20315
|
async function filesEqual(a, b) {
|
|
20026
20316
|
try {
|
|
20027
|
-
const [ba, bb] = await Promise.all([
|
|
20317
|
+
const [ba, bb] = await Promise.all([readFile25(a), readFile25(b)]);
|
|
20028
20318
|
if (ba.length !== bb.length) return false;
|
|
20029
20319
|
return ba.equals(bb);
|
|
20030
20320
|
} catch {
|
|
@@ -20146,7 +20436,7 @@ async function uninstallSkills(options) {
|
|
|
20146
20436
|
if (await isSymlink(destDir)) continue;
|
|
20147
20437
|
const skillMd = join4(destDir, "SKILL.md");
|
|
20148
20438
|
if (!await fileExists(skillMd)) continue;
|
|
20149
|
-
const content = await
|
|
20439
|
+
const content = await readFile25(skillMd, "utf-8").catch(() => "");
|
|
20150
20440
|
const match = content.match(/^name:\s*(\S+)\s*$/m);
|
|
20151
20441
|
if (!match || match[1] !== skill) continue;
|
|
20152
20442
|
await rm5(destDir, { recursive: true, force: true });
|
|
@@ -20335,16 +20625,16 @@ async function installPluginCommand(options) {
|
|
|
20335
20625
|
// src/commands/install-statusline.ts
|
|
20336
20626
|
init_paths();
|
|
20337
20627
|
init_fs();
|
|
20338
|
-
import { readFile as
|
|
20339
|
-
import { resolve as
|
|
20628
|
+
import { readFile as readFile27, writeFile as writeFile10, copyFile as copyFile2, rm as rm6, stat as stat2, symlink as symlink2, unlink as unlink8, lstat as lstat3 } from "fs/promises";
|
|
20629
|
+
import { resolve as resolve40, dirname as dirname12 } from "path";
|
|
20340
20630
|
import { homedir as homedir6 } from "os";
|
|
20341
20631
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
20342
20632
|
|
|
20343
20633
|
// src/commands/configure-statusline.ts
|
|
20344
20634
|
init_paths();
|
|
20345
20635
|
init_fs();
|
|
20346
|
-
import { readFile as
|
|
20347
|
-
import { resolve as
|
|
20636
|
+
import { readFile as readFile26, writeFile as writeFile9 } from "fs/promises";
|
|
20637
|
+
import { resolve as resolve39, dirname as dirname11 } from "path";
|
|
20348
20638
|
import { spawnSync as spawnSync5 } from "child_process";
|
|
20349
20639
|
import { checkbox, input as input2, confirm } from "@inquirer/prompts";
|
|
20350
20640
|
var AVAILABLE_SEGMENTS = [
|
|
@@ -20365,12 +20655,12 @@ var PRESETS = {
|
|
|
20365
20655
|
tracker: { segments: ["git", "assignment", "external", "session"], separator: " \xB7 " }
|
|
20366
20656
|
};
|
|
20367
20657
|
function getConfigPath(installRoot) {
|
|
20368
|
-
return
|
|
20658
|
+
return resolve39(installRoot, "statusline.config.json");
|
|
20369
20659
|
}
|
|
20370
20660
|
async function readConfig2(path) {
|
|
20371
20661
|
if (!await fileExists(path)) return null;
|
|
20372
20662
|
try {
|
|
20373
|
-
const raw = await
|
|
20663
|
+
const raw = await readFile26(path, "utf-8");
|
|
20374
20664
|
const parsed = JSON.parse(raw);
|
|
20375
20665
|
if (!parsed || typeof parsed !== "object") return null;
|
|
20376
20666
|
const segments = Array.isArray(parsed.segments) ? parsed.segments.filter(isSegmentName) : [];
|
|
@@ -20517,13 +20807,13 @@ async function configureStatuslineCommand(options = {}) {
|
|
|
20517
20807
|
return;
|
|
20518
20808
|
}
|
|
20519
20809
|
await ensureDir(dirname11(configPath));
|
|
20520
|
-
await
|
|
20810
|
+
await writeFile9(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
20521
20811
|
console.log("Wrote statusline config:");
|
|
20522
20812
|
console.log(` path: ${configPath}`);
|
|
20523
20813
|
console.log(` segments: ${config.segments.join(", ")}`);
|
|
20524
20814
|
console.log(` separator: ${JSON.stringify(config.separator)}`);
|
|
20525
20815
|
if (config.wrap) console.log(` wrap: ${config.wrap}`);
|
|
20526
|
-
const script = options.statuslineScript ??
|
|
20816
|
+
const script = options.statuslineScript ?? resolve39(installRoot, "statusline.sh");
|
|
20527
20817
|
if (await fileExists(script)) {
|
|
20528
20818
|
console.log("");
|
|
20529
20819
|
console.log("Live preview:");
|
|
@@ -20548,17 +20838,17 @@ async function writeDefaultConfigIfMissing(installRoot) {
|
|
|
20548
20838
|
segments: ["git", "assignment", "session"],
|
|
20549
20839
|
separator: " \xB7 "
|
|
20550
20840
|
};
|
|
20551
|
-
await
|
|
20841
|
+
await writeFile9(path, JSON.stringify(defaultConfig, null, 2) + "\n", "utf-8");
|
|
20552
20842
|
}
|
|
20553
20843
|
|
|
20554
20844
|
// src/commands/install-statusline.ts
|
|
20555
20845
|
function getPackageStatuslineSource() {
|
|
20556
20846
|
const here = dirname12(fileURLToPath4(import.meta.url));
|
|
20557
|
-
return
|
|
20847
|
+
return resolve40(here, "..", "statusline", "statusline.sh");
|
|
20558
20848
|
}
|
|
20559
20849
|
async function readSettingsJson(settingsPath) {
|
|
20560
20850
|
if (!await fileExists(settingsPath)) return {};
|
|
20561
|
-
const raw = await
|
|
20851
|
+
const raw = await readFile27(settingsPath, "utf-8");
|
|
20562
20852
|
if (raw.trim() === "") return {};
|
|
20563
20853
|
try {
|
|
20564
20854
|
const parsed = JSON.parse(raw);
|
|
@@ -20571,7 +20861,7 @@ async function readSettingsJson(settingsPath) {
|
|
|
20571
20861
|
}
|
|
20572
20862
|
async function writeSettingsJson(settingsPath, data) {
|
|
20573
20863
|
await ensureDir(dirname12(settingsPath));
|
|
20574
|
-
await
|
|
20864
|
+
await writeFile10(settingsPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
20575
20865
|
}
|
|
20576
20866
|
async function resolveMode(mode, existingCommand, ourCommand) {
|
|
20577
20867
|
if (mode !== "ask") return mode;
|
|
@@ -20606,7 +20896,7 @@ function extractExistingCommand(settings) {
|
|
|
20606
20896
|
}
|
|
20607
20897
|
async function backupSettings(settingsSnapshot, backupPath) {
|
|
20608
20898
|
await ensureDir(dirname12(backupPath));
|
|
20609
|
-
await
|
|
20899
|
+
await writeFile10(
|
|
20610
20900
|
backupPath,
|
|
20611
20901
|
JSON.stringify(
|
|
20612
20902
|
{
|
|
@@ -20638,12 +20928,12 @@ async function installScript(sourceScript, destScript, link) {
|
|
|
20638
20928
|
}
|
|
20639
20929
|
async function installStatuslineCommand(options = {}) {
|
|
20640
20930
|
const mode = options.mode ?? "ask";
|
|
20641
|
-
const settingsPath = options.settingsPath ??
|
|
20931
|
+
const settingsPath = options.settingsPath ?? resolve40(homedir6(), ".claude", "settings.json");
|
|
20642
20932
|
const installRoot = options.installRoot ?? syntaurRoot();
|
|
20643
20933
|
const sourceScript = options.sourceScript ?? getPackageStatuslineSource();
|
|
20644
|
-
const destScript =
|
|
20645
|
-
const confPath =
|
|
20646
|
-
const backupPath =
|
|
20934
|
+
const destScript = resolve40(installRoot, "statusline.sh");
|
|
20935
|
+
const confPath = resolve40(installRoot, "statusline.conf");
|
|
20936
|
+
const backupPath = resolve40(installRoot, "statusline.backup.json");
|
|
20647
20937
|
if (!await fileExists(sourceScript)) {
|
|
20648
20938
|
throw new Error(
|
|
20649
20939
|
`Statusline source script not found at ${sourceScript}. Try re-installing syntaur (npm install -g syntaur) or pass --source-script explicitly.`
|
|
@@ -20679,19 +20969,19 @@ async function installStatuslineCommand(options = {}) {
|
|
|
20679
20969
|
if (parsed) {
|
|
20680
20970
|
wrapTarget = parsed;
|
|
20681
20971
|
} else {
|
|
20682
|
-
const wrapperPath =
|
|
20972
|
+
const wrapperPath = resolve40(installRoot, "statusline-wrapped.sh");
|
|
20683
20973
|
const wrapperBody = `#!/usr/bin/env bash
|
|
20684
20974
|
# Auto-generated by syntaur install-statusline.
|
|
20685
20975
|
# Executes the previously configured statusLine command.
|
|
20686
20976
|
exec ${existingCommand}
|
|
20687
20977
|
`;
|
|
20688
|
-
await
|
|
20978
|
+
await writeFile10(wrapperPath, wrapperBody, "utf-8");
|
|
20689
20979
|
await chmodExec(wrapperPath);
|
|
20690
20980
|
wrapTarget = wrapperPath;
|
|
20691
20981
|
}
|
|
20692
20982
|
}
|
|
20693
20983
|
await ensureDir(dirname12(confPath));
|
|
20694
|
-
await
|
|
20984
|
+
await writeFile10(
|
|
20695
20985
|
confPath,
|
|
20696
20986
|
wrapTarget ? `# Wrap target \u2014 the command below is invoked with the same stdin; its
|
|
20697
20987
|
# stdout becomes the leading segment of the statusline. Remove this
|
|
@@ -20734,19 +21024,19 @@ async function chmodExec(path) {
|
|
|
20734
21024
|
}
|
|
20735
21025
|
}
|
|
20736
21026
|
async function uninstallStatuslineCommand(options = {}) {
|
|
20737
|
-
const settingsPath = options.settingsPath ??
|
|
21027
|
+
const settingsPath = options.settingsPath ?? resolve40(homedir6(), ".claude", "settings.json");
|
|
20738
21028
|
const installRoot = options.installRoot ?? syntaurRoot();
|
|
20739
|
-
const destScript =
|
|
20740
|
-
const confPath =
|
|
20741
|
-
const backupPath =
|
|
20742
|
-
const wrapperPath =
|
|
21029
|
+
const destScript = resolve40(installRoot, "statusline.sh");
|
|
21030
|
+
const confPath = resolve40(installRoot, "statusline.conf");
|
|
21031
|
+
const backupPath = resolve40(installRoot, "statusline.backup.json");
|
|
21032
|
+
const wrapperPath = resolve40(installRoot, "statusline-wrapped.sh");
|
|
20743
21033
|
const settings = await readSettingsJson(settingsPath);
|
|
20744
21034
|
const existing = extractExistingCommand(settings);
|
|
20745
21035
|
const ourCommand = `bash ${destScript}`;
|
|
20746
21036
|
let restored = null;
|
|
20747
21037
|
if (await fileExists(backupPath)) {
|
|
20748
21038
|
try {
|
|
20749
|
-
const raw = await
|
|
21039
|
+
const raw = await readFile27(backupPath, "utf-8");
|
|
20750
21040
|
const parsed = JSON.parse(raw);
|
|
20751
21041
|
const prev = parsed?.previousStatusLine;
|
|
20752
21042
|
if (prev && typeof prev === "object" && typeof prev.command === "string") {
|
|
@@ -20767,7 +21057,7 @@ async function uninstallStatuslineCommand(options = {}) {
|
|
|
20767
21057
|
await writeSettingsJson(settingsPath, settings);
|
|
20768
21058
|
}
|
|
20769
21059
|
if (!options.keepScript) {
|
|
20770
|
-
const configPath =
|
|
21060
|
+
const configPath = resolve40(installRoot, "statusline.config.json");
|
|
20771
21061
|
for (const path of [destScript, confPath, backupPath, wrapperPath, configPath]) {
|
|
20772
21062
|
try {
|
|
20773
21063
|
await rm6(path, { force: true });
|
|
@@ -21024,7 +21314,7 @@ async function setupCommand(options) {
|
|
|
21024
21314
|
}
|
|
21025
21315
|
|
|
21026
21316
|
// src/commands/uninstall.ts
|
|
21027
|
-
import { resolve as
|
|
21317
|
+
import { resolve as resolve41 } from "path";
|
|
21028
21318
|
init_paths();
|
|
21029
21319
|
function expandTargets(options) {
|
|
21030
21320
|
if (options.all) {
|
|
@@ -21104,7 +21394,7 @@ async function uninstallCommand(options) {
|
|
|
21104
21394
|
const configuredProjectDir = await getConfiguredProjectDir();
|
|
21105
21395
|
await removeSyntaurData();
|
|
21106
21396
|
console.log(`Removed ${syntaurRoot()}`);
|
|
21107
|
-
if (configuredProjectDir &&
|
|
21397
|
+
if (configuredProjectDir && resolve41(configuredProjectDir) !== resolve41(syntaurRoot(), "projects")) {
|
|
21108
21398
|
console.warn(
|
|
21109
21399
|
`Warning: config.md pointed to an external project directory (${configuredProjectDir}). That directory was not removed automatically.`
|
|
21110
21400
|
);
|
|
@@ -21123,7 +21413,7 @@ init_slug();
|
|
|
21123
21413
|
init_cursor_rules();
|
|
21124
21414
|
init_codex_agents();
|
|
21125
21415
|
init_opencode_config();
|
|
21126
|
-
import { resolve as
|
|
21416
|
+
import { resolve as resolve42 } from "path";
|
|
21127
21417
|
var SUPPORTED_FRAMEWORKS = ["cursor", "codex", "opencode"];
|
|
21128
21418
|
async function setupAdapterCommand(framework, options) {
|
|
21129
21419
|
if (!SUPPORTED_FRAMEWORKS.includes(framework)) {
|
|
@@ -21149,19 +21439,19 @@ async function setupAdapterCommand(framework, options) {
|
|
|
21149
21439
|
}
|
|
21150
21440
|
const config = await readConfig();
|
|
21151
21441
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
21152
|
-
const projectDir =
|
|
21153
|
-
const assignmentDir =
|
|
21442
|
+
const projectDir = resolve42(baseDir, options.project);
|
|
21443
|
+
const assignmentDir = resolve42(
|
|
21154
21444
|
projectDir,
|
|
21155
21445
|
"assignments",
|
|
21156
21446
|
options.assignment
|
|
21157
21447
|
);
|
|
21158
|
-
const projectMdPath =
|
|
21448
|
+
const projectMdPath = resolve42(projectDir, "project.md");
|
|
21159
21449
|
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
21160
21450
|
throw new Error(
|
|
21161
21451
|
`Project "${options.project}" not found at ${projectDir}.`
|
|
21162
21452
|
);
|
|
21163
21453
|
}
|
|
21164
|
-
const assignmentMdPath2 =
|
|
21454
|
+
const assignmentMdPath2 = resolve42(assignmentDir, "assignment.md");
|
|
21165
21455
|
if (!await fileExists(assignmentDir) || !await fileExists(assignmentMdPath2)) {
|
|
21166
21456
|
throw new Error(
|
|
21167
21457
|
`Assignment "${options.assignment}" not found at ${assignmentDir}.`
|
|
@@ -21189,15 +21479,15 @@ async function setupAdapterCommand(framework, options) {
|
|
|
21189
21479
|
}
|
|
21190
21480
|
}
|
|
21191
21481
|
if (framework === "cursor") {
|
|
21192
|
-
const protocolPath =
|
|
21193
|
-
const assignmentPath =
|
|
21482
|
+
const protocolPath = resolve42(cwd, ".cursor", "rules", "syntaur-protocol.mdc");
|
|
21483
|
+
const assignmentPath = resolve42(cwd, ".cursor", "rules", "syntaur-assignment.mdc");
|
|
21194
21484
|
await writeAdapterFile(protocolPath, renderCursorProtocol());
|
|
21195
21485
|
await writeAdapterFile(assignmentPath, renderCursorAssignment(rendererParams));
|
|
21196
21486
|
} else if (framework === "codex" || framework === "opencode") {
|
|
21197
|
-
const agentsPath =
|
|
21487
|
+
const agentsPath = resolve42(cwd, "AGENTS.md");
|
|
21198
21488
|
await writeAdapterFile(agentsPath, renderCodexAgents(rendererParams));
|
|
21199
21489
|
if (framework === "opencode") {
|
|
21200
|
-
const configPath =
|
|
21490
|
+
const configPath = resolve42(cwd, "opencode.json");
|
|
21201
21491
|
await writeAdapterFile(configPath, renderOpenCodeConfig({ projectDir }));
|
|
21202
21492
|
}
|
|
21203
21493
|
}
|
|
@@ -21222,7 +21512,7 @@ async function setupAdapterCommand(framework, options) {
|
|
|
21222
21512
|
init_paths();
|
|
21223
21513
|
init_fs();
|
|
21224
21514
|
init_config2();
|
|
21225
|
-
import { resolve as
|
|
21515
|
+
import { resolve as resolve43 } from "path";
|
|
21226
21516
|
init_session_db();
|
|
21227
21517
|
init_agent_sessions();
|
|
21228
21518
|
async function trackSessionCommand(options) {
|
|
@@ -21237,7 +21527,7 @@ async function trackSessionCommand(options) {
|
|
|
21237
21527
|
if (options.project) {
|
|
21238
21528
|
const config = await readConfig();
|
|
21239
21529
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
21240
|
-
const projectDir =
|
|
21530
|
+
const projectDir = resolve43(baseDir, options.project);
|
|
21241
21531
|
if (!await fileExists(projectDir)) {
|
|
21242
21532
|
throw new Error(
|
|
21243
21533
|
`Project "${options.project}" not found at ${projectDir}.`
|
|
@@ -21613,7 +21903,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
|
21613
21903
|
`Spawn failed: ${msg}. Verify the terminal is installed and on PATH.`
|
|
21614
21904
|
);
|
|
21615
21905
|
}
|
|
21616
|
-
await new Promise((
|
|
21906
|
+
await new Promise((resolve79, reject) => {
|
|
21617
21907
|
let settled = false;
|
|
21618
21908
|
let stderr = "";
|
|
21619
21909
|
const finishOk = () => {
|
|
@@ -21623,7 +21913,7 @@ async function executeLaunchPlan(plan, spawnFn = realSpawn) {
|
|
|
21623
21913
|
child.unref();
|
|
21624
21914
|
} catch {
|
|
21625
21915
|
}
|
|
21626
|
-
|
|
21916
|
+
resolve79();
|
|
21627
21917
|
};
|
|
21628
21918
|
const finishErr = (remediation) => {
|
|
21629
21919
|
if (settled) return;
|
|
@@ -21752,7 +22042,7 @@ function appleScriptString(value) {
|
|
|
21752
22042
|
init_paths();
|
|
21753
22043
|
init_fs();
|
|
21754
22044
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
21755
|
-
import { dirname as dirname13, resolve as
|
|
22045
|
+
import { dirname as dirname13, resolve as resolve45, join as join5 } from "path";
|
|
21756
22046
|
import { realpathSync, readFileSync, mkdirSync } from "fs";
|
|
21757
22047
|
var NPX_PATTERNS = [
|
|
21758
22048
|
{ kind: "npm", re: /\/_npx\/([^/]+)\/node_modules(?:\/|$)/ },
|
|
@@ -21778,7 +22068,7 @@ function normalizeSlashes(p) {
|
|
|
21778
22068
|
}
|
|
21779
22069
|
function detectInstallKind(scriptUrl, opts = {}) {
|
|
21780
22070
|
const realpath3 = opts.realpath ?? realpathSync.native;
|
|
21781
|
-
const
|
|
22071
|
+
const readFile54 = opts.readFile ?? ((p) => readFileSync(p, "utf-8"));
|
|
21782
22072
|
const ua = opts.envUserAgent !== void 0 ? opts.envUserAgent : process.env.npm_config_user_agent ?? "";
|
|
21783
22073
|
const resolved = resolveScriptPath(scriptUrl, realpath3);
|
|
21784
22074
|
if (resolved === null) {
|
|
@@ -21799,7 +22089,7 @@ function detectInstallKind(scriptUrl, opts = {}) {
|
|
|
21799
22089
|
const pkgJsonPath = join5(dir, "package.json");
|
|
21800
22090
|
let raw;
|
|
21801
22091
|
try {
|
|
21802
|
-
raw =
|
|
22092
|
+
raw = readFile54(pkgJsonPath);
|
|
21803
22093
|
} catch {
|
|
21804
22094
|
const parent2 = dirname13(dir);
|
|
21805
22095
|
if (parent2 === dir) break;
|
|
@@ -21831,7 +22121,7 @@ function extractNpxHash(scriptUrl, opts = {}) {
|
|
|
21831
22121
|
return null;
|
|
21832
22122
|
}
|
|
21833
22123
|
function nudgeStampDir() {
|
|
21834
|
-
return
|
|
22124
|
+
return resolve45(syntaurRoot(), "npx-handler-nudge");
|
|
21835
22125
|
}
|
|
21836
22126
|
function sanitizeHash(hash) {
|
|
21837
22127
|
return hash.replace(/[^A-Za-z0-9_-]/g, "_") || "_";
|
|
@@ -21975,8 +22265,8 @@ function formatInstallUrlHandlerError(err2) {
|
|
|
21975
22265
|
init_config2();
|
|
21976
22266
|
init_paths();
|
|
21977
22267
|
init_fs();
|
|
21978
|
-
import { resolve as
|
|
21979
|
-
import { readFile as
|
|
22268
|
+
import { resolve as resolve47, isAbsolute as isAbsolute7 } from "path";
|
|
22269
|
+
import { readFile as readFile28 } from "fs/promises";
|
|
21980
22270
|
import { select, confirm as confirm2, input as input3 } from "@inquirer/prompts";
|
|
21981
22271
|
async function browseCommand(options) {
|
|
21982
22272
|
const config = await readConfig();
|
|
@@ -22045,7 +22335,7 @@ async function pickAgent2(agents) {
|
|
|
22045
22335
|
return picked;
|
|
22046
22336
|
}
|
|
22047
22337
|
async function ensureWorktree(opts) {
|
|
22048
|
-
const assignmentPath =
|
|
22338
|
+
const assignmentPath = resolve47(
|
|
22049
22339
|
opts.projectsDir,
|
|
22050
22340
|
opts.projectSlug,
|
|
22051
22341
|
"assignments",
|
|
@@ -22055,7 +22345,7 @@ async function ensureWorktree(opts) {
|
|
|
22055
22345
|
if (!await fileExists(assignmentPath)) {
|
|
22056
22346
|
return void 0;
|
|
22057
22347
|
}
|
|
22058
|
-
const content = await
|
|
22348
|
+
const content = await readFile28(assignmentPath, "utf-8");
|
|
22059
22349
|
const { parseAssignmentFrontmatter: parseAssignmentFrontmatter2 } = await Promise.resolve().then(() => (init_frontmatter(), frontmatter_exports));
|
|
22060
22350
|
const fm = parseAssignmentFrontmatter2(content);
|
|
22061
22351
|
const { workspace } = fm;
|
|
@@ -22125,7 +22415,7 @@ async function ensureWorktree(opts) {
|
|
|
22125
22415
|
async function runCreate(opts) {
|
|
22126
22416
|
const { createWorktreeAndRecord: createWorktreeAndRecord2, GitWorktreeError: GitWorktreeError2 } = await Promise.resolve().then(() => (init_git_worktree(), git_worktree_exports));
|
|
22127
22417
|
const expandedWorktree = expandHome(opts.worktreePath);
|
|
22128
|
-
const absWorktree = isAbsolute7(expandedWorktree) ? expandedWorktree :
|
|
22418
|
+
const absWorktree = isAbsolute7(expandedWorktree) ? expandedWorktree : resolve47(expandedWorktree);
|
|
22129
22419
|
try {
|
|
22130
22420
|
await createWorktreeAndRecord2({
|
|
22131
22421
|
assignmentPath: opts.assignmentPath,
|
|
@@ -22154,7 +22444,7 @@ init_paths();
|
|
|
22154
22444
|
init_fs();
|
|
22155
22445
|
init_playbook();
|
|
22156
22446
|
init_playbooks();
|
|
22157
|
-
import { resolve as
|
|
22447
|
+
import { resolve as resolve48 } from "path";
|
|
22158
22448
|
async function createPlaybookCommand(name, options) {
|
|
22159
22449
|
if (!name.trim()) {
|
|
22160
22450
|
throw new Error("Playbook name cannot be empty.");
|
|
@@ -22167,7 +22457,7 @@ async function createPlaybookCommand(name, options) {
|
|
|
22167
22457
|
}
|
|
22168
22458
|
const dir = playbooksDir();
|
|
22169
22459
|
await ensureDir(dir);
|
|
22170
|
-
const filePath =
|
|
22460
|
+
const filePath = resolve48(dir, `${slug}.md`);
|
|
22171
22461
|
if (await fileExists(filePath)) {
|
|
22172
22462
|
throw new Error(
|
|
22173
22463
|
`Playbook "${slug}" already exists at ${filePath}
|
|
@@ -22189,8 +22479,8 @@ init_paths();
|
|
|
22189
22479
|
init_fs();
|
|
22190
22480
|
init_parser();
|
|
22191
22481
|
init_config2();
|
|
22192
|
-
import { readdir as readdir16, readFile as
|
|
22193
|
-
import { resolve as
|
|
22482
|
+
import { readdir as readdir16, readFile as readFile29 } from "fs/promises";
|
|
22483
|
+
import { resolve as resolve49 } from "path";
|
|
22194
22484
|
async function listPlaybooksCommand(options = {}) {
|
|
22195
22485
|
const dir = playbooksDir();
|
|
22196
22486
|
if (!await fileExists(dir)) {
|
|
@@ -22205,8 +22495,8 @@ async function listPlaybooksCommand(options = {}) {
|
|
|
22205
22495
|
);
|
|
22206
22496
|
const rows = [];
|
|
22207
22497
|
for (const entry of mdFiles) {
|
|
22208
|
-
const filePath =
|
|
22209
|
-
const raw = await
|
|
22498
|
+
const filePath = resolve49(dir, entry.name);
|
|
22499
|
+
const raw = await readFile29(filePath, "utf-8");
|
|
22210
22500
|
const parsed = parsePlaybook(raw);
|
|
22211
22501
|
const slug = parsed.slug || entry.name.replace(/\.md$/, "");
|
|
22212
22502
|
const disabled = disabledSet.has(slug);
|
|
@@ -22329,14 +22619,14 @@ init_fs();
|
|
|
22329
22619
|
init_config2();
|
|
22330
22620
|
init_slug();
|
|
22331
22621
|
import { Command as Command2 } from "commander";
|
|
22332
|
-
import { readFile as
|
|
22333
|
-
import { resolve as
|
|
22622
|
+
import { readFile as readFile31 } from "fs/promises";
|
|
22623
|
+
import { resolve as resolve51 } from "path";
|
|
22334
22624
|
|
|
22335
22625
|
// src/commands/bundle.ts
|
|
22336
22626
|
init_paths();
|
|
22337
22627
|
import { Command } from "commander";
|
|
22338
|
-
import { mkdir as mkdir7, readFile as
|
|
22339
|
-
import { resolve as
|
|
22628
|
+
import { mkdir as mkdir7, readFile as readFile30, readdir as readdir17, rm as rm7, writeFile as writeFile12 } from "fs/promises";
|
|
22629
|
+
import { resolve as resolve50 } from "path";
|
|
22340
22630
|
init_parser2();
|
|
22341
22631
|
init_fs();
|
|
22342
22632
|
init_config2();
|
|
@@ -22354,7 +22644,7 @@ async function resolveBundleScope(options) {
|
|
|
22354
22644
|
throw new Error(`Invalid project slug: "${options.project}".`);
|
|
22355
22645
|
}
|
|
22356
22646
|
const config = await readConfig();
|
|
22357
|
-
const projectMd =
|
|
22647
|
+
const projectMd = resolve50(config.defaultProjectDir, options.project, "project.md");
|
|
22358
22648
|
if (!await fileExists(projectMd)) {
|
|
22359
22649
|
throw new Error(`Project "${options.project}" not found.`);
|
|
22360
22650
|
}
|
|
@@ -22424,10 +22714,10 @@ function pickNextPlanFile(planDir, existingFiles) {
|
|
|
22424
22714
|
const m = f.match(/^plan-v(\d+)\.md$/);
|
|
22425
22715
|
if (m) versions.add(parseInt(m[1], 10));
|
|
22426
22716
|
}
|
|
22427
|
-
if (versions.size === 0) return { target:
|
|
22717
|
+
if (versions.size === 0) return { target: resolve50(planDir, "plan.md"), version: 1 };
|
|
22428
22718
|
let n = 2;
|
|
22429
22719
|
while (versions.has(n)) n++;
|
|
22430
|
-
return { target:
|
|
22720
|
+
return { target: resolve50(planDir, `plan-v${n}.md`), version: n };
|
|
22431
22721
|
}
|
|
22432
22722
|
function dedupePreserveOrder(ids) {
|
|
22433
22723
|
const out = [];
|
|
@@ -22511,7 +22801,7 @@ bundleCommand.command("new").description("Create a new bundle from 2+ existing t
|
|
|
22511
22801
|
if (options.plan) {
|
|
22512
22802
|
const planDir = bundlePlanDir(sc.todosPath, sc.scopeId, id);
|
|
22513
22803
|
await ensureDir(planDir);
|
|
22514
|
-
const target =
|
|
22804
|
+
const target = resolve50(planDir, "plan.md");
|
|
22515
22805
|
const memberLines = items.map((it) => `- ${it.description} [t:${it.id}]`).join("\n");
|
|
22516
22806
|
const stub = [
|
|
22517
22807
|
"---",
|
|
@@ -22687,7 +22977,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
22687
22977
|
}
|
|
22688
22978
|
const repository = options.repository ?? process.cwd();
|
|
22689
22979
|
const parentBranch = options.parentBranch ?? "main";
|
|
22690
|
-
const worktreePath = options.worktreePath ??
|
|
22980
|
+
const worktreePath = options.worktreePath ?? resolve50(repository, ".worktrees", options.branch);
|
|
22691
22981
|
const checklist = await readChecklist(sc.todosPath, sc.checklistKey);
|
|
22692
22982
|
for (const memberId of bundle.todoIds) {
|
|
22693
22983
|
const item = checklist.items.find((i) => i.id === memberId);
|
|
@@ -22697,8 +22987,8 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
22697
22987
|
}
|
|
22698
22988
|
const bundlesFilePath = bundlesPath(sc.todosPath);
|
|
22699
22989
|
const checklistFilePath = checklistPath(sc.todosPath, sc.checklistKey);
|
|
22700
|
-
const bundlesSnapshot = await fileExists(bundlesFilePath) ? await
|
|
22701
|
-
const checklistSnapshot = await fileExists(checklistFilePath) ? await
|
|
22990
|
+
const bundlesSnapshot = await fileExists(bundlesFilePath) ? await readFile30(bundlesFilePath, "utf-8") : null;
|
|
22991
|
+
const checklistSnapshot = await fileExists(checklistFilePath) ? await readFile30(checklistFilePath, "utf-8") : null;
|
|
22702
22992
|
const record = async () => {
|
|
22703
22993
|
bundle.branch = options.branch;
|
|
22704
22994
|
bundle.worktreePath = worktreePath;
|
|
@@ -22714,7 +23004,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
22714
23004
|
try {
|
|
22715
23005
|
await writeBundles(sc.todosPath, bundles);
|
|
22716
23006
|
await writeChecklist(sc.todosPath, checklist);
|
|
22717
|
-
const ctxDir =
|
|
23007
|
+
const ctxDir = resolve50(worktreePath, ".syntaur");
|
|
22718
23008
|
await mkdir7(ctxDir, { recursive: true });
|
|
22719
23009
|
const payload = {
|
|
22720
23010
|
bundleId: bundle.id,
|
|
@@ -22728,7 +23018,7 @@ bundleCommand.command("worktree").description("Create a git worktree for the bun
|
|
|
22728
23018
|
repository,
|
|
22729
23019
|
boundAt: nowISO()
|
|
22730
23020
|
};
|
|
22731
|
-
await
|
|
23021
|
+
await writeFile12(resolve50(ctxDir, "context.json"), JSON.stringify(payload, null, 2) + "\n");
|
|
22732
23022
|
} catch (err2) {
|
|
22733
23023
|
try {
|
|
22734
23024
|
if (bundlesSnapshot === null) {
|
|
@@ -22918,7 +23208,7 @@ async function resolveScope(options) {
|
|
|
22918
23208
|
throw new Error(`Invalid project slug: "${options.project}".`);
|
|
22919
23209
|
}
|
|
22920
23210
|
const config = await readConfig();
|
|
22921
|
-
const projectMd =
|
|
23211
|
+
const projectMd = resolve51(config.defaultProjectDir, options.project, "project.md");
|
|
22922
23212
|
if (!await fileExists(projectMd)) {
|
|
22923
23213
|
throw new Error(`Project "${options.project}" not found.`);
|
|
22924
23214
|
}
|
|
@@ -23241,10 +23531,10 @@ todoCommand.command("archive").description("Archive completed todos and their lo
|
|
|
23241
23531
|
(e) => e.itemIds.every((id) => completedIds.has(id))
|
|
23242
23532
|
);
|
|
23243
23533
|
const archFile = archivePath(todosPath, workspace, checklist.archiveInterval);
|
|
23244
|
-
await ensureDir(
|
|
23534
|
+
await ensureDir(resolve51(todosPath, "archive"));
|
|
23245
23535
|
let archContent = "";
|
|
23246
23536
|
if (await fileExists(archFile)) {
|
|
23247
|
-
archContent = await
|
|
23537
|
+
archContent = await readFile31(archFile, "utf-8");
|
|
23248
23538
|
archContent = archContent.trimEnd() + "\n\n";
|
|
23249
23539
|
} else {
|
|
23250
23540
|
archContent = `---
|
|
@@ -23421,12 +23711,12 @@ function describeScope(scope) {
|
|
|
23421
23711
|
}
|
|
23422
23712
|
async function injectPromotedTodos(assignmentDir, todos, scopeLabel) {
|
|
23423
23713
|
const { resolve: resolvePath2 } = await import("path");
|
|
23424
|
-
const { readFile:
|
|
23714
|
+
const { readFile: readFile54 } = await import("fs/promises");
|
|
23425
23715
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
23426
23716
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
23427
23717
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
23428
23718
|
const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
|
|
23429
|
-
let content = await
|
|
23719
|
+
let content = await readFile54(assignmentMdPath2, "utf-8");
|
|
23430
23720
|
content = appendTodosToAssignmentBody2(
|
|
23431
23721
|
content,
|
|
23432
23722
|
todos.map((t) => ({
|
|
@@ -23477,7 +23767,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
|
|
|
23477
23767
|
);
|
|
23478
23768
|
let target;
|
|
23479
23769
|
if (existingFiles.length === 0) {
|
|
23480
|
-
target =
|
|
23770
|
+
target = resolve51(planDir, "plan.md");
|
|
23481
23771
|
} else {
|
|
23482
23772
|
const versions = /* @__PURE__ */ new Set();
|
|
23483
23773
|
for (const f of existingFiles) {
|
|
@@ -23487,7 +23777,7 @@ todoCommand.command("plan").description("Create or open a plan directory for a t
|
|
|
23487
23777
|
}
|
|
23488
23778
|
let n = 2;
|
|
23489
23779
|
while (versions.has(n)) n++;
|
|
23490
|
-
target =
|
|
23780
|
+
target = resolve51(planDir, `plan-v${n}.md`);
|
|
23491
23781
|
}
|
|
23492
23782
|
if (!await fileExists(target)) {
|
|
23493
23783
|
const stub = `---
|
|
@@ -23676,12 +23966,12 @@ backupCommand.command("config").description("Show or update backup configuration
|
|
|
23676
23966
|
|
|
23677
23967
|
// src/commands/doctor.ts
|
|
23678
23968
|
import { Command as Command4 } from "commander";
|
|
23679
|
-
import { readFile as
|
|
23680
|
-
import { isAbsolute as isAbsolute10, resolve as
|
|
23969
|
+
import { readFile as readFile39 } from "fs/promises";
|
|
23970
|
+
import { isAbsolute as isAbsolute10, resolve as resolve63 } from "path";
|
|
23681
23971
|
|
|
23682
23972
|
// src/utils/doctor/index.ts
|
|
23683
23973
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
23684
|
-
import { readFile as
|
|
23974
|
+
import { readFile as readFile38 } from "fs/promises";
|
|
23685
23975
|
import { dirname as dirname17, join as join9 } from "path";
|
|
23686
23976
|
|
|
23687
23977
|
// src/utils/doctor/context.ts
|
|
@@ -23689,11 +23979,11 @@ init_config2();
|
|
|
23689
23979
|
init_paths();
|
|
23690
23980
|
init_fs();
|
|
23691
23981
|
import Database4 from "better-sqlite3";
|
|
23692
|
-
import { resolve as
|
|
23982
|
+
import { resolve as resolve52 } from "path";
|
|
23693
23983
|
async function buildCheckContext(cwd = process.cwd()) {
|
|
23694
23984
|
const config = await readConfig();
|
|
23695
23985
|
const root = syntaurRoot();
|
|
23696
|
-
const dbPath =
|
|
23986
|
+
const dbPath = resolve52(root, "syntaur.db");
|
|
23697
23987
|
let db5 = null;
|
|
23698
23988
|
let dbError = null;
|
|
23699
23989
|
if (await fileExists(dbPath)) {
|
|
@@ -23727,8 +24017,8 @@ function closeCheckContext(ctx) {
|
|
|
23727
24017
|
// src/utils/doctor/checks/env.ts
|
|
23728
24018
|
init_fs();
|
|
23729
24019
|
init_paths();
|
|
23730
|
-
import { resolve as
|
|
23731
|
-
import { readFile as
|
|
24020
|
+
import { resolve as resolve53, isAbsolute as isAbsolute8 } from "path";
|
|
24021
|
+
import { readFile as readFile32, stat as stat3 } from "fs/promises";
|
|
23732
24022
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
23733
24023
|
import { dirname as dirname15, join as join7 } from "path";
|
|
23734
24024
|
var CATEGORY = "env";
|
|
@@ -23768,7 +24058,7 @@ var configValid = {
|
|
|
23768
24058
|
category: CATEGORY,
|
|
23769
24059
|
title: "~/.syntaur/config.md is valid",
|
|
23770
24060
|
async run(ctx) {
|
|
23771
|
-
const configPath =
|
|
24061
|
+
const configPath = resolve53(ctx.syntaurRoot, "config.md");
|
|
23772
24062
|
if (!await fileExists(configPath)) {
|
|
23773
24063
|
return {
|
|
23774
24064
|
id: this.id,
|
|
@@ -23785,7 +24075,7 @@ var configValid = {
|
|
|
23785
24075
|
autoFixable: false
|
|
23786
24076
|
};
|
|
23787
24077
|
}
|
|
23788
|
-
const content = await
|
|
24078
|
+
const content = await readFile32(configPath, "utf-8");
|
|
23789
24079
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
23790
24080
|
if (!fmMatch || fmMatch[1].trim() === "") {
|
|
23791
24081
|
return {
|
|
@@ -24065,7 +24355,7 @@ async function readLocalPkg() {
|
|
|
24065
24355
|
for (let i = 0; i < 6; i++) {
|
|
24066
24356
|
const candidate = join7(dir, "package.json");
|
|
24067
24357
|
try {
|
|
24068
|
-
const text = await
|
|
24358
|
+
const text = await readFile32(candidate, "utf-8");
|
|
24069
24359
|
return JSON.parse(text);
|
|
24070
24360
|
} catch {
|
|
24071
24361
|
dir = dirname15(dir);
|
|
@@ -24117,7 +24407,7 @@ function versionGte(a, b) {
|
|
|
24117
24407
|
|
|
24118
24408
|
// src/utils/doctor/checks/structure.ts
|
|
24119
24409
|
init_fs();
|
|
24120
|
-
import { resolve as
|
|
24410
|
+
import { resolve as resolve54 } from "path";
|
|
24121
24411
|
import { readdir as readdir18, stat as stat4 } from "fs/promises";
|
|
24122
24412
|
var CATEGORY2 = "structure";
|
|
24123
24413
|
var KNOWN_TOP_LEVEL = /* @__PURE__ */ new Set([
|
|
@@ -24137,7 +24427,7 @@ var projectsDir = {
|
|
|
24137
24427
|
category: CATEGORY2,
|
|
24138
24428
|
title: "projects/ directory exists",
|
|
24139
24429
|
async run(ctx) {
|
|
24140
|
-
const p =
|
|
24430
|
+
const p = resolve54(ctx.syntaurRoot, "projects");
|
|
24141
24431
|
if (!await fileExists(p)) {
|
|
24142
24432
|
return {
|
|
24143
24433
|
id: this.id,
|
|
@@ -24162,7 +24452,7 @@ var playbooksDir2 = {
|
|
|
24162
24452
|
category: CATEGORY2,
|
|
24163
24453
|
title: "playbooks/ directory exists",
|
|
24164
24454
|
async run(ctx) {
|
|
24165
|
-
const p =
|
|
24455
|
+
const p = resolve54(ctx.syntaurRoot, "playbooks");
|
|
24166
24456
|
if (!await fileExists(p)) {
|
|
24167
24457
|
return {
|
|
24168
24458
|
id: this.id,
|
|
@@ -24187,7 +24477,7 @@ var todosDirValid = {
|
|
|
24187
24477
|
category: CATEGORY2,
|
|
24188
24478
|
title: "todos/ directory is readable (if present)",
|
|
24189
24479
|
async run(ctx) {
|
|
24190
|
-
const p =
|
|
24480
|
+
const p = resolve54(ctx.syntaurRoot, "todos");
|
|
24191
24481
|
if (!await fileExists(p)) {
|
|
24192
24482
|
return {
|
|
24193
24483
|
id: this.id,
|
|
@@ -24218,7 +24508,7 @@ var serversDirValid = {
|
|
|
24218
24508
|
category: CATEGORY2,
|
|
24219
24509
|
title: "servers/ directory is readable (if present)",
|
|
24220
24510
|
async run(ctx) {
|
|
24221
|
-
const p =
|
|
24511
|
+
const p = resolve54(ctx.syntaurRoot, "servers");
|
|
24222
24512
|
if (!await fileExists(p)) {
|
|
24223
24513
|
return {
|
|
24224
24514
|
id: this.id,
|
|
@@ -24263,7 +24553,7 @@ var knownFilesRecognized = {
|
|
|
24263
24553
|
title: this.title,
|
|
24264
24554
|
status: "warn",
|
|
24265
24555
|
detail: `unexpected top-level entries: ${unexpected.join(", ")}`,
|
|
24266
|
-
affected: unexpected.map((n) =>
|
|
24556
|
+
affected: unexpected.map((n) => resolve54(ctx.syntaurRoot, n)),
|
|
24267
24557
|
remediation: {
|
|
24268
24558
|
kind: "manual",
|
|
24269
24559
|
suggestion: "Review these entries \u2014 they may be leftover state from older versions",
|
|
@@ -24292,7 +24582,7 @@ function pass2(check) {
|
|
|
24292
24582
|
|
|
24293
24583
|
// src/utils/doctor/checks/project.ts
|
|
24294
24584
|
init_fs();
|
|
24295
|
-
import { resolve as
|
|
24585
|
+
import { resolve as resolve55 } from "path";
|
|
24296
24586
|
import { readdir as readdir19, stat as stat5 } from "fs/promises";
|
|
24297
24587
|
var CATEGORY3 = "project";
|
|
24298
24588
|
var REQUIRED_PROJECT_FILES = [
|
|
@@ -24322,10 +24612,10 @@ async function listProjects2(ctx) {
|
|
|
24322
24612
|
for (const e of entries) {
|
|
24323
24613
|
if (!e.isDirectory()) continue;
|
|
24324
24614
|
if (e.name.startsWith(".") || e.name.startsWith("_")) continue;
|
|
24325
|
-
const projectDir =
|
|
24615
|
+
const projectDir = resolve55(dir, e.name);
|
|
24326
24616
|
let looksLikeProject = false;
|
|
24327
24617
|
for (const marker of PROJECT_MARKERS) {
|
|
24328
|
-
if (await fileExists(
|
|
24618
|
+
if (await fileExists(resolve55(projectDir, marker))) {
|
|
24329
24619
|
looksLikeProject = true;
|
|
24330
24620
|
break;
|
|
24331
24621
|
}
|
|
@@ -24344,7 +24634,7 @@ var requiredFiles = {
|
|
|
24344
24634
|
for (const projectDir of projects) {
|
|
24345
24635
|
const missing = [];
|
|
24346
24636
|
for (const rel of REQUIRED_PROJECT_FILES) {
|
|
24347
|
-
const p =
|
|
24637
|
+
const p = resolve55(projectDir, rel);
|
|
24348
24638
|
if (!await fileExists(p)) missing.push(rel);
|
|
24349
24639
|
}
|
|
24350
24640
|
if (missing.length === 0) continue;
|
|
@@ -24354,7 +24644,7 @@ var requiredFiles = {
|
|
|
24354
24644
|
title: this.title,
|
|
24355
24645
|
status: "error",
|
|
24356
24646
|
detail: `project at ${projectDir} is missing: ${missing.join(", ")}`,
|
|
24357
|
-
affected: missing.map((m) =>
|
|
24647
|
+
affected: missing.map((m) => resolve55(projectDir, m)),
|
|
24358
24648
|
remediation: {
|
|
24359
24649
|
kind: "manual",
|
|
24360
24650
|
suggestion: "Recreate the missing scaffold files from templates",
|
|
@@ -24377,7 +24667,7 @@ var manifestStale = {
|
|
|
24377
24667
|
const projects = await listProjects2(ctx);
|
|
24378
24668
|
const results = [];
|
|
24379
24669
|
for (const projectDir of projects) {
|
|
24380
|
-
const manifestPath =
|
|
24670
|
+
const manifestPath = resolve55(projectDir, "manifest.md");
|
|
24381
24671
|
if (!await fileExists(manifestPath)) continue;
|
|
24382
24672
|
const manifestMtime = (await stat5(manifestPath)).mtimeMs;
|
|
24383
24673
|
const newestAssignment = await newestAssignmentMtime(projectDir);
|
|
@@ -24426,7 +24716,7 @@ var orphanFiles = {
|
|
|
24426
24716
|
title: this.title,
|
|
24427
24717
|
status: "warn",
|
|
24428
24718
|
detail: `project at ${projectDir} has unexpected entries: ${orphans.join(", ")}`,
|
|
24429
|
-
affected: orphans.map((o) =>
|
|
24719
|
+
affected: orphans.map((o) => resolve55(projectDir, o)),
|
|
24430
24720
|
autoFixable: false
|
|
24431
24721
|
});
|
|
24432
24722
|
}
|
|
@@ -24436,7 +24726,7 @@ var orphanFiles = {
|
|
|
24436
24726
|
};
|
|
24437
24727
|
var projectChecks = [requiredFiles, manifestStale, orphanFiles];
|
|
24438
24728
|
async function newestAssignmentMtime(projectDir) {
|
|
24439
|
-
const assignmentsRoot =
|
|
24729
|
+
const assignmentsRoot = resolve55(projectDir, "assignments");
|
|
24440
24730
|
if (!await fileExists(assignmentsRoot)) return 0;
|
|
24441
24731
|
let newest = 0;
|
|
24442
24732
|
let entries;
|
|
@@ -24447,7 +24737,7 @@ async function newestAssignmentMtime(projectDir) {
|
|
|
24447
24737
|
}
|
|
24448
24738
|
for (const e of entries) {
|
|
24449
24739
|
if (!e.isDirectory()) continue;
|
|
24450
|
-
const assignmentMd =
|
|
24740
|
+
const assignmentMd = resolve55(assignmentsRoot, e.name, "assignment.md");
|
|
24451
24741
|
try {
|
|
24452
24742
|
const s = await stat5(assignmentMd);
|
|
24453
24743
|
if (s.mtimeMs > newest) newest = s.mtimeMs;
|
|
@@ -24471,8 +24761,8 @@ init_fs();
|
|
|
24471
24761
|
init_parser();
|
|
24472
24762
|
init_types();
|
|
24473
24763
|
init_paths();
|
|
24474
|
-
import { resolve as
|
|
24475
|
-
import { readFile as
|
|
24764
|
+
import { resolve as resolve56 } from "path";
|
|
24765
|
+
import { readFile as readFile33, readdir as readdir20 } from "fs/promises";
|
|
24476
24766
|
var CATEGORY4 = "assignment";
|
|
24477
24767
|
var STATUSES_REQUIRING_HANDOFF = /* @__PURE__ */ new Set(["review", "completed"]);
|
|
24478
24768
|
var PRE_WORKSPACE_STATUSES = /* @__PURE__ */ new Set([
|
|
@@ -24566,7 +24856,7 @@ var invalidStatus = {
|
|
|
24566
24856
|
const allowed = configuredStatuses(ctx);
|
|
24567
24857
|
const results = [];
|
|
24568
24858
|
for (const a of withAssignmentMd) {
|
|
24569
|
-
const path =
|
|
24859
|
+
const path = resolve56(a.assignmentDir, "assignment.md");
|
|
24570
24860
|
const parsed = await parseSafe2(path);
|
|
24571
24861
|
if (!parsed) continue;
|
|
24572
24862
|
if (!allowed.has(parsed.status)) {
|
|
@@ -24599,7 +24889,7 @@ var workspaceMissing = {
|
|
|
24599
24889
|
const terminal = terminalStatuses(ctx);
|
|
24600
24890
|
const results = [];
|
|
24601
24891
|
for (const a of withAssignmentMd) {
|
|
24602
|
-
const path =
|
|
24892
|
+
const path = resolve56(a.assignmentDir, "assignment.md");
|
|
24603
24893
|
const parsed = await parseSafe2(path);
|
|
24604
24894
|
if (!parsed) continue;
|
|
24605
24895
|
if (terminal.has(parsed.status)) continue;
|
|
@@ -24646,12 +24936,12 @@ var requiredFilesByStatus = {
|
|
|
24646
24936
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
24647
24937
|
const results = [];
|
|
24648
24938
|
for (const a of withAssignmentMd) {
|
|
24649
|
-
const assignmentPath =
|
|
24939
|
+
const assignmentPath = resolve56(a.assignmentDir, "assignment.md");
|
|
24650
24940
|
const parsed = await parseSafe2(assignmentPath);
|
|
24651
24941
|
if (!parsed) continue;
|
|
24652
24942
|
const missing = [];
|
|
24653
24943
|
if (STATUSES_REQUIRING_HANDOFF.has(parsed.status)) {
|
|
24654
|
-
const handoffPath =
|
|
24944
|
+
const handoffPath = resolve56(a.assignmentDir, "handoff.md");
|
|
24655
24945
|
if (!await fileExists(handoffPath)) missing.push("handoff.md");
|
|
24656
24946
|
}
|
|
24657
24947
|
if (missing.length === 0) continue;
|
|
@@ -24661,7 +24951,7 @@ var requiredFilesByStatus = {
|
|
|
24661
24951
|
title: this.title,
|
|
24662
24952
|
status: "warn",
|
|
24663
24953
|
detail: `${a.projectSlug}/${a.assignmentSlug} (status: ${parsed.status}) is missing ${missing.join(", ")}`,
|
|
24664
|
-
affected: missing.map((m) =>
|
|
24954
|
+
affected: missing.map((m) => resolve56(a.assignmentDir, m)),
|
|
24665
24955
|
remediation: {
|
|
24666
24956
|
kind: "manual",
|
|
24667
24957
|
suggestion: `Create the missing ${missing.join(" and ")} files for this assignment`,
|
|
@@ -24684,7 +24974,7 @@ var companionFilesScaffolded = {
|
|
|
24684
24974
|
for (const a of withAssignmentMd) {
|
|
24685
24975
|
const missing = [];
|
|
24686
24976
|
for (const filename of ["progress.md", "comments.md"]) {
|
|
24687
|
-
if (!await fileExists(
|
|
24977
|
+
if (!await fileExists(resolve56(a.assignmentDir, filename))) {
|
|
24688
24978
|
missing.push(filename);
|
|
24689
24979
|
}
|
|
24690
24980
|
}
|
|
@@ -24696,7 +24986,7 @@ var companionFilesScaffolded = {
|
|
|
24696
24986
|
title: this.title,
|
|
24697
24987
|
status: "warn",
|
|
24698
24988
|
detail: `${label} is missing ${missing.join(" and ")} (pre-v2.0 assignment \u2014 not required, but scaffolding them keeps the dashboard and CLIs consistent)`,
|
|
24699
|
-
affected: missing.map((m) =>
|
|
24989
|
+
affected: missing.map((m) => resolve56(a.assignmentDir, m)),
|
|
24700
24990
|
remediation: {
|
|
24701
24991
|
kind: "manual",
|
|
24702
24992
|
suggestion: `Create ${missing.join(" and ")} with the renderProgress/renderComments templates, or re-scaffold via the CLI`,
|
|
@@ -24729,7 +25019,7 @@ var typeDefinition = {
|
|
|
24729
25019
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
24730
25020
|
const results = [];
|
|
24731
25021
|
for (const a of withAssignmentMd) {
|
|
24732
|
-
const path =
|
|
25022
|
+
const path = resolve56(a.assignmentDir, "assignment.md");
|
|
24733
25023
|
const parsed = await parseSafe2(path);
|
|
24734
25024
|
if (!parsed) continue;
|
|
24735
25025
|
if (!parsed.type) continue;
|
|
@@ -24763,7 +25053,7 @@ var projectFrontmatterMatchesContainer = {
|
|
|
24763
25053
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
24764
25054
|
const results = [];
|
|
24765
25055
|
for (const a of withAssignmentMd) {
|
|
24766
|
-
const path =
|
|
25056
|
+
const path = resolve56(a.assignmentDir, "assignment.md");
|
|
24767
25057
|
const parsed = await parseSafe2(path);
|
|
24768
25058
|
if (!parsed) continue;
|
|
24769
25059
|
if (a.standalone) {
|
|
@@ -24814,13 +25104,13 @@ var draftMissingObjective = {
|
|
|
24814
25104
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
24815
25105
|
const results = [];
|
|
24816
25106
|
for (const a of withAssignmentMd) {
|
|
24817
|
-
const path =
|
|
25107
|
+
const path = resolve56(a.assignmentDir, "assignment.md");
|
|
24818
25108
|
const parsed = await parseSafe2(path);
|
|
24819
25109
|
if (!parsed) continue;
|
|
24820
25110
|
if (parsed.status !== "draft") continue;
|
|
24821
25111
|
let raw;
|
|
24822
25112
|
try {
|
|
24823
|
-
raw = await
|
|
25113
|
+
raw = await readFile33(path, "utf-8");
|
|
24824
25114
|
} catch {
|
|
24825
25115
|
continue;
|
|
24826
25116
|
}
|
|
@@ -24853,7 +25143,7 @@ var readyToImplementMissingPlan = {
|
|
|
24853
25143
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
24854
25144
|
const results = [];
|
|
24855
25145
|
for (const a of withAssignmentMd) {
|
|
24856
|
-
const path =
|
|
25146
|
+
const path = resolve56(a.assignmentDir, "assignment.md");
|
|
24857
25147
|
const parsed = await parseSafe2(path);
|
|
24858
25148
|
if (!parsed) continue;
|
|
24859
25149
|
if (parsed.status !== "ready_to_implement") continue;
|
|
@@ -24862,7 +25152,7 @@ var readyToImplementMissingPlan = {
|
|
|
24862
25152
|
let hasPlanContent = false;
|
|
24863
25153
|
for (const f of planFiles) {
|
|
24864
25154
|
try {
|
|
24865
|
-
const c2 = await
|
|
25155
|
+
const c2 = await readFile33(resolve56(a.assignmentDir, f), "utf-8");
|
|
24866
25156
|
if (c2.trim().length > 0) {
|
|
24867
25157
|
hasPlanContent = true;
|
|
24868
25158
|
break;
|
|
@@ -24878,7 +25168,7 @@ var readyToImplementMissingPlan = {
|
|
|
24878
25168
|
title: this.title,
|
|
24879
25169
|
status: "warn",
|
|
24880
25170
|
detail: `${label} (status: ready_to_implement) has no plan.md or plan-v<N>.md`,
|
|
24881
|
-
affected: [
|
|
25171
|
+
affected: [resolve56(a.assignmentDir, "plan.md")],
|
|
24882
25172
|
remediation: {
|
|
24883
25173
|
kind: "manual",
|
|
24884
25174
|
suggestion: `Write a plan with '/plan-assignment' (or 'syntaur plan'), then re-mark ready_to_implement`,
|
|
@@ -24905,7 +25195,7 @@ var assignmentChecks = [
|
|
|
24905
25195
|
];
|
|
24906
25196
|
async function parseSafe2(path) {
|
|
24907
25197
|
try {
|
|
24908
|
-
const content = await
|
|
25198
|
+
const content = await readFile33(path, "utf-8");
|
|
24909
25199
|
return parseAssignmentFull(content);
|
|
24910
25200
|
} catch {
|
|
24911
25201
|
return null;
|
|
@@ -24924,7 +25214,7 @@ function pass4(check, detail) {
|
|
|
24924
25214
|
|
|
24925
25215
|
// src/utils/doctor/checks/dashboard.ts
|
|
24926
25216
|
init_fs();
|
|
24927
|
-
import { resolve as
|
|
25217
|
+
import { resolve as resolve57 } from "path";
|
|
24928
25218
|
var CATEGORY5 = "dashboard";
|
|
24929
25219
|
var dbReachable = {
|
|
24930
25220
|
id: "dashboard.db-reachable",
|
|
@@ -24938,7 +25228,7 @@ var dbReachable = {
|
|
|
24938
25228
|
title: this.title,
|
|
24939
25229
|
status: "error",
|
|
24940
25230
|
detail: `could not open syntaur.db: ${ctx.dbError ?? "unknown error"}`,
|
|
24941
|
-
affected: [
|
|
25231
|
+
affected: [resolve57(ctx.syntaurRoot, "syntaur.db")],
|
|
24942
25232
|
remediation: {
|
|
24943
25233
|
kind: "manual",
|
|
24944
25234
|
suggestion: "Start the dashboard once (`syntaur dashboard`) to initialize the DB, or restore it from backup",
|
|
@@ -24956,7 +25246,7 @@ var dbReachable = {
|
|
|
24956
25246
|
title: this.title,
|
|
24957
25247
|
status: "error",
|
|
24958
25248
|
detail: 'syntaur.db is missing the expected "sessions" table',
|
|
24959
|
-
affected: [
|
|
25249
|
+
affected: [resolve57(ctx.syntaurRoot, "syntaur.db")],
|
|
24960
25250
|
autoFixable: false
|
|
24961
25251
|
};
|
|
24962
25252
|
}
|
|
@@ -24968,7 +25258,7 @@ var dbReachable = {
|
|
|
24968
25258
|
title: this.title,
|
|
24969
25259
|
status: "error",
|
|
24970
25260
|
detail: `syntaur.db query failed: ${err2 instanceof Error ? err2.message : String(err2)}`,
|
|
24971
|
-
affected: [
|
|
25261
|
+
affected: [resolve57(ctx.syntaurRoot, "syntaur.db")],
|
|
24972
25262
|
autoFixable: false
|
|
24973
25263
|
};
|
|
24974
25264
|
}
|
|
@@ -24994,7 +25284,7 @@ var ghostSessions = {
|
|
|
24994
25284
|
const results = [];
|
|
24995
25285
|
for (const row of rows) {
|
|
24996
25286
|
if (!row.project_slug) continue;
|
|
24997
|
-
const projectPath =
|
|
25287
|
+
const projectPath = resolve57(projectsDir2, row.project_slug, "project.md");
|
|
24998
25288
|
if (!await fileExists(projectPath)) {
|
|
24999
25289
|
results.push({
|
|
25000
25290
|
id: this.id,
|
|
@@ -25013,7 +25303,7 @@ var ghostSessions = {
|
|
|
25013
25303
|
continue;
|
|
25014
25304
|
}
|
|
25015
25305
|
if (row.assignment_slug) {
|
|
25016
|
-
const assignmentPath =
|
|
25306
|
+
const assignmentPath = resolve57(
|
|
25017
25307
|
projectsDir2,
|
|
25018
25308
|
row.project_slug,
|
|
25019
25309
|
"assignments",
|
|
@@ -25065,8 +25355,8 @@ function skipped(check, reason) {
|
|
|
25065
25355
|
|
|
25066
25356
|
// src/utils/doctor/checks/integrations.ts
|
|
25067
25357
|
init_fs();
|
|
25068
|
-
import { resolve as
|
|
25069
|
-
import { readdir as readdir21, readFile as
|
|
25358
|
+
import { resolve as resolve58, dirname as dirname16, basename as basename5 } from "path";
|
|
25359
|
+
import { readdir as readdir21, readFile as readFile34 } from "fs/promises";
|
|
25070
25360
|
import { homedir as homedir8 } from "os";
|
|
25071
25361
|
var CATEGORY6 = "integrations";
|
|
25072
25362
|
var claudePluginLinked = {
|
|
@@ -25148,10 +25438,10 @@ var backupConfigured = {
|
|
|
25148
25438
|
}
|
|
25149
25439
|
};
|
|
25150
25440
|
async function readKnownMarketplaces() {
|
|
25151
|
-
const path =
|
|
25441
|
+
const path = resolve58(homedir8(), ".claude", "plugins", "known_marketplaces.json");
|
|
25152
25442
|
if (!await fileExists(path)) return {};
|
|
25153
25443
|
try {
|
|
25154
|
-
const raw = await
|
|
25444
|
+
const raw = await readFile34(path, "utf-8");
|
|
25155
25445
|
return JSON.parse(raw);
|
|
25156
25446
|
} catch {
|
|
25157
25447
|
return {};
|
|
@@ -25185,7 +25475,7 @@ var claudeMarketplaceRegistered = {
|
|
|
25185
25475
|
};
|
|
25186
25476
|
}
|
|
25187
25477
|
const marketplaceRoot = dirname16(pluginsParent);
|
|
25188
|
-
const marketplaceManifest =
|
|
25478
|
+
const marketplaceManifest = resolve58(marketplaceRoot, ".claude-plugin", "marketplace.json");
|
|
25189
25479
|
if (!await fileExists(marketplaceManifest)) {
|
|
25190
25480
|
return {
|
|
25191
25481
|
id: this.id,
|
|
@@ -25204,7 +25494,7 @@ var claudeMarketplaceRegistered = {
|
|
|
25204
25494
|
}
|
|
25205
25495
|
let parsed = {};
|
|
25206
25496
|
try {
|
|
25207
|
-
parsed = JSON.parse(await
|
|
25497
|
+
parsed = JSON.parse(await readFile34(marketplaceManifest, "utf-8"));
|
|
25208
25498
|
} catch {
|
|
25209
25499
|
return {
|
|
25210
25500
|
id: this.id,
|
|
@@ -25236,7 +25526,7 @@ var claudeMarketplaceRegistered = {
|
|
|
25236
25526
|
title: this.title,
|
|
25237
25527
|
status: "error",
|
|
25238
25528
|
detail: issues.join("; "),
|
|
25239
|
-
affected: [marketplaceManifest,
|
|
25529
|
+
affected: [marketplaceManifest, resolve58(homedir8(), ".claude", "plugins", "known_marketplaces.json")],
|
|
25240
25530
|
remediation: {
|
|
25241
25531
|
kind: "manual",
|
|
25242
25532
|
suggestion: "Re-run install-plugin to ensure both files are in sync.",
|
|
@@ -25276,8 +25566,8 @@ function skipped2(check, reason) {
|
|
|
25276
25566
|
init_fs();
|
|
25277
25567
|
init_parser();
|
|
25278
25568
|
init_types();
|
|
25279
|
-
import { resolve as
|
|
25280
|
-
import { readFile as
|
|
25569
|
+
import { resolve as resolve59 } from "path";
|
|
25570
|
+
import { readFile as readFile35 } from "fs/promises";
|
|
25281
25571
|
var CATEGORY7 = "workspace";
|
|
25282
25572
|
var ASSIGNMENT_FIELDS = ["projectSlug", "assignmentSlug", "projectDir", "assignmentDir"];
|
|
25283
25573
|
var BUNDLE_FIELDS = ["bundleId", "bundleScope", "bundleScopeId"];
|
|
@@ -25298,12 +25588,12 @@ function isStandaloneSession(ctx) {
|
|
|
25298
25588
|
return !hasAnyAssignmentField(ctx) && !hasAnyBundleField(ctx) && typeof ctx.sessionId === "string" && ctx.sessionId.length > 0;
|
|
25299
25589
|
}
|
|
25300
25590
|
async function loadContext(ctx) {
|
|
25301
|
-
const path =
|
|
25591
|
+
const path = resolve59(ctx.cwd, ".syntaur", "context.json");
|
|
25302
25592
|
if (!await fileExists(path)) {
|
|
25303
25593
|
return { data: null, path, exists: false, parseError: null };
|
|
25304
25594
|
}
|
|
25305
25595
|
try {
|
|
25306
|
-
const raw = await
|
|
25596
|
+
const raw = await readFile35(path, "utf-8");
|
|
25307
25597
|
return { data: JSON.parse(raw), path, exists: true, parseError: null };
|
|
25308
25598
|
} catch (err2) {
|
|
25309
25599
|
return {
|
|
@@ -25397,7 +25687,7 @@ var contextAssignmentResolves = {
|
|
|
25397
25687
|
if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to resolve");
|
|
25398
25688
|
if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to resolve");
|
|
25399
25689
|
if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
|
|
25400
|
-
const assignmentMd =
|
|
25690
|
+
const assignmentMd = resolve59(data.assignmentDir, "assignment.md");
|
|
25401
25691
|
if (!await fileExists(assignmentMd)) {
|
|
25402
25692
|
return {
|
|
25403
25693
|
id: this.id,
|
|
@@ -25427,10 +25717,10 @@ var contextTerminal = {
|
|
|
25427
25717
|
if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to check");
|
|
25428
25718
|
if (isBundleContext(data)) return skipped3(this, "bundle context \u2014 no assignment to check");
|
|
25429
25719
|
if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
|
|
25430
|
-
const assignmentMd =
|
|
25720
|
+
const assignmentMd = resolve59(data.assignmentDir, "assignment.md");
|
|
25431
25721
|
if (!await fileExists(assignmentMd)) return skipped3(this, "assignment file missing");
|
|
25432
25722
|
try {
|
|
25433
|
-
const content = await
|
|
25723
|
+
const content = await readFile35(assignmentMd, "utf-8");
|
|
25434
25724
|
const parsed = parseAssignmentFull(content);
|
|
25435
25725
|
const terminal = terminalStatuses2(ctx);
|
|
25436
25726
|
if (terminal.has(parsed.status)) {
|
|
@@ -25576,15 +25866,15 @@ var agentChecks = [agentsResolvable];
|
|
|
25576
25866
|
// src/utils/doctor/checks/terminal.ts
|
|
25577
25867
|
init_config2();
|
|
25578
25868
|
import { spawnSync as spawnSync8 } from "child_process";
|
|
25579
|
-
import { readFile as
|
|
25580
|
-
import { resolve as
|
|
25869
|
+
import { readFile as readFile36 } from "fs/promises";
|
|
25870
|
+
import { resolve as resolve60 } from "path";
|
|
25581
25871
|
init_paths();
|
|
25582
25872
|
init_fs();
|
|
25583
25873
|
var CATEGORY9 = "terminal";
|
|
25584
25874
|
async function readRawTerminalKey() {
|
|
25585
|
-
const configPath =
|
|
25875
|
+
const configPath = resolve60(syntaurRoot(), "config.md");
|
|
25586
25876
|
if (!await fileExists(configPath)) return null;
|
|
25587
|
-
const content = await
|
|
25877
|
+
const content = await readFile36(configPath, "utf-8");
|
|
25588
25878
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
25589
25879
|
if (!fmMatch) return null;
|
|
25590
25880
|
const line = fmMatch[1].split("\n").find((l) => /^terminal:\s*/.test(l));
|
|
@@ -25734,13 +26024,13 @@ var terminalChecks = [
|
|
|
25734
26024
|
|
|
25735
26025
|
// src/utils/doctor/checks/skills.ts
|
|
25736
26026
|
init_fs();
|
|
25737
|
-
import { resolve as
|
|
25738
|
-
import { readdir as readdir22, readFile as
|
|
26027
|
+
import { resolve as resolve61, join as join8 } from "path";
|
|
26028
|
+
import { readdir as readdir22, readFile as readFile37, lstat as lstat4 } from "fs/promises";
|
|
25739
26029
|
import { homedir as homedir9 } from "os";
|
|
25740
26030
|
var CATEGORY10 = "skills";
|
|
25741
26031
|
var skillTargets = [
|
|
25742
|
-
{ agent: "claude", dir:
|
|
25743
|
-
{ agent: "codex", dir:
|
|
26032
|
+
{ agent: "claude", dir: resolve61(homedir9(), ".claude", "skills"), label: "~/.claude/skills" },
|
|
26033
|
+
{ agent: "codex", dir: resolve61(homedir9(), ".codex", "skills"), label: "~/.codex/skills" }
|
|
25744
26034
|
];
|
|
25745
26035
|
var skillsDedupCheck = {
|
|
25746
26036
|
id: "skills.dedup",
|
|
@@ -25764,7 +26054,7 @@ var skillsDedupCheck = {
|
|
|
25764
26054
|
if (!KNOWN_SKILLS.includes(entry.name)) continue;
|
|
25765
26055
|
const skillMd = join8(dir, entry.name, "SKILL.md");
|
|
25766
26056
|
if (!await fileExists(skillMd)) continue;
|
|
25767
|
-
const content = await
|
|
26057
|
+
const content = await readFile37(skillMd, "utf-8").catch(() => "");
|
|
25768
26058
|
const match = content.match(/^name:\s*(\S+)\s*$/m);
|
|
25769
26059
|
if (!match || match[1] !== entry.name) continue;
|
|
25770
26060
|
let isSymlink2 = false;
|
|
@@ -25815,7 +26105,7 @@ var skillsChecks = [skillsDedupCheck];
|
|
|
25815
26105
|
// src/utils/doctor/checks/bundles.ts
|
|
25816
26106
|
init_fs();
|
|
25817
26107
|
init_paths();
|
|
25818
|
-
import { resolve as
|
|
26108
|
+
import { resolve as resolve62 } from "path";
|
|
25819
26109
|
import { readdir as readdir23 } from "fs/promises";
|
|
25820
26110
|
import { spawnSync as spawnSync9 } from "child_process";
|
|
25821
26111
|
init_parser2();
|
|
@@ -25845,7 +26135,7 @@ async function listScopes(ctx) {
|
|
|
25845
26135
|
if (!e.isDirectory()) continue;
|
|
25846
26136
|
const slug = e.name;
|
|
25847
26137
|
if (typeof slug !== "string" || slug.startsWith(".")) continue;
|
|
25848
|
-
const projectMd =
|
|
26138
|
+
const projectMd = resolve62(ctx.config.defaultProjectDir, slug, "project.md");
|
|
25849
26139
|
if (!await fileExists(projectMd)) continue;
|
|
25850
26140
|
out.push({
|
|
25851
26141
|
scopeLabel: `project:${slug}`,
|
|
@@ -26136,7 +26426,7 @@ async function readVersion() {
|
|
|
26136
26426
|
let dir = dirname17(here);
|
|
26137
26427
|
for (let i = 0; i < 6; i++) {
|
|
26138
26428
|
try {
|
|
26139
|
-
const raw = await
|
|
26429
|
+
const raw = await readFile38(join9(dir, "package.json"), "utf-8");
|
|
26140
26430
|
const parsed = JSON.parse(raw);
|
|
26141
26431
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
26142
26432
|
} catch {
|
|
@@ -26233,7 +26523,7 @@ var REQUIRED_WORKSPACE_FIELDS = [
|
|
|
26233
26523
|
];
|
|
26234
26524
|
var ISO_DATE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
|
|
26235
26525
|
async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
|
|
26236
|
-
const absolute = isAbsolute10(inputPath) ? inputPath :
|
|
26526
|
+
const absolute = isAbsolute10(inputPath) ? inputPath : resolve63(cwd, inputPath);
|
|
26237
26527
|
const errors = [];
|
|
26238
26528
|
const warnings = [];
|
|
26239
26529
|
if (!await fileExists(absolute)) {
|
|
@@ -26246,7 +26536,7 @@ async function validateAssignmentFile(inputPath, cwd = process.cwd()) {
|
|
|
26246
26536
|
}
|
|
26247
26537
|
let content;
|
|
26248
26538
|
try {
|
|
26249
|
-
content = await
|
|
26539
|
+
content = await readFile39(absolute, "utf-8");
|
|
26250
26540
|
} catch (err2) {
|
|
26251
26541
|
return {
|
|
26252
26542
|
ok: false,
|
|
@@ -26570,8 +26860,8 @@ init_uuid();
|
|
|
26570
26860
|
init_timestamp();
|
|
26571
26861
|
init_assignment_resolver();
|
|
26572
26862
|
init_templates();
|
|
26573
|
-
import { resolve as
|
|
26574
|
-
import { readFile as
|
|
26863
|
+
import { resolve as resolve64 } from "path";
|
|
26864
|
+
import { readFile as readFile40 } from "fs/promises";
|
|
26575
26865
|
function shortId() {
|
|
26576
26866
|
return generateId().split("-")[0];
|
|
26577
26867
|
}
|
|
@@ -26601,7 +26891,7 @@ async function commentCommand(target, text, options = {}) {
|
|
|
26601
26891
|
if (!isValidSlug(target)) {
|
|
26602
26892
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
26603
26893
|
}
|
|
26604
|
-
assignmentDir =
|
|
26894
|
+
assignmentDir = resolve64(baseDir, options.project, "assignments", target);
|
|
26605
26895
|
assignmentRef = target;
|
|
26606
26896
|
} else {
|
|
26607
26897
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
|
|
@@ -26611,13 +26901,13 @@ async function commentCommand(target, text, options = {}) {
|
|
|
26611
26901
|
assignmentDir = resolved.assignmentDir;
|
|
26612
26902
|
assignmentRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
|
|
26613
26903
|
}
|
|
26614
|
-
const commentsPath =
|
|
26904
|
+
const commentsPath = resolve64(assignmentDir, "comments.md");
|
|
26615
26905
|
const timestamp = nowTimestamp();
|
|
26616
26906
|
const author = options.author ?? process.env.USER ?? "unknown";
|
|
26617
26907
|
let currentContent;
|
|
26618
26908
|
let currentCount = 0;
|
|
26619
26909
|
if (await fileExists(commentsPath)) {
|
|
26620
|
-
currentContent = await
|
|
26910
|
+
currentContent = await readFile40(commentsPath, "utf-8");
|
|
26621
26911
|
const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
|
|
26622
26912
|
if (countMatch) currentCount = parseInt(countMatch[1], 10);
|
|
26623
26913
|
} else {
|
|
@@ -26651,8 +26941,8 @@ ${entry}`;
|
|
|
26651
26941
|
}
|
|
26652
26942
|
|
|
26653
26943
|
// src/commands/capture.ts
|
|
26654
|
-
import { resolve as
|
|
26655
|
-
import { copyFile as copyFile3, mkdir as mkdir9, realpath as realpath2, rm as rm12, stat as stat8, writeFile as
|
|
26944
|
+
import { resolve as resolve68, relative as relative4, dirname as dirname18 } from "path";
|
|
26945
|
+
import { copyFile as copyFile3, mkdir as mkdir9, realpath as realpath2, rm as rm12, stat as stat8, writeFile as writeFile14 } from "fs/promises";
|
|
26656
26946
|
import { existsSync as existsSync5 } from "fs";
|
|
26657
26947
|
|
|
26658
26948
|
// src/utils/assignment-target.ts
|
|
@@ -26662,8 +26952,8 @@ init_config2();
|
|
|
26662
26952
|
init_slug();
|
|
26663
26953
|
init_assignment_resolver();
|
|
26664
26954
|
init_parser();
|
|
26665
|
-
import { resolve as
|
|
26666
|
-
import { readFile as
|
|
26955
|
+
import { resolve as resolve65 } from "path";
|
|
26956
|
+
import { readFile as readFile41 } from "fs/promises";
|
|
26667
26957
|
var AssignmentTargetError = class extends Error {
|
|
26668
26958
|
};
|
|
26669
26959
|
function classifyContext(ctx) {
|
|
@@ -26675,10 +26965,10 @@ function classifyContext(ctx) {
|
|
|
26675
26965
|
return "empty";
|
|
26676
26966
|
}
|
|
26677
26967
|
async function readAssignmentFrontmatterId(assignmentDir) {
|
|
26678
|
-
const path =
|
|
26968
|
+
const path = resolve65(assignmentDir, "assignment.md");
|
|
26679
26969
|
if (!await fileExists(path)) return null;
|
|
26680
26970
|
try {
|
|
26681
|
-
const content = await
|
|
26971
|
+
const content = await readFile41(path, "utf-8");
|
|
26682
26972
|
const [fm] = extractFrontmatter(content);
|
|
26683
26973
|
return getField(fm, "id");
|
|
26684
26974
|
} catch {
|
|
@@ -26686,10 +26976,10 @@ async function readAssignmentFrontmatterId(assignmentDir) {
|
|
|
26686
26976
|
}
|
|
26687
26977
|
}
|
|
26688
26978
|
async function readContextJson(cwd) {
|
|
26689
|
-
const path =
|
|
26979
|
+
const path = resolve65(cwd, ".syntaur", "context.json");
|
|
26690
26980
|
if (!await fileExists(path)) return null;
|
|
26691
26981
|
try {
|
|
26692
|
-
const raw = await
|
|
26982
|
+
const raw = await readFile41(path, "utf-8");
|
|
26693
26983
|
return JSON.parse(raw);
|
|
26694
26984
|
} catch {
|
|
26695
26985
|
return null;
|
|
@@ -26710,15 +27000,15 @@ async function resolveAssignmentTarget(input4, opts = {}) {
|
|
|
26710
27000
|
if (!isValidSlug(input4)) {
|
|
26711
27001
|
throw new AssignmentTargetError(`Invalid assignment slug "${input4}".`);
|
|
26712
27002
|
}
|
|
26713
|
-
const projectDir =
|
|
26714
|
-
const projectMdPath =
|
|
27003
|
+
const projectDir = resolve65(baseDir, opts.project);
|
|
27004
|
+
const projectMdPath = resolve65(projectDir, "project.md");
|
|
26715
27005
|
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
26716
27006
|
throw new AssignmentTargetError(
|
|
26717
27007
|
`Project "${opts.project}" not found at ${projectDir}.`
|
|
26718
27008
|
);
|
|
26719
27009
|
}
|
|
26720
|
-
const assignmentDir =
|
|
26721
|
-
const assignmentMdPath2 =
|
|
27010
|
+
const assignmentDir = resolve65(projectDir, "assignments", input4);
|
|
27011
|
+
const assignmentMdPath2 = resolve65(assignmentDir, "assignment.md");
|
|
26722
27012
|
if (!await fileExists(assignmentMdPath2)) {
|
|
26723
27013
|
throw new AssignmentTargetError(
|
|
26724
27014
|
`Assignment "${input4}" not found in project "${opts.project}".`
|
|
@@ -26757,7 +27047,7 @@ async function resolveAssignmentTarget(input4, opts = {}) {
|
|
|
26757
27047
|
}
|
|
26758
27048
|
if (ctx.assignmentDir) {
|
|
26759
27049
|
const dir = expandHome(ctx.assignmentDir);
|
|
26760
|
-
const assignmentMdPath2 =
|
|
27050
|
+
const assignmentMdPath2 = resolve65(dir, "assignment.md");
|
|
26761
27051
|
if (!await fileExists(assignmentMdPath2)) {
|
|
26762
27052
|
throw new AssignmentTargetError(
|
|
26763
27053
|
`.syntaur/context.json points to a missing assignment dir: ${dir}.`
|
|
@@ -26786,8 +27076,8 @@ async function resolveAssignmentTarget(input4, opts = {}) {
|
|
|
26786
27076
|
`.syntaur/context.json contains invalid slugs: project="${ctx.projectSlug}" assignment="${ctx.assignmentSlug}".`
|
|
26787
27077
|
);
|
|
26788
27078
|
}
|
|
26789
|
-
const assignmentDir =
|
|
26790
|
-
const assignmentMdPath2 =
|
|
27079
|
+
const assignmentDir = resolve65(baseDir, ctx.projectSlug, "assignments", ctx.assignmentSlug);
|
|
27080
|
+
const assignmentMdPath2 = resolve65(assignmentDir, "assignment.md");
|
|
26791
27081
|
if (!await fileExists(assignmentMdPath2)) {
|
|
26792
27082
|
throw new AssignmentTargetError(
|
|
26793
27083
|
`.syntaur/context.json points to a missing assignment: ${assignmentDir}.`
|
|
@@ -26922,7 +27212,7 @@ async function captureScreenshot(mode) {
|
|
|
26922
27212
|
|
|
26923
27213
|
// src/utils/asciinema.ts
|
|
26924
27214
|
import { spawn as spawn6 } from "child_process";
|
|
26925
|
-
import { mkdtemp as mkdtemp3, readFile as
|
|
27215
|
+
import { mkdtemp as mkdtemp3, readFile as readFile42, rm as rm9 } from "fs/promises";
|
|
26926
27216
|
import { tmpdir as tmpdir4 } from "os";
|
|
26927
27217
|
import { join as join11 } from "path";
|
|
26928
27218
|
var SAFE_RE = /^[A-Za-z0-9_@%+=:,./-]+$/;
|
|
@@ -26985,7 +27275,7 @@ async function captureAsciinema(opts) {
|
|
|
26985
27275
|
}
|
|
26986
27276
|
throw err2;
|
|
26987
27277
|
}
|
|
26988
|
-
const text = await
|
|
27278
|
+
const text = await readFile42(castPath, "utf8").catch(() => null);
|
|
26989
27279
|
if (text === null) {
|
|
26990
27280
|
throw new Error(
|
|
26991
27281
|
`asciinema produced no cast file at ${castPath} (exit ${exitCode}). Try running 'asciinema rec ${castPath}' directly to diagnose.`
|
|
@@ -27013,9 +27303,9 @@ async function captureAsciinema(opts) {
|
|
|
27013
27303
|
// src/utils/recording.ts
|
|
27014
27304
|
init_paths();
|
|
27015
27305
|
import { spawn as spawn7 } from "child_process";
|
|
27016
|
-
import { mkdir as mkdir8, mkdtemp as mkdtemp4, open as open3, readFile as
|
|
27306
|
+
import { mkdir as mkdir8, mkdtemp as mkdtemp4, open as open3, readFile as readFile43, rm as rm10, stat as stat7, unlink as unlink9, writeFile as writeFile13 } from "fs/promises";
|
|
27017
27307
|
import { tmpdir as tmpdir5 } from "os";
|
|
27018
|
-
import { join as join12, resolve as
|
|
27308
|
+
import { join as join12, resolve as resolve66 } from "path";
|
|
27019
27309
|
import { setTimeout as sleep } from "timers/promises";
|
|
27020
27310
|
function sigintPollIntervalMs() {
|
|
27021
27311
|
const raw = process.env.SYNTAUR_RECORDING_POLL_INTERVAL_MS;
|
|
@@ -27036,13 +27326,13 @@ function sigtermWaitMs() {
|
|
|
27036
27326
|
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 1e3;
|
|
27037
27327
|
}
|
|
27038
27328
|
function pidfilePath() {
|
|
27039
|
-
return
|
|
27329
|
+
return resolve66(syntaurRoot(), "recording.pid");
|
|
27040
27330
|
}
|
|
27041
27331
|
function logPath2() {
|
|
27042
|
-
return
|
|
27332
|
+
return resolve66(syntaurRoot(), "recording.log");
|
|
27043
27333
|
}
|
|
27044
27334
|
function sidecarPath() {
|
|
27045
|
-
return
|
|
27335
|
+
return resolve66(syntaurRoot(), "recording.json");
|
|
27046
27336
|
}
|
|
27047
27337
|
function ffmpegArgs(device, fps, mp4Path) {
|
|
27048
27338
|
return [
|
|
@@ -27087,7 +27377,7 @@ async function acquirePidfile(pidfile) {
|
|
|
27087
27377
|
} catch (err2) {
|
|
27088
27378
|
if (err2.code !== "EEXIST") throw err2;
|
|
27089
27379
|
if (attempt === 1) throw err2;
|
|
27090
|
-
const existing = (await
|
|
27380
|
+
const existing = (await readFile43(pidfile, "utf-8").catch(() => "")).trim();
|
|
27091
27381
|
if (existing.startsWith(STARTING_SENTINEL_PREFIX)) {
|
|
27092
27382
|
const parentPidRaw = existing.slice(STARTING_SENTINEL_PREFIX.length);
|
|
27093
27383
|
const parentPid = Number.parseInt(parentPidRaw, 10);
|
|
@@ -27184,12 +27474,12 @@ async function startRecording(input4) {
|
|
|
27184
27474
|
const pid = await pidReady;
|
|
27185
27475
|
acquiredPid = pid;
|
|
27186
27476
|
child.unref();
|
|
27187
|
-
await
|
|
27477
|
+
await writeFile13(pidfile, String(pid));
|
|
27188
27478
|
await logHandle.close();
|
|
27189
27479
|
logHandle = null;
|
|
27190
27480
|
if (warmupMs > 0) await sleep(warmupMs);
|
|
27191
27481
|
if (!await isProcessAlive(pid)) {
|
|
27192
|
-
const tail = await
|
|
27482
|
+
const tail = await readFile43(log, "utf-8").then((s) => s.split("\n").slice(-20).join("\n")).catch(() => "");
|
|
27193
27483
|
acquiredPid = null;
|
|
27194
27484
|
throw new Error(
|
|
27195
27485
|
`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}
|
|
@@ -27212,7 +27502,7 @@ ${tail}`
|
|
|
27212
27502
|
device: input4.device,
|
|
27213
27503
|
fps: input4.fps
|
|
27214
27504
|
};
|
|
27215
|
-
await
|
|
27505
|
+
await writeFile13(sidecar, JSON.stringify(sidecarData, null, 2));
|
|
27216
27506
|
return {
|
|
27217
27507
|
pid,
|
|
27218
27508
|
logPath: log,
|
|
@@ -27246,7 +27536,7 @@ ${tail}`
|
|
|
27246
27536
|
async function stopRecording() {
|
|
27247
27537
|
const pidfile = pidfilePath();
|
|
27248
27538
|
const sidecar = sidecarPath();
|
|
27249
|
-
const pidRaw = await
|
|
27539
|
+
const pidRaw = await readFile43(pidfile, "utf-8").catch(() => null);
|
|
27250
27540
|
if (pidRaw === null) {
|
|
27251
27541
|
throw new Error(
|
|
27252
27542
|
`No active recording found (no pidfile at ${pidfile}). Did you run --start?`
|
|
@@ -27256,7 +27546,7 @@ async function stopRecording() {
|
|
|
27256
27546
|
if (!Number.isInteger(pid) || pid <= 0) {
|
|
27257
27547
|
throw new Error(`Pidfile at ${pidfile} is corrupt (got "${pidRaw}").`);
|
|
27258
27548
|
}
|
|
27259
|
-
const sidecarRaw = await
|
|
27549
|
+
const sidecarRaw = await readFile43(sidecar, "utf-8").catch(() => null);
|
|
27260
27550
|
if (sidecarRaw === null) {
|
|
27261
27551
|
throw new Error(
|
|
27262
27552
|
`No recording sidecar at ${sidecar}. The recording state is inconsistent \u2014 delete ${pidfile} and re-run --start.`
|
|
@@ -27321,7 +27611,7 @@ async function stopRecording() {
|
|
|
27321
27611
|
// src/db/proof-db.ts
|
|
27322
27612
|
init_paths();
|
|
27323
27613
|
import Database5 from "better-sqlite3";
|
|
27324
|
-
import { resolve as
|
|
27614
|
+
import { resolve as resolve67 } from "path";
|
|
27325
27615
|
var db4 = null;
|
|
27326
27616
|
var PROOF_SCHEMA_VERSION = "1";
|
|
27327
27617
|
var SCHEMA_SQL4 = `
|
|
@@ -27341,7 +27631,7 @@ CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);
|
|
|
27341
27631
|
`;
|
|
27342
27632
|
function initProofDb(dbPath) {
|
|
27343
27633
|
if (db4) return db4;
|
|
27344
|
-
const finalPath = dbPath ??
|
|
27634
|
+
const finalPath = dbPath ?? resolve67(syntaurRoot(), "syntaur.db");
|
|
27345
27635
|
db4 = new Database5(finalPath);
|
|
27346
27636
|
db4.pragma("journal_mode = WAL");
|
|
27347
27637
|
db4.exec(SCHEMA_SQL4);
|
|
@@ -27389,7 +27679,7 @@ function listArtifactsByAssignment(assignmentId) {
|
|
|
27389
27679
|
|
|
27390
27680
|
// src/utils/transcribers/elevenlabs.ts
|
|
27391
27681
|
import { spawn as spawn8 } from "child_process";
|
|
27392
|
-
import { mkdtemp as mkdtemp5, readFile as
|
|
27682
|
+
import { mkdtemp as mkdtemp5, readFile as readFile44, rm as rm11 } from "fs/promises";
|
|
27393
27683
|
import { tmpdir as tmpdir6 } from "os";
|
|
27394
27684
|
import { join as join13 } from "path";
|
|
27395
27685
|
var SCRIBE_URL = "https://api.elevenlabs.io/v1/speech-to-text";
|
|
@@ -27453,7 +27743,7 @@ async function extractAudio(videoAbsPath, wavOut) {
|
|
|
27453
27743
|
throw new TranscribeFfmpegError(`ffmpeg failed (exit ${result.code}): ${tail}`);
|
|
27454
27744
|
}
|
|
27455
27745
|
async function callScribe(wavPath, apiKey, opts) {
|
|
27456
|
-
const audio = await
|
|
27746
|
+
const audio = await readFile44(wavPath);
|
|
27457
27747
|
const form = new FormData();
|
|
27458
27748
|
form.set("file", new Blob([new Uint8Array(audio)], { type: "audio/wav" }), "audio.wav");
|
|
27459
27749
|
form.set("model_id", "scribe_v1");
|
|
@@ -27769,7 +28059,7 @@ async function captureCommand(target, options = {}) {
|
|
|
27769
28059
|
});
|
|
27770
28060
|
}
|
|
27771
28061
|
if (options.file) {
|
|
27772
|
-
const expanded = options.file.startsWith("~/") ?
|
|
28062
|
+
const expanded = options.file.startsWith("~/") ? resolve68(process.env.HOME ?? "", options.file.slice(2)) : resolve68(options.file);
|
|
27773
28063
|
if (!await fileExists(expanded)) {
|
|
27774
28064
|
throw new Error(`--file does not exist: ${options.file}`);
|
|
27775
28065
|
}
|
|
@@ -27810,7 +28100,7 @@ async function captureCommand(target, options = {}) {
|
|
|
27810
28100
|
}
|
|
27811
28101
|
initProofDb();
|
|
27812
28102
|
const subdir = criterionIndex === null ? "untagged" : String(criterionIndex);
|
|
27813
|
-
const destDir =
|
|
28103
|
+
const destDir = resolve68(proofDir(resolved.assignmentDir), subdir);
|
|
27814
28104
|
if (resolvedSource) await mkdir9(destDir, { recursive: true });
|
|
27815
28105
|
const ext = resolvedSource ? extensionForKind(kind) : null;
|
|
27816
28106
|
let id = null;
|
|
@@ -27819,7 +28109,7 @@ async function captureCommand(target, options = {}) {
|
|
|
27819
28109
|
let lastErr = null;
|
|
27820
28110
|
for (let attempt = 0; attempt < MAX_ID_RETRIES; attempt += 1) {
|
|
27821
28111
|
const candidate = generateArtifactId();
|
|
27822
|
-
const candidateAbsPath = resolvedSource && ext ?
|
|
28112
|
+
const candidateAbsPath = resolvedSource && ext ? resolve68(destDir, `${candidate}.${ext}`) : null;
|
|
27823
28113
|
const candidateRel = candidateAbsPath ? relative4(resolved.assignmentDir, candidateAbsPath) : null;
|
|
27824
28114
|
try {
|
|
27825
28115
|
insertArtifact({
|
|
@@ -27863,7 +28153,7 @@ async function captureCommand(target, options = {}) {
|
|
|
27863
28153
|
}
|
|
27864
28154
|
}
|
|
27865
28155
|
if (options.transcribe && kind === "video" && absPath && id) {
|
|
27866
|
-
const sidecarPath2 =
|
|
28156
|
+
const sidecarPath2 = resolve68(destDir, `${id}.transcript.md`);
|
|
27867
28157
|
if (existsSync5(sidecarPath2)) {
|
|
27868
28158
|
console.warn(
|
|
27869
28159
|
`transcript: ${sidecarPath2} already exists, skipping (delete to re-transcribe)`
|
|
@@ -27872,7 +28162,7 @@ async function captureCommand(target, options = {}) {
|
|
|
27872
28162
|
try {
|
|
27873
28163
|
const json = await getTranscriber().transcribe(absPath);
|
|
27874
28164
|
const md = renderMarkdown(groupIntoPhrases(json.words ?? []));
|
|
27875
|
-
await
|
|
28165
|
+
await writeFile14(sidecarPath2, md, "utf8");
|
|
27876
28166
|
console.log(`transcript: ${sidecarPath2}`);
|
|
27877
28167
|
} catch (err2) {
|
|
27878
28168
|
if (err2 instanceof TranscribeFfmpegMissingError) {
|
|
@@ -27895,7 +28185,7 @@ async function captureCommand(target, options = {}) {
|
|
|
27895
28185
|
const tagSuffix = criterionIndex === null ? "untagged" : `criterion ${criterionIndex}`;
|
|
27896
28186
|
console.log(`Captured artifact ${id} (${kind}) for ${ref} \u2014 ${tagSuffix}.`);
|
|
27897
28187
|
if (relativeFilePath) {
|
|
27898
|
-
console.log(` file: ${
|
|
28188
|
+
console.log(` file: ${resolve68(resolved.assignmentDir, relativeFilePath)}`);
|
|
27899
28189
|
}
|
|
27900
28190
|
} catch (err2) {
|
|
27901
28191
|
if (options.stop && kind === "video" && shelloutCleanup && resolvedSource) {
|
|
@@ -27918,8 +28208,8 @@ async function captureCommand(target, options = {}) {
|
|
|
27918
28208
|
|
|
27919
28209
|
// src/commands/proof.ts
|
|
27920
28210
|
import { Command as Command6 } from "commander";
|
|
27921
|
-
import { readFile as
|
|
27922
|
-
import { resolve as
|
|
28211
|
+
import { readFile as readFile45, writeFile as writeFile15, rename as rename9, stat as stat9 } from "fs/promises";
|
|
28212
|
+
import { resolve as resolve69, relative as relative5, isAbsolute as isAbsolute11, dirname as dirname19 } from "path";
|
|
27923
28213
|
import { randomBytes as randomBytes4 } from "crypto";
|
|
27924
28214
|
|
|
27925
28215
|
// src/utils/acceptance-criteria-parse.ts
|
|
@@ -28159,11 +28449,11 @@ function renderProofHtml(params2, inlineFiles = /* @__PURE__ */ new Map(), trans
|
|
|
28159
28449
|
|
|
28160
28450
|
// src/commands/proof.ts
|
|
28161
28451
|
async function readAssignmentMeta(assignmentDir) {
|
|
28162
|
-
const path =
|
|
28452
|
+
const path = resolve69(assignmentDir, "assignment.md");
|
|
28163
28453
|
if (!await fileExists(path)) {
|
|
28164
28454
|
return { title: "", body: "" };
|
|
28165
28455
|
}
|
|
28166
|
-
const content = await
|
|
28456
|
+
const content = await readFile45(path, "utf-8");
|
|
28167
28457
|
const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?/);
|
|
28168
28458
|
let title = "";
|
|
28169
28459
|
if (fmMatch) {
|
|
@@ -28177,7 +28467,7 @@ async function readAssignmentMeta(assignmentDir) {
|
|
|
28177
28467
|
}
|
|
28178
28468
|
async function atomicWrite(destPath, content) {
|
|
28179
28469
|
const tmp = `${destPath}.${randomBytes4(4).toString("hex")}.tmp`;
|
|
28180
|
-
await
|
|
28470
|
+
await writeFile15(tmp, content, "utf-8");
|
|
28181
28471
|
await rename9(tmp, destPath);
|
|
28182
28472
|
}
|
|
28183
28473
|
function isWithin(root, target) {
|
|
@@ -28210,7 +28500,7 @@ async function loadInlineFiles(rows, assignmentDir) {
|
|
|
28210
28500
|
for (const r of rows) {
|
|
28211
28501
|
if (!r.file_path) continue;
|
|
28212
28502
|
if (r.kind !== "http" && r.kind !== "text") continue;
|
|
28213
|
-
const abs =
|
|
28503
|
+
const abs = resolve69(assignmentDir, r.file_path);
|
|
28214
28504
|
if (!isWithin(proofRoot, abs)) {
|
|
28215
28505
|
out.set(r.file_path, null);
|
|
28216
28506
|
continue;
|
|
@@ -28225,7 +28515,7 @@ async function loadInlineFiles(rows, assignmentDir) {
|
|
|
28225
28515
|
continue;
|
|
28226
28516
|
}
|
|
28227
28517
|
try {
|
|
28228
|
-
out.set(r.file_path, await
|
|
28518
|
+
out.set(r.file_path, await readFile45(abs, "utf-8"));
|
|
28229
28519
|
} catch {
|
|
28230
28520
|
out.set(r.file_path, null);
|
|
28231
28521
|
}
|
|
@@ -28237,14 +28527,14 @@ async function loadTranscriptSidecars(rows, assignmentDir) {
|
|
|
28237
28527
|
const proofRoot = proofDir(assignmentDir);
|
|
28238
28528
|
for (const r of rows) {
|
|
28239
28529
|
if (r.kind !== "video" || !r.file_path) continue;
|
|
28240
|
-
const videoAbs =
|
|
28241
|
-
const sidecar =
|
|
28530
|
+
const videoAbs = resolve69(assignmentDir, r.file_path);
|
|
28531
|
+
const sidecar = resolve69(dirname19(videoAbs), `${r.id}.transcript.md`);
|
|
28242
28532
|
if (!isWithin(proofRoot, sidecar)) continue;
|
|
28243
28533
|
if (!await fileExists(sidecar)) continue;
|
|
28244
28534
|
const st = await stat9(sidecar);
|
|
28245
28535
|
if (st.size > INLINE_TEXT_LIMIT_BYTES) continue;
|
|
28246
28536
|
try {
|
|
28247
|
-
out.set(r.id, await
|
|
28537
|
+
out.set(r.id, await readFile45(sidecar, "utf-8"));
|
|
28248
28538
|
} catch {
|
|
28249
28539
|
}
|
|
28250
28540
|
}
|
|
@@ -28278,8 +28568,8 @@ async function proofBuildCommand(target, options = {}) {
|
|
|
28278
28568
|
};
|
|
28279
28569
|
const md = renderProofMarkdown(renderParams);
|
|
28280
28570
|
const html = renderProofHtml(renderParams, inlineFiles, transcriptSidecars);
|
|
28281
|
-
const mdPath =
|
|
28282
|
-
const htmlPath =
|
|
28571
|
+
const mdPath = resolve69(resolved.assignmentDir, "proof.md");
|
|
28572
|
+
const htmlPath = resolve69(resolved.assignmentDir, "proof.html");
|
|
28283
28573
|
await atomicWrite(mdPath, md);
|
|
28284
28574
|
await atomicWrite(htmlPath, html);
|
|
28285
28575
|
console.log(`Wrote ${htmlPath}`);
|
|
@@ -28944,7 +29234,7 @@ async function runCcusage(opts = {}) {
|
|
|
28944
29234
|
};
|
|
28945
29235
|
}
|
|
28946
29236
|
function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
|
|
28947
|
-
return new Promise((
|
|
29237
|
+
return new Promise((resolve79) => {
|
|
28948
29238
|
const child = spawn9(binary, args, {
|
|
28949
29239
|
env: env ?? process.env,
|
|
28950
29240
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -28958,7 +29248,7 @@ function runOnce(binary, args, env, timeoutMs, maxOutputBytes) {
|
|
|
28958
29248
|
if (settled) return;
|
|
28959
29249
|
settled = true;
|
|
28960
29250
|
clearTimeout(timer2);
|
|
28961
|
-
|
|
29251
|
+
resolve79(result);
|
|
28962
29252
|
};
|
|
28963
29253
|
const timer2 = setTimeout(() => {
|
|
28964
29254
|
timedOut = true;
|
|
@@ -29480,8 +29770,8 @@ init_slug();
|
|
|
29480
29770
|
init_timestamp();
|
|
29481
29771
|
init_assignment_resolver();
|
|
29482
29772
|
init_assignment_todos();
|
|
29483
|
-
import { resolve as
|
|
29484
|
-
import { readFile as
|
|
29773
|
+
import { resolve as resolve70 } from "path";
|
|
29774
|
+
import { readFile as readFile46 } from "fs/promises";
|
|
29485
29775
|
async function requestCommand(target, text, options = {}) {
|
|
29486
29776
|
if (!text || !text.trim()) {
|
|
29487
29777
|
throw new Error("Request text cannot be empty.");
|
|
@@ -29497,7 +29787,7 @@ async function requestCommand(target, text, options = {}) {
|
|
|
29497
29787
|
if (!isValidSlug(target)) {
|
|
29498
29788
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
29499
29789
|
}
|
|
29500
|
-
assignmentDir =
|
|
29790
|
+
assignmentDir = resolve70(baseDir, options.project, "assignments", target);
|
|
29501
29791
|
targetRef = target;
|
|
29502
29792
|
} else {
|
|
29503
29793
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
|
|
@@ -29507,12 +29797,12 @@ async function requestCommand(target, text, options = {}) {
|
|
|
29507
29797
|
assignmentDir = resolved.assignmentDir;
|
|
29508
29798
|
targetRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
|
|
29509
29799
|
}
|
|
29510
|
-
const assignmentMdPath2 =
|
|
29800
|
+
const assignmentMdPath2 = resolve70(assignmentDir, "assignment.md");
|
|
29511
29801
|
if (!await fileExists(assignmentMdPath2)) {
|
|
29512
29802
|
throw new Error(`assignment.md not found at ${assignmentMdPath2}`);
|
|
29513
29803
|
}
|
|
29514
29804
|
const source = options.from ?? process.env.SYNTAUR_ASSIGNMENT ?? "unknown";
|
|
29515
|
-
let content = await
|
|
29805
|
+
let content = await readFile46(assignmentMdPath2, "utf-8");
|
|
29516
29806
|
content = appendTodosToAssignmentBody(content, [
|
|
29517
29807
|
{ description: `${text.trim()} (from: ${source})` }
|
|
29518
29808
|
]);
|
|
@@ -29525,13 +29815,13 @@ async function requestCommand(target, text, options = {}) {
|
|
|
29525
29815
|
init_fs();
|
|
29526
29816
|
init_paths();
|
|
29527
29817
|
import { Command as Command9 } from "commander";
|
|
29528
|
-
import { readFile as
|
|
29529
|
-
import { resolve as
|
|
29818
|
+
import { readFile as readFile47, readdir as readdir25 } from "fs/promises";
|
|
29819
|
+
import { resolve as resolve71 } from "path";
|
|
29530
29820
|
async function readContextAssignmentDir(cwd) {
|
|
29531
|
-
const path =
|
|
29821
|
+
const path = resolve71(cwd, ".syntaur", "context.json");
|
|
29532
29822
|
if (!await fileExists(path)) return null;
|
|
29533
29823
|
try {
|
|
29534
|
-
const raw = await
|
|
29824
|
+
const raw = await readFile47(path, "utf-8");
|
|
29535
29825
|
const ctx = JSON.parse(raw);
|
|
29536
29826
|
if (typeof ctx.assignmentDir === "string" && ctx.assignmentDir.length > 0) {
|
|
29537
29827
|
return ctx.assignmentDir;
|
|
@@ -29545,9 +29835,9 @@ async function resolveAssignmentDir(opts) {
|
|
|
29545
29835
|
const cwd = opts.cwd ?? process.cwd();
|
|
29546
29836
|
if (opts.assignment) {
|
|
29547
29837
|
if (opts.project) {
|
|
29548
|
-
return
|
|
29838
|
+
return resolve71(defaultProjectDir(), opts.project, "assignments", opts.assignment);
|
|
29549
29839
|
}
|
|
29550
|
-
return
|
|
29840
|
+
return resolve71(assignmentsDir(), opts.assignment);
|
|
29551
29841
|
}
|
|
29552
29842
|
const fromCtx = await readContextAssignmentDir(cwd);
|
|
29553
29843
|
if (fromCtx) return fromCtx;
|
|
@@ -29690,7 +29980,7 @@ async function runPlanVersion(options) {
|
|
|
29690
29980
|
if (!await fileExists(assignmentDir)) {
|
|
29691
29981
|
throw new Error(`Assignment directory does not exist: ${assignmentDir}`);
|
|
29692
29982
|
}
|
|
29693
|
-
const assignmentMdPath2 =
|
|
29983
|
+
const assignmentMdPath2 = resolve71(assignmentDir, "assignment.md");
|
|
29694
29984
|
if (!await fileExists(assignmentMdPath2)) {
|
|
29695
29985
|
throw new Error(`Missing assignment.md at: ${assignmentMdPath2}`);
|
|
29696
29986
|
}
|
|
@@ -29702,15 +29992,15 @@ async function runPlanVersion(options) {
|
|
|
29702
29992
|
}
|
|
29703
29993
|
const current = planFiles[planFiles.length - 1];
|
|
29704
29994
|
const next = nextPlanFileName(current.version);
|
|
29705
|
-
const newPath =
|
|
29995
|
+
const newPath = resolve71(assignmentDir, next.fileName);
|
|
29706
29996
|
if (await fileExists(newPath) && !options.force) {
|
|
29707
29997
|
throw new Error(`${next.fileName} already exists. Use --force to overwrite.`);
|
|
29708
29998
|
}
|
|
29709
|
-
const assignmentMd = await
|
|
29999
|
+
const assignmentMd = await readFile47(assignmentMdPath2, "utf-8");
|
|
29710
30000
|
const slugMatch = assignmentMd.match(/^slug:\s*(.+?)\s*$/m);
|
|
29711
30001
|
const slug = slugMatch ? slugMatch[1].trim() : assignmentDir.split("/").pop() ?? "";
|
|
29712
|
-
const oldPlanPath =
|
|
29713
|
-
const oldPlanContent = await
|
|
30002
|
+
const oldPlanPath = resolve71(assignmentDir, current.fileName);
|
|
30003
|
+
const oldPlanContent = await readFile47(oldPlanPath, "utf-8");
|
|
29714
30004
|
const oldBody = oldPlanContent.replace(/^---[\s\S]*?\n---\n?/, "");
|
|
29715
30005
|
const carriedTodos = extractUncheckedTodos(oldBody);
|
|
29716
30006
|
const stub = buildNewPlanStub({
|
|
@@ -29747,26 +30037,26 @@ planCommand.command("version").description(
|
|
|
29747
30037
|
// src/commands/session.ts
|
|
29748
30038
|
init_fs();
|
|
29749
30039
|
import { Command as Command10 } from "commander";
|
|
29750
|
-
import { readFile as
|
|
29751
|
-
import { resolve as
|
|
30040
|
+
import { readFile as readFile48, readdir as readdir26, stat as stat11 } from "fs/promises";
|
|
30041
|
+
import { resolve as resolve72 } from "path";
|
|
29752
30042
|
async function readContext(cwd) {
|
|
29753
|
-
const path =
|
|
30043
|
+
const path = resolve72(cwd, ".syntaur", "context.json");
|
|
29754
30044
|
if (!await fileExists(path)) return null;
|
|
29755
30045
|
try {
|
|
29756
|
-
const raw = await
|
|
30046
|
+
const raw = await readFile48(path, "utf-8");
|
|
29757
30047
|
return JSON.parse(raw);
|
|
29758
30048
|
} catch {
|
|
29759
30049
|
return null;
|
|
29760
30050
|
}
|
|
29761
30051
|
}
|
|
29762
30052
|
async function findLatestSessionSummary(assignmentDir) {
|
|
29763
|
-
const sessionsRoot =
|
|
30053
|
+
const sessionsRoot = resolve72(assignmentDir, "sessions");
|
|
29764
30054
|
if (!await fileExists(sessionsRoot)) return null;
|
|
29765
30055
|
const entries = await readdir26(sessionsRoot, { withFileTypes: true });
|
|
29766
30056
|
let best = null;
|
|
29767
30057
|
for (const entry of entries) {
|
|
29768
30058
|
if (!entry.isDirectory()) continue;
|
|
29769
|
-
const summaryPath =
|
|
30059
|
+
const summaryPath = resolve72(sessionsRoot, entry.name, "summary.md");
|
|
29770
30060
|
if (!await fileExists(summaryPath)) continue;
|
|
29771
30061
|
const st = await stat11(summaryPath);
|
|
29772
30062
|
if (best === null || st.mtime.getTime() > best.mtime.getTime()) {
|
|
@@ -29776,9 +30066,9 @@ async function findLatestSessionSummary(assignmentDir) {
|
|
|
29776
30066
|
return best;
|
|
29777
30067
|
}
|
|
29778
30068
|
async function findOpenHandoff(assignmentDir) {
|
|
29779
|
-
const handoffPath =
|
|
30069
|
+
const handoffPath = resolve72(assignmentDir, "handoff.md");
|
|
29780
30070
|
if (!await fileExists(handoffPath)) return null;
|
|
29781
|
-
const content = await
|
|
30071
|
+
const content = await readFile48(handoffPath, "utf-8");
|
|
29782
30072
|
const body = content.replace(/^---[\s\S]*?\n---\n?/, "").trim();
|
|
29783
30073
|
if (body.length === 0) return null;
|
|
29784
30074
|
if (/^<!--[\s\S]*-->$/.test(body)) return null;
|
|
@@ -29886,13 +30176,13 @@ init_git_worktree();
|
|
|
29886
30176
|
init_fs();
|
|
29887
30177
|
init_paths();
|
|
29888
30178
|
import { Command as Command11 } from "commander";
|
|
29889
|
-
import { readFile as
|
|
29890
|
-
import { resolve as
|
|
30179
|
+
import { readFile as readFile49 } from "fs/promises";
|
|
30180
|
+
import { resolve as resolve73 } from "path";
|
|
29891
30181
|
async function readContext2(cwd) {
|
|
29892
|
-
const path =
|
|
30182
|
+
const path = resolve73(cwd, ".syntaur", "context.json");
|
|
29893
30183
|
if (!await fileExists(path)) return null;
|
|
29894
30184
|
try {
|
|
29895
|
-
return JSON.parse(await
|
|
30185
|
+
return JSON.parse(await readFile49(path, "utf-8"));
|
|
29896
30186
|
} catch {
|
|
29897
30187
|
return null;
|
|
29898
30188
|
}
|
|
@@ -29900,7 +30190,7 @@ async function readContext2(cwd) {
|
|
|
29900
30190
|
async function resolveAssignmentPath2(opts) {
|
|
29901
30191
|
if (opts.assignment) {
|
|
29902
30192
|
if (opts.project) {
|
|
29903
|
-
return
|
|
30193
|
+
return resolve73(
|
|
29904
30194
|
defaultProjectDir(),
|
|
29905
30195
|
opts.project,
|
|
29906
30196
|
"assignments",
|
|
@@ -29908,10 +30198,10 @@ async function resolveAssignmentPath2(opts) {
|
|
|
29908
30198
|
"assignment.md"
|
|
29909
30199
|
);
|
|
29910
30200
|
}
|
|
29911
|
-
return
|
|
30201
|
+
return resolve73(assignmentsDir(), opts.assignment, "assignment.md");
|
|
29912
30202
|
}
|
|
29913
30203
|
const ctx = await readContext2(opts.cwd);
|
|
29914
|
-
if (ctx?.assignmentDir) return
|
|
30204
|
+
if (ctx?.assignmentDir) return resolve73(ctx.assignmentDir, "assignment.md");
|
|
29915
30205
|
throw new Error(
|
|
29916
30206
|
"No active assignment. Pass --assignment <slug> [--project <slug>] or run from a workspace with .syntaur/context.json."
|
|
29917
30207
|
);
|
|
@@ -29922,7 +30212,7 @@ async function runWorktreeCreate(options, cwd = process.cwd()) {
|
|
|
29922
30212
|
}
|
|
29923
30213
|
const repository = options.repository ?? cwd;
|
|
29924
30214
|
const parentBranch = options.parentBranch ?? "main";
|
|
29925
|
-
const worktreePath = options.worktreePath ??
|
|
30215
|
+
const worktreePath = options.worktreePath ?? resolve73(repository, ".worktrees", options.branch);
|
|
29926
30216
|
const assignmentPath = await resolveAssignmentPath2({
|
|
29927
30217
|
assignment: options.assignment,
|
|
29928
30218
|
project: options.project,
|
|
@@ -29959,13 +30249,13 @@ init_paths();
|
|
|
29959
30249
|
init_fs();
|
|
29960
30250
|
init_slug();
|
|
29961
30251
|
import { Command as Command12 } from "commander";
|
|
29962
|
-
import { resolve as
|
|
30252
|
+
import { resolve as resolve75 } from "path";
|
|
29963
30253
|
|
|
29964
30254
|
// src/utils/project-indexes.ts
|
|
29965
30255
|
init_parser();
|
|
29966
30256
|
init_fs();
|
|
29967
|
-
import { readdir as readdir27, readFile as
|
|
29968
|
-
import { resolve as
|
|
30257
|
+
import { readdir as readdir27, readFile as readFile50 } from "fs/promises";
|
|
30258
|
+
import { resolve as resolve74 } from "path";
|
|
29969
30259
|
function nowIso3() {
|
|
29970
30260
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
29971
30261
|
}
|
|
@@ -29984,7 +30274,7 @@ function joinList(items) {
|
|
|
29984
30274
|
return items.length === 0 ? "\u2014" : items.map(escapeCell).join(", ");
|
|
29985
30275
|
}
|
|
29986
30276
|
async function rebuildResourcesIndex(projectDir) {
|
|
29987
|
-
const dir =
|
|
30277
|
+
const dir = resolve74(projectDir, "resources");
|
|
29988
30278
|
await ensureDir(dir);
|
|
29989
30279
|
const files = await listSlugFiles(dir);
|
|
29990
30280
|
const slug = readProjectSlug(projectDir);
|
|
@@ -30000,7 +30290,7 @@ async function rebuildResourcesIndex(projectDir) {
|
|
|
30000
30290
|
lines.push("| Name | Category | Source | Related Assignments | Updated |");
|
|
30001
30291
|
lines.push("|------|----------|--------|---------------------|---------|");
|
|
30002
30292
|
for (const fileName of files) {
|
|
30003
|
-
const content = await
|
|
30293
|
+
const content = await readFile50(resolve74(dir, fileName), "utf-8");
|
|
30004
30294
|
const parsed = parseResource(content);
|
|
30005
30295
|
const slugBase = fileName.replace(/\.md$/, "");
|
|
30006
30296
|
const name = parsed.name || slugBase;
|
|
@@ -30010,12 +30300,12 @@ async function rebuildResourcesIndex(projectDir) {
|
|
|
30010
30300
|
);
|
|
30011
30301
|
}
|
|
30012
30302
|
lines.push("");
|
|
30013
|
-
const indexPath =
|
|
30303
|
+
const indexPath = resolve74(dir, "_index.md");
|
|
30014
30304
|
await writeFileForce(indexPath, lines.join("\n"));
|
|
30015
30305
|
return { total: files.length, path: indexPath };
|
|
30016
30306
|
}
|
|
30017
30307
|
async function rebuildMemoriesIndex(projectDir) {
|
|
30018
|
-
const dir =
|
|
30308
|
+
const dir = resolve74(projectDir, "memories");
|
|
30019
30309
|
await ensureDir(dir);
|
|
30020
30310
|
const files = await listSlugFiles(dir);
|
|
30021
30311
|
const slug = readProjectSlug(projectDir);
|
|
@@ -30031,7 +30321,7 @@ async function rebuildMemoriesIndex(projectDir) {
|
|
|
30031
30321
|
lines.push("| Name | Source | Scope | Source Assignment | Updated |");
|
|
30032
30322
|
lines.push("|------|--------|-------|-------------------|---------|");
|
|
30033
30323
|
for (const fileName of files) {
|
|
30034
|
-
const content = await
|
|
30324
|
+
const content = await readFile50(resolve74(dir, fileName), "utf-8");
|
|
30035
30325
|
const parsed = parseMemory(content);
|
|
30036
30326
|
const slugBase = fileName.replace(/\.md$/, "");
|
|
30037
30327
|
const name = parsed.name || slugBase;
|
|
@@ -30041,7 +30331,7 @@ async function rebuildMemoriesIndex(projectDir) {
|
|
|
30041
30331
|
);
|
|
30042
30332
|
}
|
|
30043
30333
|
lines.push("");
|
|
30044
|
-
const indexPath =
|
|
30334
|
+
const indexPath = resolve74(dir, "_index.md");
|
|
30045
30335
|
await writeFileForce(indexPath, lines.join("\n"));
|
|
30046
30336
|
return { total: files.length, path: indexPath };
|
|
30047
30337
|
}
|
|
@@ -30079,8 +30369,8 @@ async function runResourceAdd(options) {
|
|
|
30079
30369
|
if (!isValidSlug(options.project)) {
|
|
30080
30370
|
throw new Error(`Invalid project slug: "${options.project}".`);
|
|
30081
30371
|
}
|
|
30082
|
-
const projectDir =
|
|
30083
|
-
if (!await fileExists(
|
|
30372
|
+
const projectDir = resolve75(defaultProjectDir(), options.project);
|
|
30373
|
+
if (!await fileExists(resolve75(projectDir, "project.md"))) {
|
|
30084
30374
|
throw new Error(`Project "${options.project}" not found at ${projectDir}.`);
|
|
30085
30375
|
}
|
|
30086
30376
|
if (!options.name) throw new Error("--name is required.");
|
|
@@ -30089,7 +30379,7 @@ async function runResourceAdd(options) {
|
|
|
30089
30379
|
if (!isValidSlug(slug)) {
|
|
30090
30380
|
throw new Error(`Invalid resource slug: "${slug}".`);
|
|
30091
30381
|
}
|
|
30092
|
-
const filePath =
|
|
30382
|
+
const filePath = resolve75(projectDir, "resources", `${slug}.md`);
|
|
30093
30383
|
if (await fileExists(filePath) && !options.force) {
|
|
30094
30384
|
throw new Error(
|
|
30095
30385
|
`Resource "${slug}" already exists at ${filePath}. Use --force to overwrite.`
|
|
@@ -30124,7 +30414,7 @@ init_paths();
|
|
|
30124
30414
|
init_fs();
|
|
30125
30415
|
init_slug();
|
|
30126
30416
|
import { Command as Command13 } from "commander";
|
|
30127
|
-
import { resolve as
|
|
30417
|
+
import { resolve as resolve76 } from "path";
|
|
30128
30418
|
function nowIso5() {
|
|
30129
30419
|
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
30130
30420
|
}
|
|
@@ -30159,8 +30449,8 @@ async function runMemoryAdd(options) {
|
|
|
30159
30449
|
if (!isValidSlug(options.project)) {
|
|
30160
30450
|
throw new Error(`Invalid project slug: "${options.project}".`);
|
|
30161
30451
|
}
|
|
30162
|
-
const projectDir =
|
|
30163
|
-
if (!await fileExists(
|
|
30452
|
+
const projectDir = resolve76(defaultProjectDir(), options.project);
|
|
30453
|
+
if (!await fileExists(resolve76(projectDir, "project.md"))) {
|
|
30164
30454
|
throw new Error(`Project "${options.project}" not found at ${projectDir}.`);
|
|
30165
30455
|
}
|
|
30166
30456
|
if (!options.name) throw new Error("--name is required.");
|
|
@@ -30169,7 +30459,7 @@ async function runMemoryAdd(options) {
|
|
|
30169
30459
|
if (!isValidSlug(slug)) {
|
|
30170
30460
|
throw new Error(`Invalid memory slug: "${slug}".`);
|
|
30171
30461
|
}
|
|
30172
|
-
const filePath =
|
|
30462
|
+
const filePath = resolve76(projectDir, "memories", `${slug}.md`);
|
|
30173
30463
|
if (await fileExists(filePath) && !options.force) {
|
|
30174
30464
|
throw new Error(
|
|
30175
30465
|
`Memory "${slug}" already exists at ${filePath}. Use --force to overwrite.`
|
|
@@ -30206,8 +30496,8 @@ init_paths();
|
|
|
30206
30496
|
init_fs();
|
|
30207
30497
|
init_frontmatter();
|
|
30208
30498
|
import { Command as Command14 } from "commander";
|
|
30209
|
-
import { readFile as
|
|
30210
|
-
import { resolve as
|
|
30499
|
+
import { readFile as readFile51 } from "fs/promises";
|
|
30500
|
+
import { resolve as resolve77 } from "path";
|
|
30211
30501
|
var AGE_PATTERN = /^(\d+)([dhwm])$/i;
|
|
30212
30502
|
function parseAgeToCutoff(age) {
|
|
30213
30503
|
const match = age.match(AGE_PATTERN);
|
|
@@ -30226,7 +30516,7 @@ function parseAgeToCutoff(age) {
|
|
|
30226
30516
|
}
|
|
30227
30517
|
function assignmentMdPath(item) {
|
|
30228
30518
|
if (item.projectSlug) {
|
|
30229
|
-
return
|
|
30519
|
+
return resolve77(
|
|
30230
30520
|
defaultProjectDir(),
|
|
30231
30521
|
item.projectSlug,
|
|
30232
30522
|
"assignments",
|
|
@@ -30234,13 +30524,13 @@ function assignmentMdPath(item) {
|
|
|
30234
30524
|
"assignment.md"
|
|
30235
30525
|
);
|
|
30236
30526
|
}
|
|
30237
|
-
return
|
|
30527
|
+
return resolve77(assignmentsDir(), item.id, "assignment.md");
|
|
30238
30528
|
}
|
|
30239
30529
|
async function loadTags(item) {
|
|
30240
30530
|
const path = assignmentMdPath(item);
|
|
30241
30531
|
if (!await fileExists(path)) return [];
|
|
30242
30532
|
try {
|
|
30243
|
-
const content = await
|
|
30533
|
+
const content = await readFile51(path, "utf-8");
|
|
30244
30534
|
return parseAssignmentFrontmatter(content).tags;
|
|
30245
30535
|
} catch {
|
|
30246
30536
|
return [];
|
|
@@ -30249,7 +30539,8 @@ async function loadTags(item) {
|
|
|
30249
30539
|
async function runLs(options) {
|
|
30250
30540
|
const board = await listAssignmentsBoard(
|
|
30251
30541
|
defaultProjectDir(),
|
|
30252
|
-
assignmentsDir()
|
|
30542
|
+
assignmentsDir(),
|
|
30543
|
+
{ archived: options.archived ? "only" : "exclude" }
|
|
30253
30544
|
);
|
|
30254
30545
|
let items = board.assignments;
|
|
30255
30546
|
if (options.status) {
|
|
@@ -30302,7 +30593,7 @@ function renderTable2(items) {
|
|
|
30302
30593
|
}
|
|
30303
30594
|
var lsCommand = new Command14("ls").description(
|
|
30304
30595
|
"List assignments across all projects with optional filters by status, project, tag, or age."
|
|
30305
|
-
).option("--status <list>", "Comma-separated status filter (e.g. pending,in_progress)").option("--project <slug>", "Filter to one project").option("--tag <list>", "Comma-separated tag filter (assignment must have ALL tags)").option("--age <duration>", "Only include assignments updated within duration (e.g. 7d, 24h, 2w, 1m)").option("--json", "Emit JSON instead of a table").action(async (options) => {
|
|
30596
|
+
).option("--status <list>", "Comma-separated status filter (e.g. pending,in_progress)").option("--project <slug>", "Filter to one project").option("--tag <list>", "Comma-separated tag filter (assignment must have ALL tags)").option("--age <duration>", "Only include assignments updated within duration (e.g. 7d, 24h, 2w, 1m)").option("--archived", "List only archived assignments (hidden from the default view)").option("--json", "Emit JSON instead of a table").action(async (options) => {
|
|
30306
30597
|
try {
|
|
30307
30598
|
const { items } = await runLs(options);
|
|
30308
30599
|
if (options.json) {
|
|
@@ -30358,20 +30649,20 @@ async function getDefaultCommandName() {
|
|
|
30358
30649
|
init_paths();
|
|
30359
30650
|
init_fs();
|
|
30360
30651
|
import { fileURLToPath as fileURLToPath10 } from "url";
|
|
30361
|
-
import { readFile as
|
|
30362
|
-
import { dirname as dirname21, join as join16, resolve as
|
|
30652
|
+
import { readFile as readFile53 } from "fs/promises";
|
|
30653
|
+
import { dirname as dirname21, join as join16, resolve as resolve78 } from "path";
|
|
30363
30654
|
import { spawn as spawn10 } from "child_process";
|
|
30364
30655
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
30365
30656
|
|
|
30366
30657
|
// src/utils/version.ts
|
|
30367
30658
|
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
30368
|
-
import { readFile as
|
|
30659
|
+
import { readFile as readFile52 } from "fs/promises";
|
|
30369
30660
|
import { dirname as dirname20, join as join15 } from "path";
|
|
30370
30661
|
async function readPackageVersion(scriptUrl) {
|
|
30371
30662
|
try {
|
|
30372
30663
|
const scriptPath = fileURLToPath9(scriptUrl);
|
|
30373
30664
|
const pkgRoot = dirname20(dirname20(scriptPath));
|
|
30374
|
-
const raw = await
|
|
30665
|
+
const raw = await readFile52(join15(pkgRoot, "package.json"), "utf-8");
|
|
30375
30666
|
const parsed = JSON.parse(raw);
|
|
30376
30667
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
30377
30668
|
} catch {
|
|
@@ -30380,7 +30671,7 @@ async function readPackageVersion(scriptUrl) {
|
|
|
30380
30671
|
}
|
|
30381
30672
|
|
|
30382
30673
|
// src/utils/npx-prompt.ts
|
|
30383
|
-
var STATE_FILE =
|
|
30674
|
+
var STATE_FILE = resolve78(syntaurRoot(), "npx-install.json");
|
|
30384
30675
|
var META_ARGS2 = /* @__PURE__ */ new Set(["-h", "--help", "-V", "--version", "help"]);
|
|
30385
30676
|
var GLOBAL_VERSION_TIMEOUT_MS = 2e3;
|
|
30386
30677
|
function isRunningViaNpx(scriptUrl) {
|
|
@@ -30401,7 +30692,7 @@ function isRunningViaNpx(scriptUrl) {
|
|
|
30401
30692
|
async function readState() {
|
|
30402
30693
|
if (!await fileExists(STATE_FILE)) return null;
|
|
30403
30694
|
try {
|
|
30404
|
-
const raw = await
|
|
30695
|
+
const raw = await readFile53(STATE_FILE, "utf-8");
|
|
30405
30696
|
return JSON.parse(raw);
|
|
30406
30697
|
} catch {
|
|
30407
30698
|
return null;
|
|
@@ -30460,7 +30751,7 @@ async function readGlobalVersion() {
|
|
|
30460
30751
|
try {
|
|
30461
30752
|
const manifestPath = join16(rootPath, "syntaur", "package.json");
|
|
30462
30753
|
if (!await fileExists(manifestPath)) return null;
|
|
30463
|
-
const raw = await
|
|
30754
|
+
const raw = await readFile53(manifestPath, "utf-8");
|
|
30464
30755
|
const parsed = JSON.parse(raw);
|
|
30465
30756
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
30466
30757
|
} catch {
|
|
@@ -30703,6 +30994,22 @@ program.command("start").description("Transition an assignment to in_progress").
|
|
|
30703
30994
|
process.exit(1);
|
|
30704
30995
|
}
|
|
30705
30996
|
});
|
|
30997
|
+
program.command("archive").description("Archive an assignment or a project (hidden from normal views; restorable)").argument("<target>", "Assignment slug/UUID, or a project slug").option("--project <slug>", "Resolve <target> as an assignment within this project").option("--reason <text>", "Optional reason recorded with the archive").option("--dir <path>", "Override default project directory").action(async (target, options) => {
|
|
30998
|
+
try {
|
|
30999
|
+
await archiveCommand(target, options);
|
|
31000
|
+
} catch (error) {
|
|
31001
|
+
console.error("Error:", error instanceof Error ? error.message : String(error));
|
|
31002
|
+
process.exit(1);
|
|
31003
|
+
}
|
|
31004
|
+
});
|
|
31005
|
+
program.command("restore").description("Restore an archived assignment or project (preserves prior status)").argument("<target>", "Assignment slug/UUID, or a project slug").option("--project <slug>", "Resolve <target> as an assignment within this project").option("--dir <path>", "Override default project directory").action(async (target, options) => {
|
|
31006
|
+
try {
|
|
31007
|
+
await restoreCommand(target, options);
|
|
31008
|
+
} catch (error) {
|
|
31009
|
+
console.error("Error:", error instanceof Error ? error.message : String(error));
|
|
31010
|
+
process.exit(1);
|
|
31011
|
+
}
|
|
31012
|
+
});
|
|
30706
31013
|
program.command("shape").description("Transition an assignment from draft to ready_for_planning").argument("<assignment>", "Assignment slug").option("--project <slug>", "Target project slug").option("--agent <name>", "Agent name (sets assignee if not already set)").option("--dir <path>", "Override default project directory").action(async (assignment, options) => {
|
|
30707
31014
|
try {
|
|
30708
31015
|
await shapeCommand(assignment, options);
|