agent-working-memory 0.7.17 → 0.8.6
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.
- package/README.md +156 -15
- package/dist/adapters/common.d.ts.map +1 -1
- package/dist/adapters/common.js +289 -202
- package/dist/adapters/common.js.map +1 -1
- package/dist/adapters/http.js +22 -22
- package/dist/api/routes.d.ts +1 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +258 -79
- package/dist/api/routes.js.map +1 -1
- package/dist/cli/migrate.d.ts +42 -0
- package/dist/cli/migrate.d.ts.map +1 -0
- package/dist/cli/migrate.js +268 -0
- package/dist/cli/migrate.js.map +1 -0
- package/dist/cli.js +147 -103
- package/dist/cli.js.map +1 -1
- package/dist/coordination/circuit-breaker.d.ts +24 -0
- package/dist/coordination/circuit-breaker.d.ts.map +1 -0
- package/dist/coordination/circuit-breaker.js +70 -0
- package/dist/coordination/circuit-breaker.js.map +1 -0
- package/dist/coordination/failure-modes.d.ts +21 -0
- package/dist/coordination/failure-modes.d.ts.map +1 -0
- package/dist/coordination/failure-modes.js +49 -0
- package/dist/coordination/failure-modes.js.map +1 -0
- package/dist/coordination/peer-decisions.js +7 -7
- package/dist/coordination/routes.d.ts.map +1 -1
- package/dist/coordination/routes.js +44 -1
- package/dist/coordination/routes.js.map +1 -1
- package/dist/coordination/schema.d.ts.map +1 -1
- package/dist/coordination/schema.js +18 -0
- package/dist/coordination/schema.js.map +1 -1
- package/dist/coordination/schemas.d.ts +9 -9
- package/dist/coordination/stale.d.ts +9 -1
- package/dist/coordination/stale.d.ts.map +1 -1
- package/dist/coordination/stale.js +53 -9
- package/dist/coordination/stale.js.map +1 -1
- package/dist/core/embeddings.d.ts +16 -10
- package/dist/core/embeddings.d.ts.map +1 -1
- package/dist/core/embeddings.js +54 -38
- package/dist/core/embeddings.js.map +1 -1
- package/dist/core/ml-worker-entry.d.ts +17 -0
- package/dist/core/ml-worker-entry.d.ts.map +1 -0
- package/dist/core/ml-worker-entry.js +193 -0
- package/dist/core/ml-worker-entry.js.map +1 -0
- package/dist/core/ml-worker.d.ts +59 -0
- package/dist/core/ml-worker.d.ts.map +1 -0
- package/dist/core/ml-worker.js +253 -0
- package/dist/core/ml-worker.js.map +1 -0
- package/dist/core/query-expander.d.ts +13 -15
- package/dist/core/query-expander.d.ts.map +1 -1
- package/dist/core/query-expander.js +48 -54
- package/dist/core/query-expander.js.map +1 -1
- package/dist/core/reranker.d.ts +9 -19
- package/dist/core/reranker.d.ts.map +1 -1
- package/dist/core/reranker.js +33 -35
- package/dist/core/reranker.js.map +1 -1
- package/dist/core/salience.d.ts +32 -5
- package/dist/core/salience.d.ts.map +1 -1
- package/dist/core/salience.js +201 -28
- package/dist/core/salience.js.map +1 -1
- package/dist/core/write-pipeline.d.ts +10 -2
- package/dist/core/write-pipeline.d.ts.map +1 -1
- package/dist/core/write-pipeline.js +218 -49
- package/dist/core/write-pipeline.js.map +1 -1
- package/dist/engine/activation.d.ts +2 -2
- package/dist/engine/activation.d.ts.map +1 -1
- package/dist/engine/activation.js +238 -115
- package/dist/engine/activation.js.map +1 -1
- package/dist/engine/confidence.d.ts +62 -0
- package/dist/engine/confidence.d.ts.map +1 -0
- package/dist/engine/confidence.js +100 -0
- package/dist/engine/confidence.js.map +1 -0
- package/dist/engine/connections.d.ts +47 -8
- package/dist/engine/connections.d.ts.map +1 -1
- package/dist/engine/connections.js +83 -22
- package/dist/engine/connections.js.map +1 -1
- package/dist/engine/consolidation-scheduler.d.ts +46 -10
- package/dist/engine/consolidation-scheduler.d.ts.map +1 -1
- package/dist/engine/consolidation-scheduler.js +170 -50
- package/dist/engine/consolidation-scheduler.js.map +1 -1
- package/dist/engine/consolidation.d.ts +16 -2
- package/dist/engine/consolidation.d.ts.map +1 -1
- package/dist/engine/consolidation.js +143 -44
- package/dist/engine/consolidation.js.map +1 -1
- package/dist/engine/eval.d.ts +2 -2
- package/dist/engine/eval.d.ts.map +1 -1
- package/dist/engine/eval.js +9 -9
- package/dist/engine/eval.js.map +1 -1
- package/dist/engine/eviction.d.ts +4 -4
- package/dist/engine/eviction.d.ts.map +1 -1
- package/dist/engine/eviction.js +15 -15
- package/dist/engine/eviction.js.map +1 -1
- package/dist/engine/retraction.d.ts +93 -5
- package/dist/engine/retraction.d.ts.map +1 -1
- package/dist/engine/retraction.js +238 -19
- package/dist/engine/retraction.js.map +1 -1
- package/dist/engine/staging.d.ts +1 -1
- package/dist/engine/staging.d.ts.map +1 -1
- package/dist/engine/staging.js +3 -3
- package/dist/engine/staging.js.map +1 -1
- package/dist/hooks/sidecar.d.ts +1 -1
- package/dist/hooks/sidecar.d.ts.map +1 -1
- package/dist/hooks/sidecar.js +4 -4
- package/dist/hooks/sidecar.js.map +1 -1
- package/dist/index.js +48 -31
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +167 -141
- package/dist/mcp.js.map +1 -1
- package/dist/storage/factory.d.ts +55 -0
- package/dist/storage/factory.d.ts.map +1 -0
- package/dist/storage/factory.js +135 -0
- package/dist/storage/factory.js.map +1 -0
- package/dist/storage/pglite-schema.d.ts +20 -0
- package/dist/storage/pglite-schema.d.ts.map +1 -0
- package/dist/storage/pglite-schema.js +165 -0
- package/dist/storage/pglite-schema.js.map +1 -0
- package/dist/storage/pglite.d.ts +187 -0
- package/dist/storage/pglite.d.ts.map +1 -0
- package/dist/storage/pglite.js +1114 -0
- package/dist/storage/pglite.js.map +1 -0
- package/dist/storage/sqlite.d.ts +117 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +357 -8
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/storage/store.d.ts +63 -0
- package/dist/storage/store.d.ts.map +1 -0
- package/dist/storage/store.js +27 -0
- package/dist/storage/store.js.map +1 -0
- package/dist/types/engram.d.ts +92 -2
- package/dist/types/engram.d.ts.map +1 -1
- package/dist/types/engram.js.map +1 -1
- package/package.json +3 -1
- package/src/adapters/claude-code.ts +218 -218
- package/src/adapters/codex.ts +252 -252
- package/src/adapters/common.ts +498 -411
- package/src/adapters/cursor.ts +135 -135
- package/src/adapters/http.ts +93 -93
- package/src/adapters/index.ts +31 -31
- package/src/adapters/types.ts +75 -75
- package/src/api/index.ts +3 -3
- package/src/api/routes.ts +970 -714
- package/src/cli/migrate.ts +307 -0
- package/src/cli.ts +755 -719
- package/src/coordination/circuit-breaker.ts +83 -0
- package/src/coordination/events.ts +90 -90
- package/src/coordination/failure-modes.ts +50 -0
- package/src/coordination/peer-decisions.ts +105 -105
- package/src/coordination/plugin-loader.ts +60 -60
- package/src/coordination/plugin.ts +44 -44
- package/src/coordination/routes.ts +56 -1
- package/src/coordination/schema.ts +13 -0
- package/src/coordination/stale.ts +80 -11
- package/src/coordination/types.ts +311 -311
- package/src/coordination/write-mutex.ts +69 -69
- package/src/core/auto-tagger.ts +168 -168
- package/src/core/decay.ts +63 -63
- package/src/core/embeddings.ts +110 -93
- package/src/core/index.ts +5 -5
- package/src/core/logger.ts +36 -36
- package/src/core/ml-worker-entry.ts +194 -0
- package/src/core/ml-worker.ts +281 -0
- package/src/core/query-expander.ts +122 -128
- package/src/core/reranker.ts +119 -125
- package/src/core/salience.ts +200 -33
- package/src/core/write-pipeline.ts +521 -343
- package/src/engine/activation.ts +235 -109
- package/src/engine/confidence.ts +120 -0
- package/src/engine/connections.ts +162 -103
- package/src/engine/consolidation-scheduler.ts +242 -125
- package/src/engine/consolidation.ts +138 -45
- package/src/engine/eval.ts +102 -102
- package/src/engine/eviction.ts +101 -101
- package/src/engine/index.ts +8 -8
- package/src/engine/retraction.ts +366 -116
- package/src/engine/staging.ts +74 -74
- package/src/hooks/sidecar.ts +5 -5
- package/src/index.ts +226 -212
- package/src/mcp.ts +1145 -1121
- package/src/storage/factory.ts +147 -0
- package/src/storage/index.ts +3 -3
- package/src/storage/pglite-schema.ts +166 -0
- package/src/storage/pglite.ts +1363 -0
- package/src/storage/sqlite.ts +402 -7
- package/src/storage/store.ts +80 -0
- package/src/types/agent.ts +67 -67
- package/src/types/checkpoint.ts +46 -46
- package/src/types/engram.ts +101 -3
- package/src/types/eval.ts +100 -100
- package/src/types/index.ts +6 -6
package/src/adapters/codex.ts
CHANGED
|
@@ -1,252 +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, 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;
|
|
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;
|