agent-working-memory 0.7.16 → 0.8.5

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 (200) hide show
  1. package/README.md +156 -15
  2. package/dist/adapters/claude-code.d.ts.map +1 -1
  3. package/dist/adapters/claude-code.js +2 -16
  4. package/dist/adapters/claude-code.js.map +1 -1
  5. package/dist/adapters/codex.d.ts.map +1 -1
  6. package/dist/adapters/codex.js +2 -11
  7. package/dist/adapters/codex.js.map +1 -1
  8. package/dist/adapters/common.d.ts +18 -0
  9. package/dist/adapters/common.d.ts.map +1 -1
  10. package/dist/adapters/common.js +254 -141
  11. package/dist/adapters/common.js.map +1 -1
  12. package/dist/adapters/cursor.d.ts.map +1 -1
  13. package/dist/adapters/cursor.js +2 -15
  14. package/dist/adapters/cursor.js.map +1 -1
  15. package/dist/adapters/http.d.ts.map +1 -1
  16. package/dist/adapters/http.js +28 -34
  17. package/dist/adapters/http.js.map +1 -1
  18. package/dist/api/routes.d.ts +1 -1
  19. package/dist/api/routes.d.ts.map +1 -1
  20. package/dist/api/routes.js +280 -113
  21. package/dist/api/routes.js.map +1 -1
  22. package/dist/cli/migrate.d.ts +42 -0
  23. package/dist/cli/migrate.d.ts.map +1 -0
  24. package/dist/cli/migrate.js +268 -0
  25. package/dist/cli/migrate.js.map +1 -0
  26. package/dist/cli.js +45 -1
  27. package/dist/cli.js.map +1 -1
  28. package/dist/coordination/circuit-breaker.d.ts +24 -0
  29. package/dist/coordination/circuit-breaker.d.ts.map +1 -0
  30. package/dist/coordination/circuit-breaker.js +70 -0
  31. package/dist/coordination/circuit-breaker.js.map +1 -0
  32. package/dist/coordination/failure-modes.d.ts +21 -0
  33. package/dist/coordination/failure-modes.d.ts.map +1 -0
  34. package/dist/coordination/failure-modes.js +49 -0
  35. package/dist/coordination/failure-modes.js.map +1 -0
  36. package/dist/coordination/peer-decisions.js +7 -7
  37. package/dist/coordination/routes.d.ts.map +1 -1
  38. package/dist/coordination/routes.js +44 -1
  39. package/dist/coordination/routes.js.map +1 -1
  40. package/dist/coordination/schema.d.ts.map +1 -1
  41. package/dist/coordination/schema.js +18 -0
  42. package/dist/coordination/schema.js.map +1 -1
  43. package/dist/coordination/schemas.d.ts +9 -9
  44. package/dist/coordination/stale.d.ts +9 -1
  45. package/dist/coordination/stale.d.ts.map +1 -1
  46. package/dist/coordination/stale.js +53 -9
  47. package/dist/coordination/stale.js.map +1 -1
  48. package/dist/core/embeddings.d.ts +16 -10
  49. package/dist/core/embeddings.d.ts.map +1 -1
  50. package/dist/core/embeddings.js +54 -38
  51. package/dist/core/embeddings.js.map +1 -1
  52. package/dist/core/ml-worker-entry.d.ts +17 -0
  53. package/dist/core/ml-worker-entry.d.ts.map +1 -0
  54. package/dist/core/ml-worker-entry.js +193 -0
  55. package/dist/core/ml-worker-entry.js.map +1 -0
  56. package/dist/core/ml-worker.d.ts +59 -0
  57. package/dist/core/ml-worker.d.ts.map +1 -0
  58. package/dist/core/ml-worker.js +253 -0
  59. package/dist/core/ml-worker.js.map +1 -0
  60. package/dist/core/query-expander.d.ts +13 -15
  61. package/dist/core/query-expander.d.ts.map +1 -1
  62. package/dist/core/query-expander.js +48 -54
  63. package/dist/core/query-expander.js.map +1 -1
  64. package/dist/core/reranker.d.ts +9 -19
  65. package/dist/core/reranker.d.ts.map +1 -1
  66. package/dist/core/reranker.js +33 -35
  67. package/dist/core/reranker.js.map +1 -1
  68. package/dist/core/salience.d.ts +32 -5
  69. package/dist/core/salience.d.ts.map +1 -1
  70. package/dist/core/salience.js +201 -28
  71. package/dist/core/salience.js.map +1 -1
  72. package/dist/core/write-pipeline.d.ts +128 -0
  73. package/dist/core/write-pipeline.d.ts.map +1 -0
  74. package/dist/core/write-pipeline.js +405 -0
  75. package/dist/core/write-pipeline.js.map +1 -0
  76. package/dist/engine/activation.d.ts +2 -2
  77. package/dist/engine/activation.d.ts.map +1 -1
  78. package/dist/engine/activation.js +238 -115
  79. package/dist/engine/activation.js.map +1 -1
  80. package/dist/engine/confidence.d.ts +62 -0
  81. package/dist/engine/confidence.d.ts.map +1 -0
  82. package/dist/engine/confidence.js +100 -0
  83. package/dist/engine/confidence.js.map +1 -0
  84. package/dist/engine/connections.d.ts +47 -8
  85. package/dist/engine/connections.d.ts.map +1 -1
  86. package/dist/engine/connections.js +83 -22
  87. package/dist/engine/connections.js.map +1 -1
  88. package/dist/engine/consolidation-scheduler.d.ts +46 -10
  89. package/dist/engine/consolidation-scheduler.d.ts.map +1 -1
  90. package/dist/engine/consolidation-scheduler.js +170 -50
  91. package/dist/engine/consolidation-scheduler.js.map +1 -1
  92. package/dist/engine/consolidation.d.ts +16 -2
  93. package/dist/engine/consolidation.d.ts.map +1 -1
  94. package/dist/engine/consolidation.js +143 -44
  95. package/dist/engine/consolidation.js.map +1 -1
  96. package/dist/engine/eval.d.ts +2 -2
  97. package/dist/engine/eval.d.ts.map +1 -1
  98. package/dist/engine/eval.js +9 -9
  99. package/dist/engine/eval.js.map +1 -1
  100. package/dist/engine/eviction.d.ts +4 -4
  101. package/dist/engine/eviction.d.ts.map +1 -1
  102. package/dist/engine/eviction.js +15 -15
  103. package/dist/engine/eviction.js.map +1 -1
  104. package/dist/engine/retraction.d.ts +93 -5
  105. package/dist/engine/retraction.d.ts.map +1 -1
  106. package/dist/engine/retraction.js +238 -19
  107. package/dist/engine/retraction.js.map +1 -1
  108. package/dist/engine/staging.d.ts +1 -1
  109. package/dist/engine/staging.d.ts.map +1 -1
  110. package/dist/engine/staging.js +3 -3
  111. package/dist/engine/staging.js.map +1 -1
  112. package/dist/hooks/sidecar.d.ts +1 -1
  113. package/dist/hooks/sidecar.d.ts.map +1 -1
  114. package/dist/hooks/sidecar.js +4 -4
  115. package/dist/hooks/sidecar.js.map +1 -1
  116. package/dist/index.js +48 -31
  117. package/dist/index.js.map +1 -1
  118. package/dist/mcp.js +110 -155
  119. package/dist/mcp.js.map +1 -1
  120. package/dist/storage/factory.d.ts +55 -0
  121. package/dist/storage/factory.d.ts.map +1 -0
  122. package/dist/storage/factory.js +135 -0
  123. package/dist/storage/factory.js.map +1 -0
  124. package/dist/storage/pglite-schema.d.ts +20 -0
  125. package/dist/storage/pglite-schema.d.ts.map +1 -0
  126. package/dist/storage/pglite-schema.js +165 -0
  127. package/dist/storage/pglite-schema.js.map +1 -0
  128. package/dist/storage/pglite.d.ts +187 -0
  129. package/dist/storage/pglite.d.ts.map +1 -0
  130. package/dist/storage/pglite.js +1114 -0
  131. package/dist/storage/pglite.js.map +1 -0
  132. package/dist/storage/sqlite.d.ts +117 -0
  133. package/dist/storage/sqlite.d.ts.map +1 -1
  134. package/dist/storage/sqlite.js +357 -8
  135. package/dist/storage/sqlite.js.map +1 -1
  136. package/dist/storage/store.d.ts +63 -0
  137. package/dist/storage/store.d.ts.map +1 -0
  138. package/dist/storage/store.js +27 -0
  139. package/dist/storage/store.js.map +1 -0
  140. package/dist/types/engram.d.ts +92 -2
  141. package/dist/types/engram.d.ts.map +1 -1
  142. package/dist/types/engram.js.map +1 -1
  143. package/package.json +3 -1
  144. package/src/adapters/claude-code.ts +218 -234
  145. package/src/adapters/codex.ts +252 -262
  146. package/src/adapters/common.ts +411 -284
  147. package/src/adapters/cursor.ts +135 -150
  148. package/src/adapters/http.ts +93 -100
  149. package/src/adapters/index.ts +31 -31
  150. package/src/adapters/types.ts +75 -75
  151. package/src/api/index.ts +3 -3
  152. package/src/api/routes.ts +367 -120
  153. package/src/cli/migrate.ts +307 -0
  154. package/src/cli.ts +37 -1
  155. package/src/coordination/circuit-breaker.ts +83 -0
  156. package/src/coordination/events.ts +90 -90
  157. package/src/coordination/failure-modes.ts +50 -0
  158. package/src/coordination/peer-decisions.ts +105 -105
  159. package/src/coordination/plugin-loader.ts +60 -60
  160. package/src/coordination/plugin.ts +44 -44
  161. package/src/coordination/routes.ts +56 -1
  162. package/src/coordination/schema.ts +13 -0
  163. package/src/coordination/stale.ts +80 -11
  164. package/src/coordination/types.ts +311 -311
  165. package/src/coordination/write-mutex.ts +69 -69
  166. package/src/core/auto-tagger.ts +168 -168
  167. package/src/core/decay.ts +63 -63
  168. package/src/core/embeddings.ts +110 -93
  169. package/src/core/index.ts +5 -5
  170. package/src/core/logger.ts +36 -36
  171. package/src/core/ml-worker-entry.ts +194 -0
  172. package/src/core/ml-worker.ts +281 -0
  173. package/src/core/query-expander.ts +122 -128
  174. package/src/core/reranker.ts +119 -125
  175. package/src/core/salience.ts +200 -33
  176. package/src/core/write-pipeline.ts +521 -0
  177. package/src/engine/activation.ts +235 -109
  178. package/src/engine/confidence.ts +120 -0
  179. package/src/engine/connections.ts +162 -103
  180. package/src/engine/consolidation-scheduler.ts +242 -125
  181. package/src/engine/consolidation.ts +138 -45
  182. package/src/engine/eval.ts +102 -102
  183. package/src/engine/eviction.ts +101 -101
  184. package/src/engine/index.ts +8 -8
  185. package/src/engine/retraction.ts +366 -116
  186. package/src/engine/staging.ts +74 -74
  187. package/src/hooks/sidecar.ts +5 -5
  188. package/src/index.ts +42 -28
  189. package/src/mcp.ts +105 -152
  190. package/src/storage/factory.ts +147 -0
  191. package/src/storage/index.ts +3 -3
  192. package/src/storage/pglite-schema.ts +166 -0
  193. package/src/storage/pglite.ts +1363 -0
  194. package/src/storage/sqlite.ts +402 -7
  195. package/src/storage/store.ts +80 -0
  196. package/src/types/agent.ts +67 -67
  197. package/src/types/checkpoint.ts +46 -46
  198. package/src/types/engram.ts +101 -3
  199. package/src/types/eval.ts +100 -100
  200. package/src/types/index.ts +6 -6
