@zilliz/memsearch-opencode 0.3.6 → 0.3.10
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/index.ts +37 -6
- package/package.json +1 -1
- package/prompts/memory_to_skill.txt +16 -6
- package/scripts/capture-daemon.py +24 -10
- package/scripts/maintenance-runner.py +54 -2
- package/skills/memory-config/SKILL.md +31 -8
- package/skills/memory-to-skill/SKILL.md +28 -10
package/index.ts
CHANGED
|
@@ -70,10 +70,43 @@ function deriveCollectionName(projectDir: string): string {
|
|
|
70
70
|
|
|
71
71
|
/**
|
|
72
72
|
* Summarize the N most recent daily .md files for cold-start context.
|
|
73
|
-
* Extracts
|
|
74
|
-
*
|
|
75
|
-
* what topics came up), not just the tail of whichever file is newest.
|
|
73
|
+
* Extracts recent non-empty session sections so empty SessionStart headings
|
|
74
|
+
* do not crowd out useful context.
|
|
76
75
|
*/
|
|
76
|
+
function recentMemoryPreviewLines(content: string, maxLines: number): string[] {
|
|
77
|
+
const sections: string[][] = [];
|
|
78
|
+
let current: string[] = [];
|
|
79
|
+
let hasBody = false;
|
|
80
|
+
|
|
81
|
+
const flush = () => {
|
|
82
|
+
if (current.length > 0 && hasBody) {
|
|
83
|
+
sections.push(current);
|
|
84
|
+
}
|
|
85
|
+
current = [];
|
|
86
|
+
hasBody = false;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
for (const rawLine of content.split("\n")) {
|
|
90
|
+
const line = rawLine.trimEnd();
|
|
91
|
+
if (/^##\s/.test(line)) {
|
|
92
|
+
flush();
|
|
93
|
+
current = [line];
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (/^#{3,4}\s/.test(line)) {
|
|
97
|
+
current.push(line);
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (line.startsWith("- ") || line.startsWith("[User]") || line.startsWith("[Assistant]")) {
|
|
101
|
+
current.push(line);
|
|
102
|
+
hasBody = true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
flush();
|
|
107
|
+
return sections.flat().slice(-maxLines);
|
|
108
|
+
}
|
|
109
|
+
|
|
77
110
|
function getRecentMemories(
|
|
78
111
|
memDir: string,
|
|
79
112
|
count = 2,
|
|
@@ -92,9 +125,7 @@ function getRecentMemories(
|
|
|
92
125
|
for (const file of files) {
|
|
93
126
|
try {
|
|
94
127
|
const content = readFileSync(join(memDir, file), "utf-8");
|
|
95
|
-
const lines = content
|
|
96
|
-
.filter((l) => /^#{2,4}\s/.test(l) || l.startsWith("- ") || l.startsWith("[User]") || l.startsWith("[Assistant]"))
|
|
97
|
-
.slice(0, maxLinesPerFile);
|
|
128
|
+
const lines = recentMemoryPreviewLines(content, maxLinesPerFile);
|
|
98
129
|
if (lines.length > 0) {
|
|
99
130
|
summary.push(`[${file}]`, ...lines);
|
|
100
131
|
}
|
package/package.json
CHANGED
|
@@ -9,9 +9,12 @@ error). It is NOT a fact, a decision, or a preference — those belong in
|
|
|
9
9
|
PROJECT.md and USER.md.
|
|
10
10
|
|
|
11
11
|
You will receive recent memory journal entries and a list of existing skills
|
|
12
|
-
(which you MAY revise). Propose or revise skills
|
|
12
|
+
(which you MAY revise). Propose or revise candidate skills when the journals show
|
|
13
|
+
the same user-requested operation repeating enough times to be useful.
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
Candidates are review drafts, not installed skills. Do not enforce a fixed ratio,
|
|
16
|
+
quota, or expectation that most runs return nothing. The number of candidates
|
|
17
|
+
should follow the evidence in the journals.
|
|
15
18
|
|
|
16
19
|
A NEW workflow qualifies as a candidate ONLY when ALL of these hold:
|
|
17
20
|
- It is a repeatable multi-step procedure, not a one-off action or a fact.
|
|
@@ -19,9 +22,10 @@ A NEW workflow qualifies as a candidate ONLY when ALL of these hold:
|
|
|
19
22
|
journals below.
|
|
20
23
|
- It generalizes into a reusable capability. If it is tightly coupled to one
|
|
21
24
|
specific ticket, one specific value, or a one-time condition, do NOT propose it.
|
|
22
|
-
- The recent runs were not immediately corrected, abandoned, or undone (a
|
|
23
|
-
workflow the user kept fighting is not a good skill).
|
|
24
25
|
- It is not already covered by an existing skill.
|
|
26
|
+
- The overall procedure was not abandoned, undone, or contradicted by later
|
|
27
|
+
evidence. Do not reject a workflow merely because individual runs included
|
|
28
|
+
failed attempts, retries, or corrected commands.
|
|
25
29
|
|
|
26
30
|
You may also REVISE an existing skill (re-emit it with the SAME name and a
|
|
27
31
|
corrected body) when the recent journals show that procedure has genuinely
|
|
@@ -30,17 +34,21 @@ there is clear evidence of change; do not rewrite for style.
|
|
|
30
34
|
|
|
31
35
|
Rules:
|
|
32
36
|
- Return only a JSON object, with no prose outside JSON.
|
|
33
|
-
- Use {"skills": []} when
|
|
37
|
+
- Use {"skills": []} when no repeated workflow qualifies.
|
|
34
38
|
- For each candidate, write a clean, portable SKILL.md body in "body": step-by-step
|
|
35
39
|
instructions an agent can follow. Do not include YAML frontmatter — only the
|
|
36
40
|
markdown body. Keep it concise and concrete.
|
|
37
41
|
- "name" must be a short lowercase slug (letters, digits, dashes), e.g. "run-tests".
|
|
38
|
-
It becomes the skill's command name.
|
|
42
|
+
It becomes the skill's command name. For new candidates, do not reuse an
|
|
43
|
+
existing skill name; for revisions, reuse the exact existing name.
|
|
39
44
|
- "description" is one line: what the skill does AND when it should trigger.
|
|
40
45
|
Lead with the verbs a user would actually type ("run", "deploy", "debug").
|
|
41
46
|
- "occurrences" is how many distinct sessions/days you saw this workflow.
|
|
42
47
|
- "sources" lists the journal file names the workflow was observed in.
|
|
43
48
|
- Do not copy raw journal text wholesale into the body; abstract it into a procedure.
|
|
49
|
+
- If a workflow qualifies but some exact commands, paths, or flags are not
|
|
50
|
+
confirmed, still create the candidate. Keep those details general and add a
|
|
51
|
+
short "Before installing" verification step instead of inventing specifics.
|
|
44
52
|
|
|
45
53
|
How to write a good skill body (this is what makes the skill usable):
|
|
46
54
|
- Write imperative, numbered steps an agent can follow top to bottom.
|
|
@@ -66,6 +74,8 @@ Accuracy — confirm from the original, do not hallucinate:
|
|
|
66
74
|
- If you cannot read the transcript or confirm a detail, capture only what the
|
|
67
75
|
summary clearly states; keep the step general or omit it — a plausible-but-wrong
|
|
68
76
|
command is worse than none. Never invent specifics.
|
|
77
|
+
- Do not drop the whole candidate only because a few low-level details remain
|
|
78
|
+
unconfirmed; make the uncertainty visible for human review before installation.
|
|
69
79
|
- Prefer fewer, verified steps over a complete-looking but partly-guessed procedure.
|
|
70
80
|
|
|
71
81
|
JSON examples:
|
|
@@ -459,6 +459,28 @@ def summarize_with_llm(
|
|
|
459
459
|
return None
|
|
460
460
|
|
|
461
461
|
|
|
462
|
+
def wake_maintenance(project_dir: str) -> None:
|
|
463
|
+
"""Start maintenance in a hook-disabled child environment."""
|
|
464
|
+
runner = Path(__file__).resolve().parent / "maintenance-runner.py"
|
|
465
|
+
subprocess.Popen(
|
|
466
|
+
[
|
|
467
|
+
"python3",
|
|
468
|
+
str(runner),
|
|
469
|
+
"--platform",
|
|
470
|
+
"opencode",
|
|
471
|
+
"--project-dir",
|
|
472
|
+
project_dir,
|
|
473
|
+
"--memsearch-dir",
|
|
474
|
+
os.path.join(project_dir, ".memsearch"),
|
|
475
|
+
],
|
|
476
|
+
env={**os.environ, "MEMSEARCH_NO_WATCH": "1", "MEMSEARCH_DISABLE": "1"},
|
|
477
|
+
stdin=subprocess.DEVNULL,
|
|
478
|
+
stdout=subprocess.DEVNULL,
|
|
479
|
+
stderr=subprocess.DEVNULL,
|
|
480
|
+
start_new_session=True,
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
|
|
462
484
|
def get_session_ids(conn: sqlite3.Connection, project_dir: str) -> list[str]:
|
|
463
485
|
"""Find OpenCode sessions that belong to the given project directory."""
|
|
464
486
|
sessions = conn.execute(
|
|
@@ -790,16 +812,8 @@ def main() -> None:
|
|
|
790
812
|
)
|
|
791
813
|
|
|
792
814
|
if any_new:
|
|
793
|
-
os.system(
|
|
794
|
-
|
|
795
|
-
f"--collection {args.collection_name} &"
|
|
796
|
-
)
|
|
797
|
-
os.system(
|
|
798
|
-
f"python3 {shlex.quote(str(Path(__file__).resolve().parent / 'maintenance-runner.py'))} "
|
|
799
|
-
f"--platform opencode "
|
|
800
|
-
f"--project-dir {shlex.quote(args.project_dir)} "
|
|
801
|
-
f"--memsearch-dir {shlex.quote(os.path.join(args.project_dir, '.memsearch'))} &"
|
|
802
|
-
)
|
|
815
|
+
os.system(f"{args.memsearch_cmd} index '{memory_dir}' --collection {args.collection_name} &")
|
|
816
|
+
wake_maintenance(args.project_dir)
|
|
803
817
|
except Exception:
|
|
804
818
|
pass
|
|
805
819
|
finally:
|
|
@@ -13,6 +13,7 @@ import contextlib
|
|
|
13
13
|
import json
|
|
14
14
|
import os
|
|
15
15
|
import re
|
|
16
|
+
import shlex
|
|
16
17
|
import shutil
|
|
17
18
|
import subprocess
|
|
18
19
|
import sys
|
|
@@ -264,7 +265,50 @@ def run_command(cmd: list[str], *, env: dict[str, str], cwd: Path, timeout: int)
|
|
|
264
265
|
timeout=timeout,
|
|
265
266
|
check=False,
|
|
266
267
|
)
|
|
267
|
-
|
|
268
|
+
stdout = (result.stdout or "").strip()
|
|
269
|
+
stderr = (result.stderr or "").strip()
|
|
270
|
+
if result.returncode != 0:
|
|
271
|
+
detail = "\n".join(part for part in (stdout, stderr) if part)
|
|
272
|
+
if len(detail) > 2000:
|
|
273
|
+
detail = detail[:1975] + "... [truncated]"
|
|
274
|
+
command = " ".join(shlex.quote(_describe_arg(part)) for part in cmd)
|
|
275
|
+
if len(command) > 300:
|
|
276
|
+
command = command[:275] + "... [truncated]"
|
|
277
|
+
message = f"Command failed ({result.returncode}): {command}"
|
|
278
|
+
if detail:
|
|
279
|
+
message = f"{message}\n{detail}"
|
|
280
|
+
raise RuntimeError(message)
|
|
281
|
+
return stdout or stderr
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def _describe_arg(arg: str, limit: int = 120) -> str:
|
|
285
|
+
if len(arg) <= limit:
|
|
286
|
+
return arg
|
|
287
|
+
return f"<arg:{len(arg)} chars>"
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
_CLAUDE_SAFE_MODE_ARGS: list[str] | None = None
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def claude_safe_mode_args() -> list[str]:
|
|
294
|
+
"""Return Claude args that suppress hooks when supported."""
|
|
295
|
+
global _CLAUDE_SAFE_MODE_ARGS
|
|
296
|
+
if _CLAUDE_SAFE_MODE_ARGS is not None:
|
|
297
|
+
return _CLAUDE_SAFE_MODE_ARGS
|
|
298
|
+
try:
|
|
299
|
+
result = subprocess.run(
|
|
300
|
+
["claude", "--help"],
|
|
301
|
+
capture_output=True,
|
|
302
|
+
text=True,
|
|
303
|
+
timeout=5,
|
|
304
|
+
check=False,
|
|
305
|
+
)
|
|
306
|
+
except (OSError, subprocess.SubprocessError):
|
|
307
|
+
_CLAUDE_SAFE_MODE_ARGS = []
|
|
308
|
+
else:
|
|
309
|
+
help_text = f"{result.stdout}\n{result.stderr}"
|
|
310
|
+
_CLAUDE_SAFE_MODE_ARGS = ["--safe-mode"] if "--safe-mode" in help_text else []
|
|
311
|
+
return _CLAUDE_SAFE_MODE_ARGS
|
|
268
312
|
|
|
269
313
|
|
|
270
314
|
def extract_task_json_output(output: str) -> str:
|
|
@@ -376,7 +420,14 @@ def run_native_provider(ctx, prompt: str) -> str:
|
|
|
376
420
|
env = {**os.environ, "MEMSEARCH_NO_WATCH": "1"}
|
|
377
421
|
|
|
378
422
|
if ctx.platform == "claude-code":
|
|
379
|
-
cmd = [
|
|
423
|
+
cmd = [
|
|
424
|
+
"claude",
|
|
425
|
+
"-p",
|
|
426
|
+
*claude_safe_mode_args(),
|
|
427
|
+
"--strict-mcp-config",
|
|
428
|
+
"--no-session-persistence",
|
|
429
|
+
"--no-chrome",
|
|
430
|
+
]
|
|
380
431
|
# Skill distillation needs to read original transcripts for exact commands.
|
|
381
432
|
# Open a narrow hole — Bash limited to the two read-only memsearch drill
|
|
382
433
|
# commands — only for that task; other maintenance keeps all tools off.
|
|
@@ -392,6 +443,7 @@ def run_native_provider(ctx, prompt: str) -> str:
|
|
|
392
443
|
prompt,
|
|
393
444
|
]
|
|
394
445
|
env["CLAUDECODE"] = ""
|
|
446
|
+
env["MEMSEARCH_DISABLE"] = "1"
|
|
395
447
|
return run_command(cmd, env=env, cwd=ctx.project_dir, timeout=120)
|
|
396
448
|
|
|
397
449
|
if ctx.platform == "codex":
|
|
@@ -15,7 +15,7 @@ When this skill is triggered, inspect the user's request text. If there is no co
|
|
|
15
15
|
|
|
16
16
|
- Empty request or "check": diagnose current MemSearch setup.
|
|
17
17
|
- "Show/get setting": read the requested resolved/global/project value.
|
|
18
|
-
- "Set/enable/disable/change":
|
|
18
|
+
- "Set/enable/disable/change": choose global vs project scope explicitly; use global config for trusted plugin automation/provider/prompt/endpoint settings and project config only for allowlisted local indexing knobs.
|
|
19
19
|
- "Not capturing/search empty/no memory": troubleshoot files, config, and index health.
|
|
20
20
|
- "Use OpenAI/Gemini/Anthropic/native/model": configure provider routing.
|
|
21
21
|
- "PROJECT.md/USER.md/profile/review": configure advanced maintenance.
|
|
@@ -114,14 +114,33 @@ Config is resolved from built-in defaults, global config, project config, env re
|
|
|
114
114
|
|
|
115
115
|
Use `memsearch config list --resolved` for effective behavior, `--global` for global overrides, and `--project` for repository-specific overrides.
|
|
116
116
|
|
|
117
|
+
Since v0.4.11, project-local `.memsearch.toml` is restricted before it is merged.
|
|
118
|
+
Only these low-risk local indexing keys are honored from project config:
|
|
119
|
+
|
|
120
|
+
- `milvus.collection`
|
|
121
|
+
- `embedding.batch_size`
|
|
122
|
+
- `chunking.max_chunk_size`
|
|
123
|
+
- `chunking.overlap_lines`
|
|
124
|
+
- `watch.debounce_ms`
|
|
125
|
+
|
|
126
|
+
Trusted settings are ignored or rejected in project config. Put these in global
|
|
127
|
+
config (`~/.memsearch/config.toml`) or pass explicit CLI flags instead:
|
|
128
|
+
|
|
129
|
+
- provider/model/API endpoint/API key settings
|
|
130
|
+
- `[llm]` and `[llm.providers.*]`
|
|
131
|
+
- `[prompts]`
|
|
132
|
+
- plugin automation such as `plugins.opencode.project_review.enabled`,
|
|
133
|
+
`plugins.opencode.user_profile.enabled`, and
|
|
134
|
+
`plugins.opencode.memory_to_skill.enabled`
|
|
135
|
+
|
|
117
136
|
Default recommendation:
|
|
118
137
|
|
|
119
138
|
- Put reusable defaults in global config so users do not repeat setup in every project.
|
|
120
|
-
- Put only
|
|
121
|
-
- Use global config for named LLM providers, common model choices,
|
|
122
|
-
-
|
|
139
|
+
- Put only allowlisted local indexing overrides in project config.
|
|
140
|
+
- Use global config for named LLM providers, common model choices, plugin enable/disable switches, task intervals, install paths, and shared prompt defaults.
|
|
141
|
+
- If the user wants advanced maintenance enabled for all projects, set the plugin keys globally. The default relative `input_dir` / `output_file` values still resolve inside each current project.
|
|
123
142
|
|
|
124
|
-
Maintenance `input_dir` and `output_file` may be relative even when configured globally. They are resolved from the current project directory at runtime. For custom prompt paths, prefer absolute paths in global config
|
|
143
|
+
Maintenance `input_dir` and `output_file` may be relative even when configured globally. They are resolved from the current project directory at runtime, so a global `output_file = ".memsearch/PROJECT.md"` writes to each project's own `.memsearch/PROJECT.md`. For custom prompt paths, prefer absolute paths in global config; project prompt paths are not trusted.
|
|
125
144
|
|
|
126
145
|
OpenCode plugin keys:
|
|
127
146
|
|
|
@@ -189,7 +208,11 @@ Model guidance:
|
|
|
189
208
|
|
|
190
209
|
Advanced maintenance runs after the plugin wakes it, only when enabled, journal input changed, and `min_interval_hours` elapsed. `PROJECT.md` and `USER.md` are maintenance artifacts by default and are not automatically indexed.
|
|
191
210
|
|
|
192
|
-
|
|
211
|
+
If advanced maintenance or `memory_to_skill` seems silent, check
|
|
212
|
+
`.memsearch/.maintenance-state.json` for `<plugin>.<task>.last_error` and
|
|
213
|
+
`last_failed_at`; background hook errors may not surface in the chat.
|
|
214
|
+
|
|
215
|
+
Before enabling advanced maintenance, ask which provider to use, whether the default 24-hour interval is acceptable, whether `.memsearch/PROJECT.md` / `.memsearch/USER.md` are acceptable output files, and whether the user wants the enablement global. Do not write plugin automation keys with `--project`; v0.4.11+ project config ignores or rejects them.
|
|
193
216
|
|
|
194
217
|
Prompt overrides:
|
|
195
218
|
|
|
@@ -203,8 +226,8 @@ memory_to_skill = ""
|
|
|
203
226
|
|
|
204
227
|
Empty prompt paths mean use the built-in MemSearch prompts. Custom prompt files may use `{{AGENT_NAME}}`, `{{TASK_NAME}}`, `{{PROJECT_DIR}}`, `{{INPUT_DIR}}`, and `{{OUTPUT_FILE}}`; the runner appends existing output, recent journals, and digest automatically.
|
|
205
228
|
|
|
206
|
-
Use `memsearch config set` for changes. After changing anything, show the command, the resolved value, and whether a new session is needed.
|
|
229
|
+
Use `memsearch config set` for changes. For trusted keys such as `plugins.*`, `[llm.providers.*]`, `[prompts]`, `embedding.provider`, or `milvus.uri`, set global config by omitting `--project`. Use `--project` only for allowlisted local indexing keys. After changing anything, show the command, the resolved value, and whether a new session is needed.
|
|
207
230
|
|
|
208
231
|
MemSearch TOML changes are read lazily by the CLI, capture daemon, and maintenance runner, so values such as `plugins.opencode.summarize.*`, `plugins.opencode.project_review.*`, `plugins.opencode.user_profile.*`, `[llm.providers.*]`, `[prompts]`, `milvus.*`, and `embedding.*` usually apply on the next capture, recall, index, or maintenance invocation. Restart OpenCode after `opencode.json` or plugin package changes; if capture behavior still looks stale after TOML edits, restart OpenCode or the capture daemon. In final diagnostic/change summaries, make clear that this is MemSearch memory configuration, not OpenCode's own memory/config system.
|
|
209
232
|
|
|
210
|
-
When useful, remind the user that they can either continue using this `memory-config` skill for guided configuration, or manually run `memsearch config init` for global interactive setup, `memsearch config init --project` for project
|
|
233
|
+
When useful, remind the user that they can either continue using this `memory-config` skill for guided configuration, or manually run `memsearch config init` for global interactive setup, `memsearch config init --project` for allowlisted project indexing setup, and `memsearch config set/get/list` for direct CLI changes.
|
|
@@ -11,6 +11,8 @@ OpenCode's built-in skills system.
|
|
|
11
11
|
Stages: **0** memory journals → **1** candidate (`.memsearch/skill-candidates/`,
|
|
12
12
|
a git-tracked store that keeps evolving) → **2** installed (an agent skill dir).
|
|
13
13
|
Candidates are never installed automatically; installing is always a human step.
|
|
14
|
+
User requests may stop at candidate creation/review, or continue to installation
|
|
15
|
+
in the same turn after explicit approval; match the requested stage.
|
|
14
16
|
|
|
15
17
|
## Intent routing
|
|
16
18
|
|
|
@@ -37,24 +39,35 @@ printf '%s' "## <title>\n\n1. ...\n2. ..." | memsearch skills add \
|
|
|
37
39
|
```
|
|
38
40
|
|
|
39
41
|
`add` handles slugging, standard frontmatter, meta.json, and the git commit — no
|
|
40
|
-
LLM is involved. Then show it to the user
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
LLM is involved. Then show it to the user; install it only if the user asked for
|
|
43
|
+
that or explicitly approves (see **B**). Finally, check whether background
|
|
44
|
+
distillation is on; if not, offer to enable it (so recurring workflows get
|
|
45
|
+
captured automatically going forward) — do not force it.
|
|
43
46
|
|
|
44
47
|
## B. Review & install candidates (1→2)
|
|
45
48
|
|
|
46
49
|
```bash
|
|
47
50
|
memsearch skills list # add -j for sources / installed paths
|
|
51
|
+
git -C .memsearch/skill-candidates log --oneline -5 2>/dev/null || true
|
|
48
52
|
```
|
|
49
53
|
|
|
50
54
|
Before recommending or installing, skim the candidate's body: if a step looks uncertain or loosely summarized, re-check it against the source (open the transcript if needed) or flag it to the user and let them decide — installing copies the candidate as-is, so this is the last chance to catch a wrong step.
|
|
55
|
+
When showing candidates, mention the store's recent git history when it helps
|
|
56
|
+
explain whether a candidate is new, evolved, removed, or re-created.
|
|
51
57
|
|
|
52
|
-
|
|
58
|
+
Treat installation as an interactive checkpoint. Show the candidate, apply any
|
|
59
|
+
requested tweaks before installing, and confirm the install destination with the
|
|
60
|
+
user. Resolve install targets from config first: if `paths` is a non-empty
|
|
61
|
+
list, present those paths as the proposed destinations and pass each entry as a
|
|
62
|
+
`--path` after confirmation. If it is empty, ask the user where to install; do
|
|
63
|
+
not silently fall back to a default path.
|
|
53
64
|
|
|
54
65
|
```bash
|
|
55
66
|
memsearch config get plugins.opencode.memory_to_skill.paths 2>/dev/null || echo "[]"
|
|
56
|
-
memsearch skills install <name> --path
|
|
67
|
+
memsearch skills install <name> --path <configured-or-user-approved-path>
|
|
57
68
|
```
|
|
69
|
+
After installation, remind the user to start a fresh agent session or reopen the
|
|
70
|
+
conversation so the newly installed skill is loaded.
|
|
58
71
|
|
|
59
72
|
If the list is **empty**, background distillation is likely off or has not run.
|
|
60
73
|
Offer the user a choice: capture from recent work now (**A**), distill from
|
|
@@ -71,20 +84,25 @@ generalize, not one-offs from a single day.
|
|
|
71
84
|
|
|
72
85
|
**Drill into the original before drafting.** The journal bullets are a lossy summary; the exact commands, flags, and paths live in the original conversation. OpenCode stores transcripts in its session database (not a file), and the journal anchor carries a `session:` id (and `turn:` when present). Run `python3 __INSTALL_DIR__/scripts/parse-transcript.py <session_id>` (add `--turn <id>` when the anchor has one) to read the original turns with their tool calls — that is where the executed commands and output live. Write the skill from that. If the shown excerpt feels incomplete, skim nearby turns in the same original source before committing to exact commands or paths. If you cannot read it or confirm a detail, keep the step general or omit it — never fabricate.
|
|
73
86
|
|
|
74
|
-
The background pass mines automatically when enabled,
|
|
87
|
+
The background pass mines automatically when enabled, starting from the summaries; doing it here on demand lets you inspect the original transcripts more deliberately, so the result can be more accurate.
|
|
75
88
|
|
|
76
89
|
## D. Configure
|
|
77
90
|
|
|
78
91
|
```bash
|
|
79
92
|
memsearch config get plugins.opencode.memory_to_skill.enabled 2>/dev/null || echo "false"
|
|
80
|
-
# enable the background pass (do not enable silently)
|
|
81
|
-
memsearch config set plugins.opencode.memory_to_skill.enabled true
|
|
93
|
+
# enable the background pass globally (do not enable silently)
|
|
94
|
+
memsearch config set plugins.opencode.memory_to_skill.enabled true
|
|
82
95
|
# how eagerly history-mining distils (default 3; lower = more eager)
|
|
83
|
-
memsearch config set plugins.opencode.memory_to_skill.min_occurrences 3
|
|
96
|
+
memsearch config set plugins.opencode.memory_to_skill.min_occurrences 3
|
|
84
97
|
# pre-set install targets (otherwise you are asked at install time)
|
|
85
|
-
memsearch config set plugins.opencode.memory_to_skill.paths '[".agents/skills"]'
|
|
98
|
+
memsearch config set plugins.opencode.memory_to_skill.paths '[".agents/skills"]'
|
|
86
99
|
```
|
|
87
100
|
|
|
101
|
+
Since v0.4.11, project-local `.memsearch.toml` accepts only allowlisted local
|
|
102
|
+
indexing keys. Do not use `--project` for `plugins.*` settings such as
|
|
103
|
+
`memory_to_skill.enabled`, `min_occurrences`, or `paths`; put them in global
|
|
104
|
+
config instead.
|
|
105
|
+
|
|
88
106
|
Note: `enabled` only gates the **background** (session-end) pass. The explicit
|
|
89
107
|
commands above (`skills add`, `skills install`) always work, and you can mine history (C) directly.
|
|
90
108
|
|