syntaur 0.1.14 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. package/README.md +5 -0
  2. package/dashboard/dist/assets/{_basePickBy-eih-KlEh.js → _basePickBy-CHKX1r7P.js} +1 -1
  3. package/dashboard/dist/assets/{_baseUniq-M21wg9ZQ.js → _baseUniq-CTxTc4MS.js} +1 -1
  4. package/dashboard/dist/assets/{arc-uKZMelpQ.js → arc-BUo5zftd.js} +1 -1
  5. package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-CpMG5exj.js → architectureDiagram-2XIMDMQ5-CrJLm-P0.js} +1 -1
  6. package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-BHnCCKl_.js → blockDiagram-WCTKOSBZ-BK60lBBJ.js} +1 -1
  7. package/dashboard/dist/assets/{c4Diagram-IC4MRINW-B-n3zU9i.js → c4Diagram-IC4MRINW-C7oJEvA0.js} +1 -1
  8. package/dashboard/dist/assets/channel-DdltvFFH.js +1 -0
  9. package/dashboard/dist/assets/{chunk-4BX2VUAB-ChD9Iuih.js → chunk-4BX2VUAB-CjUPlzHz.js} +1 -1
  10. package/dashboard/dist/assets/{chunk-55IACEB6-B3vP9Psg.js → chunk-55IACEB6-6HmWguiO.js} +1 -1
  11. package/dashboard/dist/assets/{chunk-FMBD7UC4-CIhWgxPS.js → chunk-FMBD7UC4-CLuJnd1b.js} +1 -1
  12. package/dashboard/dist/assets/{chunk-JSJVCQXG-DiGIV_cB.js → chunk-JSJVCQXG-B4d62qWV.js} +1 -1
  13. package/dashboard/dist/assets/{chunk-KX2RTZJC-DnGsx5jo.js → chunk-KX2RTZJC-AsEKRPq2.js} +1 -1
  14. package/dashboard/dist/assets/{chunk-NQ4KR5QH-BFBu1fmg.js → chunk-NQ4KR5QH-DQhHHvwY.js} +1 -1
  15. package/dashboard/dist/assets/{chunk-QZHKN3VN-DYtumHth.js → chunk-QZHKN3VN-Ds1TtI3E.js} +1 -1
  16. package/dashboard/dist/assets/{chunk-WL4C6EOR-BzCrQPuw.js → chunk-WL4C6EOR-C7jE3-cR.js} +1 -1
  17. package/dashboard/dist/assets/classDiagram-VBA2DB6C-BHqdFE-8.js +1 -0
  18. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-BHqdFE-8.js +1 -0
  19. package/dashboard/dist/assets/clone-CBJOOeOm.js +1 -0
  20. package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-Bl8mb5eY.js → cose-bilkent-S5V4N54A-C9ka5v1m.js} +1 -1
  21. package/dashboard/dist/assets/{dagre-KLK3FWXG-BHffcOgo.js → dagre-KLK3FWXG-BbgPQBKy.js} +1 -1
  22. package/dashboard/dist/assets/{diagram-E7M64L7V-Ib83qzT_.js → diagram-E7M64L7V-DpdeZFD4.js} +1 -1
  23. package/dashboard/dist/assets/{diagram-IFDJBPK2-hOdh63_T.js → diagram-IFDJBPK2-FlHLQzOV.js} +1 -1
  24. package/dashboard/dist/assets/{diagram-P4PSJMXO-D4ocLmc5.js → diagram-P4PSJMXO-B22NkEF_.js} +1 -1
  25. package/dashboard/dist/assets/{erDiagram-INFDFZHY-CHJ6zqnJ.js → erDiagram-INFDFZHY-zSqmtDid.js} +1 -1
  26. package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-DEz5g2Ye.js → flowDiagram-PKNHOUZH-BP_0XmVV.js} +1 -1
  27. package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-BSftxDHA.js → ganttDiagram-A5KZAMGK-8uRyYgZV.js} +1 -1
  28. package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-Cr3vGf07.js → gitGraphDiagram-K3NZZRJ6-JFqg8sv4.js} +1 -1
  29. package/dashboard/dist/assets/{graph-D4us8trI.js → graph-a-PAH599.js} +1 -1
  30. package/dashboard/dist/assets/index-CoVCLSh2.css +1 -0
  31. package/dashboard/dist/assets/index-yyAIuzrP.js +471 -0
  32. package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-CH_jVfru.js → infoDiagram-LFFYTUFH-C3kq7Nbv.js} +1 -1
  33. package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-BdKLa5GC.js → ishikawaDiagram-PHBUUO56-Kqi4EZ-n.js} +1 -1
  34. package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-C_SMzNGF.js → journeyDiagram-4ABVD52K-CTfv0Wcr.js} +1 -1
  35. package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-BeA-egRW.js → kanban-definition-K7BYSVSG-Dmx0lgvR.js} +1 -1
  36. package/dashboard/dist/assets/{layout-B8tDmL4j.js → layout-KKRbT2Od.js} +1 -1
  37. package/dashboard/dist/assets/{linear-CeGJyrHS.js → linear-5egaBiw7.js} +1 -1
  38. package/dashboard/dist/assets/{mermaid.core-DyEs-LPd.js → mermaid.core-C9pF_oFQ.js} +4 -4
  39. package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-DCxzAr8m.js → mindmap-definition-YRQLILUH-C7HXYEXt.js} +1 -1
  40. package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-CEj5dRDi.js → pieDiagram-SKSYHLDU-DkdZm-YP.js} +1 -1
  41. package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-CKfvAEQg.js → quadrantDiagram-337W2JSQ-DkcRJs5F.js} +1 -1
  42. package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-CTRqKPtJ.js → requirementDiagram-Z7DCOOCP-BaTDVYTl.js} +1 -1
  43. package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-BlYbz8UR.js → sankeyDiagram-WA2Y5GQK-DvPLbGV5.js} +1 -1
  44. package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-PT2t7ryQ.js → sequenceDiagram-2WXFIKYE-DQoZ2xMK.js} +1 -1
  45. package/dashboard/dist/assets/{stateDiagram-RAJIS63D-eDX7IUuV.js → stateDiagram-RAJIS63D-CS4l0OjM.js} +1 -1
  46. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-DkBtE1WJ.js +1 -0
  47. package/dashboard/dist/assets/{timeline-definition-YZTLITO2-By11B1Ow.js → timeline-definition-YZTLITO2-aC0iCFCW.js} +1 -1
  48. package/dashboard/dist/assets/{treemap-KZPCXAKY-rvdLeWWV.js → treemap-KZPCXAKY-Ie-PFjgx.js} +1 -1
  49. package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-Br_oZ1wv.js → vennDiagram-LZ73GAT5-CJN3ExTQ.js} +1 -1
  50. package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-D-MWVqrT.js → xychartDiagram-JWTSCODW-DSiDu1CN.js} +1 -1
  51. package/dashboard/dist/index.html +2 -2
  52. package/dist/dashboard/server.d.ts +1 -1
  53. package/dist/dashboard/server.js +585 -641
  54. package/dist/dashboard/server.js.map +1 -1
  55. package/dist/index.js +3388 -1315
  56. package/dist/index.js.map +1 -1
  57. package/examples/playbooks/keep-records-updated.md +1 -1
  58. package/examples/playbooks/read-before-plan.md +5 -5
  59. package/examples/{sample-mission → sample-project}/_index-assignments.md +1 -1
  60. package/examples/{sample-mission → sample-project}/_index-decisions.md +1 -1
  61. package/examples/{sample-mission → sample-project}/_index-plans.md +1 -1
  62. package/examples/{sample-mission → sample-project}/_status.md +3 -3
  63. package/examples/{sample-mission → sample-project}/assignments/design-auth-schema/assignment.md +4 -1
  64. package/examples/{sample-mission → sample-project}/assignments/implement-jwt-middleware/assignment.md +4 -1
  65. package/examples/{sample-mission → sample-project}/assignments/write-auth-tests/assignment.md +4 -1
  66. package/examples/{sample-mission → sample-project}/manifest.md +3 -3
  67. package/examples/{sample-mission → sample-project}/memories/_index.md +2 -2
  68. package/examples/{sample-mission → sample-project}/memories/postgres-connection-pooling.md +1 -1
  69. package/examples/{sample-mission → sample-project}/resources/_index.md +1 -1
  70. package/package.json +5 -3
  71. package/platforms/README.md +7 -7
  72. package/platforms/claude-code/README.md +1 -1
  73. package/platforms/claude-code/agents/syntaur-expert.md +57 -57
  74. package/platforms/claude-code/commands/doctor-syntaur/doctor-syntaur.md +112 -0
  75. package/platforms/claude-code/commands/track-session/track-session.md +8 -8
  76. package/platforms/claude-code/hooks/enforce-boundaries.sh +4 -4
  77. package/platforms/claude-code/hooks/hooks.json +1 -1
  78. package/platforms/claude-code/hooks/session-cleanup.sh +5 -5
  79. package/platforms/claude-code/references/file-ownership.md +8 -8
  80. package/platforms/claude-code/references/protocol-summary.md +7 -6
  81. package/platforms/claude-code/skills/complete-assignment/SKILL.md +21 -17
  82. package/platforms/claude-code/skills/create-assignment/SKILL.md +15 -14
  83. package/platforms/claude-code/skills/grab-assignment/SKILL.md +56 -49
  84. package/platforms/claude-code/skills/plan-assignment/SKILL.md +57 -10
  85. package/platforms/claude-code/skills/syntaur-protocol/SKILL.md +21 -17
  86. package/platforms/codex/.codex-plugin/plugin.json +3 -3
  87. package/platforms/codex/README.md +1 -1
  88. package/platforms/codex/adapters/AGENTS.md.template +3 -3
  89. package/platforms/codex/agents/openai.yaml +2 -2
  90. package/platforms/codex/agents/syntaur-operator.md +33 -30
  91. package/platforms/codex/references/file-ownership.md +8 -8
  92. package/platforms/codex/references/protocol-summary.md +11 -6
  93. package/platforms/codex/scripts/enforce-boundaries.sh +2 -2
  94. package/platforms/codex/scripts/session-cleanup.sh +2 -2
  95. package/platforms/codex/skills/complete-assignment/SKILL.md +6 -6
  96. package/platforms/codex/skills/create-assignment/SKILL.md +8 -7
  97. package/platforms/codex/skills/grab-assignment/SKILL.md +30 -20
  98. package/platforms/codex/skills/plan-assignment/SKILL.md +19 -11
  99. package/platforms/codex/skills/syntaur-protocol/SKILL.md +26 -21
  100. package/platforms/cursor/README.md +1 -1
  101. package/platforms/cursor/adapters/syntaur-protocol.mdc +1 -1
  102. package/platforms/opencode/README.md +1 -1
  103. package/platforms/opencode/adapters/opencode.json.template +1 -1
  104. package/dashboard/dist/assets/channel-DVBgSlOI.js +0 -1
  105. package/dashboard/dist/assets/classDiagram-VBA2DB6C-B7dxBacd.js +0 -1
  106. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-B7dxBacd.js +0 -1
  107. package/dashboard/dist/assets/clone-DAOrHcCC.js +0 -1
  108. package/dashboard/dist/assets/index-AXntWS_w.css +0 -1
  109. package/dashboard/dist/assets/index-CEMjexkj.js +0 -460
  110. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO--yuSBnLh.js +0 -1
  111. package/examples/sample-mission/agent.md +0 -33
  112. package/examples/sample-mission/claude.md +0 -13
  113. package/platforms/claude-code/skills/create-mission/SKILL.md +0 -51
  114. package/platforms/codex/skills/create-mission/SKILL.md +0 -35
  115. /package/examples/{sample-mission → sample-project}/assignments/design-auth-schema/decision-record.md +0 -0
  116. /package/examples/{sample-mission → sample-project}/assignments/design-auth-schema/handoff.md +0 -0
  117. /package/examples/{sample-mission → sample-project}/assignments/design-auth-schema/plan.md +0 -0
  118. /package/examples/{sample-mission → sample-project}/assignments/design-auth-schema/scratchpad.md +0 -0
  119. /package/examples/{sample-mission → sample-project}/assignments/implement-jwt-middleware/decision-record.md +0 -0
  120. /package/examples/{sample-mission → sample-project}/assignments/implement-jwt-middleware/handoff.md +0 -0
  121. /package/examples/{sample-mission → sample-project}/assignments/implement-jwt-middleware/plan.md +0 -0
  122. /package/examples/{sample-mission → sample-project}/assignments/implement-jwt-middleware/scratchpad.md +0 -0
  123. /package/examples/{sample-mission → sample-project}/assignments/write-auth-tests/decision-record.md +0 -0
  124. /package/examples/{sample-mission → sample-project}/assignments/write-auth-tests/handoff.md +0 -0
  125. /package/examples/{sample-mission → sample-project}/assignments/write-auth-tests/plan.md +0 -0
  126. /package/examples/{sample-mission → sample-project}/assignments/write-auth-tests/scratchpad.md +0 -0
  127. /package/examples/{sample-mission/mission.md → sample-project/project.md} +0 -0
  128. /package/examples/{sample-mission → sample-project}/resources/auth-requirements.md +0 -0
@@ -8,6 +8,40 @@ var __export = (target, all) => {
8
8
  __defProp(target, name, { get: all[name], enumerable: true });
9
9
  };
10
10
 
11
+ // src/utils/paths.ts
12
+ import { homedir } from "os";
13
+ import { resolve } from "path";
14
+ function expandHome(p) {
15
+ if (p.startsWith("~/") || p === "~") {
16
+ return resolve(homedir(), p.slice(2));
17
+ }
18
+ return p;
19
+ }
20
+ function syntaurRoot() {
21
+ const override = process.env.SYNTAUR_HOME;
22
+ if (override && override.length > 0) {
23
+ return resolve(expandHome(override));
24
+ }
25
+ return resolve(homedir(), ".syntaur");
26
+ }
27
+ function defaultProjectDir() {
28
+ return resolve(syntaurRoot(), "projects");
29
+ }
30
+ function serversDir() {
31
+ return resolve(syntaurRoot(), "servers");
32
+ }
33
+ function playbooksDir() {
34
+ return resolve(syntaurRoot(), "playbooks");
35
+ }
36
+ function todosDir() {
37
+ return resolve(syntaurRoot(), "todos");
38
+ }
39
+ var init_paths = __esm({
40
+ "src/utils/paths.ts"() {
41
+ "use strict";
42
+ }
43
+ });
44
+
11
45
  // src/lifecycle/types.ts
12
46
  var DEFAULT_STATUSES;
