syntaur 0.8.0 → 0.12.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 (126) hide show
  1. package/.claude-plugin/plugin.json +9 -1
  2. package/dashboard/dist/assets/{_basePickBy-CIZortGD.js → _basePickBy-XOV2F7dX.js} +1 -1
  3. package/dashboard/dist/assets/{_baseUniq-BHP5NBQ7.js → _baseUniq-Dccfk3fK.js} +1 -1
  4. package/dashboard/dist/assets/{arc-BLtQy7Cl.js → arc-TwJ5RF-H.js} +1 -1
  5. package/dashboard/dist/assets/{architectureDiagram-2XIMDMQ5-0mhIBjWy.js → architectureDiagram-2XIMDMQ5-CpkVJudw.js} +1 -1
  6. package/dashboard/dist/assets/{blockDiagram-WCTKOSBZ-CM7K5Tf5.js → blockDiagram-WCTKOSBZ-Ck6VQ4Fh.js} +1 -1
  7. package/dashboard/dist/assets/{c4Diagram-IC4MRINW-gsbeygvS.js → c4Diagram-IC4MRINW-B5V7YG03.js} +1 -1
  8. package/dashboard/dist/assets/channel-AEMTX70R.js +1 -0
  9. package/dashboard/dist/assets/{chunk-4BX2VUAB-CneRz9Nd.js → chunk-4BX2VUAB-rBdyOxAe.js} +1 -1
  10. package/dashboard/dist/assets/{chunk-55IACEB6-uetANWHd.js → chunk-55IACEB6-DKlNXjYt.js} +1 -1
  11. package/dashboard/dist/assets/{chunk-FMBD7UC4-3-tNszoF.js → chunk-FMBD7UC4-Cqm0PtUI.js} +1 -1
  12. package/dashboard/dist/assets/{chunk-JSJVCQXG-DOO88rZD.js → chunk-JSJVCQXG-CQyXQcG3.js} +1 -1
  13. package/dashboard/dist/assets/{chunk-KX2RTZJC-Bnc1CFy2.js → chunk-KX2RTZJC-DmqjHPpJ.js} +1 -1
  14. package/dashboard/dist/assets/{chunk-NQ4KR5QH-BfViC1Ms.js → chunk-NQ4KR5QH-CHXoBcYa.js} +1 -1
  15. package/dashboard/dist/assets/{chunk-QZHKN3VN-CgjyAt2l.js → chunk-QZHKN3VN-Dd4sChBS.js} +1 -1
  16. package/dashboard/dist/assets/{chunk-WL4C6EOR-BYFermSR.js → chunk-WL4C6EOR-D-QLnkY8.js} +1 -1
  17. package/dashboard/dist/assets/classDiagram-VBA2DB6C-LzuOAGzu.js +1 -0
  18. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-LzuOAGzu.js +1 -0
  19. package/dashboard/dist/assets/clone-Cw63QGUJ.js +1 -0
  20. package/dashboard/dist/assets/{cose-bilkent-S5V4N54A-CE-k5SLv.js → cose-bilkent-S5V4N54A-VZi6uV56.js} +1 -1
  21. package/dashboard/dist/assets/{dagre-KLK3FWXG-CX7Br8LQ.js → dagre-KLK3FWXG-CtT97Qhx.js} +1 -1
  22. package/dashboard/dist/assets/{diagram-E7M64L7V-t-xhWjCU.js → diagram-E7M64L7V-BjYTNNHb.js} +1 -1
  23. package/dashboard/dist/assets/{diagram-IFDJBPK2-BVhcTXsJ.js → diagram-IFDJBPK2-qj8_r6fb.js} +1 -1
  24. package/dashboard/dist/assets/{diagram-P4PSJMXO-fxt8ZSca.js → diagram-P4PSJMXO-DlfWo7Vt.js} +1 -1
  25. package/dashboard/dist/assets/{erDiagram-INFDFZHY-C20k2bxX.js → erDiagram-INFDFZHY-Ch_nApTv.js} +1 -1
  26. package/dashboard/dist/assets/{flowDiagram-PKNHOUZH-CdeygexM.js → flowDiagram-PKNHOUZH-EOVsqNyi.js} +1 -1
  27. package/dashboard/dist/assets/{ganttDiagram-A5KZAMGK-CPsoOAV-.js → ganttDiagram-A5KZAMGK-CS-lx0B-.js} +1 -1
  28. package/dashboard/dist/assets/{gitGraphDiagram-K3NZZRJ6-t0tCTIZy.js → gitGraphDiagram-K3NZZRJ6-Ba6RDzPy.js} +1 -1
  29. package/dashboard/dist/assets/{graph-Bcqd31wL.js → graph-BKaAyLk-.js} +1 -1
  30. package/dashboard/dist/assets/index-BTWPZaIA.css +1 -0
  31. package/dashboard/dist/assets/index-Dk9PBm5B.js +510 -0
  32. package/dashboard/dist/assets/{infoDiagram-LFFYTUFH-DgoKvH3N.js → infoDiagram-LFFYTUFH-Cr20xEsd.js} +1 -1
  33. package/dashboard/dist/assets/{ishikawaDiagram-PHBUUO56-CGr4P8do.js → ishikawaDiagram-PHBUUO56-CuILV9ph.js} +1 -1
  34. package/dashboard/dist/assets/{journeyDiagram-4ABVD52K-CHYRi6wF.js → journeyDiagram-4ABVD52K-CO-l7Mpm.js} +1 -1
  35. package/dashboard/dist/assets/{kanban-definition-K7BYSVSG-kwzqrFcW.js → kanban-definition-K7BYSVSG-CcirPw_S.js} +1 -1
  36. package/dashboard/dist/assets/{layout-wud47qGs.js → layout-BXvr8dMw.js} +1 -1
  37. package/dashboard/dist/assets/{linear-DG5js90X.js → linear-B85hgYnX.js} +1 -1
  38. package/dashboard/dist/assets/{mermaid.core-BDw69uC2.js → mermaid.core-DcZN9Xp2.js} +4 -4
  39. package/dashboard/dist/assets/{mindmap-definition-YRQLILUH-Dw_uyg1-.js → mindmap-definition-YRQLILUH-Bxi6p5_3.js} +1 -1
  40. package/dashboard/dist/assets/{pieDiagram-SKSYHLDU-C7NBi6tK.js → pieDiagram-SKSYHLDU-DOSvU0UD.js} +1 -1
  41. package/dashboard/dist/assets/{quadrantDiagram-337W2JSQ-_N_BSBd4.js → quadrantDiagram-337W2JSQ-Cmo-5tW9.js} +1 -1
  42. package/dashboard/dist/assets/{requirementDiagram-Z7DCOOCP-BqUiqfP1.js → requirementDiagram-Z7DCOOCP-Dw8alwuI.js} +1 -1
  43. package/dashboard/dist/assets/{sankeyDiagram-WA2Y5GQK-C2Qq5ULW.js → sankeyDiagram-WA2Y5GQK-ZPB-rzOg.js} +1 -1
  44. package/dashboard/dist/assets/{sequenceDiagram-2WXFIKYE-BOCF2z8f.js → sequenceDiagram-2WXFIKYE-Ct8bkK2h.js} +1 -1
  45. package/dashboard/dist/assets/{stateDiagram-RAJIS63D-BvDyTxC5.js → stateDiagram-RAJIS63D-DnGOXlmx.js} +1 -1
  46. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-COGzlZxn.js +1 -0
  47. package/dashboard/dist/assets/{timeline-definition-YZTLITO2-DNNZxSrG.js → timeline-definition-YZTLITO2-AUFmbJjr.js} +1 -1
  48. package/dashboard/dist/assets/{treemap-KZPCXAKY-C6hOdiGH.js → treemap-KZPCXAKY-Dw5FDFAV.js} +1 -1
  49. package/dashboard/dist/assets/{vennDiagram-LZ73GAT5-FmXl6puP.js → vennDiagram-LZ73GAT5-DfxicU4N.js} +1 -1
  50. package/dashboard/dist/assets/{xychartDiagram-JWTSCODW-Bjav12WG.js → xychartDiagram-JWTSCODW--9N0V1r_.js} +1 -1
  51. package/dashboard/dist/index.html +2 -2
  52. package/dashboard/dist/syntaur-logo.svg +5 -13
  53. package/dist/dashboard/server.js +363 -3
  54. package/dist/dashboard/server.js.map +1 -1
  55. package/dist/index.js +2140 -153
  56. package/dist/index.js.map +1 -1
  57. package/examples/playbooks/keep-records-updated.md +2 -1
  58. package/package.json +1 -1
  59. package/platforms/claude-code/.claude-plugin/plugin.json +9 -1
  60. package/platforms/claude-code/agents/syntaur-expert.md +1 -1
  61. package/platforms/claude-code/commands/add-memory/add-memory.md +18 -0
  62. package/platforms/claude-code/commands/add-resource/add-resource.md +16 -0
  63. package/platforms/claude-code/commands/list-assignments/list-assignments.md +16 -0
  64. package/platforms/claude-code/commands/log-progress/log-progress.md +16 -0
  65. package/platforms/claude-code/commands/replan/replan.md +16 -0
  66. package/platforms/claude-code/commands/resume-session/resume-session.md +16 -0
  67. package/platforms/claude-code/commands/set-workspace/set-workspace.md +16 -0
  68. package/platforms/claude-code/commands/syntaur-worktree/syntaur-worktree.md +16 -0
  69. package/platforms/claude-code/references/protocol-summary.md +8 -0
  70. package/platforms/claude-code/skills/add-memory/SKILL.md +96 -0
  71. package/platforms/claude-code/skills/add-resource/SKILL.md +85 -0
  72. package/platforms/claude-code/skills/capture-artifacts/SKILL.md +84 -0
  73. package/platforms/claude-code/skills/complete-assignment/SKILL.md +2 -1
  74. package/platforms/claude-code/skills/list-assignments/SKILL.md +73 -0
  75. package/platforms/claude-code/skills/log-progress/SKILL.md +128 -0
  76. package/platforms/claude-code/skills/plan-assignment/SKILL.md +29 -8
  77. package/platforms/claude-code/skills/replan/SKILL.md +113 -0
  78. package/platforms/claude-code/skills/resume-session/SKILL.md +83 -0
  79. package/platforms/claude-code/skills/set-workspace/SKILL.md +109 -0
  80. package/platforms/claude-code/skills/syntaur-protocol/SKILL.md +4 -0
  81. package/platforms/claude-code/skills/syntaur-protocol/references/protocol-summary.md +8 -0
  82. package/platforms/claude-code/skills/syntaur-worktree/SKILL.md +117 -0
  83. package/platforms/codex/README.md +2 -2
  84. package/platforms/codex/agents/syntaur-operator.md +7 -0
  85. package/platforms/codex/commands/add-memory.md +16 -0
  86. package/platforms/codex/commands/add-resource.md +14 -0
  87. package/platforms/codex/commands/list-assignments.md +13 -0
  88. package/platforms/codex/commands/log-progress.md +14 -0
  89. package/platforms/codex/commands/replan.md +15 -0
  90. package/platforms/codex/commands/resume-session.md +15 -0
  91. package/platforms/codex/commands/set-workspace.md +15 -0
  92. package/platforms/codex/commands/syntaur-worktree.md +15 -0
  93. package/platforms/codex/references/protocol-summary.md +8 -0
  94. package/platforms/codex/skills/add-memory/SKILL.md +96 -0
  95. package/platforms/codex/skills/add-resource/SKILL.md +85 -0
  96. package/platforms/codex/skills/capture-artifacts/SKILL.md +84 -0
  97. package/platforms/codex/skills/complete-assignment/SKILL.md +2 -1
  98. package/platforms/codex/skills/list-assignments/SKILL.md +73 -0
  99. package/platforms/codex/skills/log-progress/SKILL.md +128 -0
  100. package/platforms/codex/skills/plan-assignment/SKILL.md +29 -8
  101. package/platforms/codex/skills/replan/SKILL.md +113 -0
  102. package/platforms/codex/skills/resume-session/SKILL.md +83 -0
  103. package/platforms/codex/skills/set-workspace/SKILL.md +109 -0
  104. package/platforms/codex/skills/syntaur-protocol/SKILL.md +4 -0
  105. package/platforms/codex/skills/syntaur-protocol/references/protocol-summary.md +8 -0
  106. package/platforms/codex/skills/syntaur-worktree/SKILL.md +117 -0
  107. package/skills/add-memory/SKILL.md +96 -0
  108. package/skills/add-resource/SKILL.md +85 -0
  109. package/skills/capture-artifacts/SKILL.md +84 -0
  110. package/skills/complete-assignment/SKILL.md +2 -1
  111. package/skills/list-assignments/SKILL.md +73 -0
  112. package/skills/log-progress/SKILL.md +128 -0
  113. package/skills/plan-assignment/SKILL.md +29 -8
  114. package/skills/replan/SKILL.md +113 -0
  115. package/skills/resume-session/SKILL.md +83 -0
  116. package/skills/set-workspace/SKILL.md +109 -0
  117. package/skills/syntaur-protocol/SKILL.md +4 -0
  118. package/skills/syntaur-protocol/references/protocol-summary.md +8 -0
  119. package/skills/syntaur-worktree/SKILL.md +117 -0
  120. package/dashboard/dist/assets/channel-FWLgstBg.js +0 -1
  121. package/dashboard/dist/assets/classDiagram-VBA2DB6C-BAvQTSn_.js +0 -1
  122. package/dashboard/dist/assets/classDiagram-v2-RAHNMMFH-BAvQTSn_.js +0 -1
  123. package/dashboard/dist/assets/clone-rSEllQ1P.js +0 -1
  124. package/dashboard/dist/assets/index-bLWoSCLL.css +0 -1
  125. package/dashboard/dist/assets/index-eS0wtDO0.js +0 -500
  126. package/dashboard/dist/assets/stateDiagram-v2-FVOUBMTO-TaLLhLWD.js +0 -1
