ndomo 0.1.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 (247) hide show
  1. package/.bun-version +1 -0
  2. package/.dockerignore +79 -0
  3. package/.editorconfig +18 -0
  4. package/.env.example +19 -0
  5. package/.github/CODEOWNERS +8 -0
  6. package/.github/ISSUE_TEMPLATE/bug_report.yml +62 -0
  7. package/.github/ISSUE_TEMPLATE/config.yml +2 -0
  8. package/.github/ISSUE_TEMPLATE/feature_request.yml +34 -0
  9. package/.github/dependabot.yml +36 -0
  10. package/.github/pull_request_template.md +24 -0
  11. package/.github/release.yml +30 -0
  12. package/.github/workflows/gitleaks.yml +28 -0
  13. package/.github/workflows/release-please.yml +27 -0
  14. package/.github/workflows/smoke.yml +29 -0
  15. package/.husky/commit-msg +1 -0
  16. package/CHANGELOG.md +114 -0
  17. package/Dockerfile +32 -0
  18. package/README.es.md +174 -0
  19. package/README.md +187 -0
  20. package/agents/chronicler.md +98 -0
  21. package/agents/ci-smith.md +136 -0
  22. package/agents/craftsman.md +341 -0
  23. package/agents/deploy-smith.md +138 -0
  24. package/agents/foreman.md +377 -0
  25. package/agents/go-smith.md +164 -0
  26. package/agents/guild.md +188 -0
  27. package/agents/inspector.md +83 -0
  28. package/agents/js-smith.md +127 -0
  29. package/agents/ops-scout.md +173 -0
  30. package/agents/painter.md +200 -0
  31. package/agents/python-smith.md +120 -0
  32. package/agents/ranger.md +307 -0
  33. package/agents/release-smith.md +165 -0
  34. package/agents/rust-smith.md +159 -0
  35. package/agents/sage.md +178 -0
  36. package/agents/scout.md +144 -0
  37. package/agents/scribe.md +156 -0
  38. package/agents/smith.md +201 -0
  39. package/agents/vue-smith.md +155 -0
  40. package/agents/warden.md +216 -0
  41. package/agents/zig-smith.md +156 -0
  42. package/bin/ndomo-analyses.ts +4 -0
  43. package/bin/ndomo-status.ts +4 -0
  44. package/biome.json +57 -0
  45. package/bun.lock +514 -0
  46. package/commitlint.config.js +3 -0
  47. package/config/ndomo.config.json +258 -0
  48. package/config/ndomo.schema.json +166 -0
  49. package/docs/agents.md +375 -0
  50. package/docs/bugs/plan-create-orphan-fk.md +131 -0
  51. package/docs/bugs/task_create_batch-order-index-collision.md +158 -0
  52. package/docs/configuration.md +276 -0
  53. package/docs/database.md +364 -0
  54. package/docs/features/feature-flexible-builder-v1.md +724 -0
  55. package/docs/features/feature-flexible-builder-v2.md +882 -0
  56. package/docs/features/feature-flexible-builder.md +974 -0
  57. package/docs/http-server.md +244 -0
  58. package/docs/installation.md +259 -0
  59. package/docs/integrations.md +129 -0
  60. package/docs/operations/anti-pattern-sub-agent-verify-2026-06-21.md +32 -0
  61. package/docs/operations/audit-v1.md +417 -0
  62. package/docs/operations/audit-v2.md +197 -0
  63. package/docs/operations/audit-v3.md +306 -0
  64. package/docs/operations/db-optimize-foundations.md +123 -0
  65. package/docs/operations/verify-gate-architecture.md +82 -0
  66. package/docs/workflows.md +448 -0
  67. package/opencode.json +5 -0
  68. package/package.json +65 -0
  69. package/release-please-config.json +11 -0
  70. package/scripts/dev-bust-cache.sh +164 -0
  71. package/scripts/install.sh +688 -0
  72. package/scripts/smoke-e2e.ts +704 -0
  73. package/scripts/smoke-hot.ts +417 -0
  74. package/scripts/smoke-http.sh +228 -0
  75. package/scripts/smoke-v4.ts +256 -0
  76. package/scripts/smoke-v5.ts +397 -0
  77. package/scripts/smoke.sh +9 -0
  78. package/scripts/uninstall.sh +224 -0
  79. package/skills/api-security-best-practices/SKILL.md +915 -0
  80. package/skills/bash-scripting/SKILL.md +201 -0
  81. package/skills/bun/SKILL.md +313 -0
  82. package/skills/cavecrew/SKILL.md +82 -0
  83. package/skills/caveman/SKILL.md +74 -0
  84. package/skills/caveman-review/README.md +33 -0
  85. package/skills/caveman-review/SKILL.md +55 -0
  86. package/skills/find-skills/SKILL.md +142 -0
  87. package/skills/frontend-design/LICENSE.txt +177 -0
  88. package/skills/frontend-design/SKILL.md +55 -0
  89. package/skills/golang-patterns/SKILL.md +674 -0
  90. package/skills/golang-security/SKILL.md +185 -0
  91. package/skills/golang-security/evals/evals.json +595 -0
  92. package/skills/golang-security/references/architecture.md +268 -0
  93. package/skills/golang-security/references/checklist.md +80 -0
  94. package/skills/golang-security/references/cookies.md +200 -0
  95. package/skills/golang-security/references/cryptography.md +424 -0
  96. package/skills/golang-security/references/filesystem.md +285 -0
  97. package/skills/golang-security/references/injection.md +315 -0
  98. package/skills/golang-security/references/logging.md +163 -0
  99. package/skills/golang-security/references/memory-safety.md +241 -0
  100. package/skills/golang-security/references/network.md +253 -0
  101. package/skills/golang-security/references/secrets.md +189 -0
  102. package/skills/golang-security/references/third-party.md +159 -0
  103. package/skills/golang-security/references/threat-modeling.md +189 -0
  104. package/skills/golang-testing/SKILL.md +720 -0
  105. package/skills/grill-me/SKILL.md +7 -0
  106. package/skills/javascript-testing-patterns/SKILL.md +537 -0
  107. package/skills/javascript-testing-patterns/references/advanced-testing-patterns.md +513 -0
  108. package/skills/modern-javascript-patterns/SKILL.md +43 -0
  109. package/skills/modern-javascript-patterns/references/advanced-patterns.md +487 -0
  110. package/skills/modern-javascript-patterns/references/details.md +457 -0
  111. package/skills/python-anti-patterns/SKILL.md +349 -0
  112. package/skills/python-design-patterns/SKILL.md +85 -0
  113. package/skills/python-design-patterns/references/details.md +353 -0
  114. package/skills/python-error-handling/SKILL.md +193 -0
  115. package/skills/python-error-handling/references/details.md +171 -0
  116. package/skills/python-testing-patterns/SKILL.md +278 -0
  117. package/skills/python-testing-patterns/references/advanced-patterns.md +411 -0
  118. package/skills/python-testing-patterns/references/details.md +349 -0
  119. package/skills/rust-patterns/SKILL.md +500 -0
  120. package/skills/rust-testing/SKILL.md +501 -0
  121. package/skills/security-review/SKILL.md +504 -0
  122. package/skills/security-review/cloud-infrastructure-security.md +361 -0
  123. package/skills/vue-best-practices/SKILL.md +154 -0
  124. package/skills/vue-best-practices/references/animation-class-based-technique.md +254 -0
  125. package/skills/vue-best-practices/references/animation-state-driven-technique.md +291 -0
  126. package/skills/vue-best-practices/references/component-async.md +97 -0
  127. package/skills/vue-best-practices/references/component-data-flow.md +307 -0
  128. package/skills/vue-best-practices/references/component-fallthrough-attrs.md +174 -0
  129. package/skills/vue-best-practices/references/component-keep-alive.md +137 -0
  130. package/skills/vue-best-practices/references/component-slots.md +216 -0
  131. package/skills/vue-best-practices/references/component-suspense.md +228 -0
  132. package/skills/vue-best-practices/references/component-teleport.md +108 -0
  133. package/skills/vue-best-practices/references/component-transition-group.md +128 -0
  134. package/skills/vue-best-practices/references/component-transition.md +125 -0
  135. package/skills/vue-best-practices/references/composables.md +290 -0
  136. package/skills/vue-best-practices/references/directives.md +162 -0
  137. package/skills/vue-best-practices/references/perf-avoid-component-abstraction-in-lists.md +159 -0
  138. package/skills/vue-best-practices/references/perf-v-once-v-memo-directives.md +182 -0
  139. package/skills/vue-best-practices/references/perf-virtualize-large-lists.md +187 -0
  140. package/skills/vue-best-practices/references/plugins.md +166 -0
  141. package/skills/vue-best-practices/references/reactivity.md +344 -0
  142. package/skills/vue-best-practices/references/render-functions.md +201 -0
  143. package/skills/vue-best-practices/references/sfc.md +310 -0
  144. package/skills/vue-best-practices/references/state-management.md +135 -0
  145. package/skills/vue-best-practices/references/updated-hook-performance.md +187 -0
  146. package/skills/vue-pinia-best-practices/SKILL.md +21 -0
  147. package/skills/vue-pinia-best-practices/reference/pinia-no-active-pinia-error.md +248 -0
  148. package/skills/vue-pinia-best-practices/reference/pinia-setup-store-return-all-state.md +227 -0
  149. package/skills/vue-pinia-best-practices/reference/pinia-store-destructuring-breaks-reactivity.md +193 -0
  150. package/skills/vue-pinia-best-practices/reference/state-url-for-ephemeral-filters.md +238 -0
  151. package/skills/vue-pinia-best-practices/reference/state-use-pinia-for-large-apps.md +262 -0
  152. package/skills/vue-pinia-best-practices/reference/store-method-binding-parentheses.md +191 -0
  153. package/skills/zig-0.16/SKILL.md +840 -0
  154. package/skills/zig-0.16/scripts/check-zig-version.sh +21 -0
  155. package/src/cli/analyses.ts +280 -0
  156. package/src/cli/index.ts +108 -0
  157. package/src/cli/serve.ts +192 -0
  158. package/src/cli/smoke.ts +131 -0
  159. package/src/cli/status.test.ts +204 -0
  160. package/src/cli/status.ts +263 -0
  161. package/src/cli/vacuum.test.ts +82 -0
  162. package/src/cli/vacuum.ts +96 -0
  163. package/src/config/schema.test.ts +88 -0
  164. package/src/config/schema.ts +64 -0
  165. package/src/db/analyses-migration.test.ts +210 -0
  166. package/src/db/analyses.test.ts +466 -0
  167. package/src/db/analyses.ts +375 -0
  168. package/src/db/auto-checkpoint.ts +131 -0
  169. package/src/db/client.test.ts +129 -0
  170. package/src/db/client.ts +55 -0
  171. package/src/db/fts-escape.ts +20 -0
  172. package/src/db/incidents.test.ts +201 -0
  173. package/src/db/incidents.ts +93 -0
  174. package/src/db/index.ts +86 -0
  175. package/src/db/migrations-v13.test.ts +141 -0
  176. package/src/db/migrations-v8.test.ts +301 -0
  177. package/src/db/migrations.ts +147 -0
  178. package/src/db/plan-archive.test.ts +180 -0
  179. package/src/db/plan-archive.ts +274 -0
  180. package/src/db/plan-create.test.ts +276 -0
  181. package/src/db/plan-create.ts +78 -0
  182. package/src/db/plan-files.test.ts +289 -0
  183. package/src/db/plan-update-status.ts +287 -0
  184. package/src/db/plans.test.ts +490 -0
  185. package/src/db/plans.ts +534 -0
  186. package/src/db/resolve-project-dir.test.ts +143 -0
  187. package/src/db/resolve-project-dir.ts +75 -0
  188. package/src/db/rollbacks.test.ts +150 -0
  189. package/src/db/rollbacks.ts +67 -0
  190. package/src/db/schema.ts +907 -0
  191. package/src/db/sessions.test.ts +80 -0
  192. package/src/db/sessions.ts +135 -0
  193. package/src/db/shutdown.test.ts +147 -0
  194. package/src/db/shutdown.ts +45 -0
  195. package/src/db/tasks.test.ts +921 -0
  196. package/src/db/tasks.ts +747 -0
  197. package/src/db/types.ts +619 -0
  198. package/src/http/__tests__/auth.test.ts +196 -0
  199. package/src/http/__tests__/routes.test.ts +465 -0
  200. package/src/http/__tests__/sse.test.ts +317 -0
  201. package/src/http/auth.ts +72 -0
  202. package/src/http/middleware/cors.ts +53 -0
  203. package/src/http/middleware/security-headers.ts +21 -0
  204. package/src/http/routes/events.ts +112 -0
  205. package/src/http/routes/health.ts +51 -0
  206. package/src/http/routes/plans.ts +66 -0
  207. package/src/http/routes/sessions.ts +50 -0
  208. package/src/http/routes/tasks.ts +60 -0
  209. package/src/http/server.ts +95 -0
  210. package/src/http/sse.ts +116 -0
  211. package/src/index.ts +37 -0
  212. package/src/lib.ts +65 -0
  213. package/src/mem/scoped.ts +65 -0
  214. package/src/orchestrator/background.test.ts +268 -0
  215. package/src/orchestrator/background.ts +293 -0
  216. package/src/orchestrator/memory-hook.ts +182 -0
  217. package/src/orchestrator/reconciler.ts +123 -0
  218. package/src/orchestrator/scheduler.test.ts +300 -0
  219. package/src/orchestrator/scheduler.ts +243 -0
  220. package/src/plugin.test.ts +2574 -0
  221. package/src/plugin.ts +1690 -0
  222. package/src/sdk/client.ts +66 -0
  223. package/src/worktrees/manager.ts +236 -0
  224. package/src/worktrees/state.ts +87 -0
  225. package/tests/integration/ranger-flow.test.ts +257 -0
  226. package/tools/analysis_archive.ts +28 -0
  227. package/tools/analysis_create.ts +55 -0
  228. package/tools/analysis_get.ts +33 -0
  229. package/tools/analysis_link_plan.ts +44 -0
  230. package/tools/analysis_list.ts +48 -0
  231. package/tools/analysis_search.ts +36 -0
  232. package/tools/analysis_update.ts +44 -0
  233. package/tools/plan_approve.ts +31 -0
  234. package/tools/plan_create.ts +58 -0
  235. package/tools/plan_get.ts +40 -0
  236. package/tools/plan_list.ts +37 -0
  237. package/tools/plan_search.ts +34 -0
  238. package/tools/plan_update_status.ts +71 -0
  239. package/tools/session_checkpoint.ts +31 -0
  240. package/tools/session_end.ts +26 -0
  241. package/tools/session_start.ts +43 -0
  242. package/tools/task_create_batch.ts +70 -0
  243. package/tools/task_list.ts +35 -0
  244. package/tools/task_next_for_agent.ts +30 -0
  245. package/tools/task_search.ts +34 -0
  246. package/tools/task_update_status.ts +37 -0
  247. package/tsconfig.json +31 -0