13
47
  var init_types = __esm({
@@ -184,6 +218,8 @@ function parseAssignmentFrontmatter(fileContent) {
184
218
  id: getField2("id") ?? "",
185
219
  slug: getField2("slug") ?? "",
186
220
  title: getField2("title") ?? "",
221
+ project: getField2("project"),
222
+ type: getField2("type"),
187
223
  status: getField2("status") ?? "pending",
188
224
  priority: getField2("priority") ?? "medium",
189
225
  created: getField2("created") ?? "",
@@ -282,10 +318,10 @@ var init_timestamp = __esm({
282
318
  });
283
319
 
284
320
  // src/lifecycle/transitions.ts
285
- import { resolve } from "path";
321
+ import { resolve as resolve2 } from "path";
286
322
  import { readFile } from "fs/promises";
287
- function resolveAssignmentPath(missionDir, assignmentSlug) {
288
- return resolve(missionDir, "assignments", assignmentSlug, "assignment.md");
323
+ function resolveAssignmentPath(projectDir, assignmentSlug) {
324
+ return resolve2(projectDir, "assignments", assignmentSlug, "assignment.md");
289
325
  }
290
326
  async function readAssignment(filePath) {
291
327
  if (!await fileExists(filePath)) {
@@ -295,11 +331,11 @@ async function readAssignment(filePath) {
295
331
  const frontmatter = parseAssignmentFrontmatter(content);
296
332
  return { content, frontmatter };
297
333
  }
298
- async function checkDependencies(missionDir, dependsOn, terminalStatuses) {
334
+ async function checkDependencies(projectDir, dependsOn, terminalStatuses) {
299
335
  const terminals = terminalStatuses ?? /* @__PURE__ */ new Set(["completed"]);
300
336
  const unmet = [];
301
337
  for (const depSlug of dependsOn) {
302
- const depPath = resolveAssignmentPath(missionDir, depSlug);
338
+ const depPath = resolveAssignmentPath(projectDir, depSlug);
303
339
  if (!await fileExists(depPath)) {
304
340
  unmet.push(`${depSlug} (file not found)`);
305
341
  continue;
@@ -312,8 +348,8 @@ async function checkDependencies(missionDir, dependsOn, terminalStatuses) {
312
348
  }
313
349
  return { satisfied: unmet.length === 0, unmet };
314
350
  }
315
- async function executeTransition(missionDir, assignmentSlug, command, options = {}) {
316
- const filePath = resolveAssignmentPath(missionDir, assignmentSlug);
351
+ async function executeTransition(projectDir, assignmentSlug, command, options = {}) {
352
+ const filePath = resolveAssignmentPath(projectDir, assignmentSlug);
317
353
  const { content, frontmatter } = await readAssignment(filePath);
318
354
  const targetStatus = getTargetStatus(frontmatter.status, command, options.transitionTable);
319
355
  if (!targetStatus) {
@@ -325,7 +361,7 @@ async function executeTransition(missionDir, assignmentSlug, command, options =
325
361
  }
326
362
  const warnings = [];
327
363
  if (command === "start" && frontmatter.dependsOn.length > 0) {
328
- const depCheck = await checkDependencies(missionDir, frontmatter.dependsOn, options.terminalStatuses);
364
+ const depCheck = await checkDependencies(projectDir, frontmatter.dependsOn, options.terminalStatuses);
329
365
  if (!depCheck.satisfied) {
330
366
  warnings.push(`Starting with unmet dependencies: ${depCheck.unmet.join(", ")}`);
331
367
  }
@@ -374,41 +410,11 @@ var init_lifecycle = __esm({
374
410
  }
375
411
  });
376
412
 
377
- // src/utils/paths.ts
378
- import { homedir } from "os";
379
- import { resolve as resolve2 } from "path";
380
- function expandHome(p) {
381
- if (p.startsWith("~/") || p === "~") {
382
- return resolve2(homedir(), p.slice(2));
383
- }
384
- return p;
385
- }
386
- function syntaurRoot() {
387
- return resolve2(homedir(), ".syntaur");
388
- }
389
- function defaultMissionDir() {
390
- return resolve2(syntaurRoot(), "missions");
391
- }
392
- function serversDir() {
393
- return resolve2(syntaurRoot(), "servers");
394
- }
395
- function playbooksDir() {
396
- return resolve2(syntaurRoot(), "playbooks");
397
- }
398
- function todosDir() {
399
- return resolve2(syntaurRoot(), "todos");
400
- }
401
- var init_paths = __esm({
402
- "src/utils/paths.ts"() {
403
- "use strict";
404
- }
405
- });
406
-
407
413
  // src/templates/config.ts
408
414
  function renderConfig(params) {
409
415
  return `---
410
416
  version: "1.0"
411
- defaultMissionDir: ${params.defaultMissionDir}
417
+ defaultProjectDir: ${params.defaultProjectDir}
412
418
  onboarding:
413
419
  completed: false
414
420
  agentDefaults:
@@ -416,7 +422,7 @@ agentDefaults:
416
422
  autoApprove: false
417
423
  backup:
418
424
  repo: null
419
- categories: missions, playbooks, todos, servers, config
425
+ categories: projects, playbooks, todos, servers, config
420
426
  lastBackup: null
421
427
  lastRestore: null
422
428
  ---
@@ -628,8 +634,8 @@ async function writeStatusConfig(statuses) {
628
634
  const statusBlock = serializeStatusConfig(statuses);
629
635
  if (!await fileExists(configPath)) {
630
636
  const content = `---
631
- version: "1.0"
632
- defaultMissionDir: ~/missions
637
+ version: "2.0"
638
+ defaultProjectDir: ~/projects
633
639
  ${statusBlock}
634
640
  ---
635
641
  `;
@@ -640,7 +646,7 @@ ${statusBlock}
640
646
  const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
641
647
  if (!fmMatch) {
642
648
  const content = `---
643
- version: "1.0"
649
+ version: "2.0"
644
650
  ${statusBlock}
645
651
  ---
646
652
  ${existing}`;
@@ -696,18 +702,18 @@ async function updateBackupConfig(backup) {
696
702
  const current = (await readConfig()).backup;
697
703
  const nextBackup = {
698
704
  repo: current?.repo ?? null,
699
- categories: current?.categories ?? "missions, playbooks, todos, servers, config",
705
+ categories: current?.categories ?? "projects, playbooks, todos, servers, config",
700
706
  lastBackup: current?.lastBackup ?? null,
701
707
  lastRestore: current?.lastRestore ?? null,
702
708
  ...backup
703
709
  };
704
710
  const backupBlock = serializeBackupConfig(nextBackup);
705
- const existing = await fileExists(configPath) ? await readFile2(configPath, "utf-8") : renderConfig({ defaultMissionDir: defaultMissionDir() });
711
+ const existing = await fileExists(configPath) ? await readFile2(configPath, "utf-8") : renderConfig({ defaultProjectDir: defaultProjectDir() });
706
712
  const fmMatch = existing.match(/^(---\n)([\s\S]*?)\n(---)/);
707
713
  if (!fmMatch) {
708
714
  const content = `---
709
- version: "1.0"
710
- defaultMissionDir: ${defaultMissionDir()}
715
+ version: "2.0"
716
+ defaultProjectDir: ${defaultProjectDir()}
711
717
  ${backupBlock}
712
718
  ---
713
719
  ${existing}`;
@@ -736,16 +742,16 @@ async function readConfig() {
736
742
  console.warn("Warning: ~/.syntaur/config.md has malformed frontmatter, using defaults");
737
743
  return { ...DEFAULT_CONFIG };
738
744
  }
739
- let missionDir = fm["defaultMissionDir"] ? expandHome(String(fm["defaultMissionDir"])) : DEFAULT_CONFIG.defaultMissionDir;
740
- if (!isAbsolute(missionDir)) {
745
+ let projectDir = fm["defaultProjectDir"] ? expandHome(String(fm["defaultProjectDir"])) : DEFAULT_CONFIG.defaultProjectDir;
746
+ if (!isAbsolute(projectDir)) {
741
747
  console.warn(
742
- `Warning: config.md defaultMissionDir is not an absolute path ("${fm["defaultMissionDir"]}"), using default`
748
+ `Warning: config.md defaultProjectDir is not an absolute path ("${fm["defaultProjectDir"]}"), using default`
743
749
  );
744
- missionDir = DEFAULT_CONFIG.defaultMissionDir;
750
+ projectDir = DEFAULT_CONFIG.defaultProjectDir;
745
751
  }
746
752
  return {
747
753
  version: fm["version"] || DEFAULT_CONFIG.version,
748
- defaultMissionDir: missionDir,
754
+ defaultProjectDir: projectDir,
749
755
  onboarding: {
750
756
  completed: fm["onboarding.completed"] === "true"
751
757
  },
@@ -769,11 +775,12 @@ async function readConfig() {
769
775
  },
770
776
  backup: fm["backup.repo"] || fm["backup.categories"] ? {
771
777
  repo: fm["backup.repo"] && fm["backup.repo"] !== "null" ? fm["backup.repo"] : null,
772
- categories: fm["backup.categories"] || "missions, playbooks, todos, servers, config",
778
+ categories: fm["backup.categories"] || "projects, playbooks, todos, servers, config",
773
779
  lastBackup: fm["backup.lastBackup"] && fm["backup.lastBackup"] !== "null" ? fm["backup.lastBackup"] : null,
774
780
  lastRestore: fm["backup.lastRestore"] && fm["backup.lastRestore"] !== "null" ? fm["backup.lastRestore"] : null
775
781
  } : null,
776
- statuses: parseStatusConfig(content)
782
+ statuses: parseStatusConfig(content),
783
+ types: null
777
784
  };
778
785
  }
779
786
  var DEFAULT_CONFIG;
@@ -784,8 +791,8 @@ var init_config2 = __esm({
784
791
  init_fs();
785
792
  init_config();
786
793
  DEFAULT_CONFIG = {
787
- version: "1.0",
788
- defaultMissionDir: defaultMissionDir(),
794
+ version: "2.0",
795
+ defaultProjectDir: defaultProjectDir(),
789
796
  onboarding: {
790
797
  completed: false
791
798
  },
@@ -799,7 +806,8 @@ var init_config2 = __esm({
799
806
  codexMarketplacePath: null
800
807
  },
801
808
  backup: null,
802
- statuses: null
809
+ statuses: null,
810
+ types: null
803
811
  };
804
812
  }
805
813
  });
@@ -852,7 +860,7 @@ function parseListField(frontmatter, fieldName) {
852
860
  }
853
861
  return results;
854
862
  }
855
- function parseMission(fileContent) {
863
+ function parseProject(fileContent) {
856
864
  const [fm, body] = extractFrontmatter2(fileContent);
857
865
  return {
858
866
  id: getField(fm, "id") ?? "",
@@ -883,13 +891,13 @@ function parseStatus(fileContent) {
883
891
  }
884
892
  }
885
893
  return {
886
- mission: getField(fm, "mission") ?? "",
894
+ project: getField(fm, "project") ?? "",
887
895
  status: getField(fm, "status") ?? "pending",
888
896
  progress,
889
897
  needsAttention: {
890
898
  blockedCount: parseInt(getNestedField(fm, "needsAttention", "blockedCount") ?? "0", 10),
891
899
  failedCount: parseInt(getNestedField(fm, "needsAttention", "failedCount") ?? "0", 10),
892
- unansweredQuestions: parseInt(getNestedField(fm, "needsAttention", "unansweredQuestions") ?? "0", 10)
900
+ openQuestions: parseInt(getNestedField(fm, "needsAttention", "openQuestions") ?? "0", 10)
893
901
  },
894
902
  body
895
903
  };
@@ -931,6 +939,8 @@ function parseAssignmentFull(fileContent) {
931
939
  id: getField(fm, "id") ?? "",
932
940
  slug: getField(fm, "slug") ?? "",
933
941
  title: getField(fm, "title") ?? "",
942
+ project: getField(fm, "project"),
943
+ type: getField(fm, "type"),
934
944
  status: getField(fm, "status") ?? "pending",
935
945
  priority: getField(fm, "priority") ?? "medium",
936
946
  assignee: getField(fm, "assignee"),
@@ -1050,17 +1060,17 @@ async function getDashboardHelp() {
1050
1060
  return {
1051
1061
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1052
1062
  whatIsSyntaur: {
1053
- summary: "Syntaur is a local-first, markdown-backed agent work system. The dashboard is a live view over mission folders and files on disk.",
1063
+ summary: "Syntaur is a local-first, markdown-backed agent work system. The dashboard is a live view over project folders and files on disk.",
1054
1064
  bullets: [
1055
1065
  "Markdown files are the source of truth.",
1056
- "The UI reads mission folders, assignment files, and derived indexes from the local filesystem.",
1066
+ "The UI reads project folders, assignment files, and derived indexes from the local filesystem.",
1057
1067
  "Derived underscore-prefixed files are projections, not the canonical edit target."
1058
1068
  ]
1059
1069
  },
1060
1070
  coreConcepts: [
1061
1071
  {
1062
- term: "Mission",
1063
- description: "A mission is the higher-level objective. It owns assignments, shared resources, and mission memories."
1072
+ term: "Project",
1073
+ description: "A project is the higher-level objective. It owns assignments, shared resources, and project memories."
1064
1074
  },
1065
1075
  {
1066
1076
  term: "Assignment",
@@ -1068,15 +1078,15 @@ async function getDashboardHelp() {
1068
1078
  },
1069
1079
  {
1070
1080
  term: "Resource",
1071
- description: "A mission-level shared reference file that provides source material or constraints for the work."
1081
+ description: "A project-level shared reference file that provides source material or constraints for the work."
1072
1082
  },
1073
1083
  {
1074
1084
  term: "Memory",
1075
- description: "A mission-level learning or pattern captured during execution so future assignments can reuse it."
1085
+ description: "A project-level learning or pattern captured during execution so future assignments can reuse it."
1076
1086
  },
1077
1087
  {
1078
1088
  term: "Manifest",
1079
- description: "A derived navigation file that points agents at the mission overview, indexes, and agent instructions."
1089
+ description: "A derived navigation file that points agents at the project overview, indexes, and agent instructions."
1080
1090
  },
1081
1091
  {
1082
1092
  term: "Derived file",
@@ -1112,12 +1122,12 @@ async function getDashboardHelp() {
1112
1122
  ownershipRules: [
1113
1123
  {
1114
1124
  label: "Human-authored files",
1115
- files: ["mission.md", "agent.md", "claude.md"],
1116
- description: "These files define mission intent and instructions. The dashboard treats mission status as derived except for the archive fields."
1125
+ files: ["project.md", "agent.md", "claude.md"],
1126
+ description: "These files define project intent and instructions. The dashboard treats project status as derived except for the archive fields."
1117
1127
  },
1118
1128
  {
1119
1129
  label: "Assignment working files",
1120
- files: ["assignment.md", "plan.md", "scratchpad.md"],
1130
+ files: ["assignment.md", "plan*.md (optional, versioned)", "scratchpad.md"],
1121
1131
  description: "These are agent-writable files. The dashboard lets you edit the source markdown while preserving unsupported frontmatter keys."
1122
1132
  },
1123
1133
  {
@@ -1139,13 +1149,13 @@ async function getDashboardHelp() {
1139
1149
  href: "/"
1140
1150
  },
1141
1151
  {
1142
- label: "Missions",
1143
- description: "Browse, search, filter, and sort the mission directory. Create new missions and drill into mission workspaces.",
1144
- href: "/missions"
1152
+ label: "Projects",
1153
+ description: "Browse, search, filter, and sort the project directory. Create new projects and drill into project workspaces.",
1154
+ href: "/projects"
1145
1155
  },
1146
1156
  {
1147
1157
  label: "Assignments",
1148
- description: "Cross-mission kanban board of all assignments. Drag cards between columns to change status, or filter by mission, assignee, or status.",
1158
+ description: "Cross-project kanban board of all assignments. Drag cards between columns to change status, or filter by project, assignee, or status.",
1149
1159
  href: "/assignments"
1150
1160
  },
1151
1161
  {
@@ -1179,14 +1189,14 @@ async function getDashboardHelp() {
1179
1189
  href: "/settings"
1180
1190
  },
1181
1191
  {
1182
- label: "Mission page",
1183
- description: "The mission workspace shows health stats, assignment list, dependency graph, shared resources, and memories.",
1184
- href: "/missions"
1192
+ label: "Project page",
1193
+ description: "The project workspace shows health stats, assignment list, dependency graph, shared resources, and memories.",
1194
+ href: "/projects"
1185
1195
  },
1186
1196
  {
1187
1197
  label: "Assignment page",
1188
1198
  description: "The assignment workspace shows lifecycle actions, plan editor, scratchpad, handoff log, decision records, and agent sessions.",
1189
- href: "/missions"
1199
+ href: "/projects"
1190
1200
  }
1191
1201
  ],
1192
1202
  faq: [
@@ -1223,16 +1233,16 @@ async function getDashboardHelp() {
1223
1233
  answer: "Syntaur tracks tmux sessions to discover running dev servers, their ports, git branches, and linked assignments. Register sessions on the Servers page or let autodiscovery find them. Pane info refreshes automatically."
1224
1234
  }
1225
1235
  ],
1226
- firstMissionChecklist: [
1236
+ firstProjectChecklist: [
1227
1237
  {
1228
- title: "Create the mission",
1229
- detail: "Describe the overall objective in mission.md, then add tags and archive metadata only when needed.",
1238
+ title: "Create the project",
1239
+ detail: "Describe the overall objective in project.md, then add tags and archive metadata only when needed.",
1230
1240
  command: CLI_COMMANDS[1],
1231
- href: "/create/mission"
1241
+ href: "/create/project"
1232
1242
  },
1233
1243
  {
1234
1244
  title: "Create at least one assignment",
1235
- detail: "Break the mission into executable work units with explicit priority and dependencies.",
1245
+ detail: "Break the project into executable work units with explicit priority and dependencies.",
1236
1246
  command: CLI_COMMANDS[2]
1237
1247
  },
1238
1248
  {
@@ -1242,8 +1252,8 @@ async function getDashboardHelp() {
1242
1252
  },
1243
1253
  {
1244
1254
  title: "Use the assignment workspace for execution",
1245
- detail: "Keep the objective in assignment.md, the implementation plan in plan.md, and transient notes in scratchpad.md.",
1246
- href: "/missions"
1255
+ detail: "Keep the objective and todos in assignment.md, implementation plans in optional versioned plan files (plan.md, plan-v2.md, ...), and transient notes in scratchpad.md.",
1256
+ href: "/projects"
1247
1257
  },
1248
1258
  {
1249
1259
  title: "Record handoffs and decisions without rewriting history",
@@ -1257,14 +1267,14 @@ async function getDashboardHelp() {
1257
1267
  ],
1258
1268
  links: [
1259
1269
  { label: "Overview", href: "/" },
1260
- { label: "Mission Directory", href: "/missions" },
1270
+ { label: "Project Directory", href: "/projects" },
1261
1271
  { label: "Assignments Board", href: "/assignments" },
1262
1272
  { label: "Attention Queue", href: "/attention" },
1263
1273
  { label: "Servers", href: "/servers" },
1264
1274
  { label: "Agent Sessions", href: "/agent-sessions" },
1265
1275
  { label: "Playbooks", href: "/playbooks" },
1266
1276
  { label: "Settings", href: "/settings" },
1267
- { label: "Create Mission", href: "/create/mission" }
1277
+ { label: "Create Project", href: "/create/project" }
1268
1278
  ]
1269
1279
  };
1270
1280
  }
@@ -1286,60 +1296,60 @@ var init_help = __esm({
1286
1296
  example: "syntaur init"
1287
1297
  },
1288
1298
  {
1289
- command: "syntaur create-mission",
1290
- description: "Create a new mission folder with the required source and derived files.",
1291
- example: 'syntaur create-mission "Ship dashboard overhaul"'
1299
+ command: "syntaur create-project",
1300
+ description: "Create a new project folder with the required source and derived files.",
1301
+ example: 'syntaur create-project "Ship dashboard overhaul"'
1292
1302
  },
1293
1303
  {
1294
1304
  command: "syntaur create-assignment",
1295
- description: "Create a new assignment inside a mission.",
1296
- example: 'syntaur create-assignment "Implement overview API" --mission ui-overhaul'
1305
+ description: "Create a new assignment inside a project.",
1306
+ example: 'syntaur create-assignment "Implement overview API" --project ui-overhaul'
1297
1307
  },
1298
1308
  {
1299
1309
  command: "syntaur assign",
1300
1310
  description: "Set the assignee for an assignment before work begins.",
1301
- example: "syntaur assign implement-overview --mission ui-overhaul --agent codex-1"
1311
+ example: "syntaur assign implement-overview --project ui-overhaul --agent codex-1"
1302
1312
  },
1303
1313
  // --- Lifecycle transitions (indices 5-11) ---
1304
1314
  {
1305
1315
  command: "syntaur start",
1306
1316
  description: "Transition an assignment to in_progress.",
1307
- example: "syntaur start implement-overview --mission ui-overhaul"
1317
+ example: "syntaur start implement-overview --project ui-overhaul"
1308
1318
  },
1309
1319
  {
1310
1320
  command: "syntaur review",
1311
1321
  description: "Move active work into review once implementation is ready for inspection.",
1312
- example: "syntaur review implement-overview --mission ui-overhaul"
1322
+ example: "syntaur review implement-overview --project ui-overhaul"
1313
1323
  },
1314
1324
  {
1315
1325
  command: "syntaur complete",
1316
1326
  description: "Mark an assignment completed after review or direct completion.",
1317
- example: "syntaur complete implement-overview --mission ui-overhaul"
1327
+ example: "syntaur complete implement-overview --project ui-overhaul"
1318
1328
  },
1319
1329
  {
1320
1330
  command: "syntaur block",
1321
1331
  description: "Mark an assignment blocked and record the explicit reason.",
1322
- example: 'syntaur block implement-overview --mission ui-overhaul --reason "Waiting on API spec"'
1332
+ example: 'syntaur block implement-overview --project ui-overhaul --reason "Waiting on API spec"'
1323
1333
  },
1324
1334
  {
1325
1335
  command: "syntaur unblock",
1326
1336
  description: "Move a blocked assignment back to in_progress after the blocker is cleared.",
1327
- example: "syntaur unblock implement-overview --mission ui-overhaul"
1337
+ example: "syntaur unblock implement-overview --project ui-overhaul"
1328
1338
  },
1329
1339
  {
1330
1340
  command: "syntaur fail",
1331
1341
  description: "Mark an assignment failed when it cannot be completed as planned.",
1332
- example: "syntaur fail implement-overview --mission ui-overhaul"
1342
+ example: "syntaur fail implement-overview --project ui-overhaul"
1333
1343
  },
1334
1344
  {
1335
1345
  command: "syntaur reopen",
1336
1346
  description: "Reopen a completed or failed assignment back to in_progress.",
1337
- example: "syntaur reopen implement-overview --mission ui-overhaul"
1347
+ example: "syntaur reopen implement-overview --project ui-overhaul"
1338
1348
  },
1339
1349
  // --- Dashboard (index 12) ---
1340
1350
  {
1341
1351
  command: "syntaur dashboard",
1342
- description: "Start the local dashboard UI over the mission files on disk.",
1352
+ description: "Start the local dashboard UI over the project files on disk.",
1343
1353
  example: "syntaur dashboard --port 4800"
1344
1354
  },
1345
1355
  // --- Plugin & adapter setup (indices 13-16) ---
@@ -1361,18 +1371,18 @@ var init_help = __esm({
1361
1371
  {
1362
1372
  command: "syntaur setup-adapter",
1363
1373
  description: "Generate adapter instruction files for cursor, codex, or opencode in the current directory.",
1364
- example: "syntaur setup-adapter cursor --mission ui-overhaul --assignment implement-overview"
1374
+ example: "syntaur setup-adapter cursor --project ui-overhaul --assignment implement-overview"
1365
1375
  },
1366
1376
  // --- Session & server tracking (index 17) ---
1367
1377
  {
1368
1378
  command: "syntaur track-session",
1369
- description: "Register an agent session, optionally linked to a mission and assignment.",
1370
- example: "syntaur track-session --agent claude --mission ui-overhaul --assignment implement-overview"
1379
+ description: "Register an agent session, optionally linked to a project and assignment.",
1380
+ example: "syntaur track-session --agent claude --project ui-overhaul --assignment implement-overview"
1371
1381
  },
1372
1382
  // --- Browsing & playbooks (indices 18-20) ---
1373
1383
  {
1374
1384
  command: "syntaur browse",
1375
- description: "Interactive TUI browser for missions and assignments.",
1385
+ description: "Interactive TUI browser for projects and assignments.",
1376
1386
  example: "syntaur browse"
1377
1387
  },
1378
1388
  {
@@ -1393,14 +1403,14 @@ var init_help = __esm({
1393
1403
  command: CLI_COMMANDS[0]
1394
1404
  },
1395
1405
  {
1396
- title: "Create a mission",
1397
- detail: "Use a mission for a higher-level objective. Missions group assignments, shared resources, and memories.",
1406
+ title: "Create a project",
1407
+ detail: "Use a project for a higher-level objective. Projects group assignments, shared resources, and memories.",
1398
1408
  command: CLI_COMMANDS[2],
1399
- href: "/create/mission"
1409
+ href: "/create/project"
1400
1410
  },
1401
1411
  {
1402
1412
  title: "Create the first assignment",
1403
- detail: "Assignments are the execution unit. Create one for each concrete chunk of work inside the mission.",
1413
+ detail: "Assignments are the execution unit. Create one for each concrete chunk of work inside the project.",
1404
1414
  command: CLI_COMMANDS[3]
1405
1415
  },
1406
1416
  {
@@ -1415,7 +1425,7 @@ var init_help = __esm({
1415
1425
  },
1416
1426
  {
1417
1427
  title: "Use the dashboard for triage and context",
1418
- detail: "Overview shows the current queue, mission pages show health, assignment pages show the execution surface.",
1428
+ detail: "Overview shows the current queue, project pages show health, assignment pages show the execution surface.",
1419
1429
  command: CLI_COMMANDS[12],
1420
1430
  href: "/"
1421
1431
  }
@@ -1483,7 +1493,7 @@ function buildSessionContent(opts) {
1483
1493
  if (Object.keys(opts.overrides).length > 0) {
1484
1494
  lines.push("overrides:");
1485
1495
  for (const [key, val] of Object.entries(opts.overrides)) {
1486
- lines.push(` "${key}": { mission: "${val.mission}", assignment: "${val.assignment}" }`);
1496
+ lines.push(` "${key}": { project: "${val.project}", assignment: "${val.assignment}" }`);
1487
1497
  }
1488
1498
  }
1489
1499
  lines.push("---", "");
@@ -1520,10 +1530,10 @@ async function readSessionFile(dir, name) {
1520
1530
  const overridesMatch = frontmatter.match(/^overrides:\n((?:\s+".+\n?)*)/m);
1521
1531
  if (overridesMatch) {
1522
1532
  const overrideLines = overridesMatch[1].matchAll(
1523
- /^\s+"([^"]+)":\s*\{\s*mission:\s*"([^"]+)",\s*assignment:\s*"([^"]+)"\s*\}/gm
1533
+ /^\s+"([^"]+)":\s*\{\s*project:\s*"([^"]+)",\s*assignment:\s*"([^"]+)"\s*\}/gm
1524
1534
  );
1525
1535
  for (const m of overrideLines) {
1526
- overrides[m[1]] = { mission: m[2], assignment: m[3] };
1536
+ overrides[m[1]] = { project: m[2], assignment: m[3] };
1527
1537
  }
1528
1538
  }
1529
1539
  const autoField = getField(frontmatter, "auto");
@@ -1725,12 +1735,12 @@ async function getGitInfo(cwd) {
1725
1735
  }
1726
1736
  return { branch: branch || null, worktree: isWorktree };
1727
1737
  }
1728
- async function loadWorkspaceRecords(missionsDir) {
1738
+ async function loadWorkspaceRecords(projectsDir) {
1729
1739
  const records = [];
1730
1740
  try {
1731
- const missions = await listMissions(missionsDir);
1732
- for (const mission of missions) {
1733
- const assignmentsDir = resolve5(missionsDir, mission.slug, "assignments");
1741
+ const projects = await listProjects(projectsDir);
1742
+ for (const project of projects) {
1743
+ const assignmentsDir = resolve5(projectsDir, project.slug, "assignments");
1734
1744
  let slugs;
1735
1745
  try {
1736
1746
  slugs = await readdir2(assignmentsDir);
@@ -1744,7 +1754,7 @@ async function loadWorkspaceRecords(missionsDir) {
1744
1754
  const [fm] = extractFrontmatter2(raw);
1745
1755
  if (!fm) continue;
1746
1756
  records.push({
1747
- missionSlug: mission.slug,
1757
+ projectSlug: project.slug,
1748
1758
  assignmentSlug: aslug,
1749
1759
  assignmentTitle: getField(fm, "title") ?? aslug,
1750
1760
  worktreePath: getNestedField(fm, "workspace", "worktreePath") ?? null,
@@ -1773,14 +1783,14 @@ async function autoLinkPane(cwd, branch, records) {
1773
1783
  if (rec.worktreePath) {
1774
1784
  const normalizedWt = await resolveAndNormalize(rec.worktreePath);
1775
1785
  if (normalizedCwd === normalizedWt) {
1776
- return { mission: rec.missionSlug, slug: rec.assignmentSlug, title: rec.assignmentTitle };
1786
+ return { project: rec.projectSlug, slug: rec.assignmentSlug, title: rec.assignmentTitle };
1777
1787
  }
1778
1788
  }
1779
1789
  }
1780
1790
  if (branch) {
1781
1791
  for (const rec of records) {
1782
1792
  if (rec.branch && rec.branch === branch) {
1783
- return { mission: rec.missionSlug, slug: rec.assignmentSlug, title: rec.assignmentTitle };
1793
+ return { project: rec.projectSlug, slug: rec.assignmentSlug, title: rec.assignmentTitle };
1784
1794
  }
1785
1795
  }
1786
1796
  }
@@ -1850,10 +1860,10 @@ async function scanSession(sessionData, lsofOutput, workspaceRecords) {
1850
1860
  let assignment = null;
1851
1861
  if (override) {
1852
1862
  const rec = workspaceRecords.find(
1853
- (r) => r.missionSlug === override.mission && r.assignmentSlug === override.assignment
1863
+ (r) => r.projectSlug === override.project && r.assignmentSlug === override.assignment
1854
1864
  );
1855
1865
  assignment = {
1856
- mission: override.mission,
1866
+ project: override.project,
1857
1867
  slug: override.assignment,
1858
1868
  title: rec?.assignmentTitle ?? override.assignment
1859
1869
  };
@@ -1912,10 +1922,10 @@ async function scanProcessSession(sessionData, lsofOutput, workspaceRecords) {
1912
1922
  let assignment = null;
1913
1923
  if (override) {
1914
1924
  const rec = workspaceRecords.find(
1915
- (r) => r.missionSlug === override.mission && r.assignmentSlug === override.assignment
1925
+ (r) => r.projectSlug === override.project && r.assignmentSlug === override.assignment
1916
1926
  );
1917
1927
  assignment = {
1918
- mission: override.mission,
1928
+ project: override.project,
1919
1929
  slug: override.assignment,
1920
1930
  title: rec?.assignmentTitle ?? override.assignment
1921
1931
  };
@@ -1942,14 +1952,14 @@ async function scanProcessSession(sessionData, lsofOutput, workspaceRecords) {
1942
1952
  windows: [{ index: 0, name: "process", panes: [pane] }]
1943
1953
  };
1944
1954
  }
1945
- async function scanAllSessions(serversDir2, missionsDir, options) {
1955
+ async function scanAllSessions(serversDir2, projectsDir, options) {
1946
1956
  if (!options?.bypassCache && cache && Date.now() < cache.expiry) {
1947
1957
  return cache.data;
1948
1958
  }
1949
1959
  const tmuxAvailable = await checkTmuxAvailable();
1950
1960
  const names = await listSessionFiles(serversDir2);
1951
1961
  const lsofOutput = await getLsofOutput();
1952
- const workspaceRecords = await loadWorkspaceRecords(missionsDir);
1962
+ const workspaceRecords = await loadWorkspaceRecords(projectsDir);
1953
1963
  const sessions = [];
1954
1964
  for (const name of names) {
1955
1965
  const data = await readSessionFile(serversDir2, name);
@@ -1964,11 +1974,11 @@ async function scanAllSessions(serversDir2, missionsDir, options) {
1964
1974
  cache = { data: result, expiry: Date.now() + CACHE_TTL_MS };
1965
1975
  return result;
1966
1976
  }
1967
- async function scanSingleSession(serversDir2, missionsDir, name) {
1977
+ async function scanSingleSession(serversDir2, projectsDir, name) {
1968
1978
  const data = await readSessionFile(serversDir2, name);
1969
1979
  if (!data) return null;
1970
1980
  const lsofOutput = await getLsofOutput();
1971
- const workspaceRecords = await loadWorkspaceRecords(missionsDir);
1981
+ const workspaceRecords = await loadWorkspaceRecords(projectsDir);
1972
1982
  if (data.kind === "process") {
1973
1983
  return scanProcessSession(data, lsofOutput, workspaceRecords);
1974
1984
  }
@@ -2045,12 +2055,12 @@ async function getStatusConfig() {
2045
2055
  function clearStatusConfigCache() {
2046
2056
  _cachedConfig = null;
2047
2057
  }
2048
- async function listMissions(missionsDir) {
2049
- const missionRecords = await listMissionRecords(missionsDir);
2050
- return missionRecords.map((record) => record.summary);
2058
+ async function listProjects(projectsDir) {
2059
+ const projectRecords = await listProjectRecords(projectsDir);
2060
+ return projectRecords.map((record) => record.summary);
2051
2061
  }
2052
- async function readWorkspaceRegistry(missionsDir) {
2053
- const registryPath = resolve6(dirname2(missionsDir), "workspaces.json");
2062
+ async function readWorkspaceRegistry(projectsDir) {
2063
+ const registryPath = resolve6(dirname2(projectsDir), "workspaces.json");
2054
2064
  try {
2055
2065
  const raw = await readFile5(registryPath, "utf-8");
2056
2066
  const parsed = JSON.parse(raw);
@@ -2059,20 +2069,20 @@ async function readWorkspaceRegistry(missionsDir) {
2059
2069
  return [];
2060
2070
  }
2061
2071
  }
2062
- async function writeWorkspaceRegistry(missionsDir, workspaces) {
2063
- const registryPath = resolve6(dirname2(missionsDir), "workspaces.json");
2072
+ async function writeWorkspaceRegistry(projectsDir, workspaces) {
2073
+ const registryPath = resolve6(dirname2(projectsDir), "workspaces.json");
2064
2074
  await writeFile2(registryPath, JSON.stringify(workspaces, null, 2) + "\n", "utf-8");
2065
2075
  }
2066
- async function listWorkspaces(missionsDir) {
2067
- const [missionRecords, registered] = await Promise.all([
2068
- listMissionRecords(missionsDir),
2069
- readWorkspaceRegistry(missionsDir)
2076
+ async function listWorkspaces(projectsDir) {
2077
+ const [projectRecords, registered] = await Promise.all([
2078
+ listProjectRecords(projectsDir),
2079
+ readWorkspaceRegistry(projectsDir)
2070
2080
  ]);
2071
2081
  const workspaceSet = new Set(registered);
2072
2082
  let hasUngrouped = false;
2073
- for (const record of missionRecords) {
2074
- if (record.mission.workspace) {
2075
- workspaceSet.add(record.mission.workspace);
2083
+ for (const record of projectRecords) {
2084
+ if (record.project.workspace) {
2085
+ workspaceSet.add(record.project.workspace);
2076
2086
  } else {
2077
2087
  hasUngrouped = true;
2078
2088
  }
@@ -2080,28 +2090,28 @@ async function listWorkspaces(missionsDir) {
2080
2090
  const workspaces = Array.from(workspaceSet).sort();
2081
2091
  return { workspaces, hasUngrouped };
2082
2092
  }
2083
- async function createWorkspace(missionsDir, name) {
2084
- const registered = await readWorkspaceRegistry(missionsDir);
2093
+ async function createWorkspace(projectsDir, name) {
2094
+ const registered = await readWorkspaceRegistry(projectsDir);
2085
2095
  if (!registered.includes(name)) {
2086
2096
  registered.push(name);
2087
2097
  registered.sort();
2088
- await writeWorkspaceRegistry(missionsDir, registered);
2098
+ await writeWorkspaceRegistry(projectsDir, registered);
2089
2099
  }
2090
2100
  }
2091
- async function deleteWorkspace(missionsDir, name) {
2092
- const registered = await readWorkspaceRegistry(missionsDir);
2101
+ async function deleteWorkspace(projectsDir, name) {
2102
+ const registered = await readWorkspaceRegistry(projectsDir);
2093
2103
  const filtered = registered.filter((w) => w !== name);
2094
- await writeWorkspaceRegistry(missionsDir, filtered);
2104
+ await writeWorkspaceRegistry(projectsDir, filtered);
2095
2105
  }
2096
- async function getOverview(missionsDir, serversDir2) {
2097
- const missionRecords = await listMissionRecords(missionsDir);
2098
- const attention = buildAttentionItems(missionRecords);
2099
- const recentActivity = buildRecentActivity(missionRecords);
2106
+ async function getOverview(projectsDir, serversDir2) {
2107
+ const projectRecords = await listProjectRecords(projectsDir);
2108
+ const attention = buildAttentionItems(projectRecords);
2109
+ const recentActivity = buildRecentActivity(projectRecords);
2100
2110
  let serverStats;
2101
2111
  if (serversDir2) {
2102
2112
  try {
2103
2113
  const { scanAllSessions: scanAllSessions2 } = await Promise.resolve().then(() => (init_scanner(), scanner_exports));
2104
- const servers = await scanAllSessions2(serversDir2, missionsDir);
2114
+ const servers = await scanAllSessions2(serversDir2, projectsDir);
2105
2115
  if (servers.tmuxAvailable) {
2106
2116
  const alive = servers.sessions.filter((s) => s.alive).length;
2107
2117
  const totalPorts = servers.sessions.reduce((sum, s) => sum + s.windows.reduce((ws, w) => ws + w.panes.reduce((ps, p) => ps + p.ports.length, 0), 0), 0);
@@ -2117,50 +2127,50 @@ async function getOverview(missionsDir, serversDir2) {
2117
2127
  }
2118
2128
  return {
2119
2129
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
2120
- firstRun: missionRecords.length === 0,
2130
+ firstRun: projectRecords.length === 0,
2121
2131
  stats: {
2122
- activeMissions: missionRecords.filter((record) => record.summary.status === "active").length,
2123
- inProgressAssignments: missionRecords.reduce(
2132
+ activeProjects: projectRecords.filter((record) => record.summary.status === "active").length,
2133
+ inProgressAssignments: projectRecords.reduce(
2124
2134
  (total, record) => total + (record.summary.progress["in_progress"] ?? 0),
2125
2135
  0
2126
2136
  ),
2127
- blockedAssignments: missionRecords.reduce(
2137
+ blockedAssignments: projectRecords.reduce(
2128
2138
  (total, record) => total + (record.summary.progress["blocked"] ?? 0),
2129
2139
  0
2130
2140
  ),
2131
- reviewAssignments: missionRecords.reduce(
2141
+ reviewAssignments: projectRecords.reduce(
2132
2142
  (total, record) => total + (record.summary.progress["review"] ?? 0),
2133
2143
  0
2134
2144
  ),
2135
- failedAssignments: missionRecords.reduce(
2145
+ failedAssignments: projectRecords.reduce(
2136
2146
  (total, record) => total + (record.summary.progress["failed"] ?? 0),
2137
2147
  0
2138
2148
  ),
2139
- staleAssignments: missionRecords.reduce(
2149
+ staleAssignments: projectRecords.reduce(
2140
2150
  (total, record) => total + record.assignments.filter((assignment) => isStale(assignment.updated)).length,
2141
2151
  0
2142
2152
  )
2143
2153
  },
2144
2154
  attention: attention.slice(0, OVERVIEW_ATTENTION_LIMIT),
2145
- recentMissions: missionRecords.map((record) => record.summary).sort((left, right) => compareTimestamps(right.updated, left.updated)).slice(0, RECENT_MISSIONS_LIMIT),
2155
+ recentProjects: projectRecords.map((record) => record.summary).sort((left, right) => compareTimestamps(right.updated, left.updated)).slice(0, RECENT_PROJECTS_LIMIT),
2146
2156
  recentActivity: recentActivity.slice(0, RECENT_ACTIVITY_LIMIT),
2147
2157
  serverStats
2148
2158
  };
2149
2159
  }
2150
- async function getAttention(missionsDir, serversDir2) {
2151
- const missionRecords = await listMissionRecords(missionsDir);
2152
- const items = buildAttentionItems(missionRecords);
2160
+ async function getAttention(projectsDir, serversDir2) {
2161
+ const projectRecords = await listProjectRecords(projectsDir);
2162
+ const items = buildAttentionItems(projectRecords);
2153
2163
  if (serversDir2) {
2154
2164
  try {
2155
2165
  const { scanAllSessions: scanAllSessions2 } = await Promise.resolve().then(() => (init_scanner(), scanner_exports));
2156
- const servers = await scanAllSessions2(serversDir2, missionsDir);
2166
+ const servers = await scanAllSessions2(serversDir2, projectsDir);
2157
2167
  for (const session of servers.sessions) {
2158
2168
  if (!session.alive) {
2159
2169
  items.push({
2160
2170
  id: `server-dead-${session.name}`,
2161
2171
  severity: "low",
2162
- missionSlug: "",
2163
- missionTitle: "",
2172
+ projectSlug: "",
2173
+ projectTitle: "",
2164
2174
  assignmentSlug: "",
2165
2175
  assignmentTitle: `tmux: ${session.name}`,
2166
2176
  status: "failed",
@@ -2197,13 +2207,13 @@ async function getAttention(missionsDir, serversDir2) {
2197
2207
  items: pagedItems
2198
2208
  };
2199
2209
  }
2200
- async function listAssignmentsBoard(missionsDir) {
2201
- const missionRecords = await listMissionRecords(missionsDir);
2210
+ async function listAssignmentsBoard(projectsDir) {
2211
+ const projectRecords = await listProjectRecords(projectsDir);
2202
2212
  const assignments = await Promise.all(
2203
- missionRecords.flatMap(
2213
+ projectRecords.flatMap(
2204
2214
  async (record) => Promise.all(
2205
2215
  record.assignments.map(
2206
- async (assignment) => toAssignmentBoardItem(missionsDir, record, assignment)
2216
+ async (assignment) => toAssignmentBoardItem(projectsDir, record, assignment)
2207
2217
  )
2208
2218
  )
2209
2219
  )
@@ -2216,59 +2226,59 @@ async function listAssignmentsBoard(missionsDir) {
2216
2226
  async function getHelp() {
2217
2227
  return getDashboardHelp();
2218
2228
  }
2219
- async function getEditableDocument(missionsDir, documentType, missionSlug, assignmentSlug) {
2220
- const filePath = getDocumentPath(missionsDir, documentType, missionSlug, assignmentSlug);
2229
+ async function getEditableDocument(projectsDir, documentType, projectSlug, assignmentSlug) {
2230
+ const filePath = getDocumentPath(projectsDir, documentType, projectSlug, assignmentSlug);
2221
2231
  if (!filePath || !await fileExists(filePath)) {
2222
2232
  return null;
2223
2233
  }
2224
2234
  const content = await readFile5(filePath, "utf-8");
2225
- const title = getEditableDocumentTitle(documentType, missionSlug, assignmentSlug);
2235
+ const title = getEditableDocumentTitle(documentType, projectSlug, assignmentSlug);
2226
2236
  return {
2227
2237
  documentType,
2228
2238
  title,
2229
2239
  content,
2230
- missionSlug,
2240
+ projectSlug,
2231
2241
  assignmentSlug,
2232
2242
  appendOnly: documentType === "handoff" || documentType === "decision-record"
2233
2243
  };
2234
2244
  }
2235
- async function getMissionDetail(missionsDir, slug) {
2236
- const missionPath = resolve6(missionsDir, slug);
2237
- const missionMdPath = resolve6(missionPath, "mission.md");
2238
- if (!await fileExists(missionMdPath)) {
2245
+ async function getProjectDetail(projectsDir, slug) {
2246
+ const projectPath = resolve6(projectsDir, slug);
2247
+ const projectMdPath = resolve6(projectPath, "project.md");
2248
+ if (!await fileExists(projectMdPath)) {
2239
2249
  return null;
2240
2250
  }
2241
- const missionContent = await readFile5(missionMdPath, "utf-8");
2242
- const mission = parseMission(missionContent);
2243
- const assignments = await listAssignmentRecords(missionPath);
2244
- const rollup = buildMissionRollup(mission, assignments);
2245
- const dependencyGraph = await loadDependencyGraph(missionPath, assignments);
2246
- const resources = await listResources(missionPath);
2247
- const memories = await listMemories(missionPath);
2248
- const updated = getMissionActivityTimestamp(mission.updated, assignments);
2251
+ const projectContent = await readFile5(projectMdPath, "utf-8");
2252
+ const project = parseProject(projectContent);
2253
+ const assignments = await listAssignmentRecords(projectPath);
2254
+ const rollup = buildProjectRollup(project, assignments);
2255
+ const dependencyGraph = await loadDependencyGraph(projectPath, assignments);
2256
+ const resources = await listResources(projectPath);
2257
+ const memories = await listMemories(projectPath);
2258
+ const updated = getProjectActivityTimestamp(project.updated, assignments);
2249
2259
  return {
2250
- slug: mission.slug || slug,
2251
- title: mission.title,
2260
+ slug: project.slug || slug,
2261
+ title: project.title,
2252
2262
  status: rollup.status,
2253
- statusOverride: mission.statusOverride,
2254
- archived: mission.archived,
2255
- archivedAt: mission.archivedAt,
2256
- archivedReason: mission.archivedReason,
2257
- created: mission.created,
2263
+ statusOverride: project.statusOverride,
2264
+ archived: project.archived,
2265
+ archivedAt: project.archivedAt,
2266
+ archivedReason: project.archivedReason,
2267
+ created: project.created,
2258
2268
  updated,
2259
- tags: mission.tags,
2260
- body: mission.body,
2269
+ tags: project.tags,
2270
+ body: project.body,
2261
2271
  progress: rollup.progress,
2262
2272
  needsAttention: rollup.needsAttention,
2263
2273
  assignments: assignments.map(toAssignmentSummary).sort((left, right) => compareTimestamps(right.updated, left.updated)),
2264
2274
  resources,
2265
2275
  memories,
2266
2276
  dependencyGraph,
2267
- workspace: mission.workspace
2277
+ workspace: project.workspace
2268
2278
  };
2269
2279
  }
2270
- async function getAssignmentDetail(missionsDir, missionSlug, assignmentSlug) {
2271
- const assignmentDir = resolve6(missionsDir, missionSlug, "assignments", assignmentSlug);
2280
+ async function getAssignmentDetail(projectsDir, projectSlug, assignmentSlug) {
2281
+ const assignmentDir = resolve6(projectsDir, projectSlug, "assignments", assignmentSlug);
2272
2282
  const assignmentMdPath = resolve6(assignmentDir, "assignment.md");
2273
2283
  if (!await fileExists(assignmentMdPath)) {
2274
2284
  return null;
@@ -2320,7 +2330,7 @@ async function getAssignmentDetail(missionsDir, missionSlug, assignmentSlug) {
2320
2330
  }
2321
2331
  const detail = {
2322
2332
  id: assignment.id,
2323
- missionSlug,
2333
+ projectSlug,
2324
2334
  slug: assignment.slug || assignmentSlug,
2325
2335
  title: assignment.title,
2326
2336
  status: assignment.status,
@@ -2342,16 +2352,16 @@ async function getAssignmentDetail(missionsDir, missionSlug, assignmentSlug) {
2342
2352
  handoff,
2343
2353
  decisionRecord,
2344
2354
  availableTransitions: await getAvailableTransitions(
2345
- missionsDir,
2346
- missionSlug,
2355
+ projectsDir,
2356
+ projectSlug,
2347
2357
  assignmentSlug,
2348
2358
  assignment
2349
2359
  )
2350
2360
  };
2351
- const selfSlug = `${missionSlug}/${detail.slug}`;
2352
- const missionRecords = await listMissionRecords(missionsDir);
2361
+ const selfSlug = `${projectSlug}/${detail.slug}`;
2362
+ const projectRecords = await listProjectRecords(projectsDir);
2353
2363
  const reverseLinks = [];
2354
- for (const mr of missionRecords) {
2364
+ for (const mr of projectRecords) {
2355
2365
  for (const a of mr.assignments) {
2356
2366
  const qualifiedSlug = `${mr.summary.slug}/${a.slug}`;
2357
2367
  if (qualifiedSlug === selfSlug) continue;
@@ -2369,10 +2379,10 @@ async function getAssignmentDetail(missionsDir, missionSlug, assignmentSlug) {
2369
2379
  const dedupedReverseLinks = reverseLinks.filter((l) => !forwardSet.has(l));
2370
2380
  detail.links = forwardLinks;
2371
2381
  detail.reverseLinks = dedupedReverseLinks;
2372
- const allMissionAssignments = /* @__PURE__ */ new Map();
2373
- for (const mr of missionRecords) {
2382
+ const allProjectAssignments = /* @__PURE__ */ new Map();
2383
+ for (const mr of projectRecords) {
2374
2384
  for (const a of mr.assignments) {
2375
- allMissionAssignments.set(`${mr.summary.slug}/${a.slug}`, {
2385
+ allProjectAssignments.set(`${mr.summary.slug}/${a.slug}`, {
2376
2386
  title: a.title,
2377
2387
  status: a.status
2378
2388
  });
@@ -2381,10 +2391,10 @@ async function getAssignmentDetail(missionsDir, missionSlug, assignmentSlug) {
2381
2391
  const enrichedLinks = [];
2382
2392
  for (const linkSlug of forwardLinks) {
2383
2393
  const [ms, as] = linkSlug.split("/");
2384
- const info = allMissionAssignments.get(linkSlug);
2394
+ const info = allProjectAssignments.get(linkSlug);
2385
2395
  enrichedLinks.push({
2386
2396
  slug: linkSlug,
2387
- missionSlug: ms,
2397
+ projectSlug: ms,
2388
2398
  assignmentSlug: as,
2389
2399
  title: info?.title ?? linkSlug,
2390
2400
  status: info?.status ?? "pending",
@@ -2393,10 +2403,10 @@ async function getAssignmentDetail(missionsDir, missionSlug, assignmentSlug) {
2393
2403
  }
2394
2404
  for (const linkSlug of dedupedReverseLinks) {
2395
2405
  const [ms, as] = linkSlug.split("/");
2396
- const info = allMissionAssignments.get(linkSlug);
2406
+ const info = allProjectAssignments.get(linkSlug);
2397
2407
  enrichedLinks.push({
2398
2408
  slug: linkSlug,
2399
- missionSlug: ms,
2409
+ projectSlug: ms,
2400
2410
  assignmentSlug: as,
2401
2411
  title: info?.title ?? linkSlug,
2402
2412
  status: info?.status ?? "pending",
@@ -2406,51 +2416,51 @@ async function getAssignmentDetail(missionsDir, missionSlug, assignmentSlug) {
2406
2416
  detail.enrichedLinks = enrichedLinks;
2407
2417
  return detail;
2408
2418
  }
2409
- async function listMissionRecords(missionsDir) {
2410
- if (!await fileExists(missionsDir)) {
2419
+ async function listProjectRecords(projectsDir) {
2420
+ if (!await fileExists(projectsDir)) {
2411
2421
  return [];
2412
2422
  }
2413
- const entries = await readdir3(missionsDir, { withFileTypes: true });
2414
- const missionDirs = entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
2423
+ const entries = await readdir3(projectsDir, { withFileTypes: true });
2424
+ const projectDirs = entries.filter((entry) => entry.isDirectory() && !entry.name.startsWith("."));
2415
2425
  const records = [];
2416
- for (const entry of missionDirs) {
2417
- const missionPath = resolve6(missionsDir, entry.name);
2418
- const missionMdPath = resolve6(missionPath, "mission.md");
2419
- if (!await fileExists(missionMdPath)) {
2426
+ for (const entry of projectDirs) {
2427
+ const projectPath = resolve6(projectsDir, entry.name);
2428
+ const projectMdPath = resolve6(projectPath, "project.md");
2429
+ if (!await fileExists(projectMdPath)) {
2420
2430
  continue;
2421
2431
  }
2422
- const missionContent = await readFile5(missionMdPath, "utf-8");
2423
- const mission = parseMission(missionContent);
2424
- const assignments = await listAssignmentRecords(missionPath);
2425
- const rollup = buildMissionRollup(mission, assignments);
2426
- const updated = getMissionActivityTimestamp(mission.updated, assignments);
2432
+ const projectContent = await readFile5(projectMdPath, "utf-8");
2433
+ const project = parseProject(projectContent);
2434
+ const assignments = await listAssignmentRecords(projectPath);
2435
+ const rollup = buildProjectRollup(project, assignments);
2436
+ const updated = getProjectActivityTimestamp(project.updated, assignments);
2427
2437
  records.push({
2428
- missionPath,
2429
- mission,
2438
+ projectPath,
2439
+ project,
2430
2440
  assignments,
2431
- dependencyGraph: await loadDependencyGraph(missionPath, assignments),
2441
+ dependencyGraph: await loadDependencyGraph(projectPath, assignments),
2432
2442
  summary: {
2433
- slug: mission.slug || entry.name,
2434
- title: mission.title,
2443
+ slug: project.slug || entry.name,
2444
+ title: project.title,
2435
2445
  status: rollup.status,
2436
- statusOverride: mission.statusOverride,
2437
- archived: mission.archived,
2438
- archivedAt: mission.archivedAt,
2439
- archivedReason: mission.archivedReason,
2440
- created: mission.created,
2446
+ statusOverride: project.statusOverride,
2447
+ archived: project.archived,
2448
+ archivedAt: project.archivedAt,
2449
+ archivedReason: project.archivedReason,
2450
+ created: project.created,
2441
2451
  updated,
2442
- tags: mission.tags,
2452
+ tags: project.tags,
2443
2453
  progress: rollup.progress,
2444
2454
  needsAttention: rollup.needsAttention,
2445
- workspace: mission.workspace
2455
+ workspace: project.workspace
2446
2456
  }
2447
2457
  });
2448
2458
  }
2449
2459
  records.sort((left, right) => compareTimestamps(right.summary.updated, left.summary.updated));
2450
2460
  return records;
2451
2461
  }
2452
- async function listAssignmentRecords(missionPath) {
2453
- const assignmentsDir = resolve6(missionPath, "assignments");
2462
+ async function listAssignmentRecords(projectPath) {
2463
+ const assignmentsDir = resolve6(projectPath, "assignments");
2454
2464
  if (!await fileExists(assignmentsDir)) {
2455
2465
  return [];
2456
2466
  }
@@ -2470,8 +2480,8 @@ async function listAssignmentRecords(missionPath) {
2470
2480
  records.sort((left, right) => compareTimestamps(right.updated, left.updated));
2471
2481
  return records;
2472
2482
  }
2473
- async function listResources(missionPath) {
2474
- const resourcesDir = resolve6(missionPath, "resources");
2483
+ async function listResources(projectPath) {
2484
+ const resourcesDir = resolve6(projectPath, "resources");
2475
2485
  if (!await fileExists(resourcesDir)) {
2476
2486
  return [];
2477
2487
  }
@@ -2496,8 +2506,8 @@ async function listResources(missionPath) {
2496
2506
  results.sort((left, right) => compareTimestamps(right.updated, left.updated));
2497
2507
  return results;
2498
2508
  }
2499
- async function listMemories(missionPath) {
2500
- const memoriesDir = resolve6(missionPath, "memories");
2509
+ async function listMemories(projectPath) {
2510
+ const memoriesDir = resolve6(projectPath, "memories");
2501
2511
  if (!await fileExists(memoriesDir)) {
2502
2512
  return [];
2503
2513
  }
@@ -2522,8 +2532,8 @@ async function listMemories(missionPath) {
2522
2532
  results.sort((left, right) => compareTimestamps(right.updated, left.updated));
2523
2533
  return results;
2524
2534
  }
2525
- async function loadDependencyGraph(missionPath, assignments) {
2526
- const statusPath = resolve6(missionPath, "_status.md");
2535
+ async function loadDependencyGraph(projectPath, assignments) {
2536
+ const statusPath = resolve6(projectPath, "_status.md");
2527
2537
  if (await fileExists(statusPath)) {
2528
2538
  const statusContent = await readFile5(statusPath, "utf-8");
2529
2539
  const parsed = parseStatus(statusContent);
@@ -2534,23 +2544,23 @@ async function loadDependencyGraph(missionPath, assignments) {
2534
2544
  }
2535
2545
  return buildDependencyGraph(assignments);
2536
2546
  }
2537
- function buildMissionRollup(mission, assignments) {
2547
+ function buildProjectRollup(project, assignments) {
2538
2548
  const progress = { total: assignments.length };
2539
- let unansweredQuestions = 0;
2549
+ let openQuestions = 0;
2540
2550
  for (const assignment of assignments) {
2541
2551
  const s = assignment.status;
2542
2552
  progress[s] = (progress[s] ?? 0) + 1;
2543
- unansweredQuestions += countPendingAnswers(assignment.body);
2553
+ openQuestions += countPendingAnswers(assignment.body);
2544
2554
  }
2545
2555
  const needsAttention = {
2546
2556
  blockedCount: progress["blocked"] ?? 0,
2547
2557
  failedCount: progress["failed"] ?? 0,
2548
- unansweredQuestions
2558
+ openQuestions
2549
2559
  };
2550
2560
  let status = "pending";
2551
- if (mission.statusOverride) {
2552
- status = mission.statusOverride;
2553
- } else if (mission.archived) {
2561
+ if (project.statusOverride) {
2562
+ status = project.statusOverride;
2563
+ } else if (project.archived) {
2554
2564
  status = "archived";
2555
2565
  } else if (progress.total > 0 && (progress["completed"] ?? 0) === progress.total) {
2556
2566
  status = "completed";
@@ -2580,16 +2590,16 @@ function toAssignmentSummary(assignment) {
2580
2590
  updated: assignment.updated
2581
2591
  };
2582
2592
  }
2583
- async function toAssignmentBoardItem(missionsDir, missionRecord, assignment) {
2593
+ async function toAssignmentBoardItem(projectsDir, projectRecord, assignment) {
2584
2594
  return {
2585
2595
  ...toAssignmentSummary(assignment),
2586
- missionSlug: missionRecord.summary.slug,
2587
- missionTitle: missionRecord.summary.title,
2596
+ projectSlug: projectRecord.summary.slug,
2597
+ projectTitle: projectRecord.summary.title,
2588
2598
  blockedReason: assignment.blockedReason,
2589
- missionWorkspace: missionRecord.mission.workspace,
2599
+ projectWorkspace: projectRecord.project.workspace,
2590
2600
  availableTransitions: await getAvailableTransitions(
2591
- missionsDir,
2592
- missionRecord.summary.slug,
2601
+ projectsDir,
2602
+ projectRecord.summary.slug,
2593
2603
  assignment.slug,
2594
2604
  assignment
2595
2605
  )
@@ -2621,18 +2631,18 @@ function buildDependencyGraph(assignments) {
2621
2631
  function findAssignmentStatus(assignments, slug) {
2622
2632
  return assignments.find((assignment) => assignment.slug === slug)?.status ?? "pending";
2623
2633
  }
2624
- async function getAvailableTransitions(missionsDir, missionSlug, assignmentSlug, assignment) {
2634
+ async function getAvailableTransitions(projectsDir, projectSlug, assignmentSlug, assignment) {
2625
2635
  const config = await getStatusConfig();
2626
2636
  const transitionDefs = getTransitionDefinitions(config);
2627
2637
  const actions = [];
2628
- const missionPath = resolve6(missionsDir, missionSlug);
2638
+ const projectPath = resolve6(projectsDir, projectSlug);
2629
2639
  for (const definition of transitionDefs) {
2630
2640
  let warning = null;
2631
2641
  if (definition.command === "start" && !assignment.assignee) {
2632
2642
  warning = "No assignee set \u2014 consider assigning before starting.";
2633
2643
  }
2634
2644
  if (definition.command === "start" && assignment.dependsOn.length > 0) {
2635
- const unmetDependencies = await getUnmetDependencies(missionPath, assignment.dependsOn, config.terminalStatuses);
2645
+ const unmetDependencies = await getUnmetDependencies(projectPath, assignment.dependsOn, config.terminalStatuses);
2636
2646
  if (unmetDependencies.length > 0) {
2637
2647
  warning = `Unmet dependencies: ${unmetDependencies.join(", ")}.`;
2638
2648
  }
@@ -2651,11 +2661,11 @@ async function getAvailableTransitions(missionsDir, missionSlug, assignmentSlug,
2651
2661
  }
2652
2662
  return actions;
2653
2663
  }
2654
- async function getUnmetDependencies(missionPath, dependsOn, terminalStatuses) {
2664
+ async function getUnmetDependencies(projectPath, dependsOn, terminalStatuses) {
2655
2665
  const terminals = terminalStatuses ?? /* @__PURE__ */ new Set(["completed"]);
2656
2666
  const unmet = [];
2657
2667
  for (const dependency of dependsOn) {
2658
- const dependencyPath = resolve6(missionPath, "assignments", dependency, "assignment.md");
2668
+ const dependencyPath = resolve6(projectPath, "assignments", dependency, "assignment.md");
2659
2669
  if (!await fileExists(dependencyPath)) {
2660
2670
  unmet.push(`${dependency} (missing)`);
2661
2671
  continue;
@@ -2668,19 +2678,19 @@ async function getUnmetDependencies(missionPath, dependsOn, terminalStatuses) {
2668
2678
  }
2669
2679
  return unmet;
2670
2680
  }
2671
- function buildAttentionItems(missionRecords) {
2681
+ function buildAttentionItems(projectRecords) {
2672
2682
  const items = [];
2673
- for (const record of missionRecords) {
2683
+ for (const record of projectRecords) {
2674
2684
  for (const assignment of record.assignments) {
2675
2685
  const stale = isStale(assignment.updated);
2676
2686
  const base = {
2677
- missionSlug: record.summary.slug,
2678
- missionTitle: record.summary.title,
2687
+ projectSlug: record.summary.slug,
2688
+ projectTitle: record.summary.title,
2679
2689
  assignmentSlug: assignment.slug,
2680
2690
  assignmentTitle: assignment.title,
2681
2691
  status: assignment.status,
2682
2692
  updated: assignment.updated,
2683
- href: `/missions/${record.summary.slug}/assignments/${assignment.slug}`,
2693
+ href: `/projects/${record.summary.slug}/assignments/${assignment.slug}`,
2684
2694
  blockedReason: assignment.blockedReason,
2685
2695
  stale
2686
2696
  };
@@ -2720,19 +2730,19 @@ function buildAttentionItems(missionRecords) {
2720
2730
  }
2721
2731
  return items.sort(compareAttentionItems);
2722
2732
  }
2723
- function buildRecentActivity(missionRecords) {
2733
+ function buildRecentActivity(projectRecords) {
2724
2734
  const activity = [];
2725
- for (const record of missionRecords) {
2735
+ for (const record of projectRecords) {
2726
2736
  activity.push({
2727
- id: `mission:${record.summary.slug}`,
2728
- type: "mission",
2737
+ id: `project:${record.summary.slug}`,
2738
+ type: "project",
2729
2739
  title: record.summary.title,
2730
2740
  updated: record.summary.updated,
2731
- href: `/missions/${record.summary.slug}`,
2732
- missionSlug: record.summary.slug,
2733
- missionTitle: record.summary.title,
2741
+ href: `/projects/${record.summary.slug}`,
2742
+ projectSlug: record.summary.slug,
2743
+ projectTitle: record.summary.title,
2734
2744
  assignmentSlug: null,
2735
- summary: `Mission status is ${record.summary.status}.`
2745
+ summary: `Project status is ${record.summary.status}.`
2736
2746
  });
2737
2747
  for (const assignment of record.assignments) {
2738
2748
  activity.push({
@@ -2740,9 +2750,9 @@ function buildRecentActivity(missionRecords) {
2740
2750
  type: "assignment",
2741
2751
  title: assignment.title,
2742
2752
  updated: assignment.updated,
2743
- href: `/missions/${record.summary.slug}/assignments/${assignment.slug}`,
2744
- missionSlug: record.summary.slug,
2745
- missionTitle: record.summary.title,
2753
+ href: `/projects/${record.summary.slug}/assignments/${assignment.slug}`,
2754
+ projectSlug: record.summary.slug,
2755
+ projectTitle: record.summary.title,
2746
2756
  assignmentSlug: assignment.slug,
2747
2757
  summary: `Assignment is ${assignment.status} with ${assignment.priority} priority.`
2748
2758
  });
@@ -2777,8 +2787,8 @@ function countPendingAnswers(body) {
2777
2787
  const matches = body.match(/^\*\*A:\*\*\s+pending\s*$/gim);
2778
2788
  return matches ? matches.length : 0;
2779
2789
  }
2780
- function getMissionActivityTimestamp(missionUpdated, assignments) {
2781
- let latest = missionUpdated;
2790
+ function getProjectActivityTimestamp(projectUpdated, assignments) {
2791
+ let latest = projectUpdated;
2782
2792
  for (const assignment of assignments) {
2783
2793
  if (compareTimestamps(assignment.updated, latest) > 0) {
2784
2794
  latest = assignment.updated;
@@ -2786,28 +2796,28 @@ function getMissionActivityTimestamp(missionUpdated, assignments) {
2786
2796
  }
2787
2797
  return latest;
2788
2798
  }
2789
- function getDocumentPath(missionsDir, documentType, missionSlug, assignmentSlug) {
2799
+ function getDocumentPath(projectsDir, documentType, projectSlug, assignmentSlug) {
2790
2800
  switch (documentType) {
2791
- case "mission":
2792
- return resolve6(missionsDir, missionSlug, "mission.md");
2801
+ case "project":
2802
+ return resolve6(projectsDir, projectSlug, "project.md");
2793
2803
  case "assignment":
2794
- return assignmentSlug ? resolve6(missionsDir, missionSlug, "assignments", assignmentSlug, "assignment.md") : null;
2804
+ return assignmentSlug ? resolve6(projectsDir, projectSlug, "assignments", assignmentSlug, "assignment.md") : null;
2795
2805
  case "plan":
2796
- return assignmentSlug ? resolve6(missionsDir, missionSlug, "assignments", assignmentSlug, "plan.md") : null;
2806
+ return assignmentSlug ? resolve6(projectsDir, projectSlug, "assignments", assignmentSlug, "plan.md") : null;
2797
2807
  case "scratchpad":
2798
- return assignmentSlug ? resolve6(missionsDir, missionSlug, "assignments", assignmentSlug, "scratchpad.md") : null;
2808
+ return assignmentSlug ? resolve6(projectsDir, projectSlug, "assignments", assignmentSlug, "scratchpad.md") : null;
2799
2809
  case "handoff":
2800
- return assignmentSlug ? resolve6(missionsDir, missionSlug, "assignments", assignmentSlug, "handoff.md") : null;
2810
+ return assignmentSlug ? resolve6(projectsDir, projectSlug, "assignments", assignmentSlug, "handoff.md") : null;
2801
2811
  case "decision-record":
2802
- return assignmentSlug ? resolve6(missionsDir, missionSlug, "assignments", assignmentSlug, "decision-record.md") : null;
2812
+ return assignmentSlug ? resolve6(projectsDir, projectSlug, "assignments", assignmentSlug, "decision-record.md") : null;
2803
2813
  default:
2804
2814
  return null;
2805
2815
  }
2806
2816
  }
2807
- function getEditableDocumentTitle(documentType, missionSlug, assignmentSlug) {
2817
+ function getEditableDocumentTitle(documentType, projectSlug, assignmentSlug) {
2808
2818
  switch (documentType) {
2809
- case "mission":
2810
- return `Edit Mission: ${missionSlug}`;
2819
+ case "project":
2820
+ return `Edit Project: ${projectSlug}`;
2811
2821
  case "assignment":
2812
2822
  return `Edit Assignment: ${assignmentSlug || "assignment"}`;
2813
2823
  case "plan":
@@ -2819,9 +2829,9 @@ function getEditableDocumentTitle(documentType, missionSlug, assignmentSlug) {
2819
2829
  case "decision-record":
2820
2830
  return `Append Decision: ${assignmentSlug || "assignment"}`;
2821
2831
  case "playbook":
2822
- return `Edit Playbook: ${missionSlug}`;
2832
+ return `Edit Playbook: ${projectSlug}`;
2823
2833
  default:
2824
- return missionSlug;
2834
+ return projectSlug;
2825
2835
  }
2826
2836
  }
2827
2837
  async function listPlaybooks(playbooksDir2) {
@@ -2862,7 +2872,7 @@ async function getPlaybookDetail(playbooksDir2, slug) {
2862
2872
  body: parsed.body
2863
2873
  };
2864
2874
  }
2865
- var STALE_ASSIGNMENT_MS, ATTENTION_PAGE_LIMIT, OVERVIEW_ATTENTION_LIMIT, RECENT_MISSIONS_LIMIT, RECENT_ACTIVITY_LIMIT, DEFAULT_TRANSITION_DEFINITIONS, DEFAULT_STATUS_COLORS, _cachedConfig, DEFAULT_GRAPH_COLORS;
2875
+ var STALE_ASSIGNMENT_MS, ATTENTION_PAGE_LIMIT, OVERVIEW_ATTENTION_LIMIT, RECENT_PROJECTS_LIMIT, RECENT_ACTIVITY_LIMIT, DEFAULT_TRANSITION_DEFINITIONS, DEFAULT_STATUS_COLORS, _cachedConfig, DEFAULT_GRAPH_COLORS;
2866
2876
  var init_api = __esm({
2867
2877
  "src/dashboard/api.ts"() {
2868
2878
  "use strict";
@@ -2874,7 +2884,7 @@ var init_api = __esm({
2874
2884
  STALE_ASSIGNMENT_MS = 7 * 24 * 60 * 60 * 1e3;
2875
2885
  ATTENTION_PAGE_LIMIT = 50;
2876
2886
  OVERVIEW_ATTENTION_LIMIT = 6;
2877
- RECENT_MISSIONS_LIMIT = 6;
2887
+ RECENT_PROJECTS_LIMIT = 6;
2878
2888
  RECENT_ACTIVITY_LIMIT = 12;
2879
2889
  DEFAULT_TRANSITION_DEFINITIONS = [
2880
2890
  {
@@ -3211,11 +3221,11 @@ var init_parser2 = __esm({
3211
3221
  });
3212
3222
 
3213
3223
  // src/dashboard/server.ts
3224
+ init_paths();
3214
3225
  init_api();
3215
3226
  import express from "express";
3216
3227
  import { createServer } from "http";
3217
3228
  import { resolve as resolve15 } from "path";
3218
- import { homedir as homedir2 } from "os";
3219
3229
  import { writeFile as writeFile4, unlink as unlink4 } from "fs/promises";
3220
3230
  import { WebSocketServer, WebSocket } from "ws";
3221
3231
 
@@ -3223,34 +3233,34 @@ import { WebSocketServer, WebSocket } from "ws";
3223
3233
  import { watch } from "chokidar";
3224
3234
  import { relative, sep } from "path";
3225
3235
  function createWatcher(options) {
3226
- const { missionsDir, serversDir: serversDir2, playbooksDir: playbooksDir2, todosDir: todosDir2, onMessage, debounceMs = 300 } = options;
3236
+ const { projectsDir, serversDir: serversDir2, playbooksDir: playbooksDir2, todosDir: todosDir2, onMessage, debounceMs = 300 } = options;
3227
3237
  const pendingEvents = /* @__PURE__ */ new Map();
3228
- const missionsWatcher = watch(missionsDir, {
3238
+ const projectsWatcher = watch(projectsDir, {
3229
3239
  ignoreInitial: true,
3230
3240
  persistent: true,
3231
3241
  depth: 10,
3232
3242
  ignored: /(^|[\/\\])\../
3233
3243
  });
3234
- function handleMissionChange(filePath) {
3235
- const rel = relative(missionsDir, filePath);
3244
+ function handleProjectChange(filePath) {
3245
+ const rel = relative(projectsDir, filePath);
3236
3246
  const parts = rel.split(sep);
3237
3247
  if (parts.length === 0) return;
3238
- const missionSlug = parts[0];
3248
+ const projectSlug = parts[0];
3239
3249
  let assignmentSlug;
3240
3250
  if (parts.length >= 3 && parts[1] === "assignments") {
3241
3251
  assignmentSlug = parts[2];
3242
3252
  }
3243
- const debounceKey = assignmentSlug ? `${missionSlug}/${assignmentSlug}` : missionSlug;
3253
+ const debounceKey = assignmentSlug ? `${projectSlug}/${assignmentSlug}` : projectSlug;
3244
3254
  const existing = pendingEvents.get(debounceKey);
3245
3255
  if (existing) clearTimeout(existing);
3246
- const messageType = assignmentSlug ? "assignment-updated" : "mission-updated";
3256
+ const messageType = assignmentSlug ? "assignment-updated" : "project-updated";
3247
3257
  pendingEvents.set(
3248
3258
  debounceKey,
3249
3259
  setTimeout(() => {
3250
3260
  pendingEvents.delete(debounceKey);
3251
3261
  const message = {
3252
3262
  type: messageType,
3253
- missionSlug,
3263
+ projectSlug,
3254
3264
  assignmentSlug,
3255
3265
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
3256
3266
  };
@@ -3258,9 +3268,9 @@ function createWatcher(options) {
3258
3268
  }, debounceMs)
3259
3269
  );
3260
3270
  }
3261
- missionsWatcher.on("change", handleMissionChange);
3262
- missionsWatcher.on("add", handleMissionChange);
3263
- missionsWatcher.on("unlink", handleMissionChange);
3271
+ projectsWatcher.on("change", handleProjectChange);
3272
+ projectsWatcher.on("add", handleProjectChange);
3273
+ projectsWatcher.on("unlink", handleProjectChange);
3264
3274
  let serversWatcher = null;
3265
3275
  if (serversDir2) {
3266
3276
  let handleServerChange2 = function() {
@@ -3354,7 +3364,7 @@ function createWatcher(options) {
3354
3364
  clearTimeout(timeout);
3355
3365
  });
3356
3366
  pendingEvents.clear();
3357
- await missionsWatcher.close();
3367
+ await projectsWatcher.close();
3358
3368
  if (serversWatcher) await serversWatcher.close();
3359
3369
  if (playbooksWatcher) await playbooksWatcher.close();
3360
3370
  if (todosWatcher) await todosWatcher.close();
@@ -3442,15 +3452,15 @@ init_config();
3442
3452
  // src/templates/manifest.ts
3443
3453
  function renderManifest(params) {
3444
3454
  return `---
3445
- version: "1.0"
3446
- mission: ${params.slug}
3455
+ version: "2.0"
3456
+ project: ${params.slug}
3447
3457
  generated: "${params.timestamp}"
3448
3458
  ---
3449
3459
 
3450
- # Mission: ${params.slug}
3460
+ # Project: ${params.slug}
3451
3461
 
3452
3462
  ## Overview
3453
- - [Mission Overview](./mission.md)
3463
+ - [Project Overview](./project.md)
3454
3464
 
3455
3465
  ## Indexes
3456
3466
  - [Assignments](./_index-assignments.md)
@@ -3459,10 +3469,6 @@ generated: "${params.timestamp}"
3459
3469
  - [Status](./_status.md)
3460
3470
  - [Resources](./resources/_index.md)
3461
3471
  - [Memories](./memories/_index.md)
3462
-
3463
- ## Config
3464
- - [Agent Instructions](./agent.md)
3465
- - [Claude Code Instructions](./claude.md)
3466
3472
  `;
3467
3473
  }
3468
3474
 
@@ -3477,8 +3483,8 @@ function escapeYamlString(value) {
3477
3483
  return `"${escaped}"`;
3478
3484
  }
3479
3485
 
3480
- // src/templates/mission.ts
3481
- function renderMission(params) {
3486
+ // src/templates/project.ts
3487
+ function renderProject(params) {
3482
3488
  const safeTitle = escapeYamlString(params.title);
3483
3489
  const workspaceLine = params.workspace ? `
3484
3490
  workspace: ${params.workspace}` : "";
@@ -3499,7 +3505,7 @@ tags: []${workspaceLine}
3499
3505
 
3500
3506
  ## Overview
3501
3507
 
3502
- <!-- Describe the mission goal, context, and success criteria here. -->
3508
+ <!-- Describe the project goal, context, and success criteria here. -->
3503
3509
 
3504
3510
  ## Notes
3505
3511
 
@@ -3507,43 +3513,6 @@ tags: []${workspaceLine}
3507
3513
  `;
3508
3514
  }
3509
3515
 
3510
- // src/templates/agent.ts
3511
- function renderAgent(params) {
3512
- return `---
3513
- mission: ${params.slug}
3514
- updated: "${params.timestamp}"
3515
- ---
3516
-
3517
- # Agent Instructions
3518
-
3519
- All agents working on this mission must follow these guidelines.
3520
-
3521
- ## Conventions
3522
-
3523
- <!-- Coding conventions, naming standards, architectural patterns. -->
3524
-
3525
- ## Boundaries
3526
-
3527
- <!-- What agents should NOT do. Files/systems that are off-limits. -->
3528
-
3529
- ## Resources
3530
-
3531
- <!-- Links to key resources agents should consult. -->
3532
- `;
3533
- }
3534
-
3535
- // src/templates/claude.ts
3536
- function renderClaude(params) {
3537
- return `# Claude Code Instructions \u2014 ${params.slug}
3538
-
3539
- Read \`agent.md\` first for universal conventions and boundaries.
3540
-
3541
- ## Additional Claude Code Rules
3542
-
3543
- <!-- Add Claude Code-specific rules here. -->
3544
- `;
3545
- }
3546
-
3547
3516
  // src/templates/assignment.ts
3548
3517
  function renderAssignment(params) {
3549
3518
  const safeTitle = escapeYamlString(params.title);
@@ -3551,10 +3520,14 @@ function renderAssignment(params) {
3551
3520
  - ${params.dependsOn.join("\n - ")}`;
3552
3521
  const linksYaml = params.links.length === 0 ? "links: []" : `links:
3553
3522
  - ${params.links.join("\n - ")}`;
3523
+ const projectYaml = `project: ${params.project == null ? "null" : params.project}`;
3524
+ const typeYaml = `type: ${params.type ?? "feature"}`;
3554
3525
  return `---
3555
3526
  id: ${params.id}
3556
3527
  slug: ${params.slug}
3557
3528
  title: ${safeTitle}
3529
+ ${projectYaml}
3530
+ ${typeYaml}
3558
3531
  status: pending
3559
3532
  priority: ${params.priority}
3560
3533
  created: "${params.timestamp}"
@@ -3584,56 +3557,30 @@ tags: []
3584
3557
  - [ ] <!-- criterion 2 -->
3585
3558
  - [ ] <!-- criterion 3 -->
3586
3559
 
3587
- ## Context
3588
-
3589
- <!-- Links to relevant docs, code, or other assignments. -->
3590
-
3591
- ## Questions & Answers
3560
+ ## Todos
3592
3561
 
3593
- No questions yet.
3562
+ <!--
3563
+ Checklist of work items for this assignment. Items may be simple tasks
3564
+ or a markdown link to a plan file (e.g., "- [ ] Execute [plan](./plan.md)").
3565
+ When a plan is superseded by a new one, mark the old todo as:
3566
+ - [x] ~~Execute [old plan](./plan.md)~~ (superseded by plan-v2)
3567
+ Never delete superseded todos \u2014 preserve the history.
3568
+ -->
3594
3569
 
3595
- ## Progress
3570
+ ## Context
3596
3571
 
3597
- No progress yet.
3572
+ <!-- Links to relevant docs, code, or other assignments. -->
3598
3573
 
3599
3574
  ## Links
3600
3575
 
3601
- - [Plan](./plan.md)
3576
+ - [Progress](./progress.md)
3577
+ - [Comments](./comments.md)
3602
3578
  - [Scratchpad](./scratchpad.md)
3603
3579
  - [Handoff](./handoff.md)
3604
3580
  - [Decision Record](./decision-record.md)
3605
3581
  `;
3606
3582
  }
3607
3583
 
3608
- // src/templates/plan.ts
3609
- function renderPlan(params) {
3610
- return `---
3611
- assignment: ${params.assignmentSlug}
3612
- status: draft
3613
- created: "${params.timestamp}"
3614
- updated: "${params.timestamp}"
3615
- ---
3616
-
3617
- # Plan: ${params.title}
3618
-
3619
- ## Approach
3620
-
3621
- <!-- High-level description of how to accomplish the objective. -->
3622
-
3623
- ## Tasks
3624
-
3625
- - [ ] <!-- step 1 -->
3626
- - [ ] <!-- step 2 -->
3627
- - [ ] <!-- step 3 -->
3628
-
3629
- ## Risks & Mitigations
3630
-
3631
- | Risk | Mitigation |
3632
- |------|------------|
3633
- | <!-- risk --> | <!-- mitigation --> |
3634
- `;
3635
- }
3636
-
3637
3584
  // src/templates/scratchpad.ts
3638
3585
  function renderScratchpad(params) {
3639
3586
  return `---
@@ -3678,7 +3625,7 @@ No decisions recorded yet.
3678
3625
  // src/templates/index-stubs.ts
3679
3626
  function renderIndexAssignments(params) {
3680
3627
  return `---
3681
- mission: ${params.slug}
3628
+ project: ${params.slug}
3682
3629
  generated: "${params.timestamp}"
3683
3630
  total: 0
3684
3631
  by_status:
@@ -3698,7 +3645,7 @@ by_status:
3698
3645
  }
3699
3646
  function renderIndexPlans(params) {
3700
3647
  return `---
3701
- mission: ${params.slug}
3648
+ project: ${params.slug}
3702
3649
  generated: "${params.timestamp}"
3703
3650
  ---
3704
3651
 
@@ -3710,7 +3657,7 @@ generated: "${params.timestamp}"
3710
3657
  }
3711
3658
  function renderIndexDecisions(params) {
3712
3659
  return `---
3713
- mission: ${params.slug}
3660
+ project: ${params.slug}
3714
3661
  generated: "${params.timestamp}"
3715
3662
  ---
3716
3663
 
@@ -3722,7 +3669,7 @@ generated: "${params.timestamp}"
3722
3669
  }
3723
3670
  function renderStatus(params) {
3724
3671
  return `---
3725
- mission: ${params.slug}
3672
+ project: ${params.slug}
3726
3673
  generated: "${params.timestamp}"
3727
3674
  status: pending
3728
3675
  progress:
@@ -3736,10 +3683,10 @@ progress:
3736
3683
  needsAttention:
3737
3684
  blockedCount: 0
3738
3685
  failedCount: 0
3739
- unansweredQuestions: 0
3686
+ openQuestions: 0
3740
3687
  ---
3741
3688
 
3742
- # Mission Status: ${params.title}
3689
+ # Project Status: ${params.title}
3743
3690
 
3744
3691
  **Status:** pending
3745
3692
  **Progress:** 0/0 assignments complete
@@ -3761,7 +3708,7 @@ No dependencies yet.
3761
3708
  }
3762
3709
  function renderResourcesIndex(params) {
3763
3710
  return `---
3764
- mission: ${params.slug}
3711
+ project: ${params.slug}
3765
3712
  generated: "${params.timestamp}"
3766
3713
  total: 0
3767
3714
  ---
@@ -3774,7 +3721,7 @@ total: 0
3774
3721
  }
3775
3722
  function renderMemoriesIndex(params) {
3776
3723
  return `---
3777
- mission: ${params.slug}
3724
+ project: ${params.slug}
3778
3725
  generated: "${params.timestamp}"
3779
3726
  total: 0
3780
3727
  ---
@@ -3907,13 +3854,13 @@ async function readCurrentDocument(filePath) {
3907
3854
  }
3908
3855
  return readFile6(filePath, "utf-8");
3909
3856
  }
3910
- function createWriteRouter(missionsDir) {
3857
+ function createWriteRouter(projectsDir) {
3911
3858
  const router = Router();
3912
- router.get("/api/templates/mission", (_req, res) => {
3913
- const content = renderMission({
3859
+ router.get("/api/templates/project", (_req, res) => {
3860
+ const content = renderProject({
3914
3861
  id: generateId(),
3915
- slug: "my-new-mission",
3916
- title: "My New Mission",
3862
+ slug: "my-new-project",
3863
+ title: "My New Project",
3917
3864
  timestamp: nowTimestamp()
3918
3865
  });
3919
3866
  res.json({ content });
@@ -3930,20 +3877,20 @@ function createWriteRouter(missionsDir) {
3930
3877
  });
3931
3878
  res.json({ content });
3932
3879
  });
3933
- router.get("/api/missions/:slug/edit", async (req, res) => {
3880
+ router.get("/api/projects/:slug/edit", async (req, res) => {
3934
3881
  const slug = getParam(req.params.slug);
3935
- const document = await getEditableDocument(missionsDir, "mission", slug);
3882
+ const document = await getEditableDocument(projectsDir, "project", slug);
3936
3883
  if (!document) {
3937
- res.status(404).json({ error: `Mission "${slug}" not found` });
3884
+ res.status(404).json({ error: `Project "${slug}" not found` });
3938
3885
  return;
3939
3886
  }
3940
3887
  res.json(document);
3941
3888
  });
3942
- router.get("/api/missions/:slug/assignments/:aslug/edit", async (req, res) => {
3889
+ router.get("/api/projects/:slug/assignments/:aslug/edit", async (req, res) => {
3943
3890
  const slug = getParam(req.params.slug);
3944
3891
  const assignmentSlug = getParam(req.params.aslug);
3945
3892
  const document = await getEditableDocument(
3946
- missionsDir,
3893
+ projectsDir,
3947
3894
  "assignment",
3948
3895
  slug,
3949
3896
  assignmentSlug
@@ -3954,11 +3901,11 @@ function createWriteRouter(missionsDir) {
3954
3901
  }
3955
3902
  res.json(document);
3956
3903
  });
3957
- router.get("/api/missions/:slug/assignments/:aslug/plan/edit", async (req, res) => {
3904
+ router.get("/api/projects/:slug/assignments/:aslug/plan/edit", async (req, res) => {
3958
3905
  const slug = getParam(req.params.slug);
3959
3906
  const assignmentSlug = getParam(req.params.aslug);
3960
3907
  const document = await getEditableDocument(
3961
- missionsDir,
3908
+ projectsDir,
3962
3909
  "plan",
3963
3910
  slug,
3964
3911
  assignmentSlug
@@ -3969,11 +3916,11 @@ function createWriteRouter(missionsDir) {
3969
3916
  }
3970
3917
  res.json(document);
3971
3918
  });
3972
- router.get("/api/missions/:slug/assignments/:aslug/scratchpad/edit", async (req, res) => {
3919
+ router.get("/api/projects/:slug/assignments/:aslug/scratchpad/edit", async (req, res) => {
3973
3920
  const slug = getParam(req.params.slug);
3974
3921
  const assignmentSlug = getParam(req.params.aslug);
3975
3922
  const document = await getEditableDocument(
3976
- missionsDir,
3923
+ projectsDir,
3977
3924
  "scratchpad",
3978
3925
  slug,
3979
3926
  assignmentSlug
@@ -3984,11 +3931,11 @@ function createWriteRouter(missionsDir) {
3984
3931
  }
3985
3932
  res.json(document);
3986
3933
  });
3987
- router.get("/api/missions/:slug/assignments/:aslug/handoff/edit", async (req, res) => {
3934
+ router.get("/api/projects/:slug/assignments/:aslug/handoff/edit", async (req, res) => {
3988
3935
  const slug = getParam(req.params.slug);
3989
3936
  const assignmentSlug = getParam(req.params.aslug);
3990
3937
  const document = await getEditableDocument(
3991
- missionsDir,
3938
+ projectsDir,
3992
3939
  "handoff",
3993
3940
  slug,
3994
3941
  assignmentSlug
@@ -3999,11 +3946,11 @@ function createWriteRouter(missionsDir) {
3999
3946
  }
4000
3947
  res.json(document);
4001
3948
  });
4002
- router.get("/api/missions/:slug/assignments/:aslug/decision-record/edit", async (req, res) => {
3949
+ router.get("/api/projects/:slug/assignments/:aslug/decision-record/edit", async (req, res) => {
4003
3950
  const slug = getParam(req.params.slug);
4004
3951
  const assignmentSlug = getParam(req.params.aslug);
4005
3952
  const document = await getEditableDocument(
4006
- missionsDir,
3953
+ projectsDir,
4007
3954
  "decision-record",
4008
3955
  slug,
4009
3956
  assignmentSlug
@@ -4014,7 +3961,7 @@ function createWriteRouter(missionsDir) {
4014
3961
  }
4015
3962
  res.json(document);
4016
3963
  });
4017
- router.post("/api/missions", async (req, res) => {
3964
+ router.post("/api/projects", async (req, res) => {
4018
3965
  try {
4019
3966
  const content = requireContent(req, res);
4020
3967
  if (!content) {
@@ -4035,52 +3982,50 @@ function createWriteRouter(missionsDir) {
4035
3982
  res.status(400).json({ error: `Invalid slug "${slug}". Must be lowercase and hyphen-separated.` });
4036
3983
  return;
4037
3984
  }
4038
- const missionDir = resolve7(missionsDir, slug);
4039
- if (await fileExists(missionDir)) {
4040
- res.status(409).json({ error: `Mission "${slug}" already exists` });
3985
+ const projectDir = resolve7(projectsDir, slug);
3986
+ if (await fileExists(projectDir)) {
3987
+ res.status(409).json({ error: `Project "${slug}" already exists` });
4041
3988
  return;
4042
3989
  }
4043
3990
  const title = fields.title;
4044
3991
  const timestamp = fields.created || nowTimestamp();
4045
- await ensureDir(resolve7(missionDir, "assignments"));
4046
- await ensureDir(resolve7(missionDir, "resources"));
4047
- await ensureDir(resolve7(missionDir, "memories"));
4048
- await writeFileForce(resolve7(missionDir, "mission.md"), content);
3992
+ await ensureDir(resolve7(projectDir, "assignments"));
3993
+ await ensureDir(resolve7(projectDir, "resources"));
3994
+ await ensureDir(resolve7(projectDir, "memories"));
3995
+ await writeFileForce(resolve7(projectDir, "project.md"), content);
4049
3996
  try {
4050
3997
  const companions = [
4051
- [resolve7(missionDir, "manifest.md"), renderManifest({ slug, timestamp })],
4052
- [resolve7(missionDir, "agent.md"), renderAgent({ slug, timestamp })],
4053
- [resolve7(missionDir, "claude.md"), renderClaude({ slug })],
4054
- [resolve7(missionDir, "_index-assignments.md"), renderIndexAssignments({ slug, title, timestamp })],
4055
- [resolve7(missionDir, "_index-plans.md"), renderIndexPlans({ slug, title, timestamp })],
4056
- [resolve7(missionDir, "_index-decisions.md"), renderIndexDecisions({ slug, title, timestamp })],
4057
- [resolve7(missionDir, "_status.md"), renderStatus({ slug, title, timestamp })],
4058
- [resolve7(missionDir, "resources", "_index.md"), renderResourcesIndex({ slug, title, timestamp })],
4059
- [resolve7(missionDir, "memories", "_index.md"), renderMemoriesIndex({ slug, title, timestamp })]
3998
+ [resolve7(projectDir, "manifest.md"), renderManifest({ slug, timestamp })],
3999
+ [resolve7(projectDir, "_index-assignments.md"), renderIndexAssignments({ slug, title, timestamp })],
4000
+ [resolve7(projectDir, "_index-plans.md"), renderIndexPlans({ slug, title, timestamp })],
4001
+ [resolve7(projectDir, "_index-decisions.md"), renderIndexDecisions({ slug, title, timestamp })],
4002
+ [resolve7(projectDir, "_status.md"), renderStatus({ slug, title, timestamp })],
4003
+ [resolve7(projectDir, "resources", "_index.md"), renderResourcesIndex({ slug, title, timestamp })],
4004
+ [resolve7(projectDir, "memories", "_index.md"), renderMemoriesIndex({ slug, title, timestamp })]
4060
4005
  ];
4061
4006
  for (const [filePath, fileContent] of companions) {
4062
4007
  await writeFileForce(filePath, fileContent);
4063
4008
  }
4064
4009
  } catch (companionError) {
4065
4010
  try {
4066
- await rm(missionDir, { recursive: true, force: true });
4011
+ await rm(projectDir, { recursive: true, force: true });
4067
4012
  } catch {
4068
4013
  }
4069
4014
  throw companionError;
4070
4015
  }
4071
4016
  res.status(201).json({ slug });
4072
4017
  } catch (error) {
4073
- console.error("Error creating mission:", error);
4074
- res.status(500).json({ error: `Failed to create mission: ${error.message}` });
4018
+ console.error("Error creating project:", error);
4019
+ res.status(500).json({ error: `Failed to create project: ${error.message}` });
4075
4020
  }
4076
4021
  });
4077
- router.post("/api/missions/:slug/assignments", async (req, res) => {
4022
+ router.post("/api/projects/:slug/assignments", async (req, res) => {
4078
4023
  try {
4079
- const missionSlug = getParam(req.params.slug);
4080
- const missionDir = resolve7(missionsDir, missionSlug);
4081
- const missionMdPath = resolve7(missionDir, "mission.md");
4082
- if (!await fileExists(missionMdPath)) {
4083
- res.status(404).json({ error: `Mission "${missionSlug}" not found` });
4024
+ const projectSlug = getParam(req.params.slug);
4025
+ const projectDir = resolve7(projectsDir, projectSlug);
4026
+ const projectMdPath = resolve7(projectDir, "project.md");
4027
+ if (!await fileExists(projectMdPath)) {
4028
+ res.status(404).json({ error: `Project "${projectSlug}" not found` });
4084
4029
  return;
4085
4030
  }
4086
4031
  const content = requireContent(req, res);
@@ -4108,20 +4053,18 @@ function createWriteRouter(missionsDir) {
4108
4053
  res.status(400).json({ error: `Invalid priority "${priority}". Must be low, medium, high, or critical.` });
4109
4054
  return;
4110
4055
  }
4111
- const assignmentDir = resolve7(missionDir, "assignments", assignmentSlug);
4056
+ const assignmentDir = resolve7(projectDir, "assignments", assignmentSlug);
4112
4057
  if (await fileExists(assignmentDir)) {
4113
4058
  res.status(409).json({
4114
- error: `Assignment "${assignmentSlug}" already exists in mission "${missionSlug}"`
4059
+ error: `Assignment "${assignmentSlug}" already exists in project "${projectSlug}"`
4115
4060
  });
4116
4061
  return;
4117
4062
  }
4118
- const title = fields.title;
4119
4063
  const timestamp = fields.created || nowTimestamp();
4120
4064
  await ensureDir(assignmentDir);
4121
4065
  await writeFileForce(resolve7(assignmentDir, "assignment.md"), content);
4122
4066
  try {
4123
4067
  const companions = [
4124
- [resolve7(assignmentDir, "plan.md"), renderPlan({ assignmentSlug, title, timestamp })],
4125
4068
  [resolve7(assignmentDir, "scratchpad.md"), renderScratchpad({ assignmentSlug, timestamp })],
4126
4069
  [resolve7(assignmentDir, "handoff.md"), renderHandoff({ assignmentSlug, timestamp })],
4127
4070
  [resolve7(assignmentDir, "decision-record.md"), renderDecisionRecord({ assignmentSlug, timestamp })]
@@ -4136,51 +4079,51 @@ function createWriteRouter(missionsDir) {
4136
4079
  }
4137
4080
  throw companionError;
4138
4081
  }
4139
- res.status(201).json({ slug: assignmentSlug, missionSlug });
4082
+ res.status(201).json({ slug: assignmentSlug, projectSlug });
4140
4083
  } catch (error) {
4141
4084
  console.error("Error creating assignment:", error);
4142
4085
  res.status(500).json({ error: `Failed to create assignment: ${error.message}` });
4143
4086
  }
4144
4087
  });
4145
- router.patch("/api/missions/:slug", async (req, res) => {
4088
+ router.patch("/api/projects/:slug", async (req, res) => {
4146
4089
  try {
4147
- const missionSlug = getParam(req.params.slug);
4148
- const missionPath = resolve7(missionsDir, missionSlug, "mission.md");
4149
- const currentContent = await readCurrentDocument(missionPath);
4090
+ const projectSlug = getParam(req.params.slug);
4091
+ const projectPath = resolve7(projectsDir, projectSlug, "project.md");
4092
+ const currentContent = await readCurrentDocument(projectPath);
4150
4093
  if (!currentContent) {
4151
- res.status(404).json({ error: `Mission "${missionSlug}" not found` });
4094
+ res.status(404).json({ error: `Project "${projectSlug}" not found` });
4152
4095
  return;
4153
4096
  }
4154
4097
  const nextContentRaw = requireContent(req, res);
4155
4098
  if (!nextContentRaw) {
4156
4099
  return;
4157
4100
  }
4158
- const current = parseMission(currentContent);
4159
- const next = parseMission(nextContentRaw);
4101
+ const current = parseProject(currentContent);
4102
+ const next = parseProject(nextContentRaw);
4160
4103
  if (!next.slug || !next.title) {
4161
- res.status(400).json({ error: "Mission content must include slug and title." });
4104
+ res.status(400).json({ error: "Project content must include slug and title." });
4162
4105
  return;
4163
4106
  }
4164
4107
  if (next.slug !== current.slug) {
4165
- res.status(400).json({ error: "Mission slug cannot be changed once created." });
4108
+ res.status(400).json({ error: "Project slug cannot be changed once created." });
4166
4109
  return;
4167
4110
  }
4168
4111
  const nextContent = setTopLevelField(nextContentRaw, "updated", nowTimestamp());
4169
- await writeFileForce(missionPath, nextContent);
4170
- const mission = await getMissionDetail(missionsDir, missionSlug);
4171
- res.json({ mission, content: nextContent });
4112
+ await writeFileForce(projectPath, nextContent);
4113
+ const project = await getProjectDetail(projectsDir, projectSlug);
4114
+ res.json({ project, content: nextContent });
4172
4115
  } catch (error) {
4173
- console.error("Error updating mission:", error);
4174
- res.status(500).json({ error: `Failed to update mission: ${error.message}` });
4116
+ console.error("Error updating project:", error);
4117
+ res.status(500).json({ error: `Failed to update project: ${error.message}` });
4175
4118
  }
4176
4119
  });
4177
- router.patch("/api/missions/:slug/assignments/:aslug", async (req, res) => {
4120
+ router.patch("/api/projects/:slug/assignments/:aslug", async (req, res) => {
4178
4121
  try {
4179
- const missionSlug = getParam(req.params.slug);
4122
+ const projectSlug = getParam(req.params.slug);
4180
4123
  const assignmentSlug = getParam(req.params.aslug);
4181
4124
  const assignmentPath = resolve7(
4182
- missionsDir,
4183
- missionSlug,
4125
+ projectsDir,
4126
+ projectSlug,
4184
4127
  "assignments",
4185
4128
  assignmentSlug,
4186
4129
  "assignment.md"
@@ -4210,20 +4153,20 @@ function createWriteRouter(missionsDir) {
4210
4153
  }
4211
4154
  nextContent = setTopLevelField(nextContent, "updated", nowTimestamp());
4212
4155
  await writeFileForce(assignmentPath, nextContent);
4213
- const assignment = await getAssignmentDetail(missionsDir, missionSlug, assignmentSlug);
4156
+ const assignment = await getAssignmentDetail(projectsDir, projectSlug, assignmentSlug);
4214
4157
  res.json({ assignment, content: nextContent });
4215
4158
  } catch (error) {
4216
4159
  console.error("Error updating assignment:", error);
4217
4160
  res.status(500).json({ error: `Failed to update assignment: ${error.message}` });
4218
4161
  }
4219
4162
  });
4220
- router.patch("/api/missions/:slug/assignments/:aslug/acceptance-criteria/:index", async (req, res) => {
4163
+ router.patch("/api/projects/:slug/assignments/:aslug/acceptance-criteria/:index", async (req, res) => {
4221
4164
  try {
4222
- const missionSlug = getParam(req.params.slug);
4165
+ const projectSlug = getParam(req.params.slug);
4223
4166
  const assignmentSlug = getParam(req.params.aslug);
4224
4167
  const assignmentPath = resolve7(
4225
- missionsDir,
4226
- missionSlug,
4168
+ projectsDir,
4169
+ projectSlug,
4227
4170
  "assignments",
4228
4171
  assignmentSlug,
4229
4172
  "assignment.md"
@@ -4246,20 +4189,20 @@ function createWriteRouter(missionsDir) {
4246
4189
  }
4247
4190
  const nextContent = setTopLevelField(result.content, "updated", nowTimestamp());
4248
4191
  await writeFileForce(assignmentPath, nextContent);
4249
- const assignment = await getAssignmentDetail(missionsDir, missionSlug, assignmentSlug);
4192
+ const assignment = await getAssignmentDetail(projectsDir, projectSlug, assignmentSlug);
4250
4193
  res.json({ assignment, content: nextContent });
4251
4194
  } catch (error) {
4252
4195
  console.error("Error toggling acceptance criterion:", error);
4253
4196
  res.status(500).json({ error: `Failed to toggle acceptance criterion: ${error.message}` });
4254
4197
  }
4255
4198
  });
4256
- router.patch("/api/missions/:slug/assignments/:aslug/plan", async (req, res) => {
4199
+ router.patch("/api/projects/:slug/assignments/:aslug/plan", async (req, res) => {
4257
4200
  try {
4258
- const missionSlug = getParam(req.params.slug);
4201
+ const projectSlug = getParam(req.params.slug);
4259
4202
  const assignmentSlug = getParam(req.params.aslug);
4260
4203
  const planPath = resolve7(
4261
- missionsDir,
4262
- missionSlug,
4204
+ projectsDir,
4205
+ projectSlug,
4263
4206
  "assignments",
4264
4207
  assignmentSlug,
4265
4208
  "plan.md"
@@ -4284,20 +4227,20 @@ function createWriteRouter(missionsDir) {
4284
4227
  }
4285
4228
  const nextContent = setTopLevelField(nextContentRaw, "updated", nowTimestamp());
4286
4229
  await writeFileForce(planPath, nextContent);
4287
- const assignment = await getAssignmentDetail(missionsDir, missionSlug, assignmentSlug);
4230
+ const assignment = await getAssignmentDetail(projectsDir, projectSlug, assignmentSlug);
4288
4231
  res.json({ assignment, content: nextContent });
4289
4232
  } catch (error) {
4290
4233
  console.error("Error updating plan:", error);
4291
4234
  res.status(500).json({ error: `Failed to update plan: ${error.message}` });
4292
4235
  }
4293
4236
  });
4294
- router.patch("/api/missions/:slug/assignments/:aslug/scratchpad", async (req, res) => {
4237
+ router.patch("/api/projects/:slug/assignments/:aslug/scratchpad", async (req, res) => {
4295
4238
  try {
4296
- const missionSlug = getParam(req.params.slug);
4239
+ const projectSlug = getParam(req.params.slug);
4297
4240
  const assignmentSlug = getParam(req.params.aslug);
4298
4241
  const scratchpadPath = resolve7(
4299
- missionsDir,
4300
- missionSlug,
4242
+ projectsDir,
4243
+ projectSlug,
4301
4244
  "assignments",
4302
4245
  assignmentSlug,
4303
4246
  "scratchpad.md"
@@ -4322,20 +4265,20 @@ function createWriteRouter(missionsDir) {
4322
4265
  }
4323
4266
  const nextContent = setTopLevelField(nextContentRaw, "updated", nowTimestamp());
4324
4267
  await writeFileForce(scratchpadPath, nextContent);
4325
- const assignment = await getAssignmentDetail(missionsDir, missionSlug, assignmentSlug);
4268
+ const assignment = await getAssignmentDetail(projectsDir, projectSlug, assignmentSlug);
4326
4269
  res.json({ assignment, content: nextContent });
4327
4270
  } catch (error) {
4328
4271
  console.error("Error updating scratchpad:", error);
4329
4272
  res.status(500).json({ error: `Failed to update scratchpad: ${error.message}` });
4330
4273
  }
4331
4274
  });
4332
- router.post("/api/missions/:slug/assignments/:aslug/handoff/entries", async (req, res) => {
4275
+ router.post("/api/projects/:slug/assignments/:aslug/handoff/entries", async (req, res) => {
4333
4276
  try {
4334
- const missionSlug = getParam(req.params.slug);
4277
+ const projectSlug = getParam(req.params.slug);
4335
4278
  const assignmentSlug = getParam(req.params.aslug);
4336
4279
  const handoffPath = resolve7(
4337
- missionsDir,
4338
- missionSlug,
4280
+ projectsDir,
4281
+ projectSlug,
4339
4282
  "assignments",
4340
4283
  assignmentSlug,
4341
4284
  "handoff.md"
@@ -4360,20 +4303,20 @@ function createWriteRouter(missionsDir) {
4360
4303
  "No handoffs recorded yet."
4361
4304
  );
4362
4305
  await writeFileForce(handoffPath, nextContent);
4363
- const assignment = await getAssignmentDetail(missionsDir, missionSlug, assignmentSlug);
4306
+ const assignment = await getAssignmentDetail(projectsDir, projectSlug, assignmentSlug);
4364
4307
  res.status(201).json({ assignment, content: nextContent });
4365
4308
  } catch (error) {
4366
4309
  console.error("Error appending handoff entry:", error);
4367
4310
  res.status(500).json({ error: `Failed to append handoff entry: ${error.message}` });
4368
4311
  }
4369
4312
  });
4370
- router.post("/api/missions/:slug/assignments/:aslug/decision-record/entries", async (req, res) => {
4313
+ router.post("/api/projects/:slug/assignments/:aslug/decision-record/entries", async (req, res) => {
4371
4314
  try {
4372
- const missionSlug = getParam(req.params.slug);
4315
+ const projectSlug = getParam(req.params.slug);
4373
4316
  const assignmentSlug = getParam(req.params.aslug);
4374
4317
  const decisionPath = resolve7(
4375
- missionsDir,
4376
- missionSlug,
4318
+ projectsDir,
4319
+ projectSlug,
4377
4320
  "assignments",
4378
4321
  assignmentSlug,
4379
4322
  "decision-record.md"
@@ -4398,19 +4341,19 @@ function createWriteRouter(missionsDir) {
4398
4341
  "No decisions recorded yet."
4399
4342
  );
4400
4343
  await writeFileForce(decisionPath, nextContent);
4401
- const assignment = await getAssignmentDetail(missionsDir, missionSlug, assignmentSlug);
4344
+ const assignment = await getAssignmentDetail(projectsDir, projectSlug, assignmentSlug);
4402
4345
  res.status(201).json({ assignment, content: nextContent });
4403
4346
  } catch (error) {
4404
4347
  console.error("Error appending decision entry:", error);
4405
4348
  res.status(500).json({ error: `Failed to append decision entry: ${error.message}` });
4406
4349
  }
4407
4350
  });
4408
- router.post("/api/missions/:slug/move-workspace", async (req, res) => {
4351
+ router.post("/api/projects/:slug/move-workspace", async (req, res) => {
4409
4352
  try {
4410
- const missionSlug = getParam(req.params.slug);
4411
- const missionPath = resolve7(missionsDir, missionSlug, "mission.md");
4412
- if (!await fileExists(missionPath)) {
4413
- res.status(404).json({ error: `Mission "${missionSlug}" not found` });
4353
+ const projectSlug = getParam(req.params.slug);
4354
+ const projectPath = resolve7(projectsDir, projectSlug, "project.md");
4355
+ if (!await fileExists(projectPath)) {
4356
+ res.status(404).json({ error: `Project "${projectSlug}" not found` });
4414
4357
  return;
4415
4358
  }
4416
4359
  const { workspace } = req.body || {};
@@ -4418,23 +4361,23 @@ function createWriteRouter(missionsDir) {
4418
4361
  res.status(400).json({ error: "workspace must be a non-empty string or null (for ungrouped)." });
4419
4362
  return;
4420
4363
  }
4421
- let content = await readFile6(missionPath, "utf-8");
4364
+ let content = await readFile6(projectPath, "utf-8");
4422
4365
  content = setTopLevelField(content, "workspace", workspace ?? null);
4423
4366
  content = setTopLevelField(content, "updated", nowTimestamp());
4424
- await writeFileForce(missionPath, content);
4425
- const mission = await getMissionDetail(missionsDir, missionSlug);
4426
- res.json({ mission });
4367
+ await writeFileForce(projectPath, content);
4368
+ const project = await getProjectDetail(projectsDir, projectSlug);
4369
+ res.json({ project });
4427
4370
  } catch (error) {
4428
- console.error("Error moving mission workspace:", error);
4371
+ console.error("Error moving project workspace:", error);
4429
4372
  res.status(500).json({ error: `Failed to move workspace: ${error.message}` });
4430
4373
  }
4431
4374
  });
4432
- router.post("/api/missions/:slug/status-override", async (req, res) => {
4375
+ router.post("/api/projects/:slug/status-override", async (req, res) => {
4433
4376
  try {
4434
- const missionSlug = getParam(req.params.slug);
4435
- const missionPath = resolve7(missionsDir, missionSlug, "mission.md");
4436
- if (!await fileExists(missionPath)) {
4437
- res.status(404).json({ error: `Mission "${missionSlug}" not found` });
4377
+ const projectSlug = getParam(req.params.slug);
4378
+ const projectPath = resolve7(projectsDir, projectSlug, "project.md");
4379
+ if (!await fileExists(projectPath)) {
4380
+ res.status(404).json({ error: `Project "${projectSlug}" not found` });
4438
4381
  return;
4439
4382
  }
4440
4383
  const { status } = req.body || {};
@@ -4444,24 +4387,24 @@ function createWriteRouter(missionsDir) {
4444
4387
  res.status(400).json({ error: `Invalid status. Must be one of: ${validStatuses.join(", ")}, or null to clear.` });
4445
4388
  return;
4446
4389
  }
4447
- let content = await readFile6(missionPath, "utf-8");
4390
+ let content = await readFile6(projectPath, "utf-8");
4448
4391
  content = setTopLevelField(content, "statusOverride", status ?? null);
4449
4392
  content = setTopLevelField(content, "updated", nowTimestamp());
4450
- await writeFileForce(missionPath, content);
4451
- const mission = await getMissionDetail(missionsDir, missionSlug);
4452
- res.json({ mission });
4393
+ await writeFileForce(projectPath, content);
4394
+ const project = await getProjectDetail(projectsDir, projectSlug);
4395
+ res.json({ project });
4453
4396
  } catch (error) {
4454
- console.error("Error setting mission status override:", error);
4397
+ console.error("Error setting project status override:", error);
4455
4398
  res.status(500).json({ error: `Failed to set status override: ${error.message}` });
4456
4399
  }
4457
4400
  });
4458
- router.post("/api/missions/:slug/assignments/:aslug/status-override", async (req, res) => {
4401
+ router.post("/api/projects/:slug/assignments/:aslug/status-override", async (req, res) => {
4459
4402
  try {
4460
- const missionSlug = getParam(req.params.slug);
4403
+ const projectSlug = getParam(req.params.slug);
4461
4404
  const assignmentSlug = getParam(req.params.aslug);
4462
4405
  const assignmentPath = resolve7(
4463
- missionsDir,
4464
- missionSlug,
4406
+ projectsDir,
4407
+ projectSlug,
4465
4408
  "assignments",
4466
4409
  assignmentSlug,
4467
4410
  "assignment.md"
@@ -4484,16 +4427,16 @@ function createWriteRouter(missionsDir) {
4484
4427
  content = setTopLevelField(content, "blockedReason", null);
4485
4428
  }
4486
4429
  await writeFileForce(assignmentPath, content);
4487
- const assignment = await getAssignmentDetail(missionsDir, missionSlug, assignmentSlug);
4430
+ const assignment = await getAssignmentDetail(projectsDir, projectSlug, assignmentSlug);
4488
4431
  res.json({ assignment });
4489
4432
  } catch (error) {
4490
4433
  console.error("Error overriding assignment status:", error);
4491
4434
  res.status(500).json({ error: `Failed to override status: ${error.message}` });
4492
4435
  }
4493
4436
  });
4494
- router.post("/api/missions/:slug/assignments/:aslug/transitions/:command", async (req, res) => {
4437
+ router.post("/api/projects/:slug/assignments/:aslug/transitions/:command", async (req, res) => {
4495
4438
  try {
4496
- const missionSlug = getParam(req.params.slug);
4439
+ const projectSlug = getParam(req.params.slug);
4497
4440
  const assignmentSlug = getParam(req.params.aslug);
4498
4441
  const command = req.params.command;
4499
4442
  const config = await getStatusConfig();
@@ -4502,14 +4445,14 @@ function createWriteRouter(missionsDir) {
4502
4445
  res.status(400).json({ error: `Unsupported transition command "${req.params.command}"` });
4503
4446
  return;
4504
4447
  }
4505
- const missionDir = resolve7(missionsDir, missionSlug);
4506
- const assignmentPath = resolve7(missionDir, "assignments", assignmentSlug, "assignment.md");
4448
+ const projectDir = resolve7(projectsDir, projectSlug);
4449
+ const assignmentPath = resolve7(projectDir, "assignments", assignmentSlug, "assignment.md");
4507
4450
  if (!await fileExists(assignmentPath)) {
4508
4451
  res.status(404).json({ error: "Assignment not found" });
4509
4452
  return;
4510
4453
  }
4511
4454
  const { reason } = req.body || {};
4512
- const result = await executeTransition(missionDir, assignmentSlug, command, {
4455
+ const result = await executeTransition(projectDir, assignmentSlug, command, {
4513
4456
  reason: typeof reason === "string" ? reason : void 0,
4514
4457
  transitionTable: config.custom ? config.transitionTable : void 0,
4515
4458
  terminalStatuses: config.custom ? config.terminalStatuses : void 0
@@ -4518,25 +4461,25 @@ function createWriteRouter(missionsDir) {
4518
4461
  res.status(400).json({ error: result.message });
4519
4462
  return;
4520
4463
  }
4521
- const assignment = await getAssignmentDetail(missionsDir, missionSlug, assignmentSlug);
4464
+ const assignment = await getAssignmentDetail(projectsDir, projectSlug, assignmentSlug);
4522
4465
  res.json({ assignment, transition: result });
4523
4466
  } catch (error) {
4524
4467
  console.error("Error running assignment transition:", error);
4525
4468
  res.status(500).json({ error: `Failed to transition assignment: ${error.message}` });
4526
4469
  }
4527
4470
  });
4528
- router.delete("/api/missions/:slug/assignments/:aslug", async (req, res) => {
4471
+ router.delete("/api/projects/:slug/assignments/:aslug", async (req, res) => {
4529
4472
  try {
4530
- const missionSlug = getParam(req.params.slug);
4473
+ const projectSlug = getParam(req.params.slug);
4531
4474
  const assignmentSlug = getParam(req.params.aslug);
4532
- const assignmentDir = resolve7(missionsDir, missionSlug, "assignments", assignmentSlug);
4475
+ const assignmentDir = resolve7(projectsDir, projectSlug, "assignments", assignmentSlug);
4533
4476
  const assignmentPath = resolve7(assignmentDir, "assignment.md");
4534
4477
  if (!await fileExists(assignmentPath)) {
4535
- res.status(404).json({ error: `Assignment "${assignmentSlug}" not found in mission "${missionSlug}"` });
4478
+ res.status(404).json({ error: `Assignment "${assignmentSlug}" not found in project "${projectSlug}"` });
4536
4479
  return;
4537
4480
  }
4538
4481
  await rm(assignmentDir, { recursive: true, force: true });
4539
- res.json({ deleted: assignmentSlug, missionSlug });
4482
+ res.json({ deleted: assignmentSlug, projectSlug });
4540
4483
  } catch (error) {
4541
4484
  console.error("Error deleting assignment:", error);
4542
4485
  res.status(500).json({ error: `Failed to delete assignment: ${error.message}` });
@@ -4549,11 +4492,11 @@ function createWriteRouter(missionsDir) {
4549
4492
  init_servers();
4550
4493
  init_scanner();
4551
4494
  import { Router as Router2 } from "express";
4552
- function createServersRouter(serversDir2, missionsDir) {
4495
+ function createServersRouter(serversDir2, projectsDir) {
4553
4496
  const router = Router2();
4554
4497
  router.get("/", async (_req, res) => {
4555
4498
  try {
4556
- const result = await scanAllSessions(serversDir2, missionsDir);
4499
+ const result = await scanAllSessions(serversDir2, projectsDir);
4557
4500
  res.json(result);
4558
4501
  } catch (error) {
4559
4502
  res.status(500).json({ error: error instanceof Error ? error.message : "Scan failed" });
@@ -4561,7 +4504,7 @@ function createServersRouter(serversDir2, missionsDir) {
4561
4504
  });
4562
4505
  router.get("/:name", async (req, res) => {
4563
4506
  try {
4564
- const session = await scanSingleSession(serversDir2, missionsDir, req.params.name);
4507
+ const session = await scanSingleSession(serversDir2, projectsDir, req.params.name);
4565
4508
  if (!session) {
4566
4509
  res.status(404).json({ error: "Session not found" });
4567
4510
  return;
@@ -4612,7 +4555,7 @@ function createServersRouter(serversDir2, missionsDir) {
4612
4555
  await updateLastRefreshed(serversDir2, name);
4613
4556
  }
4614
4557
  clearScanCache();
4615
- const result = await scanAllSessions(serversDir2, missionsDir, { bypassCache: true });
4558
+ const result = await scanAllSessions(serversDir2, projectsDir, { bypassCache: true });
4616
4559
  res.json(result);
4617
4560
  } catch (error) {
4618
4561
  res.status(500).json({ error: error instanceof Error ? error.message : "Refresh failed" });
@@ -4627,7 +4570,7 @@ function createServersRouter(serversDir2, missionsDir) {
4627
4570
  }
4628
4571
  await updateLastRefreshed(serversDir2, req.params.name);
4629
4572
  clearScanCache();
4630
- const session = await scanSingleSession(serversDir2, missionsDir, req.params.name);
4573
+ const session = await scanSingleSession(serversDir2, projectsDir, req.params.name);
4631
4574
  res.json(session);
4632
4575
  } catch (error) {
4633
4576
  res.status(500).json({ error: error instanceof Error ? error.message : "Refresh failed" });
@@ -4642,7 +4585,7 @@ function createServersRouter(serversDir2, missionsDir) {
4642
4585
  return;
4643
4586
  }
4644
4587
  const body = req.body;
4645
- if (body === null || body && body.mission && body.assignment) {
4588
+ if (body === null || body && body.project && body.assignment) {
4646
4589
  await setOverride(
4647
4590
  serversDir2,
4648
4591
  name,
@@ -4653,7 +4596,7 @@ function createServersRouter(serversDir2, missionsDir) {
4653
4596
  clearScanCache();
4654
4597
  res.json({ updated: true });
4655
4598
  } else {
4656
- res.status(400).json({ error: "Body must be { mission, assignment } or null" });
4599
+ res.status(400).json({ error: "Body must be { project, assignment } or null" });
4657
4600
  }
4658
4601
  } catch (error) {
4659
4602
  res.status(500).json({ error: error instanceof Error ? error.message : "Update failed" });
@@ -4683,7 +4626,7 @@ var SCHEMA_VERSION = "2";
4683
4626
  var SCHEMA_SQL = `
4684
4627
  CREATE TABLE IF NOT EXISTS sessions (
4685
4628
  session_id TEXT PRIMARY KEY,
4686
- mission_slug TEXT,
4629
+ project_slug TEXT,
4687
4630
  assignment_slug TEXT,
4688
4631
  agent TEXT NOT NULL,
4689
4632
  started TEXT NOT NULL,
@@ -4694,8 +4637,8 @@ CREATE TABLE IF NOT EXISTS sessions (
4694
4637
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
4695
4638
  updated_at TEXT NOT NULL DEFAULT (datetime('now'))
4696
4639
  );
4697
- CREATE INDEX IF NOT EXISTS idx_sessions_mission ON sessions(mission_slug);
4698
- CREATE INDEX IF NOT EXISTS idx_sessions_assignment ON sessions(mission_slug, assignment_slug);
4640
+ CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project_slug);
4641
+ CREATE INDEX IF NOT EXISTS idx_sessions_assignment ON sessions(project_slug, assignment_slug);
4699
4642
  CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(status);
4700
4643
  CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT);
4701
4644
  `;
@@ -4714,7 +4657,7 @@ function initSessionDb(dbPath) {
4714
4657
  db.exec(`
4715
4658
  CREATE TABLE sessions_v2 (
4716
4659
  session_id TEXT PRIMARY KEY,
4717
- mission_slug TEXT,
4660
+ project_slug TEXT,
4718
4661
  assignment_slug TEXT,
4719
4662
  agent TEXT NOT NULL,
4720
4663
  started TEXT NOT NULL,
@@ -4725,11 +4668,11 @@ function initSessionDb(dbPath) {
4725
4668
  created_at TEXT NOT NULL DEFAULT (datetime('now')),
4726
4669
  updated_at TEXT NOT NULL DEFAULT (datetime('now'))
4727
4670
  );
4728
- INSERT INTO sessions_v2 SELECT session_id, mission_slug, assignment_slug, agent, started, ended, status, path, NULL, created_at, updated_at FROM sessions;
4671
+ INSERT INTO sessions_v2 SELECT session_id, project_slug, assignment_slug, agent, started, ended, status, path, NULL, created_at, updated_at FROM sessions;
4729
4672
  DROP TABLE sessions;
4730
4673
  ALTER TABLE sessions_v2 RENAME TO sessions;
4731
- CREATE INDEX IF NOT EXISTS idx_sessions_mission ON sessions(mission_slug);
4732
- CREATE INDEX IF NOT EXISTS idx_sessions_assignment ON sessions(mission_slug, assignment_slug);
4674
+ CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project_slug);
4675
+ CREATE INDEX IF NOT EXISTS idx_sessions_assignment ON sessions(project_slug, assignment_slug);
4733
4676
  CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(status);
4734
4677
  UPDATE meta SET value = '2' WHERE key = 'schema_version';
4735
4678
  `);
@@ -4750,36 +4693,36 @@ function closeSessionDb() {
4750
4693
  db = null;
4751
4694
  }
4752
4695
  }
4753
- async function migrateFromMarkdown(missionsDir) {
4696
+ async function migrateFromMarkdown(projectsDir) {
4754
4697
  const database = getSessionDb();
4755
4698
  const count = database.prepare("SELECT COUNT(*) as count FROM sessions").get();
4756
4699
  if (count.count > 0) return 0;
4757
- if (!await fileExists(missionsDir)) return 0;
4758
- const entries = await readdir4(missionsDir, { withFileTypes: true });
4700
+ if (!await fileExists(projectsDir)) return 0;
4701
+ const entries = await readdir4(projectsDir, { withFileTypes: true });
4759
4702
  const allSessions = [];
4760
4703
  for (const entry of entries) {
4761
4704
  if (!entry.isDirectory()) continue;
4762
- const missionDir = resolve8(missionsDir, entry.name);
4763
- const indexPath = resolve8(missionDir, "_index-sessions.md");
4705
+ const projectDir = resolve8(projectsDir, entry.name);
4706
+ const indexPath = resolve8(projectDir, "_index-sessions.md");
4764
4707
  if (!await fileExists(indexPath)) continue;
4765
4708
  const sessions = await parseMarkdownSessionsIndex(indexPath, entry.name);
4766
4709
  allSessions.push(...sessions);
4767
4710
  }
4768
4711
  if (allSessions.length === 0) return 0;
4769
4712
  const insert = database.prepare(`
4770
- INSERT OR IGNORE INTO sessions (session_id, mission_slug, assignment_slug, agent, started, status, path)
4713
+ INSERT OR IGNORE INTO sessions (session_id, project_slug, assignment_slug, agent, started, status, path)
4771
4714
  VALUES (?, ?, ?, ?, ?, ?, ?)
4772
4715
  `);
4773
4716
  const insertAll = database.transaction((sessions) => {
4774
4717
  for (const s of sessions) {
4775
- insert.run(s.sessionId, s.missionSlug, s.assignmentSlug, s.agent, s.started, s.status, s.path);
4718
+ insert.run(s.sessionId, s.projectSlug, s.assignmentSlug, s.agent, s.started, s.status, s.path);
4776
4719
  }
4777
4720
  });
4778
4721
  insertAll(allSessions);
4779
4722
  console.log(`Migrated ${allSessions.length} sessions from markdown to SQLite.`);
4780
4723
  return allSessions.length;
4781
4724
  }
4782
- async function parseMarkdownSessionsIndex(filePath, missionSlug) {
4725
+ async function parseMarkdownSessionsIndex(filePath, projectSlug) {
4783
4726
  const { readFile: readFile12 } = await import("fs/promises");
4784
4727
  const raw = await readFile12(filePath, "utf-8");
4785
4728
  const sessions = [];
@@ -4808,7 +4751,7 @@ async function parseMarkdownSessionsIndex(filePath, missionSlug) {
4808
4751
  started: cells[3],
4809
4752
  status: cells[4] || "active",
4810
4753
  path: cells[5],
4811
- missionSlug
4754
+ projectSlug
4812
4755
  });
4813
4756
  }
4814
4757
  }
@@ -4820,7 +4763,7 @@ async function parseMarkdownSessionsIndex(filePath, missionSlug) {
4820
4763
  function rowToSession(row) {
4821
4764
  return {
4822
4765
  sessionId: row.session_id,
4823
- missionSlug: row.mission_slug ?? null,
4766
+ projectSlug: row.project_slug ?? null,
4824
4767
  assignmentSlug: row.assignment_slug ?? null,
4825
4768
  agent: row.agent,
4826
4769
  started: row.started,
@@ -4830,14 +4773,14 @@ function rowToSession(row) {
4830
4773
  description: row.description ?? null
4831
4774
  };
4832
4775
  }
4833
- async function appendSession(_missionDir, session) {
4776
+ async function appendSession(_projectDir, session) {
4834
4777
  const db2 = getSessionDb();
4835
4778
  db2.prepare(`
4836
- INSERT INTO sessions (session_id, mission_slug, assignment_slug, agent, started, status, path, description)
4779
+ INSERT INTO sessions (session_id, project_slug, assignment_slug, agent, started, status, path, description)
4837
4780
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)
4838
4781
  `).run(
4839
4782
  session.sessionId,
4840
- session.missionSlug ?? null,
4783
+ session.projectSlug ?? null,
4841
4784
  session.assignmentSlug ?? null,
4842
4785
  session.agent,
4843
4786
  session.started,
@@ -4846,7 +4789,7 @@ async function appendSession(_missionDir, session) {
4846
4789
  session.description ?? null
4847
4790
  );
4848
4791
  }
4849
- async function updateSessionStatus(_missionDir, sessionId, status) {
4792
+ async function updateSessionStatus(_projectDir, sessionId, status) {
4850
4793
  const db2 = getSessionDb();
4851
4794
  const isTerminal = status === "completed" || status === "stopped";
4852
4795
  const result = isTerminal ? db2.prepare(
@@ -4856,20 +4799,20 @@ async function updateSessionStatus(_missionDir, sessionId, status) {
4856
4799
  ).run(status, sessionId);
4857
4800
  return result.changes > 0;
4858
4801
  }
4859
- async function listAllSessions(_missionsDir) {
4802
+ async function listAllSessions(_projectsDir) {
4860
4803
  const db2 = getSessionDb();
4861
4804
  const rows = db2.prepare("SELECT * FROM sessions ORDER BY started DESC").all();
4862
4805
  return rows.map(rowToSession);
4863
4806
  }
4864
- async function listMissionSessions(_missionsDir, missionSlug, assignmentSlug) {
4807
+ async function listProjectSessions(_projectsDir, projectSlug, assignmentSlug) {
4865
4808
  const db2 = getSessionDb();
4866
4809
  if (assignmentSlug) {
4867
4810
  const rows2 = db2.prepare(
4868
- "SELECT * FROM sessions WHERE mission_slug = ? AND assignment_slug = ? ORDER BY started DESC"
4869
- ).all(missionSlug, assignmentSlug);
4811
+ "SELECT * FROM sessions WHERE project_slug = ? AND assignment_slug = ? ORDER BY started DESC"
4812
+ ).all(projectSlug, assignmentSlug);
4870
4813
  return rows2.map(rowToSession);
4871
4814
  }
4872
- const rows = db2.prepare("SELECT * FROM sessions WHERE mission_slug = ? ORDER BY started DESC").all(missionSlug);
4815
+ const rows = db2.prepare("SELECT * FROM sessions WHERE project_slug = ? ORDER BY started DESC").all(projectSlug);
4873
4816
  return rows.map(rowToSession);
4874
4817
  }
4875
4818
  async function deleteSessions(sessionIds) {
@@ -4880,34 +4823,34 @@ async function deleteSessions(sessionIds) {
4880
4823
  return result.changes;
4881
4824
  }
4882
4825
  var DONE_ASSIGNMENT_STATUSES = /* @__PURE__ */ new Set(["completed", "failed", "review"]);
4883
- async function readAssignmentStatus(missionDir, assignmentSlug) {
4884
- const assignmentPath = resolve9(missionDir, "assignments", assignmentSlug, "assignment.md");
4826
+ async function readAssignmentStatus(projectDir, assignmentSlug) {
4827
+ const assignmentPath = resolve9(projectDir, "assignments", assignmentSlug, "assignment.md");
4885
4828
  if (!await fileExists(assignmentPath)) return null;
4886
4829
  const raw = await readFile7(assignmentPath, "utf-8");
4887
4830
  const match = raw.match(/^status:\s*(.+)$/m);
4888
4831
  return match ? match[1].trim() : null;
4889
4832
  }
4890
- async function reconcileActiveSessions(missionsDir) {
4833
+ async function reconcileActiveSessions(projectsDir) {
4891
4834
  const db2 = getSessionDb();
4892
- const activeSessions = db2.prepare("SELECT * FROM sessions WHERE status = 'active' AND mission_slug IS NOT NULL AND assignment_slug IS NOT NULL").all();
4835
+ const activeSessions = db2.prepare("SELECT * FROM sessions WHERE status = 'active' AND project_slug IS NOT NULL AND assignment_slug IS NOT NULL").all();
4893
4836
  if (activeSessions.length === 0) return 0;
4894
4837
  const toCheck = /* @__PURE__ */ new Map();
4895
4838
  for (const session of activeSessions) {
4896
- const slugs = toCheck.get(session.mission_slug) ?? /* @__PURE__ */ new Set();
4839
+ const slugs = toCheck.get(session.project_slug) ?? /* @__PURE__ */ new Set();
4897
4840
  slugs.add(session.assignment_slug);
4898
- toCheck.set(session.mission_slug, slugs);
4841
+ toCheck.set(session.project_slug, slugs);
4899
4842
  }
4900
4843
  const assignmentStatuses = /* @__PURE__ */ new Map();
4901
- for (const [missionSlug, slugs] of toCheck) {
4902
- const missionDir = resolve9(missionsDir, missionSlug);
4844
+ for (const [projectSlug, slugs] of toCheck) {
4845
+ const projectDir = resolve9(projectsDir, projectSlug);
4903
4846
  for (const slug of slugs) {
4904
- const status = await readAssignmentStatus(missionDir, slug);
4905
- if (status) assignmentStatuses.set(`${missionSlug}/${slug}`, status);
4847
+ const status = await readAssignmentStatus(projectDir, slug);
4848
+ if (status) assignmentStatuses.set(`${projectSlug}/${slug}`, status);
4906
4849
  }
4907
4850
  }
4908
4851
  let totalUpdated = 0;
4909
4852
  for (const session of activeSessions) {
4910
- const key = `${session.mission_slug}/${session.assignment_slug}`;
4853
+ const key = `${session.project_slug}/${session.assignment_slug}`;
4911
4854
  const assignmentStatus = assignmentStatuses.get(key);
4912
4855
  if (!assignmentStatus || !DONE_ASSIGNMENT_STATUSES.has(assignmentStatus)) continue;
4913
4856
  const newStatus = assignmentStatus === "failed" ? "stopped" : "completed";
@@ -4919,28 +4862,28 @@ async function reconcileActiveSessions(missionsDir) {
4919
4862
 
4920
4863
  // src/dashboard/api-agent-sessions.ts
4921
4864
  init_fs();
4922
- function createAgentSessionsRouter(missionsDir, broadcast) {
4865
+ function createAgentSessionsRouter(projectsDir, broadcast) {
4923
4866
  const router = Router3();
4924
4867
  router.get("/", async (_req, res) => {
4925
4868
  try {
4926
- await reconcileActiveSessions(missionsDir);
4927
- const sessions = await listAllSessions(missionsDir);
4869
+ await reconcileActiveSessions(projectsDir);
4870
+ const sessions = await listAllSessions(projectsDir);
4928
4871
  res.json({ sessions, generatedAt: (/* @__PURE__ */ new Date()).toISOString() });
4929
4872
  } catch (error) {
4930
4873
  res.status(500).json({ error: error instanceof Error ? error.message : "Failed to list sessions" });
4931
4874
  }
4932
4875
  });
4933
- router.get("/:missionSlug", async (req, res) => {
4876
+ router.get("/:projectSlug", async (req, res) => {
4934
4877
  try {
4935
- const { missionSlug } = req.params;
4878
+ const { projectSlug } = req.params;
4936
4879
  const assignment = req.query.assignment;
4937
- const missionDir = resolve10(missionsDir, missionSlug);
4938
- if (!await fileExists(missionDir)) {
4939
- res.status(404).json({ error: `Mission "${missionSlug}" not found` });
4880
+ const projectDir = resolve10(projectsDir, projectSlug);
4881
+ if (!await fileExists(projectDir)) {
4882
+ res.status(404).json({ error: `Project "${projectSlug}" not found` });
4940
4883
  return;
4941
4884
  }
4942
- await reconcileActiveSessions(missionsDir);
4943
- const sessions = await listMissionSessions(missionsDir, missionSlug, assignment);
4885
+ await reconcileActiveSessions(projectsDir);
4886
+ const sessions = await listProjectSessions(projectsDir, projectSlug, assignment);
4944
4887
  res.json({ sessions, generatedAt: (/* @__PURE__ */ new Date()).toISOString() });
4945
4888
  } catch (error) {
4946
4889
  res.status(500).json({ error: error instanceof Error ? error.message : "Failed to list sessions" });
@@ -4948,21 +4891,21 @@ function createAgentSessionsRouter(missionsDir, broadcast) {
4948
4891
  });
4949
4892
  router.post("/", async (req, res) => {
4950
4893
  try {
4951
- const { missionSlug, assignmentSlug, agent, sessionId, path, description } = req.body;
4894
+ const { projectSlug, assignmentSlug, agent, sessionId, path, description } = req.body;
4952
4895
  if (!agent) {
4953
4896
  res.status(400).json({ error: "agent is required" });
4954
4897
  return;
4955
4898
  }
4956
- if (missionSlug) {
4957
- const missionDir = resolve10(missionsDir, missionSlug);
4958
- if (!await fileExists(missionDir)) {
4959
- res.status(404).json({ error: `Mission "${missionSlug}" not found` });
4899
+ if (projectSlug) {
4900
+ const projectDir = resolve10(projectsDir, projectSlug);
4901
+ if (!await fileExists(projectDir)) {
4902
+ res.status(404).json({ error: `Project "${projectSlug}" not found` });
4960
4903
  return;
4961
4904
  }
4962
4905
  }
4963
4906
  const id = sessionId || randomUUID2();
4964
4907
  const session = {
4965
- missionSlug: missionSlug || null,
4908
+ projectSlug: projectSlug || null,
4966
4909
  assignmentSlug: assignmentSlug || null,
4967
4910
  agent,
4968
4911
  sessionId: id,
@@ -5619,7 +5562,7 @@ import { cp, mkdtemp, rm as rm2, readFile as readFile11, writeFile as writeFile3
5619
5562
  import { resolve as resolve14, join as join2 } from "path";
5620
5563
  import { tmpdir } from "os";
5621
5564
  var exec2 = promisify2(execFile2);
5622
- var VALID_CATEGORIES = ["missions", "playbooks", "todos", "servers", "config"];
5565
+ var VALID_CATEGORIES = ["projects", "playbooks", "todos", "servers", "config"];
5623
5566
  var LOCK_FILE_NAME = ".backup-lock";
5624
5567
  function parseCategoriesStrict(cats) {
5625
5568
  const unknown = [];
@@ -5645,9 +5588,9 @@ function validateRepoUrl(url) {
5645
5588
  }
5646
5589
  async function resolveCategoryPath(category) {
5647
5590
  switch (category) {
5648
- case "missions": {
5591
+ case "projects": {
5649
5592
  const config = await readConfig();
5650
- return { sourcePath: config.defaultMissionDir, repoPath: "missions", isFile: false };
5593
+ return { sourcePath: config.defaultProjectDir, repoPath: "projects", isFile: false };
5651
5594
  }
5652
5595
  case "playbooks":
5653
5596
  return { sourcePath: playbooksDir(), repoPath: "playbooks", isFile: false };
@@ -5737,7 +5680,7 @@ async function backupToGithub(overrides) {
5737
5680
  if (!validateRepoUrl(repo)) {
5738
5681
  throw new Error(`Invalid repo URL: "${rawRepo}". Must start with https:// or git@.`);
5739
5682
  }
5740
- const categoriesCsv = config.backup?.categories ?? "missions, playbooks, todos, servers, config";
5683
+ const categoriesCsv = config.backup?.categories ?? "projects, playbooks, todos, servers, config";
5741
5684
  const categories = overrides?.categories ?? resolveCategoriesStrict(categoriesCsv);
5742
5685
  if (categories.length === 0) {
5743
5686
  throw new Error("No valid backup categories selected.");
@@ -5866,7 +5809,7 @@ async function restoreFromGithub(overrides) {
5866
5809
  if (!validateRepoUrl(repo)) {
5867
5810
  throw new Error(`Invalid repo URL: "${rawRepo}".`);
5868
5811
  }
5869
- const categoriesCsv = config.backup?.categories ?? "missions, playbooks, todos, servers, config";
5812
+ const categoriesCsv = config.backup?.categories ?? "projects, playbooks, todos, servers, config";
5870
5813
  const categories = overrides?.categories ?? resolveCategoriesStrict(categoriesCsv);
5871
5814
  if (categories.length === 0) {
5872
5815
  throw new Error("No valid restore categories selected.");
@@ -5921,7 +5864,7 @@ async function getBackupStatus() {
5921
5864
  const locked = await fileExists(lockPath);
5922
5865
  return {
5923
5866
  repo: config.backup?.repo ?? null,
5924
- categories: config.backup?.categories ?? "missions, playbooks, todos, servers, config",
5867
+ categories: config.backup?.categories ?? "projects, playbooks, todos, servers, config",
5925
5868
  lastBackup: config.backup?.lastBackup ?? null,
5926
5869
  lastRestore: config.backup?.lastRestore ?? null,
5927
5870
  locked
@@ -6072,7 +6015,7 @@ async function stopAutodiscovery() {
6072
6015
  function runReconcile() {
6073
6016
  if (activeReconcile || !savedOptions) return;
6074
6017
  const opts = savedOptions;
6075
- activeReconcile = reconcile(opts.serversDir, opts.missionsDir, opts.excludePids).catch((err) => {
6018
+ activeReconcile = reconcile(opts.serversDir, opts.projectsDir, opts.excludePids).catch((err) => {
6076
6019
  console.error("[autodiscovery] reconcile failed:", err);
6077
6020
  }).finally(() => {
6078
6021
  activeReconcile = null;
@@ -6083,10 +6026,10 @@ async function listAllTmuxSessions() {
6083
6026
  if (!output) return [];
6084
6027
  return output.split("\n").filter((line) => line.length > 0);
6085
6028
  }
6086
- async function discoverTmuxSessions(serversDir2, missionsDir, existingNames) {
6029
+ async function discoverTmuxSessions(serversDir2, projectsDir, existingNames) {
6087
6030
  const tmuxAvailable = await checkTmuxAvailable();
6088
6031
  if (!tmuxAvailable) return false;
6089
- const workspaceRecords = await loadWorkspaceRecords(missionsDir);
6032
+ const workspaceRecords = await loadWorkspaceRecords(projectsDir);
6090
6033
  if (workspaceRecords.length === 0) return false;
6091
6034
  const sessions = await listAllTmuxSessions();
6092
6035
  let changed = false;
@@ -6127,8 +6070,8 @@ async function getProcessCwd(pid) {
6127
6070
  }
6128
6071
  return null;
6129
6072
  }
6130
- async function discoverProcesses(serversDir2, missionsDir, existingFiles, excludePids) {
6131
- const workspaceRecords = await loadWorkspaceRecords(missionsDir);
6073
+ async function discoverProcesses(serversDir2, projectsDir, existingFiles, excludePids) {
6074
+ const workspaceRecords = await loadWorkspaceRecords(projectsDir);
6132
6075
  if (workspaceRecords.length === 0) return false;
6133
6076
  const lsofOutput = await getLsofOutput();
6134
6077
  if (!lsofOutput) return false;
@@ -6193,7 +6136,7 @@ async function isProcessAlive(pid) {
6193
6136
  return false;
6194
6137
  }
6195
6138
  }
6196
- async function reconcile(serversDir2, missionsDir, excludePids) {
6139
+ async function reconcile(serversDir2, projectsDir, excludePids) {
6197
6140
  const names = await listSessionFiles(serversDir2);
6198
6141
  const existingFiles = /* @__PURE__ */ new Map();
6199
6142
  for (const name of names) {
@@ -6205,8 +6148,8 @@ async function reconcile(serversDir2, missionsDir, excludePids) {
6205
6148
  existingFiles.delete(name);
6206
6149
  }
6207
6150
  const existingNames = new Set(existingFiles.keys());
6208
- const tmuxChanged = await discoverTmuxSessions(serversDir2, missionsDir, existingNames);
6209
- const processChanged = await discoverProcesses(serversDir2, missionsDir, existingFiles, excludePids);
6151
+ const tmuxChanged = await discoverTmuxSessions(serversDir2, projectsDir, existingNames);
6152
+ const processChanged = await discoverProcesses(serversDir2, projectsDir, existingFiles, excludePids);
6210
6153
  if (tmuxChanged || processChanged || cleanupChanged) {
6211
6154
  clearScanCache();
6212
6155
  }
@@ -6214,7 +6157,7 @@ async function reconcile(serversDir2, missionsDir, excludePids) {
6214
6157
 
6215
6158
  // src/dashboard/server.ts
6216
6159
  function createDashboardServer(options) {
6217
- const { port, missionsDir, serversDir: serversDir2, playbooksDir: playbooksDir2, todosDir: todosDir2, serveStaticUi, dashboardDistPath } = options;
6160
+ const { port, projectsDir, serversDir: serversDir2, playbooksDir: playbooksDir2, todosDir: todosDir2, serveStaticUi, dashboardDistPath } = options;
6218
6161
  const app = express();
6219
6162
  const server = createServer(app);
6220
6163
  const wss = new WebSocketServer({ noServer: true });
@@ -6248,13 +6191,13 @@ function createDashboardServer(options) {
6248
6191
  }
6249
6192
  }
6250
6193
  initSessionDb();
6251
- migrateFromMarkdown(missionsDir).catch((err) => {
6194
+ migrateFromMarkdown(projectsDir).catch((err) => {
6252
6195
  console.error("Session migration from markdown failed:", err);
6253
6196
  });
6254
6197
  app.use(express.json());
6255
6198
  app.get("/api/overview", async (_req, res) => {
6256
6199
  try {
6257
- const overview = await getOverview(missionsDir, serversDir2);
6200
+ const overview = await getOverview(projectsDir, serversDir2);
6258
6201
  res.json(overview);
6259
6202
  } catch (error) {
6260
6203
  console.error("Error getting overview:", error);
@@ -6263,7 +6206,7 @@ function createDashboardServer(options) {
6263
6206
  });
6264
6207
  app.get("/api/attention", async (_req, res) => {
6265
6208
  try {
6266
- const attention = await getAttention(missionsDir, serversDir2);
6209
+ const attention = await getAttention(projectsDir, serversDir2);
6267
6210
  res.json(attention);
6268
6211
  } catch (error) {
6269
6212
  console.error("Error getting attention queue:", error);
@@ -6330,26 +6273,26 @@ function createDashboardServer(options) {
6330
6273
  res.status(500).json({ error: "Failed to reset status config" });
6331
6274
  }
6332
6275
  });
6333
- app.get("/api/missions", async (req, res) => {
6276
+ app.get("/api/projects", async (req, res) => {
6334
6277
  try {
6335
- let missions = await listMissions(missionsDir);
6278
+ let projects = await listProjects(projectsDir);
6336
6279
  const workspaceParam = req.query.workspace;
6337
6280
  if (workspaceParam) {
6338
6281
  if (workspaceParam === "_ungrouped") {
6339
- missions = missions.filter((m) => m.workspace === null);
6282
+ projects = projects.filter((m) => m.workspace === null);
6340
6283
  } else {
6341
- missions = missions.filter((m) => m.workspace === workspaceParam);
6284
+ projects = projects.filter((m) => m.workspace === workspaceParam);
6342
6285
  }
6343
6286
  }
6344
- res.json(missions);
6287
+ res.json(projects);
6345
6288
  } catch (error) {
6346
- console.error("Error listing missions:", error);
6347
- res.status(500).json({ error: "Failed to list missions" });
6289
+ console.error("Error listing projects:", error);
6290
+ res.status(500).json({ error: "Failed to list projects" });
6348
6291
  }
6349
6292
  });
6350
6293
  app.get("/api/workspaces", async (_req, res) => {
6351
6294
  try {
6352
- const result = await listWorkspaces(missionsDir);
6295
+ const result = await listWorkspaces(projectsDir);
6353
6296
  res.json(result);
6354
6297
  } catch (error) {
6355
6298
  console.error("Error listing workspaces:", error);
@@ -6363,8 +6306,8 @@ function createDashboardServer(options) {
6363
6306
  res.status(400).json({ error: "Invalid workspace name. Use lowercase letters, numbers, and hyphens." });
6364
6307
  return;
6365
6308
  }
6366
- await createWorkspace(missionsDir, name);
6367
- broadcast({ type: "mission-updated", missionSlug: "", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
6309
+ await createWorkspace(projectsDir, name);
6310
+ broadcast({ type: "project-updated", projectSlug: "", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
6368
6311
  res.json({ name });
6369
6312
  } catch (error) {
6370
6313
  console.error("Error creating workspace:", error);
@@ -6373,8 +6316,8 @@ function createDashboardServer(options) {
6373
6316
  });
6374
6317
  app.delete("/api/workspaces/:name", async (req, res) => {
6375
6318
  try {
6376
- await deleteWorkspace(missionsDir, req.params.name);
6377
- broadcast({ type: "mission-updated", missionSlug: "", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
6319
+ await deleteWorkspace(projectsDir, req.params.name);
6320
+ broadcast({ type: "project-updated", projectSlug: "", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
6378
6321
  res.json({ ok: true });
6379
6322
  } catch (error) {
6380
6323
  console.error("Error deleting workspace:", error);
@@ -6383,13 +6326,13 @@ function createDashboardServer(options) {
6383
6326
  });
6384
6327
  app.get("/api/assignments", async (req, res) => {
6385
6328
  try {
6386
- const result = await listAssignmentsBoard(missionsDir);
6329
+ const result = await listAssignmentsBoard(projectsDir);
6387
6330
  const workspaceParam = req.query.workspace;
6388
6331
  if (workspaceParam) {
6389
6332
  if (workspaceParam === "_ungrouped") {
6390
- result.assignments = result.assignments.filter((a) => a.missionWorkspace === null);
6333
+ result.assignments = result.assignments.filter((a) => a.projectWorkspace === null);
6391
6334
  } else {
6392
- result.assignments = result.assignments.filter((a) => a.missionWorkspace === workspaceParam);
6335
+ result.assignments = result.assignments.filter((a) => a.projectWorkspace === workspaceParam);
6393
6336
  }
6394
6337
  }
6395
6338
  res.json(result);
@@ -6398,29 +6341,29 @@ function createDashboardServer(options) {
6398
6341
  res.status(500).json({ error: "Failed to list assignments" });
6399
6342
  }
6400
6343
  });
6401
- app.get("/api/missions/:slug", async (req, res) => {
6344
+ app.get("/api/projects/:slug", async (req, res) => {
6402
6345
  try {
6403
- const detail = await getMissionDetail(missionsDir, req.params.slug);
6346
+ const detail = await getProjectDetail(projectsDir, req.params.slug);
6404
6347
  if (!detail) {
6405
- res.status(404).json({ error: `Mission "${req.params.slug}" not found` });
6348
+ res.status(404).json({ error: `Project "${req.params.slug}" not found` });
6406
6349
  return;
6407
6350
  }
6408
6351
  res.json(detail);
6409
6352
  } catch (error) {
6410
- console.error("Error getting mission detail:", error);
6411
- res.status(500).json({ error: "Failed to get mission detail" });
6353
+ console.error("Error getting project detail:", error);
6354
+ res.status(500).json({ error: "Failed to get project detail" });
6412
6355
  }
6413
6356
  });
6414
- app.get("/api/missions/:slug/assignments/:aslug", async (req, res) => {
6357
+ app.get("/api/projects/:slug/assignments/:aslug", async (req, res) => {
6415
6358
  try {
6416
6359
  const detail = await getAssignmentDetail(
6417
- missionsDir,
6360
+ projectsDir,
6418
6361
  req.params.slug,
6419
6362
  req.params.aslug
6420
6363
  );
6421
6364
  if (!detail) {
6422
6365
  res.status(404).json({
6423
- error: `Assignment "${req.params.aslug}" not found in mission "${req.params.slug}"`
6366
+ error: `Assignment "${req.params.aslug}" not found in project "${req.params.slug}"`
6424
6367
  });
6425
6368
  return;
6426
6369
  }
@@ -6430,9 +6373,9 @@ function createDashboardServer(options) {
6430
6373
  res.status(500).json({ error: "Failed to get assignment detail" });
6431
6374
  }
6432
6375
  });
6433
- app.use(createWriteRouter(missionsDir));
6434
- app.use("/api/servers", createServersRouter(serversDir2, missionsDir));
6435
- app.use("/api/agent-sessions", createAgentSessionsRouter(missionsDir, broadcast));
6376
+ app.use(createWriteRouter(projectsDir));
6377
+ app.use("/api/servers", createServersRouter(serversDir2, projectsDir));
6378
+ app.use("/api/agent-sessions", createAgentSessionsRouter(projectsDir, broadcast));
6436
6379
  app.use("/api/playbooks", createPlaybooksRouter(playbooksDir2));
6437
6380
  app.use("/api/todos", createTodosRouter(todosDir2, broadcast));
6438
6381
  app.use("/api/backup", createBackupRouter());
@@ -6453,13 +6396,13 @@ function createDashboardServer(options) {
6453
6396
  return {
6454
6397
  async start() {
6455
6398
  watcherHandle = createWatcher({
6456
- missionsDir,
6399
+ projectsDir,
6457
6400
  serversDir: serversDir2,
6458
6401
  playbooksDir: playbooksDir2,
6459
6402
  todosDir: todosDir2,
6460
6403
  onMessage: broadcast
6461
6404
  });
6462
- startAutodiscovery({ serversDir: serversDir2, missionsDir, excludePids: /* @__PURE__ */ new Set([process.pid]) });
6405
+ startAutodiscovery({ serversDir: serversDir2, projectsDir, excludePids: /* @__PURE__ */ new Set([process.pid]) });
6463
6406
  return new Promise((resolvePromise, reject) => {
6464
6407
  server.on("error", (err) => {
6465
6408
  if (err.code === "EADDRINUSE") {
@@ -6471,7 +6414,7 @@ function createDashboardServer(options) {
6471
6414
  }
6472
6415
  });
6473
6416
  server.listen(port, () => {
6474
- const portFile = resolve15(homedir2(), ".syntaur", "dashboard-port");
6417
+ const portFile = resolve15(syntaurRoot(), "dashboard-port");
6475
6418
  writeFile4(portFile, String(port), "utf-8").catch(() => {
6476
6419
  });
6477
6420
  resolvePromise();
@@ -6485,12 +6428,13 @@ function createDashboardServer(options) {
6485
6428
  }
6486
6429
  closeSessionDb();
6487
6430
  for (const client of clients) {
6488
- client.close();
6431
+ client.terminate();
6489
6432
  }
6490
6433
  clients.clear();
6491
- const portFile = resolve15(homedir2(), ".syntaur", "dashboard-port");
6434
+ const portFile = resolve15(syntaurRoot(), "dashboard-port");
6492
6435
  await unlink4(portFile).catch(() => {
6493
6436
  });
6437
+ server.closeAllConnections?.();
6494
6438
  return new Promise((resolvePromise) => {
6495
6439
  server.close(() => resolvePromise());
6496
6440
  });