@stackmemoryai/stackmemory 1.10.5 → 1.14.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 (225) hide show
  1. package/LICENSE +131 -64
  2. package/README.md +107 -24
  3. package/bin/claude-sm +16 -1
  4. package/bin/claude-smd +16 -1
  5. package/bin/codex-smd +16 -1
  6. package/bin/gemini-sm +16 -1
  7. package/bin/hermes-sm +21 -0
  8. package/bin/hermes-smd +21 -0
  9. package/bin/opencode-sm +16 -1
  10. package/dist/src/cli/claude-sm.js +266 -84
  11. package/dist/src/cli/codex-sm.js +225 -33
  12. package/dist/src/cli/commands/bench.js +209 -2
  13. package/dist/src/cli/commands/brain.js +206 -0
  14. package/dist/src/cli/commands/cache.js +126 -0
  15. package/dist/src/cli/commands/company-os.js +184 -0
  16. package/dist/src/cli/commands/context.js +5 -0
  17. package/dist/src/cli/commands/daemon.js +41 -0
  18. package/dist/src/cli/commands/handoff.js +40 -9
  19. package/dist/src/cli/commands/onboard.js +70 -3
  20. package/dist/src/cli/commands/operator.js +127 -0
  21. package/dist/src/cli/commands/optimize.js +117 -0
  22. package/dist/src/cli/commands/orchestrate.js +232 -5
  23. package/dist/src/cli/commands/orchestrator.js +315 -26
  24. package/dist/src/cli/commands/pack.js +322 -0
  25. package/dist/src/cli/commands/patterns.js +254 -0
  26. package/dist/src/cli/commands/portal.js +161 -0
  27. package/dist/src/cli/commands/scaffold.js +92 -0
  28. package/dist/src/cli/commands/search.js +40 -1
  29. package/dist/src/cli/commands/setup.js +178 -11
  30. package/dist/src/cli/commands/skills.js +10 -1
  31. package/dist/src/cli/commands/state.js +265 -0
  32. package/dist/src/cli/commands/sync.js +253 -0
  33. package/dist/src/cli/commands/tasks.js +130 -1
  34. package/dist/src/cli/commands/vision.js +221 -0
  35. package/dist/src/cli/gemini-sm.js +19 -29
  36. package/dist/src/cli/hermes-sm.js +224 -0
  37. package/dist/src/cli/index.js +105 -39
  38. package/dist/src/cli/opencode-sm.js +38 -21
  39. package/dist/src/cli/utils/determinism-watcher.js +66 -0
  40. package/dist/src/cli/utils/real-cli-bin.js +116 -0
  41. package/dist/src/core/brain/brain-store.js +187 -0
  42. package/dist/src/core/brain/brain-sync.js +193 -0
  43. package/dist/src/core/brain/index.js +78 -0
  44. package/dist/src/core/brain/types.js +10 -0
  45. package/dist/src/core/cache/content-cache.js +238 -0
  46. package/dist/src/{integrations/diffmem → core/cache}/index.js +5 -5
  47. package/dist/src/core/cache/token-estimator.js +39 -0
  48. package/dist/src/core/config/feature-flags.js +2 -6
  49. package/dist/src/core/context/frame-database.js +79 -27
  50. package/dist/src/core/context/recursive-context-manager.js +1 -1
  51. package/dist/src/core/context/rehydration.js +2 -1
  52. package/dist/src/core/cross-search/cross-project-search.js +269 -0
  53. package/dist/src/core/cross-search/index.js +10 -0
  54. package/dist/src/core/database/sqlite-adapter.js +14 -84
  55. package/dist/src/core/extensions/provider-adapter.js +5 -0
  56. package/dist/src/core/models/model-router.js +54 -2
  57. package/dist/src/core/models/provider-pricing.js +58 -4
  58. package/dist/src/core/monitoring/logger.js +2 -1
  59. package/dist/src/core/optimization/trace-optimizer.js +413 -0
  60. package/dist/src/core/patterns/index.js +22 -0
  61. package/dist/src/core/patterns/pattern-applier.js +39 -0
  62. package/dist/src/core/patterns/pattern-observer.js +157 -0
  63. package/dist/src/core/patterns/pattern-store.js +259 -0
  64. package/dist/src/core/patterns/types.js +19 -0
  65. package/dist/src/core/provenance/confidence-scorer.js +128 -0
  66. package/dist/src/core/provenance/index.js +40 -0
  67. package/dist/src/core/provenance/provenance-store.js +194 -0
  68. package/dist/src/core/provenance/types.js +82 -0
  69. package/dist/src/core/retrieval/llm-context-retrieval.js +5 -4
  70. package/dist/src/core/retrieval/unified-context-assembler.js +11 -66
  71. package/dist/src/core/session/project-handoff.js +64 -0
  72. package/dist/src/core/session/session-manager.js +28 -0
  73. package/dist/src/core/shared-state/canonical-store.js +564 -0
  74. package/dist/src/core/skill-packs/index.js +18 -0
  75. package/dist/src/core/skill-packs/parser.js +42 -0
  76. package/dist/src/core/skill-packs/registry.js +224 -0
  77. package/dist/src/core/skill-packs/types.js +79 -0
  78. package/dist/src/core/storage/cloud-sync-manager.js +116 -0
  79. package/dist/src/core/storage/cloud-sync.js +574 -0
  80. package/dist/src/core/storage/two-tier-storage.js +5 -1
  81. package/dist/src/core/tasks/master-tasks-template.js +43 -0
  82. package/dist/src/core/tasks/md-task-parser.js +138 -0
  83. package/dist/src/core/trace/trace-event-store.js +282 -0
  84. package/dist/src/core/vision/index.js +27 -0
  85. package/dist/src/core/vision/signals.js +79 -0
  86. package/dist/src/core/vision/types.js +22 -0
  87. package/dist/src/core/vision/vision-file.js +146 -0
  88. package/dist/src/core/vision/vision-loop.js +220 -0
  89. package/dist/src/core/wiki/wiki-compiler.js +103 -1
  90. package/dist/src/daemon/daemon-config.js +52 -0
  91. package/dist/src/daemon/services/desire-path-service.js +566 -0
  92. package/dist/src/daemon/services/github-service.js +126 -0
  93. package/dist/src/daemon/services/research-stream-service.js +320 -0
  94. package/dist/src/daemon/services/telemetry-service.js +192 -0
  95. package/dist/src/daemon/unified-daemon.js +58 -1
  96. package/dist/src/features/browser/cli-browser-agent.js +417 -0
  97. package/dist/src/features/browser/stagehand-workflows.js +578 -0
  98. package/dist/src/features/operator/adapter-factory.js +62 -0
  99. package/dist/src/features/operator/browser-adapter.js +109 -0
  100. package/dist/src/features/operator/desktop-adapter.js +125 -0
  101. package/dist/src/features/operator/index.js +39 -0
  102. package/dist/src/features/operator/llm-decision.js +137 -0
  103. package/dist/src/features/operator/operator-logger.js +92 -0
  104. package/dist/src/features/operator/overnight-runner.js +327 -0
  105. package/dist/src/features/operator/screen-adapter.js +91 -0
  106. package/dist/src/features/operator/session-manager.js +127 -0
  107. package/dist/src/features/operator/state-machine.js +227 -0
  108. package/dist/src/features/operator/task-queue.js +81 -0
  109. package/dist/src/features/portal/index.js +26 -0
  110. package/dist/src/features/portal/server.js +240 -0
  111. package/dist/src/features/portal/types.js +14 -0
  112. package/dist/src/features/portal/ui.js +195 -0
  113. package/dist/src/features/sweep/pty-wrapper.js +13 -5
  114. package/dist/src/features/tasks/task-aware-context.js +2 -1
  115. package/dist/src/features/tui/simple-monitor.js +0 -23
  116. package/dist/src/features/tui/swarm-monitor.js +8 -66
  117. package/dist/src/features/web/client/hooks/use-socket.js +12 -0
  118. package/dist/src/{core/merge/index.js → features/web/client/lib/utils.js} +8 -4
  119. package/dist/src/features/web/client/next-env.d.js +4 -0
  120. package/dist/src/features/web/client/stores/session-store.js +12 -0
  121. package/dist/src/features/web/server/gcp-billing.js +76 -0
  122. package/dist/src/features/web/server/index.js +10 -0
  123. package/dist/src/features/web/server/spend-calculator.js +228 -0
  124. package/dist/src/hooks/schemas.js +6 -1
  125. package/dist/src/integrations/anthropic/client.js +3 -2
  126. package/dist/src/integrations/claude-code/agent-bridge.js +0 -3
  127. package/dist/src/integrations/claude-code/subagent-client.js +307 -11
  128. package/dist/src/integrations/claude-code/task-coordinator.js +2 -1
  129. package/dist/src/integrations/github/pr-state.js +158 -0
  130. package/dist/src/integrations/linear/client.js +4 -1
  131. package/dist/src/integrations/linear/webhook-retry.js +196 -0
  132. package/dist/src/integrations/linear/webhook-server.js +18 -22
  133. package/dist/src/integrations/mcp/handlers/cloud-sync-handlers.js +101 -0
  134. package/dist/src/integrations/mcp/handlers/index.js +40 -84
  135. package/dist/src/integrations/mcp/server.js +542 -641
  136. package/dist/src/integrations/mcp/tool-alias-registry.js +297 -0
  137. package/dist/src/integrations/mcp/tool-definitions.js +152 -682
  138. package/dist/src/mcp/stackmemory-mcp-server.js +571 -231
  139. package/dist/src/orchestrators/multimodal/determinism.js +244 -0
  140. package/dist/src/orchestrators/multimodal/harness.js +149 -78
  141. package/dist/src/orchestrators/multimodal/providers.js +44 -3
  142. package/dist/src/skills/recursive-agent-orchestrator.js +2 -4
  143. package/dist/src/utils/hook-installer.js +0 -8
  144. package/dist/src/utils/process-cleanup.js +1 -7
  145. package/docs/README.md +42 -0
  146. package/docs/guides/README_INSTALL.md +208 -0
  147. package/package.json +27 -9
  148. package/packs/coding/python-fastapi/instructions.md +60 -0
  149. package/packs/coding/python-fastapi/pack.yaml +28 -0
  150. package/packs/coding/typescript-react/instructions.md +47 -0
  151. package/packs/coding/typescript-react/pack.yaml +28 -0
  152. package/packs/core/commands/capture.md +32 -0
  153. package/packs/core/commands/learn.md +73 -0
  154. package/packs/core/commands/next.md +36 -0
  155. package/packs/core/commands/restart.md +58 -0
  156. package/packs/core/commands/restore.md +29 -0
  157. package/packs/core/commands/start.md +57 -0
  158. package/packs/core/commands/stop.md +65 -0
  159. package/packs/core/commands/summary.md +40 -0
  160. package/packs/core/manifest.json +24 -0
  161. package/packs/ops/decision-recovery/instructions.md +65 -0
  162. package/packs/ops/decision-recovery/pack.yaml +89 -0
  163. package/scripts/claude-code-wrapper.sh +11 -0
  164. package/scripts/claude-sm-setup.sh +12 -1
  165. package/scripts/codex-wrapper.sh +11 -0
  166. package/scripts/git-hooks/branch-context-manager.sh +11 -0
  167. package/scripts/git-hooks/post-checkout-stackmemory.sh +11 -0
  168. package/scripts/git-hooks/post-commit-stackmemory.sh +11 -0
  169. package/scripts/git-hooks/pre-commit-stackmemory.sh +11 -0
  170. package/scripts/hooks/cleanup-shell.sh +12 -1
  171. package/scripts/hooks/task-complete.sh +12 -1
  172. package/scripts/install-code-execution-hooks.sh +12 -1
  173. package/scripts/install-sweep-hook.sh +12 -0
  174. package/scripts/install.sh +11 -0
  175. package/scripts/opencode-wrapper.sh +11 -0
  176. package/scripts/portal/cloud-init.yaml +69 -0
  177. package/scripts/portal/setup.sh +69 -0
  178. package/scripts/portal/stackmemory-portal.service +34 -0
  179. package/scripts/setup-claude-integration.sh +12 -1
  180. package/scripts/smoke-init-db.sh +23 -0
  181. package/scripts/stackmemory-daemon.sh +11 -0
  182. package/scripts/verify-dist.cjs +11 -4
  183. package/dist/src/cli/commands/ralph.js +0 -1053
  184. package/dist/src/cli/commands/team.js +0 -168
  185. package/dist/src/core/context/shared-context-layer.js +0 -620
  186. package/dist/src/core/context/stack-merge-resolver.js +0 -748
  187. package/dist/src/core/merge/conflict-detector.js +0 -430
  188. package/dist/src/core/merge/resolution-engine.js +0 -557
  189. package/dist/src/core/merge/stack-diff.js +0 -531
  190. package/dist/src/core/merge/unified-merge-resolver.js +0 -302
  191. package/dist/src/hooks/diffmem-hooks.js +0 -376
  192. package/dist/src/integrations/diffmem/client.js +0 -208
  193. package/dist/src/integrations/diffmem/config.js +0 -14
  194. package/dist/src/integrations/greptile/client.js +0 -101
  195. package/dist/src/integrations/greptile/config.js +0 -14
  196. package/dist/src/integrations/greptile/index.js +0 -11
  197. package/dist/src/integrations/mcp/handlers/cord-handlers.js +0 -397
  198. package/dist/src/integrations/mcp/handlers/diffmem-handlers.js +0 -455
  199. package/dist/src/integrations/mcp/handlers/greptile-handlers.js +0 -456
  200. package/dist/src/integrations/mcp/handlers/provider-handlers.js +0 -227
  201. package/dist/src/integrations/mcp/handlers/team-handlers.js +0 -211
  202. package/dist/src/integrations/ralph/bridge/ralph-stackmemory-bridge.js +0 -863
  203. package/dist/src/integrations/ralph/context/context-budget-manager.js +0 -308
  204. package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +0 -391
  205. package/dist/src/integrations/ralph/index.js +0 -17
  206. package/dist/src/integrations/ralph/learning/pattern-learner.js +0 -435
  207. package/dist/src/integrations/ralph/lifecycle/iteration-lifecycle.js +0 -448
  208. package/dist/src/integrations/ralph/loopmax.js +0 -488
  209. package/dist/src/integrations/ralph/monitoring/swarm-dashboard.js +0 -293
  210. package/dist/src/integrations/ralph/monitoring/swarm-registry.js +0 -107
  211. package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +0 -508
  212. package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +0 -407
  213. package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +0 -495
  214. package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +0 -387
  215. package/dist/src/integrations/ralph/performance/performance-optimizer.js +0 -357
  216. package/dist/src/integrations/ralph/recovery/crash-recovery.js +0 -461
  217. package/dist/src/integrations/ralph/state/state-reconciler.js +0 -420
  218. package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +0 -444
  219. package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +0 -1007
  220. package/dist/src/integrations/ralph/visualization/ralph-debugger.js +0 -635
  221. package/scripts/ralph-loop-implementation.js +0 -404
  222. /package/dist/src/core/{merge → cache}/types.js +0 -0
  223. /package/dist/src/{integrations/diffmem/types.js → core/storage/cloud-sync-types.js} +0 -0
  224. /package/dist/src/{integrations/greptile/types.js → core/trace/trace-event.js} +0 -0
  225. /package/dist/src/{integrations/ralph → features/operator}/types.js +0 -0
