@nghyane/arcane 0.1.25 → 0.1.27
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/CHANGELOG.md +31 -0
- package/package.json +1 -1
- package/src/exec/bash-executor.ts +8 -8
- package/src/modes/controllers/command-controller.ts +1 -3
- package/src/modes/controllers/selector-controller.ts +1 -4
- package/src/prompts/system/system-prompt.md +11 -2
- package/src/sdk.ts +0 -10
- package/src/session/session-index.ts +63 -36
- package/src/tools/bash-interactive.ts +23 -9
- package/src/tools/bash-interceptor.ts +0 -1
- package/src/tools/bash-normalize.ts +0 -71
- package/src/tools/bash-skill-urls.ts +0 -20
- package/src/tools/bash.ts +4 -20
- package/src/tools/create-tools.ts +2 -4
- package/src/tools/explore.ts +1 -1
- package/src/tools/fetch.ts +13 -19
- package/src/tools/github.ts +130 -28
- package/src/tools/index.ts +2 -5
- package/src/tools/librarian.ts +1 -1
- package/src/tools/oracle.ts +1 -1
- package/src/tools/render-mermaid.ts +61 -5
- package/src/tools/{reviewer-tool.ts → reviewer.ts} +1 -1
- package/src/web/github-client.ts +17 -9
- package/src/tools/gemini-image.ts +0 -905
- package/src/tools/save-memory.ts +0 -185
- /package/src/tools/{subagent-tool.ts → subagent.ts} +0 -0
package/src/tools/save-memory.ts
DELETED
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
import * as path from "node:path";
|
|
2
|
-
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@nghyane/arcane-agent";
|
|
3
|
-
import type { Component } from "@nghyane/arcane-tui";
|
|
4
|
-
import { Text } from "@nghyane/arcane-tui";
|
|
5
|
-
import { isEnoent, logger } from "@nghyane/arcane-utils";
|
|
6
|
-
import { type Static, Type } from "@sinclair/typebox";
|
|
7
|
-
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
8
|
-
import type { Theme } from "../theme/theme";
|
|
9
|
-
import { renderStatusLine } from "../tui";
|
|
10
|
-
import { shortenPath, TRUNCATE_LENGTHS, truncateToWidth } from "../ui/render-utils";
|
|
11
|
-
import type { ToolSession } from ".";
|
|
12
|
-
|
|
13
|
-
const saveMemorySchema = Type.Object({
|
|
14
|
-
fact: Type.String({ description: "A clear, self-contained statement to remember across sessions", minLength: 1 }),
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
type SaveMemoryParams = Static<typeof saveMemorySchema>;
|
|
18
|
-
|
|
19
|
-
export interface SaveMemoryToolDetails {
|
|
20
|
-
fact: string;
|
|
21
|
-
filePath: string;
|
|
22
|
-
duplicate?: boolean;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
interface SaveMemoryRenderArgs {
|
|
26
|
-
fact?: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const MEMORIES_HEADING = "## Memories";
|
|
30
|
-
const MEMORIES_HEADING_RE = /^## Memories\s*$/;
|
|
31
|
-
const NEXT_HEADING_RE = /^## /;
|
|
32
|
-
|
|
33
|
-
async function findNearestAgentsMd(startDir: string): Promise<string | null> {
|
|
34
|
-
let dir = path.resolve(startDir);
|
|
35
|
-
const root = path.parse(dir).root;
|
|
36
|
-
while (true) {
|
|
37
|
-
const candidate = path.join(dir, "AGENTS.md");
|
|
38
|
-
try {
|
|
39
|
-
await Bun.file(candidate).text();
|
|
40
|
-
return candidate;
|
|
41
|
-
} catch (err) {
|
|
42
|
-
if (!isEnoent(err)) throw err;
|
|
43
|
-
}
|
|
44
|
-
const parent = path.dirname(dir);
|
|
45
|
-
if (parent === dir || dir === root) break;
|
|
46
|
-
dir = parent;
|
|
47
|
-
}
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function insertMemory(content: string, fact: string): { content: string; duplicate: boolean } {
|
|
52
|
-
const lines = content.split("\n");
|
|
53
|
-
const bullet = `- ${fact}`;
|
|
54
|
-
|
|
55
|
-
// Find Memories section
|
|
56
|
-
let sectionStart = -1;
|
|
57
|
-
for (let i = 0; i < lines.length; i++) {
|
|
58
|
-
if (MEMORIES_HEADING_RE.test(lines[i])) {
|
|
59
|
-
sectionStart = i;
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (sectionStart === -1) {
|
|
65
|
-
// Append section at end
|
|
66
|
-
const trimmed = content.trimEnd();
|
|
67
|
-
return { content: `${trimmed}\n\n${MEMORIES_HEADING}\n${bullet}\n`, duplicate: false };
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Find section end (next ## heading or EOF)
|
|
71
|
-
let sectionEnd = lines.length;
|
|
72
|
-
for (let i = sectionStart + 1; i < lines.length; i++) {
|
|
73
|
-
if (NEXT_HEADING_RE.test(lines[i])) {
|
|
74
|
-
sectionEnd = i;
|
|
75
|
-
break;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Check duplicates among existing bullets
|
|
80
|
-
const factLower = fact.toLowerCase();
|
|
81
|
-
for (let i = sectionStart + 1; i < sectionEnd; i++) {
|
|
82
|
-
const line = lines[i].trim();
|
|
83
|
-
if (line.startsWith("- ")) {
|
|
84
|
-
const existing = line.slice(2).toLowerCase();
|
|
85
|
-
if (existing.includes(factLower) || factLower.includes(existing)) {
|
|
86
|
-
return { content, duplicate: true };
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Insert bullet before sectionEnd
|
|
92
|
-
lines.splice(sectionEnd, 0, bullet);
|
|
93
|
-
return { content: lines.join("\n"), duplicate: false };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export class SaveMemoryTool implements AgentTool<typeof saveMemorySchema, SaveMemoryToolDetails, Theme> {
|
|
97
|
-
readonly name = "save_memory";
|
|
98
|
-
readonly label = "Save Memory";
|
|
99
|
-
description =
|
|
100
|
-
'Save a fact or preference to long-term memory that persists across sessions. Use when the user explicitly asks to remember something or states a clear preference. Facts should be short, self-contained: "Prefers tabs over spaces", "Project uses pnpm". Do not save transient conversation context. If unsure, ask the user.';
|
|
101
|
-
readonly parameters = saveMemorySchema;
|
|
102
|
-
readonly concurrency = "exclusive";
|
|
103
|
-
|
|
104
|
-
constructor(private readonly session: ToolSession) {}
|
|
105
|
-
|
|
106
|
-
async execute(
|
|
107
|
-
_toolCallId: string,
|
|
108
|
-
params: SaveMemoryParams,
|
|
109
|
-
_signal?: AbortSignal,
|
|
110
|
-
_onUpdate?: AgentToolUpdateCallback<SaveMemoryToolDetails>,
|
|
111
|
-
_context?: AgentToolContext,
|
|
112
|
-
): Promise<AgentToolResult<SaveMemoryToolDetails>> {
|
|
113
|
-
const fact = params.fact.trim();
|
|
114
|
-
if (!fact) {
|
|
115
|
-
return {
|
|
116
|
-
content: [{ type: "text", text: "Fact cannot be empty." }],
|
|
117
|
-
details: { fact: "", filePath: "" },
|
|
118
|
-
};
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
let filePath = await findNearestAgentsMd(this.session.cwd);
|
|
122
|
-
let content: string;
|
|
123
|
-
|
|
124
|
-
if (filePath) {
|
|
125
|
-
content = await Bun.file(filePath).text();
|
|
126
|
-
} else {
|
|
127
|
-
filePath = path.join(this.session.cwd, "AGENTS.md");
|
|
128
|
-
content = "";
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const result = insertMemory(content, fact);
|
|
132
|
-
|
|
133
|
-
if (result.duplicate) {
|
|
134
|
-
return {
|
|
135
|
-
content: [{ type: "text", text: "This fact is already saved." }],
|
|
136
|
-
details: { fact, filePath, duplicate: true },
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
try {
|
|
141
|
-
await Bun.write(filePath, result.content);
|
|
142
|
-
} catch (err) {
|
|
143
|
-
logger.error("Failed to write AGENTS.md", { path: filePath, error: String(err) });
|
|
144
|
-
return {
|
|
145
|
-
content: [{ type: "text", text: "Failed to save memory." }],
|
|
146
|
-
details: { fact, filePath },
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return {
|
|
151
|
-
content: [{ type: "text", text: `Saved to ${filePath}` }],
|
|
152
|
-
details: { fact, filePath },
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
renderCall(args: SaveMemoryRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component {
|
|
157
|
-
const preview = args.fact ? truncateToWidth(args.fact, TRUNCATE_LENGTHS.CONTENT) : "";
|
|
158
|
-
const meta = preview ? [preview] : [];
|
|
159
|
-
const text = renderStatusLine(
|
|
160
|
-
{ icon: "running", spinnerFrame: options.spinnerFrame, title: "Save Memory", meta },
|
|
161
|
-
uiTheme,
|
|
162
|
-
);
|
|
163
|
-
return new Text(text, 0, 0);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
renderResult(
|
|
167
|
-
result: { content: Array<{ type: string; text?: string }>; details?: SaveMemoryToolDetails },
|
|
168
|
-
_options: RenderResultOptions,
|
|
169
|
-
uiTheme: Theme,
|
|
170
|
-
_args?: SaveMemoryRenderArgs,
|
|
171
|
-
): Component {
|
|
172
|
-
const details = result.details;
|
|
173
|
-
const isDuplicate = details?.duplicate === true;
|
|
174
|
-
const icon = isDuplicate ? "info" : "success";
|
|
175
|
-
const filePath = details?.filePath ? shortenPath(details.filePath) : "";
|
|
176
|
-
const meta = filePath ? [filePath] : [];
|
|
177
|
-
const header = renderStatusLine({ icon, title: "Save Memory", meta }, uiTheme);
|
|
178
|
-
|
|
179
|
-
const message = isDuplicate
|
|
180
|
-
? uiTheme.fg("dim", "This fact is already saved.")
|
|
181
|
-
: uiTheme.fg("dim", details?.fact ?? "");
|
|
182
|
-
|
|
183
|
-
return new Text(`${header}\n${message}`, 0, 0);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
File without changes
|