myagentmemory 0.4.13 → 0.4.15
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 +48 -24
- package/dist/cli-spec.d.ts +25 -0
- package/dist/cli-spec.js +211 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +387 -4
- package/dist/completions.d.ts +13 -0
- package/dist/completions.js +429 -0
- package/dist/core.d.ts +1 -0
- package/dist/core.js +17 -12
- package/dist/hooks.d.ts +42 -0
- package/dist/hooks.js +444 -0
- package/dist/plugin-bootstrap.d.ts +202 -0
- package/dist/plugin-bootstrap.js +628 -0
- package/dist/plugin-host.d.ts +136 -0
- package/dist/plugin-host.js +98 -0
- package/dist/plugin-runtime.d.ts +22 -0
- package/dist/plugin-runtime.js +238 -0
- package/dist/plugin-service.d.ts +49 -0
- package/dist/plugin-service.js +457 -0
- package/docs/official-plugin-bootstrap.md +337 -0
- package/package.json +35 -2
- package/src/cli-spec.ts +236 -0
- package/src/cli.ts +404 -4
- package/src/completions.ts +501 -0
- package/src/core.ts +17 -12
- package/src/hooks.ts +485 -0
- package/src/plugin-bootstrap.ts +944 -0
- package/src/plugin-host.ts +255 -0
- package/src/plugin-runtime.ts +326 -0
- package/src/plugin-service.ts +537 -0
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as os from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import {
|
|
5
|
+
COMMAND_DESCRIPTIONS,
|
|
6
|
+
COMMAND_OPTIONS,
|
|
7
|
+
COMMANDS,
|
|
8
|
+
GLOBAL_OPTIONS,
|
|
9
|
+
OPTION_SPECS,
|
|
10
|
+
optionDescription,
|
|
11
|
+
PLUGIN_COMMAND_DESCRIPTIONS,
|
|
12
|
+
PLUGIN_COMMAND_OPTIONS,
|
|
13
|
+
PLUGIN_COMMANDS,
|
|
14
|
+
SCRATCHPAD_ACTION_DESCRIPTIONS,
|
|
15
|
+
SCRATCHPAD_ACTION_OPTIONS,
|
|
16
|
+
SCRATCHPAD_ACTIONS,
|
|
17
|
+
SHELL_DESCRIPTIONS,
|
|
18
|
+
WORKER_ACTION_DESCRIPTIONS,
|
|
19
|
+
WORKER_ACTION_OPTIONS,
|
|
20
|
+
WORKER_ACTIONS,
|
|
21
|
+
} from "./cli-spec.js";
|
|
22
|
+
|
|
23
|
+
export type CompletionShell = "bash" | "zsh" | "fish" | "powershell";
|
|
24
|
+
|
|
25
|
+
export interface CompletionInstallResult {
|
|
26
|
+
shell: CompletionShell;
|
|
27
|
+
completionPath: string;
|
|
28
|
+
profilePath?: string;
|
|
29
|
+
profileUpdated: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function words(values: readonly string[]): string {
|
|
33
|
+
return values.join(" ");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function zshOptionValue(command: string, option: string): string {
|
|
37
|
+
if (option === "--target") {
|
|
38
|
+
return command === "read"
|
|
39
|
+
? ":target:(daily long_term scratchpad list topic topics)"
|
|
40
|
+
: ":target:(daily long_term topic)";
|
|
41
|
+
}
|
|
42
|
+
if (option === "--mode") return command === "search" ? ":mode:(keyword semantic deep)" : ":mode:(append overwrite)";
|
|
43
|
+
if (option === "--scope") return ":scope:(global current)";
|
|
44
|
+
if (option === "--host") return command === "web" ? ":host:(127.0.0.1 localhost ::1)" : ":host:(pi codex claude)";
|
|
45
|
+
if (option === "--only") return ":agent:(claude codex cursor opencode pi)";
|
|
46
|
+
const value = OPTION_SPECS[option]?.value;
|
|
47
|
+
if (value?.kind === "directory") return `:${value.label}:_directories`;
|
|
48
|
+
if (value?.kind === "file") return `:${value.label}:_files`;
|
|
49
|
+
if (value) return `:${value.label}:`;
|
|
50
|
+
return "";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function zshOptionSpecs(command: string, options: readonly string[] = COMMAND_OPTIONS[command] ?? []): string {
|
|
54
|
+
return options
|
|
55
|
+
.map((option) => `'${option}[${optionDescription(option)}]${zshOptionValue(command, option)}'`)
|
|
56
|
+
.join(" ");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function fishOption(command: string, condition: string, option: string): string {
|
|
60
|
+
const spec = OPTION_SPECS[option];
|
|
61
|
+
const value = spec?.value ? " -r" : "";
|
|
62
|
+
let suggestions = "";
|
|
63
|
+
if (option === "--target")
|
|
64
|
+
suggestions =
|
|
65
|
+
command === "read" ? " -a 'daily long_term scratchpad list topic topics'" : " -a 'daily long_term topic'";
|
|
66
|
+
else if (option === "--mode")
|
|
67
|
+
suggestions = command === "search" ? " -a 'keyword semantic deep'" : " -a 'append overwrite'";
|
|
68
|
+
else if (option === "--scope") suggestions = " -a 'global current'";
|
|
69
|
+
else if (option === "--host")
|
|
70
|
+
suggestions = command === "web" ? " -a '127.0.0.1 localhost ::1'" : " -a 'pi codex claude'";
|
|
71
|
+
else if (option === "--only") suggestions = " -a 'claude codex cursor opencode pi'";
|
|
72
|
+
else if (spec?.value?.kind === "directory") suggestions = " -a '(__fish_complete_directories)'";
|
|
73
|
+
else if (spec?.value?.kind === "file") suggestions = " -F";
|
|
74
|
+
return `complete -c agent-memory -n '${condition}' -l ${option.slice(2)}${value}${suggestions} -d '${optionDescription(option)}'`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function bashCompletion(): string {
|
|
78
|
+
return `# agent-memory completion for Bash
|
|
79
|
+
# Installed automatically by: agent-memory completion bash
|
|
80
|
+
# Print this script instead with: agent-memory completion bash --stdout
|
|
81
|
+
_agent_memory_completion() {
|
|
82
|
+
local cur command sub action
|
|
83
|
+
COMPREPLY=()
|
|
84
|
+
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
85
|
+
command="\${COMP_WORDS[1]}"
|
|
86
|
+
sub="\${COMP_WORDS[2]}"
|
|
87
|
+
action="\${COMP_WORDS[3]}"
|
|
88
|
+
|
|
89
|
+
if [[ "$cur" == --*=* ]]; then return 0; fi
|
|
90
|
+
case "\${COMP_WORDS[COMP_CWORD-1]}" in
|
|
91
|
+
--target)
|
|
92
|
+
if [[ "$command" == read ]]; then COMPREPLY=( $(compgen -W "daily long_term scratchpad list topic topics" -- "$cur") );
|
|
93
|
+
else COMPREPLY=( $(compgen -W "daily long_term topic" -- "$cur") ); fi; return 0 ;;
|
|
94
|
+
--mode)
|
|
95
|
+
if [[ "$command" == search ]]; then COMPREPLY=( $(compgen -W "keyword semantic deep" -- "$cur") );
|
|
96
|
+
else COMPREPLY=( $(compgen -W "append overwrite" -- "$cur") ); fi; return 0 ;;
|
|
97
|
+
--scope) COMPREPLY=( $(compgen -W "global current" -- "$cur") ); return 0 ;;
|
|
98
|
+
--host)
|
|
99
|
+
if [[ "$command" == web ]]; then COMPREPLY=( $(compgen -W "127.0.0.1 localhost ::1" -- "$cur") );
|
|
100
|
+
else COMPREPLY=( $(compgen -W "pi codex claude" -- "$cur") ); fi; return 0 ;;
|
|
101
|
+
--only) COMPREPLY=( $(compgen -W "claude codex cursor opencode pi" -- "$cur") ); return 0 ;;
|
|
102
|
+
--dir|--cwd|--pi|--codex|--claude|--state|--journal|--candidates-dir|--decisions-dir|--auto-dir|--output)
|
|
103
|
+
COMPREPLY=( $(compgen -f -- "$cur") ); return 0 ;;
|
|
104
|
+
esac
|
|
105
|
+
|
|
106
|
+
if (( COMP_CWORD == 1 )); then
|
|
107
|
+
COMPREPLY=( $(compgen -W "${words(COMMANDS)}" -- "$cur") ); return 0
|
|
108
|
+
fi
|
|
109
|
+
if [[ "$command" == plugin && $COMP_CWORD -eq 2 ]]; then
|
|
110
|
+
COMPREPLY=( $(compgen -W "${words(PLUGIN_COMMANDS)}" -- "$cur") ); return 0
|
|
111
|
+
fi
|
|
112
|
+
if [[ "$command" == plugin && "$sub" == worker && $COMP_CWORD -eq 3 ]]; then
|
|
113
|
+
COMPREPLY=( $(compgen -W "${words(WORKER_ACTIONS)}" -- "$cur") ); return 0
|
|
114
|
+
fi
|
|
115
|
+
if [[ "$command" == scratchpad && $COMP_CWORD -eq 2 ]]; then
|
|
116
|
+
COMPREPLY=( $(compgen -W "${words(SCRATCHPAD_ACTIONS)}" -- "$cur") ); return 0
|
|
117
|
+
fi
|
|
118
|
+
if [[ "$command" == completion && $COMP_CWORD -eq 2 ]]; then
|
|
119
|
+
COMPREPLY=( $(compgen -W "bash zsh fish powershell" -- "$cur") ); return 0
|
|
120
|
+
fi
|
|
121
|
+
local command_options=""
|
|
122
|
+
if [[ "$command" == plugin ]]; then
|
|
123
|
+
case "$sub" in
|
|
124
|
+
${Object.entries(PLUGIN_COMMAND_OPTIONS)
|
|
125
|
+
.map(([command, options]) => ` ${command}) command_options="${words(options)}" ;;`)
|
|
126
|
+
.join("\n")}
|
|
127
|
+
esac
|
|
128
|
+
if [[ "$sub" == worker && -n "$action" ]]; then
|
|
129
|
+
case "$action" in
|
|
130
|
+
${Object.entries(WORKER_ACTION_OPTIONS)
|
|
131
|
+
.map(([action, options]) => ` ${action}) command_options="${words(options)}" ;;`)
|
|
132
|
+
.join("\n")}
|
|
133
|
+
esac
|
|
134
|
+
fi
|
|
135
|
+
elif [[ "$command" == scratchpad && -n "$sub" ]]; then
|
|
136
|
+
case "$sub" in
|
|
137
|
+
${Object.entries(SCRATCHPAD_ACTION_OPTIONS)
|
|
138
|
+
.map(([action, options]) => ` ${action}) command_options="${words(options)}" ;;`)
|
|
139
|
+
.join("\n")}
|
|
140
|
+
esac
|
|
141
|
+
else
|
|
142
|
+
case "$command" in
|
|
143
|
+
${Object.entries(COMMAND_OPTIONS)
|
|
144
|
+
.map(([command, options]) => ` ${command}) command_options="${words(options)}" ;;`)
|
|
145
|
+
.join("\n")}
|
|
146
|
+
esac
|
|
147
|
+
fi
|
|
148
|
+
COMPREPLY=( $(compgen -W "${words(GLOBAL_OPTIONS)} $command_options" -- "$cur") )
|
|
149
|
+
}
|
|
150
|
+
complete -F _agent_memory_completion agent-memory
|
|
151
|
+
`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function zshCompletion(): string {
|
|
155
|
+
return `#compdef agent-memory
|
|
156
|
+
# agent-memory completion for Zsh
|
|
157
|
+
# Installed automatically by: agent-memory completion zsh
|
|
158
|
+
# Print this script instead with: agent-memory completion zsh --stdout
|
|
159
|
+
_agent-memory() {
|
|
160
|
+
local -a commands plugin_commands worker_actions scratchpad_actions shells
|
|
161
|
+
local command="$words[2]"
|
|
162
|
+
local subcommand="$words[3]"
|
|
163
|
+
local action="$words[4]"
|
|
164
|
+
local command_position=$CURRENT
|
|
165
|
+
commands=(${COMMANDS.map((command) => `'${command}:${COMMAND_DESCRIPTIONS[command] ?? command}'`).join(" ")})
|
|
166
|
+
plugin_commands=(${PLUGIN_COMMANDS.map((command) => `'${command}:${PLUGIN_COMMAND_DESCRIPTIONS[command]}'`).join(" ")})
|
|
167
|
+
worker_actions=(${WORKER_ACTIONS.map((action) => `'${action}:${WORKER_ACTION_DESCRIPTIONS[action]}'`).join(" ")})
|
|
168
|
+
scratchpad_actions=(${SCRATCHPAD_ACTIONS.map((action) => `'${action}:${SCRATCHPAD_ACTION_DESCRIPTIONS[action]}'`).join(" ")})
|
|
169
|
+
shells=(${Object.entries(SHELL_DESCRIPTIONS)
|
|
170
|
+
.map(([shell, description]) => `'${shell}:${description}'`)
|
|
171
|
+
.join(" ")})
|
|
172
|
+
|
|
173
|
+
_arguments -C \\
|
|
174
|
+
'(-h --help)'{-h,--help}'[show help]' \\
|
|
175
|
+
'(-V --version)'{-V,--version}'[show version]' \\
|
|
176
|
+
'--json[emit structured JSON]' \\
|
|
177
|
+
'--dir[memory directory]:directory:_directories' \\
|
|
178
|
+
'1:command:->command' \\
|
|
179
|
+
'*::argument:->args'
|
|
180
|
+
|
|
181
|
+
case $state in
|
|
182
|
+
command) _describe 'command' commands ;;
|
|
183
|
+
args)
|
|
184
|
+
case $command in
|
|
185
|
+
plugin)
|
|
186
|
+
if (( command_position == 3 )); then _describe 'plugin command' plugin_commands
|
|
187
|
+
elif [[ "$subcommand" == worker ]] && (( command_position == 4 )); then _describe 'worker action' worker_actions
|
|
188
|
+
else
|
|
189
|
+
if [[ "$subcommand" == worker && -n "$action" ]]; then
|
|
190
|
+
case "$action" in
|
|
191
|
+
${Object.entries(WORKER_ACTION_OPTIONS)
|
|
192
|
+
.map(
|
|
193
|
+
([action, options]) =>
|
|
194
|
+
` ${action}) _arguments ${zshOptionSpecs(`plugin:worker:${action}`, options)} ;;`,
|
|
195
|
+
)
|
|
196
|
+
.join("\n")}
|
|
197
|
+
esac
|
|
198
|
+
else
|
|
199
|
+
case "$subcommand" in
|
|
200
|
+
${Object.entries(PLUGIN_COMMAND_OPTIONS)
|
|
201
|
+
.map(
|
|
202
|
+
([command, options]) => ` ${command}) _arguments ${zshOptionSpecs(`plugin:${command}`, options)} ;;`,
|
|
203
|
+
)
|
|
204
|
+
.join("\n")}
|
|
205
|
+
esac
|
|
206
|
+
fi
|
|
207
|
+
fi ;;
|
|
208
|
+
scratchpad)
|
|
209
|
+
if (( command_position == 3 )); then _describe 'action' scratchpad_actions
|
|
210
|
+
else
|
|
211
|
+
case "$subcommand" in
|
|
212
|
+
${Object.entries(SCRATCHPAD_ACTION_OPTIONS)
|
|
213
|
+
.map(([action, options]) => ` ${action}) _arguments ${zshOptionSpecs("scratchpad", options)} ;;`)
|
|
214
|
+
.join("\n")}
|
|
215
|
+
esac
|
|
216
|
+
fi ;;
|
|
217
|
+
completion)
|
|
218
|
+
if (( command_position == 3 )); then _describe 'shell' shells
|
|
219
|
+
else _arguments ${zshOptionSpecs("completion")}; fi ;;
|
|
220
|
+
${Object.keys(COMMAND_OPTIONS)
|
|
221
|
+
.filter((command) => command !== "plugin" && command !== "scratchpad" && command !== "completion")
|
|
222
|
+
.map((command) => ` ${command}) _arguments ${zshOptionSpecs(command)} ;;`)
|
|
223
|
+
.join("\n")}
|
|
224
|
+
*) _arguments ;;
|
|
225
|
+
esac ;;
|
|
226
|
+
esac
|
|
227
|
+
}
|
|
228
|
+
compdef _agent-memory agent-memory
|
|
229
|
+
`;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function fishCompletion(): string {
|
|
233
|
+
const lines = [
|
|
234
|
+
"# agent-memory completion for Fish",
|
|
235
|
+
"# Installed automatically by: agent-memory completion fish",
|
|
236
|
+
"# Print this script instead with: agent-memory completion fish --stdout",
|
|
237
|
+
"complete -c agent-memory -f",
|
|
238
|
+
...COMMANDS.map(
|
|
239
|
+
(command) =>
|
|
240
|
+
`complete -c agent-memory -n '__fish_use_subcommand' -a '${command}' -d '${COMMAND_DESCRIPTIONS[command] ?? command}'`,
|
|
241
|
+
),
|
|
242
|
+
...PLUGIN_COMMANDS.map(
|
|
243
|
+
(command) =>
|
|
244
|
+
`complete -c agent-memory -n '__fish_seen_subcommand_from plugin; and not __fish_seen_subcommand_from ${words(PLUGIN_COMMANDS)}' -a '${command}' -d '${PLUGIN_COMMAND_DESCRIPTIONS[command]}'`,
|
|
245
|
+
),
|
|
246
|
+
...WORKER_ACTIONS.map(
|
|
247
|
+
(action) =>
|
|
248
|
+
`complete -c agent-memory -n '__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from worker; and not __fish_seen_subcommand_from ${words(WORKER_ACTIONS)}' -a '${action}' -d '${WORKER_ACTION_DESCRIPTIONS[action]}'`,
|
|
249
|
+
),
|
|
250
|
+
...SCRATCHPAD_ACTIONS.map(
|
|
251
|
+
(action) =>
|
|
252
|
+
`complete -c agent-memory -n '__fish_seen_subcommand_from scratchpad; and not __fish_seen_subcommand_from ${words(SCRATCHPAD_ACTIONS)}' -a '${action}' -d '${SCRATCHPAD_ACTION_DESCRIPTIONS[action]}'`,
|
|
253
|
+
),
|
|
254
|
+
...Object.entries(SHELL_DESCRIPTIONS).map(
|
|
255
|
+
([shell, description]) =>
|
|
256
|
+
`complete -c agent-memory -n '__fish_seen_subcommand_from completion' -a '${shell}' -d '${description}'`,
|
|
257
|
+
),
|
|
258
|
+
"complete -c agent-memory -l dir -r -a '(__fish_complete_directories)' -d 'override the active memory directory'",
|
|
259
|
+
"complete -c agent-memory -l json -d 'emit command-specific structured JSON'",
|
|
260
|
+
"complete -c agent-memory -s h -l help -d 'show help for the selected command'",
|
|
261
|
+
"complete -c agent-memory -s V -l version -d 'print the installed version and exit'",
|
|
262
|
+
];
|
|
263
|
+
for (const [command, options] of Object.entries(COMMAND_OPTIONS)) {
|
|
264
|
+
if (command === "scratchpad") continue;
|
|
265
|
+
for (const option of options) {
|
|
266
|
+
lines.push(fishOption(command, `__fish_seen_subcommand_from ${command}`, option));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
for (const [subcommand, options] of Object.entries(PLUGIN_COMMAND_OPTIONS)) {
|
|
270
|
+
if (subcommand === "worker") continue;
|
|
271
|
+
for (const option of options) {
|
|
272
|
+
lines.push(
|
|
273
|
+
fishOption(
|
|
274
|
+
`plugin:${subcommand}`,
|
|
275
|
+
`__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from ${subcommand}`,
|
|
276
|
+
option,
|
|
277
|
+
),
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
for (const [action, options] of Object.entries(WORKER_ACTION_OPTIONS)) {
|
|
282
|
+
for (const option of options) {
|
|
283
|
+
lines.push(
|
|
284
|
+
fishOption(
|
|
285
|
+
`plugin:worker:${action}`,
|
|
286
|
+
`__fish_seen_subcommand_from plugin; and __fish_seen_subcommand_from worker; and __fish_seen_subcommand_from ${action}`,
|
|
287
|
+
option,
|
|
288
|
+
),
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
for (const [action, options] of Object.entries(SCRATCHPAD_ACTION_OPTIONS)) {
|
|
293
|
+
for (const option of options) {
|
|
294
|
+
lines.push(
|
|
295
|
+
fishOption(
|
|
296
|
+
"scratchpad",
|
|
297
|
+
`__fish_seen_subcommand_from scratchpad; and __fish_seen_subcommand_from ${action}`,
|
|
298
|
+
option,
|
|
299
|
+
),
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return `${lines.join("\n")}\n`;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function powershellCompletion(): string {
|
|
307
|
+
const psOptions = (options: readonly string[]) => (options.length ? `@('${options.join("','")}')` : "@()");
|
|
308
|
+
const optionCases = Object.entries(COMMAND_OPTIONS)
|
|
309
|
+
.map(([command, options]) => ` '${command}' { $candidates += ${psOptions(options)} }`)
|
|
310
|
+
.join("\n");
|
|
311
|
+
const pluginOptionCases = Object.entries(PLUGIN_COMMAND_OPTIONS)
|
|
312
|
+
.map(([command, options]) => ` '${command}' { $candidates += ${psOptions(options)} }`)
|
|
313
|
+
.join("\n");
|
|
314
|
+
const workerOptionCases = Object.entries(WORKER_ACTION_OPTIONS)
|
|
315
|
+
.map(([action, options]) => ` '${action}' { $candidates += ${psOptions(options)} }`)
|
|
316
|
+
.join("\n");
|
|
317
|
+
const scratchpadOptionCases = Object.entries(SCRATCHPAD_ACTION_OPTIONS)
|
|
318
|
+
.map(([action, options]) => ` '${action}' { $candidates += ${psOptions(options)} }`)
|
|
319
|
+
.join("\n");
|
|
320
|
+
return `# agent-memory completion for PowerShell
|
|
321
|
+
# Installed automatically by: agent-memory completion powershell
|
|
322
|
+
# Print this script instead with: agent-memory completion powershell --stdout
|
|
323
|
+
Register-ArgumentCompleter -Native -CommandName agent-memory -ScriptBlock {
|
|
324
|
+
param($wordToComplete, $commandAst, $cursorPosition)
|
|
325
|
+
$tokens = @($commandAst.CommandElements | ForEach-Object { $_.ToString() })
|
|
326
|
+
$commands = @('${COMMANDS.join("','")}')
|
|
327
|
+
$pluginCommands = @('${PLUGIN_COMMANDS.join("','")}')
|
|
328
|
+
$workerActions = @('${WORKER_ACTIONS.join("','")}')
|
|
329
|
+
$scratchpadActions = @('${SCRATCHPAD_ACTIONS.join("','")}')
|
|
330
|
+
$shells = @('bash','zsh','fish','powershell')
|
|
331
|
+
$commandDescriptions = @{
|
|
332
|
+
${Object.entries(COMMAND_DESCRIPTIONS)
|
|
333
|
+
.map(([command, description]) => ` '${command}' = '${description}'`)
|
|
334
|
+
.join("\n")}
|
|
335
|
+
}
|
|
336
|
+
$pluginCommandDescriptions = @{
|
|
337
|
+
${Object.entries(PLUGIN_COMMAND_DESCRIPTIONS)
|
|
338
|
+
.map(([command, description]) => ` '${command}' = '${description}'`)
|
|
339
|
+
.join("\n")}
|
|
340
|
+
}
|
|
341
|
+
$workerActionDescriptions = @{
|
|
342
|
+
${Object.entries(WORKER_ACTION_DESCRIPTIONS)
|
|
343
|
+
.map(([action, description]) => ` '${action}' = '${description}'`)
|
|
344
|
+
.join("\n")}
|
|
345
|
+
}
|
|
346
|
+
$scratchpadActionDescriptions = @{
|
|
347
|
+
${Object.entries(SCRATCHPAD_ACTION_DESCRIPTIONS)
|
|
348
|
+
.map(([action, description]) => ` '${action}' = '${description}'`)
|
|
349
|
+
.join("\n")}
|
|
350
|
+
}
|
|
351
|
+
$shellDescriptions = @{
|
|
352
|
+
${Object.entries(SHELL_DESCRIPTIONS)
|
|
353
|
+
.map(([shell, description]) => ` '${shell}' = '${description}'`)
|
|
354
|
+
.join("\n")}
|
|
355
|
+
}
|
|
356
|
+
$optionDescriptions = @{
|
|
357
|
+
${Object.keys(OPTION_SPECS)
|
|
358
|
+
.map((option) => ` '${option}' = '${optionDescription(option)}'`)
|
|
359
|
+
.join("\n")}
|
|
360
|
+
}
|
|
361
|
+
$globalOptions = @('${GLOBAL_OPTIONS.join("','")}')
|
|
362
|
+
$command = if ($tokens.Count -gt 1) { $tokens[1] } else { '' }
|
|
363
|
+
$subcommand = if ($tokens.Count -gt 2) { $tokens[2] } else { '' }
|
|
364
|
+
$action = if ($tokens.Count -gt 3) { $tokens[3] } else { '' }
|
|
365
|
+
$candidates = @()
|
|
366
|
+
$candidateDescriptions = @{}
|
|
367
|
+
|
|
368
|
+
if ($tokens.Count -le 2) { $candidates = $commands + $globalOptions; $candidateDescriptions = $commandDescriptions }
|
|
369
|
+
elseif ($command -eq 'plugin' -and $tokens.Count -le 4 -and $tokens[2] -eq 'worker') { $candidates = $workerActions + $globalOptions; $candidateDescriptions = $workerActionDescriptions }
|
|
370
|
+
elseif ($command -eq 'plugin' -and $tokens.Count -le 3) { $candidates = $pluginCommands + $globalOptions; $candidateDescriptions = $pluginCommandDescriptions }
|
|
371
|
+
elseif ($command -eq 'scratchpad' -and $tokens.Count -le 3) { $candidates = $scratchpadActions + $globalOptions; $candidateDescriptions = $scratchpadActionDescriptions }
|
|
372
|
+
elseif ($command -eq 'completion' -and $tokens.Count -le 3) { $candidates = $shells; $candidateDescriptions = $shellDescriptions }
|
|
373
|
+
else {
|
|
374
|
+
$candidates = $globalOptions
|
|
375
|
+
$candidateDescriptions = $optionDescriptions
|
|
376
|
+
if ($command -eq 'plugin') {
|
|
377
|
+
if ($subcommand -eq 'worker' -and $action) {
|
|
378
|
+
switch ($action) {
|
|
379
|
+
${workerOptionCases}
|
|
380
|
+
}
|
|
381
|
+
} else {
|
|
382
|
+
switch ($subcommand) {
|
|
383
|
+
${pluginOptionCases}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
} elseif ($command -eq 'scratchpad') {
|
|
387
|
+
switch ($subcommand) {
|
|
388
|
+
${scratchpadOptionCases}
|
|
389
|
+
}
|
|
390
|
+
} else {
|
|
391
|
+
switch ($command) {
|
|
392
|
+
${optionCases}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
$candidates | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object {
|
|
398
|
+
$description = if ($candidateDescriptions.ContainsKey($_)) { $candidateDescriptions[$_] } elseif ($optionDescriptions.ContainsKey($_)) { $optionDescriptions[$_] } else { $_ }
|
|
399
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterName', $description)
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
`;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export function generateCompletion(shell: CompletionShell): string {
|
|
406
|
+
switch (shell) {
|
|
407
|
+
case "bash":
|
|
408
|
+
return bashCompletion();
|
|
409
|
+
case "zsh":
|
|
410
|
+
return zshCompletion();
|
|
411
|
+
case "fish":
|
|
412
|
+
return fishCompletion();
|
|
413
|
+
case "powershell":
|
|
414
|
+
return powershellCompletion();
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
export function detectCompletionShell(
|
|
419
|
+
environment: Record<string, string | undefined> = process.env,
|
|
420
|
+
platform = process.platform,
|
|
421
|
+
): CompletionShell | null {
|
|
422
|
+
const shell = path.basename(environment.SHELL ?? "").toLowerCase();
|
|
423
|
+
if (shell === "bash" || shell === "zsh" || shell === "fish") return shell;
|
|
424
|
+
if (shell.includes("pwsh") || shell.includes("powershell")) return "powershell";
|
|
425
|
+
if (platform === "win32" && environment.PSModulePath) return "powershell";
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function writeCompletionFile(filePath: string, content: string): void {
|
|
430
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o755 });
|
|
431
|
+
fs.writeFileSync(filePath, content, { mode: 0o644 });
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function ensureProfileBlock(filePath: string, lines: string[]): boolean {
|
|
435
|
+
const start = "# >>> agent-memory completion >>>";
|
|
436
|
+
const end = "# <<< agent-memory completion <<<";
|
|
437
|
+
const block = `${start}\n${lines.join("\n")}\n${end}`;
|
|
438
|
+
const current = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf8") : "";
|
|
439
|
+
const startIndex = current.indexOf(start);
|
|
440
|
+
const endIndex = startIndex === -1 ? -1 : current.indexOf(end, startIndex);
|
|
441
|
+
let updated: string;
|
|
442
|
+
|
|
443
|
+
if (startIndex !== -1 && endIndex !== -1) {
|
|
444
|
+
updated = `${current.slice(0, startIndex)}${block}${current.slice(endIndex + end.length)}`;
|
|
445
|
+
} else {
|
|
446
|
+
const separator = current.length === 0 || current.endsWith("\n") ? "" : "\n";
|
|
447
|
+
updated = `${current}${separator}${block}\n`;
|
|
448
|
+
}
|
|
449
|
+
if (updated === current) return false;
|
|
450
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true, mode: 0o755 });
|
|
451
|
+
fs.writeFileSync(filePath, updated, { mode: 0o600 });
|
|
452
|
+
return true;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
export function installCompletion(
|
|
456
|
+
shell: CompletionShell,
|
|
457
|
+
options: { homeDir?: string; platform?: NodeJS.Platform } = {},
|
|
458
|
+
): CompletionInstallResult {
|
|
459
|
+
const homeDir = options.homeDir ?? os.homedir();
|
|
460
|
+
const platform = options.platform ?? process.platform;
|
|
461
|
+
const completionDir = path.join(homeDir, ".config", "agent-memory", "completions");
|
|
462
|
+
|
|
463
|
+
if (shell === "fish") {
|
|
464
|
+
const completionPath = path.join(homeDir, ".config", "fish", "completions", "agent-memory.fish");
|
|
465
|
+
writeCompletionFile(completionPath, generateCompletion(shell));
|
|
466
|
+
return { shell, completionPath, profileUpdated: false };
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const extension = shell === "powershell" ? "ps1" : shell;
|
|
470
|
+
const completionPath = path.join(completionDir, `agent-memory.${extension}`);
|
|
471
|
+
writeCompletionFile(completionPath, generateCompletion(shell));
|
|
472
|
+
|
|
473
|
+
if (shell === "bash") {
|
|
474
|
+
const profilePath = path.join(homeDir, ".bashrc");
|
|
475
|
+
const profileUpdated = ensureProfileBlock(profilePath, [
|
|
476
|
+
`if [[ -r "$HOME/.config/agent-memory/completions/agent-memory.bash" ]]; then`,
|
|
477
|
+
` source "$HOME/.config/agent-memory/completions/agent-memory.bash"`,
|
|
478
|
+
"fi",
|
|
479
|
+
]);
|
|
480
|
+
return { shell, completionPath, profilePath, profileUpdated };
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (shell === "zsh") {
|
|
484
|
+
const profilePath = path.join(homeDir, ".zshrc");
|
|
485
|
+
const profileUpdated = ensureProfileBlock(profilePath, [
|
|
486
|
+
"autoload -Uz compinit",
|
|
487
|
+
"if (( ! $+functions[compdef] )); then compinit; fi",
|
|
488
|
+
`source "$HOME/.config/agent-memory/completions/agent-memory.zsh"`,
|
|
489
|
+
]);
|
|
490
|
+
return { shell, completionPath, profilePath, profileUpdated };
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const profilePath =
|
|
494
|
+
platform === "win32"
|
|
495
|
+
? path.join(homeDir, "Documents", "PowerShell", "Microsoft.PowerShell_profile.ps1")
|
|
496
|
+
: path.join(homeDir, ".config", "powershell", "Microsoft.PowerShell_profile.ps1");
|
|
497
|
+
const profileUpdated = ensureProfileBlock(profilePath, [
|
|
498
|
+
`. "$HOME/.config/agent-memory/completions/agent-memory.ps1"`,
|
|
499
|
+
]);
|
|
500
|
+
return { shell, completionPath, profilePath, profileUpdated };
|
|
501
|
+
}
|
package/src/core.ts
CHANGED
|
@@ -1396,6 +1396,7 @@ export interface ToolResult {
|
|
|
1396
1396
|
}
|
|
1397
1397
|
|
|
1398
1398
|
export async function memoryWrite(params: {
|
|
1399
|
+
directory?: string;
|
|
1399
1400
|
target?: "long_term" | "daily" | "topic";
|
|
1400
1401
|
content: string;
|
|
1401
1402
|
mode?: "append" | "overwrite";
|
|
@@ -1404,14 +1405,22 @@ export async function memoryWrite(params: {
|
|
|
1404
1405
|
date?: string;
|
|
1405
1406
|
sourceUri?: string;
|
|
1406
1407
|
}): Promise<ToolResult> {
|
|
1407
|
-
|
|
1408
|
+
const memoryDir = params.directory ? path.resolve(params.directory) : getMemoryDir();
|
|
1409
|
+
fs.mkdirSync(memoryDir, { recursive: true });
|
|
1410
|
+
fs.mkdirSync(path.join(memoryDir, "daily"), { recursive: true });
|
|
1411
|
+
fs.mkdirSync(path.join(memoryDir, "topics"), { recursive: true });
|
|
1412
|
+
const scheduleSearchRefresh = async () => {
|
|
1413
|
+
if (path.resolve(getMemoryDir()) !== memoryDir) return;
|
|
1414
|
+
await ensureQmdAvailableForUpdate();
|
|
1415
|
+
scheduleQmdUpdate();
|
|
1416
|
+
};
|
|
1408
1417
|
const target = params.target ?? "daily";
|
|
1409
1418
|
const { content, mode } = params;
|
|
1410
1419
|
const sid = shortSessionId(params.sessionId ?? "cli");
|
|
1411
1420
|
const ts = nowTimestamp();
|
|
1412
1421
|
|
|
1413
1422
|
if (target === "daily") {
|
|
1414
|
-
const filePath =
|
|
1423
|
+
const filePath = path.join(memoryDir, "daily", `${params.date?.trim() || todayStr()}.md`);
|
|
1415
1424
|
const existing = readFileSafe(filePath) ?? "";
|
|
1416
1425
|
const safeExisting = redactSecrets(existing).content;
|
|
1417
1426
|
const existingPreview = buildPreview(safeExisting, {
|
|
@@ -1426,8 +1435,7 @@ export async function memoryWrite(params: {
|
|
|
1426
1435
|
const separator = existing.trim() ? "\n\n" : "";
|
|
1427
1436
|
const stored = formatStoredEntry(content, `<!-- ${ts} [${sid}] -->`, params.sourceUri);
|
|
1428
1437
|
fs.writeFileSync(filePath, existing + separator + stored.entry, "utf-8");
|
|
1429
|
-
await
|
|
1430
|
-
scheduleQmdUpdate();
|
|
1438
|
+
await scheduleSearchRefresh();
|
|
1431
1439
|
return {
|
|
1432
1440
|
text: `Appended to daily log: ${filePath}${existingSnippet}`,
|
|
1433
1441
|
details: {
|
|
@@ -1453,7 +1461,7 @@ export async function memoryWrite(params: {
|
|
|
1453
1461
|
if (!slug) {
|
|
1454
1462
|
return { text: "Error: 'topic' must include at least one letter or number.", details: {}, isError: true };
|
|
1455
1463
|
}
|
|
1456
|
-
const filePath =
|
|
1464
|
+
const filePath = path.join(memoryDir, "topics", `${slug}.md`);
|
|
1457
1465
|
const existing = readFileSafe(filePath) ?? "";
|
|
1458
1466
|
const safeExisting = redactSecrets(existing).content;
|
|
1459
1467
|
const existingPreview = buildPreview(safeExisting, {
|
|
@@ -1475,8 +1483,7 @@ export async function memoryWrite(params: {
|
|
|
1475
1483
|
params.sourceUri,
|
|
1476
1484
|
);
|
|
1477
1485
|
fs.writeFileSync(filePath, `${base}${separator}${stored.entry}`, "utf-8");
|
|
1478
|
-
await
|
|
1479
|
-
scheduleQmdUpdate();
|
|
1486
|
+
await scheduleSearchRefresh();
|
|
1480
1487
|
return {
|
|
1481
1488
|
text: `Appended to topic: ${filePath}${existingSnippet}`,
|
|
1482
1489
|
details: {
|
|
@@ -1497,7 +1504,7 @@ export async function memoryWrite(params: {
|
|
|
1497
1504
|
}
|
|
1498
1505
|
|
|
1499
1506
|
// long_term
|
|
1500
|
-
const memFile =
|
|
1507
|
+
const memFile = path.join(memoryDir, "MEMORY.md");
|
|
1501
1508
|
const existing = readFileSafe(memFile) ?? "";
|
|
1502
1509
|
const safeExisting = redactSecrets(existing).content;
|
|
1503
1510
|
const existingPreview = buildPreview(safeExisting, {
|
|
@@ -1512,8 +1519,7 @@ export async function memoryWrite(params: {
|
|
|
1512
1519
|
if (mode === "overwrite") {
|
|
1513
1520
|
const stored = formatStoredEntry(content, `<!-- last updated: ${ts} [${sid}] -->`, params.sourceUri);
|
|
1514
1521
|
fs.writeFileSync(memFile, stored.entry, "utf-8");
|
|
1515
|
-
await
|
|
1516
|
-
scheduleQmdUpdate();
|
|
1522
|
+
await scheduleSearchRefresh();
|
|
1517
1523
|
return {
|
|
1518
1524
|
text: `Overwrote MEMORY.md${existingSnippet}`,
|
|
1519
1525
|
details: {
|
|
@@ -1534,8 +1540,7 @@ export async function memoryWrite(params: {
|
|
|
1534
1540
|
const separator = existing.trim() ? "\n\n" : "";
|
|
1535
1541
|
const stored = formatStoredEntry(content, `<!-- ${ts} [${sid}] -->`, params.sourceUri);
|
|
1536
1542
|
fs.writeFileSync(memFile, existing + separator + stored.entry, "utf-8");
|
|
1537
|
-
await
|
|
1538
|
-
scheduleQmdUpdate();
|
|
1543
|
+
await scheduleSearchRefresh();
|
|
1539
1544
|
return {
|
|
1540
1545
|
text: `Appended to MEMORY.md${existingSnippet}`,
|
|
1541
1546
|
details: {
|