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/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((input2, key) => {
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 (input2 === "q" || key.escape) {
4445
+ if (input3 === "q" || key.escape) {
4446
4446
  exit();
4447
4447
  return;
4448
4448
  }
4449
- if (input2 === "/") {
4449
+ if (input3 === "/") {
4450
4450
  setSearchActive(true);
4451
4451
  return;
4452
4452
  }
4453
- if (key.upArrow || input2 === "k") {
4453
+ if (key.upArrow || input3 === "k") {
4454
4454
  moveUp();
4455
4455
  return;
4456
4456
  }
4457
- if (key.downArrow || input2 === "j") {
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 writeFile8 } from "fs/promises";
4550
- import { resolve as resolve30 } from "path";
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 = resolve30(projectsDir2, projectSlug);
4561
- const assignmentDir = resolve30(projectDir, "assignments", assignmentSlug);
4562
- const contextDir = resolve30(workspaceDir, ".syntaur");
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 writeFile8(
4575
- resolve30(contextDir, "context.json"),
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: readFile28 } = await import("fs/promises");
5913
- const raw = await readFile28(filePath, "utf-8");
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(input2) {
7641
- return input2.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "untitled";
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: resolve44 } = await import("path");
8222
- const { readFile: readFile28 } = await import("fs/promises");
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(resolve44(todosDir2, "archive"));
8238
+ await ensureDir(resolve45(todosDir2, "archive"));
8239
8239
  let archContent = "";
8240
8240
  if (await fileExists(archFile)) {
8241
- archContent = await readFile28(archFile, "utf-8");
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 readFile17, writeFile as writeFile7, 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 resolve26, dirname as dirname8 } from "path";
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 = dirname8(fileURLToPath5(import.meta.url));
10762
- return resolve26(here, "..", "statusline", "statusline.sh");
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 readFile17(settingsPath, "utf-8");
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(dirname8(settingsPath));
10779
- await writeFile7(settingsPath, JSON.stringify(data, null, 2) + "\n", "utf-8");
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(dirname8(backupPath));
10814
- await writeFile7(
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(dirname8(destScript));
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 ?? resolve26(homedir4(), ".claude", "settings.json");
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 = resolve26(installRoot, "statusline.sh");
10850
- const confPath = resolve26(installRoot, "statusline.conf");
10851
- const backupPath = resolve26(installRoot, "statusline.backup.json");
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 = resolve26(installRoot, "statusline-wrapped.sh");
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 writeFile7(wrapperPath, wrapperBody, "utf-8");
11104
+ await writeFile8(wrapperPath, wrapperBody, "utf-8");
10893
11105
  await chmodExec(wrapperPath);
10894
11106
  wrapTarget = wrapperPath;
10895
11107
  }
10896
11108
  }
10897
- await ensureDir(dirname8(confPath));
10898
- await writeFile7(
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 ?? resolve26(homedir4(), ".claude", "settings.json");
11153
+ const settingsPath = options.settingsPath ?? resolve27(homedir4(), ".claude", "settings.json");
10942
11154
  const installRoot = options.installRoot ?? syntaurRoot();
10943
- const destScript = resolve26(installRoot, "statusline.sh");
10944
- const confPath = resolve26(installRoot, "statusline.conf");
10945
- const backupPath = resolve26(installRoot, "statusline.backup.json");
10946
- const wrapperPath = resolve26(installRoot, "statusline-wrapped.sh");
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 readFile17(backupPath, "utf-8");
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
- for (const path of [destScript, confPath, backupPath, wrapperPath]) {
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 resolve27 } from "path";
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 && resolve27(configuredProjectDir) !== resolve27(syntaurRoot(), "projects")) {
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 resolve28 } from "path";
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 = resolve28(baseDir, options.project);
11350
- const assignmentDir = resolve28(
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 = resolve28(projectDir, "project.md");
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 = resolve28(assignmentDir, "assignment.md");
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 = resolve28(cwd, ".cursor", "rules", "syntaur-protocol.mdc");
11390
- const assignmentPath = resolve28(cwd, ".cursor", "rules", "syntaur-assignment.mdc");
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 = resolve28(cwd, "AGENTS.md");
11607
+ const agentsPath = resolve29(cwd, "AGENTS.md");
11395
11608
  await writeAdapterFile(agentsPath, renderCodexAgents(rendererParams));
11396
11609
  if (framework === "opencode") {
11397
- const configPath = resolve28(cwd, "opencode.json");
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 resolve29 } from "path";
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 = resolve29(baseDir, options.project);
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 resolve31 } from "path";
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 = resolve31(dir, `${slug}.md`);
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 readFile18 } from "fs/promises";
11528
- import { resolve as resolve32 } from "path";
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 = resolve32(dir, entry.name);
11547
- const raw = await readFile18(filePath, "utf-8");
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 readFile19 } from "fs/promises";
11562
- import { resolve as resolve33 } from "path";
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(resolve33(todosPath, "archive"));
12056
+ await ensureDir(resolve34(todosPath, "archive"));
11844
12057
  let archContent = "";
11845
12058
  if (await fileExists(archFile)) {
11846
- archContent = await readFile19(archFile, "utf-8");
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 readFile23 } from "fs/promises";
12036
- import { dirname as dirname10, join as join5 } from "path";
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 resolve34 } from "path";
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 = resolve34(root, "syntaur.db");
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 resolve35, isAbsolute as isAbsolute3 } from "path";
12082
- import { readFile as readFile20, stat as stat4 } from "fs/promises";
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 dirname9, join as join4 } from "path";
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 = resolve35(ctx.syntaurRoot, "config.md");
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 readFile20(configPath, "utf-8");
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 = dirname9(here);
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 readFile20(candidate, "utf-8");
12632
+ const text = await readFile21(candidate, "utf-8");
12420
12633
  return JSON.parse(text);
