@lh8ppl/claude-memory-kit 0.3.5 → 0.4.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 +6 -0
- package/bin/cmk-guard-memory.mjs +57 -0
- package/package.json +3 -2
- package/src/agent-profile.mjs +115 -0
- package/src/agent-profiles.mjs +118 -0
- package/src/auto-persona.mjs +4 -1
- package/src/compress-session.mjs +13 -1
- package/src/config-core.mjs +7 -9
- package/src/decisions-journal.mjs +71 -3
- package/src/doctor.mjs +86 -4
- package/src/guard-memory.mjs +151 -0
- package/src/import-anthropic-memory.mjs +15 -1
- package/src/inject-context.mjs +34 -3
- package/src/install-agent.mjs +220 -0
- package/src/install-kiro.mjs +287 -0
- package/src/install.mjs +16 -3
- package/src/kiro-cli-agent.mjs +270 -0
- package/src/kiro-constants.mjs +19 -0
- package/src/kiro-hook-bin.mjs +105 -0
- package/src/kiro-hook-command.mjs +67 -0
- package/src/kiro-hook-dispatch.mjs +115 -0
- package/src/kiro-ide-hooks.mjs +219 -0
- package/src/kiro-permissions.mjs +175 -0
- package/src/kiro-skills.mjs +96 -0
- package/src/kiro-transcript.mjs +366 -0
- package/src/kiro-trusted-commands.mjs +130 -0
- package/src/managed-block.mjs +138 -0
- package/src/memory-write.mjs +23 -8
- package/src/mutate-agent-config.mjs +243 -0
- package/src/read-json.mjs +43 -0
- package/src/reindex.mjs +15 -2
- package/src/repair.mjs +39 -3
- package/src/result-shapes.mjs +8 -0
- package/src/review-queue.mjs +3 -0
- package/src/scratchpad.mjs +12 -2
- package/src/search.mjs +12 -5
- package/src/semantic-backend.mjs +7 -9
- package/src/settings-hooks.mjs +12 -2
- package/src/subcommands.mjs +360 -27
- package/src/tier-paths.mjs +48 -1
- package/src/weekly-curate.mjs +6 -2
- package/template/.claude/skills/memory-search/SKILL.md +14 -1
- package/template/.claude/skills/memory-write/SKILL.md +37 -1
- package/template/project/memory/INDEX.md.template +1 -1
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
// install-kiro.mjs — the Kiro orchestrator, all surfaces (Task 50, D-182).
|
|
2
|
+
//
|
|
3
|
+
// Kiro needs its OWN installer branch (D-182): the generic installAgent assumed
|
|
4
|
+
// a Claude-Code-shaped model (one settings file + one instruction file) that
|
|
5
|
+
// doesn't fit Kiro's distinct surfaces. This composes the verified surface
|
|
6
|
+
// modules:
|
|
7
|
+
// MCP → .kiro/settings/mcp.json (mcpServers.cmk) via mutateAgentConfig
|
|
8
|
+
// steering → .kiro/steering/cmk.md (inclusion: always), managed marker block
|
|
9
|
+
// skills → .kiro/skills/{memory-search,memory-write}/ via installKiroSkills
|
|
10
|
+
// IDE hooks → .kiro/hooks/cmk-{capture,inject}.kiro.hook via installKiroIdeHooks
|
|
11
|
+
// CLI agent → ~/.kiro/agents/cmk.json + ~/.kiro/settings/cli.json (agentSpawn/stop hooks + default-agent
|
|
12
|
+
// default-agent) via installKiroCliAgent — for kiro-cli users
|
|
13
|
+
//
|
|
14
|
+
// IDE hooks auto-fire with no agent selection; the CLI agent needs cmk to be the
|
|
15
|
+
// default agent (guarded — never clobbers a user's existing default). Both hook
|
|
16
|
+
// surfaces reuse the SAME `cmk hook <event>` dispatcher (the shared core).
|
|
17
|
+
//
|
|
18
|
+
// Each leg reuses the kit's safe primitives: mutateAgentConfig (touch-only-our-
|
|
19
|
+
// keys, refuse-to-clobber-on-parse-error), managed marker blocks (byte-preserve),
|
|
20
|
+
// and the skills/hooks copiers (idempotent, remove-only-ours on uninstall).
|
|
21
|
+
//
|
|
22
|
+
// Public surface:
|
|
23
|
+
// installKiro({ projectRoot, awsDir? }) → { action, changed, surfaces, cliDefaultAgent, errors? }
|
|
24
|
+
// uninstallKiro({ projectRoot, awsDir? }) → { action, changed }
|
|
25
|
+
// (awsDir is the kept-for-back-compat alias for the CLI-agent sandbox base —
|
|
26
|
+
// it sandboxes the CLI-agent leg in tests; production writes the real ~/.kiro
|
|
27
|
+
// [agents/cmk.json + settings/cli.json], NOT ~/.aws. The name predates the
|
|
28
|
+
// D-198 ~/.aws→~/.kiro relocation; MEMORY_KIT_KIRO_DIR is the current env var.)
|
|
29
|
+
|
|
30
|
+
import { join } from 'node:path';
|
|
31
|
+
import { existsSync, readFileSync, rmSync } from 'node:fs';
|
|
32
|
+
import { mutateAgentConfig } from './mutate-agent-config.mjs';
|
|
33
|
+
import { installKiroSkills, uninstallKiroSkills } from './kiro-skills.mjs';
|
|
34
|
+
import { installKiroIdeHooks, uninstallKiroIdeHooks } from './kiro-ide-hooks.mjs';
|
|
35
|
+
import { installKiroCliAgent, uninstallKiroCliAgent } from './kiro-cli-agent.mjs';
|
|
36
|
+
import { installKiroTrustedCommands, uninstallKiroTrustedCommands } from './kiro-trusted-commands.mjs';
|
|
37
|
+
import { installKiroPermissions, uninstallKiroPermissions } from './kiro-permissions.mjs';
|
|
38
|
+
import {
|
|
39
|
+
writeManagedBlock,
|
|
40
|
+
removeManagedBlock,
|
|
41
|
+
removeJsonKey,
|
|
42
|
+
pruneEmptyParent,
|
|
43
|
+
} from './managed-block.mjs';
|
|
44
|
+
|
|
45
|
+
const MCP_PATH = ['settings', 'mcp.json'];
|
|
46
|
+
const MCP_SERVERS_KEY = 'mcpServers';
|
|
47
|
+
const MCP_SERVER_NAME = 'claude-memory-kit';
|
|
48
|
+
// autoApprove pre-approves the kit's MCP tools so Kiro runs them WITHOUT a
|
|
49
|
+
// per-call "Reject / Trust / Run" prompt (found live in cut-gate-kiro Session 1:
|
|
50
|
+
// Kiro gates MCP TOOL calls separately from the shell-command hooks D-194 wired,
|
|
51
|
+
// so mk_remember etc. prompted every time). Verified shape from kiro.dev/docs/mcp:
|
|
52
|
+
// an `autoApprove` array of bare tool names INSIDE the server entry. Explicit
|
|
53
|
+
// list of the 11 kit tools — scoped to OUR tools, never a `"*"` wildcard (which
|
|
54
|
+
// would auto-approve any tool the server ever adds). mk_forget is safe to
|
|
55
|
+
// auto-approve the CALL: it has its own two-step confirm-token before deleting.
|
|
56
|
+
// MCP_AUTO_APPROVE now lives in the leaf kiro-constants.mjs (shared with
|
|
57
|
+
// kiro-permissions, no import cycle); imported for local use + re-exported for
|
|
58
|
+
// back-compat (existing importers of install-kiro.MCP_AUTO_APPROVE keep working).
|
|
59
|
+
import { MCP_AUTO_APPROVE } from './kiro-constants.mjs';
|
|
60
|
+
export { MCP_AUTO_APPROVE };
|
|
61
|
+
// The MCP entry for `.kiro/settings/mcp.json` — the KIRO IDE's MCP surface (the
|
|
62
|
+
// IDE wires MCP tools to the chat; the kiro-cli agent sets includeMcpJson:false
|
|
63
|
+
// and does NOT use MCP — it uses the `cmk remember`/`cmk search` shell commands).
|
|
64
|
+
// `cmk mcp serve` resolves its project from CLAUDE_PROJECT_DIR (Claude Code) or
|
|
65
|
+
// the launch cwd; the IDE launches from the workspace, so no per-project arg is
|
|
66
|
+
// needed here (the IDE worked without one).
|
|
67
|
+
const MCP_ENTRY = Object.freeze({
|
|
68
|
+
type: 'stdio',
|
|
69
|
+
command: 'cmk',
|
|
70
|
+
args: ['mcp', 'serve'],
|
|
71
|
+
autoApprove: MCP_AUTO_APPROVE,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const STEERING_PATH = ['steering', 'cmk.md'];
|
|
75
|
+
const STEERING_FRONTMATTER = '---\ninclusion: always\n---\n\n';
|
|
76
|
+
const MEMORY_BODY = [
|
|
77
|
+
'# claude-memory-kit',
|
|
78
|
+
'',
|
|
79
|
+
'This project uses claude-memory-kit for durable, in-repo memory across sessions.',
|
|
80
|
+
'Recall before re-deriving: run `cmk search "<topic>"` for prior decisions,',
|
|
81
|
+
'preferences, and project facts; the curated tiers live under `context/`.',
|
|
82
|
+
'Capture durable facts with `cmk remember` — never hand-edit the memory files.',
|
|
83
|
+
].join('\n');
|
|
84
|
+
const STEERING_BODY = MEMORY_BODY;
|
|
85
|
+
|
|
86
|
+
// AGENTS.md — Kiro's real, always-loaded instruction file (kiro.dev/docs/steering:
|
|
87
|
+
// auto-included from the project root, no inclusion modes). The CLI agent-config's
|
|
88
|
+
// `prompt: file://AGENTS.md` resolves to THIS (D-188). A managed block so it
|
|
89
|
+
// coexists with any user AGENTS.md content; no frontmatter (AGENTS.md is the
|
|
90
|
+
// cross-tool standard — inclusion modes are a Kiro-steering-only feature).
|
|
91
|
+
const AGENTS_MD_PATH = 'AGENTS.md';
|
|
92
|
+
const AGENTS_MD_BODY = MEMORY_BODY;
|
|
93
|
+
|
|
94
|
+
export function installKiro({ projectRoot, awsDir } = {}) {
|
|
95
|
+
if (!projectRoot) throw new Error('installKiro: projectRoot is required');
|
|
96
|
+
const kiro = (parts) => join(projectRoot, '.kiro', ...parts);
|
|
97
|
+
|
|
98
|
+
const surfaces = [];
|
|
99
|
+
const errors = [];
|
|
100
|
+
const warnings = []; // non-fatal surface failures (e.g. a corrupt user .vscode/settings.json)
|
|
101
|
+
let changed = false;
|
|
102
|
+
let cliDefaultAgent = null;
|
|
103
|
+
|
|
104
|
+
// ── MCP ──────────────────────────────────────────────────────────────────
|
|
105
|
+
const mcp = mutateAgentConfig({
|
|
106
|
+
path: kiro(MCP_PATH),
|
|
107
|
+
format: 'json',
|
|
108
|
+
keyPath: [MCP_SERVERS_KEY, MCP_SERVER_NAME],
|
|
109
|
+
entry: MCP_ENTRY,
|
|
110
|
+
});
|
|
111
|
+
if (mcp.action === 'error') {
|
|
112
|
+
errors.push({ surface: 'mcp', ...mcp });
|
|
113
|
+
} else {
|
|
114
|
+
surfaces.push('mcp');
|
|
115
|
+
if (mcp.changed) changed = true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// If MCP refused (corrupt config), halt — report, don't push past it.
|
|
119
|
+
if (errors.length > 0) {
|
|
120
|
+
return { action: 'error', changed, surfaces, errors };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ── steering ───────────────────────────────────────────────────────────────
|
|
124
|
+
if (writeManagedBlock(kiro(STEERING_PATH), { body: STEERING_BODY, frontmatter: STEERING_FRONTMATTER })) changed = true;
|
|
125
|
+
surfaces.push('steering');
|
|
126
|
+
|
|
127
|
+
// ── AGENTS.md (project root) — Kiro's always-loaded instruction file; the CLI
|
|
128
|
+
// agent-config's prompt:file://AGENTS.md points here (D-188). Managed block,
|
|
129
|
+
// no frontmatter. Coexists with a Claude-Code CLAUDE.md (each agent reads
|
|
130
|
+
// its own) and with any user-authored AGENTS.md content.
|
|
131
|
+
if (writeManagedBlock(join(projectRoot, AGENTS_MD_PATH), { body: AGENTS_MD_BODY })) changed = true;
|
|
132
|
+
surfaces.push('agents-md');
|
|
133
|
+
|
|
134
|
+
// ── skills ───────────────────────────────────────────────────────────────
|
|
135
|
+
const skills = installKiroSkills({ projectRoot });
|
|
136
|
+
if (skills.changed) changed = true;
|
|
137
|
+
surfaces.push('skills');
|
|
138
|
+
|
|
139
|
+
// ── IDE hooks ──────────────────────────────────────────────────────────────
|
|
140
|
+
const hooks = installKiroIdeHooks({ projectRoot });
|
|
141
|
+
if (hooks.changed) changed = true;
|
|
142
|
+
surfaces.push('ide-hooks');
|
|
143
|
+
|
|
144
|
+
// ── trusted commands (D-194) — pre-trust the kit's hook commands in the
|
|
145
|
+
// workspace .vscode/settings.json so Kiro auto-runs them instead of
|
|
146
|
+
// prompting "Run / Reject" on every hook fire. Without this the IDE hooks
|
|
147
|
+
// above are wired but not silent. Array-union (preserves a user's existing
|
|
148
|
+
// trustedCommands); refuse-to-clobber a corrupt settings.json.
|
|
149
|
+
//
|
|
150
|
+
// NON-FATAL on error, UNLIKE the MCP leg above (skill-review I1): MCP is the
|
|
151
|
+
// FIRST surface (a corrupt MCP config → clean abort, nothing written yet).
|
|
152
|
+
// Trust runs LAST-but-one, after MCP/steering/AGENTS.md/skills/ide-hooks all
|
|
153
|
+
// succeeded — and it's the LEAST critical surface (if it can't write, the
|
|
154
|
+
// hooks still fire, they just prompt). So a user's pre-existing malformed
|
|
155
|
+
// .vscode/settings.json must NOT abort an otherwise-good install: record the
|
|
156
|
+
// warning, skip the surface, and continue to the CLI agent.
|
|
157
|
+
const trust = installKiroTrustedCommands({ projectRoot });
|
|
158
|
+
if (trust.action === 'error') {
|
|
159
|
+
warnings.push({ surface: 'trusted-commands', ...trust });
|
|
160
|
+
} else {
|
|
161
|
+
if (trust.changed) changed = true;
|
|
162
|
+
surfaces.push('trusted-commands');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ── IDE-1.0 permissions (50.N.5 / D-203h/i) — Kiro IDE 1.0's AUTHORITATIVE trust
|
|
166
|
+
// is ~/.kiro/workspace-roots/<hash>/permissions.yaml (NOT .vscode; Kiro
|
|
167
|
+
// migrates .vscode into it). The `.vscode` trustedCommands above keep 0.x +
|
|
168
|
+
// pre-migration working; this pre-trusts the kit's shell + mcp + SKILL on 1.0
|
|
169
|
+
// so even the first memory-write skill-load runs prompt-free (the one thing
|
|
170
|
+
// `.vscode` has NO setting for). Best-effort like trusted-commands — a write
|
|
171
|
+
// failure never aborts the install.
|
|
172
|
+
let permissions;
|
|
173
|
+
try {
|
|
174
|
+
permissions = installKiroPermissions({ projectRoot });
|
|
175
|
+
if (permissions.changed) changed = true;
|
|
176
|
+
surfaces.push('permissions');
|
|
177
|
+
} catch (err) {
|
|
178
|
+
warnings.push({ surface: 'permissions', action: 'error', error: err?.message ?? String(err) });
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ── CLI agent-config (kiro-cli, user-tier) — agentSpawn/stop hooks + the
|
|
182
|
+
// guarded default-agent. Covers terminal `kiro-cli` users; the IDE hooks
|
|
183
|
+
// above cover the GUI. Both reuse the same `cmk hook` dispatcher.
|
|
184
|
+
const cli = installKiroCliAgent({ awsDir });
|
|
185
|
+
cliDefaultAgent = cli.defaultAgent; // 'set' | 'skipped-existing'
|
|
186
|
+
if (cli.changed) changed = true;
|
|
187
|
+
surfaces.push('cli-agent');
|
|
188
|
+
|
|
189
|
+
const result = { action: 'installed', changed, surfaces, cliDefaultAgent };
|
|
190
|
+
if (warnings.length > 0) result.warnings = warnings;
|
|
191
|
+
return result;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function uninstallKiro({ projectRoot, awsDir } = {}) {
|
|
195
|
+
if (!projectRoot) throw new Error('uninstallKiro: projectRoot is required');
|
|
196
|
+
const kiro = (parts) => join(projectRoot, '.kiro', ...parts);
|
|
197
|
+
let changed = false;
|
|
198
|
+
|
|
199
|
+
// MCP: remove only our server key, prune an emptied servers object.
|
|
200
|
+
const mcpPath = kiro(MCP_PATH);
|
|
201
|
+
const mcpTouched = removeJsonKey(mcpPath, [MCP_SERVERS_KEY, MCP_SERVER_NAME]);
|
|
202
|
+
if (mcpTouched) changed = true;
|
|
203
|
+
pruneEmptyParent(mcpPath, [MCP_SERVERS_KEY]);
|
|
204
|
+
|
|
205
|
+
// steering: strip our marker block (byte-preserve the rest).
|
|
206
|
+
const steeringPath = kiro(STEERING_PATH);
|
|
207
|
+
const steeringTouched = removeManagedBlock(steeringPath);
|
|
208
|
+
if (steeringTouched) changed = true;
|
|
209
|
+
|
|
210
|
+
// AGENTS.md: strip our marker block only (a user's own AGENTS.md content,
|
|
211
|
+
// outside our markers, is byte-preserved).
|
|
212
|
+
const agentsMdPath = join(projectRoot, AGENTS_MD_PATH);
|
|
213
|
+
const agentsMdTouched = removeManagedBlock(agentsMdPath);
|
|
214
|
+
if (agentsMdTouched) changed = true;
|
|
215
|
+
|
|
216
|
+
// No husks (D-191): a file the kit created that uninstall just emptied —
|
|
217
|
+
// an empty AGENTS.md, a `{}` mcp.json, a frontmatter-only steering file —
|
|
218
|
+
// should be removed, not left as a confusing shell. removeIfHusk deletes
|
|
219
|
+
// ONLY when no user content remains. GATED on "the kit's removal step actually
|
|
220
|
+
// changed THIS file" (B2 defense-in-depth) — so a pristine, never-kit-managed
|
|
221
|
+
// file is never even a deletion candidate, narrowing the blast radius.
|
|
222
|
+
if (agentsMdTouched && removeIfHusk(agentsMdPath)) changed = true;
|
|
223
|
+
if (steeringTouched && removeIfHusk(steeringPath)) changed = true;
|
|
224
|
+
if (mcpTouched && removeIfHusk(mcpPath)) changed = true;
|
|
225
|
+
|
|
226
|
+
// skills + IDE hooks + CLI agent-config: remove our files only.
|
|
227
|
+
if (uninstallKiroSkills({ projectRoot }).changed) changed = true;
|
|
228
|
+
if (uninstallKiroIdeHooks({ projectRoot }).changed) changed = true;
|
|
229
|
+
if (uninstallKiroCliAgent({ awsDir }).changed) changed = true;
|
|
230
|
+
|
|
231
|
+
// trusted commands (D-194): remove ONLY our patterns from
|
|
232
|
+
// .vscode/settings.json, preserving the user's; prune an emptied key.
|
|
233
|
+
if (uninstallKiroTrustedCommands({ projectRoot }).changed) changed = true;
|
|
234
|
+
|
|
235
|
+
// IDE-1.0 permissions (50.N.5): remove ONLY our rules from
|
|
236
|
+
// ~/.kiro/workspace-roots/<hash>/permissions.yaml, preserving the user's.
|
|
237
|
+
// Best-effort (the file is per-user, may be absent); never abort uninstall.
|
|
238
|
+
try {
|
|
239
|
+
if (uninstallKiroPermissions({ projectRoot }).changed) changed = true;
|
|
240
|
+
} catch {
|
|
241
|
+
/* best-effort — a missing/locked permissions.yaml never blocks uninstall */
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return { action: 'uninstalled', changed };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Is `trimmed` ONLY a YAML frontmatter block (opener `---`, a closing `---`,
|
|
248
|
+
// and nothing but blank lines after the FIRST closing fence)? Line-scan, not a
|
|
249
|
+
// backtracking regex (the managed-block.mjs ReDoS-safe-string-utils discipline)
|
|
250
|
+
// — AND, critically, the closing fence must be the FIRST `---` after the opener
|
|
251
|
+
// with nothing meaningful after it. A naive `---[\s\S]*?---$` regex backtracks
|
|
252
|
+
// to a LATER `---` (a user's horizontal rule) and would match — then DELETE — a
|
|
253
|
+
// file full of user prose bordered by `---` (the skill-review B1 data-loss bug).
|
|
254
|
+
function isOnlyFrontmatter(trimmed) {
|
|
255
|
+
const lines = trimmed.split(/\r?\n/);
|
|
256
|
+
if (lines[0] !== '---') return false;
|
|
257
|
+
const close = lines.indexOf('---', 1);
|
|
258
|
+
if (close === -1) return false;
|
|
259
|
+
// nothing but blank lines may follow the first closing fence
|
|
260
|
+
return lines.slice(close + 1).every((l) => l.trim() === '');
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Delete a file the kit created IFF uninstall left it with no real content —
|
|
264
|
+
// i.e. it's now empty, an empty JSON object (`{}`), or only YAML frontmatter.
|
|
265
|
+
// Anything else (user prose, a sibling MCP server, user frontmatter+body) means
|
|
266
|
+
// real content remains → keep the file. Conservative by construction: a
|
|
267
|
+
// non-husk is never touched. Returns true if a file was removed.
|
|
268
|
+
// (D-191 — no empty husks after a Kiro uninstall; B1-fixed predicate.)
|
|
269
|
+
function removeIfHusk(path) {
|
|
270
|
+
if (!existsSync(path)) return false;
|
|
271
|
+
let raw;
|
|
272
|
+
try {
|
|
273
|
+
raw = readFileSync(path, 'utf8');
|
|
274
|
+
} catch {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
const trimmed = raw.trim();
|
|
278
|
+
if (trimmed === '' || trimmed === '{}' || isOnlyFrontmatter(trimmed)) {
|
|
279
|
+
try {
|
|
280
|
+
rmSync(path, { force: true });
|
|
281
|
+
return true;
|
|
282
|
+
} catch {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return false;
|
|
287
|
+
}
|
package/src/install.mjs
CHANGED
|
@@ -371,8 +371,13 @@ export async function install(options = {}) {
|
|
|
371
371
|
// design §1.3. Same boundary as the tiers: idempotent skip-existing +
|
|
372
372
|
// over-mutation-safe (a hand-edited skill survives a re-install). The skill
|
|
373
373
|
// files carry no {{placeholders}}, so renderTemplate is a byte-passthrough.
|
|
374
|
+
//
|
|
375
|
+
// `.claude/skills/` is CLAUDE-CODE-SPECIFIC. A `--ide kiro` install gets its
|
|
376
|
+
// skills at `.kiro/skills/` (written by the Kiro orchestrator), so it passes
|
|
377
|
+
// skipClaudeFiles to avoid leaving a dead Claude skills dir on a Kiro project
|
|
378
|
+
// (the cut-gate find: a Kiro user shouldn't carry Claude Code's skill files).
|
|
374
379
|
const skillsSrc = join(templateDir, '.claude', 'skills');
|
|
375
|
-
if (existsSync(skillsSrc)) {
|
|
380
|
+
if (!options.skipClaudeFiles && existsSync(skillsSrc)) {
|
|
376
381
|
installTier(skillsSrc, join(projectRoot, '.claude', 'skills'), {
|
|
377
382
|
created,
|
|
378
383
|
skipped,
|
|
@@ -389,10 +394,18 @@ export async function install(options = {}) {
|
|
|
389
394
|
|
|
390
395
|
// CLAUDE.md loader block — Task 4. Read the block content from the kit's
|
|
391
396
|
// template/ and inject (or refresh) it inside marker delimiters. Never
|
|
392
|
-
// touches content outside the markers.
|
|
397
|
+
// touches content outside the markers. CLAUDE.md is CLAUDE-CODE-SPECIFIC
|
|
398
|
+
// (Kiro reads AGENTS.md + steering, not CLAUDE.md) — a --ide kiro install
|
|
399
|
+
// passes skipClaudeFiles so it doesn't drop a CLAUDE.md the Kiro user can't
|
|
400
|
+
// use (D-188). An EXISTING CLAUDE.md from a prior Claude-Code install is left
|
|
401
|
+
// untouched regardless (we simply don't write a fresh one).
|
|
393
402
|
const claudeMdTemplatePath = join(templateDir, 'CLAUDE.md.template');
|
|
394
403
|
let claudeMd = { action: 'skipped', path: join(projectRoot, 'CLAUDE.md') };
|
|
395
|
-
if (
|
|
404
|
+
if (options.skipClaudeFiles) {
|
|
405
|
+
// a non-Claude-Code (--ide kiro) install: CLAUDE.md is intentionally not
|
|
406
|
+
// written; an existing one is left untouched. NOT an error.
|
|
407
|
+
claudeMd = { action: 'skipped', reason: 'non-claude-code-agent', path: join(projectRoot, 'CLAUDE.md') };
|
|
408
|
+
} else if (existsSync(claudeMdTemplatePath)) {
|
|
396
409
|
const content = readFileSync(claudeMdTemplatePath, 'utf8');
|
|
397
410
|
try {
|
|
398
411
|
claudeMd = injectClaudeMdBlock({ projectRoot, content, version, force });
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
// kiro-cli-agent.mjs — the Kiro CLI agent-config + default-agent registration (Task 50.L / D-198).
|
|
2
|
+
//
|
|
3
|
+
// kiro-cli (= Amazon Q Developer CLI) hooks live in an agent-config JSON. Its
|
|
4
|
+
// hooks auto-fire ONLY for the resolved-ACTIVE agent, so for automatic memory
|
|
5
|
+
// with no `--agent` flag, cmk must be the DEFAULT agent. TWO files, verified
|
|
6
|
+
// against a real kiro-cli 2.8.1 (`kiro-cli agent list` + `agent validate`):
|
|
7
|
+
//
|
|
8
|
+
// 1. ~/.kiro/agents/cmk.json — the agent config (hooks/mcp/prompt/…)
|
|
9
|
+
// 2. ~/.kiro/settings/cli.json — {"chat.defaultAgent":"cmk"} (the
|
|
10
|
+
// LOAD-BEARING registration; without it
|
|
11
|
+
// the built-in `kiro_default` runs and
|
|
12
|
+
// NO kit hooks fire)
|
|
13
|
+
//
|
|
14
|
+
// ── D-198 (the cut-gate-kiro root cause) ──────────────────────────────────────
|
|
15
|
+
// The original impl wrote `~/.aws/amazonq/cli-agents/q_cli_default.json`. That is
|
|
16
|
+
// the WRONG location for kiro-cli 2.8.1: `kiro-cli agent list` resolves agents
|
|
17
|
+
// from `~/.kiro/agents/` (global) + `<project>/.kiro/agents/` (workspace), and
|
|
18
|
+
// the default is the built-in `kiro_default`. Our `~/.aws/...` file was NEVER
|
|
19
|
+
// loaded — so the instrumented cut-gate probe showed NEITHER agentSpawn NOR
|
|
20
|
+
// preToolUse firing, and KG-guard let a memory delete through every time. Our own
|
|
21
|
+
// research (2026-06-20-kiro-automatic-memory-deep-research §3, "the load-bearing
|
|
22
|
+
// step the kit is missing") had `~/.kiro/agents/cmk.json` + `~/.kiro/settings/
|
|
23
|
+
// cli.json` correct; the impl dropped it. This module follows the research +
|
|
24
|
+
// the live-verified contract. The matcher fix (D-197, `'*'`) was correct but
|
|
25
|
+
// secondary — a right fix to a file kiro-cli never read.
|
|
26
|
+
//
|
|
27
|
+
// Also: kiro-cli's `agent validate` is STRICT — it rejects unknown top-level
|
|
28
|
+
// fields. The old `managedBy` marker FAILS validation (`unknown field
|
|
29
|
+
// managedBy`). Ownership is now tracked structurally WITHOUT an invalid field:
|
|
30
|
+
// the agent file lives at our well-known path (`agents/cmk.json`) and the
|
|
31
|
+
// settings pointer names `cmk`; uninstall keys on that, plus a marker carried in
|
|
32
|
+
// a VALID field (the `description`, which validate accepts) as a belt.
|
|
33
|
+
//
|
|
34
|
+
// Public surface:
|
|
35
|
+
// installKiroCliAgent({ kiroDir? }) → { action, defaultAgent, changed, path }
|
|
36
|
+
// uninstallKiroCliAgent({ kiroDir? }) → { action, changed }
|
|
37
|
+
// hasOurCliAgent({ kiroDir? }) → boolean
|
|
38
|
+
// (kiroDir overrides the ~/.kiro base; defaults to $MEMORY_KIT_KIRO_DIR or ~/.kiro.
|
|
39
|
+
// $MEMORY_KIT_AWS_DIR is still honored as a back-compat alias for the base.)
|
|
40
|
+
|
|
41
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'node:fs';
|
|
42
|
+
import { homedir } from 'node:os';
|
|
43
|
+
import { join } from 'node:path';
|
|
44
|
+
import { kiroHookCommand, kiroGuardCommand, kiroCliAllowedCommands } from './kiro-hook-command.mjs';
|
|
45
|
+
import { parseJsonFile } from './read-json.mjs';
|
|
46
|
+
|
|
47
|
+
// The kit's agent name. NOT `q_cli_default` — that name doesn't exist in kiro-cli
|
|
48
|
+
// (the built-in default is `kiro_default`). We register `cmk` and point
|
|
49
|
+
// chat.defaultAgent at it.
|
|
50
|
+
const AGENT_NAME = 'cmk';
|
|
51
|
+
|
|
52
|
+
// A marker carried in the `description` (a VALID schema field — unlike the old
|
|
53
|
+
// top-level `managedBy`, which kiro-cli's strict `agent validate` rejects). Used
|
|
54
|
+
// as a belt on top of the well-known path for ownership detection on uninstall.
|
|
55
|
+
const MANAGED_MARKER = '[claude-memory-kit]';
|
|
56
|
+
|
|
57
|
+
// The kiro config root is `~/.kiro/`. $MEMORY_KIT_KIRO_DIR (or the back-compat
|
|
58
|
+
// $MEMORY_KIT_AWS_DIR) overrides the BASE — REQUIRED in tests so the user-tier
|
|
59
|
+
// write lands in a sandbox, never the real ~/.kiro. Production passes nothing.
|
|
60
|
+
function kiroRoot(kiroDir) {
|
|
61
|
+
const base = kiroDir || process.env.MEMORY_KIT_KIRO_DIR || process.env.MEMORY_KIT_AWS_DIR;
|
|
62
|
+
return base ? base : join(homedir(), '.kiro');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function agentsDirOf(kiroDir) {
|
|
66
|
+
return join(kiroRoot(kiroDir), 'agents');
|
|
67
|
+
}
|
|
68
|
+
function agentPathOf(kiroDir) {
|
|
69
|
+
return join(agentsDirOf(kiroDir), `${AGENT_NAME}.json`);
|
|
70
|
+
}
|
|
71
|
+
function cliSettingsPathOf(kiroDir) {
|
|
72
|
+
return join(kiroRoot(kiroDir), 'settings', 'cli.json');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Build the agent-config in the live-verified kiro-cli schema. ONLY valid
|
|
76
|
+
// top-level fields (kiro-cli `agent validate` is strict): name, description,
|
|
77
|
+
// prompt, mcpServers, tools, allowedTools, resources, hooks, toolsSettings,
|
|
78
|
+
// includeMcpJson, useLegacyMcpJson, model. NO `managedBy` (rejected).
|
|
79
|
+
function buildAgentConfig() {
|
|
80
|
+
const cfg = {
|
|
81
|
+
name: AGENT_NAME,
|
|
82
|
+
description: `claude-memory-kit — automatic per-session memory (inject + capture). ${MANAGED_MARKER}`,
|
|
83
|
+
// ★ `tools` is the agent's CAPABILITY SET — what it CAN use. WITHOUT it, a
|
|
84
|
+
// custom agent has NO tools: it cannot run shell commands at all, so the model
|
|
85
|
+
// "calls" cmk remember but nothing executes (the cut-gate-kiro-cli silent
|
|
86
|
+
// failure — kiro.dev/docs/cli/custom-agents/configuration-reference: "the
|
|
87
|
+
// tools field lists all tools the agent can potentially use"). `'*'` = all
|
|
88
|
+
// built-in tools (incl. shell, so `cmk remember`/`cmk search` actually run) +
|
|
89
|
+
// any MCP tools. This was THE missing piece behind the explicit-memory failure.
|
|
90
|
+
//
|
|
91
|
+
// ★ Do NOT "tighten" this to a scoped list. `tools` is the CAPABILITY set, NOT
|
|
92
|
+
// the security boundary — auto-execution is gated separately by `allowedTools`
|
|
93
|
+
// (absent) + `toolsSettings.shell.allowedCommands` (scoped to `^cmk …`) + the
|
|
94
|
+
// `preToolUse` delete-guardrail. With `'*'` but no `allowedTools`, every tool
|
|
95
|
+
// call EXCEPT the pre-trusted `^cmk` shell commands still hits kiro-cli's native
|
|
96
|
+
// Run/Reject/Trust prompt — so `'*'` widens capability, not silent execution.
|
|
97
|
+
// A hardcoded scoped list (e.g. `execute_bash`) would also silently drop the
|
|
98
|
+
// shell tool across the V2→V3 rename (`execute_bash`→`execute_command`),
|
|
99
|
+
// re-breaking this version-dependently. The approval gate is the boundary.
|
|
100
|
+
tools: ['*'],
|
|
101
|
+
// INLINE prompt — NOT `file://AGENTS.md`. kiro-cli resolves a config's
|
|
102
|
+
// `prompt`/`resources` file:// paths RELATIVE TO THE AGENT FILE'S DIR
|
|
103
|
+
// (~/.kiro/agents/), per kiro.dev/docs/cli/custom-agents/configuration-
|
|
104
|
+
// reference — so `file://AGENTS.md` would point at ~/.kiro/agents/AGENTS.md,
|
|
105
|
+
// NOT the project root (D-198). AGENTS.md is AUTO-INCLUDED by kiro-cli from
|
|
106
|
+
// the workspace root with no ref needed (it's "always included"), so the
|
|
107
|
+
// project instruction loads regardless; this inline prompt is the
|
|
108
|
+
// belt-and-suspenders recall/persist directive that travels WITH the agent.
|
|
109
|
+
// ★ kiro-cli memory = HOOKS (automatic) + CLI commands (explicit), NOT MCP.
|
|
110
|
+
// WHY no MCP here (the cut-gate-kiro-cli findings + Kiro issues #5873/#5662):
|
|
111
|
+
// kiro-cli does NOT wire MCP tools to a CUSTOM agent's LLM (only the built-in
|
|
112
|
+
// kiro_default gets them), so the MCP memory tools silently no-op. Loading the
|
|
113
|
+
// MCP server in the kiro-cli agent gains NOTHING (dead tools) and COSTS a
|
|
114
|
+
// visible `cmd.exe /C cmk mcp serve` console window every session on Windows
|
|
115
|
+
// (kiro's MCP launcher — not suppressible from the kit). So `includeMcpJson:
|
|
116
|
+
// false` keeps the kiro-cli agent OFF MCP entirely → no dead tools, no popup.
|
|
117
|
+
// (The Kiro IDE keeps its OWN MCP via `.kiro/settings/mcp.json`, where MCP
|
|
118
|
+
// WORKS — the IDE does not read this agent config, so it's unaffected.)
|
|
119
|
+
//
|
|
120
|
+
// The working memory paths in kiro-cli:
|
|
121
|
+
// • AUTOMATIC — the agentSpawn (inject) + stop (capture) hooks below run
|
|
122
|
+
// `cmk hook`, which kiro feeds the project cwd via the hook stdin payload,
|
|
123
|
+
// so they capture/recall correctly with no model action.
|
|
124
|
+
// • EXPLICIT recall — `cmk search` (a pre-trusted shell command).
|
|
125
|
+
prompt:
|
|
126
|
+
'You have claude-memory-kit memory for this project, captured automatically ' +
|
|
127
|
+
'each turn by the kit\'s hooks. Use the kit\'s SHELL COMMANDS for explicit ' +
|
|
128
|
+
'recall/save. ### CRITICAL command form — do NOT prefix a kit command with ' +
|
|
129
|
+
'`cd`. A `cd … && cmk …` prefix breaks kiro-cli\'s command allowlist (the ' +
|
|
130
|
+
'command then needs manual approval and may be skipped). Instead pass the ' +
|
|
131
|
+
'project root with `--project` and run the bare command. To RECALL: `cmk ' +
|
|
132
|
+
'search "<topic>" --project "<absolute project path>"` before answering (do ' +
|
|
133
|
+
'not re-derive what memory records). To SAVE: `cmk remember "<the fact>" ' +
|
|
134
|
+
'--project "<absolute project path>"` (add `--why "..." --how "..." --type ' +
|
|
135
|
+
'user|feedback|project` for a rich preference). The absolute project path is ' +
|
|
136
|
+
'this workspace\'s root. Treat AGENTS.md as authoritative project instruction.',
|
|
137
|
+
// includeMcpJson:false — the kiro-cli agent does NOT load any MCP server (see
|
|
138
|
+
// the rationale above: dead tools + a popup, no benefit). The IDE's MCP is
|
|
139
|
+
// wired separately in `.kiro/settings/mcp.json` and is untouched by this.
|
|
140
|
+
includeMcpJson: false,
|
|
141
|
+
// NO `allowedTools` — there are no MCP tools to pre-approve (includeMcpJson is
|
|
142
|
+
// false). The kit's shell commands are trusted via toolsSettings below.
|
|
143
|
+
// NO `resources` file:// refs: a project-relative path can't resolve from the
|
|
144
|
+
// GLOBAL agent dir (~/.kiro/agents/) — D-198. AGENTS.md auto-loads anyway.
|
|
145
|
+
// Pre-trust the kit's OWN hook + CLI commands (no per-command approval) — D-194.
|
|
146
|
+
toolsSettings: { shell: { allowedCommands: kiroCliAllowedCommands() } },
|
|
147
|
+
};
|
|
148
|
+
// hooks: object keyed by trigger → array of {command, timeout_ms}. agentSpawn
|
|
149
|
+
// (inject) + userPromptSubmit (inject + prompt-capture) + stop (capture) +
|
|
150
|
+
// preToolUse (the delete-guardrail). timeout_ms (NOT `timeout`). preToolUse
|
|
151
|
+
// `matcher: '*'` (all tools) — kiro-cli matchers are literal strings, not regex
|
|
152
|
+
// (D-197); the bin exits 2 to BLOCK. userPromptSubmit (50.N.1) routes to BOTH
|
|
153
|
+
// inject AND capturePrompt (the <private>-strip + transcript-append half of
|
|
154
|
+
// Claude Code's UserPromptSubmit); kiro-cli's userPromptSubmit stdin carries
|
|
155
|
+
// `prompt`, which capturePrompt reads.
|
|
156
|
+
// postToolUse (50.N.2) → observe-edit, scoped to the file-write tool with
|
|
157
|
+
// `matcher: 'fs_write'` (kiro-cli matchers are literal tool names — the runHook
|
|
158
|
+
// adapter maps fs_write → Write for the shared observeEdit core). Only fires on
|
|
159
|
+
// a file write, so it's cheap.
|
|
160
|
+
cfg.hooks = {
|
|
161
|
+
agentSpawn: [{ command: kiroHookCommand('agentSpawn'), timeout_ms: 10000 }],
|
|
162
|
+
userPromptSubmit: [{ command: kiroHookCommand('userPromptSubmit'), timeout_ms: 10000 }],
|
|
163
|
+
postToolUse: [{ command: kiroHookCommand('postToolUse'), timeout_ms: 10000, matcher: 'fs_write' }],
|
|
164
|
+
stop: [{ command: kiroHookCommand('stop'), timeout_ms: 30000 }],
|
|
165
|
+
preToolUse: [{ command: kiroGuardCommand(), timeout_ms: 5000, matcher: '*' }],
|
|
166
|
+
};
|
|
167
|
+
return cfg;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function installKiroCliAgent({ kiroDir, awsDir } = {}) {
|
|
171
|
+
kiroDir = kiroDir ?? awsDir; // `awsDir` accepted as a back-compat alias for the sandbox base
|
|
172
|
+
const agentsDir = agentsDirOf(kiroDir);
|
|
173
|
+
const agentPath = agentPathOf(kiroDir);
|
|
174
|
+
const settingsPath = cliSettingsPathOf(kiroDir);
|
|
175
|
+
|
|
176
|
+
mkdirSync(agentsDir, { recursive: true });
|
|
177
|
+
|
|
178
|
+
// 1. write the agent config
|
|
179
|
+
const changedAgent = writeIfChanged(agentPath, `${JSON.stringify(buildAgentConfig(), null, 2)}\n`);
|
|
180
|
+
|
|
181
|
+
// 2. register chat.defaultAgent — the LOAD-BEARING step. GUARDED: never clobber
|
|
182
|
+
// a user's existing default that ISN'T ours. If they already point at a
|
|
183
|
+
// different agent, we install `cmk` but leave their default alone (they run
|
|
184
|
+
// `kiro-cli --agent cmk` or set it themselves) and report skipped-existing.
|
|
185
|
+
const existingDefault = readDefaultAgentSetting(settingsPath);
|
|
186
|
+
const userHasForeignDefault = existingDefault != null && existingDefault !== AGENT_NAME;
|
|
187
|
+
|
|
188
|
+
let defaultAgent;
|
|
189
|
+
let changedSettings = false;
|
|
190
|
+
if (userHasForeignDefault) {
|
|
191
|
+
defaultAgent = 'skipped-existing';
|
|
192
|
+
} else {
|
|
193
|
+
changedSettings = mergeDefaultAgentSetting(settingsPath, AGENT_NAME);
|
|
194
|
+
defaultAgent = 'set';
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
action: 'installed',
|
|
199
|
+
defaultAgent,
|
|
200
|
+
changed: changedAgent || changedSettings,
|
|
201
|
+
path: agentPath,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Write only if the serialized content differs (idempotent re-install).
|
|
206
|
+
function writeIfChanged(path, serialized) {
|
|
207
|
+
const existing = existsSync(path) ? readFileSync(path, 'utf8') : null;
|
|
208
|
+
if (existing === serialized) return false;
|
|
209
|
+
writeFileSync(path, serialized, 'utf8');
|
|
210
|
+
return true;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Merge {"chat.defaultAgent": name} into ~/.kiro/settings/cli.json, BYTE-
|
|
214
|
+
// PRESERVING every other key (managed-merge discipline — same as the Claude
|
|
215
|
+
// settings path). Returns true if the file changed.
|
|
216
|
+
function mergeDefaultAgentSetting(settingsPath, name) {
|
|
217
|
+
mkdirSync(join(settingsPath, '..'), { recursive: true });
|
|
218
|
+
const current = parseJsonFile(settingsPath, { fallback: null }) ?? {};
|
|
219
|
+
if (current['chat.defaultAgent'] === name) return false;
|
|
220
|
+
current['chat.defaultAgent'] = name;
|
|
221
|
+
writeFileSync(settingsPath, `${JSON.stringify(current, null, 2)}\n`, 'utf8');
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function uninstallKiroCliAgent({ kiroDir, awsDir } = {}) {
|
|
226
|
+
kiroDir = kiroDir ?? awsDir;
|
|
227
|
+
const agentPath = agentPathOf(kiroDir);
|
|
228
|
+
const settingsPath = cliSettingsPathOf(kiroDir);
|
|
229
|
+
let changed = false;
|
|
230
|
+
|
|
231
|
+
// remove OUR agent file only (well-known path + our marker)
|
|
232
|
+
if (existsSync(agentPath) && isOurAgent(agentPath)) {
|
|
233
|
+
rmSync(agentPath, { force: true });
|
|
234
|
+
changed = true;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// un-register the default ONLY if it points at us (byte-preserve other keys)
|
|
238
|
+
const current = parseJsonFile(settingsPath, { fallback: null });
|
|
239
|
+
if (current != null && current['chat.defaultAgent'] === AGENT_NAME) {
|
|
240
|
+
delete current['chat.defaultAgent'];
|
|
241
|
+
writeFileSync(settingsPath, `${JSON.stringify(current, null, 2)}\n`, 'utf8');
|
|
242
|
+
changed = true;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return { action: 'uninstalled', changed };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Does a cmk-owned kiro-cli agent exist? (the well-known ~/.kiro/agents/cmk.json
|
|
249
|
+
// with our marker). Used by `cmk doctor` HC-1 — a kiro-cli user's capture/inject
|
|
250
|
+
// fires via THIS surface (D-186).
|
|
251
|
+
export function hasOurCliAgent({ kiroDir, awsDir } = {}) {
|
|
252
|
+
kiroDir = kiroDir ?? awsDir;
|
|
253
|
+
const agentPath = agentPathOf(kiroDir);
|
|
254
|
+
return existsSync(agentPath) && isOurAgent(agentPath);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// ── internal ─────────────────────────────────────────────────────────────────
|
|
258
|
+
|
|
259
|
+
// Is the agent at `path` ours? Keyed on our marker in the (valid) `description`
|
|
260
|
+
// field — NOT a rejected top-level field. BOM-tolerant.
|
|
261
|
+
function isOurAgent(path) {
|
|
262
|
+
const j = parseJsonFile(path, { fallback: null });
|
|
263
|
+
return j != null && typeof j.description === 'string' && j.description.includes(MANAGED_MARKER);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Read the user's chat.defaultAgent from ~/.kiro/settings/cli.json. BOM-tolerant.
|
|
267
|
+
function readDefaultAgentSetting(settingsPath) {
|
|
268
|
+
const j = parseJsonFile(settingsPath, { fallback: null });
|
|
269
|
+
return j != null ? (j['chat.defaultAgent'] ?? null) : null;
|
|
270
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// kiro-constants.mjs — leaf module of shared Kiro constants, so multiple Kiro
|
|
2
|
+
// modules can import them without an import cycle (install-kiro ↔ kiro-permissions).
|
|
3
|
+
|
|
4
|
+
// The kit's 11 MCP tool names (bare). Used for BOTH the IDE mcp.json `autoApprove`
|
|
5
|
+
// (install-kiro) AND the IDE-1.0 permissions.yaml `capability: mcp` match list
|
|
6
|
+
// (kiro-permissions) — one source so the two never drift.
|
|
7
|
+
export const MCP_AUTO_APPROVE = Object.freeze([
|
|
8
|
+
'mk_remember',
|
|
9
|
+
'mk_search',
|
|
10
|
+
'mk_get',
|
|
11
|
+
'mk_timeline',
|
|
12
|
+
'mk_cite',
|
|
13
|
+
'mk_recent_activity',
|
|
14
|
+
'mk_trust',
|
|
15
|
+
'mk_lessons_promote',
|
|
16
|
+
'mk_forget',
|
|
17
|
+
'mk_queue_list',
|
|
18
|
+
'mk_queue_resolve',
|
|
19
|
+
]);
|