@@ -0,0 +1,276 @@
1
+ # Configuration Reference
2
+
3
+ ## Config File Location
4
+
5
+ `~/.config/opencode/ndomo.json`
6
+
7
+ The schema is defined in `config/ndomo.schema.json` (JSON Schema draft-07) for editor validation.
8
+
9
+ ## Presets
10
+
11
+ These presets are the source of truth for agent models at install time. The installer rewrites each agent's `model:` and `temperature:` from the active preset on every install.
12
+
13
+ Two built-in presets control which models each agent uses.
14
+
15
+ ### Per-agent fields
16
+
17
+ Each agent entry in a preset supports three fields:
18
+
19
+ | Field | Type | Required | Description |
20
+ |---|---|---|---|
21
+ | `model` | string | yes | Model identifier in `provider/model-id` format |
22
+ | `temperature` | number (0-1) | yes | Sampling temperature (0 = deterministic, 1 = creative) |
23
+ | `reasoning_effort` | enum: `low`/`medium`/`high`/`xhigh` | no | Reasoning effort level for reasoning-capable models (DeepSeek, MiMo, OpenAI). Translated to camelCase `reasoningEffort:` in agent `.md` frontmatter. Omit for non-reasoning models. |
24
+
25
+ Example:
26
+ ```json
27
+ {
28
+ "presets": {
29
+ "default": {
30
+ "smith": {
31
+ "model": "opencode-go/deepseek-v4-flash",
32
+ "temperature": 0.1,
33
+ "reasoning_effort": "medium"
34
+ }
35
+ }
36
+ }
37
+ }
38
+ ```
39
+
40
+ ### default
41
+
42
+ | Agent | Model | Temperature |
43
+ |---|---|---|
44
+ | foreman | minimax/MiniMax-M3 | 0.3 |
45
+ | scout | opencode-go/minimax-m2.7 | 0.3 |
46
+ | scribe | opencode-go/minimax-m2.7 | 0.3 |
47
+ | painter | opencode-go/kimi-k2.6 | 0.2 |
48
+ | smith | opencode-go/deepseek-v4-flash | 0.1 |
49
+ | ranger | minimax/MiniMax-M3 | 0.3 |
50
+ | sage | opencode-go/deepseek-v4-pro | 0.2 |
51
+ | guild | opencode-go/deepseek-v4-pro | 0.3 |
52
+ | go-smith | xiaomi/mimo-v2.5-pro | 0.1 |
53
+ | js-smith | xiaomi/mimo-v2.5-pro | 0.1 |
54
+ | python-smith | xiaomi/mimo-v2.5-pro | 0.1 |
55
+ | vue-smith | xiaomi/mimo-v2.5-pro | 0.1 |
56
+ | zig-smith | xiaomi/mimo-v2.5-pro | 0.1 |
57
+ | chronicler | opencode-go/deepseek-v4-flash | 0.2 |
58
+ | inspector | opencode-go/deepseek-v4-pro | 0.2 |
59
+
60
+ ### budget
61
+
62
+ All agents use `opencode-go/deepseek-v4-flash` at their respective temperatures. Reduces API costs at the expense of specialist model quality for stack-smiths and advisors.
63
+
64
+ ## Provider Override at Install Time
65
+
66
+ The `install.sh` script includes a provider override that modifies agent models before registration. This is not a runtime setting — it applies once during installation and is baked into each agent's frontmatter.
67
+
68
+ The preset (not the provider) defines the model ID. `--provider=ID` only changes the `provider/` prefix in each agent's `model:` field, so the literal `default` model ID is never used.
69
+
70
+ ### How it works
71
+
72
+ 1. Without `--provider` and without `--no-provider-prompt`, the script shows the active preset and asks for confirmation.
73
+ 2. Selecting `override` enters the interactive provider picker from models.dev.
74
+ 3. The selected provider prefix replaces the existing `provider/` segment in every agent's `model:` field in `agents/*.md`.
75
+
76
+ ### Example transformation
77
+
78
+ If you override with provider `opencode`, the agent models transform as follows:
79
+
80
+ | Original | After provider prefix override |
81
+ |---|---|
82
+ | `minimax/MiniMax-M3` | `opencode/MiniMax-M3` (provider prefix changed) |
83
+ | `opencode-go/deepseek-v4-flash` | `opencode/deepseek-v4-flash` (provider prefix changed) |
84
+ | `xiaomi/mimo-v2.5-pro` | `opencode/mimo-v2.5-pro` (provider prefix changed) |
85
+
86
+ ### Flags controlling provider override
87
+
88
+ | Flag | Behavior |
89
+ |---|---|
90
+ | `--provider=ID` | Non-interactive provider prefix override. The model ID is taken from the active preset; only the `provider/` segment of the `model:` field is swapped. |
91
+ | `--no-provider-prompt` | Skips the interactive picker. The preset is still applied; no prefix override is performed. |
92
+
93
+ The install also wires the plugin into the OpenCode config directory (`~/.config/opencode/`) so tools are auto-registered on OpenCode launch — see [plugin docs](https://opencode.ai/docs/es/plugins/) and [custom tools docs](https://opencode.ai/docs/es/custom-tools/).
94
+
95
+ ### Relevant files modified
96
+
97
+ - `agents/*.md` — the `model:` field in each agent's frontmatter is modified during Step 5.5 of the install script.
98
+ - `~/.cache/ndomo/models-catalog.json` — cached catalog (re-fetched weekly or on cache miss).
99
+
100
+ The provider override is a one-time install operation. To change providers after installation, either re-run `install.sh` with a different `--provider` flag, or manually edit the `model:` fields in `agents/*.md`.
101
+
102
+ ## Hot-swap: editing models without re-running install
103
+
104
+ `ndomo.json::presets[preset][agent].model` is the **runtime source of truth** for agent models. The plugin's `syncAgentFrontmatter()` runs at every OpenCode session startup, compares each agent's `model:` and `temperature:` in `~/.config/opencode/agent/<agent>.md` against the active preset in `ndomo.json`, and rewrites the file when the values differ. This means you can edit `ndomo.json` directly and have the changes take effect on the next session — no need to re-run `install.sh`.
105
+
106
+ **Workflow:**
107
+
108
+ 1. Open `~/.config/opencode/ndomo.json`.
109
+ 2. Edit `presets[default|...][<agent-name>].model` and/or `.temperature`. Example:
110
+
111
+ ```json
112
+ {
113
+ "preset": "default",
114
+ "presets": {
115
+ "default": {
116
+ "foreman": { "model": "anthropic/claude-sonnet-4.5", "temperature": 0.3 }
117
+ }
118
+ }
119
+ }
120
+ ```
121
+
122
+ 3. Restart OpenCode. The plugin logs `[ndomo] frontmatter sync: preset=default synced=N skipped=M errors=0` on startup.
123
+
124
+ **Notes:**
125
+
126
+ - Only agents present in the active preset are synced. User-added custom agents (not in `ndomo.json`) are left untouched.
127
+ - The function is **idempotent** — running it with an unchanged config is a no-op (`skipped=N, synced=0`).
128
+ - To disable hot-swap (e.g., for read-only configs or CI), set env var `NDOMO_SKIP_FRONTMATTER_SYNC=1` before launching OpenCode.
129
+ - If the active preset is missing from `ndomo.json` (e.g., `"preset": "production"` but only `default` and `budget` are defined), sync is skipped with a warning — agent files keep their current values.
130
+ - `reasoning_effort` syncing supports both updating an existing `reasoningEffort:` line and inserting a new one (placed after `temperature:`, then `model:`, then `---` as fallback).
131
+
132
+ ## Agent Routing
133
+
134
+ The `agentRouting` field defines the delegation graph. Four agents are defined as `mode: "primary"` peer primaries: `foreman` (planner/orchestrator), `craftsman` (implementer), `warden` (ops custodian), and `ranger` (analyst/cartographer/onboarding). All other agents are subagents reachable via the primaries' `delegates_to` arrays. The foreman's `delegates_to` array lists all subagents it can dispatch.
135
+
136
+ **Ranger** is the 4th primary, registered separately with `mode: "primary"` and its own delegation graph (`scout`, `sage`, `scribe`). Unlike foreman/craftsman/warden, ranger does not create plans — it produces rows in the `analyses` DB table linkable retroactively via `analysis_link_plan`.
137
+
138
+ Routing decisions are made by the scheduler (`src/orchestrator/scheduler.ts`):
139
+
140
+ | Task type | Stack | Risk | Routed to |
141
+ |---|---|---|---|
142
+ | explore | any | any | scout |
143
+ | research | any | any | scribe |
144
+ | design | vue | any | painter |
145
+ | audit | any | any | inspector |
146
+ | document | any | any | chronicler |
147
+ | debate | any | any | guild |
148
+ | debug | any | high | sage |
149
+ | implement | go | any | go-smith |
150
+ | implement | vue | any | vue-smith |
151
+ | implement | js | any | js-smith |
152
+ | implement | python | any | python-smith |
153
+ | implement | zig | any | zig-smith |
154
+ | implement | generic/unknown | any | smith |
155
+ | implement | known stack | high | stack-smith + sage advisory |
156
+ | any other | any | any | smith (fallback) |
157
+
158
+ ## Caveman Settings
159
+
160
+ ```json
161
+ {
162
+ "caveman": {
163
+ "intensity": "full",
164
+ "autoClarity": true
165
+ }
166
+ }
167
+ ```
168
+
169
+ | Field | Type | Values | Description |
170
+ |---|---|---|---|
171
+ | `intensity` | string | `lite`, `full`, `ultra` | Compression level. `lite` keeps some articles; `full` strips all fillers; `ultra` maximum compression. |
172
+ | `autoClarity` | boolean | `true`, `false` | When `true`, agents switch to full verbosity for safety warnings, irreversible actions, or multi-step ambiguity, then resume caveman. |
173
+
174
+ ## DCP Overrides
175
+
176
+ Per-agent context limits for the DCP plugin. Only takes effect when `@tarquinen/opencode-dcp` is installed.
177
+
178
+ ```json
179
+ {
180
+ "dcp_overrides": {
181
+ "scout": { "minContextLimit": 30000, "maxContextLimit": 80000 },
182
+ "scribe": { "minContextLimit": 30000, "maxContextLimit": 80000 },
183
+ "foreman": { "minContextLimit": 50000, "maxContextLimit": 100000 },
184
+ "sage": { "minContextLimit": 50000, "maxContextLimit": 100000 },
185
+ "guild": { "minContextLimit": 50000, "maxContextLimit": 100000 },
186
+ "inspector": { "minContextLimit": 40000, "maxContextLimit": 90000 },
187
+ "ranger": { "minContextLimit": 50000, "maxContextLimit": 100000 }
188
+ }
189
+ }
190
+ ```
191
+
192
+ Agents without explicit overrides use DCP defaults. The foreman monitors context size and triggers compression when approaching `maxContextLimit`.
193
+
194
+ ## Memory Config
195
+
196
+ ```json
197
+ {
198
+ "mem": {
199
+ "storagePath": "~/.ndomo/mem",
200
+ "defaultScope": "project",
201
+ "autoCaptureEnabled": true,
202
+ "cavemanCompress": true
203
+ }
204
+ }
205
+ ```
206
+
207
+ | Field | Type | Description |
208
+ |---|---|---|
209
+ | `storagePath` | string | Directory for opencode-mem storage only. Does NOT control plan archive location. Supports `~` expansion. |
210
+ | `defaultScope` | string | `"project"` — search only current project memories; `"all-projects"` — search across all projects. |
211
+ | `autoCaptureEnabled` | boolean | Automatically capture insights during sessions without explicit `memory({mode:"add"})` calls. |
212
+ | `cavemanCompress` | boolean | Apply caveman regex compression to memories before storage. Saves tokens on retrieval. |
213
+
214
+ > **Note:** `~/.ndomo/mem/` is the **opencode-mem** plugin's storage (USearch
215
+ > vector DB for semantic memory). The **ndomo plugin's** database is at
216
+ > `<project>/.ndomo/state.db` (SQLite) — see [docs/database.md](docs/database.md).
217
+ > These are two separate systems; both can run simultaneously.
218
+ >
219
+ > **Plan archive path:** The ndomo plugin auto-archives completed/failed plans
220
+ > to `<project>/.ndomo/archives/plans/<slug>-YYYY-MM-DD.md` (markdown snapshots).
221
+ > This path is **not configurable** via `ndomo.json` — it always lives under
222
+ > `<project>/.ndomo/archives/plans/`. See [docs/database.md#auto-archive](docs/database.md#auto-archive).
223
+
224
+ ## Protected Tools
225
+
226
+ Tools listed in `protectedTools` cannot be disabled, overridden, or pruned from context by any subagent:
227
+
228
+ ```json
229
+ "protectedTools": ["memory", "compress", "task", "todowrite", "skill"]
230
+ ```
231
+
232
+ | Tool | Why Protected |
233
+ |---|---|
234
+ | `memory` | Required for cross-session persistence and context retrieval |
235
+ | `compress` | Required for DCP context pruning when near token limits |
236
+ | `task` | Required for subagent delegation and background dispatch |
237
+ | `todowrite` | Required for structured task tracking |
238
+ | `skill` | Required for loading agent skill definitions |
239
+
240
+ ## Troubleshooting
241
+
242
+ ### Tools not registered / DB not created
243
+
244
+ The ndomo package is not in `~/.config/opencode/node_modules/ndomo/`. OpenCode's plugin loader silently skips plugins it cannot resolve, so no tools, DB, or agents will appear.
245
+
246
+ **Fix:** Re-run `./scripts/install.sh` or symlink it manually:
247
+
248
+ ```bash
249
+ ln -sfn $(pwd) ~/.config/opencode/node_modules/ndomo
250
+ ```
251
+
252
+ Then ensure `ndomo` is listed in `opencode.json` under `plugins`. See [plugin docs](https://opencode.ai/docs/es/plugins/) for details.
253
+
254
+ ## Custom Agents
255
+
256
+ To add a custom agent to the routing table:
257
+
258
+ 1. Create an agent definition file in `agents/<name>.md` with frontmatter:
259
+ ```yaml
260
+ ---
261
+ description: My custom agent
262
+ mode: subagent
263
+ model: provider/model-id
264
+ temperature: 0.1
265
+ permission:
266
+ edit: allow
267
+ bash: allow
268
+ question: allow
269
+ ---
270
+ ```
271
+
272
+ 2. Add the agent to the foreman's `delegates_to` array in `config/ndomo.config.json`.
273
+
274
+ 3. Add a routing rule in `src/orchestrator/scheduler.ts` or rely on the Foreman prompt routing table.
275
+
276
+ 4. Optionally add a model entry in the `default` and `budget` presets.
@@ -0,0 +1,364 @@
1
+ # Database & Plans
2
+
3
+ ## Overview
4
+
5
+ ndomo persists plans, tasks, and sessions in a project-local SQLite database
6
+ located at `<project>/.ndomo/state.db` (`src/db/client.ts:12-13`). Created
7
+ automatically on first plugin load via `openDb(projectDir)` (`src/db/client.ts:15-20`).
8
+ Survives OpenCode restarts. Indexed with FTS5 for full-text search.
9
+
10
+ ### What gets stored
11
+
12
+ - **Plans** — long-running initiatives with a slug, status (draft/approved/executing/completed/failed/abandoned),
13
+ priority (1-4), complexity (1-5), category, and audit trail
14
+ - **Tasks** — atomic units of work assigned to a specific agent, with
15
+ dependencies (by order_index), result/error fields, and artifacts
16
+ - **Sessions** — continuity across agent dispatches, with checkpoints,
17
+ agent history, and key decisions
18
+
19
+ ### What is NOT stored here
20
+
21
+ - `~/.ndomo/mem/` is the **opencode-mem** plugin's storage (USearch vector DB
22
+ for semantic memory, configured via `ndomo.json` `mem.storagePath`)
23
+ - `docs/plans/<slug>.md` is the **opencode-planning-toolkit** markdown storage
24
+ (different system, simpler, git-friendly)
25
+ - The ndomo DB archive output goes to `<project>/.ndomo/archives/plans/` (markdown snapshots)
26
+
27
+ ## Schema
28
+
29
+ ### Tables
30
+
31
+ #### `plans`
32
+
33
+ | Column | Type | Notes |
34
+ |---|---|---|
35
+ | `id` | TEXT PK | UUID v4 |
36
+ | `slug` | TEXT UNIQUE | kebab-case, 1-60 chars, validated via trigger (`src/db/schema.ts:257-268`) |
37
+ | `title` | TEXT | Short actionable phrase |
38
+ | `status` | TEXT | CHECK: `draft`, `approved`, `executing`, `completed`, `failed`, `abandoned` |
39
+ | `priority` | INTEGER | 1-4, validated via trigger (`src/db/schema.ts:244-254`) |
40
+ | `created_at` | INTEGER | Epoch ms |
41
+ | `updated_at` | INTEGER | Auto-updated via trigger (`src/db/schema.ts:205-209`) |
42
+ | `approved_at` | INTEGER | Null until approved |
43
+ | `completed_at` | INTEGER | Null until terminal status |
44
+ | `session_id` | TEXT | FK to sessions (app-level validation, `src/db/plans.ts:118-121`) |
45
+ | `overview` | TEXT | 2-4 line description |
46
+ | `approach` | TEXT | Implementation strategy |
47
+ | `complexity` | INTEGER | 1-5 |
48
+ | `metadata` | JSON | Discriminated via `PlanMetadata` type |
49
+ | `created_by` | TEXT | Agent name |
50
+ | `updated_by` | TEXT | Agent name |
51
+ | `source_session_id` | TEXT | Originating session |
52
+ | `source_message_id` | TEXT | Originating message ID |
53
+ | `category` | TEXT | CHECK: `feature`, `refactor`, `bugfix`, `docs`, `infra` |
54
+ | `archived_at` | INTEGER | Null when active, epoch ms when archived (v5 soft delete) |
55
+
56
+ Indexes: `idx_plans_status`, `idx_plans_session`, `idx_plans_created`,
57
+ `idx_plans_status_priority`, `idx_plans_archived`.
58
+
59
+ #### `plan_tasks`
60
+
61
+ | Column | Type | Notes |
62
+ |---|---|---|
63
+ | `id` | TEXT PK | UUID v4 |
64
+ | `plan_id` | TEXT FK | References `plans(id)` ON DELETE CASCADE |
65
+ | `order_index` | INTEGER | Sequential within plan. UNIQUE(plan_id, order_index) |
66
+ | `description` | TEXT | Task description |
67
+ | `agent` | TEXT | Assigned agent name |
68
+ | `files` | JSON | Expected output files `[]` |
69
+ | `complexity` | INTEGER | 1-5 |
70
+ | `status` | TEXT | CHECK: `pending`, `running`, `done`, `failed`, `blocked` |
71
+ | `started_at` | INTEGER | Epoch ms, set when `running` |
72
+ | `completed_at` | INTEGER | Epoch ms, set when `done` or `failed` |
73
+ | `result` | TEXT | Free text, truncated to 16KB (`src/db/tasks.ts:109-121`) |
74
+ | `error` | TEXT | Error message, truncated to 16KB |
75
+ | `dependencies` | JSON | Array of order_index values |
76
+ | `metadata` | JSON | Discriminated via `TaskMetadata` type, default `'{}'` |
77
+ | `created_by` | TEXT | Agent name |
78
+ | `updated_by` | TEXT | Agent name |
79
+ | `source_session_id` | TEXT | Originating session |
80
+ | `source_message_id` | TEXT | Originating message ID |
81
+ | `reviewed_by` | TEXT | Reviewer agent name |
82
+ | `tokens_used` | INTEGER | Token count for this task |
83
+ | `duration_ms` | INTEGER | Execution duration |
84
+ | `artifacts` | JSON | Array of artifact paths |
85
+ | `archived_at` | INTEGER | Null when active (v5 soft delete) |
86
+
87
+ Indexes: `idx_tasks_plan`, `idx_tasks_status`, `idx_tasks_agent`,
88
+ `idx_tasks_plan_status`, `idx_tasks_agent_status`, `idx_tasks_archived`.
89
+
90
+ #### `sessions`
91
+
92
+ | Column | Type | Notes |
93
+ |---|---|---|
94
+ | `id` | TEXT PK | UUID v4 |
95
+ | `started_at` | INTEGER | Epoch ms |
96
+ | `ended_at` | INTEGER | Null until ended |
97
+ | `last_checkpoint` | INTEGER | Updated by `checkpointSession()` via trigger (`src/db/schema.ts:218-223`) |
98
+ | `plan_id` | TEXT | References `plans(id)` ON DELETE SET NULL |
99
+ | `goal` | TEXT | Session goal description |
100
+ | `state` | JSON | Arbitrary checkpoint state `{}` |
101
+ | `agent_history` | JSON | Array of `{agent, taskId, startedAt, endedAt}` |
102
+ | `key_decisions` | TEXT | Free text decision log |
103
+ | `metadata` | JSON | Discriminated via `SessionMetadata` type |
104
+ | `created_by` | TEXT | Agent name |
105
+ | `source_message_id` | TEXT | Originating message ID |
106
+ | `parent_session_id` | TEXT | Self-referencing FK |
107
+ | `outcome` | TEXT | CHECK: `success`, `partial`, `failed`, `abandoned` |
108
+ | `archived_at` | INTEGER | Null when active (v5 soft delete) |
109
+
110
+ Indexes: `idx_sessions_started`, `idx_sessions_plan`, `idx_sessions_archived`.
111
+
112
+ ### FTS5 indexes
113
+
114
+ - **`plans_fts_v2`** — content=`plans` (external), columns `id` (UNINDEXED), `title`,
115
+ `overview`, `approach`, `category`. Tokenizer: `unicode61 remove_diacritics 1`.
116
+ Auto-synced via `AFTER INSERT/UPDATE/DELETE` triggers (`src/db/schema.ts:441-463`).
117
+ - **`tasks_fts`** — content=`plan_tasks` (external), columns `id` (UNINDEXED), `description`,
118
+ `result`, `error`. Same tokenizer. Auto-synced via triggers (`src/db/schema.ts:345-363`).
119
+
120
+ Query escaping: `escapeFtsQuery()` wraps user input in double quotes to prevent
121
+ FTS5 syntax injection from hyphens and special characters (`src/db/fts-escape.ts:18-19`).
122
+
123
+ ### Tag tables (M:N)
124
+
125
+ - **`plan_tags(plan_id, tag, added_by, added_at)`** — PK `(plan_id, tag)`.
126
+ Indexed on `tag`. Insert with `OR IGNORE` for idempotency.
127
+ - **`task_tags(task_id, tag, added_by, added_at)`** — PK `(task_id, tag)`.
128
+ Indexed on `tag`. Same idempotent insert pattern.
129
+
130
+ ### View
131
+
132
+ - **`plan_progress`** — computed from `plans` LEFT JOIN `plan_tasks` (excluding
133
+ archived tasks, `src/db/schema.ts:413-431`). Columns: `plan_id`, `slug`, `title`,
134
+ `status`, `total_tasks`, `done`, `failed`, `running`, `pending`, `blocked`,
135
+ `progress_pct` (percentage rounded to integer).
136
+
137
+ ### Migrations
138
+
139
+ 5 migrations applied automatically by `runMigrations(db)` ordered by version:
140
+
141
+ | Version | Description |
142
+ |---|---|
143
+ | v1 | Initial schema: plans, plan_tasks, sessions, FTS5 (porter), sync triggers |
144
+ | v2 | Discriminated metadata, audit columns, plan_tags/task_tags M:N, plans_fts_v2 (contentless) |
145
+ | v3 | `updated_at` triggers, composite indexes, session FK app-level validation |
146
+ | v4 | Priority/slug validation triggers, `plan_progress` view, FTS5 unicode61 diacritics, Spanish stopwords table, metadata default trigger |
147
+ | v5 | Soft delete (`archived_at` columns), `plans_fts_v2` switched to external content (`content='plans'`), `plan_progress` excludes archived tasks |
148
+
149
+ `SCHEMA_V5_SQL` note: columns are added via `addColumnIfMissing()` in `migrations.ts`
150
+ because SQLite 3.45 lacks `IF NOT EXISTS` for `ALTER TABLE ADD COLUMN` (`src/db/schema.ts:400-403`).
151
+
152
+ ## Tools (22)
153
+
154
+ ### Plans (8)
155
+
156
+ | Tool | Args | Returns |
157
+ |---|---|---|
158
+ | `plan_create` | `slug`, `title`, `overview`, `approach?`, `priority?`, `complexity?`, `sessionId?`, `metadata?` | `Plan` (JSON) |
159
+ | `plan_get` | `id?` OR `slug?` | `Plan \| null` (JSON) |
160
+ | `plan_list` | `status?`, `sessionId?`, `limit?` | `Plan[]` (JSON) |
161
+ | `plan_search` | `query`, `limit?`, `includeArchived?` | `Plan[]` (JSON, FTS5 ranked) |
162
+ | `plan_approve` | `id` | `Plan \| null` (JSON) |
163
+ | `plan_update_status` | `id`, `status` (enum: `draft`/`approved`/`executing`/`completed`/`failed`/`abandoned`), `dryRun?`, `force?`, `forceReason?` | `{plan, statusChanged, blocked, forced, dryRun, blockers, warnings, archived, archiveError, auditId}` (JSON) |
164
+ | `plan_progress` | `planId?`, `owner?` | Progress rows[] (JSON) |
165
+ | `plan_files_write` | `planId`, `files[]` (array of `{filePath, role}`) | `{planId, inserted, totalRequested}` (JSON) |
166
+
167
+ - `plan_create` generates a UUID v4 internally via `crypto.randomUUID()` (`src/plugin.ts:581`).
168
+ - `plan_update_status` auto-archives when status is `completed`, `failed`, or `abandoned`
169
+ (`src/plugin.ts:688-697`). Archive errors are non-blocking (logged as warning).
170
+ Extended with readiness checks: `dryRun=true` returns blockers/warnings without mutating.
171
+ `force=true` with `forceReason` bypasses blockers (except `status_invalid`), captured to `plan_audit`.
172
+ Triggers auto-checkpoint on phase transition (`src/db/auto-checkpoint.ts`).
173
+ - `plan_progress` queries the `plan_progress_active` view (excludes archived plans). When `owner` is provided,
174
+ JOINs with `plans` and filters via `json_extract(metadata, '$.ownedBy')` (`src/plugin.ts:843`).
175
+ - `plan_files_write` uses `INSERT OR IGNORE` for idempotency. PK is `(plan_id, file_path, role)`.
176
+ Roles: "input", "modified", "output", "reference" (`src/plugin.ts:875`).
177
+
178
+ ### Tasks (9)
179
+
180
+ | Tool | Args | Returns |
181
+ |---|---|---|
182
+ | `task_create_batch` | `planId`, `tasks[]` (array of `{description, agent, files?, complexity?, dependencies?, metadata?}`) | `PlanTask[]` (JSON) |
183
+ | `task_list` | `planId`, `status?` | `PlanTask[]` (JSON) |
184
+ | `task_update_status` | `id`, `status` (enum), `result?`, `error?`, `artifacts?`, `metadataPatch?`, `reviewedBy?`, `reviewedVerdict?` | `PlanTask \| null` (JSON) |
185
+ | `task_search` | `query`, `limit?`, `includeArchived?` | `PlanTask[]` (JSON, FTS5 ranked) |
186
+ | `task_next_for_agent` | `agent`, `planId?` | `PlanTask \| null` (JSON) |
187
+ | `task_peek_for_agent` | `agent`, `planId?`, `limit?` | `PlanTask[]` (JSON) |
188
+ | `task_add_artifact` | `taskId`, `artifact`, `role?` | `{task, added}` (JSON) |
189
+ | `task_review` | `taskId`, `reviewedBy`, `verdict` | `{task}` (JSON) |
190
+ | `task_dependency_resolver` | `taskId?` OR `planId?` + `orderIndex?` | `{canStart, pendingDeps, runningDeps, failedDeps, blockedDeps, doneDeps, missingDeps, dependencies}` (JSON) |
191
+
192
+ - `task_create_batch` is transactional — all tasks insert or none (`src/db/tasks.ts:24-77`).
193
+ Status defaults to `pending`. Each task gets a UUID and sequential `order_index`.
194
+ - `task_update_status` truncates `result` and `error` to 16KB max (`"…[truncated]"` suffix,
195
+ `src/db/tasks.ts:109-121`). Sets `started_at` on `running`, `completed_at` on `done`/`failed`.
196
+ Also accepts `artifacts` (replaces array, truncated if >16KB), `metadataPatch` (deep merged into existing
197
+ metadata), `reviewedBy` (sets `reviewed_by` column), and `reviewedVerdict` (stored in `metadata` JSON)
198
+ (`src/db/tasks.ts:316-428`). All new fields are optional — retrocompatible with existing callers.
199
+ - `task_next_for_agent` returns the first `pending` task ordered by `order_index`
200
+ for the given agent, optionally scoped to a plan.
201
+ - `task_peek_for_agent` is read-only — unlike `task_next_for_agent`, it does NOT claim the task (no status
202
+ change). Returns pending tasks ordered by `order_index`, excludes archived (`src/plugin.ts:1030`).
203
+ - `task_add_artifact` appends to the existing `artifacts` array (dedup — returns `added: false` if already
204
+ present). If `role` is provided, also inserts into `plan_files` (`src/plugin.ts:1056`).
205
+ - `task_review` only works on tasks with `status='done'`. Sets `reviewed_by` column and stores `reviewedVerdict`
206
+ in `metadata` JSON (`src/plugin.ts:1089`).
207
+ - `task_dependency_resolver` resolves a task's dependencies by classifying each dep ID by its current status.
208
+ Returns `canStart` (true iff all deps are `done`) plus categorized arrays. Accepts `taskId` directly,
209
+ or `planId` + `orderIndex` to look up the task (`src/db/tasks.ts:449-510`).
210
+
211
+ ### Sessions (3)
212
+
213
+ | Tool | Args | Returns |
214
+ |---|---|---|
215
+ | `session_start` | `id`, `goal`, `planId?`, `metadata?` | `Session` (JSON) |
216
+ | `session_checkpoint` | `id`, `state` (record), `keyDecisions?` | `Session \| null` (JSON) |
217
+ | `session_end` | `id` | `Session \| null` (JSON) |
218
+
219
+ - `session_start` sets `started_at` and `last_checkpoint` to now (`src/db/sessions.ts:24-38`).
220
+ - `session_checkpoint` updates `last_checkpoint`, `state`, and appends `key_decisions`
221
+ using `COALESCE(?, key_decisions)` to preserve existing decisions (`src/db/sessions.ts:81-85`).
222
+ - `session_end` sets `ended_at` only if not already set (`WHERE ended_at IS NULL`,
223
+ `src/db/sessions.ts:110`). Returns `null` if already ended.
224
+
225
+ ### Ops (2)
226
+
227
+ | Tool | Args | Returns |
228
+ |---|---|---|
229
+ | `incident_create` | `title`, `severity` (enum: sev1-4), `summary?`, `triggeredByDeploymentId?`, `metadata?` | `Incident` (JSON) |
230
+ | `rollback_record` | `deploymentId`, `plan`, `incidentId?`, `status?` (enum), `newDeploymentId?`, `metadata?` | `RollbackExecution` (JSON) |
231
+
232
+ - `incident_create` validates severity enum (sev1-4) and FK on `triggeredByDeploymentId` if provided.
233
+ Sets `metadata.created_by` from `ctx.agent ?? "unknown"` (`src/plugin.ts:1121`).
234
+ - `rollback_record` requires `deploymentId` (FK validated), optional `incidentId`/`newDeploymentId` (FK validated).
235
+ Sets `metadata.executed_by_agent` from `ctx.agent ?? "unknown"` (`src/plugin.ts:1144`).
236
+
237
+ ## Lifecycle
238
+
239
+ ### Foreman 10-step lifecycle (`agents/foreman.md`)
240
+
241
+ ```
242
+ User → foreman
243
+ | 1. Receive user request (clarify if ambiguous)
244
+ | 2. plan_create("draft") — UUID, slug, priority, overview, approach
245
+ | → auto-creates a session row in `sessions` (ensureSession) for FK integrity,
246
+ | keyed by ctx.sessionID with `goal = "Plan: <title>"`. If `session_start` is needed (step 5) for explicit dispatch tracking, use a different session id — calling it with the same `ctx.sessionID` would collide with the auto-row (PK violation).
247
+ | ensureSession is idempotent.
248
+ | 3. plan_approve — seal approved_at, transition to "approved"
249
+ | 4. task_create_batch — one task per agent with dependencies
250
+ | 5. session_start — link to planId
251
+ | 6. Dispatch loop:
252
+ | task_next_for_agent → agent executes → task_update_status
253
+ | 7. session_checkpoint — at each milestone (min 1 per phase)
254
+ | 8. Reconcile — verify all tasks done/failed before closing
255
+ | 9. plan_update_status("completed") — triggers auto-archive
256
+ | 10. session_end — set ended_at
257
+
258
+ Done
259
+ ```
260
+
261
+ ### Auto-archive
262
+
263
+ - **Trigger**: `plan_update_status` when `status` ∈ `{completed, failed, abandoned}`
264
+ (`src/plugin.ts:688-694`).
265
+ - **Output**: Markdown file at `<project>/.ndomo/archives/plans/<slug>-YYYY-MM-DD.md` with:
266
+ - Title, slug, status, priority, complexity, plan ID
267
+ - Overview and approach sections
268
+ - Task list with checkboxes (`[x]` for done), agent, complexity, status, result, error
269
+ - Session list with timestamps, goal, and key decisions
270
+ - Metadata as JSON block
271
+ - **Collision handling**: If filename exists, appends HHMMSS suffix (`src/db/plan-archive.ts:180-190`).
272
+ - **Transactional**: DB update wrapped in `db.transaction()`. Markdown file deleted on
273
+ DB failure (rollback, `src/db/plan-archive.ts:222-229`).
274
+ - **Non-blocking**: Archive errors are caught and logged as warnings; status update
275
+ succeeds regardless (`src/plugin.ts:692-697`).
276
+ - **DB effect**: Sets `archived_at` on plan, all its tasks, and linked sessions via
277
+ cascade UPDATE (`src/db/plan-archive.ts:196-219`).
278
+ - **Filters**: Archived records are excluded by default. Pass `includeArchived: true`
279
+ to `plan_search`, `task_search`, `task_list`, `task_next_for_agent` to retrieve them.
280
+ `plan_list` does not currently expose `includeArchived` in the tool schema.
281
+
282
+ ## Dual plan system
283
+
284
+ | System | Storage | Purpose |
285
+ |---|---|---|
286
+ | ndomo plugin DB | `<project>/.ndomo/state.db` (SQLite) | Structured plans with FTS5 search, audit trail, tag taxonomy, cascade archive |
287
+ | ndomo plan archive | `<project>/.ndomo/archives/plans/<slug>-YYYY-MM-DD.md` (markdown) | Auto-generated snapshot on plan completion/failure; not configurable via `mem.storagePath` |
288
+ | opencode-planning-toolkit | `docs/plans/<slug>.md` (markdown) | Lightweight plan notes, git-committable, human-readable |
289
+ | opencode-mem | `~/.ndomo/mem/` (USearch vector DB) | Semantic memory for cross-session recall only — path controlled by `mem.storagePath`, does NOT affect plan archive |
290
+
291
+ These are complementary. Use ndomo DB for orchestration (plans → tasks → sessions);
292
+ use the plan archive for auto-generated markdown snapshots per project;
293
+ use planning-toolkit for plan-as-document; use opencode-mem for semantic recall.
294
+
295
+ ## Inspection
296
+
297
+ ```bash
298
+ # Count records by status
299
+ sqlite3 .ndomo/state.db "SELECT status, COUNT(*) FROM plans GROUP BY status"
300
+ sqlite3 .ndomo/state.db "SELECT agent, status, COUNT(*) FROM plan_tasks GROUP BY agent, status"
301
+
302
+ # Find plans by tag
303
+ sqlite3 .ndomo/state.db "SELECT p.title, p.status FROM plans p JOIN plan_tags t ON t.plan_id = p.id WHERE t.tag = 'refactor'"
304
+
305
+ # FTS5 search
306
+ sqlite3 .ndomo/state.db "SELECT title FROM plans_fts_v2 WHERE plans_fts_v2 MATCH 'auth'"
307
+
308
+ # View progress
309
+ sqlite3 .ndomo/state.db "SELECT slug, total_tasks, done, progress_pct FROM plan_progress ORDER BY progress_pct DESC"
310
+ ```
311
+
312
+ ## Backup & migration
313
+
314
+ The DB is project-local at `<project>/.ndomo/state.db`. To backup:
315
+
316
+ ```bash
317
+ cp .ndomo/state.db ~/.ndomo/backups/state-$(date +%Y%m%d).db
318
+ ```
319
+
320
+ Archived plans live at `<project>/.ndomo/archives/plans/<slug>-YYYY-MM-DD.md` — these are
321
+ git-friendly and survive DB deletion.
322
+
323
+ Migrations are applied automatically by `runMigrations(db)` on plugin startup.
324
+ The `schema_version` table tracks applied versions. Manual migration is not
325
+ required.
326
+
327
+ ## Schema v13 — ops tables
328
+
329
+ Migration v13 adds 5 tables for operational tracking (warden scope). All use `CREATE TABLE IF NOT EXISTS`
330
+ (idempotent — no `addColumnIfMissing` needed). Registered in `MIGRATIONS` array (`src/db/schema.ts:820-823`).
331
+
332
+ ### Tables
333
+
334
+ | Table | Purpose | Key columns |
335
+ |---|---|---|
336
+ | `environments` | Named deployment targets (prod, staging, dev) | id, name (UNIQUE), slug (UNIQUE), description, metadata, archived_at |
337
+ | `releases` | Versioned artifacts deployable to environments | id, version, title, notes, metadata, archived_at |
338
+ | `deployments` | A release deployed to an environment | id, release_id (FK), environment_id (FK), status (CHECK: planned/in_progress/succeeded/failed/rolled_back), deployed_at |
339
+ | `incidents` | Operational events, optionally linked to a deployment | id, title, severity (CHECK: sev1-4), status (CHECK: open/triaging/mitigated/resolved/postmortem), summary, triggered_by_deployment_id (FK nullable) |
340
+ | `rollback_executions` | Record of a rollback action | id, deployment_id (FK required), incident_id (FK nullable), new_deployment_id (FK nullable), status (CHECK: planned/approved/dry_run/executing/success/failed/cancelled), plan |
341
+
342
+ ### FK graph
343
+
344
+ ```
345
+ environments ←─── deployments ───→ releases
346
+
347
+ │ triggered_by_deployment_id (nullable)
348
+ incidents
349
+
350
+ │ incident_id (nullable)
351
+ rollback_executions
352
+
353
+ ├── deployment_id (required) ──→ deployments
354
+ └── new_deployment_id (nullable) ──→ deployments
355
+ ```
356
+
357
+ ### Migration notes
358
+
359
+ - **Idempotent**: all 5 tables use `CREATE TABLE IF NOT EXISTS`. Re-running v13 is a no-op.
360
+ - **No special-casing**: the generic `runMigrations(db)` runner handles v13 without addColumnIfMissing.
361
+ - **Rollback strategy**: tables are additive (no ALTER TABLE on existing tables). To remove:
362
+ `DROP TABLE` in FK-safe order: `rollback_executions` → `incidents` → `deployments` → `releases` → `environments`.
363
+ Then decrement `schema_version` to 12.
364
+ - **Indices**: all FK columns + status/severity/created_at have indices for query performance.