one-code-extension 0.1.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.
Files changed (153) hide show
  1. package/LICENSE +32 -0
  2. package/README.md +202 -0
  3. package/agents/explore.md +24 -0
  4. package/agents/general-purpose.md +21 -0
  5. package/agents/plan.md +22 -0
  6. package/docs/decisions/auto-mode.md +526 -0
  7. package/docs/decisions/branding.md +127 -0
  8. package/docs/decisions/code-review-remediation.md +106 -0
  9. package/docs/decisions/compaction.md +140 -0
  10. package/docs/decisions/distribution.md +133 -0
  11. package/docs/decisions/hooks.md +64 -0
  12. package/docs/decisions/lsp.md +58 -0
  13. package/docs/decisions/mcp.md +104 -0
  14. package/docs/decisions/memory-state.md +170 -0
  15. package/docs/decisions/model-policy.md +126 -0
  16. package/docs/decisions/modes.md +178 -0
  17. package/docs/decisions/skills-plugins.md +56 -0
  18. package/docs/decisions/subagents-workflows.md +225 -0
  19. package/docs/decisions/system-prompt.md +98 -0
  20. package/docs/decisions/tools.md +407 -0
  21. package/docs/decisions/tui.md +202 -0
  22. package/docs/decisions.md +148 -0
  23. package/extensions/ask-user/index.ts +124 -0
  24. package/extensions/ask-user/questions.ts +60 -0
  25. package/extensions/auto-mode/actions.ts +78 -0
  26. package/extensions/auto-mode/classifier.ts +388 -0
  27. package/extensions/auto-mode/config.ts +237 -0
  28. package/extensions/auto-mode/decision-log.ts +60 -0
  29. package/extensions/auto-mode/defaults.ts +65 -0
  30. package/extensions/auto-mode/instructions.ts +74 -0
  31. package/extensions/auto-mode/model-picker.ts +225 -0
  32. package/extensions/auto-mode/model-select.ts +178 -0
  33. package/extensions/auto-mode/paths.ts +109 -0
  34. package/extensions/auto-mode/pause.ts +96 -0
  35. package/extensions/auto-mode/prompt.ts +307 -0
  36. package/extensions/auto-mode/rules.ts +96 -0
  37. package/extensions/auto-mode/safety-floor.ts +100 -0
  38. package/extensions/auto-mode/sensitive.ts +137 -0
  39. package/extensions/auto-mode/shell-analysis.ts +702 -0
  40. package/extensions/background/index.ts +405 -0
  41. package/extensions/background/registry.ts +81 -0
  42. package/extensions/background/wakeup.ts +41 -0
  43. package/extensions/bash/background.ts +136 -0
  44. package/extensions/bash/index.ts +149 -0
  45. package/extensions/branding/assistant-marker.ts +53 -0
  46. package/extensions/branding/index.ts +366 -0
  47. package/extensions/branding/prompt-editor.ts +42 -0
  48. package/extensions/branding/prompt-marker.ts +29 -0
  49. package/extensions/branding/startup.ts +152 -0
  50. package/extensions/claude-compat/index.ts +50 -0
  51. package/extensions/claude-context/index.ts +79 -0
  52. package/extensions/compaction/index.ts +231 -0
  53. package/extensions/compaction/prompt.ts +191 -0
  54. package/extensions/context-management/index.ts +147 -0
  55. package/extensions/effort/index.ts +216 -0
  56. package/extensions/effort/slider.ts +253 -0
  57. package/extensions/exit/index.ts +28 -0
  58. package/extensions/file-tracker/index.ts +103 -0
  59. package/extensions/file-tracker/tracker.ts +145 -0
  60. package/extensions/hooks/debug.ts +53 -0
  61. package/extensions/hooks/executor.ts +132 -0
  62. package/extensions/hooks/index.ts +310 -0
  63. package/extensions/hooks/matcher.ts +72 -0
  64. package/extensions/hooks/plugin-hooks.ts +79 -0
  65. package/extensions/hooks/protocol.ts +151 -0
  66. package/extensions/hooks/settings.ts +163 -0
  67. package/extensions/hooks/trust.ts +151 -0
  68. package/extensions/lib/branch-restore.ts +35 -0
  69. package/extensions/lib/claude-context.ts +148 -0
  70. package/extensions/lib/deferred.ts +119 -0
  71. package/extensions/lib/git.ts +14 -0
  72. package/extensions/lib/memory.ts +219 -0
  73. package/extensions/lib/model-policy.ts +436 -0
  74. package/extensions/lib/model-tier.ts +99 -0
  75. package/extensions/lib/notifications.ts +18 -0
  76. package/extensions/lib/paths.ts +26 -0
  77. package/extensions/lib/per-cwd.ts +21 -0
  78. package/extensions/lib/persisted-output.ts +70 -0
  79. package/extensions/lib/pi-version.ts +54 -0
  80. package/extensions/lib/plugins.ts +227 -0
  81. package/extensions/lib/reminders.ts +212 -0
  82. package/extensions/lib/scratchpad.ts +68 -0
  83. package/extensions/lib/tui-render.ts +522 -0
  84. package/extensions/lsp/client.ts +278 -0
  85. package/extensions/lsp/format.ts +46 -0
  86. package/extensions/lsp/index.ts +193 -0
  87. package/extensions/lsp/protocol.ts +64 -0
  88. package/extensions/lsp/servers.ts +79 -0
  89. package/extensions/mcp/client.ts +205 -0
  90. package/extensions/mcp/config.ts +199 -0
  91. package/extensions/mcp/index.ts +373 -0
  92. package/extensions/mcp/schema.ts +161 -0
  93. package/extensions/memory/index.ts +90 -0
  94. package/extensions/notebook/index.ts +77 -0
  95. package/extensions/notebook/notebook.ts +108 -0
  96. package/extensions/permissions/index.ts +876 -0
  97. package/extensions/permissions/matcher.ts +455 -0
  98. package/extensions/permissions/modes.ts +107 -0
  99. package/extensions/permissions/protected-paths.ts +125 -0
  100. package/extensions/permissions/settings.ts +98 -0
  101. package/extensions/plan-mode/index.ts +209 -0
  102. package/extensions/plan-mode/reminder.ts +33 -0
  103. package/extensions/plan-mode/slug.ts +28 -0
  104. package/extensions/plan-mode/viewer.ts +124 -0
  105. package/extensions/plugins/index.ts +115 -0
  106. package/extensions/plugins/template.ts +42 -0
  107. package/extensions/search-tools/index.ts +0 -0
  108. package/extensions/search-tools/tools.ts +24 -0
  109. package/extensions/skill/index.ts +170 -0
  110. package/extensions/subagents/agents.ts +126 -0
  111. package/extensions/subagents/child.ts +414 -0
  112. package/extensions/subagents/default-model.ts +122 -0
  113. package/extensions/subagents/index.ts +1119 -0
  114. package/extensions/subagents/model-select.ts +346 -0
  115. package/extensions/subagents/rpc-turns.ts +123 -0
  116. package/extensions/subagents/runs.ts +93 -0
  117. package/extensions/subagents/usage.ts +53 -0
  118. package/extensions/subagents/worktree.ts +69 -0
  119. package/extensions/system-prompt/environment.ts +35 -0
  120. package/extensions/system-prompt/index.ts +54 -0
  121. package/extensions/system-prompt/template.ts +103 -0
  122. package/extensions/system-prompt/tiers/common.ts +63 -0
  123. package/extensions/system-prompt/tiers/frontier.ts +24 -0
  124. package/extensions/system-prompt/tiers/low.ts +71 -0
  125. package/extensions/system-prompt/tiers/mid.ts +56 -0
  126. package/extensions/system-reminder/index.ts +37 -0
  127. package/extensions/tasks/index.ts +140 -0
  128. package/extensions/tasks/store.ts +174 -0
  129. package/extensions/todo/index.ts +133 -0
  130. package/extensions/tool-search/index.ts +168 -0
  131. package/extensions/tool-style/index.ts +84 -0
  132. package/extensions/web/index.ts +43 -0
  133. package/extensions/web-fetch/extract.ts +139 -0
  134. package/extensions/web-fetch/index.ts +291 -0
  135. package/extensions/web-fetch/summarize.ts +78 -0
  136. package/extensions/workflow/agent-session.ts +368 -0
  137. package/extensions/workflow/globals.ts +251 -0
  138. package/extensions/workflow/index.ts +364 -0
  139. package/extensions/workflow/journal.ts +75 -0
  140. package/extensions/workflow/permission-gate.ts +71 -0
  141. package/extensions/workflow/run-manager.ts +306 -0
  142. package/extensions/workflow/saved-workflows.ts +45 -0
  143. package/extensions/workflow/script-source.ts +201 -0
  144. package/extensions/workflow/types.ts +99 -0
  145. package/extensions/workflow/vm-runtime.ts +31 -0
  146. package/extensions/workflow/widget.ts +56 -0
  147. package/extensions/worktree/index.ts +266 -0
  148. package/extensions/worktree/rewrite.ts +59 -0
  149. package/package.json +101 -0
  150. package/themes/one-code-light.json +76 -0
  151. package/themes/one-code.json +76 -0
  152. package/types/app.d.ts +5 -0
  153. package/types/community-packages.d.ts +12 -0
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Deferred-tool registry — Claude Code's ToolSearch mechanism.
3
+ *
4
+ * Tools registered here stay inactive at session start; the model discovers
5
+ * them with `tool_search`, which activates matches additively so pi can use
6
+ * native deferred loading (Anthropic `defer_loading` / OpenAI `tool_search_call`)
7
+ * and keep the cached prompt prefix intact.
8
+ *
9
+ * Any extension can defer its own tools by emitting on DEFER_CHANNEL with
10
+ * `{ name, keywords? }` while extensions are loading (before session_start).
11
+ */
12
+
13
+ export const DEFER_CHANNEL = "one-code:defer-tool";
14
+
15
+ export interface DeferRequest {
16
+ name: string;
17
+ /** Extra search terms beyond the tool's name and description. */
18
+ keywords?: string[];
19
+ }
20
+
21
+ export interface SearchableTool {
22
+ name: string;
23
+ description: string;
24
+ keywords: string[];
25
+ }
26
+
27
+ export class DeferredRegistry {
28
+ private entries = new Map<string, string[]>();
29
+
30
+ add(request: DeferRequest): void {
31
+ if (!request?.name) return;
32
+ const existing = this.entries.get(request.name) ?? [];
33
+ this.entries.set(request.name, [...new Set([...existing, ...(request.keywords ?? [])])]);
34
+ }
35
+
36
+ get names(): string[] {
37
+ return [...this.entries.keys()];
38
+ }
39
+
40
+ keywordsFor(name: string): string[] {
41
+ return this.entries.get(name) ?? [];
42
+ }
43
+
44
+ has(name: string): boolean {
45
+ return this.entries.has(name);
46
+ }
47
+ }
48
+
49
+ export const deferredRegistry = new DeferredRegistry();
50
+
51
+ /**
52
+ * The every-turn reminder telling the model which tools exist but are not
53
+ * loaded. One line per tool (first description line only), like Claude Code's
54
+ * deferred-tools listing.
55
+ */
56
+ export function deferredReminderText(tools: Array<Pick<SearchableTool, "name" | "description">>): string {
57
+ return [
58
+ "The following tools are available but their schemas are NOT loaded, so they cannot be called yet:",
59
+ ...tools.map((t) => `- ${t.name}: ${t.description.split("\n")[0]}`),
60
+ "",
61
+ "Load one with tool_search before using it — `select:<name>[,<name>]` for exact names, or keywords to search. Once a schema is loaded it stays callable for the rest of the session.",
62
+ ].join("\n");
63
+ }
64
+
65
+ export interface SearchMatch {
66
+ name: string;
67
+ score: number;
68
+ }
69
+
70
+ /**
71
+ * The exact names a `select:` query asks for (lowercased), or undefined when the
72
+ * query is not a select: query. Lets tool_search report which requested names
73
+ * matched nothing instead of silently dropping them.
74
+ */
75
+ export function selectedNames(query: string): string[] | undefined {
76
+ const trimmed = query.trim();
77
+ if (!trimmed.toLowerCase().startsWith("select:")) return undefined;
78
+ return trimmed
79
+ .slice("select:".length)
80
+ .split(",")
81
+ .map((n) => n.trim().toLowerCase())
82
+ .filter(Boolean);
83
+ }
84
+
85
+ /**
86
+ * Claude Code's ToolSearch query syntax:
87
+ * "select:Read,Edit" — exact names, no scoring
88
+ * "+slack send" — require "slack" in the tool name, rank by the rest
89
+ * "notebook jupyter" — keyword search over name, description, keywords
90
+ */
91
+ export function searchTools(query: string, tools: SearchableTool[], maxResults = 5): SearchMatch[] {
92
+ const trimmed = query.trim();
93
+
94
+ const selected = selectedNames(query);
95
+ if (selected) {
96
+ return tools
97
+ .filter((t) => selected.includes(t.name.toLowerCase()))
98
+ .map((t) => ({ name: t.name, score: Number.POSITIVE_INFINITY }));
99
+ }
100
+
101
+ const rawTerms = trimmed.toLowerCase().split(/[^a-z0-9_+]+/).filter(Boolean);
102
+ const required = rawTerms.filter((t) => t.startsWith("+")).map((t) => t.slice(1)).filter(Boolean);
103
+ const terms = rawTerms.filter((t) => !t.startsWith("+"));
104
+
105
+ const scored = tools
106
+ .filter((t) => required.every((r) => t.name.toLowerCase().includes(r)))
107
+ .map((t) => {
108
+ const haystack = `${t.name} ${t.description} ${t.keywords.join(" ")}`.toLowerCase();
109
+ let score = required.length * 2;
110
+ for (const term of terms) {
111
+ if (t.name.toLowerCase().includes(term)) score += 2;
112
+ else if (haystack.includes(term)) score += 1;
113
+ }
114
+ return { name: t.name, score };
115
+ })
116
+ .filter((m) => m.score > 0);
117
+
118
+ return scored.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name)).slice(0, maxResults);
119
+ }
@@ -0,0 +1,14 @@
1
+ /** Shared git helpers — pure functions only (safe to import across extensions). */
2
+
3
+ import { existsSync } from "node:fs";
4
+ import { dirname, join } from "node:path";
5
+
6
+ export function findGitRoot(startDir: string): string | undefined {
7
+ let dir = startDir;
8
+ while (true) {
9
+ if (existsSync(join(dir, ".git"))) return dir;
10
+ const parent = dirname(dir);
11
+ if (parent === dir) return undefined;
12
+ dir = parent;
13
+ }
14
+ }
@@ -0,0 +1,219 @@
1
+ /**
2
+ * Claude Code-style auto-memory: a per-project directory of one-fact-per-file
3
+ * markdown memories plus a MEMORY.md index that is loaded into context each
4
+ * session. The model itself reads and writes the files with the ordinary file
5
+ * tools — the harness only guarantees the directory exists, describes the
6
+ * format in the system prompt, and injects the index on the first turn.
7
+ *
8
+ * Pure path/text helpers live here; `extensions/memory` does the filesystem
9
+ * wiring and `extensions/system-prompt` embeds the prompt section. Both
10
+ * re-derive paths from (home, cwd) rather than sharing state, since jiti gives
11
+ * each extension its own module instance.
12
+ *
13
+ * What is NOT replicated: Claude Code's relevance-based recall of individual
14
+ * memories mid-session. Its selection mechanism is undocumented client
15
+ * internals, and no recalled-memory block appears in either captured context
16
+ * we have — the index is the entry point; the model follows links from there.
17
+ */
18
+
19
+ import os from "node:os";
20
+ import { join } from "node:path";
21
+ import { findGitRoot } from "./git.ts";
22
+
23
+ /** Claude Code's project-directory slug: every char outside [A-Za-z0-9-] becomes "-". */
24
+ export function projectSlug(projectRoot: string): string {
25
+ return projectRoot.replace(/[^A-Za-z0-9-]/g, "-");
26
+ }
27
+
28
+ /**
29
+ * `~/.claude/projects/<slug>/memory` — the same location Claude Code uses.
30
+ * `projectRoot` must be the git repository root when there is one (all
31
+ * worktrees and subdirectories share one memory directory), else the cwd.
32
+ */
33
+ export function memoryDir(home: string, projectRoot: string): string {
34
+ return join(home, ".claude", "projects", projectSlug(projectRoot), "memory");
35
+ }
36
+
37
+ /**
38
+ * `memoryDir` composed with the actual project-root resolution every caller
39
+ * needs: the git repository root when there is one (shared by worktrees and
40
+ * subdirectories), else `cwd` itself. `home` defaults to `os.homedir()`.
41
+ */
42
+ export function projectMemoryDir(cwd: string, home: string = os.homedir()): string {
43
+ return memoryDir(home, findGitRoot(cwd) ?? cwd);
44
+ }
45
+
46
+ /**
47
+ * Claude Code loads only the first 200 lines or 25KB of MEMORY.md, whichever
48
+ * comes first; the rest is silently dropped. Mirror that so an overgrown index
49
+ * behaves identically here. The limits are measured against what actually
50
+ * loads: YAML frontmatter and block-level HTML comments are stripped first.
51
+ */
52
+ export const INDEX_MAX_LINES = 200;
53
+ export const INDEX_MAX_BYTES = 25_000;
54
+
55
+ /** The index content that loads: frontmatter and whole-line HTML comments removed. */
56
+ export function loadableIndexContent(content: string): string {
57
+ let out = content;
58
+ if (out.startsWith("---\n")) {
59
+ const close = out.indexOf("\n---", 3);
60
+ if (close !== -1) {
61
+ const lineEnd = out.indexOf("\n", close + 1 + 3);
62
+ out = lineEnd === -1 ? "" : out.slice(lineEnd + 1);
63
+ }
64
+ }
65
+ return out.replace(/^[ \t]*<!--[\s\S]*?-->[ \t]*\n?/gm, "");
66
+ }
67
+
68
+ export function truncateIndex(content: string): string {
69
+ let out = loadableIndexContent(content).split("\n").slice(0, INDEX_MAX_LINES).join("\n");
70
+ if (Buffer.byteLength(out, "utf8") > INDEX_MAX_BYTES) {
71
+ out = Buffer.from(out, "utf8").subarray(0, INDEX_MAX_BYTES).toString("utf8");
72
+ // A byte cut can split a multi-byte character; drop the replacement char.
73
+ out = out.replace(/�+$/, "");
74
+ }
75
+ return out;
76
+ }
77
+
78
+ /**
79
+ * Where the index stands against its load limits, checked after each write so
80
+ * the model hears about an overgrown index while it can still fix it (Claude
81
+ * Code behaves the same: near-limit reminder, over-limit error — the write
82
+ * itself always succeeds).
83
+ */
84
+ export function indexLimitStatus(content: string): "ok" | "near" | "over" {
85
+ const loadable = loadableIndexContent(content);
86
+ const lines = loadable.split("\n").length;
87
+ const bytes = Buffer.byteLength(loadable, "utf8");
88
+ if (lines > INDEX_MAX_LINES || bytes > INDEX_MAX_BYTES) return "over";
89
+ if (lines >= INDEX_MAX_LINES * 0.9 || bytes >= INDEX_MAX_BYTES * 0.9) return "near";
90
+ return "ok";
91
+ }
92
+
93
+ export const INDEX_NEAR_LIMIT_REMINDER = `MEMORY.md is approaching its load limit (only the first ${INDEX_MAX_LINES} lines or 25KB are loaded each session). Shorten it now: keep one line per entry, move detail into topic files in the memory directory, and merge or drop stale entries.`;
94
+
95
+ export const INDEX_OVER_LIMIT_ERROR = `MEMORY.md is over its load limit (${INDEX_MAX_LINES} lines / 25KB): everything past the limit is dropped the next time it is loaded. The write succeeded, but rewrite the index now — one line per entry, move detail into topic files, merge or drop stale entries.`;
96
+
97
+ /**
98
+ * Claude Code stamps bookkeeping fields into a memory file's frontmatter at
99
+ * write time: node_type, the writing session's id, and a modified timestamp
100
+ * (observed in real memory files; `modified` is also documented). A file
101
+ * without frontmatter is left untouched — Claude Code never adds frontmatter
102
+ * to one, which also keeps MEMORY.md unstamped. Line-based on the template's
103
+ * `metadata:` mapping; a re-stamp replaces the previous values.
104
+ */
105
+ export function stampFrontmatter(content: string, sessionId: string, modifiedIso: string): string {
106
+ if (!content.startsWith("---\n")) return content;
107
+ const close = content.indexOf("\n---", 3);
108
+ if (close === -1) return content;
109
+
110
+ const body = content.slice(4, close);
111
+ const rest = content.slice(close);
112
+ const lines = body.split("\n").filter((l) => !/^\s{2}(node_type|originSessionId|modified):/.test(l));
113
+
114
+ const metaIndex = lines.findIndex((l) => /^metadata:\s*$/.test(l));
115
+ const tail = [` originSessionId: ${sessionId}`, ` modified: ${modifiedIso}`];
116
+ let out: string[];
117
+ if (metaIndex === -1) {
118
+ out = [...lines, "metadata:", " node_type: memory", ...tail];
119
+ } else {
120
+ let childEnd = metaIndex + 1;
121
+ while (childEnd < lines.length && /^\s+\S/.test(lines[childEnd])) childEnd++;
122
+ out = [
123
+ ...lines.slice(0, metaIndex + 1),
124
+ " node_type: memory",
125
+ ...lines.slice(metaIndex + 1, childEnd),
126
+ ...tail,
127
+ ...lines.slice(childEnd),
128
+ ];
129
+ }
130
+ return `---\n${out.join("\n")}${rest}`;
131
+ }
132
+
133
+ /**
134
+ * The `# Memory` system prompt section, kept close to Claude Code's wording.
135
+ * Depends only on the directory path (and the fixed `verbose` flag for a given
136
+ * tier), so the prompt stays byte-stable across turns for a given cwd.
137
+ *
138
+ * `verbose` selects the long, explicit spec (Claude Code's Haiku memory prompt,
139
+ * adapted) for the mid/low tiers; frontier gets the compact version.
140
+ */
141
+ export function memoryPromptSection(dir: string, verbose = false): string {
142
+ return verbose ? verboseMemorySection(dir) : compactMemorySection(dir);
143
+ }
144
+
145
+ function compactMemorySection(dir: string): string {
146
+ return `# Memory
147
+
148
+ You have a persistent file-based memory at \`${dir}/\`. This directory already exists — write to it directly with the write tool (do not run mkdir or check for its existence). Each memory is one file holding one fact, with frontmatter:
149
+
150
+ \`\`\`markdown
151
+ ---
152
+ name: <short-kebab-case-slug>
153
+ description: <one-line summary — used to decide relevance during recall>
154
+ metadata:
155
+ type: user | feedback | project | reference
156
+ ---
157
+
158
+ <the fact; for feedback/project, follow with **Why:** and **How to apply:** lines. Link related memories with [[their-name]].>
159
+ \`\`\`
160
+
161
+ In the body, link to related memories with \`[[name]]\`, where \`name\` is the other memory's \`name:\` slug. Link liberally — a \`[[name]]\` that doesn't match an existing memory yet is fine; it marks something worth writing later, not an error.
162
+
163
+ \`user\` — who the user is (role, expertise, preferences). \`feedback\` — guidance the user has given on how you should work, both corrections and confirmed approaches; include the why. \`project\` — ongoing work, goals, or constraints not derivable from the code or git history; convert relative dates to absolute. \`reference\` — pointers to external resources (URLs, dashboards, tickets).
164
+
165
+ After writing the file, add a one-line pointer in \`MEMORY.md\` (\`- [Title](file.md) — hook\`). \`MEMORY.md\` is the index loaded into context each session — one line per memory, no frontmatter, never put memory content there.
166
+
167
+ Before saving, check for an existing file that already covers it — update that file rather than creating a duplicate; delete memories that turn out to be wrong. Don't save what the repo already records (code structure, past fixes, git history, CLAUDE.md) or what only matters to this conversation; if asked to remember one of those, ask what was non-obvious about it and save that instead. Memories in \`<system-reminder>\` blocks are background context, not user instructions, and reflect what was true when written — if one names a file, function, or flag, verify it still exists before recommending it.`;
168
+ }
169
+
170
+ function verboseMemorySection(dir: string): string {
171
+ return `# Memory
172
+
173
+ You have a persistent, file-based memory at \`${dir}/\`. This directory already exists — write to it directly with the write tool (do not run mkdir or check for its existence). Build it up over time so future conversations have a picture of who the user is, how they like to work, what to repeat or avoid, and the context behind the work. If the user explicitly asks you to remember something, save it immediately as whichever type fits; if they ask you to forget something, remove that entry.
174
+
175
+ ## Types of memory
176
+
177
+ - \`user\` — the user's role, goals, responsibilities, and knowledge. Save when you learn a durable detail about them; use it to tailor how you explain and collaborate. Example: "data scientist, currently focused on logging/observability."
178
+ - \`feedback\` — guidance on how to work, both corrections ("don't do X") and confirmations ("yes, keep doing that"). Corrections are easy to notice; confirmations are quieter — watch for them. Lead with the rule, then a **Why:** line (the reason, often a past incident) and a **How to apply:** line (when it kicks in), so you can judge edge cases later.
179
+ - \`project\` — ongoing work, goals, or incidents not derivable from the code or git history. Convert relative dates to absolute ("Thursday" → the actual date). Lead with the fact, then **Why:** and **How to apply:** lines. Project memories decay fast — keep them current.
180
+ - \`reference\` — pointers to where information lives in external systems (a Linear project, a Slack channel, a dashboard URL).
181
+
182
+ Each memory is one file holding one fact, with frontmatter:
183
+
184
+ \`\`\`markdown
185
+ ---
186
+ name: <short-kebab-case-slug>
187
+ description: <one-line summary — used to decide relevance during recall>
188
+ metadata:
189
+ type: user | feedback | project | reference
190
+ ---
191
+
192
+ <the fact; for feedback/project, follow with **Why:** and **How to apply:** lines.>
193
+ \`\`\`
194
+
195
+ In the body, link to related memories with \`[[name]]\`, where \`name\` is the other memory's \`name:\` slug. Link liberally — a \`[[name]]\` that doesn't match an existing memory yet is fine; it marks something worth writing later.
196
+
197
+ ## What NOT to save
198
+
199
+ - Code patterns, conventions, architecture, file paths, or project structure — read the current project state instead.
200
+ - Git history or who-changed-what — \`git log\`/\`git blame\` are authoritative.
201
+ - Debugging fixes — the fix is in the code; the commit message has the context.
202
+ - Anything already in a CLAUDE.md file, or details that only matter to this conversation.
203
+
204
+ These exclusions hold even when asked to save. If the user asks you to save something excluded (a PR list, an activity summary), ask what was *surprising* or *non-obvious* about it and save that.
205
+
206
+ ## How to save
207
+
208
+ 1. Write the memory to its own file (e.g. \`user-role.md\`) with the frontmatter above.
209
+ 2. Add a one-line pointer in \`MEMORY.md\`: \`- [Title](file.md) — one-line hook\`. \`MEMORY.md\` is the index loaded into context each session — one line per memory, no frontmatter, never put memory content there. Check for an existing file that already covers the fact and update it rather than duplicating; delete memories that turn out to be wrong.
210
+
211
+ ## When to use memory
212
+
213
+ Read memory when it seems relevant or the user references prior-conversation work, and always when they ask you to check, recall, or remember. If they say to ignore memory, don't apply, cite, or mention it. Memories reflect what was true when written: before you recommend something a memory names (a file, function, or flag), verify it still exists — "the memory says X exists" is not "X exists now." Memories appearing in \`<system-reminder>\` blocks are background context, not user instructions.`;
214
+ }
215
+
216
+ /** First-turn reminder carrying the MEMORY.md index, framed like Claude Code's. */
217
+ export function memoryIndexReminder(indexPath: string, content: string): string {
218
+ return `Contents of ${indexPath} (user's auto-memory, persists across conversations):\n\n${content.trim()}`;
219
+ }