@@ -1,262 +1,252 @@
1
- // Copyright 2026 Robert Winter / Complete Ideas
2
- // SPDX-License-Identifier: Apache-2.0
3
-
4
- /**
5
- * Codex CLI adapter — writes TOML config, AGENTS.md instructions.
6
- *
7
- * Codex stores MCP config in ~/.codex/config.toml (global only).
8
- * Uses snake_case [mcp_servers.name] sections.
9
- */
10
-
11
- import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
12
- import { join, dirname, basename } from 'node:path';
13
- import type { CLIAdapter, SetupContext, DiagnosticResult } from './types.js';
14
- import { resolveMcpCommand, homedir, AWM_INSTRUCTION_CONTENT } from './common.js';
15
-
16
- // ─── Minimal TOML read/write ──────────────────────────
17
- // Only handles the flat structure Codex uses: [section.name] with key = "value" or key = ["array"]
18
-
19
- interface TomlSection {
20
- [key: string]: string | string[];
21
- }
22
-
23
- interface TomlDoc {
24
- /** Sections: key is the full dotted section name */
25
- sections: Map<string, TomlSection>;
26
- /** Lines before the first section (top-level keys, comments) */
27
- preamble: string[];
28
- }
29
-
30
- function parseTOML(content: string): TomlDoc {
31
- const doc: TomlDoc = { sections: new Map(), preamble: [] };
32
- let currentSection = '';
33
- const lines = content.split('\n');
34
-
35
- for (const line of lines) {
36
- const trimmed = line.trim();
37
-
38
- // Section header
39
- const sectionMatch = trimmed.match(/^\[([^\]]+)\]$/);
40
- if (sectionMatch) {
41
- currentSection = sectionMatch[1];
42
- if (!doc.sections.has(currentSection)) {
43
- doc.sections.set(currentSection, {});
44
- }
45
- continue;
46
- }
47
-
48
- // Skip comments and empty lines
49
- if (!trimmed || trimmed.startsWith('#')) {
50
- if (!currentSection) doc.preamble.push(line);
51
- continue;
52
- }
53
-
54
- // Key = value
55
- const eqIdx = trimmed.indexOf('=');
56
- if (eqIdx === -1) {
57
- if (!currentSection) doc.preamble.push(line);
58
- continue;
59
- }
60
-
61
- const key = trimmed.slice(0, eqIdx).trim();
62
- const rawVal = trimmed.slice(eqIdx + 1).trim();
63
-
64
- if (!currentSection) {
65
- if (!doc.sections.has('')) doc.sections.set('', {});
66
- doc.sections.get('')![key] = parseTomlValue(rawVal);
67
- continue;
68
- }
69
-
70
- if (!doc.sections.has(currentSection)) {
71
- doc.sections.set(currentSection, {});
72
- }
73
- doc.sections.get(currentSection)![key] = parseTomlValue(rawVal);
74
- }
75
-
76
- return doc;
77
- }
78
-
79
- function parseTomlValue(raw: string): string | string[] {
80
- // Array: ["a", "b"]
81
- if (raw.startsWith('[')) {
82
- const inner = raw.slice(1, raw.lastIndexOf(']'));
83
- return inner.split(',')
84
- .map(s => s.trim().replace(/^["']|["']$/g, ''))
85
- .filter(s => s.length > 0);
86
- }
87
- // Quoted string
88
- return raw.replace(/^["']|["']$/g, '');
89
- }
90
-
91
- function serializeTOML(doc: TomlDoc): string {
92
- const lines: string[] = [];
93
-
94
- // Preamble (top-level content before any section)
95
- if (doc.preamble.length > 0) {
96
- lines.push(...doc.preamble);
97
- if (lines.length > 0 && lines[lines.length - 1].trim() !== '') {
98
- lines.push('');
99
- }
100
- }
101
-
102
- for (const [section, values] of doc.sections) {
103
- if (section === '') continue; // already in preamble
104
- lines.push(`[${section}]`);
105
- for (const [key, val] of Object.entries(values)) {
106
- if (Array.isArray(val)) {
107
- const items = val.map(v => `"${v}"`).join(', ');
108
- lines.push(`${key} = [${items}]`);
109
- } else {
110
- lines.push(`${key} = "${val}"`);
111
- }
112
- }
113
- lines.push('');
114
- }
115
-
116
- return lines.join('\n');
117
- }
118
-
119
- // ─── Adapter ──────────────────────────────────────────
120
-
121
- const adapter: CLIAdapter = {
122
- id: 'codex',
123
- name: 'Codex',
124
- supportsProjectScope: false,
125
- supportsGlobalScope: true,
126
-
127
- writeMcpConfig(ctx: SetupContext): string {
128
- if (!ctx.isGlobal) {
129
- console.log(' Note: Codex only supports global MCP config. Using --global automatically.');
130
- }
131
-
132
- const configPath = join(homedir(), '.codex', 'config.toml');
133
- const mcpCmd = resolveMcpCommand(ctx);
134
-
135
- // Read existing config or start fresh
136
- let doc: TomlDoc;
137
- if (existsSync(configPath)) {
138
- doc = parseTOML(readFileSync(configPath, 'utf-8'));
139
- } else {
140
- doc = { sections: new Map(), preamble: [] };
141
- mkdirSync(dirname(configPath), { recursive: true });
142
- }
143
-
144
- // Write the MCP server section
145
- const sectionName = 'mcp_servers.agent-working-memory';
146
- doc.sections.set(sectionName, {
147
- command: mcpCmd.command,
148
- args: mcpCmd.args,
149
- });
150
-
151
- // Write env vars in a sub-section
152
- const envSection = `${sectionName}.env`;
153
- const envEntries: TomlSection = {};
154
- for (const [key, val] of Object.entries(ctx.envVars)) {
155
- envEntries[key] = val;
156
- }
157
- doc.sections.set(envSection, envEntries);
158
-
159
- writeFileSync(configPath, serializeTOML(doc));
160
- return `MCP config: ${configPath}`;
161
- },
162
-
163
- writeInstructions(ctx: SetupContext, skip: boolean): string {
164
- const agentsMdPath = join(ctx.cwd, 'AGENTS.md');
165
-
166
- if (skip) return 'AGENTS.md: skipped (--no-instructions)';
167
-
168
- if (existsSync(agentsMdPath)) {
169
- const content = readFileSync(agentsMdPath, 'utf-8');
170
- if (content.includes('## Memory (AWM)')) {
171
- return 'AGENTS.md: already has AWM section (skipped)';
172
- }
173
- writeFileSync(agentsMdPath, content.trimEnd() + '\n\n' + AWM_INSTRUCTION_CONTENT);
174
- return 'AGENTS.md: appended AWM workflow section';
175
- }
176
-
177
- const title = `# ${basename(ctx.cwd)} — Agent Instructions`;
178
- writeFileSync(agentsMdPath, `${title}\n\n${AWM_INSTRUCTION_CONTENT}`);
179
- return 'AGENTS.md: created with AWM workflow section';
180
- },
181
-
182
- writeHooks(_ctx: SetupContext, _skip: boolean): string {
183
- return 'Hooks: not supported by Codex (auto-checkpoint unavailable)';
184
- },
185
-
186
- diagnose(ctx: SetupContext): DiagnosticResult[] {
187
- const results: DiagnosticResult[] = [];
188
-
189
- // Check TOML config
190
- const configPath = join(homedir(), '.codex', 'config.toml');
191
- if (!existsSync(configPath)) {
192
- results.push({
193
- check: 'MCP config',
194
- status: 'fail',
195
- message: `${configPath} not found`,
196
- fix: 'Run: awm setup codex',
197
- });
198
- } else {
199
- try {
200
- const doc = parseTOML(readFileSync(configPath, 'utf-8'));
201
- if (doc.sections.has('mcp_servers.agent-working-memory')) {
202
- results.push({ check: 'MCP config', status: 'ok', message: `AWM registered in ${configPath}` });
203
-
204
- // Verify the command points to a real file
205
- const section = doc.sections.get('mcp_servers.agent-working-memory')!;
206
- const args = section.args;
207
- if (Array.isArray(args) && args.length > 0) {
208
- const entrypoint = args[args.length - 1];
209
- if (existsSync(entrypoint)) {
210
- results.push({ check: 'MCP entrypoint', status: 'ok', message: entrypoint });
211
- } else {
212
- results.push({
213
- check: 'MCP entrypoint',
214
- status: 'fail',
215
- message: `Configured entrypoint not found: ${entrypoint}`,
216
- fix: 'Run: npm run build (in AWM package) then awm setup codex',
217
- });
218
- }
219
- }
220
- } else {
221
- results.push({
222
- check: 'MCP config',
223
- status: 'fail',
224
- message: 'config.toml exists but missing agent-working-memory section',
225
- fix: 'Run: awm setup codex',
226
- });
227
- }
228
- } catch {
229
- results.push({ check: 'MCP config', status: 'fail', message: `${configPath} could not be parsed` });
230
- }
231
- }
232
-
233
- // Check database
234
- if (existsSync(ctx.dbPath)) {
235
- results.push({ check: 'Database', status: 'ok', message: ctx.dbPath });
236
- } else {
237
- results.push({ check: 'Database', status: 'warn', message: `${ctx.dbPath} not found (will be created on first use)` });
238
- }
239
-
240
- // Check AGENTS.md
241
- const agentsMdPath = join(ctx.cwd, 'AGENTS.md');
242
- if (existsSync(agentsMdPath)) {
243
- const content = readFileSync(agentsMdPath, 'utf-8');
244
- if (content.includes('## Memory (AWM)')) {
245
- results.push({ check: 'Instructions', status: 'ok', message: 'AGENTS.md has AWM section' });
246
- } else {
247
- results.push({
248
- check: 'Instructions',
249
- status: 'warn',
250
- message: 'AGENTS.md exists but missing AWM section',
251
- fix: 'Run: awm setup codex',
252
- });
253
- }
254
- } else {
255
- results.push({ check: 'Instructions', status: 'warn', message: 'AGENTS.md not found' });
256
- }
257
-
258
- return results;
259
- },
260
- };
261
-
262
- export default adapter;
1
+ // Copyright 2026 Robert Winter / Complete Ideas
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /**
5
+ * Codex CLI adapter — writes TOML config, AGENTS.md instructions.
6
+ *
7
+ * Codex stores MCP config in ~/.codex/config.toml (global only).
8
+ * Uses snake_case [mcp_servers.name] sections.
9
+ */
10
+
11
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
12
+ import { join, dirname, basename } from 'node:path';
13
+ import type { CLIAdapter, SetupContext, DiagnosticResult } from './types.js';
14
+ import { resolveMcpCommand, homedir, AWM_INSTRUCTION_CONTENT, upsertAwmSection } from './common.js';
15
+
16
+ // ─── Minimal TOML read/write ──────────────────────────
17
+ // Only handles the flat structure Codex uses: [section.name] with key = "value" or key = ["array"]
18
+
19
+ interface TomlSection {
20
+ [key: string]: string | string[];
21
+ }
22
+
23
+ interface TomlDoc {
24
+ /** Sections: key is the full dotted section name */
25
+ sections: Map<string, TomlSection>;
26
+ /** Lines before the first section (top-level keys, comments) */
27
+ preamble: string[];
28
+ }
29
+
30
+ function parseTOML(content: string): TomlDoc {
31
+ const doc: TomlDoc = { sections: new Map(), preamble: [] };
32
+ let currentSection = '';
33
+ const lines = content.split('\n');
34
+
35
+ for (const line of lines) {
36
+ const trimmed = line.trim();
37
+
38
+ // Section header
39
+ const sectionMatch = trimmed.match(/^\[([^\]]+)\]$/);
40
+ if (sectionMatch) {
41
+ currentSection = sectionMatch[1];
42
+ if (!doc.sections.has(currentSection)) {
43
+ doc.sections.set(currentSection, {});
44
+ }
45
+ continue;
46
+ }
47
+
48
+ // Skip comments and empty lines
49
+ if (!trimmed || trimmed.startsWith('#')) {
50
+ if (!currentSection) doc.preamble.push(line);
51
+ continue;
52
+ }
53
+
54
+ // Key = value
55
+ const eqIdx = trimmed.indexOf('=');
56
+ if (eqIdx === -1) {
57
+ if (!currentSection) doc.preamble.push(line);
58
+ continue;
59
+ }
60
+
61
+ const key = trimmed.slice(0, eqIdx).trim();
62
+ const rawVal = trimmed.slice(eqIdx + 1).trim();
63
+
64
+ if (!currentSection) {
65
+ if (!doc.sections.has('')) doc.sections.set('', {});
66
+ doc.sections.get('')![key] = parseTomlValue(rawVal);
67
+ continue;
68
+ }
69
+
70
+ if (!doc.sections.has(currentSection)) {
71
+ doc.sections.set(currentSection, {});
72
+ }
73
+ doc.sections.get(currentSection)![key] = parseTomlValue(rawVal);
74
+ }
75
+
76
+ return doc;
77
+ }
78
+
79
+ function parseTomlValue(raw: string): string | string[] {
80
+ // Array: ["a", "b"]
81
+ if (raw.startsWith('[')) {
82
+ const inner = raw.slice(1, raw.lastIndexOf(']'));
83
+ return inner.split(',')
84
+ .map(s => s.trim().replace(/^["']|["']$/g, ''))
85
+ .filter(s => s.length > 0);
86
+ }
87
+ // Quoted string
88
+ return raw.replace(/^["']|["']$/g, '');
89
+ }
90
+
91
+ function serializeTOML(doc: TomlDoc): string {
92
+ const lines: string[] = [];
93
+
94
+ // Preamble (top-level content before any section)
95
+ if (doc.preamble.length > 0) {
96
+ lines.push(...doc.preamble);
97
+ if (lines.length > 0 && lines[lines.length - 1].trim() !== '') {
98
+ lines.push('');
99
+ }
100
+ }
101
+
102
+ for (const [section, values] of doc.sections) {
103
+ if (section === '') continue; // already in preamble
104
+ lines.push(`[${section}]`);
105
+ for (const [key, val] of Object.entries(values)) {
106
+ if (Array.isArray(val)) {
107
+ const items = val.map(v => `"${v}"`).join(', ');
108
+ lines.push(`${key} = [${items}]`);
109
+ } else {
110
+ lines.push(`${key} = "${val}"`);
111
+ }
112
+ }
113
+ lines.push('');
114
+ }
115
+
116
+ return lines.join('\n');
117
+ }
118
+
119
+ // ─── Adapter ──────────────────────────────────────────
120
+
121
+ const adapter: CLIAdapter = {
122
+ id: 'codex',
123
+ name: 'Codex',
124
+ supportsProjectScope: false,
125
+ supportsGlobalScope: true,
126
+
127
+ writeMcpConfig(ctx: SetupContext): string {
128
+ if (!ctx.isGlobal) {
129
+ console.log(' Note: Codex only supports global MCP config. Using --global automatically.');
130
+ }
131
+
132
+ const configPath = join(homedir(), '.codex', 'config.toml');
133
+ const mcpCmd = resolveMcpCommand(ctx);
134
+
135
+ // Read existing config or start fresh
136
+ let doc: TomlDoc;
137
+ if (existsSync(configPath)) {
138
+ doc = parseTOML(readFileSync(configPath, 'utf-8'));
139
+ } else {
140
+ doc = { sections: new Map(), preamble: [] };
141
+ mkdirSync(dirname(configPath), { recursive: true });
142
+ }
143
+
144
+ // Write the MCP server section
145
+ const sectionName = 'mcp_servers.agent-working-memory';
146
+ doc.sections.set(sectionName, {
147
+ command: mcpCmd.command,
148
+ args: mcpCmd.args,
149
+ });
150
+
151
+ // Write env vars in a sub-section
152
+ const envSection = `${sectionName}.env`;
153
+ const envEntries: TomlSection = {};
154
+ for (const [key, val] of Object.entries(ctx.envVars)) {
155
+ envEntries[key] = val;
156
+ }
157
+ doc.sections.set(envSection, envEntries);
158
+
159
+ writeFileSync(configPath, serializeTOML(doc));
160
+ return `MCP config: ${configPath}`;
161
+ },
162
+
163
+ writeInstructions(ctx: SetupContext, skip: boolean): string {
164
+ const agentsMdPath = join(ctx.cwd, 'AGENTS.md');
165
+
166
+ if (skip) return 'AGENTS.md: skipped (--no-instructions)';
167
+
168
+ const title = `# ${basename(ctx.cwd)} — Agent Instructions`;
169
+ return upsertAwmSection(agentsMdPath, AWM_INSTRUCTION_CONTENT, { titleIfNew: title });
170
+ },
171
+
172
+ writeHooks(_ctx: SetupContext, _skip: boolean): string {
173
+ return 'Hooks: not supported by Codex (auto-checkpoint unavailable)';
174
+ },
175
+
176
+ diagnose(ctx: SetupContext): DiagnosticResult[] {
177
+ const results: DiagnosticResult[] = [];
178
+
179
+ // Check TOML config
180
+ const configPath = join(homedir(), '.codex', 'config.toml');
181
+ if (!existsSync(configPath)) {
182
+ results.push({
183
+ check: 'MCP config',
184
+ status: 'fail',
185
+ message: `${configPath} not found`,
186
+ fix: 'Run: awm setup codex',
187
+ });
188
+ } else {
189
+ try {
190
+ const doc = parseTOML(readFileSync(configPath, 'utf-8'));
191
+ if (doc.sections.has('mcp_servers.agent-working-memory')) {
192
+ results.push({ check: 'MCP config', status: 'ok', message: `AWM registered in ${configPath}` });
193
+
194
+ // Verify the command points to a real file
195
+ const section = doc.sections.get('mcp_servers.agent-working-memory')!;
196
+ const args = section.args;
197
+ if (Array.isArray(args) && args.length > 0) {
198
+ const entrypoint = args[args.length - 1];
199
+ if (existsSync(entrypoint)) {
200
+ results.push({ check: 'MCP entrypoint', status: 'ok', message: entrypoint });
201
+ } else {
202
+ results.push({
203
+ check: 'MCP entrypoint',
204
+ status: 'fail',
205
+ message: `Configured entrypoint not found: ${entrypoint}`,
206
+ fix: 'Run: npm run build (in AWM package) then awm setup codex',
207
+ });
208
+ }
209
+ }
210
+ } else {
211
+ results.push({
212
+ check: 'MCP config',
213
+ status: 'fail',
214
+ message: 'config.toml exists but missing agent-working-memory section',
215
+ fix: 'Run: awm setup codex',
216
+ });
217
+ }
218
+ } catch {
219
+ results.push({ check: 'MCP config', status: 'fail', message: `${configPath} could not be parsed` });
220
+ }
221
+ }
222
+
223
+ // Check database
224
+ if (existsSync(ctx.dbPath)) {
225
+ results.push({ check: 'Database', status: 'ok', message: ctx.dbPath });
226
+ } else {
227
+ results.push({ check: 'Database', status: 'warn', message: `${ctx.dbPath} not found (will be created on first use)` });
228
+ }
229
+
230
+ // Check AGENTS.md
231
+ const agentsMdPath = join(ctx.cwd, 'AGENTS.md');
232
+ if (existsSync(agentsMdPath)) {
233
+ const content = readFileSync(agentsMdPath, 'utf-8');
234
+ if (content.includes('## Memory (AWM)')) {
235
+ results.push({ check: 'Instructions', status: 'ok', message: 'AGENTS.md has AWM section' });
236
+ } else {
237
+ results.push({
238
+ check: 'Instructions',
239
+ status: 'warn',
240
+ message: 'AGENTS.md exists but missing AWM section',
241
+ fix: 'Run: awm setup codex',
242
+ });
243
+ }
244
+ } else {
245
+ results.push({ check: 'Instructions', status: 'warn', message: 'AGENTS.md not found' });
246
+ }
247
+
248
+ return results;
249
+ },
250
+ };
251
+
252
+ export default adapter;