opencode-codex-memory 0.4.11 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +158 -28
- package/dist/src/agent-health.d.ts +21 -0
- package/dist/src/agent-health.js +133 -0
- package/dist/src/claude-import.d.ts +65 -0
- package/dist/src/claude-import.js +499 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +69 -8
- package/dist/src/options.d.ts +5 -2
- package/dist/src/options.js +3 -0
- package/dist/src/phase2.d.ts +2 -0
- package/dist/src/phase2.js +14 -1
- package/dist/tools/control.js +42 -1
- package/dist/tools/memory.d.ts +2 -0
- package/dist/tools/memory.js +50 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,10 +27,10 @@ from there everything lives under `~/.local/share/opencode/`, so you can read it
|
|
|
27
27
|
grep it, edit it, or delete it like anything else you own. Nothing leaves your
|
|
28
28
|
machine beyond the model calls OpenCode already makes.
|
|
29
29
|
|
|
30
|
-
If you *do* also use the Codex CLI
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
[
|
|
30
|
+
If you *do* also use the Codex CLI or Claude Code on the same machine: the
|
|
31
|
+
plugin can bring their memories in (and, for Codex, push ours back). Off by
|
|
32
|
+
default — see [Sharing memory with the Codex CLI](#sharing-memory-with-the-codex-cli)
|
|
33
|
+
and [Importing memory from Claude Code](#importing-memory-from-claude-code).
|
|
34
34
|
|
|
35
35
|
## Why
|
|
36
36
|
|
|
@@ -52,7 +52,7 @@ If you want the mental model before the details, jump to
|
|
|
52
52
|
|
|
53
53
|
```json
|
|
54
54
|
{
|
|
55
|
-
"plugin": ["opencode-codex-memory@0.
|
|
55
|
+
"plugin": ["opencode-codex-memory@0.6.0"]
|
|
56
56
|
}
|
|
57
57
|
```
|
|
58
58
|
|
|
@@ -191,6 +191,20 @@ them — it's yours. (The `memories/` folder also holds a few working files and
|
|
|
191
191
|
an internal `.git/` the plugin uses for change tracking; `memory_reset` wipes
|
|
192
192
|
those too.)
|
|
193
193
|
|
|
194
|
+
### Backup and restore
|
|
195
|
+
|
|
196
|
+
Back up the whole OpenCode data directory while OpenCode is stopped. The
|
|
197
|
+
SQLite database and `memories/` workspace are a pair: restoring only one can
|
|
198
|
+
leave job state, Git baseline, and memory files out of sync. Include hidden
|
|
199
|
+
files, especially `memories/.git/`, and SQLite sidecars such as `memory.db-wal`
|
|
200
|
+
or `memory.db-shm` when present.
|
|
201
|
+
|
|
202
|
+
The directory is `$XDG_DATA_HOME/opencode` when `XDG_DATA_HOME` is set,
|
|
203
|
+
otherwise `~/.local/share/opencode`. Copy that whole directory to a dated
|
|
204
|
+
backup location. To restore, stop OpenCode, replace the current `opencode/`
|
|
205
|
+
data directory with the backup copy, then start OpenCode again. Do not restore
|
|
206
|
+
while OpenCode is running or copy only `memory.db` or only `memories/`.
|
|
207
|
+
|
|
194
208
|
## Privacy & safety
|
|
195
209
|
|
|
196
210
|
- **Local only.** There is no remote storage option to enable, by accident or
|
|
@@ -233,13 +247,14 @@ Codex's `[memories]` config so the two stay easy to compare:
|
|
|
233
247
|
| `max_rollouts_per_startup` | `2` | Max sessions extracted per pass |
|
|
234
248
|
| `max_unused_days` | `30` | Prune memories unused for this long |
|
|
235
249
|
| `codex_interop` | `{ "import": false, "export": false }` | Two-way memory exchange with a local Codex CLI (see below) |
|
|
250
|
+
| `claude_import` | `{ "enabled": false }` | One-way import of Claude Code project memories (see below) |
|
|
236
251
|
|
|
237
252
|
To set options, turn the plugin entry into a `[name, options]` pair:
|
|
238
253
|
|
|
239
254
|
```json
|
|
240
255
|
{
|
|
241
256
|
"plugin": [
|
|
242
|
-
["opencode-codex-memory@0.
|
|
257
|
+
["opencode-codex-memory@0.6.0", { "disable_on_external_context": true, "min_rollout_idle_hours": 2 }]
|
|
243
258
|
]
|
|
244
259
|
}
|
|
245
260
|
```
|
|
@@ -291,36 +306,150 @@ explicitly, so they win over an agent-level `model`.
|
|
|
291
306
|
|
|
292
307
|
### Sharing memory with the Codex CLI
|
|
293
308
|
|
|
294
|
-
If you
|
|
295
|
-
|
|
296
|
-
|
|
309
|
+
If you use OpenCode and the Codex CLI on the same machine, turn this on so each
|
|
310
|
+
side can pick up what the other already learned. **Either or both directions.**
|
|
311
|
+
Off by default; no changes to Codex's own config are required.
|
|
312
|
+
|
|
313
|
+
**Enable both directions:**
|
|
314
|
+
|
|
315
|
+
```json
|
|
316
|
+
{
|
|
317
|
+
"plugin": [
|
|
318
|
+
[
|
|
319
|
+
"opencode-codex-memory@0.6.0",
|
|
320
|
+
{ "codex_interop": { "import": true, "export": true } }
|
|
321
|
+
]
|
|
322
|
+
]
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
- **Import:** on each consolidation pass here, durable memory from Codex is
|
|
327
|
+
merged into this plugin's store (tagged so you can tell it came from Codex).
|
|
328
|
+
- **Export:** after a successful consolidation here, this plugin's memory is
|
|
329
|
+
offered to Codex; Codex merges it on *its* next consolidation pass.
|
|
330
|
+
|
|
331
|
+
**Options** (all under `codex_interop`):
|
|
332
|
+
|
|
333
|
+
| Option | Default | Meaning |
|
|
334
|
+
| --- | --- | --- |
|
|
335
|
+
| `import` | `false` | Bring Codex's consolidated memory into OpenCode. |
|
|
336
|
+
| `export` | `false` | Offer this plugin's consolidated memory to Codex. |
|
|
337
|
+
| `codex_home` | `$CODEX_HOME`, else `~/.codex` | Where the Codex CLI keeps its data. Set this if you use a non-default location. |
|
|
338
|
+
|
|
339
|
+
**Notes**
|
|
340
|
+
|
|
341
|
+
- Export only runs once Codex has created its own memory workspace
|
|
342
|
+
(`$CODEX_HOME/memories`). Until then, import/export quietly do nothing for
|
|
343
|
+
the missing side.
|
|
344
|
+
- Codex's own files are never rewritten in place — export only adds a side
|
|
345
|
+
channel Codex already knows how to read.
|
|
346
|
+
- Both sides tag origin (`[from codex]` / `[from opencode]`) and skip the other
|
|
347
|
+
tag on re-import, so the same facts don't bounce back and forth.
|
|
348
|
+
- Safe to combine with `claude_import`. Check status with `memory_inspect`
|
|
349
|
+
(`codex_interop:` section).
|
|
350
|
+
|
|
351
|
+
**Turning it off**
|
|
352
|
+
|
|
353
|
+
Setting `import` / `export` back to `false` (or removing `codex_interop`) only
|
|
354
|
+
stops further sync. Already merged memory stays in this plugin's store, and any
|
|
355
|
+
staging copies stay on disk (harmless while import is off). The last export left
|
|
356
|
+
in Codex stays until Codex consolidates it away or you remove it there.
|
|
357
|
+
|
|
358
|
+
To drop the local staging copies too: turn import off first, then delete
|
|
359
|
+
`extensions/codex_import/` under the plugin memory directory (see
|
|
360
|
+
[Where your data lives](#where-your-data-lives)). Leave import off — if you turn
|
|
361
|
+
it back on, the next pass will recreate them from Codex. After a deletion, the
|
|
362
|
+
next consolidation can treat the missing files as a signal to drop entries that
|
|
363
|
+
only came from that import.
|
|
364
|
+
|
|
365
|
+
### Importing memory from Claude Code
|
|
366
|
+
|
|
367
|
+
If you use Claude Code as well as OpenCode, turn this on so OpenCode can learn
|
|
368
|
+
from the project memories Claude already keeps on your machine. **One-way only**
|
|
369
|
+
(Claude → OpenCode); nothing is written back into Claude. Off by default.
|
|
370
|
+
|
|
371
|
+
**Enable everything Claude has:**
|
|
297
372
|
|
|
298
373
|
```json
|
|
299
374
|
{
|
|
300
375
|
"plugin": [
|
|
301
|
-
["opencode-codex-memory@0.
|
|
376
|
+
["opencode-codex-memory@0.6.0", { "claude_import": { "enabled": true } }]
|
|
302
377
|
]
|
|
303
378
|
}
|
|
304
379
|
```
|
|
305
380
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
381
|
+
After the next consolidation pass, durable facts from Claude's memories show up
|
|
382
|
+
in this plugin's memory the same way OpenCode's own extractions do. Project-specific
|
|
383
|
+
detail stays labeled by project; broadly useful preferences can land in the
|
|
384
|
+
global summary.
|
|
385
|
+
|
|
386
|
+
**Options** (all under `claude_import`):
|
|
387
|
+
|
|
388
|
+
| Option | Default | Meaning |
|
|
389
|
+
| --- | --- | --- |
|
|
390
|
+
| `enabled` | `false` | Turn the importer on. |
|
|
391
|
+
| `claude_home` | `~/.claude` | Where Claude Code stores its data. Set this if you use a non-default location. |
|
|
392
|
+
| `projects` | all projects | Optional allowlist of Claude project ids to import (see below). |
|
|
393
|
+
|
|
394
|
+
**Limit to specific projects:**
|
|
395
|
+
|
|
396
|
+
Claude names each project with an opaque id (a folder under
|
|
397
|
+
`~/.claude/projects/`), not the path you work in. List those folder names:
|
|
398
|
+
|
|
399
|
+
```json
|
|
400
|
+
{
|
|
401
|
+
"plugin": [
|
|
402
|
+
[
|
|
403
|
+
"opencode-codex-memory@0.6.0",
|
|
404
|
+
{
|
|
405
|
+
"claude_import": {
|
|
406
|
+
"enabled": true,
|
|
407
|
+
"projects": ["-Users-you-Desktop-git-my-app"]
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
]
|
|
411
|
+
]
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Find the ids on your machine:
|
|
416
|
+
|
|
417
|
+
```bash
|
|
418
|
+
ls ~/.claude/projects
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
- Omit `projects` (or leave it empty) to import every Claude project the plugin
|
|
422
|
+
can place on disk.
|
|
423
|
+
- Add or remove ids anytime while import is **on**. Dropping an id (or deleting
|
|
424
|
+
that project's memory in Claude) stops syncing it and removes the staging
|
|
425
|
+
copy on the next pass so consolidation can drop derived entries. Memory
|
|
426
|
+
already merged into the main store is not instantly deleted; the consolidator
|
|
427
|
+
cleans it up when it next runs.
|
|
428
|
+
- Turning `enabled` off (see below) does **not** prune anything.
|
|
429
|
+
|
|
430
|
+
**Turning it off**
|
|
431
|
+
|
|
432
|
+
Setting `enabled` to `false` (or removing `claude_import`) only stops further
|
|
433
|
+
import. Already merged memory stays in this plugin's store, and staging copies
|
|
434
|
+
stay on disk (harmless while import is off).
|
|
435
|
+
|
|
436
|
+
To drop the local staging copies too: turn import off first, then delete
|
|
437
|
+
`extensions/external_agent_import/` under the plugin memory directory (see
|
|
438
|
+
[Where your data lives](#where-your-data-lives)). Leave import off — if you turn
|
|
439
|
+
it back on, the next pass will recreate them from Claude. After a deletion, the
|
|
440
|
+
next consolidation can treat the missing files as a signal to drop entries that
|
|
441
|
+
only came from that import.
|
|
442
|
+
|
|
443
|
+
To stop importing *some* projects without turning the feature off: shrink
|
|
444
|
+
`projects` (or delete that memory in Claude) and let the next pass run. That
|
|
445
|
+
only affects the Claude import channel.
|
|
446
|
+
|
|
447
|
+
**Notes**
|
|
448
|
+
|
|
449
|
+
- Safe to combine with `codex_interop` (Codex sharing uses a separate channel).
|
|
450
|
+
- If you also run another OpenCode plugin that *writes* Claude-style memory,
|
|
451
|
+
pick one writer — two systems updating the same long-term store will fight.
|
|
452
|
+
- Check status anytime with `memory_inspect` (`claude_import:` section).
|
|
324
453
|
|
|
325
454
|
## Why one global memory?
|
|
326
455
|
|
|
@@ -360,6 +489,7 @@ It reports:
|
|
|
360
489
|
- phase-2 status / last error / cooldown
|
|
361
490
|
- last session-discovery outcome
|
|
362
491
|
- effective options (after clamping) and config warnings
|
|
492
|
+
- effective memory-agent health, including user overrides and required permissions
|
|
363
493
|
- a short eligibility reminder (`min_rollout_idle_hours`, default **6h**)
|
|
364
494
|
|
|
365
495
|
Common causes:
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
declare const AGENT_NAMES: readonly ["memorize", "memorize-extract"];
|
|
2
|
+
type AgentName = (typeof AGENT_NAMES)[number];
|
|
3
|
+
export interface AgentHealthEntry {
|
|
4
|
+
source: "shipped" | "user_override" | "missing";
|
|
5
|
+
healthy: boolean;
|
|
6
|
+
issues: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface AgentHealthSnapshot {
|
|
9
|
+
observed: boolean;
|
|
10
|
+
generationEnabled: boolean | null;
|
|
11
|
+
agents: Record<AgentName, AgentHealthEntry>;
|
|
12
|
+
}
|
|
13
|
+
export declare function loadBundledAgentDefinitions(): Record<string, unknown>;
|
|
14
|
+
/** Record the effective agent config after the plugin config hook runs. */
|
|
15
|
+
export declare function recordAgentConfig(config: {
|
|
16
|
+
agent?: Record<string, unknown>;
|
|
17
|
+
}, generationEnabled: boolean, shipped: Record<string, unknown>): void;
|
|
18
|
+
export declare function getAgentHealth(): AgentHealthSnapshot;
|
|
19
|
+
/** Test seam and boot boundary. */
|
|
20
|
+
export declare function resetAgentHealth(): void;
|
|
21
|
+
export {};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { memoryRoot } from "./paths.js";
|
|
4
|
+
const AGENT_NAMES = ["memorize", "memorize-extract"];
|
|
5
|
+
const REQUIRED_ALLOWS = {
|
|
6
|
+
memorize: ["read", "edit", "write", "glob", "grep"],
|
|
7
|
+
"memorize-extract": ["StructuredOutput"],
|
|
8
|
+
};
|
|
9
|
+
const SAFE_ALLOWS = {
|
|
10
|
+
memorize: new Set(["read", "edit", "write", "glob", "grep", "external_directory"]),
|
|
11
|
+
"memorize-extract": new Set(["StructuredOutput"]),
|
|
12
|
+
};
|
|
13
|
+
const initialEntry = () => ({ source: "missing", healthy: false, issues: ["config hook has not run"] });
|
|
14
|
+
let snapshot = {
|
|
15
|
+
observed: false,
|
|
16
|
+
generationEnabled: null,
|
|
17
|
+
agents: {
|
|
18
|
+
memorize: initialEntry(),
|
|
19
|
+
"memorize-extract": initialEntry(),
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
export function loadBundledAgentDefinitions() {
|
|
23
|
+
const raw = fs.readFileSync(path.join(import.meta.dirname, "..", "opencode.json"), "utf8");
|
|
24
|
+
return JSON.parse(raw).agent ?? {};
|
|
25
|
+
}
|
|
26
|
+
function asRecord(value) {
|
|
27
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
28
|
+
}
|
|
29
|
+
function hasNonDenyAction(value) {
|
|
30
|
+
const rules = asRecord(value);
|
|
31
|
+
return rules ? Object.values(rules).some((action) => action !== "deny") : value !== "deny";
|
|
32
|
+
}
|
|
33
|
+
/** Structural compare — config reload re-parses shipped defs into new objects. */
|
|
34
|
+
function definitionsEqual(a, b) {
|
|
35
|
+
try {
|
|
36
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function permissionIssues(name, definition) {
|
|
43
|
+
const issues = [];
|
|
44
|
+
const record = asRecord(definition);
|
|
45
|
+
if (record?.mode !== "subagent")
|
|
46
|
+
issues.push("agent mode must be 'subagent'");
|
|
47
|
+
const permission = asRecord(record?.permission);
|
|
48
|
+
if (!permission) {
|
|
49
|
+
issues.push("missing permission map");
|
|
50
|
+
return issues;
|
|
51
|
+
}
|
|
52
|
+
const keys = Object.keys(permission);
|
|
53
|
+
if (keys[0] !== "*")
|
|
54
|
+
issues.push("permission wildcard '*' must be the first rule");
|
|
55
|
+
if (permission["*"] !== "deny")
|
|
56
|
+
issues.push("permission wildcard '*' must be 'deny'");
|
|
57
|
+
for (const toolName of REQUIRED_ALLOWS[name]) {
|
|
58
|
+
if (permission[toolName] !== "allow") {
|
|
59
|
+
issues.push(`required permission '${toolName}: allow' is missing`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
for (const [toolName, value] of Object.entries(permission)) {
|
|
63
|
+
if (toolName === "*")
|
|
64
|
+
continue;
|
|
65
|
+
if (!SAFE_ALLOWS[name].has(toolName)) {
|
|
66
|
+
if (hasNonDenyAction(value))
|
|
67
|
+
issues.push(`unexpected permission '${toolName}' must be denied`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (name === "memorize") {
|
|
71
|
+
const external = asRecord(permission.external_directory);
|
|
72
|
+
const expectedPath = path.join(memoryRoot(), "*");
|
|
73
|
+
if (external?.[expectedPath] !== "allow") {
|
|
74
|
+
issues.push(`consolidator must allow external_directory '${expectedPath}'`);
|
|
75
|
+
}
|
|
76
|
+
if (external) {
|
|
77
|
+
for (const [grantedPath, action] of Object.entries(external)) {
|
|
78
|
+
if (grantedPath === expectedPath)
|
|
79
|
+
continue;
|
|
80
|
+
if (hasNonDenyAction(action)) {
|
|
81
|
+
issues.push(`consolidator must deny extra external_directory '${grantedPath}'`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
else if (permission.external_directory !== undefined) {
|
|
87
|
+
issues.push("extractor must not have external_directory access");
|
|
88
|
+
}
|
|
89
|
+
return issues;
|
|
90
|
+
}
|
|
91
|
+
function inspectAgent(name, definition, source) {
|
|
92
|
+
const issues = permissionIssues(name, definition);
|
|
93
|
+
return { source, healthy: issues.length === 0, issues };
|
|
94
|
+
}
|
|
95
|
+
/** Record the effective agent config after the plugin config hook runs. */
|
|
96
|
+
export function recordAgentConfig(config, generationEnabled, shipped) {
|
|
97
|
+
const configured = asRecord(config.agent);
|
|
98
|
+
const agents = {};
|
|
99
|
+
for (const name of AGENT_NAMES) {
|
|
100
|
+
const definition = configured?.[name];
|
|
101
|
+
const source = definition === undefined
|
|
102
|
+
? "missing"
|
|
103
|
+
: definitionsEqual(shipped[name], definition)
|
|
104
|
+
? "shipped"
|
|
105
|
+
: "user_override";
|
|
106
|
+
agents[name] = inspectAgent(name, definition, source);
|
|
107
|
+
if (!generationEnabled && definition === undefined) {
|
|
108
|
+
agents[name] = { source: "missing", healthy: true, issues: ["generation disabled; agent not injected"] };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
snapshot = { observed: true, generationEnabled, agents };
|
|
112
|
+
}
|
|
113
|
+
export function getAgentHealth() {
|
|
114
|
+
return {
|
|
115
|
+
observed: snapshot.observed,
|
|
116
|
+
generationEnabled: snapshot.generationEnabled,
|
|
117
|
+
agents: {
|
|
118
|
+
memorize: { ...snapshot.agents.memorize, issues: [...snapshot.agents.memorize.issues] },
|
|
119
|
+
"memorize-extract": { ...snapshot.agents["memorize-extract"], issues: [...snapshot.agents["memorize-extract"].issues] },
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/** Test seam and boot boundary. */
|
|
124
|
+
export function resetAgentHealth() {
|
|
125
|
+
snapshot = {
|
|
126
|
+
observed: false,
|
|
127
|
+
generationEnabled: null,
|
|
128
|
+
agents: {
|
|
129
|
+
memorize: initialEntry(),
|
|
130
|
+
"memorize-extract": initialEntry(),
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code memory import — port of codex's external-agent memory sync
|
|
3
|
+
* (codex-rs/external-agent-migration/src/memory.rs + memory_import.rs).
|
|
4
|
+
*
|
|
5
|
+
* One-way: reads Claude project memory markdown under
|
|
6
|
+
* `~/.claude/projects/<key>/memory/`, copies into
|
|
7
|
+
* `extensions/external_agent_import/resources/<key>/` with `scope.json`, and
|
|
8
|
+
* seeds `instructions.md` so phase-2 consolidation merges them. Never writes
|
|
9
|
+
* back to Claude. Never touches Claude session transcripts except to resolve
|
|
10
|
+
* a project cwd (newest *.jsonl with an absolute, existing cwd).
|
|
11
|
+
*
|
|
12
|
+
* Codex selects projects via a migration UI; this plugin has no such surface.
|
|
13
|
+
* When enabled, continuous phase-2 sync imports every project that has a
|
|
14
|
+
* reliable cwd (optional `projects` allowlist). Default-off.
|
|
15
|
+
*/
|
|
16
|
+
export declare const EXTENSION_NAME = "external_agent_import";
|
|
17
|
+
export interface ClaudeImportOptions {
|
|
18
|
+
/** When true, sync Claude project memories into the extension each phase 2. */
|
|
19
|
+
enabled: boolean;
|
|
20
|
+
/** Override Claude home (default: `~/.claude`). */
|
|
21
|
+
claude_home?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Optional project-key allowlist (Claude's `projects/<key>` directory names).
|
|
24
|
+
* Omitted/empty = every project with a reliable cwd.
|
|
25
|
+
*/
|
|
26
|
+
projects?: string[];
|
|
27
|
+
}
|
|
28
|
+
export interface ExternalMemoryFile {
|
|
29
|
+
projectKey: string;
|
|
30
|
+
projectCwd: string | null;
|
|
31
|
+
sourcePath: string;
|
|
32
|
+
relativePath: string;
|
|
33
|
+
}
|
|
34
|
+
export interface ClaudeImportSyncResult {
|
|
35
|
+
changed: boolean;
|
|
36
|
+
synchronizedProjects: string[];
|
|
37
|
+
skippedNoCwd: string[];
|
|
38
|
+
failures: Array<{
|
|
39
|
+
projectKey: string;
|
|
40
|
+
message: string;
|
|
41
|
+
}>;
|
|
42
|
+
}
|
|
43
|
+
export declare function resolveClaudeHome(opts: ClaudeImportOptions): string;
|
|
44
|
+
/**
|
|
45
|
+
* Resolve project cwd from Claude session jsonl under the project root.
|
|
46
|
+
* Mirrors codex project_cwd_from_sessions: newest *.jsonl first, first absolute
|
|
47
|
+
* cwd that canonicalizes to an existing directory wins.
|
|
48
|
+
*/
|
|
49
|
+
export declare function projectCwdFromSessions(projectRoot: string): string | null;
|
|
50
|
+
/** Discover every Markdown file under each Claude project memory directory. */
|
|
51
|
+
export declare function discoverExternalMemoryFiles(claudeHome: string): ExternalMemoryFile[];
|
|
52
|
+
/** Owned = resource dirs that carry a regular scope.json (codex owned_project_keys). */
|
|
53
|
+
export declare function ownedProjectKeys(): string[];
|
|
54
|
+
/**
|
|
55
|
+
* Sync Claude project memories into `extensions/external_agent_import/`.
|
|
56
|
+
* Call inside a claimed phase-2 job after baseline, before diff capture.
|
|
57
|
+
* Unreachable Claude home → no-op (never a deletion signal).
|
|
58
|
+
*/
|
|
59
|
+
export declare function syncClaudeImport(opts: ClaudeImportOptions): ClaudeImportSyncResult;
|
|
60
|
+
/** Inspect helpers: imported project keys + instruction mtime. */
|
|
61
|
+
export declare function claudeImportStatus(): {
|
|
62
|
+
extensionPresent: boolean;
|
|
63
|
+
projects: string[];
|
|
64
|
+
instructionsMtimeMs: number | null;
|
|
65
|
+
};
|