@@ -0,0 +1,206 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
5
+ import { Command } from "commander";
6
+ import chalk from "chalk";
7
+ import { openBrain } from "../../core/brain/index.js";
8
+ function fmtEntry(e, verbose = false) {
9
+ const id = chalk.dim(e.entryId.slice(0, 8));
10
+ const kind = chalk.cyan(e.kind.padEnd(10));
11
+ const agent = chalk.magenta(`@${e.agent}`);
12
+ const when = new Date(e.createdAt).toISOString().slice(0, 10);
13
+ const head = `${id} ${kind} ${agent} ${chalk.gray(when)} ${chalk.bold(e.title)}`;
14
+ if (!verbose) {
15
+ const concl = e.conclusion ? `
16
+ ${chalk.green("\u2192")} ${e.conclusion}` : "";
17
+ return head + concl;
18
+ }
19
+ const lines = [head];
20
+ if (e.summary) lines.push(` ${chalk.gray("summary:")} ${e.summary}`);
21
+ if (e.conclusion)
22
+ lines.push(` ${chalk.green("conclusion:")} ${e.conclusion}`);
23
+ if (e.tags.length)
24
+ lines.push(` ${chalk.gray("tags:")} ${e.tags.join(", ")}`);
25
+ if (e.refs.length)
26
+ lines.push(` ${chalk.gray("refs:")} ${e.refs.join(", ")}`);
27
+ lines.push(` ${chalk.gray("confidence:")} ${e.confidence}`);
28
+ return lines.join("\n");
29
+ }
30
+ function createBrainCommand() {
31
+ const cmd = new Command("brain").description("Shared, compounding context state (per repo + org)").addHelpText(
32
+ "after",
33
+ `
34
+ Examples:
35
+ stackmemory brain record --kind experiment \\
36
+ --title "Retry with jitter cut 5xx" \\
37
+ --summary "Tried exp backoff + jitter on the sync client" \\
38
+ --conclusion "p99 errors dropped 60%; ship it" --tags sync,reliability
39
+ stackmemory brain recall "retry" Search this repo's brain
40
+ stackmemory brain recall "auth" --org Search the whole org
41
+ stackmemory brain list --limit 10
42
+ stackmemory brain show <id>
43
+ stackmemory brain sync Push + pull online
44
+ stackmemory brain status
45
+
46
+ Every agent (Claude, Codex, OpenCode, Hermes) shares this brain \u2014 log
47
+ experiment conclusions so mutual thinking compounds. See docs/guides/BRAIN.md.
48
+ `
49
+ );
50
+ cmd.command("record").description("Record an experiment / decision / insight / note").option("--title <title>", "Short title (required)").option("--summary <text>", "What was done / context").option("--conclusion <text>", "What was concluded (the payload)").option("--kind <kind>", "experiment | decision | insight | note", "note").option("--agent <name>", "Agent that produced this", "claude").option("--tags <tags>", "Comma-separated tags").option("--refs <refs>", "Comma-separated refs (issues, commits, files)").option("--confidence <n>", "Confidence 0..1", "0.7").option("--json", "Output as JSON").action((options) => {
51
+ if (!options.title) {
52
+ console.error(chalk.red("--title is required"));
53
+ process.exit(1);
54
+ }
55
+ const ctx = openBrain();
56
+ try {
57
+ const entry = ctx.store.record({
58
+ title: options.title,
59
+ summary: options.summary,
60
+ conclusion: options.conclusion,
61
+ kind: options.kind,
62
+ agent: options.agent,
63
+ tags: splitList(options.tags),
64
+ refs: splitList(options.refs),
65
+ confidence: parseFloat(options.confidence)
66
+ });
67
+ if (options.json) {
68
+ console.log(JSON.stringify(entry, null, 2));
69
+ } else {
70
+ console.log(
71
+ chalk.green("\u2713 recorded"),
72
+ chalk.dim(entry.entryId.slice(0, 8))
73
+ );
74
+ console.log(fmtEntry(entry));
75
+ }
76
+ } finally {
77
+ ctx.close();
78
+ }
79
+ });
80
+ cmd.command("recall").description("Search the brain (this repo by default, --org for the org)").argument("[query]", "Free-text query").option("--org", "Search across the whole org (all repos)").option("--agent <name>", "Filter by agent").option("--kind <kind>", "Filter by kind").option("--limit <n>", "Max results", "20").option("--all", "Include superseded entries").option("--json", "Output as JSON").action((query, options) => {
81
+ const ctx = openBrain();
82
+ try {
83
+ const q = {
84
+ org: !!options.org,
85
+ limit: parseInt(options.limit, 10),
86
+ includeSuperseded: !!options.all,
87
+ ...query ? { text: query } : {},
88
+ ...options.agent ? { agent: options.agent } : {},
89
+ ...options.kind ? { kind: options.kind } : {}
90
+ };
91
+ const results = ctx.store.recall(q);
92
+ if (options.json) {
93
+ console.log(JSON.stringify(results, null, 2));
94
+ return;
95
+ }
96
+ if (results.length === 0) {
97
+ console.log(chalk.yellow("No matching brain entries."));
98
+ return;
99
+ }
100
+ const scope = options.org ? "org" : "repo";
101
+ console.log(chalk.bold(`${results.length} result(s) [${scope}]`));
102
+ for (const e of results) console.log("\n" + fmtEntry(e));
103
+ } finally {
104
+ ctx.close();
105
+ }
106
+ });
107
+ cmd.command("list").description("List recent brain entries for this repo").option("--limit <n>", "Max results", "20").option("--json", "Output as JSON").action((options) => {
108
+ const ctx = openBrain();
109
+ try {
110
+ const results = ctx.store.recall({
111
+ limit: parseInt(options.limit, 10)
112
+ });
113
+ if (options.json) {
114
+ console.log(JSON.stringify(results, null, 2));
115
+ return;
116
+ }
117
+ if (results.length === 0) {
118
+ console.log(chalk.yellow("Brain is empty for this repo."));
119
+ return;
120
+ }
121
+ for (const e of results) console.log(fmtEntry(e) + "\n");
122
+ } finally {
123
+ ctx.close();
124
+ }
125
+ });
126
+ cmd.command("show").description("Show a single entry in full").argument("<id>", "Entry id (or prefix)").option("--json", "Output as JSON").action((id, options) => {
127
+ const ctx = openBrain();
128
+ try {
129
+ const entry = ctx.store.get(id);
130
+ if (!entry) {
131
+ console.error(chalk.red(`No entry matching '${id}'`));
132
+ process.exit(1);
133
+ }
134
+ console.log(
135
+ options.json ? JSON.stringify(entry, null, 2) : fmtEntry(entry, true)
136
+ );
137
+ } finally {
138
+ ctx.close();
139
+ }
140
+ });
141
+ cmd.command("sync").description("Push + pull brain entries online").option("--push", "Push only").option("--pull", "Pull only").option("--json", "Output as JSON").action(async (options) => {
142
+ const ctx = openBrain();
143
+ try {
144
+ if (!ctx.sync) {
145
+ console.error(
146
+ chalk.yellow(
147
+ "Online brain not configured. Run `stackmemory login`."
148
+ )
149
+ );
150
+ process.exit(1);
151
+ }
152
+ const result = options.push ? await ctx.sync.push() : options.pull ? await ctx.sync.pull() : await ctx.sync.sync();
153
+ if (options.json) {
154
+ console.log(JSON.stringify(result, null, 2));
155
+ } else if (result.success) {
156
+ console.log(
157
+ chalk.green(
158
+ `\u2713 pushed ${result.pushed}, pulled ${result.pulled} (applied ${result.applied})`
159
+ )
160
+ );
161
+ } else {
162
+ console.error(chalk.red(`Sync failed: ${result.error}`));
163
+ process.exit(1);
164
+ }
165
+ } finally {
166
+ ctx.close();
167
+ }
168
+ });
169
+ cmd.command("status").description("Show brain scope + entry counts").option("--json", "Output as JSON").action((options) => {
170
+ const ctx = openBrain();
171
+ try {
172
+ const status = {
173
+ projectId: ctx.projectId,
174
+ workspaceId: ctx.workspaceId || null,
175
+ repoEntries: ctx.store.count(false),
176
+ orgEntries: ctx.workspaceId ? ctx.store.count(true) : 0,
177
+ online: !!ctx.sync
178
+ };
179
+ if (options.json) {
180
+ console.log(JSON.stringify(status, null, 2));
181
+ return;
182
+ }
183
+ console.log(chalk.bold("Brain Status"));
184
+ console.log(` Repo (project): ${status.projectId}`);
185
+ console.log(
186
+ ` Org (workspace): ${status.workspaceId ?? chalk.dim("not logged in")}`
187
+ );
188
+ console.log(` Repo entries: ${status.repoEntries}`);
189
+ if (ctx.workspaceId)
190
+ console.log(` Org entries: ${status.orgEntries}`);
191
+ console.log(
192
+ ` Online sync: ${status.online ? chalk.green("configured") : chalk.dim("local-only")}`
193
+ );
194
+ } finally {
195
+ ctx.close();
196
+ }
197
+ });
198
+ return cmd;
199
+ }
200
+ function splitList(v) {
201
+ if (!v) return [];
202
+ return v.split(",").map((s) => s.trim()).filter(Boolean);
203
+ }
204
+ export {
205
+ createBrainCommand
206
+ };
@@ -0,0 +1,126 @@
1
+ import { fileURLToPath as __fileURLToPath } from 'url';
2
+ import { dirname as __pathDirname } from 'path';
3
+ const __filename = __fileURLToPath(import.meta.url);
4
+ const __dirname = __pathDirname(__filename);
5
+ import { Command } from "commander";
6
+ import chalk from "chalk";
7
+ import * as path from "path";
8
+ import { existsSync } from "fs";
9
+ import Database from "better-sqlite3";
10
+ import { ContentCache } from "../../core/cache/index.js";
11
+ function findProjectDbPath() {
12
+ let dir = process.cwd();
13
+ while (dir !== "/") {
14
+ const dbPath = path.join(dir, ".stackmemory", "context.db");
15
+ if (existsSync(dbPath)) return dbPath;
16
+ dir = path.dirname(dir);
17
+ }
18
+ const home = process.env["HOME"] || "/tmp";
19
+ const homePath = path.join(home, ".stackmemory", "context.db");
20
+ if (existsSync(homePath)) return homePath;
21
+ return void 0;
22
+ }
23
+ function getProjectDb() {
24
+ const dbPath = findProjectDbPath();
25
+ if (!dbPath) return void 0;
26
+ return new Database(dbPath);
27
+ }
28
+ function createCacheCommand() {
29
+ const cmd = new Command("cache").description(
30
+ "Content-hash cache \u2014 view token savings and manage cached content"
31
+ );
32
+ cmd.command("stats").description("Show cache statistics and token savings").option("--json", "Output as JSON").action((options) => {
33
+ const db = getProjectDb();
34
+ if (!db) {
35
+ console.log(chalk.yellow("No project database found"));
36
+ return;
37
+ }
38
+ try {
39
+ const cache = new ContentCache(db);
40
+ const stats = cache.getStats();
41
+ if (options.json) {
42
+ console.log(JSON.stringify(stats, null, 2));
43
+ return;
44
+ }
45
+ console.log(chalk.bold("Content Cache Statistics\n"));
46
+ console.log(` Entries: ${stats.totalEntries.toLocaleString()}`);
47
+ console.log(
48
+ ` Tokens cached: ${stats.totalTokensCached.toLocaleString()}`
49
+ );
50
+ console.log(
51
+ ` Tokens saved: ${chalk.green(stats.totalTokensSaved.toLocaleString())}`
52
+ );
53
+ console.log(` Hit rate: ${(stats.hitRate * 100).toFixed(1)}%`);
54
+ if (stats.topSources.length > 0) {
55
+ console.log(chalk.bold("\n Top sources by tokens saved:"));
56
+ for (const src of stats.topSources.slice(0, 5)) {
57
+ console.log(
58
+ ` ${chalk.dim(src.source)}: ${src.tokensSaved.toLocaleString()} tokens`
59
+ );
60
+ }
61
+ }
62
+ const costSaved = stats.totalTokensSaved / 1e6 * 3;
63
+ if (costSaved > 0.01) {
64
+ console.log(
65
+ `
66
+ Est. cost saved: ${chalk.green("$" + costSaved.toFixed(2))}`
67
+ );
68
+ }
69
+ } finally {
70
+ db.close();
71
+ }
72
+ });
73
+ cmd.command("clear").description("Clear the content cache").option("--confirm", "Skip confirmation prompt").action((options) => {
74
+ if (!options.confirm) {
75
+ console.log(
76
+ chalk.yellow(
77
+ "This will clear all cached content. Run with --confirm to proceed."
78
+ )
79
+ );
80
+ return;
81
+ }
82
+ const db = getProjectDb();
83
+ if (!db) {
84
+ console.log(chalk.yellow("No project database found"));
85
+ return;
86
+ }
87
+ try {
88
+ const cache = new ContentCache(db);
89
+ cache.clear();
90
+ console.log(chalk.green("Cache cleared"));
91
+ } finally {
92
+ db.close();
93
+ }
94
+ });
95
+ cmd.command("search <query>").description("Search cached content by keyword").option("--limit <n>", "Max results", "10").action((query, options) => {
96
+ const db = getProjectDb();
97
+ if (!db) {
98
+ console.log(chalk.yellow("No project database found"));
99
+ return;
100
+ }
101
+ try {
102
+ const cache = new ContentCache(db);
103
+ const results = cache.search(query, parseInt(options.limit));
104
+ if (results.length === 0) {
105
+ console.log(chalk.dim(`No cached content matching "${query}"`));
106
+ return;
107
+ }
108
+ console.log(chalk.bold(`${results.length} result(s):
109
+ `));
110
+ for (const entry of results) {
111
+ const preview = entry.content.slice(0, 120).replace(/\n/g, " ");
112
+ console.log(
113
+ ` ${chalk.dim(entry.hash.slice(0, 8))} ${chalk.dim(`(${entry.tokenCount} tokens, ${entry.hitCount} hits)`)}`
114
+ );
115
+ console.log(` ${preview}${entry.content.length > 120 ? "..." : ""}`);
116
+ console.log();
117
+ }
118
+ } finally {
119
+ db.close();
120
+ }
121
+ });
122
+ return cmd;
123
+ }
124
+ export {
125
+ createCacheCommand
126
+ };
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath as __fileURLToPath } from 'url';
3
+ import { dirname as __pathDirname } from 'path';
4
+ const __filename = __fileURLToPath(import.meta.url);
5
+ const __dirname = __pathDirname(__filename);
6
+ import { Command } from "commander";
7
+ import chalk from "chalk";
8
+ import { readdirSync, readFileSync, existsSync } from "node:fs";
9
+ import { join, extname } from "node:path";
10
+ const WIKI_DIR = join(process.cwd(), "wiki");
11
+ const WIKI_SOPS_DIR = join(WIKI_DIR, "sops");
12
+ const PROSE_SPEC_PATH = join(
13
+ process.cwd(),
14
+ "docs",
15
+ "specs",
16
+ "COMPANY-OS-PROSE.md"
17
+ );
18
+ function collectSopFiles() {
19
+ const files = [];
20
+ const dirs = [WIKI_DIR];
21
+ if (existsSync(WIKI_SOPS_DIR)) dirs.push(WIKI_SOPS_DIR);
22
+ for (const dir of dirs) {
23
+ if (!existsSync(dir)) continue;
24
+ for (const entry of readdirSync(dir)) {
25
+ if (extname(entry) !== ".md") continue;
26
+ const filePath = join(dir, entry);
27
+ const content = readFileSync(filePath, "utf8");
28
+ const idMatch = content.match(/^# (SOP-\d+)/m);
29
+ const titleMatch = content.match(/^# SOP-\d+\s+(.+)/m);
30
+ const statusMatch = content.match(/\*\*Status:\*\*\s*(\w+)/);
31
+ const proseMatch = content.match(/Related PROSE Expectation.*\[(E\.\d+)/);
32
+ files.push({
33
+ id: idMatch?.[1] ?? entry.replace(".md", ""),
34
+ title: titleMatch?.[1] ?? entry,
35
+ status: statusMatch?.[1] ?? "Unknown",
36
+ proseId: proseMatch?.[1] ?? null,
37
+ path: filePath
38
+ });
39
+ }
40
+ }
41
+ return files;
42
+ }
43
+ function loadValidProseIds() {
44
+ const ids = /* @__PURE__ */ new Set();
45
+ if (!existsSync(PROSE_SPEC_PATH)) return ids;
46
+ const content = readFileSync(PROSE_SPEC_PATH, "utf8");
47
+ const headingRegex = /^###\s+(E\.\d+)\s+/gm;
48
+ let match;
49
+ while ((match = headingRegex.exec(content)) !== null) {
50
+ ids.add(match[1]);
51
+ }
52
+ return ids;
53
+ }
54
+ function validateSop(content) {
55
+ const errors = [];
56
+ const required = ["## Objective", "## Procedure", "## Verification"];
57
+ for (const section of required) {
58
+ if (!content.includes(section)) {
59
+ errors.push(`Missing section: ${section}`);
60
+ }
61
+ }
62
+ if (!content.match(/Related PROSE Expectation.*\[E\.\d+/)) {
63
+ errors.push("Missing or invalid Related PROSE Expectation");
64
+ }
65
+ if (!content.match(/^# SOP-\d+/m)) {
66
+ errors.push("Missing or invalid SOP ID (expected # SOP-NNN ...)");
67
+ }
68
+ return errors;
69
+ }
70
+ function createCompanyOsCommand() {
71
+ const cmd = new Command("company-os").alias("cos").description("Manage Company OS processes, SOPs, and audits");
72
+ cmd.command("list").description("List SOPs in the Company OS wiki").option("--json", "Output as JSON").action(() => {
73
+ const sops = collectSopFiles();
74
+ if (sops.length === 0) {
75
+ console.log(chalk.yellow("No SOPs found in wiki/"));
76
+ return;
77
+ }
78
+ console.log(chalk.cyan(`
79
+ Company OS SOPs (${sops.length})
80
+ `));
81
+ for (const sop of sops.sort((a, b) => a.id.localeCompare(b.id))) {
82
+ console.log(
83
+ ` ${chalk.bold(sop.id)} ${chalk.white(sop.title.slice(0, 50))}`
84
+ );
85
+ console.log(
86
+ chalk.gray(
87
+ ` status: ${sop.status} PROSE: ${sop.proseId ?? "none"} ${sop.path}`
88
+ )
89
+ );
90
+ }
91
+ console.log();
92
+ });
93
+ cmd.command("validate").description("Validate all Company OS SOPs against the schema").option("--json", "Output as JSON").action(() => {
94
+ const sops = collectSopFiles();
95
+ const validProseIds = loadValidProseIds();
96
+ const results = [];
97
+ for (const sop of sops) {
98
+ const content = readFileSync(sop.path, "utf8");
99
+ const errors = validateSop(content);
100
+ if (sop.proseId && !validProseIds.has(sop.proseId)) {
101
+ errors.push(`Invalid PROSE Expectation: ${sop.proseId}`);
102
+ }
103
+ results.push({
104
+ id: sop.id,
105
+ path: sop.path,
106
+ valid: errors.length === 0,
107
+ errors
108
+ });
109
+ }
110
+ const validCount = results.filter((r) => r.valid).length;
111
+ if (results.every((r) => r.valid)) {
112
+ console.log(chalk.green(`
113
+ \u2713 All ${validCount} SOPs are valid.
114
+ `));
115
+ } else {
116
+ console.log(chalk.yellow(`
117
+ SOP Validation Results
118
+ `));
119
+ for (const result of results) {
120
+ const icon = result.valid ? chalk.green("\u2713") : chalk.red("\u2717");
121
+ console.log(`${icon} ${result.id}`);
122
+ for (const error of result.errors) {
123
+ console.log(chalk.gray(` - ${error}`));
124
+ }
125
+ }
126
+ console.log(chalk.gray(`
127
+ Valid: ${validCount} / ${results.length}
128
+ `));
129
+ process.exitCode = 1;
130
+ }
131
+ });
132
+ cmd.command("audit <process>").description(
133
+ "Audit a Company OS process against its SOP (e.g. onboarding, pto)"
134
+ ).option("--json", "Output as JSON").action((processName) => {
135
+ const sops = collectSopFiles();
136
+ const normalized = processName.toLowerCase().replace(/\s+/g, "-");
137
+ const sop = sops.find((s) => {
138
+ const sopNormalized = s.title.toLowerCase().replace(/\s+/g, "-");
139
+ return sopNormalized.includes(normalized) || s.id.toLowerCase().includes(normalized);
140
+ });
141
+ if (!sop) {
142
+ console.log(
143
+ chalk.red(`No SOP found matching process "${processName}"`)
144
+ );
145
+ console.log(chalk.gray("Run: stackmemory company-os list"));
146
+ process.exitCode = 1;
147
+ return;
148
+ }
149
+ const content = readFileSync(sop.path, "utf8");
150
+ const errors = validateSop(content);
151
+ const validProseIds = loadValidProseIds();
152
+ if (sop.proseId && !validProseIds.has(sop.proseId)) {
153
+ errors.push(`Invalid PROSE Expectation: ${sop.proseId}`);
154
+ }
155
+ const compliant = errors.length === 0;
156
+ console.log(chalk.cyan(`
157
+ Company OS Audit: ${sop.id} ${sop.title}
158
+ `));
159
+ console.log(` PROSE Expectation: ${sop.proseId ?? "none"}`);
160
+ console.log(
161
+ ` Status: ${compliant ? chalk.green("compliant") : chalk.red("non-compliant")}`
162
+ );
163
+ if (errors.length > 0) {
164
+ console.log(chalk.yellow("\n Findings:"));
165
+ for (const error of errors) {
166
+ console.log(chalk.gray(` - ${error}`));
167
+ }
168
+ } else {
169
+ console.log(
170
+ chalk.gray(
171
+ "\n Note: This POC audit validates SOP structure only. A full audit would check operational data against the SOP."
172
+ )
173
+ );
174
+ }
175
+ console.log();
176
+ if (!compliant) {
177
+ process.exitCode = 1;
178
+ }
179
+ });
180
+ return cmd;
181
+ }
182
+ export {
183
+ createCompanyOsCommand
184
+ };
@@ -34,6 +34,7 @@ function createContextCommands() {
34
34
  const frameManager = new FrameManager(db, projectId, {
35
35
  skipContextBridge: true
36
36
  });
37
+ await frameManager.initialize();
37
38
  const depth = frameManager.getStackDepth();
38
39
  const activePath = frameManager.getActiveFramePath();
39
40
  console.log(`
@@ -101,6 +102,7 @@ function createContextCommands() {
101
102
  const frameManager = new FrameManager(db, projectId, {
102
103
  skipContextBridge: true
103
104
  });
105
+ await frameManager.initialize();
104
106
  const activePath = frameManager.getActiveFramePath();
105
107
  const parentId = activePath.length > 0 ? activePath[activePath.length - 1].frame_id : void 0;
106
108
  let inputs = {};
@@ -147,6 +149,7 @@ function createContextCommands() {
147
149
  const frameManager = new FrameManager(db, projectId, {
148
150
  skipContextBridge: true
149
151
  });
152
+ await frameManager.initialize();
150
153
  const activePath = frameManager.getActiveFramePath();
151
154
  if (activePath.length === 0) {
152
155
  console.log("\u{1F4DA} Stack is already empty.");
@@ -193,6 +196,7 @@ function createContextCommands() {
193
196
  const frameManager = new FrameManager(db, projectId, {
194
197
  skipContextBridge: true
195
198
  });
199
+ await frameManager.initialize();
196
200
  const activePath = frameManager.getActiveFramePath();
197
201
  if (activePath.length === 0) {
198
202
  console.log("\u26A0\uFE0F No active context frame. Creating one...");
@@ -248,6 +252,7 @@ function createContextCommands() {
248
252
  const frameManager = new FrameManager(db, projectId, {
249
253
  skipContextBridge: true
250
254
  });
255
+ await frameManager.initialize();
251
256
  if (options.list || action === "list") {
252
257
  const worktreeFrames = db.prepare(
253
258
  `
@@ -107,6 +107,7 @@ The daemon provides:
107
107
  const services = [];
108
108
  if (newStatus.services.context.enabled) services.push("context");
109
109
  if (newStatus.services.linear.enabled) services.push("linear");
110
+ if (newStatus.services.github?.enabled) services.push("github");
110
111
  if (newStatus.services.maintenance?.enabled)
111
112
  services.push("maintenance");
112
113
  if (newStatus.services.memory?.enabled) services.push("memory");
@@ -232,6 +233,25 @@ The daemon provides:
232
233
  console.log(chalk.gray(` Syncs: ${lin.syncCount}`));
233
234
  }
234
235
  }
236
+ const gh = status.services.github;
237
+ if (gh) {
238
+ console.log(
239
+ ` GitHub: ${gh.enabled ? chalk.green("Enabled") : chalk.gray("Disabled")}`
240
+ );
241
+ if (gh.enabled) {
242
+ console.log(
243
+ chalk.gray(` Interval: ${config.github.interval} min`)
244
+ );
245
+ if (gh.syncCount) {
246
+ console.log(chalk.gray(` Refreshes: ${gh.syncCount}`));
247
+ }
248
+ if (gh.lastProjectionState) {
249
+ console.log(
250
+ chalk.gray(` Last PR state: ${gh.lastProjectionState}`)
251
+ );
252
+ }
253
+ }
254
+ }
235
255
  const maint = status.services.maintenance;
236
256
  if (maint) {
237
257
  console.log(
@@ -605,6 +625,22 @@ function getServiceHealthChecks(status, config) {
605
625
  detail: "Disabled"
606
626
  });
607
627
  }
628
+ const gh = status.services.github;
629
+ if (gh?.enabled) {
630
+ const intervalMs = config.github.interval * 6e4;
631
+ const overdue = gh.lastRun ? Date.now() - gh.lastRun > intervalMs * 2 : false;
632
+ checks.push({
633
+ name: "GitHub Service",
634
+ status: overdue ? "warn" : "ok",
635
+ detail: gh.lastRun ? `Last refresh: ${formatTimeAgo(gh.lastRun)} | Refreshes: ${gh.syncCount ?? 0}${gh.lastProjectionState ? ` | Last PR state: ${gh.lastProjectionState}` : ""}` : `Enabled (interval: ${config.github.interval}m) | No refreshes yet`
636
+ });
637
+ } else {
638
+ checks.push({
639
+ name: "GitHub Service",
640
+ status: "ok",
641
+ detail: "Disabled"
642
+ });
643
+ }
608
644
  const maint = status.services.maintenance;
609
645
  if (maint?.enabled) {
610
646
  const intervalMs = config.maintenance.interval * 6e4;
@@ -701,6 +737,11 @@ function buildHealthReport(status, config, paths) {
701
737
  enabled: status.services.linear.enabled,
702
738
  lastRun: status.services.linear.lastRun
703
739
  },
740
+ {
741
+ key: "github",
742
+ enabled: status.services.github?.enabled ?? false,
743
+ lastRun: status.services.github?.lastRun
744
+ },
704
745
  {
705
746
  key: "maintenance",
706
747
  enabled: status.services.maintenance?.enabled ?? false,