syntaur 0.6.0 → 0.6.1
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-BQIP1Ca7.js → _basePickBy-DTYUlCEg.js} +1 -1
- package/dashboard/dist/assets/{_baseUniq-BnBWRwT7.js → _baseUniq-C0Y4HRd5.js} +1 -1
- package/dashboard/dist/assets/{arc-BYWL4eq0.js → arc-BFx2eqN9.js} +1 -1
- package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-CD_SWPSa.js → architectureDiagram-2XIMDMQ5-Erol1JD6.js} +1 -1
- package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-BS1ZbFBU.js → blockDiagram-WCTKOSBZ-kSkh6VkS.js} +1 -1
- package/dashboard/dist/assets/{c4Diagram-IC4MRINW-D99yg-l2.js → c4Diagram-IC4MRINW-C04oKzvX.js} +1 -1
- package/dashboard/dist/assets/channel-C82tBKZ7.js +1 -0
- package/dashboard/dist/assets/{chunk-4BX2VUAB-BkN9IORC.js → chunk-4BX2VUAB-C3t0tXt-.js} +1 -1
- package/dashboard/dist/assets/{chunk-55IACEB6-BQPHWefV.js → chunk-55IACEB6-2cnyEL0b.js} +1 -1
- package/dashboard/dist/assets/{chunk-FMBD7UC4-CNcExMdx.js → chunk-FMBD7UC4-DIY9MTNi.js} +1 -1
- package/dashboard/dist/assets/{chunk-JSJVCQXG-LXBmftkC.js → chunk-JSJVCQXG-Cw8fpqpE.js} +1 -1
- package/dashboard/dist/assets/{chunk-KX2RTZJC-Tqi7zNqq.js → chunk-KX2RTZJC-BAhd66XV.js} +1 -1
- package/dashboard/dist/assets/{chunk-NQ4KR5QH-DkMbx-rW.js → chunk-NQ4KR5QH-RzXwoxk3.js} +1 -1
- package/dashboard/dist/assets/{chunk-QZHKN3VN-BlrRCfkJ.js → chunk-QZHKN3VN-Dgri4sGz.js} +1 -1
- package/dashboard/dist/assets/{chunk-WL4C6EOR-of3XBzMu.js → chunk-WL4C6EOR-DYLj9JRa.js} +1 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-STOZ51tg.js +1 -0
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-STOZ51tg.js +1 -0
- package/dashboard/dist/assets/clone-TzhWk-Bj.js +1 -0
- package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-BlIiyO76.js → cose-bilkent-S5V4N54A-DfY_Fnfu.js} +1 -1
- package/dashboard/dist/assets/{dagre-KLK3FWXG-CYQjSI9N.js → dagre-KLK3FWXG-CyTKIVSK.js} +1 -1
- package/dashboard/dist/assets/{diagram-E7M64L7V-BZHzTKct.js → diagram-E7M64L7V-Krub7Xxo.js} +1 -1
- package/dashboard/dist/assets/{diagram-IFDJBPK2-kMP3WqBV.js → diagram-IFDJBPK2-giUl9uHz.js} +1 -1
- package/dashboard/dist/assets/{diagram-P4PSJMXO-BWSHyFOv.js → diagram-P4PSJMXO-oAtnO3C9.js} +1 -1
- package/dashboard/dist/assets/{erDiagram-INFDFZHY-B5HrvsPP.js → erDiagram-INFDFZHY-eYaVjXqo.js} +1 -1
- package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-Dm4ewP7w.js → flowDiagram-PKNHOUZH-or5S0_Sb.js} +1 -1
- package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-DB3k27zu.js → ganttDiagram-A5KZAMGK-C9R1lsme.js} +1 -1
- package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-G7y6Ey-m.js → gitGraphDiagram-K3NZZRJ6-BQwsDzvp.js} +1 -1
- package/dashboard/dist/assets/{graph-CaM4i6vq.js → graph-EQOX1wg8.js} +1 -1
- package/dashboard/dist/assets/index-Cy7yjuqO.js +500 -0
- package/dashboard/dist/assets/index-u80fISp0.css +1 -0
- package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-JNTUbTjg.js → infoDiagram-LFFYTUFH-BjLlQWxk.js} +1 -1
- package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-BZJt1ht8.js → ishikawaDiagram-PHBUUO56-BNjydh4j.js} +1 -1
- package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-DPcqvl9A.js → journeyDiagram-4ABVD52K-DNzE7TgQ.js} +1 -1
- package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-D1D7AuOV.js → kanban-definition-K7BYSVSG-FbNvGx6i.js} +1 -1
- package/dashboard/dist/assets/{layout-BTOh3EDT.js → layout-B2yZvlWs.js} +1 -1
- package/dashboard/dist/assets/{linear-MbCpC_Cg.js → linear-p68yY_14.js} +1 -1
- package/dashboard/dist/assets/{mermaid.core-CYbhqlNy.js → mermaid.core-D558akcW.js} +4 -4
- package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-CwYCISFH.js → mindmap-definition-YRQLILUH-CZPgesSK.js} +1 -1
- package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-5qfZ73SG.js → pieDiagram-SKSYHLDU-CdXMWspp.js} +1 -1
- package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-WI8y1sQ_.js → quadrantDiagram-337W2JSQ-D7tq22ZY.js} +1 -1
- package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-BFlD0ZTS.js → requirementDiagram-Z7DCOOCP-ByZxUSmd.js} +1 -1
- package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-Bdckv1Se.js → sankeyDiagram-WA2Y5GQK-CZon9rRY.js} +1 -1
- package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-DgzxKAlZ.js → sequenceDiagram-2WXFIKYE-nbELB6rb.js} +1 -1
- package/dashboard/dist/assets/{stateDiagram-RAJIS63D-DO4OXahC.js → stateDiagram-RAJIS63D-D_OPKr5B.js} +1 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-4pLM5B3m.js +1 -0
- package/dashboard/dist/assets/{timeline-definition-YZTLITO2-BBB01JWw.js → timeline-definition-YZTLITO2-Cxuvk1D2.js} +1 -1
- package/dashboard/dist/assets/{treemap-KZPCXAKY-Dr0jb8op.js → treemap-KZPCXAKY-C0CRpL92.js} +1 -1
- package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-D40KFl2o.js → vennDiagram-LZ73GAT5-DijCj6M3.js} +1 -1
- package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-DBUmWQfT.js → xychartDiagram-JWTSCODW-CdpE0oRi.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/dashboard/server.js +419 -38
- package/dist/dashboard/server.js.map +1 -1
- package/dist/index.js +577 -88
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dashboard/dist/assets/channel-Df6VrFK5.js +0 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-CyfzumTY.js +0 -1
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-CyfzumTY.js +0 -1
- package/dashboard/dist/assets/clone-CMs4Aqrx.js +0 -1
- package/dashboard/dist/assets/index-B4QMu-Oq.css +0 -1
- package/dashboard/dist/assets/index-BBWZjPBC.js +0 -495
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-o8bgX-J3.js +0 -1
package/dist/index.js
CHANGED
|
@@ -6684,7 +6684,7 @@ __export(launch_exports, {
|
|
|
6684
6684
|
shellQuote: () => shellQuote
|
|
6685
6685
|
});
|
|
6686
6686
|
import { spawn as spawn2 } from "child_process";
|
|
6687
|
-
import { mkdir as
|
|
6687
|
+
import { mkdir as mkdir6, writeFile as writeFile9 } from "fs/promises";
|
|
6688
6688
|
import { isAbsolute as isAbsolute3, resolve as resolve32 } from "path";
|
|
6689
6689
|
function formatFallbackCwdWarning(opts) {
|
|
6690
6690
|
const missing = [];
|
|
@@ -6743,7 +6743,7 @@ async function launchAgent(options) {
|
|
|
6743
6743
|
if (warning) console.warn(warning);
|
|
6744
6744
|
}
|
|
6745
6745
|
const contextDir = resolve32(workspaceDir, ".syntaur");
|
|
6746
|
-
await
|
|
6746
|
+
await mkdir6(contextDir, { recursive: true });
|
|
6747
6747
|
const context = {
|
|
6748
6748
|
projectSlug,
|
|
6749
6749
|
assignmentSlug,
|
|
@@ -7070,7 +7070,7 @@ init_create_assignment();
|
|
|
7070
7070
|
init_config2();
|
|
7071
7071
|
import { spawn } from "child_process";
|
|
7072
7072
|
import { createServer as createNetServer } from "net";
|
|
7073
|
-
import { resolve as resolve22, dirname as
|
|
7073
|
+
import { resolve as resolve22, dirname as dirname6 } from "path";
|
|
7074
7074
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7075
7075
|
|
|
7076
7076
|
// src/dashboard/server.ts
|
|
@@ -9536,37 +9536,62 @@ init_fs_migration();
|
|
|
9536
9536
|
// src/dashboard/api-todos.ts
|
|
9537
9537
|
init_parser2();
|
|
9538
9538
|
init_fs();
|
|
9539
|
+
init_paths();
|
|
9539
9540
|
import { Router as Router5 } from "express";
|
|
9540
9541
|
import { readdir as readdir9 } from "fs/promises";
|
|
9541
|
-
|
|
9542
|
-
|
|
9543
|
-
|
|
9544
|
-
|
|
9545
|
-
}
|
|
9546
|
-
return value ?? "";
|
|
9547
|
-
}
|
|
9542
|
+
import { resolve as resolvePath, dirname as dirname4 } from "path";
|
|
9543
|
+
import { rename as rename3, mkdir as mkdir2 } from "fs/promises";
|
|
9544
|
+
|
|
9545
|
+
// src/dashboard/todos-locks.ts
|
|
9548
9546
|
var writeLocks = /* @__PURE__ */ new Map();
|
|
9549
9547
|
function withLock(lockKey, fn) {
|
|
9550
9548
|
const prev = writeLocks.get(lockKey) ?? Promise.resolve();
|
|
9551
9549
|
const next = prev.then(fn);
|
|
9552
|
-
writeLocks.set(
|
|
9553
|
-
|
|
9554
|
-
|
|
9550
|
+
writeLocks.set(
|
|
9551
|
+
lockKey,
|
|
9552
|
+
next.then(
|
|
9553
|
+
() => {
|
|
9554
|
+
},
|
|
9555
|
+
() => {
|
|
9556
|
+
}
|
|
9557
|
+
)
|
|
9558
|
+
);
|
|
9555
9559
|
return next;
|
|
9556
9560
|
}
|
|
9557
9561
|
function wsLock(workspace, fn) {
|
|
9558
9562
|
return withLock(`ws:${workspace}`, fn);
|
|
9559
9563
|
}
|
|
9564
|
+
function projLock(slug, fn) {
|
|
9565
|
+
return withLock(`proj:${slug}`, fn);
|
|
9566
|
+
}
|
|
9567
|
+
function withTwoLocks(keyA, keyB, fn) {
|
|
9568
|
+
if (keyA === keyB) return withLock(keyA, fn);
|
|
9569
|
+
const [first, second] = keyA < keyB ? [keyA, keyB] : [keyB, keyA];
|
|
9570
|
+
return withLock(first, () => withLock(second, fn));
|
|
9571
|
+
}
|
|
9572
|
+
|
|
9573
|
+
// src/dashboard/api-todos.ts
|
|
9574
|
+
init_slug();
|
|
9575
|
+
var WORKSPACE_REGEX = /^[a-z0-9_][a-z0-9-]*$/;
|
|
9576
|
+
function getWorkspaceParam(value) {
|
|
9577
|
+
if (Array.isArray(value)) {
|
|
9578
|
+
return value[0] ?? "";
|
|
9579
|
+
}
|
|
9580
|
+
return value ?? "";
|
|
9581
|
+
}
|
|
9560
9582
|
function touchItem(item) {
|
|
9561
9583
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
9562
9584
|
if (item.createdAt === null) item.createdAt = now;
|
|
9563
9585
|
item.updatedAt = now;
|
|
9564
9586
|
}
|
|
9565
|
-
function createTodosRouter(todosDir2, broadcast) {
|
|
9587
|
+
function createTodosRouter(todosDir2, broadcast, projectsDir2) {
|
|
9566
9588
|
const router = Router5();
|
|
9567
9589
|
function broadcastUpdate() {
|
|
9568
9590
|
broadcast({ type: "todos-updated", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
9569
9591
|
}
|
|
9592
|
+
function broadcastProject(slug) {
|
|
9593
|
+
broadcast({ type: "todos-updated", projectSlug: slug, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
9594
|
+
}
|
|
9570
9595
|
function validateWorkspace(req, res, next) {
|
|
9571
9596
|
const workspace = getWorkspaceParam(req.params.workspace);
|
|
9572
9597
|
if (workspace && !WORKSPACE_REGEX.test(workspace)) {
|
|
@@ -9965,7 +9990,7 @@ workspace: ${workspace}
|
|
|
9965
9990
|
items.push(item);
|
|
9966
9991
|
}
|
|
9967
9992
|
const scopeLabel = workspace === "_global" ? "_global" : `workspace:${workspace}`;
|
|
9968
|
-
const { resolve:
|
|
9993
|
+
const { resolve: resolvePath2 } = await import("path");
|
|
9969
9994
|
const { readConfig: readConfig3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
9970
9995
|
const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
9971
9996
|
const { fileExists: fileExists2, writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
@@ -9995,18 +10020,18 @@ workspace: ${workspace}
|
|
|
9995
10020
|
const parts = tg.split("/");
|
|
9996
10021
|
if (parts.length !== 2) return { error: `Invalid target.assignment "${tg}"` };
|
|
9997
10022
|
const config = await readConfig3();
|
|
9998
|
-
assignmentDir =
|
|
10023
|
+
assignmentDir = resolvePath2(config.defaultProjectDir, parts[0], "assignments", parts[1]);
|
|
9999
10024
|
assignmentRef = `${parts[0]}/${parts[1]}`;
|
|
10000
10025
|
} else if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(tg)) {
|
|
10001
|
-
assignmentDir =
|
|
10026
|
+
assignmentDir = resolvePath2(assignmentsDirFn(), tg);
|
|
10002
10027
|
assignmentRef = tg;
|
|
10003
10028
|
} else {
|
|
10004
10029
|
return { error: `Invalid target.assignment "${tg}"` };
|
|
10005
10030
|
}
|
|
10006
|
-
const assignmentMdPath2 =
|
|
10031
|
+
const assignmentMdPath2 = resolvePath2(assignmentDir, "assignment.md");
|
|
10007
10032
|
if (!await fileExists2(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
|
|
10008
10033
|
}
|
|
10009
|
-
const assignmentMdPath =
|
|
10034
|
+
const assignmentMdPath = resolvePath2(assignmentDir, "assignment.md");
|
|
10010
10035
|
let content = await readFile32(assignmentMdPath, "utf-8");
|
|
10011
10036
|
content = appendTodosToAssignmentBody2(
|
|
10012
10037
|
content,
|
|
@@ -10050,6 +10075,115 @@ workspace: ${workspace}
|
|
|
10050
10075
|
res.status(500).json({ error: error instanceof Error ? error.message : "Failed to promote todos" });
|
|
10051
10076
|
}
|
|
10052
10077
|
});
|
|
10078
|
+
router.post("/:workspace/:id/move", async (req, res) => {
|
|
10079
|
+
try {
|
|
10080
|
+
const sourceWs = getWorkspaceParam(req.params.workspace);
|
|
10081
|
+
const id = req.params.id;
|
|
10082
|
+
const to = req.body?.to;
|
|
10083
|
+
if (!to || typeof to !== "object") {
|
|
10084
|
+
res.status(400).json({ error: "body.to is required" });
|
|
10085
|
+
return;
|
|
10086
|
+
}
|
|
10087
|
+
const targetCount = [Boolean(to.workspace), Boolean(to.project), Boolean(to.global)].filter(Boolean).length;
|
|
10088
|
+
if (targetCount !== 1) {
|
|
10089
|
+
res.status(400).json({ error: "body.to must specify exactly one of workspace, project, or global" });
|
|
10090
|
+
return;
|
|
10091
|
+
}
|
|
10092
|
+
if (to.project && !isValidSlug(to.project)) {
|
|
10093
|
+
res.status(400).json({ error: `Invalid target project slug: "${to.project}"` });
|
|
10094
|
+
return;
|
|
10095
|
+
}
|
|
10096
|
+
if (to.workspace && !WORKSPACE_REGEX.test(to.workspace)) {
|
|
10097
|
+
res.status(400).json({ error: `Invalid target workspace name: "${to.workspace}"` });
|
|
10098
|
+
return;
|
|
10099
|
+
}
|
|
10100
|
+
let target;
|
|
10101
|
+
if (to.global) {
|
|
10102
|
+
target = { kind: "workspace", id: "_global", todosPath: todosDir2, lockKey: "ws:_global" };
|
|
10103
|
+
} else if (to.workspace) {
|
|
10104
|
+
target = { kind: "workspace", id: to.workspace, todosPath: todosDir2, lockKey: `ws:${to.workspace}` };
|
|
10105
|
+
} else {
|
|
10106
|
+
if (!projectsDir2) {
|
|
10107
|
+
res.status(500).json({ error: "Server not configured with projectsDir; cannot move to project scope" });
|
|
10108
|
+
return;
|
|
10109
|
+
}
|
|
10110
|
+
const slug = to.project;
|
|
10111
|
+
const projectMd = resolvePath(projectsDir2, slug, "project.md");
|
|
10112
|
+
if (!await fileExists(projectMd)) {
|
|
10113
|
+
res.status(404).json({ error: `Target project "${slug}" not found` });
|
|
10114
|
+
return;
|
|
10115
|
+
}
|
|
10116
|
+
target = {
|
|
10117
|
+
kind: "project",
|
|
10118
|
+
id: slug,
|
|
10119
|
+
todosPath: projectTodosDir(projectsDir2, slug),
|
|
10120
|
+
lockKey: `proj:${slug}`
|
|
10121
|
+
};
|
|
10122
|
+
}
|
|
10123
|
+
const sourceLockKey = `ws:${sourceWs}`;
|
|
10124
|
+
if (sourceLockKey === target.lockKey) {
|
|
10125
|
+
res.status(400).json({ error: "cannot move to the same scope" });
|
|
10126
|
+
return;
|
|
10127
|
+
}
|
|
10128
|
+
const result = await withTwoLocks(sourceLockKey, target.lockKey, async () => {
|
|
10129
|
+
const sourceChecklist = await readChecklist(todosDir2, sourceWs);
|
|
10130
|
+
const targetChecklist = await readChecklist(target.todosPath, target.id);
|
|
10131
|
+
const idx = sourceChecklist.items.findIndex((i) => i.id === id);
|
|
10132
|
+
if (idx === -1) return { status: 404, error: `Todo "${id}" not found` };
|
|
10133
|
+
if (targetChecklist.items.some((i) => i.id === id)) {
|
|
10134
|
+
return { status: 409, error: "id already exists in target" };
|
|
10135
|
+
}
|
|
10136
|
+
const item = sourceChecklist.items[idx];
|
|
10137
|
+
if (item.planDir) {
|
|
10138
|
+
const newPlanDir = todoPlanDir(target.todosPath, target.id, id);
|
|
10139
|
+
if (await fileExists(newPlanDir)) {
|
|
10140
|
+
return { status: 409, error: "plan dir already exists in target" };
|
|
10141
|
+
}
|
|
10142
|
+
await mkdir2(dirname4(newPlanDir), { recursive: true });
|
|
10143
|
+
await rename3(item.planDir, newPlanDir);
|
|
10144
|
+
item.planDir = newPlanDir;
|
|
10145
|
+
}
|
|
10146
|
+
sourceChecklist.items.splice(idx, 1);
|
|
10147
|
+
targetChecklist.items.push(item);
|
|
10148
|
+
await writeChecklist(todosDir2, sourceChecklist);
|
|
10149
|
+
await writeChecklist(target.todosPath, targetChecklist);
|
|
10150
|
+
const sourceLabel = sourceWs === "_global" ? "_global" : `workspace:${sourceWs}`;
|
|
10151
|
+
const targetLabel = target.kind === "project" ? `project:${target.id}` : target.id === "_global" ? "_global" : `workspace:${target.id}`;
|
|
10152
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
10153
|
+
await appendLogEntry2(todosDir2, sourceWs, {
|
|
10154
|
+
timestamp: ts,
|
|
10155
|
+
itemIds: [id],
|
|
10156
|
+
items: item.description,
|
|
10157
|
+
session: null,
|
|
10158
|
+
branch: item.branch || null,
|
|
10159
|
+
summary: `Moved to ${targetLabel}`,
|
|
10160
|
+
blockers: null,
|
|
10161
|
+
status: null
|
|
10162
|
+
});
|
|
10163
|
+
await appendLogEntry2(target.todosPath, target.id, {
|
|
10164
|
+
timestamp: ts,
|
|
10165
|
+
itemIds: [id],
|
|
10166
|
+
items: item.description,
|
|
10167
|
+
session: null,
|
|
10168
|
+
branch: item.branch || null,
|
|
10169
|
+
summary: `Moved from ${sourceLabel}`,
|
|
10170
|
+
blockers: null,
|
|
10171
|
+
status: null
|
|
10172
|
+
});
|
|
10173
|
+
return { status: 200, item };
|
|
10174
|
+
});
|
|
10175
|
+
if (result.status !== 200) {
|
|
10176
|
+
res.status(result.status).json({ error: result.error });
|
|
10177
|
+
return;
|
|
10178
|
+
}
|
|
10179
|
+
broadcastUpdate();
|
|
10180
|
+
if (target.kind === "project") broadcastProject(target.id);
|
|
10181
|
+
else broadcastUpdate();
|
|
10182
|
+
res.json({ moved: id, to: target });
|
|
10183
|
+
} catch (error) {
|
|
10184
|
+
res.status(500).json({ error: error instanceof Error ? error.message : "Failed to move todo" });
|
|
10185
|
+
}
|
|
10186
|
+
});
|
|
10053
10187
|
router.post("/:workspace/:id/unblock", async (req, res) => {
|
|
10054
10188
|
try {
|
|
10055
10189
|
const workspace = getWorkspaceParam(req.params.workspace);
|
|
@@ -10082,18 +10216,9 @@ init_fs();
|
|
|
10082
10216
|
init_paths();
|
|
10083
10217
|
init_slug();
|
|
10084
10218
|
import { Router as Router6 } from "express";
|
|
10085
|
-
import { mkdir as
|
|
10086
|
-
import { resolve as resolve19 } from "path";
|
|
10087
|
-
var
|
|
10088
|
-
function projLock(slug, fn) {
|
|
10089
|
-
const key = `proj:${slug}`;
|
|
10090
|
-
const prev = writeLocks2.get(key) ?? Promise.resolve();
|
|
10091
|
-
const next = prev.then(fn);
|
|
10092
|
-
writeLocks2.set(key, next.then(() => {
|
|
10093
|
-
}, () => {
|
|
10094
|
-
}));
|
|
10095
|
-
return next;
|
|
10096
|
-
}
|
|
10219
|
+
import { mkdir as mkdir3, readFile as readFile14, rename as rename4 } from "fs/promises";
|
|
10220
|
+
import { resolve as resolve19, dirname as dirname5 } from "path";
|
|
10221
|
+
var WORKSPACE_REGEX2 = /^[a-z0-9_][a-z0-9-]*$/;
|
|
10097
10222
|
function touchItem2(item) {
|
|
10098
10223
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
10099
10224
|
if (item.createdAt === null) item.createdAt = now;
|
|
@@ -10112,7 +10237,7 @@ async function projectExists(projectsDir2, slug) {
|
|
|
10112
10237
|
async function ensureProjectTodosDir(projectsDir2, slug) {
|
|
10113
10238
|
const todosDir2 = projectTodosDir(projectsDir2, slug);
|
|
10114
10239
|
try {
|
|
10115
|
-
await
|
|
10240
|
+
await mkdir3(todosDir2, { recursive: false });
|
|
10116
10241
|
} catch (err2) {
|
|
10117
10242
|
const code = err2.code;
|
|
10118
10243
|
if (code === "EEXIST") return;
|
|
@@ -10124,7 +10249,7 @@ async function ensureProjectTodosDir(projectsDir2, slug) {
|
|
|
10124
10249
|
throw err2;
|
|
10125
10250
|
}
|
|
10126
10251
|
try {
|
|
10127
|
-
await
|
|
10252
|
+
await mkdir3(resolve19(todosDir2, "archive"), { recursive: false });
|
|
10128
10253
|
} catch (err2) {
|
|
10129
10254
|
const code = err2.code;
|
|
10130
10255
|
if (code === "EEXIST") return;
|
|
@@ -10139,11 +10264,14 @@ async function ensureProjectTodosDir(projectsDir2, slug) {
|
|
|
10139
10264
|
function notFound(res, slug) {
|
|
10140
10265
|
res.status(404).json({ error: `Project "${slug}" not found` });
|
|
10141
10266
|
}
|
|
10142
|
-
function createProjectTodosRouter(projectsDir2, broadcast) {
|
|
10267
|
+
function createProjectTodosRouter(projectsDir2, broadcast, workspaceTodosDir) {
|
|
10143
10268
|
const router = Router6({ mergeParams: true });
|
|
10144
10269
|
function broadcastUpdate(projectSlug) {
|
|
10145
10270
|
broadcast({ type: "todos-updated", projectSlug, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
10146
10271
|
}
|
|
10272
|
+
function broadcastWorkspace() {
|
|
10273
|
+
broadcast({ type: "todos-updated", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
10274
|
+
}
|
|
10147
10275
|
function validateProjectId(req, res, next) {
|
|
10148
10276
|
const slug = getProjectIdParam(params(req).projectId);
|
|
10149
10277
|
if (!slug || !isValidSlug(slug)) {
|
|
@@ -10691,6 +10819,259 @@ workspace: ${slug}
|
|
|
10691
10819
|
res.status(500).json({ error: error instanceof Error ? error.message : "Failed to unblock todo" });
|
|
10692
10820
|
}
|
|
10693
10821
|
});
|
|
10822
|
+
router.post("/promote", async (req, res) => {
|
|
10823
|
+
try {
|
|
10824
|
+
const slug = getProjectIdParam(params(req).projectId);
|
|
10825
|
+
if (!await projectExists(projectsDir2, slug)) {
|
|
10826
|
+
notFound(res, slug);
|
|
10827
|
+
return;
|
|
10828
|
+
}
|
|
10829
|
+
const { todoIds, mode, target, title, type, priority, keepSource } = req.body ?? {};
|
|
10830
|
+
if (!Array.isArray(todoIds) || todoIds.length === 0) {
|
|
10831
|
+
res.status(400).json({ error: "todoIds (non-empty array of strings) is required" });
|
|
10832
|
+
return;
|
|
10833
|
+
}
|
|
10834
|
+
if (mode !== "new-assignment" && mode !== "to-assignment") {
|
|
10835
|
+
res.status(400).json({ error: 'mode must be "new-assignment" or "to-assignment"' });
|
|
10836
|
+
return;
|
|
10837
|
+
}
|
|
10838
|
+
const result = await projLock(slug, async () => {
|
|
10839
|
+
if (!await projectExists(projectsDir2, slug)) return { gone: true };
|
|
10840
|
+
await ensureProjectTodosDir(projectsDir2, slug);
|
|
10841
|
+
const todosDir2 = projectTodosDir(projectsDir2, slug);
|
|
10842
|
+
const checklist = await readChecklist(todosDir2, slug);
|
|
10843
|
+
const items = [];
|
|
10844
|
+
for (const id of todoIds) {
|
|
10845
|
+
const item = checklist.items.find((i) => i.id === id);
|
|
10846
|
+
if (!item) return { error: `Todo "${id}" not found` };
|
|
10847
|
+
if (item.status === "completed") return { error: `Todo "${id}" is already completed` };
|
|
10848
|
+
items.push(item);
|
|
10849
|
+
}
|
|
10850
|
+
const scopeLabel = `project:${slug}`;
|
|
10851
|
+
const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
10852
|
+
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
10853
|
+
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
10854
|
+
let assignmentRef;
|
|
10855
|
+
let assignmentDir;
|
|
10856
|
+
if (mode === "new-assignment") {
|
|
10857
|
+
const targetProject = target?.project ?? slug;
|
|
10858
|
+
if (!targetProject) return { error: "target.project is required for new-assignment mode" };
|
|
10859
|
+
if (items.length > 1 && !title) return { error: "title is required when promoting multiple todos" };
|
|
10860
|
+
const { createAssignmentCommand: createAssignmentCommand2 } = await Promise.resolve().then(() => (init_create_assignment(), create_assignment_exports));
|
|
10861
|
+
const created = await createAssignmentCommand2(title || items[0].description, {
|
|
10862
|
+
project: targetProject,
|
|
10863
|
+
type,
|
|
10864
|
+
priority,
|
|
10865
|
+
withTodos: true,
|
|
10866
|
+
silent: true
|
|
10867
|
+
});
|
|
10868
|
+
assignmentDir = created.assignmentDir;
|
|
10869
|
+
assignmentRef = `${created.projectSlug}/${created.slug}`;
|
|
10870
|
+
} else {
|
|
10871
|
+
const tg = target?.assignment || "";
|
|
10872
|
+
if (!tg) return { error: "target.assignment is required for to-assignment mode" };
|
|
10873
|
+
if (tg.includes("/")) {
|
|
10874
|
+
const parts = tg.split("/");
|
|
10875
|
+
if (parts.length !== 2) return { error: `Invalid target.assignment "${tg}"` };
|
|
10876
|
+
assignmentDir = resolve19(projectsDir2, parts[0], "assignments", parts[1]);
|
|
10877
|
+
assignmentRef = `${parts[0]}/${parts[1]}`;
|
|
10878
|
+
} else if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(tg)) {
|
|
10879
|
+
assignmentDir = resolve19(assignmentsDirFn(), tg);
|
|
10880
|
+
assignmentRef = tg;
|
|
10881
|
+
} else {
|
|
10882
|
+
return { error: `Invalid target.assignment "${tg}"` };
|
|
10883
|
+
}
|
|
10884
|
+
const assignmentMdPath2 = resolve19(assignmentDir, "assignment.md");
|
|
10885
|
+
if (!await fileExists(assignmentMdPath2)) return { error: `Target assignment not found: ${assignmentMdPath2}` };
|
|
10886
|
+
}
|
|
10887
|
+
const assignmentMdPath = resolve19(assignmentDir, "assignment.md");
|
|
10888
|
+
let content = await readFile14(assignmentMdPath, "utf-8");
|
|
10889
|
+
content = appendTodosToAssignmentBody2(
|
|
10890
|
+
content,
|
|
10891
|
+
items.map((it) => ({
|
|
10892
|
+
description: it.description,
|
|
10893
|
+
trace: `promoted from t:${it.id} in ${scopeLabel}`
|
|
10894
|
+
}))
|
|
10895
|
+
);
|
|
10896
|
+
content = touchAssignmentUpdated2(content, nowTimestamp3());
|
|
10897
|
+
await writeFileForce(assignmentMdPath, content);
|
|
10898
|
+
if (!keepSource) {
|
|
10899
|
+
for (const item of items) {
|
|
10900
|
+
item.status = "completed";
|
|
10901
|
+
item.session = null;
|
|
10902
|
+
touchItem2(item);
|
|
10903
|
+
}
|
|
10904
|
+
checklist.workspace = slug;
|
|
10905
|
+
await writeChecklist(todosDir2, checklist);
|
|
10906
|
+
for (const item of items) {
|
|
10907
|
+
const entry = {
|
|
10908
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10909
|
+
itemIds: [item.id],
|
|
10910
|
+
items: item.description,
|
|
10911
|
+
session: null,
|
|
10912
|
+
branch: item.branch || null,
|
|
10913
|
+
summary: `Promoted to assignment ${assignmentRef}`,
|
|
10914
|
+
blockers: null,
|
|
10915
|
+
status: null
|
|
10916
|
+
};
|
|
10917
|
+
await appendLogEntry2(todosDir2, slug, entry);
|
|
10918
|
+
}
|
|
10919
|
+
}
|
|
10920
|
+
return { assignmentRef, assignmentDir, promoted: items.map((i) => i.id) };
|
|
10921
|
+
});
|
|
10922
|
+
if ("gone" in result) {
|
|
10923
|
+
notFound(res, slug);
|
|
10924
|
+
return;
|
|
10925
|
+
}
|
|
10926
|
+
if ("error" in result) {
|
|
10927
|
+
res.status(400).json({ error: result.error });
|
|
10928
|
+
return;
|
|
10929
|
+
}
|
|
10930
|
+
broadcastUpdate(slug);
|
|
10931
|
+
res.json(result);
|
|
10932
|
+
} catch (error) {
|
|
10933
|
+
if (error.code === "PROJECT_GONE") {
|
|
10934
|
+
notFound(res, getProjectIdParam(params(req).projectId));
|
|
10935
|
+
return;
|
|
10936
|
+
}
|
|
10937
|
+
res.status(500).json({ error: error instanceof Error ? error.message : "Failed to promote todos" });
|
|
10938
|
+
}
|
|
10939
|
+
});
|
|
10940
|
+
router.post("/:id/move", async (req, res) => {
|
|
10941
|
+
try {
|
|
10942
|
+
const sourceSlug = getProjectIdParam(params(req).projectId);
|
|
10943
|
+
const id = params(req).id ?? "";
|
|
10944
|
+
if (!await projectExists(projectsDir2, sourceSlug)) {
|
|
10945
|
+
notFound(res, sourceSlug);
|
|
10946
|
+
return;
|
|
10947
|
+
}
|
|
10948
|
+
const to = req.body?.to;
|
|
10949
|
+
if (!to || typeof to !== "object") {
|
|
10950
|
+
res.status(400).json({ error: "body.to is required" });
|
|
10951
|
+
return;
|
|
10952
|
+
}
|
|
10953
|
+
const targetCount = [Boolean(to.workspace), Boolean(to.project), Boolean(to.global)].filter(Boolean).length;
|
|
10954
|
+
if (targetCount !== 1) {
|
|
10955
|
+
res.status(400).json({ error: "body.to must specify exactly one of workspace, project, or global" });
|
|
10956
|
+
return;
|
|
10957
|
+
}
|
|
10958
|
+
if (to.project && !isValidSlug(to.project)) {
|
|
10959
|
+
res.status(400).json({ error: `Invalid target project slug: "${to.project}"` });
|
|
10960
|
+
return;
|
|
10961
|
+
}
|
|
10962
|
+
if (to.workspace && !WORKSPACE_REGEX2.test(to.workspace)) {
|
|
10963
|
+
res.status(400).json({ error: `Invalid target workspace name: "${to.workspace}"` });
|
|
10964
|
+
return;
|
|
10965
|
+
}
|
|
10966
|
+
let target;
|
|
10967
|
+
if (to.global) {
|
|
10968
|
+
if (!workspaceTodosDir) {
|
|
10969
|
+
res.status(500).json({ error: "Server not configured with workspaceTodosDir; cannot move to global scope" });
|
|
10970
|
+
return;
|
|
10971
|
+
}
|
|
10972
|
+
target = { kind: "workspace", id: "_global", todosPath: workspaceTodosDir, lockKey: "ws:_global" };
|
|
10973
|
+
} else if (to.workspace) {
|
|
10974
|
+
if (!workspaceTodosDir) {
|
|
10975
|
+
res.status(500).json({ error: "Server not configured with workspaceTodosDir; cannot move to workspace scope" });
|
|
10976
|
+
return;
|
|
10977
|
+
}
|
|
10978
|
+
target = { kind: "workspace", id: to.workspace, todosPath: workspaceTodosDir, lockKey: `ws:${to.workspace}` };
|
|
10979
|
+
} else {
|
|
10980
|
+
const tslug = to.project;
|
|
10981
|
+
if (!await projectExists(projectsDir2, tslug)) {
|
|
10982
|
+
res.status(404).json({ error: `Target project "${tslug}" not found` });
|
|
10983
|
+
return;
|
|
10984
|
+
}
|
|
10985
|
+
target = {
|
|
10986
|
+
kind: "project",
|
|
10987
|
+
id: tslug,
|
|
10988
|
+
todosPath: projectTodosDir(projectsDir2, tslug),
|
|
10989
|
+
lockKey: `proj:${tslug}`
|
|
10990
|
+
};
|
|
10991
|
+
}
|
|
10992
|
+
const sourceLockKey = `proj:${sourceSlug}`;
|
|
10993
|
+
if (sourceLockKey === target.lockKey) {
|
|
10994
|
+
res.status(400).json({ error: "cannot move to the same scope" });
|
|
10995
|
+
return;
|
|
10996
|
+
}
|
|
10997
|
+
const result = await withTwoLocks(sourceLockKey, target.lockKey, async () => {
|
|
10998
|
+
if (!await projectExists(projectsDir2, sourceSlug)) return { status: "gone" };
|
|
10999
|
+
if (target.kind === "project" && !await projectExists(projectsDir2, target.id)) {
|
|
11000
|
+
return { status: "targetGone" };
|
|
11001
|
+
}
|
|
11002
|
+
await ensureProjectTodosDir(projectsDir2, sourceSlug);
|
|
11003
|
+
const sourceTodosDir = projectTodosDir(projectsDir2, sourceSlug);
|
|
11004
|
+
const sourceChecklist = await readChecklist(sourceTodosDir, sourceSlug);
|
|
11005
|
+
const targetChecklist = await readChecklist(target.todosPath, target.id);
|
|
11006
|
+
const idx = sourceChecklist.items.findIndex((i) => i.id === id);
|
|
11007
|
+
if (idx === -1) return { status: 404, error: `Todo "${id}" not found` };
|
|
11008
|
+
if (targetChecklist.items.some((i) => i.id === id)) {
|
|
11009
|
+
return { status: 409, error: "id already exists in target" };
|
|
11010
|
+
}
|
|
11011
|
+
const item = sourceChecklist.items[idx];
|
|
11012
|
+
if (item.planDir) {
|
|
11013
|
+
const newPlanDir = todoPlanDir(target.todosPath, target.id, id);
|
|
11014
|
+
if (await fileExists(newPlanDir)) {
|
|
11015
|
+
return { status: 409, error: "plan dir already exists in target" };
|
|
11016
|
+
}
|
|
11017
|
+
await mkdir3(dirname5(newPlanDir), { recursive: true });
|
|
11018
|
+
await rename4(item.planDir, newPlanDir);
|
|
11019
|
+
item.planDir = newPlanDir;
|
|
11020
|
+
}
|
|
11021
|
+
sourceChecklist.items.splice(idx, 1);
|
|
11022
|
+
targetChecklist.items.push(item);
|
|
11023
|
+
sourceChecklist.workspace = sourceSlug;
|
|
11024
|
+
await writeChecklist(sourceTodosDir, sourceChecklist);
|
|
11025
|
+
await writeChecklist(target.todosPath, targetChecklist);
|
|
11026
|
+
const sourceLabel = `project:${sourceSlug}`;
|
|
11027
|
+
const targetLabel = target.kind === "project" ? `project:${target.id}` : target.id === "_global" ? "_global" : `workspace:${target.id}`;
|
|
11028
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
11029
|
+
await appendLogEntry2(sourceTodosDir, sourceSlug, {
|
|
11030
|
+
timestamp: ts,
|
|
11031
|
+
itemIds: [id],
|
|
11032
|
+
items: item.description,
|
|
11033
|
+
session: null,
|
|
11034
|
+
branch: item.branch || null,
|
|
11035
|
+
summary: `Moved to ${targetLabel}`,
|
|
11036
|
+
blockers: null,
|
|
11037
|
+
status: null
|
|
11038
|
+
});
|
|
11039
|
+
await appendLogEntry2(target.todosPath, target.id, {
|
|
11040
|
+
timestamp: ts,
|
|
11041
|
+
itemIds: [id],
|
|
11042
|
+
items: item.description,
|
|
11043
|
+
session: null,
|
|
11044
|
+
branch: item.branch || null,
|
|
11045
|
+
summary: `Moved from ${sourceLabel}`,
|
|
11046
|
+
blockers: null,
|
|
11047
|
+
status: null
|
|
11048
|
+
});
|
|
11049
|
+
return { status: 200, item };
|
|
11050
|
+
});
|
|
11051
|
+
if (result.status === "gone") {
|
|
11052
|
+
notFound(res, sourceSlug);
|
|
11053
|
+
return;
|
|
11054
|
+
}
|
|
11055
|
+
if (result.status === "targetGone") {
|
|
11056
|
+
res.status(404).json({ error: `Target project "${target.id}" not found` });
|
|
11057
|
+
return;
|
|
11058
|
+
}
|
|
11059
|
+
if (result.status !== 200) {
|
|
11060
|
+
res.status(result.status).json({ error: result.error });
|
|
11061
|
+
return;
|
|
11062
|
+
}
|
|
11063
|
+
broadcastUpdate(sourceSlug);
|
|
11064
|
+
if (target.kind === "project") broadcastUpdate(target.id);
|
|
11065
|
+
else broadcastWorkspace();
|
|
11066
|
+
res.json({ moved: id, to: target });
|
|
11067
|
+
} catch (error) {
|
|
11068
|
+
if (error.code === "PROJECT_GONE") {
|
|
11069
|
+
notFound(res, getProjectIdParam(params(req).projectId));
|
|
11070
|
+
return;
|
|
11071
|
+
}
|
|
11072
|
+
res.status(500).json({ error: error instanceof Error ? error.message : "Failed to move todo" });
|
|
11073
|
+
}
|
|
11074
|
+
});
|
|
10694
11075
|
return router;
|
|
10695
11076
|
}
|
|
10696
11077
|
|
|
@@ -10704,7 +11085,7 @@ init_fs();
|
|
|
10704
11085
|
init_config2();
|
|
10705
11086
|
import { execFile as execFile2 } from "child_process";
|
|
10706
11087
|
import { promisify as promisify2 } from "util";
|
|
10707
|
-
import { cp, mkdtemp, rm as rm2, readFile as readFile15, writeFile as writeFile4, unlink as unlink3, stat, open, rename as
|
|
11088
|
+
import { cp, mkdtemp, rm as rm2, readFile as readFile15, writeFile as writeFile4, unlink as unlink3, stat, open, rename as rename5 } from "fs/promises";
|
|
10708
11089
|
import { resolve as resolve20, join as join2 } from "path";
|
|
10709
11090
|
import { tmpdir } from "os";
|
|
10710
11091
|
var exec2 = promisify2(execFile2);
|
|
@@ -10916,7 +11297,7 @@ async function safeRestoreCategory(localPath, repoSrcPath, isFile) {
|
|
|
10916
11297
|
const localExistsBefore = await fileExists(localPath);
|
|
10917
11298
|
if (backupExistsBefore) {
|
|
10918
11299
|
if (!localExistsBefore) {
|
|
10919
|
-
await
|
|
11300
|
+
await rename5(backupPath, localPath);
|
|
10920
11301
|
} else {
|
|
10921
11302
|
throw new Error(
|
|
10922
11303
|
`Cannot restore "${localPath}": a stale crash-recovery backup exists at ${backupPath} while the current path also exists. Inspect both and remove the one you don't need, then retry.`
|
|
@@ -10928,15 +11309,15 @@ async function safeRestoreCategory(localPath, repoSrcPath, isFile) {
|
|
|
10928
11309
|
await cp(repoSrcPath, stagingPath, { recursive: true, force: true });
|
|
10929
11310
|
const localExists = await fileExists(localPath);
|
|
10930
11311
|
if (localExists) {
|
|
10931
|
-
await
|
|
11312
|
+
await rename5(localPath, backupPath);
|
|
10932
11313
|
localMovedAside = true;
|
|
10933
11314
|
}
|
|
10934
|
-
await
|
|
11315
|
+
await rename5(stagingPath, localPath);
|
|
10935
11316
|
await rm2(backupPath, { recursive: true, force: true }).catch(() => {
|
|
10936
11317
|
});
|
|
10937
11318
|
} catch (err2) {
|
|
10938
11319
|
if (localMovedAside && await fileExists(backupPath)) {
|
|
10939
|
-
await
|
|
11320
|
+
await rename5(backupPath, localPath).catch(() => {
|
|
10940
11321
|
});
|
|
10941
11322
|
}
|
|
10942
11323
|
await rm2(stagingPath, { recursive: true, force: true }).catch(() => {
|
|
@@ -11675,8 +12056,8 @@ function createDashboardServer(options) {
|
|
|
11675
12056
|
app.use("/api/servers", createServersRouter(serversDir2, projectsDir2, assignmentsDir2));
|
|
11676
12057
|
app.use("/api/agent-sessions", createAgentSessionsRouter(projectsDir2, broadcast, assignmentsDir2));
|
|
11677
12058
|
app.use("/api/playbooks", createPlaybooksRouter(playbooksDir3));
|
|
11678
|
-
app.use("/api/todos", createTodosRouter(todosDir2, broadcast));
|
|
11679
|
-
app.use("/api/projects/:projectId/todos", createProjectTodosRouter(projectsDir2, broadcast));
|
|
12059
|
+
app.use("/api/todos", createTodosRouter(todosDir2, broadcast, projectsDir2));
|
|
12060
|
+
app.use("/api/projects/:projectId/todos", createProjectTodosRouter(projectsDir2, broadcast, todosDir2));
|
|
11680
12061
|
app.use("/api/backup", createBackupRouter());
|
|
11681
12062
|
if (serveStaticUi && dashboardDistPath) {
|
|
11682
12063
|
const sendOpts = { dotfiles: "allow" };
|
|
@@ -11822,7 +12203,7 @@ async function dashboardCommand(options) {
|
|
|
11822
12203
|
port = availablePort;
|
|
11823
12204
|
}
|
|
11824
12205
|
const thisFile = fileURLToPath2(import.meta.url);
|
|
11825
|
-
const packageRoot = resolve22(
|
|
12206
|
+
const packageRoot = resolve22(dirname6(thisFile), "..");
|
|
11826
12207
|
const dashboardDist = resolve22(packageRoot, "dashboard", "dist");
|
|
11827
12208
|
const server = createDashboardServer({
|
|
11828
12209
|
port,
|
|
@@ -12051,14 +12432,14 @@ import {
|
|
|
12051
12432
|
} from "fs/promises";
|
|
12052
12433
|
import { existsSync } from "fs";
|
|
12053
12434
|
import { homedir as homedir2 } from "os";
|
|
12054
|
-
import { basename, dirname as
|
|
12435
|
+
import { basename, dirname as dirname8, isAbsolute as isAbsolute2, relative as relative2, resolve as resolve25 } from "path";
|
|
12055
12436
|
|
|
12056
12437
|
// src/utils/package-root.ts
|
|
12057
12438
|
init_fs();
|
|
12058
|
-
import { dirname as
|
|
12439
|
+
import { dirname as dirname7, resolve as resolve24 } from "path";
|
|
12059
12440
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
12060
12441
|
async function findPackageRoot(expectedRelativePath) {
|
|
12061
|
-
let currentDir =
|
|
12442
|
+
let currentDir = dirname7(fileURLToPath3(import.meta.url));
|
|
12062
12443
|
while (true) {
|
|
12063
12444
|
const candidate = resolve24(currentDir, expectedRelativePath);
|
|
12064
12445
|
if (await fileExists(candidate)) {
|
|
@@ -12150,7 +12531,7 @@ async function getInstallStatus(targetDir, pluginKind) {
|
|
|
12150
12531
|
const info = await lstat(targetDir);
|
|
12151
12532
|
if (info.isSymbolicLink()) {
|
|
12152
12533
|
const symlinkTarget = await readlink(targetDir);
|
|
12153
|
-
const resolvedTarget = resolve25(
|
|
12534
|
+
const resolvedTarget = resolve25(dirname8(targetDir), symlinkTarget);
|
|
12154
12535
|
const manifestName2 = await readPluginManifestName(resolvedTarget, pluginKind);
|
|
12155
12536
|
return {
|
|
12156
12537
|
exists: true,
|
|
@@ -12187,15 +12568,15 @@ async function writeInstallMetadata(targetDir, pluginKind, installMode, packageM
|
|
|
12187
12568
|
);
|
|
12188
12569
|
}
|
|
12189
12570
|
async function installCopy(paths, pluginKind) {
|
|
12190
|
-
await ensureDir(
|
|
12571
|
+
await ensureDir(dirname8(paths.targetDir));
|
|
12191
12572
|
await cp2(paths.sourceDir, paths.targetDir, { recursive: true });
|
|
12192
12573
|
const packageManifest = await readPackageManifest(paths.packageRoot);
|
|
12193
12574
|
await writeInstallMetadata(paths.targetDir, pluginKind, "copy", packageManifest);
|
|
12194
12575
|
}
|
|
12195
12576
|
async function installLink(paths) {
|
|
12196
|
-
await ensureDir(
|
|
12577
|
+
await ensureDir(dirname8(paths.targetDir));
|
|
12197
12578
|
await rm3(paths.targetDir, { recursive: true, force: true });
|
|
12198
|
-
await ensureDir(
|
|
12579
|
+
await ensureDir(dirname8(paths.targetDir));
|
|
12199
12580
|
await symlink(resolve25(paths.sourceDir), paths.targetDir, "dir");
|
|
12200
12581
|
}
|
|
12201
12582
|
async function removeInstallMarker(targetDir) {
|
|
@@ -12250,7 +12631,7 @@ async function readClaudeMarketplaceFile(manifestPath) {
|
|
|
12250
12631
|
};
|
|
12251
12632
|
}
|
|
12252
12633
|
async function writeClaudeMarketplaceFile(manifestPath, marketplace) {
|
|
12253
|
-
await ensureDir(
|
|
12634
|
+
await ensureDir(dirname8(manifestPath));
|
|
12254
12635
|
await writeFile6(manifestPath, `${JSON.stringify(marketplace, null, 2)}
|
|
12255
12636
|
`, "utf-8");
|
|
12256
12637
|
}
|
|
@@ -12398,7 +12779,7 @@ async function registerKnownClaudeMarketplace(name, rootDir) {
|
|
|
12398
12779
|
};
|
|
12399
12780
|
existing[name].lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
|
|
12400
12781
|
existing[name].autoUpdate = true;
|
|
12401
|
-
await ensureDir(
|
|
12782
|
+
await ensureDir(dirname8(manifestPath));
|
|
12402
12783
|
await writeFile6(manifestPath, `${JSON.stringify(existing, null, 2)}
|
|
12403
12784
|
`, "utf-8");
|
|
12404
12785
|
}
|
|
@@ -12433,11 +12814,11 @@ async function ensureClaudeUserMarketplace() {
|
|
|
12433
12814
|
}
|
|
12434
12815
|
async function detectClaudeMarketplaceForTarget(targetDir) {
|
|
12435
12816
|
const normalizedTargetDir = normalizeAbsoluteInstallPath(targetDir, "Claude plugin target");
|
|
12436
|
-
const pluginsDir =
|
|
12817
|
+
const pluginsDir = dirname8(normalizedTargetDir);
|
|
12437
12818
|
if (basename(pluginsDir) !== "plugins") {
|
|
12438
12819
|
return null;
|
|
12439
12820
|
}
|
|
12440
|
-
const rootDir =
|
|
12821
|
+
const rootDir = dirname8(pluginsDir);
|
|
12441
12822
|
const manifestPath = resolve25(rootDir, ".claude-plugin", "marketplace.json");
|
|
12442
12823
|
if (!await fileExists(manifestPath)) {
|
|
12443
12824
|
return null;
|
|
@@ -12603,7 +12984,7 @@ async function installManagedPlugin(options) {
|
|
|
12603
12984
|
};
|
|
12604
12985
|
}
|
|
12605
12986
|
function buildMarketplaceSourcePath(pluginTargetDir, marketplacePath) {
|
|
12606
|
-
const relPath = relative2(
|
|
12987
|
+
const relPath = relative2(dirname8(marketplacePath), pluginTargetDir).replaceAll("\\", "/");
|
|
12607
12988
|
if (relPath === "") {
|
|
12608
12989
|
return ".";
|
|
12609
12990
|
}
|
|
@@ -12640,7 +13021,7 @@ async function readMarketplaceFile(marketplacePath) {
|
|
|
12640
13021
|
};
|
|
12641
13022
|
}
|
|
12642
13023
|
async function writeMarketplaceFile(marketplacePath, marketplace) {
|
|
12643
|
-
await ensureDir(
|
|
13024
|
+
await ensureDir(dirname8(marketplacePath));
|
|
12644
13025
|
await writeFile6(marketplacePath, `${JSON.stringify(marketplace, null, 2)}
|
|
12645
13026
|
`, "utf-8");
|
|
12646
13027
|
}
|
|
@@ -12867,8 +13248,8 @@ async function textPrompt(question, defaultValue) {
|
|
|
12867
13248
|
|
|
12868
13249
|
// src/utils/install-skills.ts
|
|
12869
13250
|
init_fs();
|
|
12870
|
-
import { readFile as readFile17, readdir as readdir11, mkdir as
|
|
12871
|
-
import { dirname as
|
|
13251
|
+
import { readFile as readFile17, readdir as readdir11, mkdir as mkdir4, copyFile, rm as rm4 } from "fs/promises";
|
|
13252
|
+
import { dirname as dirname9, resolve as resolve26, relative as relative3, join as join3 } from "path";
|
|
12872
13253
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
12873
13254
|
import { homedir as homedir3 } from "os";
|
|
12874
13255
|
var REQUIRED_SKILLS = [
|
|
@@ -12880,7 +13261,7 @@ var REQUIRED_SKILLS = [
|
|
|
12880
13261
|
"create-project"
|
|
12881
13262
|
];
|
|
12882
13263
|
function getVendoredSkillsDir() {
|
|
12883
|
-
const here =
|
|
13264
|
+
const here = dirname9(fileURLToPath4(import.meta.url));
|
|
12884
13265
|
return resolve26(here, "..", "vendor", "syntaur-skills", "skills");
|
|
12885
13266
|
}
|
|
12886
13267
|
function defaultSkillTargetDir(target) {
|
|
@@ -12913,7 +13294,7 @@ async function filesEqual(a, b) {
|
|
|
12913
13294
|
}
|
|
12914
13295
|
}
|
|
12915
13296
|
async function copyDir(srcDir, destDir) {
|
|
12916
|
-
await
|
|
13297
|
+
await mkdir4(destDir, { recursive: true });
|
|
12917
13298
|
const entries = await readdir11(srcDir, { withFileTypes: true });
|
|
12918
13299
|
for (const entry of entries) {
|
|
12919
13300
|
const src = join3(srcDir, entry.name);
|
|
@@ -12947,7 +13328,7 @@ async function installSkills(options) {
|
|
|
12947
13328
|
);
|
|
12948
13329
|
}
|
|
12949
13330
|
const results = [];
|
|
12950
|
-
await
|
|
13331
|
+
await mkdir4(targetRoot, { recursive: true });
|
|
12951
13332
|
for (const skill of REQUIRED_SKILLS) {
|
|
12952
13333
|
const srcDir = join3(source, skill);
|
|
12953
13334
|
const destDir = join3(targetRoot, skill);
|
|
@@ -13134,7 +13515,7 @@ async function installPluginCommand(options) {
|
|
|
13134
13515
|
init_paths();
|
|
13135
13516
|
init_fs();
|
|
13136
13517
|
import { readFile as readFile19, writeFile as writeFile8, copyFile as copyFile2, rm as rm5, stat as stat3, symlink as symlink2, unlink as unlink6, lstat as lstat2 } from "fs/promises";
|
|
13137
|
-
import { resolve as resolve28, dirname as
|
|
13518
|
+
import { resolve as resolve28, dirname as dirname11 } from "path";
|
|
13138
13519
|
import { homedir as homedir4 } from "os";
|
|
13139
13520
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
13140
13521
|
|
|
@@ -13142,7 +13523,7 @@ import { fileURLToPath as fileURLToPath5 } from "url";
|
|
|
13142
13523
|
init_paths();
|
|
13143
13524
|
init_fs();
|
|
13144
13525
|
import { readFile as readFile18, writeFile as writeFile7 } from "fs/promises";
|
|
13145
|
-
import { resolve as resolve27, dirname as
|
|
13526
|
+
import { resolve as resolve27, dirname as dirname10 } from "path";
|
|
13146
13527
|
import { spawnSync } from "child_process";
|
|
13147
13528
|
import { checkbox, input as input2, confirm } from "@inquirer/prompts";
|
|
13148
13529
|
var AVAILABLE_SEGMENTS = [
|
|
@@ -13263,7 +13644,7 @@ function renderPreview(config, statuslineScript, cwd) {
|
|
|
13263
13644
|
env: {
|
|
13264
13645
|
...process.env,
|
|
13265
13646
|
// Force the child to pick up the freshly-written config from install root.
|
|
13266
|
-
HOME:
|
|
13647
|
+
HOME: dirname10(dirname10(statuslineScript))
|
|
13267
13648
|
}
|
|
13268
13649
|
});
|
|
13269
13650
|
if (res.status !== 0) return null;
|
|
@@ -13314,7 +13695,7 @@ async function configureStatuslineCommand(options = {}) {
|
|
|
13314
13695
|
console.log("(preview mode \u2014 config NOT written)");
|
|
13315
13696
|
return;
|
|
13316
13697
|
}
|
|
13317
|
-
await ensureDir(
|
|
13698
|
+
await ensureDir(dirname10(configPath));
|
|
13318
13699
|
await writeFile7(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
13319
13700
|
console.log("Wrote statusline config:");
|
|
13320
13701
|
console.log(` path: ${configPath}`);
|
|
@@ -13341,7 +13722,7 @@ async function configureStatuslineCommand(options = {}) {
|
|
|
13341
13722
|
async function writeDefaultConfigIfMissing(installRoot) {
|
|
13342
13723
|
const path = getConfigPath(installRoot);
|
|
13343
13724
|
if (await fileExists(path)) return;
|
|
13344
|
-
await ensureDir(
|
|
13725
|
+
await ensureDir(dirname10(path));
|
|
13345
13726
|
const defaultConfig = {
|
|
13346
13727
|
segments: ["git", "assignment", "session"],
|
|
13347
13728
|
separator: " \xB7 "
|
|
@@ -13351,7 +13732,7 @@ async function writeDefaultConfigIfMissing(installRoot) {
|
|
|
13351
13732
|
|
|
13352
13733
|
// src/commands/install-statusline.ts
|
|
13353
13734
|
function getPackageStatuslineSource() {
|
|
13354
|
-
const here =
|
|
13735
|
+
const here = dirname11(fileURLToPath5(import.meta.url));
|
|
13355
13736
|
return resolve28(here, "..", "statusline", "statusline.sh");
|
|
13356
13737
|
}
|
|
13357
13738
|
async function readSettingsJson(settingsPath) {
|
|
@@ -13368,7 +13749,7 @@ async function readSettingsJson(settingsPath) {
|
|
|
13368
13749
|
}
|
|
13369
13750
|
}
|
|
13370
13751
|
async function writeSettingsJson(settingsPath, data) {
|
|
13371
|
-
await ensureDir(
|
|
13752
|
+
await ensureDir(dirname11(settingsPath));
|
|
13372
13753
|
await writeFile8(settingsPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
13373
13754
|
}
|
|
13374
13755
|
async function resolveMode(mode, existingCommand, ourCommand) {
|
|
@@ -13403,7 +13784,7 @@ function extractExistingCommand(settings) {
|
|
|
13403
13784
|
};
|
|
13404
13785
|
}
|
|
13405
13786
|
async function backupSettings(settingsSnapshot, backupPath) {
|
|
13406
|
-
await ensureDir(
|
|
13787
|
+
await ensureDir(dirname11(backupPath));
|
|
13407
13788
|
await writeFile8(
|
|
13408
13789
|
backupPath,
|
|
13409
13790
|
JSON.stringify(
|
|
@@ -13420,7 +13801,7 @@ async function backupSettings(settingsSnapshot, backupPath) {
|
|
|
13420
13801
|
);
|
|
13421
13802
|
}
|
|
13422
13803
|
async function installScript(sourceScript, destScript, link) {
|
|
13423
|
-
await ensureDir(
|
|
13804
|
+
await ensureDir(dirname11(destScript));
|
|
13424
13805
|
try {
|
|
13425
13806
|
const s = await lstat2(destScript);
|
|
13426
13807
|
if (s.isSymbolicLink() || s.isFile()) {
|
|
@@ -13488,7 +13869,7 @@ exec ${existingCommand}
|
|
|
13488
13869
|
wrapTarget = wrapperPath;
|
|
13489
13870
|
}
|
|
13490
13871
|
}
|
|
13491
|
-
await ensureDir(
|
|
13872
|
+
await ensureDir(dirname11(confPath));
|
|
13492
13873
|
await writeFile8(
|
|
13493
13874
|
confPath,
|
|
13494
13875
|
wrapTarget ? `# Wrap target \u2014 the command below is invoked with the same stdin; its
|
|
@@ -14408,6 +14789,19 @@ async function disablePlaybookCommand(slug) {
|
|
|
14408
14789
|
}
|
|
14409
14790
|
}
|
|
14410
14791
|
|
|
14792
|
+
// src/commands/regen-playbook-manifest.ts
|
|
14793
|
+
init_paths();
|
|
14794
|
+
init_playbooks();
|
|
14795
|
+
init_fs();
|
|
14796
|
+
async function regenPlaybookManifestCommand() {
|
|
14797
|
+
const dir = playbooksDir();
|
|
14798
|
+
if (!await fileExists(dir)) {
|
|
14799
|
+
throw new Error(`Playbooks directory not found at ${dir}. Run "syntaur init" first.`);
|
|
14800
|
+
}
|
|
14801
|
+
await rebuildPlaybookManifest(dir);
|
|
14802
|
+
console.log(`Rebuilt playbook manifest at ${dir}/manifest.md`);
|
|
14803
|
+
}
|
|
14804
|
+
|
|
14411
14805
|
// src/commands/todo.ts
|
|
14412
14806
|
init_paths();
|
|
14413
14807
|
init_parser2();
|
|
@@ -14417,7 +14811,7 @@ init_slug();
|
|
|
14417
14811
|
import { Command } from "commander";
|
|
14418
14812
|
import { readFile as readFile23 } from "fs/promises";
|
|
14419
14813
|
import { resolve as resolve36 } from "path";
|
|
14420
|
-
var
|
|
14814
|
+
var WORKSPACE_REGEX3 = /^[a-z0-9_][a-z0-9-]*$/;
|
|
14421
14815
|
async function resolveScope(options) {
|
|
14422
14816
|
const flagCount = [Boolean(options.project), Boolean(options.workspace), Boolean(options.global)].filter(Boolean).length;
|
|
14423
14817
|
if (flagCount > 1) {
|
|
@@ -14435,7 +14829,7 @@ async function resolveScope(options) {
|
|
|
14435
14829
|
return { kind: "project", id: options.project, todosPath: projectTodosDir(config.defaultProjectDir, options.project) };
|
|
14436
14830
|
}
|
|
14437
14831
|
if (options.workspace) {
|
|
14438
|
-
if (!
|
|
14832
|
+
if (!WORKSPACE_REGEX3.test(options.workspace)) {
|
|
14439
14833
|
throw new Error(`Invalid workspace name: "${options.workspace}". Use lowercase letters, numbers, hyphens, and underscores.`);
|
|
14440
14834
|
}
|
|
14441
14835
|
return { kind: "workspace", id: options.workspace, todosPath: todosDir() };
|
|
@@ -14886,7 +15280,7 @@ async function promoteTodos(ids, options) {
|
|
|
14886
15280
|
return;
|
|
14887
15281
|
}
|
|
14888
15282
|
const target = options.toAssignment;
|
|
14889
|
-
const { resolve:
|
|
15283
|
+
const { resolve: resolvePath2 } = await import("path");
|
|
14890
15284
|
const { readConfig: readConfig3 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
14891
15285
|
const { assignmentsDir: assignmentsDirFn } = await Promise.resolve().then(() => (init_paths(), paths_exports));
|
|
14892
15286
|
const config = await readConfig3();
|
|
@@ -14897,16 +15291,16 @@ async function promoteTodos(ids, options) {
|
|
|
14897
15291
|
if (parts.length !== 2 || !isValidSlug(parts[0]) || !isValidSlug(parts[1])) {
|
|
14898
15292
|
throw new Error(`Invalid --to-assignment target "${target}". Use <project>/<slug> or a bare UUID.`);
|
|
14899
15293
|
}
|
|
14900
|
-
assignmentDir =
|
|
15294
|
+
assignmentDir = resolvePath2(config.defaultProjectDir, parts[0], "assignments", parts[1]);
|
|
14901
15295
|
displayRef = `${parts[0]}/${parts[1]}`;
|
|
14902
15296
|
} else if (UUID_REGEX.test(target)) {
|
|
14903
|
-
assignmentDir =
|
|
15297
|
+
assignmentDir = resolvePath2(assignmentsDirFn(), target);
|
|
14904
15298
|
displayRef = target;
|
|
14905
15299
|
} else {
|
|
14906
15300
|
throw new Error(`Invalid --to-assignment target "${target}". Use <project>/<slug> or a bare UUID.`);
|
|
14907
15301
|
}
|
|
14908
15302
|
const { fileExists: fileExists2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
14909
|
-
const assignmentMdPath =
|
|
15303
|
+
const assignmentMdPath = resolvePath2(assignmentDir, "assignment.md");
|
|
14910
15304
|
if (!await fileExists2(assignmentMdPath)) {
|
|
14911
15305
|
throw new Error(`Target assignment not found: ${assignmentMdPath}`);
|
|
14912
15306
|
}
|
|
@@ -14922,12 +15316,12 @@ function describeScope(scope) {
|
|
|
14922
15316
|
return `workspace:${scope.id}`;
|
|
14923
15317
|
}
|
|
14924
15318
|
async function injectPromotedTodos(assignmentDir, todos, scopeLabel) {
|
|
14925
|
-
const { resolve:
|
|
15319
|
+
const { resolve: resolvePath2 } = await import("path");
|
|
14926
15320
|
const { readFile: readFile32 } = await import("fs/promises");
|
|
14927
15321
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
14928
15322
|
const { appendTodosToAssignmentBody: appendTodosToAssignmentBody2, touchAssignmentUpdated: touchAssignmentUpdated2 } = await Promise.resolve().then(() => (init_assignment_todos(), assignment_todos_exports));
|
|
14929
15323
|
const { nowTimestamp: nowTimestamp3 } = await Promise.resolve().then(() => (init_timestamp(), timestamp_exports));
|
|
14930
|
-
const assignmentMdPath =
|
|
15324
|
+
const assignmentMdPath = resolvePath2(assignmentDir, "assignment.md");
|
|
14931
15325
|
let content = await readFile32(assignmentMdPath, "utf-8");
|
|
14932
15326
|
content = appendTodosToAssignmentBody2(
|
|
14933
15327
|
content,
|
|
@@ -15014,6 +15408,90 @@ ${item.description}
|
|
|
15014
15408
|
process.exit(1);
|
|
15015
15409
|
}
|
|
15016
15410
|
});
|
|
15411
|
+
todoCommand.command("move").description("Move a todo between scopes (workspace \u2194 project \u2194 global) without converting it").argument("<id>", "Todo short ID").option("--to-workspace <slug>", "Target workspace slug").option("--to-project <slug>", "Target project slug").option("--to-global", "Move to global todos").option("--workspace <slug>", "Source workspace slug").option("--project <slug>", "Source project slug (mutually exclusive with --workspace/--global)").option("--global", "Source: global todos").action(async (id, options) => {
|
|
15412
|
+
try {
|
|
15413
|
+
await moveTodo(id, options);
|
|
15414
|
+
} catch (error) {
|
|
15415
|
+
console.error("Error:", error instanceof Error ? error.message : String(error));
|
|
15416
|
+
process.exit(1);
|
|
15417
|
+
}
|
|
15418
|
+
});
|
|
15419
|
+
async function moveTodo(id, options) {
|
|
15420
|
+
const targetCount = [
|
|
15421
|
+
Boolean(options.toWorkspace),
|
|
15422
|
+
Boolean(options.toProject),
|
|
15423
|
+
Boolean(options.toGlobal)
|
|
15424
|
+
].filter(Boolean).length;
|
|
15425
|
+
if (targetCount !== 1) {
|
|
15426
|
+
throw new Error("Specify exactly one of --to-workspace <slug>, --to-project <slug>, --to-global.");
|
|
15427
|
+
}
|
|
15428
|
+
const sourceScope = await resolveScope({
|
|
15429
|
+
project: options.project,
|
|
15430
|
+
workspace: options.workspace,
|
|
15431
|
+
global: options.global
|
|
15432
|
+
});
|
|
15433
|
+
const targetScope = await resolveScope({
|
|
15434
|
+
project: options.toProject,
|
|
15435
|
+
workspace: options.toWorkspace,
|
|
15436
|
+
global: options.toGlobal
|
|
15437
|
+
});
|
|
15438
|
+
if (sourceScope.kind === targetScope.kind && sourceScope.id === targetScope.id) {
|
|
15439
|
+
throw new Error("Source and target scopes are the same; nothing to move.");
|
|
15440
|
+
}
|
|
15441
|
+
const sourceChecklist = await readChecklist(sourceScope.todosPath, sourceScope.id);
|
|
15442
|
+
const targetChecklist = sourceScope.todosPath === targetScope.todosPath && sourceScope.id === targetScope.id ? sourceChecklist : await readChecklist(targetScope.todosPath, targetScope.id);
|
|
15443
|
+
const idx = sourceChecklist.items.findIndex((i) => i.id === id);
|
|
15444
|
+
if (idx === -1) {
|
|
15445
|
+
throw new Error(`Todo [t:${id}] not found in scope ${describeScope(sourceScope)}.`);
|
|
15446
|
+
}
|
|
15447
|
+
const item = sourceChecklist.items[idx];
|
|
15448
|
+
if (targetChecklist.items.some((i) => i.id === id)) {
|
|
15449
|
+
throw new Error(`Todo id [t:${id}] already exists in target scope ${describeScope(targetScope)}; refusing to move (collision).`);
|
|
15450
|
+
}
|
|
15451
|
+
if (item.planDir) {
|
|
15452
|
+
const newPlanDir = todoPlanDir(targetScope.todosPath, targetScope.id, id);
|
|
15453
|
+
if (await fileExists(newPlanDir)) {
|
|
15454
|
+
throw new Error(`Plan directory already exists at target: ${newPlanDir}; refusing to move.`);
|
|
15455
|
+
}
|
|
15456
|
+
const { rename: rename6, mkdir: mkdir7 } = await import("fs/promises");
|
|
15457
|
+
const { dirname: dirname16 } = await import("path");
|
|
15458
|
+
await mkdir7(dirname16(newPlanDir), { recursive: true });
|
|
15459
|
+
await rename6(item.planDir, newPlanDir);
|
|
15460
|
+
item.planDir = newPlanDir;
|
|
15461
|
+
}
|
|
15462
|
+
sourceChecklist.items.splice(idx, 1);
|
|
15463
|
+
targetChecklist.items.push(item);
|
|
15464
|
+
await writeChecklist(sourceScope.todosPath, sourceChecklist);
|
|
15465
|
+
if (targetChecklist !== sourceChecklist) {
|
|
15466
|
+
await writeChecklist(targetScope.todosPath, targetChecklist);
|
|
15467
|
+
}
|
|
15468
|
+
const sourceLabel = describeScope(sourceScope);
|
|
15469
|
+
const targetLabel = describeScope(targetScope);
|
|
15470
|
+
const ts = nowISO();
|
|
15471
|
+
const sourceEntry = {
|
|
15472
|
+
timestamp: ts,
|
|
15473
|
+
itemIds: [id],
|
|
15474
|
+
items: item.description,
|
|
15475
|
+
session: null,
|
|
15476
|
+
branch: item.branch || null,
|
|
15477
|
+
summary: `Moved to ${targetLabel}`,
|
|
15478
|
+
blockers: null,
|
|
15479
|
+
status: null
|
|
15480
|
+
};
|
|
15481
|
+
const targetEntry = {
|
|
15482
|
+
timestamp: ts,
|
|
15483
|
+
itemIds: [id],
|
|
15484
|
+
items: item.description,
|
|
15485
|
+
session: null,
|
|
15486
|
+
branch: item.branch || null,
|
|
15487
|
+
summary: `Moved from ${sourceLabel}`,
|
|
15488
|
+
blockers: null,
|
|
15489
|
+
status: null
|
|
15490
|
+
};
|
|
15491
|
+
await appendLogEntry2(sourceScope.todosPath, sourceScope.id, sourceEntry);
|
|
15492
|
+
await appendLogEntry2(targetScope.todosPath, targetScope.id, targetEntry);
|
|
15493
|
+
console.log(`Moved [t:${id}] from ${sourceLabel} to ${targetLabel}`);
|
|
15494
|
+
}
|
|
15017
15495
|
|
|
15018
15496
|
// src/commands/backup.ts
|
|
15019
15497
|
init_config2();
|
|
@@ -15098,7 +15576,7 @@ import { Command as Command3 } from "commander";
|
|
|
15098
15576
|
// src/utils/doctor/index.ts
|
|
15099
15577
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
15100
15578
|
import { readFile as readFile27 } from "fs/promises";
|
|
15101
|
-
import { dirname as
|
|
15579
|
+
import { dirname as dirname13, join as join5 } from "path";
|
|
15102
15580
|
|
|
15103
15581
|
// src/utils/doctor/context.ts
|
|
15104
15582
|
init_config2();
|
|
@@ -15146,7 +15624,7 @@ init_paths();
|
|
|
15146
15624
|
import { resolve as resolve38, isAbsolute as isAbsolute5 } from "path";
|
|
15147
15625
|
import { readFile as readFile24, stat as stat4 } from "fs/promises";
|
|
15148
15626
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
15149
|
-
import { dirname as
|
|
15627
|
+
import { dirname as dirname12, join as join4 } from "path";
|
|
15150
15628
|
var CATEGORY = "env";
|
|
15151
15629
|
var syntaurRootExists = {
|
|
15152
15630
|
id: "env.syntaur-root-exists",
|
|
@@ -15477,14 +15955,14 @@ async function readLocalVersion() {
|
|
|
15477
15955
|
async function readLocalPkg() {
|
|
15478
15956
|
try {
|
|
15479
15957
|
const here = fileURLToPath6(import.meta.url);
|
|
15480
|
-
let dir =
|
|
15958
|
+
let dir = dirname12(here);
|
|
15481
15959
|
for (let i = 0; i < 6; i++) {
|
|
15482
15960
|
const candidate = join4(dir, "package.json");
|
|
15483
15961
|
try {
|
|
15484
15962
|
const text = await readFile24(candidate, "utf-8");
|
|
15485
15963
|
return JSON.parse(text);
|
|
15486
15964
|
} catch {
|
|
15487
|
-
dir =
|
|
15965
|
+
dir = dirname12(dir);
|
|
15488
15966
|
}
|
|
15489
15967
|
}
|
|
15490
15968
|
return null;
|
|
@@ -16885,14 +17363,14 @@ async function finalize(checks) {
|
|
|
16885
17363
|
async function readVersion() {
|
|
16886
17364
|
try {
|
|
16887
17365
|
const here = fileURLToPath7(import.meta.url);
|
|
16888
|
-
let dir =
|
|
17366
|
+
let dir = dirname13(here);
|
|
16889
17367
|
for (let i = 0; i < 6; i++) {
|
|
16890
17368
|
try {
|
|
16891
17369
|
const raw = await readFile27(join5(dir, "package.json"), "utf-8");
|
|
16892
17370
|
const parsed = JSON.parse(raw);
|
|
16893
17371
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
16894
17372
|
} catch {
|
|
16895
|
-
dir =
|
|
17373
|
+
dir = dirname13(dir);
|
|
16896
17374
|
}
|
|
16897
17375
|
}
|
|
16898
17376
|
return null;
|
|
@@ -17408,18 +17886,18 @@ init_paths();
|
|
|
17408
17886
|
init_fs();
|
|
17409
17887
|
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
17410
17888
|
import { readFile as readFile31 } from "fs/promises";
|
|
17411
|
-
import { dirname as
|
|
17889
|
+
import { dirname as dirname15, join as join7, resolve as resolve46 } from "path";
|
|
17412
17890
|
import { spawn as spawn4 } from "child_process";
|
|
17413
17891
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
17414
17892
|
|
|
17415
17893
|
// src/utils/version.ts
|
|
17416
17894
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
17417
17895
|
import { readFile as readFile30 } from "fs/promises";
|
|
17418
|
-
import { dirname as
|
|
17896
|
+
import { dirname as dirname14, join as join6 } from "path";
|
|
17419
17897
|
async function readPackageVersion(scriptUrl) {
|
|
17420
17898
|
try {
|
|
17421
17899
|
const scriptPath = fileURLToPath8(scriptUrl);
|
|
17422
|
-
const pkgRoot =
|
|
17900
|
+
const pkgRoot = dirname14(dirname14(scriptPath));
|
|
17423
17901
|
const raw = await readFile30(join6(pkgRoot, "package.json"), "utf-8");
|
|
17424
17902
|
const parsed = JSON.parse(raw);
|
|
17425
17903
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
@@ -17461,7 +17939,7 @@ async function writeState(state) {
|
|
|
17461
17939
|
`);
|
|
17462
17940
|
}
|
|
17463
17941
|
async function resolveNpmBin() {
|
|
17464
|
-
const nodeDir =
|
|
17942
|
+
const nodeDir = dirname15(process.execPath);
|
|
17465
17943
|
const isWin = process.platform === "win32";
|
|
17466
17944
|
const npmName = isWin ? "npm.cmd" : "npm";
|
|
17467
17945
|
const nearNode = join7(nodeDir, npmName);
|
|
@@ -17974,6 +18452,17 @@ program.command("disable-playbook").description("Disable a playbook so agents no
|
|
|
17974
18452
|
process.exit(1);
|
|
17975
18453
|
}
|
|
17976
18454
|
});
|
|
18455
|
+
program.command("regen-playbook-manifest").description("Rebuild ~/.syntaur/playbooks/manifest.md from current playbook files").action(async () => {
|
|
18456
|
+
try {
|
|
18457
|
+
await regenPlaybookManifestCommand();
|
|
18458
|
+
} catch (error) {
|
|
18459
|
+
console.error(
|
|
18460
|
+
"Error:",
|
|
18461
|
+
error instanceof Error ? error.message : String(error)
|
|
18462
|
+
);
|
|
18463
|
+
process.exit(1);
|
|
18464
|
+
}
|
|
18465
|
+
});
|
|
17977
18466
|
program.addCommand(todoCommand);
|
|
17978
18467
|
program.addCommand(backupCommand);
|
|
17979
18468
|
program.addCommand(doctorCommand);
|