@amoree-code/ocean 0.0.0-stage → 1.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 (300) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +284 -2
  3. package/dist/application/auth/auth-orchestrator.js +77 -0
  4. package/dist/application/clients/clients.js +646 -0
  5. package/dist/application/clients/editors.js +398 -0
  6. package/dist/application/clients/hooks.js +335 -0
  7. package/dist/application/clients/native.js +410 -0
  8. package/dist/application/clients/opencode-plugin.js +158 -0
  9. package/dist/application/clients/registry.js +188 -0
  10. package/dist/application/gateway/webhook-gateway.js +137 -0
  11. package/dist/application/install/provider-installer.js +184 -0
  12. package/dist/application/integrations/charter-copies.js +76 -0
  13. package/dist/application/integrations/claude-vscode.js +106 -0
  14. package/dist/application/integrations/engine-entry-repair.js +105 -0
  15. package/dist/application/integrations/skill-filter.js +254 -0
  16. package/dist/application/layout/layout-apply.js +443 -0
  17. package/dist/application/layout/layout-plan.js +236 -0
  18. package/dist/application/layout/workspace-template.js +34 -0
  19. package/dist/application/onboarding/state.js +23 -0
  20. package/dist/application/onboarding/steps.js +249 -0
  21. package/dist/application/profiles/profile-distribution.js +53 -0
  22. package/dist/application/profiles/profile-suggestion.js +39 -0
  23. package/dist/application/profiles/profile-upgrade-service.js +208 -0
  24. package/dist/application/pull/history-index.js +476 -0
  25. package/dist/application/pull/project-match.js +132 -0
  26. package/dist/application/pull/pull.js +202 -0
  27. package/dist/application/pull/server-capture.js +307 -0
  28. package/dist/application/scheduler/local-scheduler.js +210 -0
  29. package/dist/composition/runtime.js +64 -0
  30. package/dist/engine-root.js +9 -0
  31. package/dist/infrastructure/filesystem/profile-loader.js +46 -0
  32. package/dist/infrastructure/filesystem/runtime-config-loader.js +29 -0
  33. package/dist/infrastructure/filesystem/skill-loader.js +84 -0
  34. package/dist/infrastructure/filesystem/workspace-capability.js +37 -0
  35. package/dist/infrastructure/observability/runtime-logger.js +16 -0
  36. package/dist/infrastructure/process/agent-tool-executor.js +99 -0
  37. package/dist/infrastructure/process/cli-process.js +49 -0
  38. package/dist/infrastructure/process/interactive-process.js +145 -0
  39. package/dist/infrastructure/process/process-supervisor.js +194 -0
  40. package/dist/infrastructure/process/service.js +252 -0
  41. package/dist/infrastructure/providers/browser-provider.js +2 -0
  42. package/dist/infrastructure/providers/claude-sdk.js +137 -0
  43. package/dist/infrastructure/providers/client-home.js +14 -0
  44. package/dist/infrastructure/providers/fake-browser-provider.js +146 -0
  45. package/dist/infrastructure/providers/ollama-embedder.js +74 -0
  46. package/dist/infrastructure/providers/openai-compatible.js +268 -0
  47. package/dist/infrastructure/providers/playwright-browser-provider.js +380 -0
  48. package/dist/infrastructure/providers/provider-capabilities.js +50 -0
  49. package/dist/infrastructure/providers/provider-invocation.js +184 -0
  50. package/dist/infrastructure/providers/provider-registry.js +77 -0
  51. package/dist/infrastructure/providers/providers.js +134 -0
  52. package/dist/infrastructure/wrappers/wrapper-manager.js +374 -0
  53. package/dist/interfaces/cli/auth-command.js +54 -0
  54. package/dist/interfaces/cli/browser-bridge-command.js +93 -0
  55. package/dist/interfaces/cli/browser-command.js +119 -0
  56. package/dist/interfaces/cli/capture-command.js +125 -0
  57. package/dist/interfaces/cli/client-test-command.js +133 -0
  58. package/dist/interfaces/cli/clients-command.js +129 -0
  59. package/dist/interfaces/cli/command-registry.js +1211 -0
  60. package/dist/interfaces/cli/context-command.js +172 -0
  61. package/dist/interfaces/cli/daily-command.js +131 -0
  62. package/dist/interfaces/cli/governance-command.js +55 -0
  63. package/dist/interfaces/cli/handoff-command.js +35 -0
  64. package/dist/interfaces/cli/help-command.js +207 -0
  65. package/dist/interfaces/cli/idea-command.js +47 -0
  66. package/dist/interfaces/cli/intercept-command.js +317 -0
  67. package/dist/interfaces/cli/layout-command.js +27 -0
  68. package/dist/interfaces/cli/market-command.js +52 -0
  69. package/dist/interfaces/cli/mcp-gateway-command.js +30 -0
  70. package/dist/interfaces/cli/memory-command.js +317 -0
  71. package/dist/interfaces/cli/migrate-command.js +26 -0
  72. package/dist/interfaces/cli/observe-command.js +78 -0
  73. package/dist/interfaces/cli/onboard-command.js +123 -0
  74. package/dist/interfaces/cli/operate-command.js +59 -0
  75. package/dist/interfaces/cli/profiles-command.js +70 -0
  76. package/dist/interfaces/cli/pull-command.js +54 -0
  77. package/dist/interfaces/cli/setup-command.js +143 -0
  78. package/dist/interfaces/cli/tasks-command.js +249 -0
  79. package/dist/main.js +49 -0
  80. package/dist/node-warnings.js +17 -0
  81. package/dist/version.js +8 -0
  82. package/market/mcp/chrome-devtools/ocean.capability.json +28 -0
  83. package/market/mcp/gateway/ocean.capability.json +21 -0
  84. package/market/mcp/obsidian/ocean.capability.json +21 -0
  85. package/market/mcp/ocean/ocean.capability.json +21 -0
  86. package/market/mcp/playwright/ocean.capability.json +23 -0
  87. package/node_modules/@ocean/brain/dist/brain/brain-eval.js +29 -0
  88. package/node_modules/@ocean/brain/dist/brain/brain-markdown.js +226 -0
  89. package/node_modules/@ocean/brain/dist/brain/brain-reindex.js +247 -0
  90. package/node_modules/@ocean/brain/dist/brain/brain-service.js +320 -0
  91. package/node_modules/@ocean/brain/dist/capture/inbox-sync.js +22 -0
  92. package/node_modules/@ocean/brain/dist/evidence/verification-report.js +13 -0
  93. package/node_modules/@ocean/brain/dist/infrastructure/brain/brain-index.js +285 -0
  94. package/node_modules/@ocean/brain/dist/memory/brain-dump.js +145 -0
  95. package/node_modules/@ocean/brain/dist/memory/daily-narrative.js +142 -0
  96. package/node_modules/@ocean/brain/dist/memory/index-sync.js +124 -0
  97. package/node_modules/@ocean/brain/dist/memory/lesson-activation.js +93 -0
  98. package/node_modules/@ocean/brain/dist/memory/lesson-learning.js +188 -0
  99. package/node_modules/@ocean/brain/dist/memory/lessons.js +321 -0
  100. package/node_modules/@ocean/brain/dist/memory/memory-audit.js +100 -0
  101. package/node_modules/@ocean/brain/dist/memory/memory-digest.js +49 -0
  102. package/node_modules/@ocean/brain/dist/memory/memory-harvest.js +345 -0
  103. package/node_modules/@ocean/brain/dist/memory/memory-paths.js +174 -0
  104. package/node_modules/@ocean/brain/dist/memory/memory-review.js +278 -0
  105. package/node_modules/@ocean/brain/dist/memory/memory-trash.js +152 -0
  106. package/node_modules/@ocean/brain/dist/memory/memory-write.js +216 -0
  107. package/node_modules/@ocean/brain/dist/memory/model-narrative.js +129 -0
  108. package/node_modules/@ocean/brain/dist/memory/profile-facts.js +103 -0
  109. package/node_modules/@ocean/brain/dist/memory/session-promotion.js +61 -0
  110. package/node_modules/@ocean/brain/dist/memory/session-summary.js +113 -0
  111. package/node_modules/@ocean/brain/package.json +11 -0
  112. package/node_modules/@ocean/context/dist/context/context-compression.js +66 -0
  113. package/node_modules/@ocean/context/dist/context/context-cost.js +587 -0
  114. package/node_modules/@ocean/context/dist/context/context-ladder.js +363 -0
  115. package/node_modules/@ocean/context/dist/context/context-packet.js +213 -0
  116. package/node_modules/@ocean/context/dist/context/context-references.js +255 -0
  117. package/node_modules/@ocean/context/dist/context/intent-router.js +259 -0
  118. package/node_modules/@ocean/context/dist/context/resource-injection.js +36 -0
  119. package/node_modules/@ocean/context/package.json +11 -0
  120. package/node_modules/@ocean/core/dist/domain/brain/brain-record.js +22 -0
  121. package/node_modules/@ocean/core/dist/domain/brain/record-scope.js +22 -0
  122. package/node_modules/@ocean/core/dist/domain/capabilities/browser-contract.js +64 -0
  123. package/node_modules/@ocean/core/dist/domain/capabilities/capability-contract.js +16 -0
  124. package/node_modules/@ocean/core/dist/domain/capabilities/capability-manifest.js +72 -0
  125. package/node_modules/@ocean/core/dist/domain/context/context-validator.js +5 -0
  126. package/node_modules/@ocean/core/dist/domain/context/context.js +34 -0
  127. package/node_modules/@ocean/core/dist/domain/context/project-id.js +30 -0
  128. package/node_modules/@ocean/core/dist/domain/evidence/evidence.js +16 -0
  129. package/node_modules/@ocean/core/dist/domain/mcp/mcp-contract.js +36 -0
  130. package/node_modules/@ocean/core/dist/domain/mcp/memory-contract.js +41 -0
  131. package/node_modules/@ocean/core/dist/domain/ports/brain-index-port.js +7 -0
  132. package/node_modules/@ocean/core/dist/domain/ports/browser-port.js +8 -0
  133. package/node_modules/@ocean/core/dist/domain/ports/embedder-port.js +2 -0
  134. package/node_modules/@ocean/core/dist/domain/ports/platform-ports.js +2 -0
  135. package/node_modules/@ocean/core/dist/domain/ports/runtime-ports.js +2 -0
  136. package/node_modules/@ocean/core/dist/domain/ports/session-store-port.js +2 -0
  137. package/node_modules/@ocean/core/dist/domain/process/process-events.js +5 -0
  138. package/node_modules/@ocean/core/dist/domain/profiles/profile-distribution.js +19 -0
  139. package/node_modules/@ocean/core/dist/domain/profiles/profile-policy.js +34 -0
  140. package/node_modules/@ocean/core/dist/domain/profiles/profile-selection.js +73 -0
  141. package/node_modules/@ocean/core/dist/domain/profiles/profile-upgrade.js +123 -0
  142. package/node_modules/@ocean/core/dist/domain/profiles/profile-validator.js +5 -0
  143. package/node_modules/@ocean/core/dist/domain/profiles/profile.js +218 -0
  144. package/node_modules/@ocean/core/dist/domain/profiles/runtime-config.js +30 -0
  145. package/node_modules/@ocean/core/dist/domain/providers/provider-capability.js +28 -0
  146. package/node_modules/@ocean/core/dist/domain/providers/provider.js +2 -0
  147. package/node_modules/@ocean/core/dist/domain/redaction/redaction.js +86 -0
  148. package/node_modules/@ocean/core/dist/domain/runs/agent-tools.js +92 -0
  149. package/node_modules/@ocean/core/dist/domain/runs/execution-plan.js +71 -0
  150. package/node_modules/@ocean/core/dist/domain/runs/run-contract.js +38 -0
  151. package/node_modules/@ocean/core/dist/domain/sessions/entry-contract.js +31 -0
  152. package/node_modules/@ocean/core/dist/domain/sessions/handoff.js +78 -0
  153. package/node_modules/@ocean/core/dist/domain/sessions/session.js +58 -0
  154. package/node_modules/@ocean/core/dist/domain/skills/default-skills.js +29 -0
  155. package/node_modules/@ocean/core/dist/domain/skills/skill-validator.js +5 -0
  156. package/node_modules/@ocean/core/dist/domain/skills/skill.js +11 -0
  157. package/node_modules/@ocean/core/dist/domain/tasks/task-id.js +85 -0
  158. package/node_modules/@ocean/core/dist/shared/fs-utils.js +212 -0
  159. package/node_modules/@ocean/core/dist/shared/local-date.js +16 -0
  160. package/node_modules/@ocean/core/dist/shared/paths.js +324 -0
  161. package/node_modules/@ocean/core/dist/shared/project-resolution.js +1065 -0
  162. package/node_modules/@ocean/core/dist/shared/task-reference.js +133 -0
  163. package/node_modules/@ocean/core/package.json +14 -0
  164. package/node_modules/@ocean/loop/dist/handoff/handoff-service.js +133 -0
  165. package/node_modules/@ocean/loop/dist/hooks/lifecycle-hooks.js +15 -0
  166. package/node_modules/@ocean/loop/dist/hooks/session-end-hook.js +183 -0
  167. package/node_modules/@ocean/loop/dist/hooks/session-start-hook.js +237 -0
  168. package/node_modules/@ocean/loop/dist/infrastructure/persistence/session-store.js +690 -0
  169. package/node_modules/@ocean/loop/dist/infrastructure/persistence/session-validator.js +5 -0
  170. package/node_modules/@ocean/loop/dist/loops/iteration-graph.js +147 -0
  171. package/node_modules/@ocean/loop/dist/loops/task-loop-record.js +2 -0
  172. package/node_modules/@ocean/loop/dist/loops/task-loop.js +281 -0
  173. package/node_modules/@ocean/loop/dist/operations/memory-knowledge-operations.js +263 -0
  174. package/node_modules/@ocean/loop/dist/operations/operation-contract.js +227 -0
  175. package/node_modules/@ocean/loop/dist/operations/operation-scope.js +28 -0
  176. package/node_modules/@ocean/loop/dist/operations/project-operations.js +93 -0
  177. package/node_modules/@ocean/loop/dist/operations/record-io.js +65 -0
  178. package/node_modules/@ocean/loop/dist/operations/record-operations.js +45 -0
  179. package/node_modules/@ocean/loop/dist/operations/task-linker.js +40 -0
  180. package/node_modules/@ocean/loop/dist/operations/task-operations.js +279 -0
  181. package/node_modules/@ocean/loop/dist/operations/write-guard.js +157 -0
  182. package/node_modules/@ocean/loop/dist/runs/prompt-assembly.js +112 -0
  183. package/node_modules/@ocean/loop/dist/runs/run-agent.js +425 -0
  184. package/node_modules/@ocean/loop/dist/runs/run-authorization.js +24 -0
  185. package/node_modules/@ocean/loop/dist/runs/tool-loop.js +173 -0
  186. package/node_modules/@ocean/loop/dist/sessions/session-closeout.js +150 -0
  187. package/node_modules/@ocean/loop/dist/sessions/session-pointer.js +133 -0
  188. package/node_modules/@ocean/loop/dist/sessions/session-projects.js +26 -0
  189. package/node_modules/@ocean/loop/dist/sessions/session-retention.js +132 -0
  190. package/node_modules/@ocean/loop/dist/tasks/archive-tasks.js +210 -0
  191. package/node_modules/@ocean/loop/dist/tasks/checkpoint-task.js +31 -0
  192. package/node_modules/@ocean/loop/dist/tasks/claim-task.js +126 -0
  193. package/node_modules/@ocean/loop/dist/tasks/list-tasks.js +113 -0
  194. package/node_modules/@ocean/loop/dist/tasks/new-task.js +42 -0
  195. package/node_modules/@ocean/loop/dist/tasks/renumber-tasks.js +134 -0
  196. package/node_modules/@ocean/loop/dist/tasks/verification-readiness.js +78 -0
  197. package/node_modules/@ocean/loop/dist/tasks/verify-task.js +234 -0
  198. package/node_modules/@ocean/loop/package.json +11 -0
  199. package/node_modules/@ocean/market/dist/adapters/mcp-adapter.js +64 -0
  200. package/node_modules/@ocean/market/dist/adapters/owned-directory.js +89 -0
  201. package/node_modules/@ocean/market/dist/adapters/plugin-adapter.js +39 -0
  202. package/node_modules/@ocean/market/dist/adapters/skill-adapter.js +32 -0
  203. package/node_modules/@ocean/market/dist/capability/adapter.js +2 -0
  204. package/node_modules/@ocean/market/dist/capability/catalog.js +63 -0
  205. package/node_modules/@ocean/market/dist/capability/content-digest.js +40 -0
  206. package/node_modules/@ocean/market/dist/capability/registry.js +81 -0
  207. package/node_modules/@ocean/market/dist/install/default-marketplace.js +47 -0
  208. package/node_modules/@ocean/market/dist/install/lock.js +34 -0
  209. package/node_modules/@ocean/market/dist/install/marketplace-service.js +165 -0
  210. package/node_modules/@ocean/market/dist/mcp/browser-bridge.js +263 -0
  211. package/node_modules/@ocean/market/dist/mcp/mcp-connection.js +67 -0
  212. package/node_modules/@ocean/market/dist/mcp/mcp-gateway.js +317 -0
  213. package/node_modules/@ocean/market/dist/mcp/mcp-launch.js +102 -0
  214. package/node_modules/@ocean/market/dist/mcp/mcp-runner.js +32 -0
  215. package/node_modules/@ocean/market/dist/security/description-scan.js +45 -0
  216. package/node_modules/@ocean/market/dist/security/permissions.js +57 -0
  217. package/node_modules/@ocean/market/dist/skills/core-skill-sync.js +113 -0
  218. package/node_modules/@ocean/market/dist/skills/signal-detection.js +84 -0
  219. package/node_modules/@ocean/market/dist/skills/skill-hub.js +220 -0
  220. package/node_modules/@ocean/market/package.json +11 -0
  221. package/node_modules/@ocean/services/dist/browser/browser-service.js +124 -0
  222. package/node_modules/@ocean/services/dist/browser/browser-session.js +287 -0
  223. package/node_modules/@ocean/services/dist/browser/browser-task-runner.js +70 -0
  224. package/node_modules/@ocean/services/dist/doctor/workspace-doctor.js +547 -0
  225. package/node_modules/@ocean/services/dist/infrastructure/mcp/call-ledger.js +114 -0
  226. package/node_modules/@ocean/services/dist/infrastructure/mcp/mcp-client.js +162 -0
  227. package/node_modules/@ocean/services/dist/infrastructure/mcp/obsidian-server.js +251 -0
  228. package/node_modules/@ocean/services/dist/infrastructure/mcp/ocean-server.js +880 -0
  229. package/node_modules/@ocean/services/dist/infrastructure/mcp/server-version.js +20 -0
  230. package/node_modules/@ocean/services/dist/infrastructure/mcp/tool-registry.js +29 -0
  231. package/node_modules/@ocean/services/dist/obsidian/conflict-log.js +68 -0
  232. package/node_modules/@ocean/services/dist/obsidian/inbox-promotion.js +80 -0
  233. package/node_modules/@ocean/services/dist/obsidian/vault-discovery.js +113 -0
  234. package/node_modules/@ocean/services/dist/obsidian/vault-ingestion.js +77 -0
  235. package/node_modules/@ocean/services/dist/obsidian/vault-sync.js +73 -0
  236. package/node_modules/@ocean/services/dist/obsidian/vault-writer.js +75 -0
  237. package/node_modules/@ocean/services/package.json +11 -0
  238. package/ocean-marketplace.json +415 -0
  239. package/package.json +67 -4
  240. package/skills/core/business-logic/SKILL.md +22 -0
  241. package/skills/core/catch-up/SKILL.md +17 -0
  242. package/skills/core/core-thinking/SKILL.md +11 -0
  243. package/skills/core/design-thinking/SKILL.md +19 -0
  244. package/skills/core/graft/SKILL.md +156 -0
  245. package/skills/core/loop/SKILL.md +20 -0
  246. package/skills/core/session-handoff/SKILL.md +15 -0
  247. package/skills/core/use-browser/SKILL.md +69 -0
  248. package/skills/core/verification/SKILL.md +11 -0
  249. package/skills/index.json +56 -0
  250. package/templates/README.md +4 -0
  251. package/templates/profiles/default.json +51 -0
  252. package/templates/profiles/developer.json +51 -0
  253. package/templates/profiles/reviewer.json +51 -0
  254. package/templates/profiles/strategist.json +51 -0
  255. package/templates/workspace/00-inbox/INBOX.md +7 -0
  256. package/templates/workspace/00-inbox/candidates/.gitkeep +0 -0
  257. package/templates/workspace/00-inbox/native/.gitkeep +0 -0
  258. package/templates/workspace/01-daily/.gitkeep +0 -0
  259. package/templates/workspace/02-personal/CONFLICTS.md +7 -0
  260. package/templates/workspace/02-personal/MEMORY.md +15 -0
  261. package/templates/workspace/02-personal/goals/.gitkeep +0 -0
  262. package/templates/workspace/02-personal/identity/.gitkeep +0 -0
  263. package/templates/workspace/02-personal/preferences/.gitkeep +0 -0
  264. package/templates/workspace/03-professional/.gitkeep +0 -0
  265. package/templates/workspace/04-projects/PROJECTS.md +5 -0
  266. package/templates/workspace/04-projects/_template/docs/.gitkeep +0 -0
  267. package/templates/workspace/04-projects/_template/memory/.gitkeep +0 -0
  268. package/templates/workspace/04-projects/_template/tasks/.gitkeep +0 -0
  269. package/templates/workspace/05-knowledge/KNOWLEDGE.md +8 -0
  270. package/templates/workspace/05-knowledge/decisions/.gitkeep +0 -0
  271. package/templates/workspace/05-knowledge/lessons/.gitkeep +0 -0
  272. package/templates/workspace/06-templates/TEMPLATES.md +25 -0
  273. package/templates/workspace/06-templates/decision.md +8 -0
  274. package/templates/workspace/06-templates/intelligence.md +32 -0
  275. package/templates/workspace/06-templates/project-claude.md +48 -0
  276. package/templates/workspace/06-templates/session.md +38 -0
  277. package/templates/workspace/06-templates/task.md +42 -0
  278. package/templates/workspace/MANIFEST.json +51 -0
  279. package/templates/workspace/bridge/config/CONFIG.md +21 -0
  280. package/templates/workspace/bridge/config/runtime.json +4 -0
  281. package/templates/workspace/charter/core.md +104 -0
  282. package/templates/workspace/charter/policies/README.md +31 -0
  283. package/templates/workspace/charter/policies/claude-code-context.md +90 -0
  284. package/templates/workspace/charter/policies/concise-output.md +29 -0
  285. package/templates/workspace/charter/policies/context.md +91 -0
  286. package/templates/workspace/charter/policies/contracts/task.md +189 -0
  287. package/templates/workspace/charter/policies/git.md +60 -0
  288. package/templates/workspace/charter/policies/graph.md +47 -0
  289. package/templates/workspace/charter/policies/knowledge.md +33 -0
  290. package/templates/workspace/charter/policies/lifecycle.md +119 -0
  291. package/templates/workspace/charter/policies/memory.md +67 -0
  292. package/templates/workspace/charter/policies/minimal-change.md +43 -0
  293. package/templates/workspace/charter/policies/models.md +57 -0
  294. package/templates/workspace/charter/policies/privacy-terms.txt +3 -0
  295. package/templates/workspace/charter/policies/response.md +39 -0
  296. package/templates/workspace/charter/policies/skill-observer.md +44 -0
  297. package/templates/workspace/charter/policies/stack.md +23 -0
  298. package/templates/workspace/charter/policies/task.md +139 -0
  299. package/templates/workspace/charter/policies/verification.md +29 -0
  300. package/templates/workspace/sessions/.gitkeep +0 -0
