@contorium/mcp 0.8.1

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 (187) hide show
  1. package/README.md +97 -0
  2. package/bin/contorium-mcp.js +34 -0
  3. package/dist/autoContext.d.ts +2 -0
  4. package/dist/autoContext.js +2 -0
  5. package/dist/bootstrapCli.d.ts +2 -0
  6. package/dist/bootstrapCli.js +21 -0
  7. package/dist/conflicts.d.ts +19 -0
  8. package/dist/conflicts.js +49 -0
  9. package/dist/dashboardEnsure.d.ts +10 -0
  10. package/dist/dashboardEnsure.js +41 -0
  11. package/dist/intelligence.d.ts +16 -0
  12. package/dist/intelligence.js +38 -0
  13. package/dist/intentGraph.d.ts +20 -0
  14. package/dist/intentGraph.js +51 -0
  15. package/dist/mcpBootstrap.d.ts +8 -0
  16. package/dist/mcpBootstrap.js +81 -0
  17. package/dist/memoryStore.d.ts +18 -0
  18. package/dist/memoryStore.js +57 -0
  19. package/dist/paths.d.ts +12 -0
  20. package/dist/paths.js +66 -0
  21. package/dist/runtimeState.d.ts +14 -0
  22. package/dist/runtimeState.js +36 -0
  23. package/dist/server.d.ts +3 -0
  24. package/dist/server.js +567 -0
  25. package/dist/stateBuilder.d.ts +16 -0
  26. package/dist/stateBuilder.js +40 -0
  27. package/dist/understanding.d.ts +1 -0
  28. package/dist/understanding.js +1 -0
  29. package/dist/workspace.d.ts +12 -0
  30. package/dist/workspace.js +40 -0
  31. package/dist/workspaceConfig.d.ts +14 -0
  32. package/dist/workspaceConfig.js +80 -0
  33. package/node_modules/@contora/state-core/dist/adapterSync.d.ts +29 -0
  34. package/node_modules/@contora/state-core/dist/adapterSync.d.ts.map +1 -0
  35. package/node_modules/@contora/state-core/dist/adapterSync.js +142 -0
  36. package/node_modules/@contora/state-core/dist/adapters.d.ts +20 -0
  37. package/node_modules/@contora/state-core/dist/adapters.d.ts.map +1 -0
  38. package/node_modules/@contora/state-core/dist/adapters.js +24 -0
  39. package/node_modules/@contora/state-core/dist/bootstrap/bootstrapState.d.ts +6 -0
  40. package/node_modules/@contora/state-core/dist/bootstrap/bootstrapState.d.ts.map +1 -0
  41. package/node_modules/@contora/state-core/dist/bootstrap/bootstrapState.js +116 -0
  42. package/node_modules/@contora/state-core/dist/bootstrapArtifacts.d.ts +18 -0
  43. package/node_modules/@contora/state-core/dist/bootstrapArtifacts.d.ts.map +1 -0
  44. package/node_modules/@contora/state-core/dist/bootstrapArtifacts.js +43 -0
  45. package/node_modules/@contora/state-core/dist/bootstrapState.d.ts +6 -0
  46. package/node_modules/@contora/state-core/dist/bootstrapState.d.ts.map +1 -0
  47. package/node_modules/@contora/state-core/dist/bootstrapState.js +60 -0
  48. package/node_modules/@contora/state-core/dist/dashboardActivity.d.ts +12 -0
  49. package/node_modules/@contora/state-core/dist/dashboardActivity.d.ts.map +1 -0
  50. package/node_modules/@contora/state-core/dist/dashboardActivity.js +58 -0
  51. package/node_modules/@contora/state-core/dist/dualMode.d.ts +11 -0
  52. package/node_modules/@contora/state-core/dist/dualMode.d.ts.map +1 -0
  53. package/node_modules/@contora/state-core/dist/dualMode.js +38 -0
  54. package/node_modules/@contora/state-core/dist/gitScan.d.ts +7 -0
  55. package/node_modules/@contora/state-core/dist/gitScan.d.ts.map +1 -0
  56. package/node_modules/@contora/state-core/dist/gitScan.js +39 -0
  57. package/node_modules/@contora/state-core/dist/index.d.ts +44 -0
  58. package/node_modules/@contora/state-core/dist/index.d.ts.map +1 -0
  59. package/node_modules/@contora/state-core/dist/index.js +141 -0
  60. package/node_modules/@contora/state-core/dist/minimalSnapshot.d.ts +4 -0
  61. package/node_modules/@contora/state-core/dist/minimalSnapshot.d.ts.map +1 -0
  62. package/node_modules/@contora/state-core/dist/minimalSnapshot.js +60 -0
  63. package/node_modules/@contora/state-core/dist/scanner/gitScan.d.ts +7 -0
  64. package/node_modules/@contora/state-core/dist/scanner/gitScan.d.ts.map +1 -0
  65. package/node_modules/@contora/state-core/dist/scanner/gitScan.js +42 -0
  66. package/node_modules/@contora/state-core/dist/scanner/workspaceScanner.d.ts +4 -0
  67. package/node_modules/@contora/state-core/dist/scanner/workspaceScanner.d.ts.map +1 -0
  68. package/node_modules/@contora/state-core/dist/scanner/workspaceScanner.js +148 -0
  69. package/node_modules/@contora/state-core/dist/semiAutoHandoff.d.ts +46 -0
  70. package/node_modules/@contora/state-core/dist/semiAutoHandoff.d.ts.map +1 -0
  71. package/node_modules/@contora/state-core/dist/semiAutoHandoff.js +227 -0
  72. package/node_modules/@contora/state-core/dist/sourceMetadata.d.ts +4 -0
  73. package/node_modules/@contora/state-core/dist/sourceMetadata.d.ts.map +1 -0
  74. package/node_modules/@contora/state-core/dist/sourceMetadata.js +27 -0
  75. package/node_modules/@contora/state-core/dist/state-builder/buildFromScan.d.ts +5 -0
  76. package/node_modules/@contora/state-core/dist/state-builder/buildFromScan.d.ts.map +1 -0
  77. package/node_modules/@contora/state-core/dist/state-builder/buildFromScan.js +33 -0
  78. package/node_modules/@contora/state-core/dist/state-builder/normalization.d.ts +5 -0
  79. package/node_modules/@contora/state-core/dist/state-builder/normalization.d.ts.map +1 -0
  80. package/node_modules/@contora/state-core/dist/state-builder/normalization.js +107 -0
  81. package/node_modules/@contora/state-core/dist/state-builder/rebuildFromScan.d.ts +4 -0
  82. package/node_modules/@contora/state-core/dist/state-builder/rebuildFromScan.d.ts.map +1 -0
  83. package/node_modules/@contora/state-core/dist/state-builder/rebuildFromScan.js +22 -0
  84. package/node_modules/@contora/state-core/dist/state-builder/snapshot.d.ts +5 -0
  85. package/node_modules/@contora/state-core/dist/state-builder/snapshot.d.ts.map +1 -0
  86. package/node_modules/@contora/state-core/dist/state-builder/snapshot.js +53 -0
  87. package/node_modules/@contora/state-core/dist/state-builder/store.d.ts +7 -0
  88. package/node_modules/@contora/state-core/dist/state-builder/store.d.ts.map +1 -0
  89. package/node_modules/@contora/state-core/dist/state-builder/store.js +99 -0
  90. package/node_modules/@contora/state-core/dist/state-builder/types.d.ts +18 -0
  91. package/node_modules/@contora/state-core/dist/state-builder/types.d.ts.map +1 -0
  92. package/node_modules/@contora/state-core/dist/state-builder/types.js +20 -0
  93. package/node_modules/@contora/state-core/dist/types.d.ts +42 -0
  94. package/node_modules/@contora/state-core/dist/types.d.ts.map +1 -0
  95. package/node_modules/@contora/state-core/dist/types.js +3 -0
  96. package/node_modules/@contora/state-core/dist/understanding/buildUnderstanding.d.ts +22 -0
  97. package/node_modules/@contora/state-core/dist/understanding/buildUnderstanding.d.ts.map +1 -0
  98. package/node_modules/@contora/state-core/dist/understanding/buildUnderstanding.js +87 -0
  99. package/node_modules/@contora/state-core/dist/understanding/changeDetector.d.ts +7 -0
  100. package/node_modules/@contora/state-core/dist/understanding/changeDetector.d.ts.map +1 -0
  101. package/node_modules/@contora/state-core/dist/understanding/changeDetector.js +68 -0
  102. package/node_modules/@contora/state-core/dist/understanding/chpHandoff.d.ts +49 -0
  103. package/node_modules/@contora/state-core/dist/understanding/chpHandoff.d.ts.map +1 -0
  104. package/node_modules/@contora/state-core/dist/understanding/chpHandoff.js +214 -0
  105. package/node_modules/@contora/state-core/dist/understanding/extractor.d.ts +21 -0
  106. package/node_modules/@contora/state-core/dist/understanding/extractor.d.ts.map +1 -0
  107. package/node_modules/@contora/state-core/dist/understanding/extractor.js +173 -0
  108. package/node_modules/@contora/state-core/dist/understanding/formatCanonicalExport.d.ts +30 -0
  109. package/node_modules/@contora/state-core/dist/understanding/formatCanonicalExport.d.ts.map +1 -0
  110. package/node_modules/@contora/state-core/dist/understanding/formatCanonicalExport.js +363 -0
  111. package/node_modules/@contora/state-core/dist/understanding/formatHandoff.d.ts +20 -0
  112. package/node_modules/@contora/state-core/dist/understanding/formatHandoff.d.ts.map +1 -0
  113. package/node_modules/@contora/state-core/dist/understanding/formatHandoff.js +57 -0
  114. package/node_modules/@contora/state-core/dist/understanding/graphBuilder.d.ts +9 -0
  115. package/node_modules/@contora/state-core/dist/understanding/graphBuilder.d.ts.map +1 -0
  116. package/node_modules/@contora/state-core/dist/understanding/graphBuilder.js +107 -0
  117. package/node_modules/@contora/state-core/dist/understanding/handoffBuilder.d.ts +12 -0
  118. package/node_modules/@contora/state-core/dist/understanding/handoffBuilder.d.ts.map +1 -0
  119. package/node_modules/@contora/state-core/dist/understanding/handoffBuilder.js +101 -0
  120. package/node_modules/@contora/state-core/dist/understanding/impactAnalyzer.d.ts +5 -0
  121. package/node_modules/@contora/state-core/dist/understanding/impactAnalyzer.d.ts.map +1 -0
  122. package/node_modules/@contora/state-core/dist/understanding/impactAnalyzer.js +58 -0
  123. package/node_modules/@contora/state-core/dist/understanding/intentFusion.d.ts +9 -0
  124. package/node_modules/@contora/state-core/dist/understanding/intentFusion.d.ts.map +1 -0
  125. package/node_modules/@contora/state-core/dist/understanding/intentFusion.js +54 -0
  126. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/closureConstants.d.ts +24 -0
  127. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/closureConstants.d.ts.map +1 -0
  128. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/closureConstants.js +33 -0
  129. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/confidence.d.ts +20 -0
  130. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/confidence.d.ts.map +1 -0
  131. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/confidence.js +53 -0
  132. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/gitFrequency.d.ts +5 -0
  133. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/gitFrequency.d.ts.map +1 -0
  134. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/gitFrequency.js +19 -0
  135. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/hotspotBuilder.d.ts +13 -0
  136. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/hotspotBuilder.d.ts.map +1 -0
  137. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/hotspotBuilder.js +101 -0
  138. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/intentFunctionMapper.d.ts +22 -0
  139. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/intentFunctionMapper.d.ts.map +1 -0
  140. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/intentFunctionMapper.js +106 -0
  141. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/knowledgeGraphBuilder.d.ts +23 -0
  142. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/knowledgeGraphBuilder.d.ts.map +1 -0
  143. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/knowledgeGraphBuilder.js +415 -0
  144. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/normalize.d.ts +11 -0
  145. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/normalize.d.ts.map +1 -0
  146. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/normalize.js +136 -0
  147. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/rebuildTrigger.d.ts +13 -0
  148. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/rebuildTrigger.d.ts.map +1 -0
  149. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/rebuildTrigger.js +34 -0
  150. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/snapshotBuilder.d.ts +17 -0
  151. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/snapshotBuilder.d.ts.map +1 -0
  152. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/snapshotBuilder.js +73 -0
  153. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/store.d.ts +7 -0
  154. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/store.d.ts.map +1 -0
  155. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/store.js +127 -0
  156. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/types.d.ts +116 -0
  157. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/types.d.ts.map +1 -0
  158. package/node_modules/@contora/state-core/dist/understanding/knowledgeGraph/types.js +6 -0
  159. package/node_modules/@contora/state-core/dist/understanding/miniGraph.d.ts +4 -0
  160. package/node_modules/@contora/state-core/dist/understanding/miniGraph.d.ts.map +1 -0
  161. package/node_modules/@contora/state-core/dist/understanding/miniGraph.js +18 -0
  162. package/node_modules/@contora/state-core/dist/understanding/store.d.ts +24 -0
  163. package/node_modules/@contora/state-core/dist/understanding/store.d.ts.map +1 -0
  164. package/node_modules/@contora/state-core/dist/understanding/store.js +243 -0
  165. package/node_modules/@contora/state-core/dist/understanding/symbolValidator.d.ts +9 -0
  166. package/node_modules/@contora/state-core/dist/understanding/symbolValidator.d.ts.map +1 -0
  167. package/node_modules/@contora/state-core/dist/understanding/symbolValidator.js +51 -0
  168. package/node_modules/@contora/state-core/dist/understanding/timelineTracker.d.ts +3 -0
  169. package/node_modules/@contora/state-core/dist/understanding/timelineTracker.d.ts.map +1 -0
  170. package/node_modules/@contora/state-core/dist/understanding/timelineTracker.js +129 -0
  171. package/node_modules/@contora/state-core/dist/understanding/treeSitterParser.d.ts +24 -0
  172. package/node_modules/@contora/state-core/dist/understanding/treeSitterParser.d.ts.map +1 -0
  173. package/node_modules/@contora/state-core/dist/understanding/treeSitterParser.js +56 -0
  174. package/node_modules/@contora/state-core/dist/understanding/types.d.ts +136 -0
  175. package/node_modules/@contora/state-core/dist/understanding/types.d.ts.map +1 -0
  176. package/node_modules/@contora/state-core/dist/understanding/types.js +5 -0
  177. package/node_modules/@contora/state-core/dist/understanding/understandingGraphBuilder.d.ts +23 -0
  178. package/node_modules/@contora/state-core/dist/understanding/understandingGraphBuilder.d.ts.map +1 -0
  179. package/node_modules/@contora/state-core/dist/understanding/understandingGraphBuilder.js +67 -0
  180. package/node_modules/@contora/state-core/dist/version.d.ts +3 -0
  181. package/node_modules/@contora/state-core/dist/version.d.ts.map +1 -0
  182. package/node_modules/@contora/state-core/dist/version.js +21 -0
  183. package/node_modules/@contora/state-core/dist/workspaceScanner.d.ts +4 -0
  184. package/node_modules/@contora/state-core/dist/workspaceScanner.d.ts.map +1 -0
  185. package/node_modules/@contora/state-core/dist/workspaceScanner.js +112 -0
  186. package/node_modules/@contora/state-core/package.json +49 -0
  187. package/package.json +61 -0