12421
12634
  } catch {
12422
- dir = dirname9(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 resolve36 } from "path";
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 = resolve36(ctx.syntaurRoot, "projects");
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 = resolve36(ctx.syntaurRoot, "playbooks");
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 = resolve36(ctx.syntaurRoot, "todos");
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 = resolve36(ctx.syntaurRoot, "servers");
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) => resolve36(ctx.syntaurRoot, 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 resolve37 } from "path";
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 = resolve37(dir, e.name);
12889
+ const projectDir = resolve38(dir, e.name);
12677
12890
  let looksLikeProject = false;
12678
12891
  for (const marker of PROJECT_MARKERS) {
12679
- if (await fileExists(resolve37(projectDir, marker))) {
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 = resolve37(projectDir, rel);
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) => resolve37(projectDir, 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 = resolve37(projectDir, "manifest.md");
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) => resolve37(projectDir, 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 = resolve37(projectDir, "assignments");
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 = resolve37(assignmentsRoot, e.name, "assignment.md");
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 resolve38 } from "path";
12826
- import { readdir as readdir15, readFile as readFile21 } from "fs/promises";
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 = resolve38(projectsDir2, m.name, "assignments");
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 = resolve38(assignmentsDir2, a.name);
12844
- const assignmentMd = resolve38(assignmentDir, "assignment.md");
13056
+ const assignmentDir = resolve39(assignmentsDir2, a.name);
13057
+ const assignmentMd = resolve39(assignmentDir, "assignment.md");
12845
13058
  const entry = {
12846
- projectDir: resolve38(projectsDir2, m.name),
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 = resolve38(standaloneRoot, a.name);
12867
- const assignmentMd = resolve38(assignmentDir, "assignment.md");
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 = resolve38(a.assignmentDir, "assignment.md");
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 = resolve38(a.assignmentDir, "assignment.md");
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 = resolve38(a.assignmentDir, "assignment.md");
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 = resolve38(a.assignmentDir, "handoff.md");
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) => resolve38(a.assignmentDir, 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(resolve38(a.assignmentDir, filename))) {
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) => resolve38(a.assignmentDir, 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 = resolve38(a.assignmentDir, "assignment.md");
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 = resolve38(a.assignmentDir, "assignment.md");
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 readFile21(path, "utf-8");
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 resolve39 } from "path";
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: [resolve39(ctx.syntaurRoot, "syntaur.db")],
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: [resolve39(ctx.syntaurRoot, "syntaur.db")],
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: [resolve39(ctx.syntaurRoot, "syntaur.db")],
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 = resolve39(projectsDir2, row.project_slug, "project.md");
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 = resolve39(
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 resolve40 } from "path";
13463
- import { readFile as readFile22 } from "fs/promises";
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 = resolve40(ctx.cwd, ".syntaur", "context.json");
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 readFile22(path, "utf-8");
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 = resolve40(data.assignmentDir, "assignment.md");
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 = resolve40(data.assignmentDir, "assignment.md");
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 readFile22(assignmentMd, "utf-8");
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 = dirname10(here);
13944
+ let dir = dirname11(here);
13732
13945
  for (let i = 0; i < 6; i++) {
13733
13946
  try {
13734
- const raw = await readFile23(join5(dir, "package.json"), "utf-8");
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 = dirname10(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 resolve41 } from "path";
13872
- import { readFile as readFile24 } from "fs/promises";
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 = resolve41(baseDir, options.project, "assignments", target);
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 = resolve41(assignmentDir, "comments.md");
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 readFile24(commentsPath, "utf-8");
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 resolve42 } from "path";
13958
- import { readFile as readFile25 } from "fs/promises";
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 = resolve42(baseDir, options.project, "assignments", target);
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 = resolve42(assignmentDir, "assignment.md");
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 readFile25(assignmentMdPath, "utf-8");
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 readFile27 } from "fs/promises";
14067
- import { dirname as dirname12, join as join7, resolve as resolve43 } from "path";
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 readFile26 } from "fs/promises";
14074
- import { dirname as dirname11, join as join6 } from "path";
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 = dirname11(dirname11(scriptPath));
14079
- const raw = await readFile26(join6(pkgRoot, "package.json"), "utf-8");
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 = resolve43(syntaurRoot(), "npx-install.json");
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 readFile27(STATE_FILE, "utf-8");
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 = dirname12(process.execPath);
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 readFile27(manifestPath, "utf-8");
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);