syntaur 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +218 -54
- package/dashboard/dist/assets/{_basePickBy-BhaCV7eH.js → _basePickBy-CWivToyi.js} +1 -1
- package/dashboard/dist/assets/{_baseUniq-CDPcqrs2.js → _baseUniq-C-qj6E4l.js} +1 -1
- package/dashboard/dist/assets/{arc-BP0RxLwl.js → arc-Dn5BIqMa.js} +1 -1
- package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-BDzvaeJp.js → architectureDiagram-2XIMDMQ5-D5D0K7rY.js} +1 -1
- package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-ZeL9mROo.js → blockDiagram-WCTKOSBZ-DVmYPMbu.js} +1 -1
- package/dashboard/dist/assets/{c4Diagram-IC4MRINW-7S5bvFLp.js → c4Diagram-IC4MRINW-BEasxbnl.js} +1 -1
- package/dashboard/dist/assets/channel-DqU_8tiy.js +1 -0
- package/dashboard/dist/assets/{chunk-4BX2VUAB-Ca7R4nv5.js → chunk-4BX2VUAB-LDIrtI5E.js} +1 -1
- package/dashboard/dist/assets/{chunk-55IACEB6-flEv13FB.js → chunk-55IACEB6-CaEBUJYu.js} +1 -1
- package/dashboard/dist/assets/{chunk-FMBD7UC4-CfcYWBM6.js → chunk-FMBD7UC4-B-GjCpdr.js} +1 -1
- package/dashboard/dist/assets/{chunk-JSJVCQXG-Dw4yL0VS.js → chunk-JSJVCQXG-BLVVcezm.js} +1 -1
- package/dashboard/dist/assets/{chunk-KX2RTZJC-B2cDe40G.js → chunk-KX2RTZJC-DqCNEw4h.js} +1 -1
- package/dashboard/dist/assets/{chunk-NQ4KR5QH-LZVm0IWg.js → chunk-NQ4KR5QH-BCPbFf5I.js} +1 -1
- package/dashboard/dist/assets/{chunk-QZHKN3VN-Dg0EeHNI.js → chunk-QZHKN3VN-Ci0C85q_.js} +1 -1
- package/dashboard/dist/assets/{chunk-WL4C6EOR-v3rXNwXc.js → chunk-WL4C6EOR-VVhAMMYU.js} +1 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-D29Eeoe8.js +1 -0
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-D29Eeoe8.js +1 -0
- package/dashboard/dist/assets/clone-Bok8Q3Jj.js +1 -0
- package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-D-3JzLoS.js → cose-bilkent-S5V4N54A-CO9uwgYO.js} +1 -1
- package/dashboard/dist/assets/{dagre-KLK3FWXG-d_mbczhU.js → dagre-KLK3FWXG-bwLLXcL4.js} +1 -1
- package/dashboard/dist/assets/{diagram-E7M64L7V-BUyAp8pW.js → diagram-E7M64L7V-RuS5R6V1.js} +1 -1
- package/dashboard/dist/assets/{diagram-IFDJBPK2-C8doXcyQ.js → diagram-IFDJBPK2-BQDJAHQd.js} +1 -1
- package/dashboard/dist/assets/{diagram-P4PSJMXO-BUSmHa55.js → diagram-P4PSJMXO-yLEsgzE5.js} +1 -1
- package/dashboard/dist/assets/{erDiagram-INFDFZHY-Bn5_0LPU.js → erDiagram-INFDFZHY-na6dUhY0.js} +1 -1
- package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-CnEjerQM.js → flowDiagram-PKNHOUZH-BIcrzwJR.js} +1 -1
- package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-CL94fbyy.js → ganttDiagram-A5KZAMGK-DHWRJn-D.js} +1 -1
- package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-4i_PeG8V.js → gitGraphDiagram-K3NZZRJ6-LGxDjL71.js} +1 -1
- package/dashboard/dist/assets/{graph-BtoFhoAd.js → graph-BUqNu277.js} +1 -1
- package/dashboard/dist/assets/{index-Dv_-SxuL.js → index-D-fepllQ.js} +3 -3
- package/dashboard/dist/assets/index-DnHyQJJH.css +1 -0
- package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-CdUsuNgZ.js → infoDiagram-LFFYTUFH-DidoA2hb.js} +1 -1
- package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-BjggRlUx.js → ishikawaDiagram-PHBUUO56-CdlZkbhV.js} +1 -1
- package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-V4AgexlR.js → journeyDiagram-4ABVD52K-luhcz_gn.js} +1 -1
- package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-ChlylQRf.js → kanban-definition-K7BYSVSG-Coidw9XE.js} +1 -1
- package/dashboard/dist/assets/{layout-DLcz9AmA.js → layout-_aBAAleE.js} +1 -1
- package/dashboard/dist/assets/{linear-l2xnSHze.js → linear-D8mFnDSx.js} +1 -1
- package/dashboard/dist/assets/{mermaid.core-DKO1ytRW.js → mermaid.core-BpP2keU-.js} +4 -4
- package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-DTmTPHrT.js → mindmap-definition-YRQLILUH-LjvrTe2z.js} +1 -1
- package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-CwK80y8Y.js → pieDiagram-SKSYHLDU-CkA2iU6e.js} +1 -1
- package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-Be1xqW_w.js → quadrantDiagram-337W2JSQ-BRmhKHQG.js} +1 -1
- package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-JcspXCs0.js → requirementDiagram-Z7DCOOCP-BYCQ4uFX.js} +1 -1
- package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-nJb1BInq.js → sankeyDiagram-WA2Y5GQK-C8SVk50M.js} +1 -1
- package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-DUrclEgA.js → sequenceDiagram-2WXFIKYE-CbA_2lnP.js} +1 -1
- package/dashboard/dist/assets/{stateDiagram-RAJIS63D-CjinnNtF.js → stateDiagram-RAJIS63D-D6ZtjAHE.js} +1 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-DeYN4wV6.js +1 -0
- package/dashboard/dist/assets/{timeline-definition-YZTLITO2-kM-oVLNz.js → timeline-definition-YZTLITO2-B2Uf-emK.js} +1 -1
- package/dashboard/dist/assets/{treemap-KZPCXAKY-CYziFlrQ.js → treemap-KZPCXAKY-CYnFKsuJ.js} +1 -1
- package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-DX0DbxBN.js → vennDiagram-LZ73GAT5-Cnj0qiDO.js} +1 -1
- package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-BGqM42ZM.js → xychartDiagram-JWTSCODW-BJy2mbL9.js} +1 -1
- package/dashboard/dist/index.html +4 -4
- package/dist/index.js +380 -149
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/scripts/postinstall-submodules.mjs +40 -0
- package/statusline/statusline.sh +166 -46
- package/dashboard/dist/assets/channel-CcB_wcgb.js +0 -1
- package/dashboard/dist/assets/classDiagram-VBA2DB6C-BJr38z2g.js +0 -1
- package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-BJr38z2g.js +0 -1
- package/dashboard/dist/assets/clone-Cfs2GUGt.js +0 -1
- package/dashboard/dist/assets/index-DZUGYrvE.css +0 -1
- package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-yfclw-nM.js +0 -1
package/dist/index.js
CHANGED
|
@@ -4429,7 +4429,7 @@ function App({ projectsDir: projectsDir2, onLaunch }) {
|
|
|
4429
4429
|
collapseNode,
|
|
4430
4430
|
currentNode
|
|
4431
4431
|
} = useTreeState(nodes, filteredIds);
|
|
4432
|
-
useInput((
|
|
4432
|
+
useInput((input3, key) => {
|
|
4433
4433
|
if (searchActive) {
|
|
4434
4434
|
if (key.escape) {
|
|
4435
4435
|
setSearchActive(false);
|
|
@@ -4442,19 +4442,19 @@ function App({ projectsDir: projectsDir2, onLaunch }) {
|
|
|
4442
4442
|
}
|
|
4443
4443
|
return;
|
|
4444
4444
|
}
|
|
4445
|
-
if (
|
|
4445
|
+
if (input3 === "q" || key.escape) {
|
|
4446
4446
|
exit();
|
|
4447
4447
|
return;
|
|
4448
4448
|
}
|
|
4449
|
-
if (
|
|
4449
|
+
if (input3 === "/") {
|
|
4450
4450
|
setSearchActive(true);
|
|
4451
4451
|
return;
|
|
4452
4452
|
}
|
|
4453
|
-
if (key.upArrow ||
|
|
4453
|
+
if (key.upArrow || input3 === "k") {
|
|
4454
4454
|
moveUp();
|
|
4455
4455
|
return;
|
|
4456
4456
|
}
|
|
4457
|
-
if (key.downArrow ||
|
|
4457
|
+
if (key.downArrow || input3 === "j") {
|
|
4458
4458
|
moveDown();
|
|
4459
4459
|
return;
|
|
4460
4460
|
}
|
|
@@ -4546,8 +4546,8 @@ __export(launch_exports, {
|
|
|
4546
4546
|
launchAgent: () => launchAgent
|
|
4547
4547
|
});
|
|
4548
4548
|
import { spawn as spawn2 } from "child_process";
|
|
4549
|
-
import { mkdir as mkdir4, writeFile as
|
|
4550
|
-
import { resolve as
|
|
4549
|
+
import { mkdir as mkdir4, writeFile as writeFile9 } from "fs/promises";
|
|
4550
|
+
import { resolve as resolve31 } from "path";
|
|
4551
4551
|
async function launchAgent(options) {
|
|
4552
4552
|
const { projectsDir: projectsDir2, projectSlug, assignmentSlug, agent } = options;
|
|
4553
4553
|
const command = AGENT_COMMANDS[agent];
|
|
@@ -4557,9 +4557,9 @@ async function launchAgent(options) {
|
|
|
4557
4557
|
process.exit(1);
|
|
4558
4558
|
}
|
|
4559
4559
|
const workspaceDir = detail.workspace.worktreePath ?? (detail.workspace.repository?.startsWith("/") ? detail.workspace.repository : null) ?? process.cwd();
|
|
4560
|
-
const projectDir =
|
|
4561
|
-
const assignmentDir =
|
|
4562
|
-
const contextDir =
|
|
4560
|
+
const projectDir = resolve31(projectsDir2, projectSlug);
|
|
4561
|
+
const assignmentDir = resolve31(projectDir, "assignments", assignmentSlug);
|
|
4562
|
+
const contextDir = resolve31(workspaceDir, ".syntaur");
|
|
4563
4563
|
await mkdir4(contextDir, { recursive: true });
|
|
4564
4564
|
const context = {
|
|
4565
4565
|
projectSlug,
|
|
@@ -4571,8 +4571,8 @@ async function launchAgent(options) {
|
|
|
4571
4571
|
branch: detail.workspace.branch ?? null,
|
|
4572
4572
|
grabbedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4573
4573
|
};
|
|
4574
|
-
await
|
|
4575
|
-
|
|
4574
|
+
await writeFile9(
|
|
4575
|
+
resolve31(contextDir, "context.json"),
|
|
4576
4576
|
JSON.stringify(context, null, 2) + "\n"
|
|
4577
4577
|
);
|
|
4578
4578
|
return new Promise((resolvePromise, reject) => {
|
|
@@ -5909,8 +5909,8 @@ async function migrateFromMarkdown(projectsDir2) {
|
|
|
5909
5909
|
return allSessions.length;
|
|
5910
5910
|
}
|
|
5911
5911
|
async function parseMarkdownSessionsIndex(filePath, projectSlug) {
|
|
5912
|
-
const { readFile:
|
|
5913
|
-
const raw = await
|
|
5912
|
+
const { readFile: readFile29 } = await import("fs/promises");
|
|
5913
|
+
const raw = await readFile29(filePath, "utf-8");
|
|
5914
5914
|
const sessions = [];
|
|
5915
5915
|
const lines = raw.split("\n");
|
|
5916
5916
|
let inTable = false;
|
|
@@ -7637,8 +7637,8 @@ ${entry}`;
|
|
|
7637
7637
|
});
|
|
7638
7638
|
return router;
|
|
7639
7639
|
}
|
|
7640
|
-
function slugifyLocal(
|
|
7641
|
-
return
|
|
7640
|
+
function slugifyLocal(input3) {
|
|
7641
|
+
return input3.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "untitled";
|
|
7642
7642
|
}
|
|
7643
7643
|
async function appendCommentTo(assignmentDir, assignmentRef, req, res, reloadDetail) {
|
|
7644
7644
|
const commentsPath = resolve15(assignmentDir, "comments.md");
|
|
@@ -8218,8 +8218,8 @@ function createTodosRouter(todosDir2, broadcast) {
|
|
|
8218
8218
|
router.post("/:workspace/archive", async (req, res) => {
|
|
8219
8219
|
try {
|
|
8220
8220
|
const { archivePath: archivePath2 } = await Promise.resolve().then(() => (init_parser2(), parser_exports));
|
|
8221
|
-
const { resolve:
|
|
8222
|
-
const { readFile:
|
|
8221
|
+
const { resolve: resolve45 } = await import("path");
|
|
8222
|
+
const { readFile: readFile29 } = await import("fs/promises");
|
|
8223
8223
|
const { writeFileForce: writeFileForce2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
|
|
8224
8224
|
const workspace = getWorkspaceParam(req.params.workspace);
|
|
8225
8225
|
const checklist = await readChecklist(todosDir2, workspace);
|
|
@@ -8235,10 +8235,10 @@ function createTodosRouter(todosDir2, broadcast) {
|
|
|
8235
8235
|
(e) => e.itemIds.every((id) => completedIds.has(id))
|
|
8236
8236
|
);
|
|
8237
8237
|
const archFile = archivePath2(todosDir2, workspace, checklist.archiveInterval);
|
|
8238
|
-
await ensureDir(
|
|
8238
|
+
await ensureDir(resolve45(todosDir2, "archive"));
|
|
8239
8239
|
let archContent = "";
|
|
8240
8240
|
if (await fileExists(archFile)) {
|
|
8241
|
-
archContent = await
|
|
8241
|
+
archContent = await readFile29(archFile, "utf-8");
|
|
8242
8242
|
archContent = archContent.trimEnd() + "\n\n";
|
|
8243
8243
|
} else {
|
|
8244
8244
|
archContent = `---
|
|
@@ -10753,17 +10753,230 @@ async function installPluginCommand(options) {
|
|
|
10753
10753
|
// src/commands/install-statusline.ts
|
|
10754
10754
|
init_paths();
|
|
10755
10755
|
init_fs();
|
|
10756
|
-
import { readFile as
|
|
10757
|
-
import { resolve as
|
|
10756
|
+
import { readFile as readFile18, writeFile as writeFile8, copyFile as copyFile2, rm as rm5, stat as stat3, symlink as symlink2, unlink as unlink6, lstat as lstat2 } from "fs/promises";
|
|
10757
|
+
import { resolve as resolve27, dirname as dirname9 } from "path";
|
|
10758
10758
|
import { homedir as homedir4 } from "os";
|
|
10759
10759
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
10760
|
+
|
|
10761
|
+
// src/commands/configure-statusline.ts
|
|
10762
|
+
init_paths();
|
|
10763
|
+
init_fs();
|
|
10764
|
+
import { readFile as readFile17, writeFile as writeFile7 } from "fs/promises";
|
|
10765
|
+
import { resolve as resolve26, dirname as dirname8 } from "path";
|
|
10766
|
+
import { spawnSync } from "child_process";
|
|
10767
|
+
import { checkbox, input as input2, confirm } from "@inquirer/prompts";
|
|
10768
|
+
var AVAILABLE_SEGMENTS = [
|
|
10769
|
+
{ name: "git", preview: "syntaur:main* +2", description: "repo:branch (with dirty marker and ahead/behind)" },
|
|
10770
|
+
{ name: "assignment", preview: "my-proj/demo-assn \u2014 Demo Assignment", description: "active syntaur assignment (project/slug or standalone/uuid)" },
|
|
10771
|
+
{ name: "external", preview: "PROJ-123, ENG-456", description: "external tracker IDs from assignment externalIds (Jira, Linear, \u2026)" },
|
|
10772
|
+
{ name: "session", preview: "\u2026ccddeeff", description: "Claude Code session id \u2014 last 8 chars prefixed by \u2026" },
|
|
10773
|
+
{ name: "model", preview: "Opus 4.7", description: "Claude model display name" },
|
|
10774
|
+
{ name: "ctx", preview: "ctx:[####------] 42%", description: "context window fill bar" },
|
|
10775
|
+
{ name: "cwd", preview: "syntaur", description: "basename of current working directory" },
|
|
10776
|
+
{ name: "wrap", preview: "<output of an external script>", description: "compose another statusline script as a leading segment" }
|
|
10777
|
+
];
|
|
10778
|
+
var PRESETS = {
|
|
10779
|
+
minimal: { segments: ["git", "session"], separator: " \xB7 " },
|
|
10780
|
+
syntaur: { segments: ["git", "assignment", "session"], separator: " \xB7 " },
|
|
10781
|
+
full: { segments: ["wrap", "git", "assignment", "external", "model", "ctx", "session"], separator: " \xB7 " },
|
|
10782
|
+
dev: { segments: ["git", "assignment", "external", "ctx", "session"], separator: " \xB7 " },
|
|
10783
|
+
tracker: { segments: ["git", "assignment", "external", "session"], separator: " \xB7 " }
|
|
10784
|
+
};
|
|
10785
|
+
function getConfigPath(installRoot) {
|
|
10786
|
+
return resolve26(installRoot, "statusline.config.json");
|
|
10787
|
+
}
|
|
10788
|
+
async function readConfig2(path) {
|
|
10789
|
+
if (!await fileExists(path)) return null;
|
|
10790
|
+
try {
|
|
10791
|
+
const raw = await readFile17(path, "utf-8");
|
|
10792
|
+
const parsed = JSON.parse(raw);
|
|
10793
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
10794
|
+
const segments = Array.isArray(parsed.segments) ? parsed.segments.filter(isSegmentName) : [];
|
|
10795
|
+
const separator = typeof parsed.separator === "string" ? parsed.separator : " \xB7 ";
|
|
10796
|
+
const wrap = typeof parsed.wrap === "string" ? parsed.wrap : void 0;
|
|
10797
|
+
return { segments, separator, wrap };
|
|
10798
|
+
} catch {
|
|
10799
|
+
return null;
|
|
10800
|
+
}
|
|
10801
|
+
}
|
|
10802
|
+
function isSegmentName(value) {
|
|
10803
|
+
return typeof value === "string" && AVAILABLE_SEGMENTS.some((s) => s.name === value);
|
|
10804
|
+
}
|
|
10805
|
+
function parseSegmentsFlag(flag) {
|
|
10806
|
+
const parts = flag.split(",").map((s) => s.trim()).filter(Boolean);
|
|
10807
|
+
const invalid = parts.filter((p) => !AVAILABLE_SEGMENTS.some((s) => s.name === p));
|
|
10808
|
+
if (invalid.length > 0) {
|
|
10809
|
+
throw new Error(
|
|
10810
|
+
`Unknown segment${invalid.length > 1 ? "s" : ""}: ${invalid.join(", ")}. Valid segments: ${AVAILABLE_SEGMENTS.map((s) => s.name).join(", ")}.`
|
|
10811
|
+
);
|
|
10812
|
+
}
|
|
10813
|
+
return parts;
|
|
10814
|
+
}
|
|
10815
|
+
async function promptSegmentsInteractive(current) {
|
|
10816
|
+
const canonicalOrder = ["wrap", "git", "assignment", "external", "session", "model", "ctx", "cwd"];
|
|
10817
|
+
const selectedSet = new Set(current?.segments ?? ["git", "assignment", "session"]);
|
|
10818
|
+
const choices = canonicalOrder.map((name) => {
|
|
10819
|
+
const def = AVAILABLE_SEGMENTS.find((s) => s.name === name);
|
|
10820
|
+
return {
|
|
10821
|
+
name: `${def.name.padEnd(11)} ${def.description}`,
|
|
10822
|
+
value: name,
|
|
10823
|
+
checked: selectedSet.has(name),
|
|
10824
|
+
description: `preview: ${def.preview}`
|
|
10825
|
+
};
|
|
10826
|
+
});
|
|
10827
|
+
const selected = await checkbox({
|
|
10828
|
+
message: "Pick segments (space to toggle, enter to confirm):",
|
|
10829
|
+
choices,
|
|
10830
|
+
loop: false,
|
|
10831
|
+
pageSize: choices.length,
|
|
10832
|
+
required: true
|
|
10833
|
+
});
|
|
10834
|
+
const defaultReorderHint = selected.join(", ");
|
|
10835
|
+
let orderedSegments = [...selected];
|
|
10836
|
+
const wantReorder = await confirm({
|
|
10837
|
+
message: `Order will be: ${defaultReorderHint}. Customize order?`,
|
|
10838
|
+
default: false
|
|
10839
|
+
});
|
|
10840
|
+
if (wantReorder) {
|
|
10841
|
+
const raw = await input2({
|
|
10842
|
+
message: `Enter the segments in the order you want, comma-separated:`,
|
|
10843
|
+
default: defaultReorderHint,
|
|
10844
|
+
validate: (value) => {
|
|
10845
|
+
const parts = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
10846
|
+
const invalid = parts.filter((p) => !canonicalOrder.includes(p));
|
|
10847
|
+
if (invalid.length > 0) {
|
|
10848
|
+
return `Unknown: ${invalid.join(", ")}. Valid: ${canonicalOrder.join(", ")}.`;
|
|
10849
|
+
}
|
|
10850
|
+
const missing = selected.filter((s) => !parts.includes(s));
|
|
10851
|
+
if (missing.length > 0) {
|
|
10852
|
+
return `Missing previously-selected segment(s): ${missing.join(", ")}. Include all of them or go back.`;
|
|
10853
|
+
}
|
|
10854
|
+
return true;
|
|
10855
|
+
}
|
|
10856
|
+
});
|
|
10857
|
+
orderedSegments = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
10858
|
+
}
|
|
10859
|
+
const separator = await input2({
|
|
10860
|
+
message: "Separator between segments:",
|
|
10861
|
+
default: current?.separator ?? " \xB7 "
|
|
10862
|
+
});
|
|
10863
|
+
let wrap = current?.wrap;
|
|
10864
|
+
if (orderedSegments.includes("wrap")) {
|
|
10865
|
+
wrap = await input2({
|
|
10866
|
+
message: "Path to external script to wrap (leave blank to skip):",
|
|
10867
|
+
default: current?.wrap ?? ""
|
|
10868
|
+
});
|
|
10869
|
+
wrap = wrap.trim() ? wrap.trim() : void 0;
|
|
10870
|
+
}
|
|
10871
|
+
return { segments: orderedSegments, separator, wrap };
|
|
10872
|
+
}
|
|
10873
|
+
function renderPreview(config, statuslineScript, cwd) {
|
|
10874
|
+
const payload = {
|
|
10875
|
+
session_id: "preview-demo-0000000000abcdef12",
|
|
10876
|
+
cwd,
|
|
10877
|
+
model: { display_name: "Opus 4.7" },
|
|
10878
|
+
context_window: { used_percentage: 42 }
|
|
10879
|
+
};
|
|
10880
|
+
const res = spawnSync("bash", [statuslineScript], {
|
|
10881
|
+
input: JSON.stringify(payload),
|
|
10882
|
+
encoding: "utf-8",
|
|
10883
|
+
env: {
|
|
10884
|
+
...process.env,
|
|
10885
|
+
// Force the child to pick up the freshly-written config from install root.
|
|
10886
|
+
HOME: dirname8(dirname8(statuslineScript))
|
|
10887
|
+
}
|
|
10888
|
+
});
|
|
10889
|
+
if (res.status !== 0) return null;
|
|
10890
|
+
return res.stdout;
|
|
10891
|
+
}
|
|
10892
|
+
async function configureStatuslineCommand(options = {}) {
|
|
10893
|
+
const installRoot = options.installRoot ?? syntaurRoot();
|
|
10894
|
+
const configPath = getConfigPath(installRoot);
|
|
10895
|
+
const current = await readConfig2(configPath);
|
|
10896
|
+
let segments;
|
|
10897
|
+
let separator;
|
|
10898
|
+
let wrap;
|
|
10899
|
+
if (options.preset) {
|
|
10900
|
+
const preset = PRESETS[options.preset.toLowerCase()];
|
|
10901
|
+
if (!preset) {
|
|
10902
|
+
throw new Error(
|
|
10903
|
+
`Unknown preset "${options.preset}". Presets: ${Object.keys(PRESETS).join(", ")}.`
|
|
10904
|
+
);
|
|
10905
|
+
}
|
|
10906
|
+
segments = [...preset.segments];
|
|
10907
|
+
separator = options.separator ?? preset.separator;
|
|
10908
|
+
wrap = options.wrap ?? current?.wrap;
|
|
10909
|
+
} else if (options.segments) {
|
|
10910
|
+
segments = parseSegmentsFlag(options.segments);
|
|
10911
|
+
separator = options.separator ?? current?.separator ?? " \xB7 ";
|
|
10912
|
+
wrap = options.wrap ?? current?.wrap;
|
|
10913
|
+
} else if (isInteractiveTerminal()) {
|
|
10914
|
+
const answers = await promptSegmentsInteractive(current);
|
|
10915
|
+
segments = answers.segments;
|
|
10916
|
+
separator = answers.separator;
|
|
10917
|
+
wrap = answers.wrap;
|
|
10918
|
+
} else {
|
|
10919
|
+
throw new Error(
|
|
10920
|
+
"Non-interactive invocation requires --preset, --segments, or run in a TTY."
|
|
10921
|
+
);
|
|
10922
|
+
}
|
|
10923
|
+
if (segments.includes("wrap") && !wrap) {
|
|
10924
|
+
console.warn(
|
|
10925
|
+
`Note: the "wrap" segment is selected but no wrap path is configured. Set one with --wrap <path> or edit ${configPath} afterwards.`
|
|
10926
|
+
);
|
|
10927
|
+
}
|
|
10928
|
+
const config = { segments, separator, ...wrap ? { wrap } : {} };
|
|
10929
|
+
if (options.preview) {
|
|
10930
|
+
console.log("Segments: " + config.segments.join(", "));
|
|
10931
|
+
console.log("Separator: " + JSON.stringify(config.separator));
|
|
10932
|
+
if (config.wrap) console.log("Wrap: " + config.wrap);
|
|
10933
|
+
console.log("");
|
|
10934
|
+
console.log("(preview mode \u2014 config NOT written)");
|
|
10935
|
+
return;
|
|
10936
|
+
}
|
|
10937
|
+
await ensureDir(dirname8(configPath));
|
|
10938
|
+
await writeFile7(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
10939
|
+
console.log("Wrote statusline config:");
|
|
10940
|
+
console.log(` path: ${configPath}`);
|
|
10941
|
+
console.log(` segments: ${config.segments.join(", ")}`);
|
|
10942
|
+
console.log(` separator: ${JSON.stringify(config.separator)}`);
|
|
10943
|
+
if (config.wrap) console.log(` wrap: ${config.wrap}`);
|
|
10944
|
+
const script = options.statuslineScript ?? resolve26(installRoot, "statusline.sh");
|
|
10945
|
+
if (await fileExists(script)) {
|
|
10946
|
+
console.log("");
|
|
10947
|
+
console.log("Live preview:");
|
|
10948
|
+
const out = renderPreview(config, script, process.cwd());
|
|
10949
|
+
if (out) {
|
|
10950
|
+
console.log(" " + out);
|
|
10951
|
+
} else {
|
|
10952
|
+
console.log(" (preview failed \u2014 run `syntaur install-statusline` if the script is missing)");
|
|
10953
|
+
}
|
|
10954
|
+
} else {
|
|
10955
|
+
console.log("");
|
|
10956
|
+
console.log(
|
|
10957
|
+
"(statusline script not yet installed \u2014 run `syntaur install-statusline` to wire it up)"
|
|
10958
|
+
);
|
|
10959
|
+
}
|
|
10960
|
+
}
|
|
10961
|
+
async function writeDefaultConfigIfMissing(installRoot) {
|
|
10962
|
+
const path = getConfigPath(installRoot);
|
|
10963
|
+
if (await fileExists(path)) return;
|
|
10964
|
+
await ensureDir(dirname8(path));
|
|
10965
|
+
const defaultConfig = {
|
|
10966
|
+
segments: ["git", "assignment", "session"],
|
|
10967
|
+
separator: " \xB7 "
|
|
10968
|
+
};
|
|
10969
|
+
await writeFile7(path, JSON.stringify(defaultConfig, null, 2) + "\n", "utf-8");
|
|
10970
|
+
}
|
|
10971
|
+
|
|
10972
|
+
// src/commands/install-statusline.ts
|
|
10760
10973
|
function getPackageStatuslineSource() {
|
|
10761
|
-
const here =
|
|
10762
|
-
return
|
|
10974
|
+
const here = dirname9(fileURLToPath5(import.meta.url));
|
|
10975
|
+
return resolve27(here, "..", "statusline", "statusline.sh");
|
|
10763
10976
|
}
|
|
10764
10977
|
async function readSettingsJson(settingsPath) {
|
|
10765
10978
|
if (!await fileExists(settingsPath)) return {};
|
|
10766
|
-
const raw = await
|
|
10979
|
+
const raw = await readFile18(settingsPath, "utf-8");
|
|
10767
10980
|
if (raw.trim() === "") return {};
|
|
10768
10981
|
try {
|
|
10769
10982
|
const parsed = JSON.parse(raw);
|
|
@@ -10775,8 +10988,8 @@ async function readSettingsJson(settingsPath) {
|
|
|
10775
10988
|
}
|
|
10776
10989
|
}
|
|
10777
10990
|
async function writeSettingsJson(settingsPath, data) {
|
|
10778
|
-
await ensureDir(
|
|
10779
|
-
await
|
|
10991
|
+
await ensureDir(dirname9(settingsPath));
|
|
10992
|
+
await writeFile8(settingsPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
10780
10993
|
}
|
|
10781
10994
|
async function resolveMode(mode, existingCommand, ourCommand) {
|
|
10782
10995
|
if (mode !== "ask") return mode;
|
|
@@ -10810,8 +11023,8 @@ function extractExistingCommand(settings) {
|
|
|
10810
11023
|
};
|
|
10811
11024
|
}
|
|
10812
11025
|
async function backupSettings(settingsSnapshot, backupPath) {
|
|
10813
|
-
await ensureDir(
|
|
10814
|
-
await
|
|
11026
|
+
await ensureDir(dirname9(backupPath));
|
|
11027
|
+
await writeFile8(
|
|
10815
11028
|
backupPath,
|
|
10816
11029
|
JSON.stringify(
|
|
10817
11030
|
{
|
|
@@ -10827,7 +11040,7 @@ async function backupSettings(settingsSnapshot, backupPath) {
|
|
|
10827
11040
|
);
|
|
10828
11041
|
}
|
|
10829
11042
|
async function installScript(sourceScript, destScript, link) {
|
|
10830
|
-
await ensureDir(
|
|
11043
|
+
await ensureDir(dirname9(destScript));
|
|
10831
11044
|
try {
|
|
10832
11045
|
const s = await lstat2(destScript);
|
|
10833
11046
|
if (s.isSymbolicLink() || s.isFile()) {
|
|
@@ -10843,18 +11056,19 @@ async function installScript(sourceScript, destScript, link) {
|
|
|
10843
11056
|
}
|
|
10844
11057
|
async function installStatuslineCommand(options = {}) {
|
|
10845
11058
|
const mode = options.mode ?? "ask";
|
|
10846
|
-
const settingsPath = options.settingsPath ??
|
|
11059
|
+
const settingsPath = options.settingsPath ?? resolve27(homedir4(), ".claude", "settings.json");
|
|
10847
11060
|
const installRoot = options.installRoot ?? syntaurRoot();
|
|
10848
11061
|
const sourceScript = options.sourceScript ?? getPackageStatuslineSource();
|
|
10849
|
-
const destScript =
|
|
10850
|
-
const confPath =
|
|
10851
|
-
const backupPath =
|
|
11062
|
+
const destScript = resolve27(installRoot, "statusline.sh");
|
|
11063
|
+
const confPath = resolve27(installRoot, "statusline.conf");
|
|
11064
|
+
const backupPath = resolve27(installRoot, "statusline.backup.json");
|
|
10852
11065
|
if (!await fileExists(sourceScript)) {
|
|
10853
11066
|
throw new Error(
|
|
10854
11067
|
`Statusline source script not found at ${sourceScript}. Try re-installing syntaur (npm install -g syntaur) or pass --source-script explicitly.`
|
|
10855
11068
|
);
|
|
10856
11069
|
}
|
|
10857
11070
|
await installScript(sourceScript, destScript, Boolean(options.link));
|
|
11071
|
+
await writeDefaultConfigIfMissing(installRoot);
|
|
10858
11072
|
const settings = await readSettingsJson(settingsPath);
|
|
10859
11073
|
const existingStatusLine = extractExistingCommand(settings);
|
|
10860
11074
|
const existingCommand = existingStatusLine?.command;
|
|
@@ -10883,19 +11097,19 @@ async function installStatuslineCommand(options = {}) {
|
|
|
10883
11097
|
if (parsed) {
|
|
10884
11098
|
wrapTarget = parsed;
|
|
10885
11099
|
} else {
|
|
10886
|
-
const wrapperPath =
|
|
11100
|
+
const wrapperPath = resolve27(installRoot, "statusline-wrapped.sh");
|
|
10887
11101
|
const wrapperBody = `#!/usr/bin/env bash
|
|
10888
11102
|
# Auto-generated by syntaur install-statusline.
|
|
10889
11103
|
# Executes the previously configured statusLine command.
|
|
10890
11104
|
exec ${existingCommand}
|
|
10891
11105
|
`;
|
|
10892
|
-
await
|
|
11106
|
+
await writeFile8(wrapperPath, wrapperBody, "utf-8");
|
|
10893
11107
|
await chmodExec(wrapperPath);
|
|
10894
11108
|
wrapTarget = wrapperPath;
|
|
10895
11109
|
}
|
|
10896
11110
|
}
|
|
10897
|
-
await ensureDir(
|
|
10898
|
-
await
|
|
11111
|
+
await ensureDir(dirname9(confPath));
|
|
11112
|
+
await writeFile8(
|
|
10899
11113
|
confPath,
|
|
10900
11114
|
wrapTarget ? `# Wrap target \u2014 the command below is invoked with the same stdin; its
|
|
10901
11115
|
# stdout becomes the leading segment of the statusline. Remove this
|
|
@@ -10938,19 +11152,19 @@ async function chmodExec(path) {
|
|
|
10938
11152
|
}
|
|
10939
11153
|
}
|
|
10940
11154
|
async function uninstallStatuslineCommand(options = {}) {
|
|
10941
|
-
const settingsPath = options.settingsPath ??
|
|
11155
|
+
const settingsPath = options.settingsPath ?? resolve27(homedir4(), ".claude", "settings.json");
|
|
10942
11156
|
const installRoot = options.installRoot ?? syntaurRoot();
|
|
10943
|
-
const destScript =
|
|
10944
|
-
const confPath =
|
|
10945
|
-
const backupPath =
|
|
10946
|
-
const wrapperPath =
|
|
11157
|
+
const destScript = resolve27(installRoot, "statusline.sh");
|
|
11158
|
+
const confPath = resolve27(installRoot, "statusline.conf");
|
|
11159
|
+
const backupPath = resolve27(installRoot, "statusline.backup.json");
|
|
11160
|
+
const wrapperPath = resolve27(installRoot, "statusline-wrapped.sh");
|
|
10947
11161
|
const settings = await readSettingsJson(settingsPath);
|
|
10948
11162
|
const existing = extractExistingCommand(settings);
|
|
10949
11163
|
const ourCommand = `bash ${destScript}`;
|
|
10950
11164
|
let restored = null;
|
|
10951
11165
|
if (await fileExists(backupPath)) {
|
|
10952
11166
|
try {
|
|
10953
|
-
const raw = await
|
|
11167
|
+
const raw = await readFile18(backupPath, "utf-8");
|
|
10954
11168
|
const parsed = JSON.parse(raw);
|
|
10955
11169
|
const prev = parsed?.previousStatusLine;
|
|
10956
11170
|
if (prev && typeof prev === "object" && typeof prev.command === "string") {
|
|
@@ -10971,7 +11185,8 @@ async function uninstallStatuslineCommand(options = {}) {
|
|
|
10971
11185
|
await writeSettingsJson(settingsPath, settings);
|
|
10972
11186
|
}
|
|
10973
11187
|
if (!options.keepScript) {
|
|
10974
|
-
|
|
11188
|
+
const configPath = resolve27(installRoot, "statusline.config.json");
|
|
11189
|
+
for (const path of [destScript, confPath, backupPath, wrapperPath, configPath]) {
|
|
10975
11190
|
try {
|
|
10976
11191
|
await rm5(path, { force: true });
|
|
10977
11192
|
} catch {
|
|
@@ -11225,7 +11440,7 @@ async function setupCommand(options) {
|
|
|
11225
11440
|
}
|
|
11226
11441
|
|
|
11227
11442
|
// src/commands/uninstall.ts
|
|
11228
|
-
import { resolve as
|
|
11443
|
+
import { resolve as resolve28 } from "path";
|
|
11229
11444
|
init_paths();
|
|
11230
11445
|
function expandTargets(options) {
|
|
11231
11446
|
if (options.all) {
|
|
@@ -11305,7 +11520,7 @@ async function uninstallCommand(options) {
|
|
|
11305
11520
|
const configuredProjectDir = await getConfiguredProjectDir();
|
|
11306
11521
|
await removeSyntaurData();
|
|
11307
11522
|
console.log(`Removed ${syntaurRoot()}`);
|
|
11308
|
-
if (configuredProjectDir &&
|
|
11523
|
+
if (configuredProjectDir && resolve28(configuredProjectDir) !== resolve28(syntaurRoot(), "projects")) {
|
|
11309
11524
|
console.warn(
|
|
11310
11525
|
`Warning: config.md pointed to an external project directory (${configuredProjectDir}). That directory was not removed automatically.`
|
|
11311
11526
|
);
|
|
@@ -11320,7 +11535,7 @@ async function uninstallCommand(options) {
|
|
|
11320
11535
|
init_paths();
|
|
11321
11536
|
init_fs();
|
|
11322
11537
|
init_config2();
|
|
11323
|
-
import { resolve as
|
|
11538
|
+
import { resolve as resolve29 } from "path";
|
|
11324
11539
|
var SUPPORTED_FRAMEWORKS = ["cursor", "codex", "opencode"];
|
|
11325
11540
|
async function setupAdapterCommand(framework, options) {
|
|
11326
11541
|
if (!SUPPORTED_FRAMEWORKS.includes(framework)) {
|
|
@@ -11346,19 +11561,19 @@ async function setupAdapterCommand(framework, options) {
|
|
|
11346
11561
|
}
|
|
11347
11562
|
const config = await readConfig();
|
|
11348
11563
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
11349
|
-
const projectDir =
|
|
11350
|
-
const assignmentDir =
|
|
11564
|
+
const projectDir = resolve29(baseDir, options.project);
|
|
11565
|
+
const assignmentDir = resolve29(
|
|
11351
11566
|
projectDir,
|
|
11352
11567
|
"assignments",
|
|
11353
11568
|
options.assignment
|
|
11354
11569
|
);
|
|
11355
|
-
const projectMdPath =
|
|
11570
|
+
const projectMdPath = resolve29(projectDir, "project.md");
|
|
11356
11571
|
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
11357
11572
|
throw new Error(
|
|
11358
11573
|
`Project "${options.project}" not found at ${projectDir}.`
|
|
11359
11574
|
);
|
|
11360
11575
|
}
|
|
11361
|
-
const assignmentMdPath =
|
|
11576
|
+
const assignmentMdPath = resolve29(assignmentDir, "assignment.md");
|
|
11362
11577
|
if (!await fileExists(assignmentDir) || !await fileExists(assignmentMdPath)) {
|
|
11363
11578
|
throw new Error(
|
|
11364
11579
|
`Assignment "${options.assignment}" not found at ${assignmentDir}.`
|
|
@@ -11386,15 +11601,15 @@ async function setupAdapterCommand(framework, options) {
|
|
|
11386
11601
|
}
|
|
11387
11602
|
}
|
|
11388
11603
|
if (framework === "cursor") {
|
|
11389
|
-
const protocolPath =
|
|
11390
|
-
const assignmentPath =
|
|
11604
|
+
const protocolPath = resolve29(cwd, ".cursor", "rules", "syntaur-protocol.mdc");
|
|
11605
|
+
const assignmentPath = resolve29(cwd, ".cursor", "rules", "syntaur-assignment.mdc");
|
|
11391
11606
|
await writeAdapterFile(protocolPath, renderCursorProtocol());
|
|
11392
11607
|
await writeAdapterFile(assignmentPath, renderCursorAssignment(rendererParams));
|
|
11393
11608
|
} else if (framework === "codex" || framework === "opencode") {
|
|
11394
|
-
const agentsPath =
|
|
11609
|
+
const agentsPath = resolve29(cwd, "AGENTS.md");
|
|
11395
11610
|
await writeAdapterFile(agentsPath, renderCodexAgents(rendererParams));
|
|
11396
11611
|
if (framework === "opencode") {
|
|
11397
|
-
const configPath =
|
|
11612
|
+
const configPath = resolve29(cwd, "opencode.json");
|
|
11398
11613
|
await writeAdapterFile(configPath, renderOpenCodeConfig({ projectDir }));
|
|
11399
11614
|
}
|
|
11400
11615
|
}
|
|
@@ -11419,7 +11634,7 @@ async function setupAdapterCommand(framework, options) {
|
|
|
11419
11634
|
init_paths();
|
|
11420
11635
|
init_fs();
|
|
11421
11636
|
init_config2();
|
|
11422
|
-
import { resolve as
|
|
11637
|
+
import { resolve as resolve30 } from "path";
|
|
11423
11638
|
async function trackSessionCommand(options) {
|
|
11424
11639
|
if (!options.agent) {
|
|
11425
11640
|
throw new Error("--agent <name> is required.");
|
|
@@ -11432,7 +11647,7 @@ async function trackSessionCommand(options) {
|
|
|
11432
11647
|
if (options.project) {
|
|
11433
11648
|
const config = await readConfig();
|
|
11434
11649
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
11435
|
-
const projectDir =
|
|
11650
|
+
const projectDir = resolve30(baseDir, options.project);
|
|
11436
11651
|
if (!await fileExists(projectDir)) {
|
|
11437
11652
|
throw new Error(
|
|
11438
11653
|
`Project "${options.project}" not found at ${projectDir}.`
|
|
@@ -11487,7 +11702,7 @@ async function browseCommand(options) {
|
|
|
11487
11702
|
}
|
|
11488
11703
|
|
|
11489
11704
|
// src/commands/create-playbook.ts
|
|
11490
|
-
import { resolve as
|
|
11705
|
+
import { resolve as resolve32 } from "path";
|
|
11491
11706
|
init_timestamp();
|
|
11492
11707
|
init_paths();
|
|
11493
11708
|
init_fs();
|
|
@@ -11503,7 +11718,7 @@ async function createPlaybookCommand(name, options) {
|
|
|
11503
11718
|
}
|
|
11504
11719
|
const dir = playbooksDir();
|
|
11505
11720
|
await ensureDir(dir);
|
|
11506
|
-
const filePath =
|
|
11721
|
+
const filePath = resolve32(dir, `${slug}.md`);
|
|
11507
11722
|
if (await fileExists(filePath)) {
|
|
11508
11723
|
throw new Error(
|
|
11509
11724
|
`Playbook "${slug}" already exists at ${filePath}
|
|
@@ -11524,8 +11739,8 @@ Use --slug to specify a different slug.`
|
|
|
11524
11739
|
init_paths();
|
|
11525
11740
|
init_fs();
|
|
11526
11741
|
init_parser();
|
|
11527
|
-
import { readdir as readdir12, readFile as
|
|
11528
|
-
import { resolve as
|
|
11742
|
+
import { readdir as readdir12, readFile as readFile19 } from "fs/promises";
|
|
11743
|
+
import { resolve as resolve33 } from "path";
|
|
11529
11744
|
async function listPlaybooksCommand() {
|
|
11530
11745
|
const dir = playbooksDir();
|
|
11531
11746
|
if (!await fileExists(dir)) {
|
|
@@ -11543,8 +11758,8 @@ async function listPlaybooksCommand() {
|
|
|
11543
11758
|
console.log(`${"Slug".padEnd(30)} ${"Name".padEnd(30)} Description`);
|
|
11544
11759
|
console.log(`${"\u2500".repeat(30)} ${"\u2500".repeat(30)} ${"\u2500".repeat(40)}`);
|
|
11545
11760
|
for (const entry of mdFiles) {
|
|
11546
|
-
const filePath =
|
|
11547
|
-
const raw = await
|
|
11761
|
+
const filePath = resolve33(dir, entry.name);
|
|
11762
|
+
const raw = await readFile19(filePath, "utf-8");
|
|
11548
11763
|
const parsed = parsePlaybook(raw);
|
|
11549
11764
|
const slug = parsed.slug || entry.name.replace(/\.md$/, "");
|
|
11550
11765
|
const name = parsed.name || slug;
|
|
@@ -11558,8 +11773,8 @@ init_paths();
|
|
|
11558
11773
|
init_parser2();
|
|
11559
11774
|
init_fs();
|
|
11560
11775
|
import { Command } from "commander";
|
|
11561
|
-
import { readFile as
|
|
11562
|
-
import { resolve as
|
|
11776
|
+
import { readFile as readFile20 } from "fs/promises";
|
|
11777
|
+
import { resolve as resolve34 } from "path";
|
|
11563
11778
|
var WORKSPACE_REGEX2 = /^[a-z0-9_][a-z0-9-]*$/;
|
|
11564
11779
|
function resolveWorkspace(options) {
|
|
11565
11780
|
if (options.global) return "_global";
|
|
@@ -11840,10 +12055,10 @@ todoCommand.command("archive").description("Archive completed todos and their lo
|
|
|
11840
12055
|
(e) => e.itemIds.every((id) => completedIds.has(id))
|
|
11841
12056
|
);
|
|
11842
12057
|
const archFile = archivePath(todosPath, workspace, checklist.archiveInterval);
|
|
11843
|
-
await ensureDir(
|
|
12058
|
+
await ensureDir(resolve34(todosPath, "archive"));
|
|
11844
12059
|
let archContent = "";
|
|
11845
12060
|
if (await fileExists(archFile)) {
|
|
11846
|
-
archContent = await
|
|
12061
|
+
archContent = await readFile20(archFile, "utf-8");
|
|
11847
12062
|
archContent = archContent.trimEnd() + "\n\n";
|
|
11848
12063
|
} else {
|
|
11849
12064
|
archContent = `---
|
|
@@ -12032,19 +12247,19 @@ import { Command as Command3 } from "commander";
|
|
|
12032
12247
|
|
|
12033
12248
|
// src/utils/doctor/index.ts
|
|
12034
12249
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
12035
|
-
import { readFile as
|
|
12036
|
-
import { dirname as
|
|
12250
|
+
import { readFile as readFile24 } from "fs/promises";
|
|
12251
|
+
import { dirname as dirname11, join as join5 } from "path";
|
|
12037
12252
|
|
|
12038
12253
|
// src/utils/doctor/context.ts
|
|
12039
12254
|
init_config2();
|
|
12040
12255
|
init_paths();
|
|
12041
12256
|
init_fs();
|
|
12042
12257
|
import Database2 from "better-sqlite3";
|
|
12043
|
-
import { resolve as
|
|
12258
|
+
import { resolve as resolve35 } from "path";
|
|
12044
12259
|
async function buildCheckContext(cwd = process.cwd()) {
|
|
12045
12260
|
const config = await readConfig();
|
|
12046
12261
|
const root = syntaurRoot();
|
|
12047
|
-
const dbPath =
|
|
12262
|
+
const dbPath = resolve35(root, "syntaur.db");
|
|
12048
12263
|
let db2 = null;
|
|
12049
12264
|
let dbError = null;
|
|
12050
12265
|
if (await fileExists(dbPath)) {
|
|
@@ -12078,10 +12293,10 @@ function closeCheckContext(ctx) {
|
|
|
12078
12293
|
// src/utils/doctor/checks/env.ts
|
|
12079
12294
|
init_fs();
|
|
12080
12295
|
init_paths();
|
|
12081
|
-
import { resolve as
|
|
12082
|
-
import { readFile as
|
|
12296
|
+
import { resolve as resolve36, isAbsolute as isAbsolute3 } from "path";
|
|
12297
|
+
import { readFile as readFile21, stat as stat4 } from "fs/promises";
|
|
12083
12298
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
12084
|
-
import { dirname as
|
|
12299
|
+
import { dirname as dirname10, join as join4 } from "path";
|
|
12085
12300
|
var CATEGORY = "env";
|
|
12086
12301
|
var syntaurRootExists = {
|
|
12087
12302
|
id: "env.syntaur-root-exists",
|
|
@@ -12119,7 +12334,7 @@ var configValid = {
|
|
|
12119
12334
|
category: CATEGORY,
|
|
12120
12335
|
title: "~/.syntaur/config.md is valid",
|
|
12121
12336
|
async run(ctx) {
|
|
12122
|
-
const configPath =
|
|
12337
|
+
const configPath = resolve36(ctx.syntaurRoot, "config.md");
|
|
12123
12338
|
if (!await fileExists(configPath)) {
|
|
12124
12339
|
return {
|
|
12125
12340
|
id: this.id,
|
|
@@ -12136,7 +12351,7 @@ var configValid = {
|
|
|
12136
12351
|
autoFixable: false
|
|
12137
12352
|
};
|
|
12138
12353
|
}
|
|
12139
|
-
const content = await
|
|
12354
|
+
const content = await readFile21(configPath, "utf-8");
|
|
12140
12355
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
12141
12356
|
if (!fmMatch || fmMatch[1].trim() === "") {
|
|
12142
12357
|
return {
|
|
@@ -12412,14 +12627,14 @@ async function readLocalVersion() {
|
|
|
12412
12627
|
async function readLocalPkg() {
|
|
12413
12628
|
try {
|
|
12414
12629
|
const here = fileURLToPath6(import.meta.url);
|
|
12415
|
-
let dir =
|
|
12630
|
+
let dir = dirname10(here);
|
|
12416
12631
|
for (let i = 0; i < 6; i++) {
|
|
12417
12632
|
const candidate = join4(dir, "package.json");
|
|
12418
12633
|
try {
|
|
12419
|
-
const text = await
|
|
12634
|
+
const text = await readFile21(candidate, "utf-8");
|
|
12420
12635
|
return JSON.parse(text);
|
|
12421
12636
|
} catch {
|
|
12422
|
-
dir =
|
|
12637
|
+
dir = dirname10(dir);
|
|
12423
12638
|
}
|
|
12424
12639
|
}
|
|
12425
12640
|
return null;
|
|
@@ -12468,7 +12683,7 @@ function versionGte(a, b) {
|
|
|
12468
12683
|
|
|
12469
12684
|
// src/utils/doctor/checks/structure.ts
|
|
12470
12685
|
init_fs();
|
|
12471
|
-
import { resolve as
|
|
12686
|
+
import { resolve as resolve37 } from "path";
|
|
12472
12687
|
import { readdir as readdir13, stat as stat5 } from "fs/promises";
|
|
12473
12688
|
var CATEGORY2 = "structure";
|
|
12474
12689
|
var KNOWN_TOP_LEVEL = /* @__PURE__ */ new Set([
|
|
@@ -12488,7 +12703,7 @@ var projectsDir = {
|
|
|
12488
12703
|
category: CATEGORY2,
|
|
12489
12704
|
title: "projects/ directory exists",
|
|
12490
12705
|
async run(ctx) {
|
|
12491
|
-
const p =
|
|
12706
|
+
const p = resolve37(ctx.syntaurRoot, "projects");
|
|
12492
12707
|
if (!await fileExists(p)) {
|
|
12493
12708
|
return {
|
|
12494
12709
|
id: this.id,
|
|
@@ -12513,7 +12728,7 @@ var playbooksDir2 = {
|
|
|
12513
12728
|
category: CATEGORY2,
|
|
12514
12729
|
title: "playbooks/ directory exists",
|
|
12515
12730
|
async run(ctx) {
|
|
12516
|
-
const p =
|
|
12731
|
+
const p = resolve37(ctx.syntaurRoot, "playbooks");
|
|
12517
12732
|
if (!await fileExists(p)) {
|
|
12518
12733
|
return {
|
|
12519
12734
|
id: this.id,
|
|
@@ -12538,7 +12753,7 @@ var todosDirValid = {
|
|
|
12538
12753
|
category: CATEGORY2,
|
|
12539
12754
|
title: "todos/ directory is readable (if present)",
|
|
12540
12755
|
async run(ctx) {
|
|
12541
|
-
const p =
|
|
12756
|
+
const p = resolve37(ctx.syntaurRoot, "todos");
|
|
12542
12757
|
if (!await fileExists(p)) {
|
|
12543
12758
|
return {
|
|
12544
12759
|
id: this.id,
|
|
@@ -12569,7 +12784,7 @@ var serversDirValid = {
|
|
|
12569
12784
|
category: CATEGORY2,
|
|
12570
12785
|
title: "servers/ directory is readable (if present)",
|
|
12571
12786
|
async run(ctx) {
|
|
12572
|
-
const p =
|
|
12787
|
+
const p = resolve37(ctx.syntaurRoot, "servers");
|
|
12573
12788
|
if (!await fileExists(p)) {
|
|
12574
12789
|
return {
|
|
12575
12790
|
id: this.id,
|
|
@@ -12614,7 +12829,7 @@ var knownFilesRecognized = {
|
|
|
12614
12829
|
title: this.title,
|
|
12615
12830
|
status: "warn",
|
|
12616
12831
|
detail: `unexpected top-level entries: ${unexpected.join(", ")}`,
|
|
12617
|
-
affected: unexpected.map((n) =>
|
|
12832
|
+
affected: unexpected.map((n) => resolve37(ctx.syntaurRoot, n)),
|
|
12618
12833
|
remediation: {
|
|
12619
12834
|
kind: "manual",
|
|
12620
12835
|
suggestion: "Review these entries \u2014 they may be leftover state from older versions",
|
|
@@ -12643,7 +12858,7 @@ function pass2(check) {
|
|
|
12643
12858
|
|
|
12644
12859
|
// src/utils/doctor/checks/project.ts
|
|
12645
12860
|
init_fs();
|
|
12646
|
-
import { resolve as
|
|
12861
|
+
import { resolve as resolve38 } from "path";
|
|
12647
12862
|
import { readdir as readdir14, stat as stat6 } from "fs/promises";
|
|
12648
12863
|
var CATEGORY3 = "project";
|
|
12649
12864
|
var REQUIRED_PROJECT_FILES = [
|
|
@@ -12673,10 +12888,10 @@ async function listProjects2(ctx) {
|
|
|
12673
12888
|
for (const e of entries) {
|
|
12674
12889
|
if (!e.isDirectory()) continue;
|
|
12675
12890
|
if (e.name.startsWith(".") || e.name.startsWith("_")) continue;
|
|
12676
|
-
const projectDir =
|
|
12891
|
+
const projectDir = resolve38(dir, e.name);
|
|
12677
12892
|
let looksLikeProject = false;
|
|
12678
12893
|
for (const marker of PROJECT_MARKERS) {
|
|
12679
|
-
if (await fileExists(
|
|
12894
|
+
if (await fileExists(resolve38(projectDir, marker))) {
|
|
12680
12895
|
looksLikeProject = true;
|
|
12681
12896
|
break;
|
|
12682
12897
|
}
|
|
@@ -12695,7 +12910,7 @@ var requiredFiles = {
|
|
|
12695
12910
|
for (const projectDir of projects) {
|
|
12696
12911
|
const missing = [];
|
|
12697
12912
|
for (const rel of REQUIRED_PROJECT_FILES) {
|
|
12698
|
-
const p =
|
|
12913
|
+
const p = resolve38(projectDir, rel);
|
|
12699
12914
|
if (!await fileExists(p)) missing.push(rel);
|
|
12700
12915
|
}
|
|
12701
12916
|
if (missing.length === 0) continue;
|
|
@@ -12705,7 +12920,7 @@ var requiredFiles = {
|
|
|
12705
12920
|
title: this.title,
|
|
12706
12921
|
status: "error",
|
|
12707
12922
|
detail: `project at ${projectDir} is missing: ${missing.join(", ")}`,
|
|
12708
|
-
affected: missing.map((m) =>
|
|
12923
|
+
affected: missing.map((m) => resolve38(projectDir, m)),
|
|
12709
12924
|
remediation: {
|
|
12710
12925
|
kind: "manual",
|
|
12711
12926
|
suggestion: "Recreate the missing scaffold files from templates",
|
|
@@ -12728,7 +12943,7 @@ var manifestStale = {
|
|
|
12728
12943
|
const projects = await listProjects2(ctx);
|
|
12729
12944
|
const results = [];
|
|
12730
12945
|
for (const projectDir of projects) {
|
|
12731
|
-
const manifestPath =
|
|
12946
|
+
const manifestPath = resolve38(projectDir, "manifest.md");
|
|
12732
12947
|
if (!await fileExists(manifestPath)) continue;
|
|
12733
12948
|
const manifestMtime = (await stat6(manifestPath)).mtimeMs;
|
|
12734
12949
|
const newestAssignment = await newestAssignmentMtime(projectDir);
|
|
@@ -12777,7 +12992,7 @@ var orphanFiles = {
|
|
|
12777
12992
|
title: this.title,
|
|
12778
12993
|
status: "warn",
|
|
12779
12994
|
detail: `project at ${projectDir} has unexpected entries: ${orphans.join(", ")}`,
|
|
12780
|
-
affected: orphans.map((o) =>
|
|
12995
|
+
affected: orphans.map((o) => resolve38(projectDir, o)),
|
|
12781
12996
|
autoFixable: false
|
|
12782
12997
|
});
|
|
12783
12998
|
}
|
|
@@ -12787,7 +13002,7 @@ var orphanFiles = {
|
|
|
12787
13002
|
};
|
|
12788
13003
|
var projectChecks = [requiredFiles, manifestStale, orphanFiles];
|
|
12789
13004
|
async function newestAssignmentMtime(projectDir) {
|
|
12790
|
-
const assignmentsRoot =
|
|
13005
|
+
const assignmentsRoot = resolve38(projectDir, "assignments");
|
|
12791
13006
|
if (!await fileExists(assignmentsRoot)) return 0;
|
|
12792
13007
|
let newest = 0;
|
|
12793
13008
|
let entries;
|
|
@@ -12798,7 +13013,7 @@ async function newestAssignmentMtime(projectDir) {
|
|
|
12798
13013
|
}
|
|
12799
13014
|
for (const e of entries) {
|
|
12800
13015
|
if (!e.isDirectory()) continue;
|
|
12801
|
-
const assignmentMd =
|
|
13016
|
+
const assignmentMd = resolve38(assignmentsRoot, e.name, "assignment.md");
|
|
12802
13017
|
try {
|
|
12803
13018
|
const s = await stat6(assignmentMd);
|
|
12804
13019
|
if (s.mtimeMs > newest) newest = s.mtimeMs;
|
|
@@ -12822,8 +13037,8 @@ init_fs();
|
|
|
12822
13037
|
init_parser();
|
|
12823
13038
|
init_types();
|
|
12824
13039
|
init_paths();
|
|
12825
|
-
import { resolve as
|
|
12826
|
-
import { readdir as readdir15, readFile as
|
|
13040
|
+
import { resolve as resolve39 } from "path";
|
|
13041
|
+
import { readdir as readdir15, readFile as readFile22 } from "fs/promises";
|
|
12827
13042
|
var CATEGORY4 = "assignment";
|
|
12828
13043
|
var STATUSES_REQUIRING_HANDOFF = /* @__PURE__ */ new Set(["review", "completed"]);
|
|
12829
13044
|
async function listAssignments(ctx) {
|
|
@@ -12834,16 +13049,16 @@ async function listAssignments(ctx) {
|
|
|
12834
13049
|
for (const m of projects) {
|
|
12835
13050
|
if (!m.isDirectory()) continue;
|
|
12836
13051
|
if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
|
|
12837
|
-
const assignmentsDir2 =
|
|
13052
|
+
const assignmentsDir2 = resolve39(projectsDir2, m.name, "assignments");
|
|
12838
13053
|
if (!await fileExists(assignmentsDir2)) continue;
|
|
12839
13054
|
const entries = await readdir15(assignmentsDir2, { withFileTypes: true });
|
|
12840
13055
|
for (const a of entries) {
|
|
12841
13056
|
if (!a.isDirectory()) continue;
|
|
12842
13057
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
12843
|
-
const assignmentDir =
|
|
12844
|
-
const assignmentMd =
|
|
13058
|
+
const assignmentDir = resolve39(assignmentsDir2, a.name);
|
|
13059
|
+
const assignmentMd = resolve39(assignmentDir, "assignment.md");
|
|
12845
13060
|
const entry = {
|
|
12846
|
-
projectDir:
|
|
13061
|
+
projectDir: resolve39(projectsDir2, m.name),
|
|
12847
13062
|
projectSlug: m.name,
|
|
12848
13063
|
assignmentDir,
|
|
12849
13064
|
assignmentSlug: a.name,
|
|
@@ -12863,8 +13078,8 @@ async function listAssignments(ctx) {
|
|
|
12863
13078
|
for (const a of entries) {
|
|
12864
13079
|
if (!a.isDirectory()) continue;
|
|
12865
13080
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
12866
|
-
const assignmentDir =
|
|
12867
|
-
const assignmentMd =
|
|
13081
|
+
const assignmentDir = resolve39(standaloneRoot, a.name);
|
|
13082
|
+
const assignmentMd = resolve39(assignmentDir, "assignment.md");
|
|
12868
13083
|
const entry = {
|
|
12869
13084
|
projectDir: standaloneRoot,
|
|
12870
13085
|
projectSlug: null,
|
|
@@ -12942,7 +13157,7 @@ var invalidStatus = {
|
|
|
12942
13157
|
const allowed = configuredStatuses(ctx);
|
|
12943
13158
|
const results = [];
|
|
12944
13159
|
for (const a of withAssignmentMd) {
|
|
12945
|
-
const path =
|
|
13160
|
+
const path = resolve39(a.assignmentDir, "assignment.md");
|
|
12946
13161
|
const parsed = await parseSafe(path);
|
|
12947
13162
|
if (!parsed) continue;
|
|
12948
13163
|
if (!allowed.has(parsed.status)) {
|
|
@@ -12975,7 +13190,7 @@ var workspaceMissing = {
|
|
|
12975
13190
|
const terminal = terminalStatuses(ctx);
|
|
12976
13191
|
const results = [];
|
|
12977
13192
|
for (const a of withAssignmentMd) {
|
|
12978
|
-
const path =
|
|
13193
|
+
const path = resolve39(a.assignmentDir, "assignment.md");
|
|
12979
13194
|
const parsed = await parseSafe(path);
|
|
12980
13195
|
if (!parsed) continue;
|
|
12981
13196
|
if (terminal.has(parsed.status)) continue;
|
|
@@ -13022,12 +13237,12 @@ var requiredFilesByStatus = {
|
|
|
13022
13237
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
13023
13238
|
const results = [];
|
|
13024
13239
|
for (const a of withAssignmentMd) {
|
|
13025
|
-
const assignmentPath =
|
|
13240
|
+
const assignmentPath = resolve39(a.assignmentDir, "assignment.md");
|
|
13026
13241
|
const parsed = await parseSafe(assignmentPath);
|
|
13027
13242
|
if (!parsed) continue;
|
|
13028
13243
|
const missing = [];
|
|
13029
13244
|
if (STATUSES_REQUIRING_HANDOFF.has(parsed.status)) {
|
|
13030
|
-
const handoffPath =
|
|
13245
|
+
const handoffPath = resolve39(a.assignmentDir, "handoff.md");
|
|
13031
13246
|
if (!await fileExists(handoffPath)) missing.push("handoff.md");
|
|
13032
13247
|
}
|
|
13033
13248
|
if (missing.length === 0) continue;
|
|
@@ -13037,7 +13252,7 @@ var requiredFilesByStatus = {
|
|
|
13037
13252
|
title: this.title,
|
|
13038
13253
|
status: "warn",
|
|
13039
13254
|
detail: `${a.projectSlug}/${a.assignmentSlug} (status: ${parsed.status}) is missing ${missing.join(", ")}`,
|
|
13040
|
-
affected: missing.map((m) =>
|
|
13255
|
+
affected: missing.map((m) => resolve39(a.assignmentDir, m)),
|
|
13041
13256
|
remediation: {
|
|
13042
13257
|
kind: "manual",
|
|
13043
13258
|
suggestion: `Create the missing ${missing.join(" and ")} files for this assignment`,
|
|
@@ -13060,7 +13275,7 @@ var companionFilesScaffolded = {
|
|
|
13060
13275
|
for (const a of withAssignmentMd) {
|
|
13061
13276
|
const missing = [];
|
|
13062
13277
|
for (const filename of ["progress.md", "comments.md"]) {
|
|
13063
|
-
if (!await fileExists(
|
|
13278
|
+
if (!await fileExists(resolve39(a.assignmentDir, filename))) {
|
|
13064
13279
|
missing.push(filename);
|
|
13065
13280
|
}
|
|
13066
13281
|
}
|
|
@@ -13072,7 +13287,7 @@ var companionFilesScaffolded = {
|
|
|
13072
13287
|
title: this.title,
|
|
13073
13288
|
status: "warn",
|
|
13074
13289
|
detail: `${label} is missing ${missing.join(" and ")} (pre-v2.0 assignment \u2014 not required, but scaffolding them keeps the dashboard and CLIs consistent)`,
|
|
13075
|
-
affected: missing.map((m) =>
|
|
13290
|
+
affected: missing.map((m) => resolve39(a.assignmentDir, m)),
|
|
13076
13291
|
remediation: {
|
|
13077
13292
|
kind: "manual",
|
|
13078
13293
|
suggestion: `Create ${missing.join(" and ")} with the renderProgress/renderComments templates, or re-scaffold via the CLI`,
|
|
@@ -13105,7 +13320,7 @@ var typeDefinition = {
|
|
|
13105
13320
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
13106
13321
|
const results = [];
|
|
13107
13322
|
for (const a of withAssignmentMd) {
|
|
13108
|
-
const path =
|
|
13323
|
+
const path = resolve39(a.assignmentDir, "assignment.md");
|
|
13109
13324
|
const parsed = await parseSafe(path);
|
|
13110
13325
|
if (!parsed) continue;
|
|
13111
13326
|
if (!parsed.type) continue;
|
|
@@ -13139,7 +13354,7 @@ var projectFrontmatterMatchesContainer = {
|
|
|
13139
13354
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
13140
13355
|
const results = [];
|
|
13141
13356
|
for (const a of withAssignmentMd) {
|
|
13142
|
-
const path =
|
|
13357
|
+
const path = resolve39(a.assignmentDir, "assignment.md");
|
|
13143
13358
|
const parsed = await parseSafe(path);
|
|
13144
13359
|
if (!parsed) continue;
|
|
13145
13360
|
if (a.standalone) {
|
|
@@ -13194,7 +13409,7 @@ var assignmentChecks = [
|
|
|
13194
13409
|
];
|
|
13195
13410
|
async function parseSafe(path) {
|
|
13196
13411
|
try {
|
|
13197
|
-
const content = await
|
|
13412
|
+
const content = await readFile22(path, "utf-8");
|
|
13198
13413
|
return parseAssignmentFull(content);
|
|
13199
13414
|
} catch {
|
|
13200
13415
|
return null;
|
|
@@ -13213,7 +13428,7 @@ function pass4(check, detail) {
|
|
|
13213
13428
|
|
|
13214
13429
|
// src/utils/doctor/checks/dashboard.ts
|
|
13215
13430
|
init_fs();
|
|
13216
|
-
import { resolve as
|
|
13431
|
+
import { resolve as resolve40 } from "path";
|
|
13217
13432
|
var CATEGORY5 = "dashboard";
|
|
13218
13433
|
var dbReachable = {
|
|
13219
13434
|
id: "dashboard.db-reachable",
|
|
@@ -13227,7 +13442,7 @@ var dbReachable = {
|
|
|
13227
13442
|
title: this.title,
|
|
13228
13443
|
status: "error",
|
|
13229
13444
|
detail: `could not open syntaur.db: ${ctx.dbError ?? "unknown error"}`,
|
|
13230
|
-
affected: [
|
|
13445
|
+
affected: [resolve40(ctx.syntaurRoot, "syntaur.db")],
|
|
13231
13446
|
remediation: {
|
|
13232
13447
|
kind: "manual",
|
|
13233
13448
|
suggestion: "Start the dashboard once (`syntaur dashboard`) to initialize the DB, or restore it from backup",
|
|
@@ -13245,7 +13460,7 @@ var dbReachable = {
|
|
|
13245
13460
|
title: this.title,
|
|
13246
13461
|
status: "error",
|
|
13247
13462
|
detail: 'syntaur.db is missing the expected "sessions" table',
|
|
13248
|
-
affected: [
|
|
13463
|
+
affected: [resolve40(ctx.syntaurRoot, "syntaur.db")],
|
|
13249
13464
|
autoFixable: false
|
|
13250
13465
|
};
|
|
13251
13466
|
}
|
|
@@ -13257,7 +13472,7 @@ var dbReachable = {
|
|
|
13257
13472
|
title: this.title,
|
|
13258
13473
|
status: "error",
|
|
13259
13474
|
detail: `syntaur.db query failed: ${err2 instanceof Error ? err2.message : String(err2)}`,
|
|
13260
|
-
affected: [
|
|
13475
|
+
affected: [resolve40(ctx.syntaurRoot, "syntaur.db")],
|
|
13261
13476
|
autoFixable: false
|
|
13262
13477
|
};
|
|
13263
13478
|
}
|
|
@@ -13283,7 +13498,7 @@ var ghostSessions = {
|
|
|
13283
13498
|
const results = [];
|
|
13284
13499
|
for (const row of rows) {
|
|
13285
13500
|
if (!row.project_slug) continue;
|
|
13286
|
-
const projectPath =
|
|
13501
|
+
const projectPath = resolve40(projectsDir2, row.project_slug, "project.md");
|
|
13287
13502
|
if (!await fileExists(projectPath)) {
|
|
13288
13503
|
results.push({
|
|
13289
13504
|
id: this.id,
|
|
@@ -13302,7 +13517,7 @@ var ghostSessions = {
|
|
|
13302
13517
|
continue;
|
|
13303
13518
|
}
|
|
13304
13519
|
if (row.assignment_slug) {
|
|
13305
|
-
const assignmentPath =
|
|
13520
|
+
const assignmentPath = resolve40(
|
|
13306
13521
|
projectsDir2,
|
|
13307
13522
|
row.project_slug,
|
|
13308
13523
|
"assignments",
|
|
@@ -13459,8 +13674,8 @@ function skipped2(check, reason) {
|
|
|
13459
13674
|
init_fs();
|
|
13460
13675
|
init_parser();
|
|
13461
13676
|
init_types();
|
|
13462
|
-
import { resolve as
|
|
13463
|
-
import { readFile as
|
|
13677
|
+
import { resolve as resolve41 } from "path";
|
|
13678
|
+
import { readFile as readFile23 } from "fs/promises";
|
|
13464
13679
|
var CATEGORY7 = "workspace";
|
|
13465
13680
|
var ASSIGNMENT_FIELDS = ["projectSlug", "assignmentSlug", "projectDir", "assignmentDir"];
|
|
13466
13681
|
function hasAnyAssignmentField(ctx) {
|
|
@@ -13472,12 +13687,12 @@ function isStandaloneSession(ctx) {
|
|
|
13472
13687
|
return !hasAnyAssignmentField(ctx) && typeof ctx.sessionId === "string" && ctx.sessionId.length > 0;
|
|
13473
13688
|
}
|
|
13474
13689
|
async function loadContext(ctx) {
|
|
13475
|
-
const path =
|
|
13690
|
+
const path = resolve41(ctx.cwd, ".syntaur", "context.json");
|
|
13476
13691
|
if (!await fileExists(path)) {
|
|
13477
13692
|
return { data: null, path, exists: false, parseError: null };
|
|
13478
13693
|
}
|
|
13479
13694
|
try {
|
|
13480
|
-
const raw = await
|
|
13695
|
+
const raw = await readFile23(path, "utf-8");
|
|
13481
13696
|
return { data: JSON.parse(raw), path, exists: true, parseError: null };
|
|
13482
13697
|
} catch (err2) {
|
|
13483
13698
|
return {
|
|
@@ -13552,7 +13767,7 @@ var contextAssignmentResolves = {
|
|
|
13552
13767
|
if (!exists) return skipped3(this, "no context to resolve");
|
|
13553
13768
|
if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to resolve");
|
|
13554
13769
|
if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
|
|
13555
|
-
const assignmentMd =
|
|
13770
|
+
const assignmentMd = resolve41(data.assignmentDir, "assignment.md");
|
|
13556
13771
|
if (!await fileExists(assignmentMd)) {
|
|
13557
13772
|
return {
|
|
13558
13773
|
id: this.id,
|
|
@@ -13581,10 +13796,10 @@ var contextTerminal = {
|
|
|
13581
13796
|
if (!exists) return skipped3(this, "no context to check");
|
|
13582
13797
|
if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to check");
|
|
13583
13798
|
if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
|
|
13584
|
-
const assignmentMd =
|
|
13799
|
+
const assignmentMd = resolve41(data.assignmentDir, "assignment.md");
|
|
13585
13800
|
if (!await fileExists(assignmentMd)) return skipped3(this, "assignment file missing");
|
|
13586
13801
|
try {
|
|
13587
|
-
const content = await
|
|
13802
|
+
const content = await readFile23(assignmentMd, "utf-8");
|
|
13588
13803
|
const parsed = parseAssignmentFull(content);
|
|
13589
13804
|
const terminal = terminalStatuses2(ctx);
|
|
13590
13805
|
if (terminal.has(parsed.status)) {
|
|
@@ -13728,14 +13943,14 @@ async function finalize(checks) {
|
|
|
13728
13943
|
async function readVersion() {
|
|
13729
13944
|
try {
|
|
13730
13945
|
const here = fileURLToPath7(import.meta.url);
|
|
13731
|
-
let dir =
|
|
13946
|
+
let dir = dirname11(here);
|
|
13732
13947
|
for (let i = 0; i < 6; i++) {
|
|
13733
13948
|
try {
|
|
13734
|
-
const raw = await
|
|
13949
|
+
const raw = await readFile24(join5(dir, "package.json"), "utf-8");
|
|
13735
13950
|
const parsed = JSON.parse(raw);
|
|
13736
13951
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
13737
13952
|
} catch {
|
|
13738
|
-
dir =
|
|
13953
|
+
dir = dirname11(dir);
|
|
13739
13954
|
}
|
|
13740
13955
|
}
|
|
13741
13956
|
return null;
|
|
@@ -13868,8 +14083,8 @@ var doctorCommand = new Command3("doctor").description("Diagnose Syntaur state a
|
|
|
13868
14083
|
init_paths();
|
|
13869
14084
|
init_fs();
|
|
13870
14085
|
init_config2();
|
|
13871
|
-
import { resolve as
|
|
13872
|
-
import { readFile as
|
|
14086
|
+
import { resolve as resolve42 } from "path";
|
|
14087
|
+
import { readFile as readFile25 } from "fs/promises";
|
|
13873
14088
|
init_timestamp();
|
|
13874
14089
|
init_assignment_resolver();
|
|
13875
14090
|
function shortId() {
|
|
@@ -13901,7 +14116,7 @@ async function commentCommand(target, text, options = {}) {
|
|
|
13901
14116
|
if (!isValidSlug(target)) {
|
|
13902
14117
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
13903
14118
|
}
|
|
13904
|
-
assignmentDir =
|
|
14119
|
+
assignmentDir = resolve42(baseDir, options.project, "assignments", target);
|
|
13905
14120
|
assignmentRef = target;
|
|
13906
14121
|
} else {
|
|
13907
14122
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
|
|
@@ -13911,13 +14126,13 @@ async function commentCommand(target, text, options = {}) {
|
|
|
13911
14126
|
assignmentDir = resolved.assignmentDir;
|
|
13912
14127
|
assignmentRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
|
|
13913
14128
|
}
|
|
13914
|
-
const commentsPath =
|
|
14129
|
+
const commentsPath = resolve42(assignmentDir, "comments.md");
|
|
13915
14130
|
const timestamp = nowTimestamp();
|
|
13916
14131
|
const author = options.author ?? process.env.USER ?? "unknown";
|
|
13917
14132
|
let currentContent;
|
|
13918
14133
|
let currentCount = 0;
|
|
13919
14134
|
if (await fileExists(commentsPath)) {
|
|
13920
|
-
currentContent = await
|
|
14135
|
+
currentContent = await readFile25(commentsPath, "utf-8");
|
|
13921
14136
|
const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
|
|
13922
14137
|
if (countMatch) currentCount = parseInt(countMatch[1], 10);
|
|
13923
14138
|
} else {
|
|
@@ -13954,8 +14169,8 @@ ${entry}`;
|
|
|
13954
14169
|
init_paths();
|
|
13955
14170
|
init_fs();
|
|
13956
14171
|
init_config2();
|
|
13957
|
-
import { resolve as
|
|
13958
|
-
import { readFile as
|
|
14172
|
+
import { resolve as resolve43 } from "path";
|
|
14173
|
+
import { readFile as readFile26 } from "fs/promises";
|
|
13959
14174
|
init_timestamp();
|
|
13960
14175
|
init_assignment_resolver();
|
|
13961
14176
|
function setTopLevelField3(content, key, value) {
|
|
@@ -13980,7 +14195,7 @@ async function requestCommand(target, text, options = {}) {
|
|
|
13980
14195
|
if (!isValidSlug(target)) {
|
|
13981
14196
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
13982
14197
|
}
|
|
13983
|
-
assignmentDir =
|
|
14198
|
+
assignmentDir = resolve43(baseDir, options.project, "assignments", target);
|
|
13984
14199
|
targetRef = target;
|
|
13985
14200
|
} else {
|
|
13986
14201
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
|
|
@@ -13990,12 +14205,12 @@ async function requestCommand(target, text, options = {}) {
|
|
|
13990
14205
|
assignmentDir = resolved.assignmentDir;
|
|
13991
14206
|
targetRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
|
|
13992
14207
|
}
|
|
13993
|
-
const assignmentMdPath =
|
|
14208
|
+
const assignmentMdPath = resolve43(assignmentDir, "assignment.md");
|
|
13994
14209
|
if (!await fileExists(assignmentMdPath)) {
|
|
13995
14210
|
throw new Error(`assignment.md not found at ${assignmentMdPath}`);
|
|
13996
14211
|
}
|
|
13997
14212
|
const source = options.from ?? process.env.SYNTAUR_ASSIGNMENT ?? "unknown";
|
|
13998
|
-
let content = await
|
|
14213
|
+
let content = await readFile26(assignmentMdPath, "utf-8");
|
|
13999
14214
|
const todoLine = `- [ ] ${text.trim()} (from: ${source})`;
|
|
14000
14215
|
const todosHeading = /^## Todos\s*$/m;
|
|
14001
14216
|
if (todosHeading.test(content)) {
|
|
@@ -14063,20 +14278,20 @@ async function getDefaultCommandName() {
|
|
|
14063
14278
|
init_paths();
|
|
14064
14279
|
init_fs();
|
|
14065
14280
|
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
14066
|
-
import { readFile as
|
|
14067
|
-
import { dirname as
|
|
14281
|
+
import { readFile as readFile28 } from "fs/promises";
|
|
14282
|
+
import { dirname as dirname13, join as join7, resolve as resolve44 } from "path";
|
|
14068
14283
|
import { spawn as spawn3 } from "child_process";
|
|
14069
14284
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
14070
14285
|
|
|
14071
14286
|
// src/utils/version.ts
|
|
14072
14287
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
14073
|
-
import { readFile as
|
|
14074
|
-
import { dirname as
|
|
14288
|
+
import { readFile as readFile27 } from "fs/promises";
|
|
14289
|
+
import { dirname as dirname12, join as join6 } from "path";
|
|
14075
14290
|
async function readPackageVersion(scriptUrl) {
|
|
14076
14291
|
try {
|
|
14077
14292
|
const scriptPath = fileURLToPath8(scriptUrl);
|
|
14078
|
-
const pkgRoot =
|
|
14079
|
-
const raw = await
|
|
14293
|
+
const pkgRoot = dirname12(dirname12(scriptPath));
|
|
14294
|
+
const raw = await readFile27(join6(pkgRoot, "package.json"), "utf-8");
|
|
14080
14295
|
const parsed = JSON.parse(raw);
|
|
14081
14296
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
14082
14297
|
} catch {
|
|
@@ -14085,7 +14300,7 @@ async function readPackageVersion(scriptUrl) {
|
|
|
14085
14300
|
}
|
|
14086
14301
|
|
|
14087
14302
|
// src/utils/npx-prompt.ts
|
|
14088
|
-
var STATE_FILE =
|
|
14303
|
+
var STATE_FILE = resolve44(syntaurRoot(), "npx-install.json");
|
|
14089
14304
|
var META_ARGS = /* @__PURE__ */ new Set(["-h", "--help", "-V", "--version", "help"]);
|
|
14090
14305
|
var GLOBAL_VERSION_TIMEOUT_MS = 2e3;
|
|
14091
14306
|
function isRunningViaNpx(scriptUrl) {
|
|
@@ -14106,7 +14321,7 @@ function isRunningViaNpx(scriptUrl) {
|
|
|
14106
14321
|
async function readState() {
|
|
14107
14322
|
if (!await fileExists(STATE_FILE)) return null;
|
|
14108
14323
|
try {
|
|
14109
|
-
const raw = await
|
|
14324
|
+
const raw = await readFile28(STATE_FILE, "utf-8");
|
|
14110
14325
|
return JSON.parse(raw);
|
|
14111
14326
|
} catch {
|
|
14112
14327
|
return null;
|
|
@@ -14117,7 +14332,7 @@ async function writeState(state) {
|
|
|
14117
14332
|
`);
|
|
14118
14333
|
}
|
|
14119
14334
|
async function resolveNpmBin() {
|
|
14120
|
-
const nodeDir =
|
|
14335
|
+
const nodeDir = dirname13(process.execPath);
|
|
14121
14336
|
const isWin = process.platform === "win32";
|
|
14122
14337
|
const npmName = isWin ? "npm.cmd" : "npm";
|
|
14123
14338
|
const nearNode = join7(nodeDir, npmName);
|
|
@@ -14165,7 +14380,7 @@ async function readGlobalVersion() {
|
|
|
14165
14380
|
try {
|
|
14166
14381
|
const manifestPath = join7(rootPath, "syntaur", "package.json");
|
|
14167
14382
|
if (!await fileExists(manifestPath)) return null;
|
|
14168
|
-
const raw = await
|
|
14383
|
+
const raw = await readFile28(manifestPath, "utf-8");
|
|
14169
14384
|
const parsed = JSON.parse(raw);
|
|
14170
14385
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
14171
14386
|
} catch {
|
|
@@ -14501,6 +14716,22 @@ program.command("uninstall-statusline").description(
|
|
|
14501
14716
|
process.exit(1);
|
|
14502
14717
|
}
|
|
14503
14718
|
});
|
|
14719
|
+
program.command("configure-statusline").description(
|
|
14720
|
+
"Configure which segments (git, assignment, session, model, ctx, cwd, wrap) appear in the syntaur statusLine and in what order."
|
|
14721
|
+
).option(
|
|
14722
|
+
"--preset <name>",
|
|
14723
|
+
`Preset shortcut. Choices: ${Object.keys(PRESETS).join(", ")}.`
|
|
14724
|
+
).option(
|
|
14725
|
+
"--segments <list>",
|
|
14726
|
+
'Comma-separated segment list, e.g. "git,assignment,session,model,ctx".'
|
|
14727
|
+
).option("--separator <string>", 'Segment separator (default " \xB7 ")').option("--wrap <path>", 'Path to an external statusline script to compose as a "wrap" segment').option("--preview", "Print the resolved config and a preview line without writing").action(async (options) => {
|
|
14728
|
+
try {
|
|
14729
|
+
await configureStatuslineCommand(options);
|
|
14730
|
+
} catch (error) {
|
|
14731
|
+
console.error("Error:", error instanceof Error ? error.message : String(error));
|
|
14732
|
+
process.exit(1);
|
|
14733
|
+
}
|
|
14734
|
+
});
|
|
14504
14735
|
program.command("uninstall-skills").description("Remove Syntaur protocol skills from ~/.claude/skills and/or ~/.codex/skills").option("--claude", "Remove from ~/.claude/skills").option("--codex", "Remove from ~/.codex/skills").option("--all", "Remove from both").action(async (options) => {
|
|
14505
14736
|
try {
|
|
14506
14737
|
await uninstallSkillsCommand(options);
|