syntaur 0.4.0 → 0.4.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/README.md +218 -54
- package/dist/index.js +378 -149
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/statusline/statusline.sh +137 -46
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,228 @@ 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: "session", preview: "\u2026ccddeeff", description: "Claude Code session id \u2014 last 8 chars prefixed by \u2026" },
|
|
10772
|
+
{ name: "model", preview: "Opus 4.7", description: "Claude model display name" },
|
|
10773
|
+
{ name: "ctx", preview: "ctx:[####------] 42%", description: "context window fill bar" },
|
|
10774
|
+
{ name: "cwd", preview: "syntaur", description: "basename of current working directory" },
|
|
10775
|
+
{ name: "wrap", preview: "<output of an external script>", description: "compose another statusline script as a leading segment" }
|
|
10776
|
+
];
|
|
10777
|
+
var PRESETS = {
|
|
10778
|
+
minimal: { segments: ["git", "session"], separator: " \xB7 " },
|
|
10779
|
+
syntaur: { segments: ["git", "assignment", "session"], separator: " \xB7 " },
|
|
10780
|
+
full: { segments: ["wrap", "git", "assignment", "model", "ctx", "session"], separator: " \xB7 " },
|
|
10781
|
+
dev: { segments: ["git", "assignment", "ctx", "session"], separator: " \xB7 " }
|
|
10782
|
+
};
|
|
10783
|
+
function getConfigPath(installRoot) {
|
|
10784
|
+
return resolve26(installRoot, "statusline.config.json");
|
|
10785
|
+
}
|
|
10786
|
+
async function readConfig2(path) {
|
|
10787
|
+
if (!await fileExists(path)) return null;
|
|
10788
|
+
try {
|
|
10789
|
+
const raw = await readFile17(path, "utf-8");
|
|
10790
|
+
const parsed = JSON.parse(raw);
|
|
10791
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
10792
|
+
const segments = Array.isArray(parsed.segments) ? parsed.segments.filter(isSegmentName) : [];
|
|
10793
|
+
const separator = typeof parsed.separator === "string" ? parsed.separator : " \xB7 ";
|
|
10794
|
+
const wrap = typeof parsed.wrap === "string" ? parsed.wrap : void 0;
|
|
10795
|
+
return { segments, separator, wrap };
|
|
10796
|
+
} catch {
|
|
10797
|
+
return null;
|
|
10798
|
+
}
|
|
10799
|
+
}
|
|
10800
|
+
function isSegmentName(value) {
|
|
10801
|
+
return typeof value === "string" && AVAILABLE_SEGMENTS.some((s) => s.name === value);
|
|
10802
|
+
}
|
|
10803
|
+
function parseSegmentsFlag(flag) {
|
|
10804
|
+
const parts = flag.split(",").map((s) => s.trim()).filter(Boolean);
|
|
10805
|
+
const invalid = parts.filter((p) => !AVAILABLE_SEGMENTS.some((s) => s.name === p));
|
|
10806
|
+
if (invalid.length > 0) {
|
|
10807
|
+
throw new Error(
|
|
10808
|
+
`Unknown segment${invalid.length > 1 ? "s" : ""}: ${invalid.join(", ")}. Valid segments: ${AVAILABLE_SEGMENTS.map((s) => s.name).join(", ")}.`
|
|
10809
|
+
);
|
|
10810
|
+
}
|
|
10811
|
+
return parts;
|
|
10812
|
+
}
|
|
10813
|
+
async function promptSegmentsInteractive(current) {
|
|
10814
|
+
const canonicalOrder = ["wrap", "git", "assignment", "session", "model", "ctx", "cwd"];
|
|
10815
|
+
const selectedSet = new Set(current?.segments ?? ["git", "assignment", "session"]);
|
|
10816
|
+
const choices = canonicalOrder.map((name) => {
|
|
10817
|
+
const def = AVAILABLE_SEGMENTS.find((s) => s.name === name);
|
|
10818
|
+
return {
|
|
10819
|
+
name: `${def.name.padEnd(11)} ${def.description}`,
|
|
10820
|
+
value: name,
|
|
10821
|
+
checked: selectedSet.has(name),
|
|
10822
|
+
description: `preview: ${def.preview}`
|
|
10823
|
+
};
|
|
10824
|
+
});
|
|
10825
|
+
const selected = await checkbox({
|
|
10826
|
+
message: "Pick segments (space to toggle, enter to confirm):",
|
|
10827
|
+
choices,
|
|
10828
|
+
loop: false,
|
|
10829
|
+
pageSize: choices.length,
|
|
10830
|
+
required: true
|
|
10831
|
+
});
|
|
10832
|
+
const defaultReorderHint = selected.join(", ");
|
|
10833
|
+
let orderedSegments = [...selected];
|
|
10834
|
+
const wantReorder = await confirm({
|
|
10835
|
+
message: `Order will be: ${defaultReorderHint}. Customize order?`,
|
|
10836
|
+
default: false
|
|
10837
|
+
});
|
|
10838
|
+
if (wantReorder) {
|
|
10839
|
+
const raw = await input2({
|
|
10840
|
+
message: `Enter the segments in the order you want, comma-separated:`,
|
|
10841
|
+
default: defaultReorderHint,
|
|
10842
|
+
validate: (value) => {
|
|
10843
|
+
const parts = value.split(",").map((s) => s.trim()).filter(Boolean);
|
|
10844
|
+
const invalid = parts.filter((p) => !canonicalOrder.includes(p));
|
|
10845
|
+
if (invalid.length > 0) {
|
|
10846
|
+
return `Unknown: ${invalid.join(", ")}. Valid: ${canonicalOrder.join(", ")}.`;
|
|
10847
|
+
}
|
|
10848
|
+
const missing = selected.filter((s) => !parts.includes(s));
|
|
10849
|
+
if (missing.length > 0) {
|
|
10850
|
+
return `Missing previously-selected segment(s): ${missing.join(", ")}. Include all of them or go back.`;
|
|
10851
|
+
}
|
|
10852
|
+
return true;
|
|
10853
|
+
}
|
|
10854
|
+
});
|
|
10855
|
+
orderedSegments = raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
10856
|
+
}
|
|
10857
|
+
const separator = await input2({
|
|
10858
|
+
message: "Separator between segments:",
|
|
10859
|
+
default: current?.separator ?? " \xB7 "
|
|
10860
|
+
});
|
|
10861
|
+
let wrap = current?.wrap;
|
|
10862
|
+
if (orderedSegments.includes("wrap")) {
|
|
10863
|
+
wrap = await input2({
|
|
10864
|
+
message: "Path to external script to wrap (leave blank to skip):",
|
|
10865
|
+
default: current?.wrap ?? ""
|
|
10866
|
+
});
|
|
10867
|
+
wrap = wrap.trim() ? wrap.trim() : void 0;
|
|
10868
|
+
}
|
|
10869
|
+
return { segments: orderedSegments, separator, wrap };
|
|
10870
|
+
}
|
|
10871
|
+
function renderPreview(config, statuslineScript, cwd) {
|
|
10872
|
+
const payload = {
|
|
10873
|
+
session_id: "preview-demo-0000000000abcdef12",
|
|
10874
|
+
cwd,
|
|
10875
|
+
model: { display_name: "Opus 4.7" },
|
|
10876
|
+
context_window: { used_percentage: 42 }
|
|
10877
|
+
};
|
|
10878
|
+
const res = spawnSync("bash", [statuslineScript], {
|
|
10879
|
+
input: JSON.stringify(payload),
|
|
10880
|
+
encoding: "utf-8",
|
|
10881
|
+
env: {
|
|
10882
|
+
...process.env,
|
|
10883
|
+
// Force the child to pick up the freshly-written config from install root.
|
|
10884
|
+
HOME: dirname8(dirname8(statuslineScript))
|
|
10885
|
+
}
|
|
10886
|
+
});
|
|
10887
|
+
if (res.status !== 0) return null;
|
|
10888
|
+
return res.stdout;
|
|
10889
|
+
}
|
|
10890
|
+
async function configureStatuslineCommand(options = {}) {
|
|
10891
|
+
const installRoot = options.installRoot ?? syntaurRoot();
|
|
10892
|
+
const configPath = getConfigPath(installRoot);
|
|
10893
|
+
const current = await readConfig2(configPath);
|
|
10894
|
+
let segments;
|
|
10895
|
+
let separator;
|
|
10896
|
+
let wrap;
|
|
10897
|
+
if (options.preset) {
|
|
10898
|
+
const preset = PRESETS[options.preset.toLowerCase()];
|
|
10899
|
+
if (!preset) {
|
|
10900
|
+
throw new Error(
|
|
10901
|
+
`Unknown preset "${options.preset}". Presets: ${Object.keys(PRESETS).join(", ")}.`
|
|
10902
|
+
);
|
|
10903
|
+
}
|
|
10904
|
+
segments = [...preset.segments];
|
|
10905
|
+
separator = options.separator ?? preset.separator;
|
|
10906
|
+
wrap = options.wrap ?? current?.wrap;
|
|
10907
|
+
} else if (options.segments) {
|
|
10908
|
+
segments = parseSegmentsFlag(options.segments);
|
|
10909
|
+
separator = options.separator ?? current?.separator ?? " \xB7 ";
|
|
10910
|
+
wrap = options.wrap ?? current?.wrap;
|
|
10911
|
+
} else if (isInteractiveTerminal()) {
|
|
10912
|
+
const answers = await promptSegmentsInteractive(current);
|
|
10913
|
+
segments = answers.segments;
|
|
10914
|
+
separator = answers.separator;
|
|
10915
|
+
wrap = answers.wrap;
|
|
10916
|
+
} else {
|
|
10917
|
+
throw new Error(
|
|
10918
|
+
"Non-interactive invocation requires --preset, --segments, or run in a TTY."
|
|
10919
|
+
);
|
|
10920
|
+
}
|
|
10921
|
+
if (segments.includes("wrap") && !wrap) {
|
|
10922
|
+
console.warn(
|
|
10923
|
+
`Note: the "wrap" segment is selected but no wrap path is configured. Set one with --wrap <path> or edit ${configPath} afterwards.`
|
|
10924
|
+
);
|
|
10925
|
+
}
|
|
10926
|
+
const config = { segments, separator, ...wrap ? { wrap } : {} };
|
|
10927
|
+
if (options.preview) {
|
|
10928
|
+
console.log("Segments: " + config.segments.join(", "));
|
|
10929
|
+
console.log("Separator: " + JSON.stringify(config.separator));
|
|
10930
|
+
if (config.wrap) console.log("Wrap: " + config.wrap);
|
|
10931
|
+
console.log("");
|
|
10932
|
+
console.log("(preview mode \u2014 config NOT written)");
|
|
10933
|
+
return;
|
|
10934
|
+
}
|
|
10935
|
+
await ensureDir(dirname8(configPath));
|
|
10936
|
+
await writeFile7(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
10937
|
+
console.log("Wrote statusline config:");
|
|
10938
|
+
console.log(` path: ${configPath}`);
|
|
10939
|
+
console.log(` segments: ${config.segments.join(", ")}`);
|
|
10940
|
+
console.log(` separator: ${JSON.stringify(config.separator)}`);
|
|
10941
|
+
if (config.wrap) console.log(` wrap: ${config.wrap}`);
|
|
10942
|
+
const script = options.statuslineScript ?? resolve26(installRoot, "statusline.sh");
|
|
10943
|
+
if (await fileExists(script)) {
|
|
10944
|
+
console.log("");
|
|
10945
|
+
console.log("Live preview:");
|
|
10946
|
+
const out = renderPreview(config, script, process.cwd());
|
|
10947
|
+
if (out) {
|
|
10948
|
+
console.log(" " + out);
|
|
10949
|
+
} else {
|
|
10950
|
+
console.log(" (preview failed \u2014 run `syntaur install-statusline` if the script is missing)");
|
|
10951
|
+
}
|
|
10952
|
+
} else {
|
|
10953
|
+
console.log("");
|
|
10954
|
+
console.log(
|
|
10955
|
+
"(statusline script not yet installed \u2014 run `syntaur install-statusline` to wire it up)"
|
|
10956
|
+
);
|
|
10957
|
+
}
|
|
10958
|
+
}
|
|
10959
|
+
async function writeDefaultConfigIfMissing(installRoot) {
|
|
10960
|
+
const path = getConfigPath(installRoot);
|
|
10961
|
+
if (await fileExists(path)) return;
|
|
10962
|
+
await ensureDir(dirname8(path));
|
|
10963
|
+
const defaultConfig = {
|
|
10964
|
+
segments: ["git", "assignment", "session"],
|
|
10965
|
+
separator: " \xB7 "
|
|
10966
|
+
};
|
|
10967
|
+
await writeFile7(path, JSON.stringify(defaultConfig, null, 2) + "\n", "utf-8");
|
|
10968
|
+
}
|
|
10969
|
+
|
|
10970
|
+
// src/commands/install-statusline.ts
|
|
10760
10971
|
function getPackageStatuslineSource() {
|
|
10761
|
-
const here =
|
|
10762
|
-
return
|
|
10972
|
+
const here = dirname9(fileURLToPath5(import.meta.url));
|
|
10973
|
+
return resolve27(here, "..", "statusline", "statusline.sh");
|
|
10763
10974
|
}
|
|
10764
10975
|
async function readSettingsJson(settingsPath) {
|
|
10765
10976
|
if (!await fileExists(settingsPath)) return {};
|
|
10766
|
-
const raw = await
|
|
10977
|
+
const raw = await readFile18(settingsPath, "utf-8");
|
|
10767
10978
|
if (raw.trim() === "") return {};
|
|
10768
10979
|
try {
|
|
10769
10980
|
const parsed = JSON.parse(raw);
|
|
@@ -10775,8 +10986,8 @@ async function readSettingsJson(settingsPath) {
|
|
|
10775
10986
|
}
|
|
10776
10987
|
}
|
|
10777
10988
|
async function writeSettingsJson(settingsPath, data) {
|
|
10778
|
-
await ensureDir(
|
|
10779
|
-
await
|
|
10989
|
+
await ensureDir(dirname9(settingsPath));
|
|
10990
|
+
await writeFile8(settingsPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
10780
10991
|
}
|
|
10781
10992
|
async function resolveMode(mode, existingCommand, ourCommand) {
|
|
10782
10993
|
if (mode !== "ask") return mode;
|
|
@@ -10810,8 +11021,8 @@ function extractExistingCommand(settings) {
|
|
|
10810
11021
|
};
|
|
10811
11022
|
}
|
|
10812
11023
|
async function backupSettings(settingsSnapshot, backupPath) {
|
|
10813
|
-
await ensureDir(
|
|
10814
|
-
await
|
|
11024
|
+
await ensureDir(dirname9(backupPath));
|
|
11025
|
+
await writeFile8(
|
|
10815
11026
|
backupPath,
|
|
10816
11027
|
JSON.stringify(
|
|
10817
11028
|
{
|
|
@@ -10827,7 +11038,7 @@ async function backupSettings(settingsSnapshot, backupPath) {
|
|
|
10827
11038
|
);
|
|
10828
11039
|
}
|
|
10829
11040
|
async function installScript(sourceScript, destScript, link) {
|
|
10830
|
-
await ensureDir(
|
|
11041
|
+
await ensureDir(dirname9(destScript));
|
|
10831
11042
|
try {
|
|
10832
11043
|
const s = await lstat2(destScript);
|
|
10833
11044
|
if (s.isSymbolicLink() || s.isFile()) {
|
|
@@ -10843,18 +11054,19 @@ async function installScript(sourceScript, destScript, link) {
|
|
|
10843
11054
|
}
|
|
10844
11055
|
async function installStatuslineCommand(options = {}) {
|
|
10845
11056
|
const mode = options.mode ?? "ask";
|
|
10846
|
-
const settingsPath = options.settingsPath ??
|
|
11057
|
+
const settingsPath = options.settingsPath ?? resolve27(homedir4(), ".claude", "settings.json");
|
|
10847
11058
|
const installRoot = options.installRoot ?? syntaurRoot();
|
|
10848
11059
|
const sourceScript = options.sourceScript ?? getPackageStatuslineSource();
|
|
10849
|
-
const destScript =
|
|
10850
|
-
const confPath =
|
|
10851
|
-
const backupPath =
|
|
11060
|
+
const destScript = resolve27(installRoot, "statusline.sh");
|
|
11061
|
+
const confPath = resolve27(installRoot, "statusline.conf");
|
|
11062
|
+
const backupPath = resolve27(installRoot, "statusline.backup.json");
|
|
10852
11063
|
if (!await fileExists(sourceScript)) {
|
|
10853
11064
|
throw new Error(
|
|
10854
11065
|
`Statusline source script not found at ${sourceScript}. Try re-installing syntaur (npm install -g syntaur) or pass --source-script explicitly.`
|
|
10855
11066
|
);
|
|
10856
11067
|
}
|
|
10857
11068
|
await installScript(sourceScript, destScript, Boolean(options.link));
|
|
11069
|
+
await writeDefaultConfigIfMissing(installRoot);
|
|
10858
11070
|
const settings = await readSettingsJson(settingsPath);
|
|
10859
11071
|
const existingStatusLine = extractExistingCommand(settings);
|
|
10860
11072
|
const existingCommand = existingStatusLine?.command;
|
|
@@ -10883,19 +11095,19 @@ async function installStatuslineCommand(options = {}) {
|
|
|
10883
11095
|
if (parsed) {
|
|
10884
11096
|
wrapTarget = parsed;
|
|
10885
11097
|
} else {
|
|
10886
|
-
const wrapperPath =
|
|
11098
|
+
const wrapperPath = resolve27(installRoot, "statusline-wrapped.sh");
|
|
10887
11099
|
const wrapperBody = `#!/usr/bin/env bash
|
|
10888
11100
|
# Auto-generated by syntaur install-statusline.
|
|
10889
11101
|
# Executes the previously configured statusLine command.
|
|
10890
11102
|
exec ${existingCommand}
|
|
10891
11103
|
`;
|
|
10892
|
-
await
|
|
11104
|
+
await writeFile8(wrapperPath, wrapperBody, "utf-8");
|
|
10893
11105
|
await chmodExec(wrapperPath);
|
|
10894
11106
|
wrapTarget = wrapperPath;
|
|
10895
11107
|
}
|
|
10896
11108
|
}
|
|
10897
|
-
await ensureDir(
|
|
10898
|
-
await
|
|
11109
|
+
await ensureDir(dirname9(confPath));
|
|
11110
|
+
await writeFile8(
|
|
10899
11111
|
confPath,
|
|
10900
11112
|
wrapTarget ? `# Wrap target \u2014 the command below is invoked with the same stdin; its
|
|
10901
11113
|
# stdout becomes the leading segment of the statusline. Remove this
|
|
@@ -10938,19 +11150,19 @@ async function chmodExec(path) {
|
|
|
10938
11150
|
}
|
|
10939
11151
|
}
|
|
10940
11152
|
async function uninstallStatuslineCommand(options = {}) {
|
|
10941
|
-
const settingsPath = options.settingsPath ??
|
|
11153
|
+
const settingsPath = options.settingsPath ?? resolve27(homedir4(), ".claude", "settings.json");
|
|
10942
11154
|
const installRoot = options.installRoot ?? syntaurRoot();
|
|
10943
|
-
const destScript =
|
|
10944
|
-
const confPath =
|
|
10945
|
-
const backupPath =
|
|
10946
|
-
const wrapperPath =
|
|
11155
|
+
const destScript = resolve27(installRoot, "statusline.sh");
|
|
11156
|
+
const confPath = resolve27(installRoot, "statusline.conf");
|
|
11157
|
+
const backupPath = resolve27(installRoot, "statusline.backup.json");
|
|
11158
|
+
const wrapperPath = resolve27(installRoot, "statusline-wrapped.sh");
|
|
10947
11159
|
const settings = await readSettingsJson(settingsPath);
|
|
10948
11160
|
const existing = extractExistingCommand(settings);
|
|
10949
11161
|
const ourCommand = `bash ${destScript}`;
|
|
10950
11162
|
let restored = null;
|
|
10951
11163
|
if (await fileExists(backupPath)) {
|
|
10952
11164
|
try {
|
|
10953
|
-
const raw = await
|
|
11165
|
+
const raw = await readFile18(backupPath, "utf-8");
|
|
10954
11166
|
const parsed = JSON.parse(raw);
|
|
10955
11167
|
const prev = parsed?.previousStatusLine;
|
|
10956
11168
|
if (prev && typeof prev === "object" && typeof prev.command === "string") {
|
|
@@ -10971,7 +11183,8 @@ async function uninstallStatuslineCommand(options = {}) {
|
|
|
10971
11183
|
await writeSettingsJson(settingsPath, settings);
|
|
10972
11184
|
}
|
|
10973
11185
|
if (!options.keepScript) {
|
|
10974
|
-
|
|
11186
|
+
const configPath = resolve27(installRoot, "statusline.config.json");
|
|
11187
|
+
for (const path of [destScript, confPath, backupPath, wrapperPath, configPath]) {
|
|
10975
11188
|
try {
|
|
10976
11189
|
await rm5(path, { force: true });
|
|
10977
11190
|
} catch {
|
|
@@ -11225,7 +11438,7 @@ async function setupCommand(options) {
|
|
|
11225
11438
|
}
|
|
11226
11439
|
|
|
11227
11440
|
// src/commands/uninstall.ts
|
|
11228
|
-
import { resolve as
|
|
11441
|
+
import { resolve as resolve28 } from "path";
|
|
11229
11442
|
init_paths();
|
|
11230
11443
|
function expandTargets(options) {
|
|
11231
11444
|
if (options.all) {
|
|
@@ -11305,7 +11518,7 @@ async function uninstallCommand(options) {
|
|
|
11305
11518
|
const configuredProjectDir = await getConfiguredProjectDir();
|
|
11306
11519
|
await removeSyntaurData();
|
|
11307
11520
|
console.log(`Removed ${syntaurRoot()}`);
|
|
11308
|
-
if (configuredProjectDir &&
|
|
11521
|
+
if (configuredProjectDir && resolve28(configuredProjectDir) !== resolve28(syntaurRoot(), "projects")) {
|
|
11309
11522
|
console.warn(
|
|
11310
11523
|
`Warning: config.md pointed to an external project directory (${configuredProjectDir}). That directory was not removed automatically.`
|
|
11311
11524
|
);
|
|
@@ -11320,7 +11533,7 @@ async function uninstallCommand(options) {
|
|
|
11320
11533
|
init_paths();
|
|
11321
11534
|
init_fs();
|
|
11322
11535
|
init_config2();
|
|
11323
|
-
import { resolve as
|
|
11536
|
+
import { resolve as resolve29 } from "path";
|
|
11324
11537
|
var SUPPORTED_FRAMEWORKS = ["cursor", "codex", "opencode"];
|
|
11325
11538
|
async function setupAdapterCommand(framework, options) {
|
|
11326
11539
|
if (!SUPPORTED_FRAMEWORKS.includes(framework)) {
|
|
@@ -11346,19 +11559,19 @@ async function setupAdapterCommand(framework, options) {
|
|
|
11346
11559
|
}
|
|
11347
11560
|
const config = await readConfig();
|
|
11348
11561
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
11349
|
-
const projectDir =
|
|
11350
|
-
const assignmentDir =
|
|
11562
|
+
const projectDir = resolve29(baseDir, options.project);
|
|
11563
|
+
const assignmentDir = resolve29(
|
|
11351
11564
|
projectDir,
|
|
11352
11565
|
"assignments",
|
|
11353
11566
|
options.assignment
|
|
11354
11567
|
);
|
|
11355
|
-
const projectMdPath =
|
|
11568
|
+
const projectMdPath = resolve29(projectDir, "project.md");
|
|
11356
11569
|
if (!await fileExists(projectDir) || !await fileExists(projectMdPath)) {
|
|
11357
11570
|
throw new Error(
|
|
11358
11571
|
`Project "${options.project}" not found at ${projectDir}.`
|
|
11359
11572
|
);
|
|
11360
11573
|
}
|
|
11361
|
-
const assignmentMdPath =
|
|
11574
|
+
const assignmentMdPath = resolve29(assignmentDir, "assignment.md");
|
|
11362
11575
|
if (!await fileExists(assignmentDir) || !await fileExists(assignmentMdPath)) {
|
|
11363
11576
|
throw new Error(
|
|
11364
11577
|
`Assignment "${options.assignment}" not found at ${assignmentDir}.`
|
|
@@ -11386,15 +11599,15 @@ async function setupAdapterCommand(framework, options) {
|
|
|
11386
11599
|
}
|
|
11387
11600
|
}
|
|
11388
11601
|
if (framework === "cursor") {
|
|
11389
|
-
const protocolPath =
|
|
11390
|
-
const assignmentPath =
|
|
11602
|
+
const protocolPath = resolve29(cwd, ".cursor", "rules", "syntaur-protocol.mdc");
|
|
11603
|
+
const assignmentPath = resolve29(cwd, ".cursor", "rules", "syntaur-assignment.mdc");
|
|
11391
11604
|
await writeAdapterFile(protocolPath, renderCursorProtocol());
|
|
11392
11605
|
await writeAdapterFile(assignmentPath, renderCursorAssignment(rendererParams));
|
|
11393
11606
|
} else if (framework === "codex" || framework === "opencode") {
|
|
11394
|
-
const agentsPath =
|
|
11607
|
+
const agentsPath = resolve29(cwd, "AGENTS.md");
|
|
11395
11608
|
await writeAdapterFile(agentsPath, renderCodexAgents(rendererParams));
|
|
11396
11609
|
if (framework === "opencode") {
|
|
11397
|
-
const configPath =
|
|
11610
|
+
const configPath = resolve29(cwd, "opencode.json");
|
|
11398
11611
|
await writeAdapterFile(configPath, renderOpenCodeConfig({ projectDir }));
|
|
11399
11612
|
}
|
|
11400
11613
|
}
|
|
@@ -11419,7 +11632,7 @@ async function setupAdapterCommand(framework, options) {
|
|
|
11419
11632
|
init_paths();
|
|
11420
11633
|
init_fs();
|
|
11421
11634
|
init_config2();
|
|
11422
|
-
import { resolve as
|
|
11635
|
+
import { resolve as resolve30 } from "path";
|
|
11423
11636
|
async function trackSessionCommand(options) {
|
|
11424
11637
|
if (!options.agent) {
|
|
11425
11638
|
throw new Error("--agent <name> is required.");
|
|
@@ -11432,7 +11645,7 @@ async function trackSessionCommand(options) {
|
|
|
11432
11645
|
if (options.project) {
|
|
11433
11646
|
const config = await readConfig();
|
|
11434
11647
|
const baseDir = options.dir ? expandHome(options.dir) : config.defaultProjectDir;
|
|
11435
|
-
const projectDir =
|
|
11648
|
+
const projectDir = resolve30(baseDir, options.project);
|
|
11436
11649
|
if (!await fileExists(projectDir)) {
|
|
11437
11650
|
throw new Error(
|
|
11438
11651
|
`Project "${options.project}" not found at ${projectDir}.`
|
|
@@ -11487,7 +11700,7 @@ async function browseCommand(options) {
|
|
|
11487
11700
|
}
|
|
11488
11701
|
|
|
11489
11702
|
// src/commands/create-playbook.ts
|
|
11490
|
-
import { resolve as
|
|
11703
|
+
import { resolve as resolve32 } from "path";
|
|
11491
11704
|
init_timestamp();
|
|
11492
11705
|
init_paths();
|
|
11493
11706
|
init_fs();
|
|
@@ -11503,7 +11716,7 @@ async function createPlaybookCommand(name, options) {
|
|
|
11503
11716
|
}
|
|
11504
11717
|
const dir = playbooksDir();
|
|
11505
11718
|
await ensureDir(dir);
|
|
11506
|
-
const filePath =
|
|
11719
|
+
const filePath = resolve32(dir, `${slug}.md`);
|
|
11507
11720
|
if (await fileExists(filePath)) {
|
|
11508
11721
|
throw new Error(
|
|
11509
11722
|
`Playbook "${slug}" already exists at ${filePath}
|
|
@@ -11524,8 +11737,8 @@ Use --slug to specify a different slug.`
|
|
|
11524
11737
|
init_paths();
|
|
11525
11738
|
init_fs();
|
|
11526
11739
|
init_parser();
|
|
11527
|
-
import { readdir as readdir12, readFile as
|
|
11528
|
-
import { resolve as
|
|
11740
|
+
import { readdir as readdir12, readFile as readFile19 } from "fs/promises";
|
|
11741
|
+
import { resolve as resolve33 } from "path";
|
|
11529
11742
|
async function listPlaybooksCommand() {
|
|
11530
11743
|
const dir = playbooksDir();
|
|
11531
11744
|
if (!await fileExists(dir)) {
|
|
@@ -11543,8 +11756,8 @@ async function listPlaybooksCommand() {
|
|
|
11543
11756
|
console.log(`${"Slug".padEnd(30)} ${"Name".padEnd(30)} Description`);
|
|
11544
11757
|
console.log(`${"\u2500".repeat(30)} ${"\u2500".repeat(30)} ${"\u2500".repeat(40)}`);
|
|
11545
11758
|
for (const entry of mdFiles) {
|
|
11546
|
-
const filePath =
|
|
11547
|
-
const raw = await
|
|
11759
|
+
const filePath = resolve33(dir, entry.name);
|
|
11760
|
+
const raw = await readFile19(filePath, "utf-8");
|
|
11548
11761
|
const parsed = parsePlaybook(raw);
|
|
11549
11762
|
const slug = parsed.slug || entry.name.replace(/\.md$/, "");
|
|
11550
11763
|
const name = parsed.name || slug;
|
|
@@ -11558,8 +11771,8 @@ init_paths();
|
|
|
11558
11771
|
init_parser2();
|
|
11559
11772
|
init_fs();
|
|
11560
11773
|
import { Command } from "commander";
|
|
11561
|
-
import { readFile as
|
|
11562
|
-
import { resolve as
|
|
11774
|
+
import { readFile as readFile20 } from "fs/promises";
|
|
11775
|
+
import { resolve as resolve34 } from "path";
|
|
11563
11776
|
var WORKSPACE_REGEX2 = /^[a-z0-9_][a-z0-9-]*$/;
|
|
11564
11777
|
function resolveWorkspace(options) {
|
|
11565
11778
|
if (options.global) return "_global";
|
|
@@ -11840,10 +12053,10 @@ todoCommand.command("archive").description("Archive completed todos and their lo
|
|
|
11840
12053
|
(e) => e.itemIds.every((id) => completedIds.has(id))
|
|
11841
12054
|
);
|
|
11842
12055
|
const archFile = archivePath(todosPath, workspace, checklist.archiveInterval);
|
|
11843
|
-
await ensureDir(
|
|
12056
|
+
await ensureDir(resolve34(todosPath, "archive"));
|
|
11844
12057
|
let archContent = "";
|
|
11845
12058
|
if (await fileExists(archFile)) {
|
|
11846
|
-
archContent = await
|
|
12059
|
+
archContent = await readFile20(archFile, "utf-8");
|
|
11847
12060
|
archContent = archContent.trimEnd() + "\n\n";
|
|
11848
12061
|
} else {
|
|
11849
12062
|
archContent = `---
|
|
@@ -12032,19 +12245,19 @@ import { Command as Command3 } from "commander";
|
|
|
12032
12245
|
|
|
12033
12246
|
// src/utils/doctor/index.ts
|
|
12034
12247
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
12035
|
-
import { readFile as
|
|
12036
|
-
import { dirname as
|
|
12248
|
+
import { readFile as readFile24 } from "fs/promises";
|
|
12249
|
+
import { dirname as dirname11, join as join5 } from "path";
|
|
12037
12250
|
|
|
12038
12251
|
// src/utils/doctor/context.ts
|
|
12039
12252
|
init_config2();
|
|
12040
12253
|
init_paths();
|
|
12041
12254
|
init_fs();
|
|
12042
12255
|
import Database2 from "better-sqlite3";
|
|
12043
|
-
import { resolve as
|
|
12256
|
+
import { resolve as resolve35 } from "path";
|
|
12044
12257
|
async function buildCheckContext(cwd = process.cwd()) {
|
|
12045
12258
|
const config = await readConfig();
|
|
12046
12259
|
const root = syntaurRoot();
|
|
12047
|
-
const dbPath =
|
|
12260
|
+
const dbPath = resolve35(root, "syntaur.db");
|
|
12048
12261
|
let db2 = null;
|
|
12049
12262
|
let dbError = null;
|
|
12050
12263
|
if (await fileExists(dbPath)) {
|
|
@@ -12078,10 +12291,10 @@ function closeCheckContext(ctx) {
|
|
|
12078
12291
|
// src/utils/doctor/checks/env.ts
|
|
12079
12292
|
init_fs();
|
|
12080
12293
|
init_paths();
|
|
12081
|
-
import { resolve as
|
|
12082
|
-
import { readFile as
|
|
12294
|
+
import { resolve as resolve36, isAbsolute as isAbsolute3 } from "path";
|
|
12295
|
+
import { readFile as readFile21, stat as stat4 } from "fs/promises";
|
|
12083
12296
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
12084
|
-
import { dirname as
|
|
12297
|
+
import { dirname as dirname10, join as join4 } from "path";
|
|
12085
12298
|
var CATEGORY = "env";
|
|
12086
12299
|
var syntaurRootExists = {
|
|
12087
12300
|
id: "env.syntaur-root-exists",
|
|
@@ -12119,7 +12332,7 @@ var configValid = {
|
|
|
12119
12332
|
category: CATEGORY,
|
|
12120
12333
|
title: "~/.syntaur/config.md is valid",
|
|
12121
12334
|
async run(ctx) {
|
|
12122
|
-
const configPath =
|
|
12335
|
+
const configPath = resolve36(ctx.syntaurRoot, "config.md");
|
|
12123
12336
|
if (!await fileExists(configPath)) {
|
|
12124
12337
|
return {
|
|
12125
12338
|
id: this.id,
|
|
@@ -12136,7 +12349,7 @@ var configValid = {
|
|
|
12136
12349
|
autoFixable: false
|
|
12137
12350
|
};
|
|
12138
12351
|
}
|
|
12139
|
-
const content = await
|
|
12352
|
+
const content = await readFile21(configPath, "utf-8");
|
|
12140
12353
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
12141
12354
|
if (!fmMatch || fmMatch[1].trim() === "") {
|
|
12142
12355
|
return {
|
|
@@ -12412,14 +12625,14 @@ async function readLocalVersion() {
|
|
|
12412
12625
|
async function readLocalPkg() {
|
|
12413
12626
|
try {
|
|
12414
12627
|
const here = fileURLToPath6(import.meta.url);
|
|
12415
|
-
let dir =
|
|
12628
|
+
let dir = dirname10(here);
|
|
12416
12629
|
for (let i = 0; i < 6; i++) {
|
|
12417
12630
|
const candidate = join4(dir, "package.json");
|
|
12418
12631
|
try {
|
|
12419
|
-
const text = await
|
|
12632
|
+
const text = await readFile21(candidate, "utf-8");
|
|
12420
12633
|
return JSON.parse(text);
|
|
12421
12634
|
} catch {
|
|
12422
|
-
dir =
|
|
12635
|
+
dir = dirname10(dir);
|
|
12423
12636
|
}
|
|
12424
12637
|
}
|
|
12425
12638
|
return null;
|
|
@@ -12468,7 +12681,7 @@ function versionGte(a, b) {
|
|
|
12468
12681
|
|
|
12469
12682
|
// src/utils/doctor/checks/structure.ts
|
|
12470
12683
|
init_fs();
|
|
12471
|
-
import { resolve as
|
|
12684
|
+
import { resolve as resolve37 } from "path";
|
|
12472
12685
|
import { readdir as readdir13, stat as stat5 } from "fs/promises";
|
|
12473
12686
|
var CATEGORY2 = "structure";
|
|
12474
12687
|
var KNOWN_TOP_LEVEL = /* @__PURE__ */ new Set([
|
|
@@ -12488,7 +12701,7 @@ var projectsDir = {
|
|
|
12488
12701
|
category: CATEGORY2,
|
|
12489
12702
|
title: "projects/ directory exists",
|
|
12490
12703
|
async run(ctx) {
|
|
12491
|
-
const p =
|
|
12704
|
+
const p = resolve37(ctx.syntaurRoot, "projects");
|
|
12492
12705
|
if (!await fileExists(p)) {
|
|
12493
12706
|
return {
|
|
12494
12707
|
id: this.id,
|
|
@@ -12513,7 +12726,7 @@ var playbooksDir2 = {
|
|
|
12513
12726
|
category: CATEGORY2,
|
|
12514
12727
|
title: "playbooks/ directory exists",
|
|
12515
12728
|
async run(ctx) {
|
|
12516
|
-
const p =
|
|
12729
|
+
const p = resolve37(ctx.syntaurRoot, "playbooks");
|
|
12517
12730
|
if (!await fileExists(p)) {
|
|
12518
12731
|
return {
|
|
12519
12732
|
id: this.id,
|
|
@@ -12538,7 +12751,7 @@ var todosDirValid = {
|
|
|
12538
12751
|
category: CATEGORY2,
|
|
12539
12752
|
title: "todos/ directory is readable (if present)",
|
|
12540
12753
|
async run(ctx) {
|
|
12541
|
-
const p =
|
|
12754
|
+
const p = resolve37(ctx.syntaurRoot, "todos");
|
|
12542
12755
|
if (!await fileExists(p)) {
|
|
12543
12756
|
return {
|
|
12544
12757
|
id: this.id,
|
|
@@ -12569,7 +12782,7 @@ var serversDirValid = {
|
|
|
12569
12782
|
category: CATEGORY2,
|
|
12570
12783
|
title: "servers/ directory is readable (if present)",
|
|
12571
12784
|
async run(ctx) {
|
|
12572
|
-
const p =
|
|
12785
|
+
const p = resolve37(ctx.syntaurRoot, "servers");
|
|
12573
12786
|
if (!await fileExists(p)) {
|
|
12574
12787
|
return {
|
|
12575
12788
|
id: this.id,
|
|
@@ -12614,7 +12827,7 @@ var knownFilesRecognized = {
|
|
|
12614
12827
|
title: this.title,
|
|
12615
12828
|
status: "warn",
|
|
12616
12829
|
detail: `unexpected top-level entries: ${unexpected.join(", ")}`,
|
|
12617
|
-
affected: unexpected.map((n) =>
|
|
12830
|
+
affected: unexpected.map((n) => resolve37(ctx.syntaurRoot, n)),
|
|
12618
12831
|
remediation: {
|
|
12619
12832
|
kind: "manual",
|
|
12620
12833
|
suggestion: "Review these entries \u2014 they may be leftover state from older versions",
|
|
@@ -12643,7 +12856,7 @@ function pass2(check) {
|
|
|
12643
12856
|
|
|
12644
12857
|
// src/utils/doctor/checks/project.ts
|
|
12645
12858
|
init_fs();
|
|
12646
|
-
import { resolve as
|
|
12859
|
+
import { resolve as resolve38 } from "path";
|
|
12647
12860
|
import { readdir as readdir14, stat as stat6 } from "fs/promises";
|
|
12648
12861
|
var CATEGORY3 = "project";
|
|
12649
12862
|
var REQUIRED_PROJECT_FILES = [
|
|
@@ -12673,10 +12886,10 @@ async function listProjects2(ctx) {
|
|
|
12673
12886
|
for (const e of entries) {
|
|
12674
12887
|
if (!e.isDirectory()) continue;
|
|
12675
12888
|
if (e.name.startsWith(".") || e.name.startsWith("_")) continue;
|
|
12676
|
-
const projectDir =
|
|
12889
|
+
const projectDir = resolve38(dir, e.name);
|
|
12677
12890
|
let looksLikeProject = false;
|
|
12678
12891
|
for (const marker of PROJECT_MARKERS) {
|
|
12679
|
-
if (await fileExists(
|
|
12892
|
+
if (await fileExists(resolve38(projectDir, marker))) {
|
|
12680
12893
|
looksLikeProject = true;
|
|
12681
12894
|
break;
|
|
12682
12895
|
}
|
|
@@ -12695,7 +12908,7 @@ var requiredFiles = {
|
|
|
12695
12908
|
for (const projectDir of projects) {
|
|
12696
12909
|
const missing = [];
|
|
12697
12910
|
for (const rel of REQUIRED_PROJECT_FILES) {
|
|
12698
|
-
const p =
|
|
12911
|
+
const p = resolve38(projectDir, rel);
|
|
12699
12912
|
if (!await fileExists(p)) missing.push(rel);
|
|
12700
12913
|
}
|
|
12701
12914
|
if (missing.length === 0) continue;
|
|
@@ -12705,7 +12918,7 @@ var requiredFiles = {
|
|
|
12705
12918
|
title: this.title,
|
|
12706
12919
|
status: "error",
|
|
12707
12920
|
detail: `project at ${projectDir} is missing: ${missing.join(", ")}`,
|
|
12708
|
-
affected: missing.map((m) =>
|
|
12921
|
+
affected: missing.map((m) => resolve38(projectDir, m)),
|
|
12709
12922
|
remediation: {
|
|
12710
12923
|
kind: "manual",
|
|
12711
12924
|
suggestion: "Recreate the missing scaffold files from templates",
|
|
@@ -12728,7 +12941,7 @@ var manifestStale = {
|
|
|
12728
12941
|
const projects = await listProjects2(ctx);
|
|
12729
12942
|
const results = [];
|
|
12730
12943
|
for (const projectDir of projects) {
|
|
12731
|
-
const manifestPath =
|
|
12944
|
+
const manifestPath = resolve38(projectDir, "manifest.md");
|
|
12732
12945
|
if (!await fileExists(manifestPath)) continue;
|
|
12733
12946
|
const manifestMtime = (await stat6(manifestPath)).mtimeMs;
|
|
12734
12947
|
const newestAssignment = await newestAssignmentMtime(projectDir);
|
|
@@ -12777,7 +12990,7 @@ var orphanFiles = {
|
|
|
12777
12990
|
title: this.title,
|
|
12778
12991
|
status: "warn",
|
|
12779
12992
|
detail: `project at ${projectDir} has unexpected entries: ${orphans.join(", ")}`,
|
|
12780
|
-
affected: orphans.map((o) =>
|
|
12993
|
+
affected: orphans.map((o) => resolve38(projectDir, o)),
|
|
12781
12994
|
autoFixable: false
|
|
12782
12995
|
});
|
|
12783
12996
|
}
|
|
@@ -12787,7 +13000,7 @@ var orphanFiles = {
|
|
|
12787
13000
|
};
|
|
12788
13001
|
var projectChecks = [requiredFiles, manifestStale, orphanFiles];
|
|
12789
13002
|
async function newestAssignmentMtime(projectDir) {
|
|
12790
|
-
const assignmentsRoot =
|
|
13003
|
+
const assignmentsRoot = resolve38(projectDir, "assignments");
|
|
12791
13004
|
if (!await fileExists(assignmentsRoot)) return 0;
|
|
12792
13005
|
let newest = 0;
|
|
12793
13006
|
let entries;
|
|
@@ -12798,7 +13011,7 @@ async function newestAssignmentMtime(projectDir) {
|
|
|
12798
13011
|
}
|
|
12799
13012
|
for (const e of entries) {
|
|
12800
13013
|
if (!e.isDirectory()) continue;
|
|
12801
|
-
const assignmentMd =
|
|
13014
|
+
const assignmentMd = resolve38(assignmentsRoot, e.name, "assignment.md");
|
|
12802
13015
|
try {
|
|
12803
13016
|
const s = await stat6(assignmentMd);
|
|
12804
13017
|
if (s.mtimeMs > newest) newest = s.mtimeMs;
|
|
@@ -12822,8 +13035,8 @@ init_fs();
|
|
|
12822
13035
|
init_parser();
|
|
12823
13036
|
init_types();
|
|
12824
13037
|
init_paths();
|
|
12825
|
-
import { resolve as
|
|
12826
|
-
import { readdir as readdir15, readFile as
|
|
13038
|
+
import { resolve as resolve39 } from "path";
|
|
13039
|
+
import { readdir as readdir15, readFile as readFile22 } from "fs/promises";
|
|
12827
13040
|
var CATEGORY4 = "assignment";
|
|
12828
13041
|
var STATUSES_REQUIRING_HANDOFF = /* @__PURE__ */ new Set(["review", "completed"]);
|
|
12829
13042
|
async function listAssignments(ctx) {
|
|
@@ -12834,16 +13047,16 @@ async function listAssignments(ctx) {
|
|
|
12834
13047
|
for (const m of projects) {
|
|
12835
13048
|
if (!m.isDirectory()) continue;
|
|
12836
13049
|
if (m.name.startsWith(".") || m.name.startsWith("_")) continue;
|
|
12837
|
-
const assignmentsDir2 =
|
|
13050
|
+
const assignmentsDir2 = resolve39(projectsDir2, m.name, "assignments");
|
|
12838
13051
|
if (!await fileExists(assignmentsDir2)) continue;
|
|
12839
13052
|
const entries = await readdir15(assignmentsDir2, { withFileTypes: true });
|
|
12840
13053
|
for (const a of entries) {
|
|
12841
13054
|
if (!a.isDirectory()) continue;
|
|
12842
13055
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
12843
|
-
const assignmentDir =
|
|
12844
|
-
const assignmentMd =
|
|
13056
|
+
const assignmentDir = resolve39(assignmentsDir2, a.name);
|
|
13057
|
+
const assignmentMd = resolve39(assignmentDir, "assignment.md");
|
|
12845
13058
|
const entry = {
|
|
12846
|
-
projectDir:
|
|
13059
|
+
projectDir: resolve39(projectsDir2, m.name),
|
|
12847
13060
|
projectSlug: m.name,
|
|
12848
13061
|
assignmentDir,
|
|
12849
13062
|
assignmentSlug: a.name,
|
|
@@ -12863,8 +13076,8 @@ async function listAssignments(ctx) {
|
|
|
12863
13076
|
for (const a of entries) {
|
|
12864
13077
|
if (!a.isDirectory()) continue;
|
|
12865
13078
|
if (a.name.startsWith(".") || a.name.startsWith("_")) continue;
|
|
12866
|
-
const assignmentDir =
|
|
12867
|
-
const assignmentMd =
|
|
13079
|
+
const assignmentDir = resolve39(standaloneRoot, a.name);
|
|
13080
|
+
const assignmentMd = resolve39(assignmentDir, "assignment.md");
|
|
12868
13081
|
const entry = {
|
|
12869
13082
|
projectDir: standaloneRoot,
|
|
12870
13083
|
projectSlug: null,
|
|
@@ -12942,7 +13155,7 @@ var invalidStatus = {
|
|
|
12942
13155
|
const allowed = configuredStatuses(ctx);
|
|
12943
13156
|
const results = [];
|
|
12944
13157
|
for (const a of withAssignmentMd) {
|
|
12945
|
-
const path =
|
|
13158
|
+
const path = resolve39(a.assignmentDir, "assignment.md");
|
|
12946
13159
|
const parsed = await parseSafe(path);
|
|
12947
13160
|
if (!parsed) continue;
|
|
12948
13161
|
if (!allowed.has(parsed.status)) {
|
|
@@ -12975,7 +13188,7 @@ var workspaceMissing = {
|
|
|
12975
13188
|
const terminal = terminalStatuses(ctx);
|
|
12976
13189
|
const results = [];
|
|
12977
13190
|
for (const a of withAssignmentMd) {
|
|
12978
|
-
const path =
|
|
13191
|
+
const path = resolve39(a.assignmentDir, "assignment.md");
|
|
12979
13192
|
const parsed = await parseSafe(path);
|
|
12980
13193
|
if (!parsed) continue;
|
|
12981
13194
|
if (terminal.has(parsed.status)) continue;
|
|
@@ -13022,12 +13235,12 @@ var requiredFilesByStatus = {
|
|
|
13022
13235
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
13023
13236
|
const results = [];
|
|
13024
13237
|
for (const a of withAssignmentMd) {
|
|
13025
|
-
const assignmentPath =
|
|
13238
|
+
const assignmentPath = resolve39(a.assignmentDir, "assignment.md");
|
|
13026
13239
|
const parsed = await parseSafe(assignmentPath);
|
|
13027
13240
|
if (!parsed) continue;
|
|
13028
13241
|
const missing = [];
|
|
13029
13242
|
if (STATUSES_REQUIRING_HANDOFF.has(parsed.status)) {
|
|
13030
|
-
const handoffPath =
|
|
13243
|
+
const handoffPath = resolve39(a.assignmentDir, "handoff.md");
|
|
13031
13244
|
if (!await fileExists(handoffPath)) missing.push("handoff.md");
|
|
13032
13245
|
}
|
|
13033
13246
|
if (missing.length === 0) continue;
|
|
@@ -13037,7 +13250,7 @@ var requiredFilesByStatus = {
|
|
|
13037
13250
|
title: this.title,
|
|
13038
13251
|
status: "warn",
|
|
13039
13252
|
detail: `${a.projectSlug}/${a.assignmentSlug} (status: ${parsed.status}) is missing ${missing.join(", ")}`,
|
|
13040
|
-
affected: missing.map((m) =>
|
|
13253
|
+
affected: missing.map((m) => resolve39(a.assignmentDir, m)),
|
|
13041
13254
|
remediation: {
|
|
13042
13255
|
kind: "manual",
|
|
13043
13256
|
suggestion: `Create the missing ${missing.join(" and ")} files for this assignment`,
|
|
@@ -13060,7 +13273,7 @@ var companionFilesScaffolded = {
|
|
|
13060
13273
|
for (const a of withAssignmentMd) {
|
|
13061
13274
|
const missing = [];
|
|
13062
13275
|
for (const filename of ["progress.md", "comments.md"]) {
|
|
13063
|
-
if (!await fileExists(
|
|
13276
|
+
if (!await fileExists(resolve39(a.assignmentDir, filename))) {
|
|
13064
13277
|
missing.push(filename);
|
|
13065
13278
|
}
|
|
13066
13279
|
}
|
|
@@ -13072,7 +13285,7 @@ var companionFilesScaffolded = {
|
|
|
13072
13285
|
title: this.title,
|
|
13073
13286
|
status: "warn",
|
|
13074
13287
|
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) =>
|
|
13288
|
+
affected: missing.map((m) => resolve39(a.assignmentDir, m)),
|
|
13076
13289
|
remediation: {
|
|
13077
13290
|
kind: "manual",
|
|
13078
13291
|
suggestion: `Create ${missing.join(" and ")} with the renderProgress/renderComments templates, or re-scaffold via the CLI`,
|
|
@@ -13105,7 +13318,7 @@ var typeDefinition = {
|
|
|
13105
13318
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
13106
13319
|
const results = [];
|
|
13107
13320
|
for (const a of withAssignmentMd) {
|
|
13108
|
-
const path =
|
|
13321
|
+
const path = resolve39(a.assignmentDir, "assignment.md");
|
|
13109
13322
|
const parsed = await parseSafe(path);
|
|
13110
13323
|
if (!parsed) continue;
|
|
13111
13324
|
if (!parsed.type) continue;
|
|
@@ -13139,7 +13352,7 @@ var projectFrontmatterMatchesContainer = {
|
|
|
13139
13352
|
const { withAssignmentMd } = await listAssignments(ctx);
|
|
13140
13353
|
const results = [];
|
|
13141
13354
|
for (const a of withAssignmentMd) {
|
|
13142
|
-
const path =
|
|
13355
|
+
const path = resolve39(a.assignmentDir, "assignment.md");
|
|
13143
13356
|
const parsed = await parseSafe(path);
|
|
13144
13357
|
if (!parsed) continue;
|
|
13145
13358
|
if (a.standalone) {
|
|
@@ -13194,7 +13407,7 @@ var assignmentChecks = [
|
|
|
13194
13407
|
];
|
|
13195
13408
|
async function parseSafe(path) {
|
|
13196
13409
|
try {
|
|
13197
|
-
const content = await
|
|
13410
|
+
const content = await readFile22(path, "utf-8");
|
|
13198
13411
|
return parseAssignmentFull(content);
|
|
13199
13412
|
} catch {
|
|
13200
13413
|
return null;
|
|
@@ -13213,7 +13426,7 @@ function pass4(check, detail) {
|
|
|
13213
13426
|
|
|
13214
13427
|
// src/utils/doctor/checks/dashboard.ts
|
|
13215
13428
|
init_fs();
|
|
13216
|
-
import { resolve as
|
|
13429
|
+
import { resolve as resolve40 } from "path";
|
|
13217
13430
|
var CATEGORY5 = "dashboard";
|
|
13218
13431
|
var dbReachable = {
|
|
13219
13432
|
id: "dashboard.db-reachable",
|
|
@@ -13227,7 +13440,7 @@ var dbReachable = {
|
|
|
13227
13440
|
title: this.title,
|
|
13228
13441
|
status: "error",
|
|
13229
13442
|
detail: `could not open syntaur.db: ${ctx.dbError ?? "unknown error"}`,
|
|
13230
|
-
affected: [
|
|
13443
|
+
affected: [resolve40(ctx.syntaurRoot, "syntaur.db")],
|
|
13231
13444
|
remediation: {
|
|
13232
13445
|
kind: "manual",
|
|
13233
13446
|
suggestion: "Start the dashboard once (`syntaur dashboard`) to initialize the DB, or restore it from backup",
|
|
@@ -13245,7 +13458,7 @@ var dbReachable = {
|
|
|
13245
13458
|
title: this.title,
|
|
13246
13459
|
status: "error",
|
|
13247
13460
|
detail: 'syntaur.db is missing the expected "sessions" table',
|
|
13248
|
-
affected: [
|
|
13461
|
+
affected: [resolve40(ctx.syntaurRoot, "syntaur.db")],
|
|
13249
13462
|
autoFixable: false
|
|
13250
13463
|
};
|
|
13251
13464
|
}
|
|
@@ -13257,7 +13470,7 @@ var dbReachable = {
|
|
|
13257
13470
|
title: this.title,
|
|
13258
13471
|
status: "error",
|
|
13259
13472
|
detail: `syntaur.db query failed: ${err2 instanceof Error ? err2.message : String(err2)}`,
|
|
13260
|
-
affected: [
|
|
13473
|
+
affected: [resolve40(ctx.syntaurRoot, "syntaur.db")],
|
|
13261
13474
|
autoFixable: false
|
|
13262
13475
|
};
|
|
13263
13476
|
}
|
|
@@ -13283,7 +13496,7 @@ var ghostSessions = {
|
|
|
13283
13496
|
const results = [];
|
|
13284
13497
|
for (const row of rows) {
|
|
13285
13498
|
if (!row.project_slug) continue;
|
|
13286
|
-
const projectPath =
|
|
13499
|
+
const projectPath = resolve40(projectsDir2, row.project_slug, "project.md");
|
|
13287
13500
|
if (!await fileExists(projectPath)) {
|
|
13288
13501
|
results.push({
|
|
13289
13502
|
id: this.id,
|
|
@@ -13302,7 +13515,7 @@ var ghostSessions = {
|
|
|
13302
13515
|
continue;
|
|
13303
13516
|
}
|
|
13304
13517
|
if (row.assignment_slug) {
|
|
13305
|
-
const assignmentPath =
|
|
13518
|
+
const assignmentPath = resolve40(
|
|
13306
13519
|
projectsDir2,
|
|
13307
13520
|
row.project_slug,
|
|
13308
13521
|
"assignments",
|
|
@@ -13459,8 +13672,8 @@ function skipped2(check, reason) {
|
|
|
13459
13672
|
init_fs();
|
|
13460
13673
|
init_parser();
|
|
13461
13674
|
init_types();
|
|
13462
|
-
import { resolve as
|
|
13463
|
-
import { readFile as
|
|
13675
|
+
import { resolve as resolve41 } from "path";
|
|
13676
|
+
import { readFile as readFile23 } from "fs/promises";
|
|
13464
13677
|
var CATEGORY7 = "workspace";
|
|
13465
13678
|
var ASSIGNMENT_FIELDS = ["projectSlug", "assignmentSlug", "projectDir", "assignmentDir"];
|
|
13466
13679
|
function hasAnyAssignmentField(ctx) {
|
|
@@ -13472,12 +13685,12 @@ function isStandaloneSession(ctx) {
|
|
|
13472
13685
|
return !hasAnyAssignmentField(ctx) && typeof ctx.sessionId === "string" && ctx.sessionId.length > 0;
|
|
13473
13686
|
}
|
|
13474
13687
|
async function loadContext(ctx) {
|
|
13475
|
-
const path =
|
|
13688
|
+
const path = resolve41(ctx.cwd, ".syntaur", "context.json");
|
|
13476
13689
|
if (!await fileExists(path)) {
|
|
13477
13690
|
return { data: null, path, exists: false, parseError: null };
|
|
13478
13691
|
}
|
|
13479
13692
|
try {
|
|
13480
|
-
const raw = await
|
|
13693
|
+
const raw = await readFile23(path, "utf-8");
|
|
13481
13694
|
return { data: JSON.parse(raw), path, exists: true, parseError: null };
|
|
13482
13695
|
} catch (err2) {
|
|
13483
13696
|
return {
|
|
@@ -13552,7 +13765,7 @@ var contextAssignmentResolves = {
|
|
|
13552
13765
|
if (!exists) return skipped3(this, "no context to resolve");
|
|
13553
13766
|
if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to resolve");
|
|
13554
13767
|
if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
|
|
13555
|
-
const assignmentMd =
|
|
13768
|
+
const assignmentMd = resolve41(data.assignmentDir, "assignment.md");
|
|
13556
13769
|
if (!await fileExists(assignmentMd)) {
|
|
13557
13770
|
return {
|
|
13558
13771
|
id: this.id,
|
|
@@ -13581,10 +13794,10 @@ var contextTerminal = {
|
|
|
13581
13794
|
if (!exists) return skipped3(this, "no context to check");
|
|
13582
13795
|
if (isStandaloneSession(data)) return skipped3(this, "standalone session context \u2014 no assignment to check");
|
|
13583
13796
|
if (!data?.assignmentDir) return skipped3(this, "context has no assignmentDir");
|
|
13584
|
-
const assignmentMd =
|
|
13797
|
+
const assignmentMd = resolve41(data.assignmentDir, "assignment.md");
|
|
13585
13798
|
if (!await fileExists(assignmentMd)) return skipped3(this, "assignment file missing");
|
|
13586
13799
|
try {
|
|
13587
|
-
const content = await
|
|
13800
|
+
const content = await readFile23(assignmentMd, "utf-8");
|
|
13588
13801
|
const parsed = parseAssignmentFull(content);
|
|
13589
13802
|
const terminal = terminalStatuses2(ctx);
|
|
13590
13803
|
if (terminal.has(parsed.status)) {
|
|
@@ -13728,14 +13941,14 @@ async function finalize(checks) {
|
|
|
13728
13941
|
async function readVersion() {
|
|
13729
13942
|
try {
|
|
13730
13943
|
const here = fileURLToPath7(import.meta.url);
|
|
13731
|
-
let dir =
|
|
13944
|
+
let dir = dirname11(here);
|
|
13732
13945
|
for (let i = 0; i < 6; i++) {
|
|
13733
13946
|
try {
|
|
13734
|
-
const raw = await
|
|
13947
|
+
const raw = await readFile24(join5(dir, "package.json"), "utf-8");
|
|
13735
13948
|
const parsed = JSON.parse(raw);
|
|
13736
13949
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
13737
13950
|
} catch {
|
|
13738
|
-
dir =
|
|
13951
|
+
dir = dirname11(dir);
|
|
13739
13952
|
}
|
|
13740
13953
|
}
|
|
13741
13954
|
return null;
|
|
@@ -13868,8 +14081,8 @@ var doctorCommand = new Command3("doctor").description("Diagnose Syntaur state a
|
|
|
13868
14081
|
init_paths();
|
|
13869
14082
|
init_fs();
|
|
13870
14083
|
init_config2();
|
|
13871
|
-
import { resolve as
|
|
13872
|
-
import { readFile as
|
|
14084
|
+
import { resolve as resolve42 } from "path";
|
|
14085
|
+
import { readFile as readFile25 } from "fs/promises";
|
|
13873
14086
|
init_timestamp();
|
|
13874
14087
|
init_assignment_resolver();
|
|
13875
14088
|
function shortId() {
|
|
@@ -13901,7 +14114,7 @@ async function commentCommand(target, text, options = {}) {
|
|
|
13901
14114
|
if (!isValidSlug(target)) {
|
|
13902
14115
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
13903
14116
|
}
|
|
13904
|
-
assignmentDir =
|
|
14117
|
+
assignmentDir = resolve42(baseDir, options.project, "assignments", target);
|
|
13905
14118
|
assignmentRef = target;
|
|
13906
14119
|
} else {
|
|
13907
14120
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
|
|
@@ -13911,13 +14124,13 @@ async function commentCommand(target, text, options = {}) {
|
|
|
13911
14124
|
assignmentDir = resolved.assignmentDir;
|
|
13912
14125
|
assignmentRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
|
|
13913
14126
|
}
|
|
13914
|
-
const commentsPath =
|
|
14127
|
+
const commentsPath = resolve42(assignmentDir, "comments.md");
|
|
13915
14128
|
const timestamp = nowTimestamp();
|
|
13916
14129
|
const author = options.author ?? process.env.USER ?? "unknown";
|
|
13917
14130
|
let currentContent;
|
|
13918
14131
|
let currentCount = 0;
|
|
13919
14132
|
if (await fileExists(commentsPath)) {
|
|
13920
|
-
currentContent = await
|
|
14133
|
+
currentContent = await readFile25(commentsPath, "utf-8");
|
|
13921
14134
|
const countMatch = currentContent.match(/^entryCount:\s*(\d+)/m);
|
|
13922
14135
|
if (countMatch) currentCount = parseInt(countMatch[1], 10);
|
|
13923
14136
|
} else {
|
|
@@ -13954,8 +14167,8 @@ ${entry}`;
|
|
|
13954
14167
|
init_paths();
|
|
13955
14168
|
init_fs();
|
|
13956
14169
|
init_config2();
|
|
13957
|
-
import { resolve as
|
|
13958
|
-
import { readFile as
|
|
14170
|
+
import { resolve as resolve43 } from "path";
|
|
14171
|
+
import { readFile as readFile26 } from "fs/promises";
|
|
13959
14172
|
init_timestamp();
|
|
13960
14173
|
init_assignment_resolver();
|
|
13961
14174
|
function setTopLevelField3(content, key, value) {
|
|
@@ -13980,7 +14193,7 @@ async function requestCommand(target, text, options = {}) {
|
|
|
13980
14193
|
if (!isValidSlug(target)) {
|
|
13981
14194
|
throw new Error(`Invalid assignment slug "${target}".`);
|
|
13982
14195
|
}
|
|
13983
|
-
assignmentDir =
|
|
14196
|
+
assignmentDir = resolve43(baseDir, options.project, "assignments", target);
|
|
13984
14197
|
targetRef = target;
|
|
13985
14198
|
} else {
|
|
13986
14199
|
const resolved = await resolveAssignmentById(baseDir, assignmentsDir(), target);
|
|
@@ -13990,12 +14203,12 @@ async function requestCommand(target, text, options = {}) {
|
|
|
13990
14203
|
assignmentDir = resolved.assignmentDir;
|
|
13991
14204
|
targetRef = resolved.standalone ? resolved.id : resolved.assignmentSlug;
|
|
13992
14205
|
}
|
|
13993
|
-
const assignmentMdPath =
|
|
14206
|
+
const assignmentMdPath = resolve43(assignmentDir, "assignment.md");
|
|
13994
14207
|
if (!await fileExists(assignmentMdPath)) {
|
|
13995
14208
|
throw new Error(`assignment.md not found at ${assignmentMdPath}`);
|
|
13996
14209
|
}
|
|
13997
14210
|
const source = options.from ?? process.env.SYNTAUR_ASSIGNMENT ?? "unknown";
|
|
13998
|
-
let content = await
|
|
14211
|
+
let content = await readFile26(assignmentMdPath, "utf-8");
|
|
13999
14212
|
const todoLine = `- [ ] ${text.trim()} (from: ${source})`;
|
|
14000
14213
|
const todosHeading = /^## Todos\s*$/m;
|
|
14001
14214
|
if (todosHeading.test(content)) {
|
|
@@ -14063,20 +14276,20 @@ async function getDefaultCommandName() {
|
|
|
14063
14276
|
init_paths();
|
|
14064
14277
|
init_fs();
|
|
14065
14278
|
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
14066
|
-
import { readFile as
|
|
14067
|
-
import { dirname as
|
|
14279
|
+
import { readFile as readFile28 } from "fs/promises";
|
|
14280
|
+
import { dirname as dirname13, join as join7, resolve as resolve44 } from "path";
|
|
14068
14281
|
import { spawn as spawn3 } from "child_process";
|
|
14069
14282
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
14070
14283
|
|
|
14071
14284
|
// src/utils/version.ts
|
|
14072
14285
|
import { fileURLToPath as fileURLToPath8 } from "url";
|
|
14073
|
-
import { readFile as
|
|
14074
|
-
import { dirname as
|
|
14286
|
+
import { readFile as readFile27 } from "fs/promises";
|
|
14287
|
+
import { dirname as dirname12, join as join6 } from "path";
|
|
14075
14288
|
async function readPackageVersion(scriptUrl) {
|
|
14076
14289
|
try {
|
|
14077
14290
|
const scriptPath = fileURLToPath8(scriptUrl);
|
|
14078
|
-
const pkgRoot =
|
|
14079
|
-
const raw = await
|
|
14291
|
+
const pkgRoot = dirname12(dirname12(scriptPath));
|
|
14292
|
+
const raw = await readFile27(join6(pkgRoot, "package.json"), "utf-8");
|
|
14080
14293
|
const parsed = JSON.parse(raw);
|
|
14081
14294
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
14082
14295
|
} catch {
|
|
@@ -14085,7 +14298,7 @@ async function readPackageVersion(scriptUrl) {
|
|
|
14085
14298
|
}
|
|
14086
14299
|
|
|
14087
14300
|
// src/utils/npx-prompt.ts
|
|
14088
|
-
var STATE_FILE =
|
|
14301
|
+
var STATE_FILE = resolve44(syntaurRoot(), "npx-install.json");
|
|
14089
14302
|
var META_ARGS = /* @__PURE__ */ new Set(["-h", "--help", "-V", "--version", "help"]);
|
|
14090
14303
|
var GLOBAL_VERSION_TIMEOUT_MS = 2e3;
|
|
14091
14304
|
function isRunningViaNpx(scriptUrl) {
|
|
@@ -14106,7 +14319,7 @@ function isRunningViaNpx(scriptUrl) {
|
|
|
14106
14319
|
async function readState() {
|
|
14107
14320
|
if (!await fileExists(STATE_FILE)) return null;
|
|
14108
14321
|
try {
|
|
14109
|
-
const raw = await
|
|
14322
|
+
const raw = await readFile28(STATE_FILE, "utf-8");
|
|
14110
14323
|
return JSON.parse(raw);
|
|
14111
14324
|
} catch {
|
|
14112
14325
|
return null;
|
|
@@ -14117,7 +14330,7 @@ async function writeState(state) {
|
|
|
14117
14330
|
`);
|
|
14118
14331
|
}
|
|
14119
14332
|
async function resolveNpmBin() {
|
|
14120
|
-
const nodeDir =
|
|
14333
|
+
const nodeDir = dirname13(process.execPath);
|
|
14121
14334
|
const isWin = process.platform === "win32";
|
|
14122
14335
|
const npmName = isWin ? "npm.cmd" : "npm";
|
|
14123
14336
|
const nearNode = join7(nodeDir, npmName);
|
|
@@ -14165,7 +14378,7 @@ async function readGlobalVersion() {
|
|
|
14165
14378
|
try {
|
|
14166
14379
|
const manifestPath = join7(rootPath, "syntaur", "package.json");
|
|
14167
14380
|
if (!await fileExists(manifestPath)) return null;
|
|
14168
|
-
const raw = await
|
|
14381
|
+
const raw = await readFile28(manifestPath, "utf-8");
|
|
14169
14382
|
const parsed = JSON.parse(raw);
|
|
14170
14383
|
return typeof parsed.version === "string" ? parsed.version : null;
|
|
14171
14384
|
} catch {
|
|
@@ -14501,6 +14714,22 @@ program.command("uninstall-statusline").description(
|
|
|
14501
14714
|
process.exit(1);
|
|
14502
14715
|
}
|
|
14503
14716
|
});
|
|
14717
|
+
program.command("configure-statusline").description(
|
|
14718
|
+
"Configure which segments (git, assignment, session, model, ctx, cwd, wrap) appear in the syntaur statusLine and in what order."
|
|
14719
|
+
).option(
|
|
14720
|
+
"--preset <name>",
|
|
14721
|
+
`Preset shortcut. Choices: ${Object.keys(PRESETS).join(", ")}.`
|
|
14722
|
+
).option(
|
|
14723
|
+
"--segments <list>",
|
|
14724
|
+
'Comma-separated segment list, e.g. "git,assignment,session,model,ctx".'
|
|
14725
|
+
).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) => {
|
|
14726
|
+
try {
|
|
14727
|
+
await configureStatuslineCommand(options);
|
|
14728
|
+
} catch (error) {
|
|
14729
|
+
console.error("Error:", error instanceof Error ? error.message : String(error));
|
|
14730
|
+
process.exit(1);
|
|
14731
|
+
}
|
|
14732
|
+
});
|
|
14504
14733
|
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
14734
|
try {
|
|
14506
14735
|
await uninstallSkillsCommand(options);
|