@@ -16,6 +16,7 @@ __export(paths_exports, {
16
16
  expandHome: () => expandHome,
17
17
  playbooksDir: () => playbooksDir,
18
18
  projectTodosDir: () => projectTodosDir,
19
+ proofDir: () => proofDir,
19
20
  serversDir: () => serversDir,
20
21
  syntaurRoot: () => syntaurRoot,
21
22
  todoPlanDir: () => todoPlanDir,
@@ -57,6 +58,9 @@ function projectTodosDir(projectsDir, projectSlug) {
57
58
  function todoPlanDir(todosDir2, workspaceOrProject, todoId) {
58
59
  return resolve(todosDir2, "plans", workspaceOrProject, todoId);
59
60
  }
61
+ function proofDir(assignmentDir) {
62
+ return resolve(assignmentDir, "proof");
63
+ }
60
64
  var init_paths = __esm({
61
65
  "src/utils/paths.ts"() {
62
66
  "use strict";
@@ -2071,6 +2075,7 @@ function parseMemory(fileContent) {
2071
2075
  scope: getField(fm, "scope") ?? "",
2072
2076
  sourceAssignment: getField(fm, "sourceAssignment"),
2073
2077
  relatedAssignments: parseListField(fm, "relatedAssignments"),
2078
+ tags: parseListField(fm, "tags"),
2074
2079
  created: getField(fm, "created") ?? "",
2075
2080
  updated: getField(fm, "updated") ?? "",
2076
2081
  body
@@ -3269,7 +3274,7 @@ var init_scanner = __esm({
3269
3274
 
3270
3275
  // src/dashboard/api.ts
3271
3276
  import { readdir as readdir6, readFile as readFile8, writeFile as writeFile3 } from "fs/promises";
3272
- import { resolve as resolve9, dirname as dirname2 } from "path";
3277
+ import { resolve as resolve9, dirname as dirname2, basename } from "path";
3273
3278
  async function listStandaloneRecords(assignmentsDir2) {
3274
3279
  if (!assignmentsDir2) return [];
3275
3280
  if (!await fileExists(assignmentsDir2)) return [];
@@ -4108,12 +4113,102 @@ async function listMemories(projectPath) {
4108
4113
  source: parsed.source,
4109
4114
  scope: parsed.scope,
4110
4115
  sourceAssignment: parsed.sourceAssignment,
4116
+ relatedAssignments: parsed.relatedAssignments,
4111
4117
  updated: parsed.updated
4112
4118
  });
4113
4119
  }
4114
4120
  results.sort((left, right) => compareTimestamps(right.updated, left.updated));
4115
4121
  return results;
4116
4122
  }
4123
+ async function listAllMemories(projectsDir) {
4124
+ const projectRecords = await listProjectRecords(projectsDir);
4125
+ const all = [];
4126
+ for (const record of projectRecords) {
4127
+ const memories = await listMemories(record.projectPath);
4128
+ for (const memory of memories) {
4129
+ all.push({
4130
+ ...memory,
4131
+ projectSlug: basename(record.projectPath),
4132
+ projectTitle: record.summary.title
4133
+ });
4134
+ }
4135
+ }
4136
+ all.sort((left, right) => compareTimestamps(right.updated, left.updated));
4137
+ return all;
4138
+ }
4139
+ async function listAllResources(projectsDir) {
4140
+ const projectRecords = await listProjectRecords(projectsDir);
4141
+ const all = [];
4142
+ for (const record of projectRecords) {
4143
+ const resources = await listResources(record.projectPath);
4144
+ for (const resource of resources) {
4145
+ all.push({
4146
+ ...resource,
4147
+ projectSlug: basename(record.projectPath),
4148
+ projectTitle: record.summary.title
4149
+ });
4150
+ }
4151
+ }
4152
+ all.sort((left, right) => compareTimestamps(right.updated, left.updated));
4153
+ return all;
4154
+ }
4155
+ async function resolveProjectPath(projectsDir, projectSlug) {
4156
+ const direct = resolve9(projectsDir, projectSlug);
4157
+ if (await fileExists(resolve9(direct, "project.md"))) return direct;
4158
+ const records = await listProjectRecords(projectsDir);
4159
+ const match = records.find((r) => r.summary.slug === projectSlug);
4160
+ return match ? match.projectPath : null;
4161
+ }
4162
+ async function getMemoryDetail(projectsDir, projectSlug, itemSlug) {
4163
+ if (itemSlug.startsWith("_")) return null;
4164
+ const projectRecords = await listProjectRecords(projectsDir);
4165
+ const projectRecord = projectRecords.find(
4166
+ (p) => basename(p.projectPath) === projectSlug || p.summary.slug === projectSlug
4167
+ );
4168
+ if (!projectRecord) return null;
4169
+ const filePath = resolve9(projectRecord.projectPath, "memories", `${itemSlug}.md`);
4170
+ if (!await fileExists(filePath)) return null;
4171
+ const content = await readFile8(filePath, "utf-8");
4172
+ const parsed = parseMemory(content);
4173
+ return {
4174
+ name: parsed.name,
4175
+ slug: itemSlug,
4176
+ source: parsed.source,
4177
+ scope: parsed.scope,
4178
+ sourceAssignment: parsed.sourceAssignment,
4179
+ relatedAssignments: parsed.relatedAssignments,
4180
+ updated: parsed.updated,
4181
+ created: parsed.created,
4182
+ body: parsed.body,
4183
+ tags: parsed.tags,
4184
+ projectSlug: basename(projectRecord.projectPath),
4185
+ projectTitle: projectRecord.summary.title
4186
+ };
4187
+ }
4188
+ async function getResourceDetail(projectsDir, projectSlug, itemSlug) {
4189
+ if (itemSlug.startsWith("_")) return null;
4190
+ const projectRecords = await listProjectRecords(projectsDir);
4191
+ const projectRecord = projectRecords.find(
4192
+ (p) => basename(p.projectPath) === projectSlug || p.summary.slug === projectSlug
4193
+ );
4194
+ if (!projectRecord) return null;
4195
+ const filePath = resolve9(projectRecord.projectPath, "resources", `${itemSlug}.md`);
4196
+ if (!await fileExists(filePath)) return null;
4197
+ const content = await readFile8(filePath, "utf-8");
4198
+ const parsed = parseResource(content);
4199
+ return {
4200
+ name: parsed.name,
4201
+ slug: itemSlug,
4202
+ category: parsed.category,
4203
+ source: parsed.source,
4204
+ relatedAssignments: parsed.relatedAssignments,
4205
+ updated: parsed.updated,
4206
+ created: parsed.created,
4207
+ body: parsed.body,
4208
+ projectSlug: basename(projectRecord.projectPath),
4209
+ projectTitle: projectRecord.summary.title
4210
+ };
4211
+ }
4117
4212
  async function loadDependencyGraph(projectPath, assignments) {
4118
4213
  const statusPath = resolve9(projectPath, "_status.md");
4119
4214
  if (await fileExists(statusPath)) {
@@ -4449,6 +4544,10 @@ function getDocumentPath(projectsDir, documentType, projectSlug, assignmentSlug)
4449
4544
  return assignmentSlug ? resolve9(projectsDir, projectSlug, "assignments", assignmentSlug, "handoff.md") : null;
4450
4545
  case "decision-record":
4451
4546
  return assignmentSlug ? resolve9(projectsDir, projectSlug, "assignments", assignmentSlug, "decision-record.md") : null;
4547
+ case "memory":
4548
+ return assignmentSlug ? resolve9(projectsDir, projectSlug, "memories", `${assignmentSlug}.md`) : null;
4549
+ case "resource":
4550
+ return assignmentSlug ? resolve9(projectsDir, projectSlug, "resources", `${assignmentSlug}.md`) : null;
4452
4551
  default:
4453
4552
  return null;
4454
4553
  }
@@ -4469,6 +4568,10 @@ function getEditableDocumentTitle(documentType, projectSlug, assignmentSlug) {
4469
4568
  return `Append Decision: ${assignmentSlug || "assignment"}`;
4470
4569
  case "playbook":
4471
4570
  return `Edit Playbook: ${projectSlug}`;
4571
+ case "memory":
4572
+ return `Edit Memory: ${assignmentSlug || "memory"}`;
4573
+ case "resource":
4574
+ return `Edit Resource: ${assignmentSlug || "resource"}`;
4472
4575
  default:
4473
4576
  return projectSlug;
4474
4577
  }
@@ -4910,6 +5013,40 @@ var init_decision_record = __esm({
4910
5013
  });
4911
5014
 
4912
5015
  // src/templates/index-stubs.ts
5016
+ function renderMemoryStub(params2) {
5017
+ return `---
5018
+ type: memory
5019
+ name: ${escapeYamlString(params2.name)}
5020
+ source: claude
5021
+ sourceAssignment: null
5022
+ relatedAssignments: []
5023
+ scope: project
5024
+ created: "${params2.timestamp}"
5025
+ updated: "${params2.timestamp}"
5026
+ tags: []
5027
+ ---
5028
+
5029
+ # ${params2.name}
5030
+
5031
+ `;
5032
+ }
5033
+ function renderResourceStub(params2) {
5034
+ return `---
5035
+ type: resource
5036
+ name: ${escapeYamlString(params2.name)}
5037
+ source: claude
5038
+ category: documentation
5039
+ sourceUrl: null
5040
+ sourceAssignment: null
5041
+ relatedAssignments: []
5042
+ created: "${params2.timestamp}"
5043
+ updated: "${params2.timestamp}"
5044
+ ---
5045
+
5046
+ # ${params2.name}
5047
+
5048
+ `;
5049
+ }
4913
5050
  function renderIndexAssignments(params2) {
4914
5051
  return `---
4915
5052
  project: ${params2.slug}
@@ -5022,6 +5159,7 @@ total: 0
5022
5159
  var init_index_stubs = __esm({
5023
5160
  "src/templates/index-stubs.ts"() {
5024
5161
  "use strict";
5162
+ init_yaml();
5025
5163
  }
5026
5164
  });
5027
5165
 
@@ -6255,8 +6393,8 @@ init_timestamp();
6255
6393
  init_fs();
6256
6394
  init_parser();
6257
6395
  import { Router } from "express";
6258
- import { resolve as resolve12 } from "path";
6259
- import { rm, readFile as readFile10 } from "fs/promises";
6396
+ import { resolve as resolve12, basename as basename2 } from "path";
6397
+ import { rm, readFile as readFile10, open as fsOpen } from "fs/promises";
6260
6398
 
6261
6399
  // src/dashboard/acceptance-criteria.ts
6262
6400
  function splitFrontmatter(content) {
@@ -6528,6 +6666,210 @@ function createWriteRouter(projectsDir, assignmentsDir2) {
6528
6666
  }
6529
6667
  res.json(document);
6530
6668
  });
6669
+ router.get("/api/projects/:slug/memories/:itemSlug/edit", async (req, res) => {
6670
+ const slug = getParam(req.params.slug);
6671
+ const itemSlug = getParam(req.params.itemSlug);
6672
+ if (!isValidSlug(itemSlug)) {
6673
+ res.status(400).json({ error: "Invalid memory slug." });
6674
+ return;
6675
+ }
6676
+ const projectDir = await resolveProjectPath(projectsDir, slug);
6677
+ if (!projectDir) {
6678
+ res.status(404).json({ error: `Project "${slug}" not found` });
6679
+ return;
6680
+ }
6681
+ const document = await getEditableDocument(projectsDir, "memory", basename2(projectDir), itemSlug);
6682
+ if (!document) {
6683
+ res.status(404).json({ error: "Memory not found" });
6684
+ return;
6685
+ }
6686
+ res.json(document);
6687
+ });
6688
+ router.get("/api/projects/:slug/resources/:itemSlug/edit", async (req, res) => {
6689
+ const slug = getParam(req.params.slug);
6690
+ const itemSlug = getParam(req.params.itemSlug);
6691
+ if (!isValidSlug(itemSlug)) {
6692
+ res.status(400).json({ error: "Invalid resource slug." });
6693
+ return;
6694
+ }
6695
+ const projectDir = await resolveProjectPath(projectsDir, slug);
6696
+ if (!projectDir) {
6697
+ res.status(404).json({ error: `Project "${slug}" not found` });
6698
+ return;
6699
+ }
6700
+ const document = await getEditableDocument(projectsDir, "resource", basename2(projectDir), itemSlug);
6701
+ if (!document) {
6702
+ res.status(404).json({ error: "Resource not found" });
6703
+ return;
6704
+ }
6705
+ res.json(document);
6706
+ });
6707
+ function itemFolder(kind) {
6708
+ return kind === "memory" ? "memories" : "resources";
6709
+ }
6710
+ function renderItemStub(kind, params2) {
6711
+ return kind === "memory" ? renderMemoryStub(params2) : renderResourceStub(params2);
6712
+ }
6713
+ function replaceStubBody(stub, body) {
6714
+ const match = stub.match(/^(---\r?\n[\s\S]*?\r?\n---\r?\n)/);
6715
+ if (!match) return stub;
6716
+ return `${match[1]}
6717
+ ${body.startsWith("\n") ? body.slice(1) : body}${body.endsWith("\n") ? "" : "\n"}`;
6718
+ }
6719
+ function extractFrontmatterBlock(content) {
6720
+ const match = content.match(/^(---\r?\n[\s\S]*?\r?\n---\r?\n)/);
6721
+ return match ? match[1] : null;
6722
+ }
6723
+ function parseItem(kind, content) {
6724
+ return kind === "memory" ? parseMemory(content) : parseResource(content);
6725
+ }
6726
+ async function getItemDetail(kind, projectSlug, itemSlug) {
6727
+ return kind === "memory" ? await getMemoryDetail(projectsDir, projectSlug, itemSlug) : await getResourceDetail(projectsDir, projectSlug, itemSlug);
6728
+ }
6729
+ async function resolveProjectDir(projectSlug) {
6730
+ return resolveProjectPath(projectsDir, projectSlug);
6731
+ }
6732
+ function rejectBadItemSlug(itemSlug, kind, res) {
6733
+ if (isValidSlug(itemSlug)) return false;
6734
+ res.status(400).json({
6735
+ error: `Invalid ${kind} slug "${itemSlug}". Slugs must be lowercase letters, numbers, and hyphens only.`
6736
+ });
6737
+ return true;
6738
+ }
6739
+ for (const kind of ["memory", "resource"]) {
6740
+ const folder = itemFolder(kind);
6741
+ router.get(`/api/projects/:slug/${folder}/:itemSlug`, async (req, res) => {
6742
+ try {
6743
+ const projectSlug = getParam(req.params.slug);
6744
+ const itemSlug = getParam(req.params.itemSlug);
6745
+ if (rejectBadItemSlug(itemSlug, kind, res)) return;
6746
+ const detail = await getItemDetail(kind, projectSlug, itemSlug);
6747
+ if (!detail) {
6748
+ res.status(404).json({ error: `${kind === "memory" ? "Memory" : "Resource"} not found` });
6749
+ return;
6750
+ }
6751
+ res.json(detail);
6752
+ } catch (error) {
6753
+ console.error(`Error fetching ${kind} detail:`, error);
6754
+ res.status(500).json({ error: `Failed to load ${kind}: ${error.message}` });
6755
+ }
6756
+ });
6757
+ router.post(`/api/projects/:slug/${folder}`, async (req, res) => {
6758
+ try {
6759
+ const projectSlug = getParam(req.params.slug);
6760
+ const projectDir = await resolveProjectDir(projectSlug);
6761
+ if (!projectDir) {
6762
+ res.status(404).json({ error: `Project "${projectSlug}" not found` });
6763
+ return;
6764
+ }
6765
+ const body = req.body ?? {};
6766
+ const name = typeof body.name === "string" ? body.name.trim() : "";
6767
+ if (!name) {
6768
+ res.status(400).json({ error: "Name is required." });
6769
+ return;
6770
+ }
6771
+ const requestedSlug = typeof body.slug === "string" && body.slug.trim() ? body.slug.trim() : slugify(name);
6772
+ if (!requestedSlug || !isValidSlug(requestedSlug)) {
6773
+ res.status(400).json({
6774
+ error: `Slug "${requestedSlug}" is invalid. Slugs must be lowercase letters, numbers, and hyphens only.`
6775
+ });
6776
+ return;
6777
+ }
6778
+ const folderPath = resolve12(projectDir, folder);
6779
+ await ensureDir(folderPath);
6780
+ const filePath = resolve12(folderPath, `${requestedSlug}.md`);
6781
+ const timestamp = nowTimestamp();
6782
+ let content = renderItemStub(kind, {
6783
+ slug: requestedSlug,
6784
+ name,
6785
+ projectSlug: basename2(projectDir),
6786
+ timestamp
6787
+ });
6788
+ const customBody = typeof body.body === "string" ? body.body : "";
6789
+ if (customBody.trim()) {
6790
+ content = replaceStubBody(content, customBody);
6791
+ }
6792
+ try {
6793
+ const handle = await fsOpen(filePath, "wx");
6794
+ try {
6795
+ await handle.writeFile(content, "utf-8");
6796
+ } finally {
6797
+ await handle.close();
6798
+ }
6799
+ } catch (err) {
6800
+ if (err.code === "EEXIST") {
6801
+ res.status(409).json({
6802
+ error: `${kind === "memory" ? "Memory" : "Resource"} with slug "${requestedSlug}" already exists in project "${basename2(projectDir)}".`
6803
+ });
6804
+ return;
6805
+ }
6806
+ throw err;
6807
+ }
6808
+ res.status(201).json({ slug: requestedSlug, projectSlug: basename2(projectDir), content });
6809
+ } catch (error) {
6810
+ console.error(`Error creating ${kind}:`, error);
6811
+ res.status(500).json({ error: `Failed to create ${kind}: ${error.message}` });
6812
+ }
6813
+ });
6814
+ router.patch(`/api/projects/:slug/${folder}/:itemSlug`, async (req, res) => {
6815
+ try {
6816
+ const projectSlug = getParam(req.params.slug);
6817
+ const itemSlug = getParam(req.params.itemSlug);
6818
+ if (rejectBadItemSlug(itemSlug, kind, res)) return;
6819
+ const projectDir = await resolveProjectDir(projectSlug);
6820
+ if (!projectDir) {
6821
+ res.status(404).json({ error: `Project "${projectSlug}" not found` });
6822
+ return;
6823
+ }
6824
+ const filePath = resolve12(projectDir, folder, `${itemSlug}.md`);
6825
+ if (!await fileExists(filePath)) {
6826
+ res.status(404).json({ error: `${kind === "memory" ? "Memory" : "Resource"} not found` });
6827
+ return;
6828
+ }
6829
+ const nextContentRaw = requireContent(req, res);
6830
+ if (!nextContentRaw) return;
6831
+ const currentContent = await readFile10(filePath, "utf-8");
6832
+ const frontmatterBlock = extractFrontmatterBlock(currentContent);
6833
+ if (!frontmatterBlock) {
6834
+ res.status(500).json({ error: `${kind} file is malformed (no frontmatter)` });
6835
+ return;
6836
+ }
6837
+ const next = parseItem(kind, nextContentRaw);
6838
+ const nextBody = next.body.trimStart();
6839
+ let merged = `${frontmatterBlock}
6840
+ ${nextBody}${nextBody.endsWith("\n") ? "" : "\n"}`;
6841
+ merged = setTopLevelField(merged, "updated", nowTimestamp());
6842
+ await writeFileForce(filePath, merged);
6843
+ const detail = await getItemDetail(kind, basename2(projectDir), itemSlug);
6844
+ res.json({ [kind]: detail, content: merged });
6845
+ } catch (error) {
6846
+ console.error(`Error updating ${kind}:`, error);
6847
+ res.status(500).json({ error: `Failed to update ${kind}: ${error.message}` });
6848
+ }
6849
+ });
6850
+ router.delete(`/api/projects/:slug/${folder}/:itemSlug`, async (req, res) => {
6851
+ try {
6852
+ const projectSlug = getParam(req.params.slug);
6853
+ const itemSlug = getParam(req.params.itemSlug);
6854
+ if (rejectBadItemSlug(itemSlug, kind, res)) return;
6855
+ const projectDir = await resolveProjectDir(projectSlug);
6856
+ if (!projectDir) {
6857
+ res.status(404).json({ error: `Project "${projectSlug}" not found` });
6858
+ return;
6859
+ }
6860
+ const filePath = resolve12(projectDir, folder, `${itemSlug}.md`);
6861
+ if (!await fileExists(filePath)) {
6862
+ res.status(404).json({ error: `${kind === "memory" ? "Memory" : "Resource"} not found` });
6863
+ return;
6864
+ }
6865
+ await rm(filePath);
6866
+ res.status(204).end();
6867
+ } catch (error) {
6868
+ console.error(`Error deleting ${kind}:`, error);
6869
+ res.status(500).json({ error: `Failed to delete ${kind}: ${error.message}` });
6870
+ }
6871
+ });
6872
+ }
6531
6873
  router.post("/api/projects", async (req, res) => {
6532
6874
  try {
6533
6875
  const content = requireContent(req, res);
@@ -10699,6 +11041,24 @@ function createDashboardServer(options) {
10699
11041
  app.use("/api/servers", createServersRouter(serversDir2, projectsDir, assignmentsDir2));
10700
11042
  app.use("/api/agent-sessions", createAgentSessionsRouter(projectsDir, broadcast, assignmentsDir2));
10701
11043
  app.use("/api/playbooks", createPlaybooksRouter(playbooksDir2));
11044
+ app.get("/api/memories", async (_req, res) => {
11045
+ try {
11046
+ const memories = await listAllMemories(projectsDir);
11047
+ res.json({ generatedAt: (/* @__PURE__ */ new Date()).toISOString(), memories });
11048
+ } catch (error) {
11049
+ console.error("Error listing memories:", error);
11050
+ res.status(500).json({ error: `Failed to load memories: ${error.message}` });
11051
+ }
11052
+ });
11053
+ app.get("/api/resources", async (_req, res) => {
11054
+ try {
11055
+ const resources = await listAllResources(projectsDir);
11056
+ res.json({ generatedAt: (/* @__PURE__ */ new Date()).toISOString(), resources });
11057
+ } catch (error) {
11058
+ console.error("Error listing resources:", error);
11059
+ res.status(500).json({ error: `Failed to load resources: ${error.message}` });
11060
+ }
11061
+ });
10702
11062
  app.use("/api/todos", createTodosRouter(todosDir2, broadcast, projectsDir));
10703
11063
  app.use("/api/projects/:projectId/todos", createProjectTodosRouter(projectsDir, broadcast, todosDir2));
10704
11064
  app.use("/api/backup", createBackupRouter());