package/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # @contorium/mcp
2
+
3
+ Standard MCP server for **Claude Code · Cursor · Codex · Gemini CLI**.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ # global
9
+ npm install -g @contorium/mcp
10
+
11
+ # or one-shot
12
+ npx @contorium/mcp
13
+ ```
14
+
15
+ Monorepo dev (this repo):
16
+
17
+ ```bash
18
+ cd packages/mcp && npm run build
19
+ npx contorium-mcp --workspace /path/to/project
20
+ ```
21
+
22
+ ## Publish to npm
23
+
24
+ Only **one** npm package is required: `@contorium/mcp`.
25
+ `@contora/state-core` is **bundled inside** the tarball (no separate `@contora` org needed).
26
+
27
+ ```bash
28
+ npm login # once
29
+ npm run publish:npm # publish @contorium/mcp
30
+ npm run publish:npm:dry-run # validate tarball without login
31
+ ```
32
+
33
+ ## Bootstrap (without starting stdio server)
34
+
35
+ Pre-sync `.contora` and schedule dashboard before opening the AI host:
36
+
37
+ ```bash
38
+ npx contorium-mcp bootstrap --workspace /path/to/your-project
39
+ # or after global install:
40
+ contorium-mcp bootstrap
41
+ ```
42
+
43
+ ## Start (stdio MCP server)
44
+
45
+ ```bash
46
+ npx contorium-mcp
47
+ # or with explicit workspace
48
+ npx contorium-mcp --workspace E:/your-project
49
+ ```
50
+
51
+ **Workspace priority:** `--workspace` → `CONTORIUM_WORKSPACE` → `.mcp.json` → current directory.
52
+
53
+ ## MCP host config
54
+
55
+ ### Cursor / Claude (`.mcp.json`)
56
+
57
+ ```json
58
+ {
59
+ "mcpServers": {
60
+ "contorium": {
61
+ "command": "npx",
62
+ "args": ["@contorium/mcp"],
63
+ "env": {
64
+ "CONTORIUM_WORKSPACE": "E:/your-project"
65
+ }
66
+ }
67
+ }
68
+ }
69
+ ```
70
+
71
+ ### Codex (`config.toml`)
72
+
73
+ ```toml
74
+ [mcp_servers.contorium]
75
+ command = "npx"
76
+ args = ["@contorium/mcp"]
77
+ env = { CONTORIUM_WORKSPACE = "." }
78
+ ```
79
+
80
+ ## Standard tools (MCP v1)
81
+
82
+ | Tool | Purpose |
83
+ |------|---------|
84
+ | `get_handoff_injection_status` | Semi-auto new-chat prompt |
85
+ | `confirm_handoff_injection` | User confirmed (Y) |
86
+ | `skip_handoff_injection` | User declined (N) |
87
+ | `get_project_handoff` | CHP v1 AI memory (compact / markdown / json) |
88
+ | `get_recent_changes` | File & function updates |
89
+ | `get_understanding_graph` | Call chains + impact |
90
+ | `get_runtime_state` | Bootstrap / dashboard / session |
91
+
92
+ Legacy tools (`get_project_change`, `get_project_graph`, …) remain available.
93
+
94
+ ## Requirements
95
+
96
+ - Node.js 18+
97
+ - Optional: `@contora/cli` (`npm i -g @contora/cli`) for dashboard bootstrap when not in monorepo
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+
5
+ * @contorium/mcp — standard npm entry (npx contorium-mcp / npx @contorium/mcp)
6
+
7
+ * Workspace: --workspace > CONTORIUM_WORKSPACE > .mcp.json > cwd
8
+
9
+ *
10
+
11
+ * Subcommands:
12
+
13
+ * contorium-mcp bootstrap [--workspace PATH] — sync .contora + schedule dashboard (no stdio server)
14
+
15
+ * contorium-mcp — start MCP stdio server (default)
16
+
17
+ */
18
+
19
+ const sub = process.argv[2];
20
+
21
+ if (sub === 'bootstrap') {
22
+
23
+ const { runMcpBootstrapCli } = await import('../dist/bootstrapCli.js');
24
+
25
+ await runMcpBootstrapCli(process.argv.slice(2));
26
+
27
+ } else {
28
+
29
+ const { startMcpServer } = await import('../dist/server.js');
30
+
31
+ await startMcpServer();
32
+
33
+ }
34
+
@@ -0,0 +1,2 @@
1
+ /** @deprecated Use readConfirmedHandoffContext from @contora/state-core — only present after user confirms injection. */
2
+ export { readConfirmedHandoffContext as readMcpAutoContext } from '@contora/state-core';
@@ -0,0 +1,2 @@
1
+ /** @deprecated Use readConfirmedHandoffContext from @contora/state-core — only present after user confirms injection. */
2
+ export { readConfirmedHandoffContext as readMcpAutoContext } from '@contora/state-core';
@@ -0,0 +1,2 @@
1
+ /** `contorium-mcp bootstrap` — one-shot runtime sync without starting stdio MCP. */
2
+ export declare function runMcpBootstrapCli(argv: string[]): Promise<void>;
@@ -0,0 +1,21 @@
1
+ import { prepareHandoffInjection, syncWorkspaceState } from '@contora/state-core';
2
+ import { scheduleMcpRuntimeBootstrap } from './dashboardEnsure.js';
3
+ import { findWorkspaceRoot, initWorkspaceFromArgv, resolveWorkspaceRoot } from './paths.js';
4
+ /** `contorium-mcp bootstrap` — one-shot runtime sync without starting stdio MCP. */
5
+ export async function runMcpBootstrapCli(argv) {
6
+ initWorkspaceFromArgv(argv);
7
+ const hint = resolveWorkspaceRoot();
8
+ const root = await findWorkspaceRoot(hint);
9
+ console.error(`[contorium-mcp] bootstrap workspace: ${root}`);
10
+ const result = await syncWorkspaceState(root, 'mcp', { forceArtifacts: true });
11
+ scheduleMcpRuntimeBootstrap(root);
12
+ const prep = await prepareHandoffInjection(root);
13
+ console.error(`[contorium-mcp] bootstrap: mode=${result.mode} artifacts=${result.created ? 'created' : 'updated'}`);
14
+ if (prep.shouldPrompt) {
15
+ console.error('[contorium-mcp] handoff: pending — new AI chat will auto-prompt (Enter/i · n skip in terminal, [?] in IDE)');
16
+ }
17
+ else if (prep.alreadyInjected) {
18
+ console.error('[contorium-mcp] handoff: already injected for this runtime');
19
+ }
20
+ console.error('[contorium-mcp] bootstrap complete — configure MCP host to spawn contorium-mcp for stdio server');
21
+ }
@@ -0,0 +1,19 @@
1
+ export interface McpStateConflictSource {
2
+ source: string;
3
+ detail: string;
4
+ }
5
+ export interface McpStateConflict {
6
+ id: string;
7
+ type: string;
8
+ title: string;
9
+ sources: McpStateConflictSource[];
10
+ status: string;
11
+ action: string;
12
+ detectedAt: number;
13
+ }
14
+ export interface McpConflictsArtifact {
15
+ version: number;
16
+ generatedAt: number;
17
+ conflicts: McpStateConflict[];
18
+ }
19
+ export declare function loadStateConflicts(workspaceRoot: string): Promise<McpConflictsArtifact | null>;
@@ -0,0 +1,49 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import { conflictsFile } from './paths.js';
3
+ export async function loadStateConflicts(workspaceRoot) {
4
+ const fp = conflictsFile(workspaceRoot);
5
+ try {
6
+ const text = await fs.readFile(fp, 'utf8');
7
+ const o = JSON.parse(text);
8
+ if (!o || o.version !== 1) {
9
+ return null;
10
+ }
11
+ const conflicts = [];
12
+ if (Array.isArray(o.conflicts)) {
13
+ for (const item of o.conflicts) {
14
+ if (!item || typeof item !== 'object') {
15
+ continue;
16
+ }
17
+ const c = item;
18
+ const sources = [];
19
+ if (Array.isArray(c.sources)) {
20
+ for (const s of c.sources) {
21
+ if (s && typeof s === 'object' && typeof s.detail === 'string') {
22
+ sources.push({
23
+ source: String(s.source ?? 'unknown'),
24
+ detail: s.detail,
25
+ });
26
+ }
27
+ }
28
+ }
29
+ conflicts.push({
30
+ id: typeof c.id === 'string' ? c.id : 'conf_unknown',
31
+ type: typeof c.type === 'string' ? c.type : 'unknown',
32
+ title: typeof c.title === 'string' ? c.title : 'Conflict',
33
+ sources,
34
+ status: typeof c.status === 'string' ? c.status : 'UNRESOLVED',
35
+ action: typeof c.action === 'string' ? c.action : 'Developer review required',
36
+ detectedAt: typeof c.detectedAt === 'number' ? c.detectedAt : 0,
37
+ });
38
+ }
39
+ }
40
+ return {
41
+ version: 1,
42
+ generatedAt: typeof o.generatedAt === 'number' ? o.generatedAt : 0,
43
+ conflicts,
44
+ };
45
+ }
46
+ catch {
47
+ return null;
48
+ }
49
+ }
@@ -0,0 +1,10 @@
1
+ export interface CliSpawnPlan {
2
+ command: string;
3
+ args: string[];
4
+ }
5
+ /** Resolve contorium CLI for bootstrap/wake — monorepo dev or global/npx when published. */
6
+ export declare function resolveContoriumSpawn(subcommand: string, workspaceRoot: string, extraArgs?: string[]): CliSpawnPlan;
7
+ /** CRBP — MCP client initialize → bootstrap runtime attach. */
8
+ export declare function scheduleMcpRuntimeBootstrap(workspaceRoot: string): void;
9
+ /** Activity update after bootstrap (file/git events). */
10
+ export declare function scheduleMcpDashboardWake(workspaceRoot: string, detail?: string): void;
@@ -0,0 +1,41 @@
1
+ import { spawn } from 'node:child_process';
2
+ import * as fs from 'node:fs';
3
+ import * as path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ /** Resolve contorium CLI for bootstrap/wake — monorepo dev or global/npx when published. */
6
+ export function resolveContoriumSpawn(subcommand, workspaceRoot, extraArgs = []) {
7
+ const root = path.resolve(workspaceRoot);
8
+ const here = path.dirname(fileURLToPath(import.meta.url));
9
+ const monorepoCli = path.resolve(here, '../../cli/bin/contorium.cjs');
10
+ if (fs.existsSync(monorepoCli)) {
11
+ return {
12
+ command: process.execPath,
13
+ args: [monorepoCli, subcommand, root, ...extraArgs],
14
+ };
15
+ }
16
+ return {
17
+ command: process.platform === 'win32' ? 'npx.cmd' : 'npx',
18
+ args: ['contorium', subcommand, root, ...extraArgs],
19
+ };
20
+ }
21
+ function spawnDetached(plan) {
22
+ const useShell = process.platform === 'win32' && !plan.args[0]?.endsWith('.cjs');
23
+ spawn(plan.command, plan.args, {
24
+ detached: true,
25
+ stdio: 'ignore',
26
+ windowsHide: true,
27
+ shell: useShell,
28
+ }).unref();
29
+ }
30
+ /** CRBP — MCP client initialize → bootstrap runtime attach. */
31
+ export function scheduleMcpRuntimeBootstrap(workspaceRoot) {
32
+ spawnDetached(resolveContoriumSpawn('bootstrap', workspaceRoot, ['--source', 'mcp']));
33
+ }
34
+ /** Activity update after bootstrap (file/git events). */
35
+ export function scheduleMcpDashboardWake(workspaceRoot, detail) {
36
+ const extra = ['--source', 'mcp'];
37
+ if (detail) {
38
+ extra.push('--detail', detail);
39
+ }
40
+ spawnDetached(resolveContoriumSpawn('dashboard', workspaceRoot, ['wake', ...extra]));
41
+ }
@@ -0,0 +1,16 @@
1
+ export interface McpStateSummary {
2
+ version: number;
3
+ generatedAt: number;
4
+ project_intent: string;
5
+ current_focus: string;
6
+ active_domains: string[];
7
+ active_problem_area: string;
8
+ activity_clusters: Array<{
9
+ cluster: string;
10
+ files: string[];
11
+ weight: number;
12
+ }>;
13
+ next_likely_actions: string[];
14
+ confidence: number;
15
+ }
16
+ export declare function loadProjectIntelligence(workspaceRoot: string): Promise<McpStateSummary | null>;
@@ -0,0 +1,38 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import { stateSummaryFile } from './paths.js';
3
+ export async function loadProjectIntelligence(workspaceRoot) {
4
+ const fp = stateSummaryFile(workspaceRoot);
5
+ try {
6
+ const text = await fs.readFile(fp, 'utf8');
7
+ const o = JSON.parse(text);
8
+ if (!o || typeof o !== 'object' || o.version !== 1) {
9
+ return null;
10
+ }
11
+ return {
12
+ version: 1,
13
+ generatedAt: typeof o.generatedAt === 'number' ? o.generatedAt : 0,
14
+ project_intent: typeof o.project_intent === 'string' ? o.project_intent : '',
15
+ current_focus: typeof o.current_focus === 'string' ? o.current_focus : '',
16
+ active_domains: Array.isArray(o.active_domains)
17
+ ? o.active_domains.filter((x) => typeof x === 'string')
18
+ : [],
19
+ active_problem_area: typeof o.active_problem_area === 'string' ? o.active_problem_area : '',
20
+ activity_clusters: Array.isArray(o.activity_clusters)
21
+ ? o.activity_clusters
22
+ .filter((x) => !!x && typeof x === 'object')
23
+ .map((c) => ({
24
+ cluster: typeof c.cluster === 'string' ? c.cluster : '',
25
+ files: Array.isArray(c.files) ? c.files.filter((f) => typeof f === 'string') : [],
26
+ weight: typeof c.weight === 'number' ? c.weight : 0,
27
+ }))
28
+ : [],
29
+ next_likely_actions: Array.isArray(o.next_likely_actions)
30
+ ? o.next_likely_actions.filter((x) => typeof x === 'string')
31
+ : [],
32
+ confidence: typeof o.confidence === 'number' ? o.confidence : 0,
33
+ };
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
@@ -0,0 +1,20 @@
1
+ export interface McpIntentGraph {
2
+ version: number;
3
+ updatedAt: number;
4
+ nodes: Array<{
5
+ id: string;
6
+ text: string;
7
+ status: string;
8
+ confidence: number;
9
+ relatedFiles: string[];
10
+ lastUpdated: number;
11
+ learnedAt: number;
12
+ }>;
13
+ edges: Array<{
14
+ from: string;
15
+ to: string;
16
+ type: string;
17
+ }>;
18
+ }
19
+ export declare function loadIntentGraph(workspaceRoot: string): Promise<McpIntentGraph | null>;
20
+ export declare function activeIntentNodes(graph: McpIntentGraph, max?: number): McpIntentGraph['nodes'];
@@ -0,0 +1,51 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import { intentGraphFile } from './paths.js';
3
+ export async function loadIntentGraph(workspaceRoot) {
4
+ const fp = intentGraphFile(workspaceRoot);
5
+ try {
6
+ const text = await fs.readFile(fp, 'utf8');
7
+ const o = JSON.parse(text);
8
+ if (!o || typeof o !== 'object' || o.version !== 1) {
9
+ return null;
10
+ }
11
+ return {
12
+ version: 1,
13
+ updatedAt: typeof o.updatedAt === 'number' ? o.updatedAt : 0,
14
+ nodes: Array.isArray(o.nodes)
15
+ ? o.nodes
16
+ .filter((x) => !!x && typeof x === 'object')
17
+ .map((n) => ({
18
+ id: typeof n.id === 'string' ? n.id : '',
19
+ text: typeof n.text === 'string' ? n.text : '',
20
+ status: typeof n.status === 'string' ? n.status : 'PARTIAL',
21
+ confidence: typeof n.confidence === 'number' ? n.confidence : 0,
22
+ relatedFiles: Array.isArray(n.relatedFiles)
23
+ ? n.relatedFiles.filter((f) => typeof f === 'string')
24
+ : [],
25
+ lastUpdated: typeof n.lastUpdated === 'number' ? n.lastUpdated : 0,
26
+ learnedAt: typeof n.learnedAt === 'number' ? n.learnedAt : 0,
27
+ }))
28
+ .filter((n) => n.id && n.text)
29
+ : [],
30
+ edges: Array.isArray(o.edges)
31
+ ? o.edges
32
+ .filter((x) => !!x && typeof x === 'object')
33
+ .map((e) => ({
34
+ from: typeof e.from === 'string' ? e.from : '',
35
+ to: typeof e.to === 'string' ? e.to : '',
36
+ type: typeof e.type === 'string' ? e.type : 'RELATED_TO',
37
+ }))
38
+ .filter((e) => e.from && e.to)
39
+ : [],
40
+ };
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ }
46
+ export function activeIntentNodes(graph, max = 8) {
47
+ return graph.nodes
48
+ .filter((n) => n.status === 'ACTIVE' || n.status === 'WEAKENING' || n.status === 'PARTIAL')
49
+ .sort((a, b) => b.confidence - a.confidence)
50
+ .slice(0, max);
51
+ }
@@ -0,0 +1,8 @@
1
+ /** MCP startup bootstrap. */
2
+ export declare function ensureWorkspaceBootstrapped(workspaceRoot: string): Promise<{
3
+ bootstrapped: boolean;
4
+ mode: string;
5
+ }>;
6
+ /** Light sync: 5s poll + watch events dir and git HEAD. */
7
+ export declare function startMcpLightSync(workspaceRoot: string): void;
8
+ export declare function stopMcpLightSync(): void;
@@ -0,0 +1,81 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { syncWorkspaceState } from '@contora/state-core';
4
+ import { scheduleMcpDashboardWake } from './dashboardEnsure.js';
5
+ import { checkActiveRuntime, confirmHandoffInjection, readHandoffInjectionState, syncInjectionWithRuntime, } from '@contora/state-core';
6
+ const CONTORA_EVENTS = '.contora/events';
7
+ const SYNC_MS = 5_000;
8
+ const DEBOUNCE_MS = 400;
9
+ let syncTimer;
10
+ let debounceTimer;
11
+ let eventsWatcher;
12
+ let gitHeadWatcher;
13
+ function scheduleReactiveSync(workspaceRoot) {
14
+ if (debounceTimer) {
15
+ clearTimeout(debounceTimer);
16
+ }
17
+ debounceTimer = setTimeout(() => {
18
+ debounceTimer = undefined;
19
+ void syncWorkspaceState(workspaceRoot, 'mcp')
20
+ .then(async () => {
21
+ scheduleMcpDashboardWake(workspaceRoot, 'mcp-reactive-sync');
22
+ await syncInjectionWithRuntime(workspaceRoot);
23
+ const { runtime_id } = await checkActiveRuntime(workspaceRoot);
24
+ const injection = await readHandoffInjectionState(workspaceRoot);
25
+ if (runtime_id &&
26
+ injection?.runtime_id === runtime_id &&
27
+ injection.status === 'injected') {
28
+ await confirmHandoffInjection(workspaceRoot, injection.format ?? 'markdown');
29
+ }
30
+ })
31
+ .catch(() => undefined);
32
+ }, DEBOUNCE_MS);
33
+ }
34
+ /** MCP startup bootstrap. */
35
+ export async function ensureWorkspaceBootstrapped(workspaceRoot) {
36
+ const result = await syncWorkspaceState(workspaceRoot, 'mcp', { forceArtifacts: true });
37
+ if (result.created) {
38
+ console.error('[contorium-mcp] bootstrap: created .contora/state.json (scan-driven)');
39
+ }
40
+ return { bootstrapped: result.created, mode: result.mode };
41
+ }
42
+ /** Light sync: 5s poll + watch events dir and git HEAD. */
43
+ export function startMcpLightSync(workspaceRoot) {
44
+ stopMcpLightSync();
45
+ const root = path.resolve(workspaceRoot);
46
+ syncTimer = setInterval(() => {
47
+ void syncWorkspaceState(root, 'mcp').catch(() => undefined);
48
+ }, SYNC_MS);
49
+ syncTimer.unref?.();
50
+ const eventsDir = path.join(root, CONTORA_EVENTS);
51
+ try {
52
+ fs.mkdirSync(eventsDir, { recursive: true });
53
+ eventsWatcher = fs.watch(eventsDir, () => scheduleReactiveSync(root));
54
+ eventsWatcher.unref?.();
55
+ }
56
+ catch {
57
+ /* optional */
58
+ }
59
+ const gitHead = path.join(root, '.git', 'HEAD');
60
+ try {
61
+ gitHeadWatcher = fs.watch(gitHead, () => scheduleReactiveSync(root));
62
+ gitHeadWatcher.unref?.();
63
+ }
64
+ catch {
65
+ /* not a git repo */
66
+ }
67
+ }
68
+ export function stopMcpLightSync() {
69
+ if (syncTimer) {
70
+ clearInterval(syncTimer);
71
+ syncTimer = undefined;
72
+ }
73
+ if (debounceTimer) {
74
+ clearTimeout(debounceTimer);
75
+ debounceTimer = undefined;
76
+ }
77
+ eventsWatcher?.close();
78
+ eventsWatcher = undefined;
79
+ gitHeadWatcher?.close();
80
+ gitHeadWatcher = undefined;
81
+ }
@@ -0,0 +1,18 @@
1
+ export type MemoryEntryType = 'note' | 'decision' | 'architecture';
2
+ export interface MemoryEntry {
3
+ value: string;
4
+ type: MemoryEntryType;
5
+ timestamp: number;
6
+ }
7
+ export interface MemoryFile {
8
+ version: 1;
9
+ entries: Record<string, MemoryEntry>;
10
+ }
11
+ export declare function storeMemory(workspaceRoot: string, key: string, value: string, type?: MemoryEntryType): Promise<{
12
+ success: true;
13
+ key: string;
14
+ }>;
15
+ export declare function getMemory(workspaceRoot: string, key: string): Promise<MemoryEntry | null>;
16
+ export declare function searchMemory(workspaceRoot: string, query: string): Promise<Array<{
17
+ key: string;
18
+ } & MemoryEntry>>;
@@ -0,0 +1,57 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as path from 'node:path';
3
+ import { mcpMemoryFile } from './paths.js';
4
+ function emptyFile() {
5
+ return { version: 1, entries: {} };
6
+ }
7
+ async function loadFile(workspaceRoot) {
8
+ const fp = mcpMemoryFile(workspaceRoot);
9
+ try {
10
+ const raw = await fs.readFile(fp, 'utf8');
11
+ const parsed = JSON.parse(raw);
12
+ if (!parsed || typeof parsed !== 'object' || !parsed.entries || typeof parsed.entries !== 'object') {
13
+ return emptyFile();
14
+ }
15
+ return { version: 1, entries: parsed.entries };
16
+ }
17
+ catch {
18
+ return emptyFile();
19
+ }
20
+ }
21
+ async function saveFile(workspaceRoot, data) {
22
+ const fp = mcpMemoryFile(workspaceRoot);
23
+ await fs.mkdir(path.dirname(fp), { recursive: true });
24
+ await fs.writeFile(fp, JSON.stringify(data, null, 2), 'utf8');
25
+ }
26
+ export async function storeMemory(workspaceRoot, key, value, type = 'note') {
27
+ const k = key.trim();
28
+ if (!k) {
29
+ throw new Error('key is required');
30
+ }
31
+ const file = await loadFile(workspaceRoot);
32
+ file.entries[k] = {
33
+ value,
34
+ type,
35
+ timestamp: Date.now(),
36
+ };
37
+ await saveFile(workspaceRoot, file);
38
+ return { success: true, key: k };
39
+ }
40
+ export async function getMemory(workspaceRoot, key) {
41
+ const file = await loadFile(workspaceRoot);
42
+ return file.entries[key.trim()] ?? null;
43
+ }
44
+ export async function searchMemory(workspaceRoot, query) {
45
+ const q = query.trim().toLowerCase();
46
+ if (!q) {
47
+ return [];
48
+ }
49
+ const file = await loadFile(workspaceRoot);
50
+ const results = [];
51
+ for (const [key, entry] of Object.entries(file.entries)) {
52
+ if (key.toLowerCase().includes(q) || entry.value.toLowerCase().includes(q)) {
53
+ results.push({ key, ...entry });
54
+ }
55
+ }
56
+ return results.sort((a, b) => b.timestamp - a.timestamp).slice(0, 32);
57
+ }
@@ -0,0 +1,12 @@
1
+ /** Apply CLI --workspace before other resolution (call once at startup). */
2
+ export declare function initWorkspaceFromArgv(argv?: string[]): string;
3
+ /** Workspace root for MCP (priority: startup override → env → .mcp.json → cwd). */
4
+ export declare function resolveWorkspaceRoot(): string;
5
+ export declare function contoraDir(workspaceRoot: string): string;
6
+ export declare function mcpMemoryFile(workspaceRoot: string): string;
7
+ export declare function stateSummaryFile(workspaceRoot: string): string;
8
+ export declare function intentGraphFile(workspaceRoot: string): string;
9
+ export declare function projectStateFile(workspaceRoot: string): string;
10
+ export declare function projectSnapshotFile(workspaceRoot: string): string;
11
+ export declare function conflictsFile(workspaceRoot: string): string;
12
+ export declare function findWorkspaceRoot(startDir: string): Promise<string>;
package/dist/paths.js ADDED
@@ -0,0 +1,66 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as path from 'node:path';
3
+ import { resolveMcpStartupConfig, setStartupWorkspace } from './workspaceConfig.js';
4
+ const CONTORA_DATA_DIR = '.contora';
5
+ const LEGACY_DATA_DIR = '.context-recall';
6
+ /** Apply CLI --workspace before other resolution (call once at startup). */
7
+ export function initWorkspaceFromArgv(argv = process.argv.slice(2)) {
8
+ const config = resolveMcpStartupConfig(argv);
9
+ if (config.workspaceFromArgv) {
10
+ setStartupWorkspace(config.workspaceHint);
11
+ }
12
+ return config.workspaceHint;
13
+ }
14
+ /** Workspace root for MCP (priority: startup override → env → .mcp.json → cwd). */
15
+ export function resolveWorkspaceRoot() {
16
+ const config = resolveMcpStartupConfig();
17
+ return config.workspaceHint;
18
+ }
19
+ export function contoraDir(workspaceRoot) {
20
+ return path.join(workspaceRoot, CONTORA_DATA_DIR);
21
+ }
22
+ export function mcpMemoryFile(workspaceRoot) {
23
+ return path.join(contoraDir(workspaceRoot), 'mcp', 'memories.json');
24
+ }
25
+ export function stateSummaryFile(workspaceRoot) {
26
+ return path.join(contoraDir(workspaceRoot), 'intelligence', 'state-summary.json');
27
+ }
28
+ export function intentGraphFile(workspaceRoot) {
29
+ return path.join(contoraDir(workspaceRoot), 'intent-graph', 'graph.json');
30
+ }
31
+ export function projectStateFile(workspaceRoot) {
32
+ return path.join(contoraDir(workspaceRoot), 'state-builder', 'project-state.json');
33
+ }
34
+ export function projectSnapshotFile(workspaceRoot) {
35
+ return path.join(contoraDir(workspaceRoot), 'state-builder', 'project-snapshot.md');
36
+ }
37
+ export function conflictsFile(workspaceRoot) {
38
+ return path.join(contoraDir(workspaceRoot), 'state-engine', 'conflicts.json');
39
+ }
40
+ export async function findWorkspaceRoot(startDir) {
41
+ let dir = path.resolve(startDir);
42
+ for (let i = 0; i < 12; i++) {
43
+ const primary = path.join(dir, CONTORA_DATA_DIR, 'state.json');
44
+ const legacy = path.join(dir, LEGACY_DATA_DIR, 'state.json');
45
+ try {
46
+ await fs.access(primary);
47
+ return dir;
48
+ }
49
+ catch {
50
+ /* continue */
51
+ }
52
+ try {
53
+ await fs.access(legacy);
54
+ return dir;
55
+ }
56
+ catch {
57
+ /* continue */
58
+ }
59
+ const parent = path.dirname(dir);
60
+ if (parent === dir) {
61
+ break;
62
+ }
63
+ dir = parent;
64
+ }
65
+ return path.resolve(startDir);
66
+ }