@@ -0,0 +1,133 @@
1
+ import { access, readdir } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { parseTaskId, taskIdSpellings } from "../domain/tasks/task-id.js";
4
+ import { FRONTMATTER_READ_BYTES, readFrontmatterFile } from "./fs-utils.js";
5
+ import { isWorkspaceProject, oceanRoot, PROJECTS_DIR, projectSubdir, resolveWithin, } from "./paths.js";
6
+ import { folderProject, projectForPrefix, projectsWithTasks, } from "./project-resolution.js";
7
+ // Archive namespace per project; the workspace project archives under "Ocean".
8
+ export const WORKSPACE_ARCHIVE_NAMESPACE = "Ocean";
9
+ export const projectArchiveName = (project) => isWorkspaceProject(project) ? WORKSPACE_ARCHIVE_NAMESPACE : project;
10
+ const exists = (file) => access(file).then(() => true, () => false);
11
+ // One project's task by exact id: live first, then archive/<namespace>/<id>.
12
+ async function findInProject(projectsDir, project, id) {
13
+ const tasks = resolveWithin(projectsDir, projectSubdir(projectsDir, project), "tasks");
14
+ for (const spelling of taskIdSpellings(id)) {
15
+ const live = resolveWithin(tasks, spelling, "task.md");
16
+ if (await exists(live))
17
+ return { projectId: project, id: spelling, file: live, archived: false };
18
+ }
19
+ for (const spelling of taskIdSpellings(id)) {
20
+ const file = resolveWithin(tasks, "archive", projectArchiveName(project), spelling, "task.md");
21
+ if (await exists(file))
22
+ return { projectId: project, id: spelling, file, archived: true };
23
+ }
24
+ return null;
25
+ }
26
+ async function directories(parent) {
27
+ try {
28
+ return (await readdir(parent, { withFileTypes: true })).filter((entry) => entry.isDirectory());
29
+ }
30
+ catch {
31
+ return [];
32
+ }
33
+ }
34
+ // Every task folder of a project, live and archived, as [folder name, task.md, archived].
35
+ export async function taskFolders(projectsDir, project) {
36
+ const tasks = resolveWithin(projectsDir, projectSubdir(projectsDir, project), "tasks");
37
+ const found = [];
38
+ for (const entry of await directories(tasks)) {
39
+ if (entry.name !== "archive")
40
+ found.push([entry.name, path.join(tasks, entry.name, "task.md"), false]);
41
+ }
42
+ const archive = path.join(tasks, "archive");
43
+ for (const entry of await directories(archive)) {
44
+ // archive/<namespace>/<id>, or a flat archive/<id>.
45
+ if (await exists(path.join(archive, entry.name, "task.md")))
46
+ found.push([entry.name, path.join(archive, entry.name, "task.md"), true]);
47
+ else
48
+ for (const inner of await directories(path.join(archive, entry.name)))
49
+ found.push([
50
+ inner.name,
51
+ path.join(archive, entry.name, inner.name, "task.md"),
52
+ true,
53
+ ]);
54
+ }
55
+ return found;
56
+ }
57
+ // Tasks renumbered by `ocean tasks renumber` keep their old id in `legacyId`, so a reference
58
+ // written before the rename (a commit message, a handoff) still finds its task. Only the
59
+ // frontmatter head of each record is read.
60
+ async function findByLegacyId(projectsDir, projects, wanted) {
61
+ const hits = [];
62
+ for (const project of projects) {
63
+ for (const [id, file, archived] of await taskFolders(projectsDir, project)) {
64
+ const record = await readFrontmatterFile(file, FRONTMATTER_READ_BYTES);
65
+ const legacy = record?.fields.legacyId
66
+ ? parseTaskId(record.fields.legacyId)
67
+ : null;
68
+ if (legacy?.prefix === wanted.prefix && legacy.number === wanted.number)
69
+ hits.push({ projectId: project, id, file, archived });
70
+ }
71
+ }
72
+ return hits;
73
+ }
74
+ // Resolves a task reference to the task that holds it (T-256). A prefixed id goes to the
75
+ // project that owns the prefix. A bare T id looks in the current project, then (unless told to
76
+ // stay) in whichever project holds it, then in `legacyId` fields. Ambiguity is an error, never
77
+ // a guess.
78
+ export async function resolveTaskReference(ref, scope = {}) {
79
+ const parsed = parseTaskId(ref);
80
+ if (!parsed)
81
+ throw new Error(`'${ref}' is not a task id`);
82
+ // As typed, prefix upper-cased: the spelling tried first (T-0003 may be a real folder name).
83
+ const canonical = `${parsed.prefix}${ref.slice(ref.indexOf("-"))}`;
84
+ const projectsDir = path.join(scope.root ?? oceanRoot(), PROJECTS_DIR);
85
+ const acrossProjects = scope.acrossProjects !== false && !scope.explicit;
86
+ const current = scope.projectId ?? (scope.cwd ? await folderProject(scope.cwd) : null);
87
+ const owner = await projectForPrefix(parsed.prefix);
88
+ if (owner) {
89
+ if (current &&
90
+ owner !== current &&
91
+ (scope.explicit || scope.acrossProjects === false))
92
+ throw new Error(`${canonical} belongs to ${owner}, not ${current}`);
93
+ const hit = await findInProject(projectsDir, owner, canonical);
94
+ if (hit)
95
+ return hit;
96
+ throw new Error(`${canonical} is not a task in project ${owner}`);
97
+ }
98
+ if (parsed.prefix !== "T")
99
+ throw new Error(`no project uses prefix ${parsed.prefix}`);
100
+ if (current) {
101
+ const hit = await findInProject(projectsDir, current, canonical);
102
+ if (hit)
103
+ return hit;
104
+ }
105
+ if (scope.explicit && current) {
106
+ const legacy = await findByLegacyId(projectsDir, [current], parsed);
107
+ if (legacy.length === 1)
108
+ return legacy[0];
109
+ throw new Error(`${canonical} is not a task in project ${current}`);
110
+ }
111
+ if (acrossProjects) {
112
+ const projects = await projectsWithTasks();
113
+ for (const archived of [false, true]) {
114
+ const holders = [];
115
+ for (const project of projects) {
116
+ const hit = await findInProject(projectsDir, project, canonical);
117
+ if (hit && hit.archived === archived)
118
+ holders.push(hit);
119
+ }
120
+ if (holders.length === 1)
121
+ return holders[0];
122
+ if (holders.length > 1)
123
+ throw new Error(`${canonical} exists in ${holders.map((holder) => holder.projectId).join(", ")}; pass --project <id>`);
124
+ }
125
+ }
126
+ const legacy = await findByLegacyId(projectsDir, acrossProjects ? await projectsWithTasks() : current ? [current] : [], parsed);
127
+ if (legacy.length === 1)
128
+ return legacy[0];
129
+ if (legacy.length > 1)
130
+ throw new Error(`${canonical} was the old id of tasks in ${legacy.map((hit) => hit.projectId).join(", ")}; pass --project <id>`);
131
+ throw new Error(`${canonical} is not a task in any project`);
132
+ }
133
+ //# sourceMappingURL=task-reference.js.map
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "@ocean/core",
3
+ "version": "1.2.0",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "exports": {
7
+ "./domain/*.js": {
8
+ "import": "./dist/domain/*.js"
9
+ },
10
+ "./shared/*.js": {
11
+ "import": "./dist/shared/*.js"
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,133 @@
1
+ import { execFile as execFileCallback } from "node:child_process";
2
+ import { randomUUID } from "node:crypto";
3
+ import { readFile } from "node:fs/promises";
4
+ import { promisify } from "node:util";
5
+ import { compactHandoff, validateHandoff, } from "@ocean/core/domain/sessions/handoff.js";
6
+ import { engineRoot } from "@ocean/core/shared/paths.js";
7
+ import { resolveTaskReference } from "@ocean/core/shared/task-reference.js";
8
+ const execFile = promisify(execFileCallback);
9
+ const maxContentBytes = 16_000;
10
+ export async function createHandoff(input, openStore) {
11
+ const store = await openStore();
12
+ try {
13
+ return await createHandoffWithStore(store, input);
14
+ }
15
+ finally {
16
+ store.close();
17
+ }
18
+ }
19
+ export async function createHandoffWithStore(store, input) {
20
+ const session = input.sessionId ? store.get(input.sessionId) : null;
21
+ if (input.sessionId && !session)
22
+ throw new Error(`Session not found: ${input.sessionId}`);
23
+ // The task is found from where the session worked: its folder decides the project.
24
+ const task = input.task === undefined && input.taskId
25
+ ? await getTask(input.taskId, {
26
+ cwd: session?.workingDirectory ?? process.cwd(),
27
+ })
28
+ : (input.task ?? null);
29
+ const commit = await currentCommit();
30
+ const now = new Date().toISOString();
31
+ const content = (input.content ?? task?.body ?? "").slice(0, maxContentBytes);
32
+ const handoff = validateHandoff({
33
+ handoffId: `handoff-${randomUUID()}`,
34
+ taskId: task?.id ?? session?.taskId ?? null,
35
+ title: input.title ?? task?.title ?? session?.title ?? "Ocean session handoff",
36
+ objective: task?.objective ??
37
+ "Continue the selected Ocean session with bounded context.",
38
+ state: task?.state ?? session?.status ?? "paused",
39
+ profileId: input.profileId ?? session?.profile ?? "",
40
+ profileIdentity: session?.profileIdentity ?? "",
41
+ sourceSessionId: session?.sessionId ?? null,
42
+ projectId: task?.project ?? input.projectId ?? session?.projectId ?? null,
43
+ provider: input.provider ?? session?.provider ?? "",
44
+ parentSessionId: session?.parentSessionId ?? null,
45
+ decisions: [],
46
+ changedFiles: input.changedFiles ?? [],
47
+ commit,
48
+ verification: input.verification ?? [],
49
+ notProven: input.notProven ?? [],
50
+ blocked: input.blocked ?? [],
51
+ permissions: { profile: input.profileId ?? session?.profile ?? null },
52
+ contextManifest: {
53
+ bytes: session?.contextBytes ?? 0,
54
+ hash: session?.contextHash ?? null,
55
+ mode: "bounded",
56
+ },
57
+ nextAction: input.nextAction ??
58
+ session?.nextAction ??
59
+ "Verify the current state before changing files.",
60
+ sourceSummaryPath: input.sourceSummaryPath ?? null,
61
+ content,
62
+ contentBytes: Buffer.byteLength(content),
63
+ createdAt: now,
64
+ updatedAt: now,
65
+ });
66
+ store.saveHandoff(handoff);
67
+ if (session)
68
+ store.updateHandoff(session.sessionId, handoff.handoffId, handoff.nextAction);
69
+ return handoff;
70
+ }
71
+ export async function getHandoff(handoffId, openStore, maxBytes = 8_000) {
72
+ const store = await openStore();
73
+ try {
74
+ const handoff = store.getHandoff(handoffId);
75
+ if (!handoff)
76
+ throw new Error(`Handoff not found: ${handoffId}`);
77
+ return {
78
+ ...handoff,
79
+ compactContext: compactHandoff(validateHandoff(handoff), maxBytes),
80
+ };
81
+ }
82
+ finally {
83
+ store.close();
84
+ }
85
+ }
86
+ export async function listHandoffs(openStore, taskId, projectId) {
87
+ const store = await openStore();
88
+ try {
89
+ return store
90
+ .listHandoffs(taskId, projectId)
91
+ .map(({ content: _content, ...handoff }) => handoff);
92
+ }
93
+ finally {
94
+ store.close();
95
+ }
96
+ }
97
+ export async function getTask(id, scope = {}) {
98
+ const location = await resolveTaskReference(id, scope);
99
+ const file = await readFile(location.file, "utf8");
100
+ const end = file.indexOf("\n---", 4);
101
+ const frontmatter = file
102
+ .slice(0, end < 0 ? 0 : end)
103
+ .split("\n")
104
+ .slice(1);
105
+ const fields = Object.fromEntries(frontmatter
106
+ .filter((line) => /^[\w-]+:/.test(line))
107
+ .map((line) => {
108
+ const index = line.indexOf(":");
109
+ return [line.slice(0, index), line.slice(index + 1).trim()];
110
+ }));
111
+ const body = file.slice(end < 0 ? 0 : end + 5);
112
+ const objective = body.match(/(?:^|\n)## Objective\n\n([\s\S]*?)(?=\n## |$)/)?.[1]?.trim() ??
113
+ "";
114
+ return {
115
+ id: fields.id ?? location.id,
116
+ project: location.projectId,
117
+ title: fields.title ?? location.id,
118
+ state: fields.state ?? "unknown",
119
+ goal: objective || fields.goal || "",
120
+ objective: objective || fields.requirement || fields.goal || "",
121
+ body,
122
+ updatedAt: fields.updated ?? fields.updated_at ?? "",
123
+ };
124
+ }
125
+ async function currentCommit() {
126
+ try {
127
+ return ((await execFile("git", ["-C", engineRoot(), "rev-parse", "HEAD"])).stdout.trim() || null);
128
+ }
129
+ catch {
130
+ return null;
131
+ }
132
+ }
133
+ //# sourceMappingURL=handoff-service.js.map
@@ -0,0 +1,15 @@
1
+ const hooks = new Map();
2
+ export function registerHook(event, hook) {
3
+ const registered = hooks.get(event) ?? new Set();
4
+ registered.add(hook);
5
+ hooks.set(event, registered);
6
+ return () => registered.delete(hook);
7
+ }
8
+ export async function emitHook(event, payload) {
9
+ for (const hook of hooks.get(event) ?? [])
10
+ await hook(event, payload);
11
+ }
12
+ export function clearHooks() {
13
+ hooks.clear();
14
+ }
15
+ //# sourceMappingURL=lifecycle-hooks.js.map
@@ -0,0 +1,183 @@
1
+ import { createReadStream } from "node:fs";
2
+ import { stat } from "node:fs/promises";
3
+ import readline from "node:readline";
4
+ import { finalizeSession } from "../sessions/session-closeout.js";
5
+ import { sessionProjectId } from "../sessions/session-projects.js";
6
+ // Gemini CLI, Qwen Code and Cursor fire a session-end event too (T-307); their transcript
7
+ // formats are not read, so their sessions are recorded from metadata alone.
8
+ export const SESSION_END_CLIENTS = [
9
+ "claude",
10
+ "codex",
11
+ "gemini",
12
+ "qwen",
13
+ "cursor",
14
+ ];
15
+ const TRANSCRIPT_CLIENTS = ["claude", "codex"];
16
+ export function isSessionEndClient(value) {
17
+ return SESSION_END_CLIENTS.includes(value);
18
+ }
19
+ const MAX_TRANSCRIPT_BYTES = 8 * 1024 * 1024;
20
+ const MAX_EVENTS = 30;
21
+ const MAX_EVENT_CHARS = 800;
22
+ function safeSlice(value, max) {
23
+ return value.replace(/\s+/g, " ").trim().slice(0, max);
24
+ }
25
+ function textFromContent(content) {
26
+ if (typeof content === "string")
27
+ return content;
28
+ if (!Array.isArray(content))
29
+ return "";
30
+ return content
31
+ .filter((block) => block &&
32
+ typeof block === "object" &&
33
+ block.type === "text" &&
34
+ typeof block.text === "string")
35
+ .map((block) => block.text)
36
+ .join(" ");
37
+ }
38
+ // Codex prepends context blocks (AGENTS.md, environment and plugin lists) as
39
+ // user messages; they are injected text, not something the user typed.
40
+ function isInjectedCodexText(text) {
41
+ return text.startsWith("<") || text.startsWith("# AGENTS.md");
42
+ }
43
+ // One transcript line → the turn it holds, or null. Claude Code writes
44
+ // {type, message:{role, content}}; Codex writes {type:"response_item",
45
+ // payload:{type:"message", role, content:[{type:"input_text"|"output_text", text}]}}.
46
+ function transcriptTurn(record, client) {
47
+ if (client === "codex") {
48
+ const payload = record.payload;
49
+ if (record.type !== "response_item" || payload?.type !== "message")
50
+ return null;
51
+ if (payload.role !== "user" && payload.role !== "assistant")
52
+ return null;
53
+ if (!Array.isArray(payload.content))
54
+ return null;
55
+ const text = payload.content
56
+ .filter((block) => block &&
57
+ typeof block === "object" &&
58
+ typeof block.text === "string" &&
59
+ !(payload.role === "user" &&
60
+ isInjectedCodexText(block.text)))
61
+ .map((block) => block.text)
62
+ .join(" ");
63
+ return { role: payload.role, text };
64
+ }
65
+ if (record.type !== "user" && record.type !== "assistant")
66
+ return null;
67
+ const message = record.message;
68
+ return { role: record.type, text: textFromContent(message?.content) };
69
+ }
70
+ /**
71
+ * Extracts a bounded set of plain-text user/assistant turns from a Claude
72
+ * Code transcript (JSONL: one record per line, {type, message:{role,
73
+ * content}}) or a Codex rollout (see transcriptTurn). Skips tool calls, thinking blocks, and any non-text content —
74
+ * this feeds the same human-narrative pipeline intercepted CLI sessions use
75
+ * (see session-closeout.ts), not a full transcript mirror. Returns the most
76
+ * recent MAX_EVENTS turns; never throws (a missing/oversized/malformed
77
+ * transcript just yields no events, and the caller still closes out the
78
+ * session on its title alone).
79
+ */
80
+ export async function readTranscriptEvents(transcriptPath, client = "claude") {
81
+ try {
82
+ const info = await stat(transcriptPath);
83
+ if (info.size > MAX_TRANSCRIPT_BYTES)
84
+ return [];
85
+ }
86
+ catch {
87
+ return [];
88
+ }
89
+ const events = [];
90
+ try {
91
+ const rl = readline.createInterface({
92
+ input: createReadStream(transcriptPath, { encoding: "utf8" }),
93
+ crlfDelay: Infinity,
94
+ });
95
+ for await (const line of rl) {
96
+ if (!line.trim())
97
+ continue;
98
+ let record;
99
+ try {
100
+ record = JSON.parse(line);
101
+ }
102
+ catch {
103
+ continue;
104
+ }
105
+ if (!record || typeof record !== "object")
106
+ continue;
107
+ const turn = transcriptTurn(record, client);
108
+ if (!turn)
109
+ continue;
110
+ const text = safeSlice(turn.text, MAX_EVENT_CHARS);
111
+ if (!text)
112
+ continue;
113
+ events.push({
114
+ type: turn.role === "user" ? "user_input" : "provider_output",
115
+ data: text,
116
+ });
117
+ }
118
+ rl.close();
119
+ }
120
+ catch {
121
+ return [];
122
+ }
123
+ return events.slice(-MAX_EVENTS);
124
+ }
125
+ /**
126
+ * SessionEnd counterpart to claudeSessionStartHook. Unlike SessionStart
127
+ * (context injection only, no session-store write), this registers the
128
+ * Claude Code session in sessions.sqlite the first time it is seen, replays
129
+ * its transcript as bounded user_input/provider_output events, and runs it
130
+ * through the same finalizeSession pipeline as `ocean run` and `ocean
131
+ * intercept` sessions — producing the summary, daily narrative, and
132
+ * brain/00-inbox/brain-dump/ entry. Reuses the sessionId Claude Code itself
133
+ * assigned, so a hook re-run for the same session (e.g. `/clear` followed by
134
+ * another SessionEnd) is idempotent via finalizeSession's own closeout guard.
135
+ * Best-effort throughout: any failure is swallowed so a broken hook can never
136
+ * block the user's actual session from ending.
137
+ */
138
+ export async function claudeSessionEndHook(payload, openStore, client = "claude") {
139
+ const sessionId = payload.session_id;
140
+ if (!sessionId)
141
+ return;
142
+ const roots = payload.workspace_roots;
143
+ const cwd = payload.cwd ||
144
+ (Array.isArray(roots) && typeof roots[0] === "string" ? roots[0] : "") ||
145
+ process.cwd();
146
+ const store = await openStore();
147
+ try {
148
+ let session = store.get(sessionId);
149
+ if (!session) {
150
+ store.create({
151
+ sessionId,
152
+ provider: client,
153
+ providerSessionId: sessionId,
154
+ parentSessionId: null,
155
+ profile: `desktop:${client}`,
156
+ profileIdentity: "",
157
+ workingDirectory: cwd,
158
+ resumeData: null,
159
+ projectId: await sessionProjectId(cwd),
160
+ });
161
+ store.updateStatus(sessionId, "running");
162
+ session = store.get(sessionId);
163
+ }
164
+ if (!session || session.closedAt)
165
+ return;
166
+ const events = payload.transcript_path && TRANSCRIPT_CLIENTS.includes(client)
167
+ ? await readTranscriptEvents(payload.transcript_path, client)
168
+ : [];
169
+ for (const event of events)
170
+ store.appendEvent(sessionId, event.type, event.data);
171
+ store.appendEvent(sessionId, "process_exit", JSON.stringify({ exitCode: 0 }));
172
+ if (session.status !== "completed")
173
+ store.updateStatus(sessionId, "completed");
174
+ await finalizeSession(store, sessionId, { exitCode: 0 });
175
+ }
176
+ catch {
177
+ // Best-effort: never surface a hook failure to the user's session end.
178
+ }
179
+ finally {
180
+ store.close();
181
+ }
182
+ }
183
+ //# sourceMappingURL=session-end-hook.js.map