@scitrera/memorylayer-opencode-plugin 0.1.22
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 +178 -0
- package/dist/src/hooks/event.d.ts +15 -0
- package/dist/src/hooks/event.d.ts.map +1 -0
- package/dist/src/hooks/event.js +53 -0
- package/dist/src/hooks/event.js.map +1 -0
- package/dist/src/hooks/message.d.ts +20 -0
- package/dist/src/hooks/message.d.ts.map +1 -0
- package/dist/src/hooks/message.js +128 -0
- package/dist/src/hooks/message.js.map +1 -0
- package/dist/src/hooks/session.d.ts +20 -0
- package/dist/src/hooks/session.d.ts.map +1 -0
- package/dist/src/hooks/session.js +121 -0
- package/dist/src/hooks/session.js.map +1 -0
- package/dist/src/hooks/tool.d.ts +23 -0
- package/dist/src/hooks/tool.d.ts.map +1 -0
- package/dist/src/hooks/tool.js +168 -0
- package/dist/src/hooks/tool.js.map +1 -0
- package/dist/src/index.d.ts +31 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +176 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/shared/client.d.ts +27 -0
- package/dist/src/shared/client.d.ts.map +1 -0
- package/dist/src/shared/client.js +69 -0
- package/dist/src/shared/client.js.map +1 -0
- package/dist/src/shared/formatters.d.ts +33 -0
- package/dist/src/shared/formatters.d.ts.map +1 -0
- package/dist/src/shared/formatters.js +187 -0
- package/dist/src/shared/formatters.js.map +1 -0
- package/dist/src/shared/observation.d.ts +59 -0
- package/dist/src/shared/observation.d.ts.map +1 -0
- package/dist/src/shared/observation.js +406 -0
- package/dist/src/shared/observation.js.map +1 -0
- package/dist/src/shared/state.d.ts +66 -0
- package/dist/src/shared/state.d.ts.map +1 -0
- package/dist/src/shared/state.js +144 -0
- package/dist/src/shared/state.js.map +1 -0
- package/dist/src/shared/types.d.ts +141 -0
- package/dist/src/shared/types.d.ts.map +1 -0
- package/dist/src/shared/types.js +9 -0
- package/dist/src/shared/types.js.map +1 -0
- package/opencode.example.json +15 -0
- package/package.json +52 -0
- package/src/commands/recall.md +55 -0
- package/src/commands/remember.md +37 -0
- package/src/commands/setup.md +47 -0
- package/src/commands/status.md +59 -0
- package/src/hooks/event.ts +63 -0
- package/src/hooks/message.ts +147 -0
- package/src/hooks/session.ts +127 -0
- package/src/hooks/tool.ts +207 -0
- package/src/index.ts +197 -0
- package/src/shared/client.ts +73 -0
- package/src/shared/formatters.ts +237 -0
- package/src/shared/observation.ts +447 -0
- package/src/shared/state.ts +159 -0
- package/src/shared/types.ts +144 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format API responses for injection into OpenCode's context.
|
|
3
|
+
*
|
|
4
|
+
* Uses the same types returned by MemoryLayerClient (the MCP adapter),
|
|
5
|
+
* so hooks and MCP tools share the same data contracts.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Format a single memory for display
|
|
9
|
+
*/
|
|
10
|
+
function formatMemory(memory, index) {
|
|
11
|
+
const lines = [];
|
|
12
|
+
const typeStr = memory.subtype
|
|
13
|
+
? `${memory.type}/${memory.subtype}`
|
|
14
|
+
: memory.type;
|
|
15
|
+
const relevanceStr = memory.relevance_score
|
|
16
|
+
? ` (relevance: ${(memory.relevance_score * 100).toFixed(0)}%)`
|
|
17
|
+
: "";
|
|
18
|
+
lines.push(`${index + 1}. [${typeStr}]${relevanceStr}`);
|
|
19
|
+
lines.push(` ${memory.content}`);
|
|
20
|
+
const tags = memory.tags ?? [];
|
|
21
|
+
if (tags.length > 0) {
|
|
22
|
+
lines.push(` Tags: ${tags.join(", ")}`);
|
|
23
|
+
}
|
|
24
|
+
return lines.join("\n");
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Format recall results for context injection
|
|
28
|
+
*/
|
|
29
|
+
export function formatRecallResult(result, query) {
|
|
30
|
+
if (result.memories.length === 0) {
|
|
31
|
+
return `No memories found matching "${query}".`;
|
|
32
|
+
}
|
|
33
|
+
const totalCount = result.total_count ?? result.memories.length;
|
|
34
|
+
const lines = [
|
|
35
|
+
`Found ${totalCount} memories for "${query}" (showing ${result.memories.length}):`,
|
|
36
|
+
"",
|
|
37
|
+
];
|
|
38
|
+
for (let i = 0; i < result.memories.length; i++) {
|
|
39
|
+
lines.push(formatMemory(result.memories[i], i));
|
|
40
|
+
if (i < result.memories.length - 1) {
|
|
41
|
+
lines.push("");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return lines.join("\n");
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Format briefing for context injection.
|
|
48
|
+
* Accepts the ToolResponse from MemoryLayerClient.getBriefing().
|
|
49
|
+
*/
|
|
50
|
+
export function formatBriefing(briefing) {
|
|
51
|
+
if (!briefing) {
|
|
52
|
+
return "=== Workspace Briefing ===\n\nNo workspace data available.";
|
|
53
|
+
}
|
|
54
|
+
const totalMemories = briefing.total_memories ?? 0;
|
|
55
|
+
const activeTopics = briefing.active_topics ?? [];
|
|
56
|
+
const memoryTypes = briefing.memory_types ?? {};
|
|
57
|
+
const recentActivity = briefing.recent_activity ?? [];
|
|
58
|
+
const lines = [
|
|
59
|
+
"=== Workspace Briefing ===",
|
|
60
|
+
"",
|
|
61
|
+
`Total memories: ${totalMemories}`,
|
|
62
|
+
];
|
|
63
|
+
if (activeTopics.length > 0) {
|
|
64
|
+
lines.push(`Active topics: ${activeTopics.join(", ")}`);
|
|
65
|
+
}
|
|
66
|
+
// Memory type breakdown
|
|
67
|
+
const types = Object.entries(memoryTypes);
|
|
68
|
+
if (types.length > 0) {
|
|
69
|
+
lines.push("");
|
|
70
|
+
lines.push("Memory types:");
|
|
71
|
+
for (const [type, count] of types) {
|
|
72
|
+
lines.push(` - ${type}: ${count}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Recent activity
|
|
76
|
+
if (recentActivity.length > 0) {
|
|
77
|
+
lines.push("");
|
|
78
|
+
lines.push("Recent activity:");
|
|
79
|
+
for (const activity of recentActivity.slice(0, 3)) {
|
|
80
|
+
const date = new Date(activity.timestamp ?? new Date().toISOString()).toLocaleDateString();
|
|
81
|
+
lines.push(` - ${date}: ${activity.summary ?? ""} (${activity.memories_created ?? 0} memories)`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return lines.join("\n");
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Format directive memories specially (high importance user instructions)
|
|
88
|
+
*/
|
|
89
|
+
export function formatDirectives(memories) {
|
|
90
|
+
const directives = memories.filter(m => m.subtype === "directive" || (m.subtype === "preference" && (m.importance ?? 0) >= 0.9));
|
|
91
|
+
if (directives.length === 0) {
|
|
92
|
+
return "";
|
|
93
|
+
}
|
|
94
|
+
const lines = [
|
|
95
|
+
"=== User Directives (must follow) ===",
|
|
96
|
+
"",
|
|
97
|
+
];
|
|
98
|
+
for (const directive of directives) {
|
|
99
|
+
lines.push(`- ${directive.content}`);
|
|
100
|
+
}
|
|
101
|
+
return lines.join("\n");
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Format sandbox state for context injection (post-compaction recovery)
|
|
105
|
+
*/
|
|
106
|
+
export function formatSandboxState(inspectResult) {
|
|
107
|
+
const variableCount = inspectResult.variable_count ?? 0;
|
|
108
|
+
const variables = inspectResult.variables ?? {};
|
|
109
|
+
const lines = [
|
|
110
|
+
"=== Existing Sandbox State (server-side) ===",
|
|
111
|
+
"",
|
|
112
|
+
`The server-side sandbox has ${variableCount} variable(s) from a prior session or before context compaction.`,
|
|
113
|
+
"These variables are live and available for memory_context_exec, memory_context_query, and memory_context_rlm.",
|
|
114
|
+
"",
|
|
115
|
+
];
|
|
116
|
+
for (const [name, info] of Object.entries(variables)) {
|
|
117
|
+
lines.push(` ${name} (${info.type}): ${info.preview}`);
|
|
118
|
+
}
|
|
119
|
+
lines.push("");
|
|
120
|
+
lines.push("Use `memory_context_inspect` for detailed variable inspection.");
|
|
121
|
+
return lines.join("\n");
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Format combined session start output
|
|
125
|
+
*/
|
|
126
|
+
export function formatSessionStart(briefing, directives, topicRecall, topic, sandboxState) {
|
|
127
|
+
const sections = [];
|
|
128
|
+
// Briefing first
|
|
129
|
+
if (briefing) {
|
|
130
|
+
sections.push(formatBriefing(briefing));
|
|
131
|
+
}
|
|
132
|
+
// Directives (high priority)
|
|
133
|
+
const directiveSection = formatDirectives(directives);
|
|
134
|
+
if (directiveSection) {
|
|
135
|
+
sections.push(directiveSection);
|
|
136
|
+
}
|
|
137
|
+
// Topic-specific recall
|
|
138
|
+
if (topicRecall && topicRecall.memories.length > 0 && topic) {
|
|
139
|
+
sections.push(formatRecallResult(topicRecall, topic));
|
|
140
|
+
}
|
|
141
|
+
// Existing sandbox state (post-compaction or resumed session)
|
|
142
|
+
if (sandboxState) {
|
|
143
|
+
sections.push(formatSandboxState(sandboxState));
|
|
144
|
+
}
|
|
145
|
+
// Add session guidance
|
|
146
|
+
const guidance = `=== Session Guidance ===
|
|
147
|
+
|
|
148
|
+
Recalling Memories:
|
|
149
|
+
- Before answering questions about preferences, setup, conventions, or past decisions, call \`memory_recall\` first
|
|
150
|
+
- For broad context gathering, use \`memory_recall\` with relevant keywords
|
|
151
|
+
- Directives (subtype: directive) and preferences (subtype: preference) represent explicit user instructions — prioritize these
|
|
152
|
+
|
|
153
|
+
Storing Memories:
|
|
154
|
+
- \`memory_remember\`: store to long-term memory with type, subtype, and importance
|
|
155
|
+
- \`memory_session_commit\`: checkpoint working memory mid-session (without ending it)
|
|
156
|
+
|
|
157
|
+
Importance Guide: directives/preferences -> 0.9 | decisions/architecture -> 0.7-0.8 | fixes/solutions -> 0.7 | patterns -> 0.5-0.6
|
|
158
|
+
Types: semantic (facts), procedural (how-to), episodic (events), working (current context, auto-expires)
|
|
159
|
+
Subtypes: directive, decision, fix, solution, code_pattern, error, workflow, preference, problem
|
|
160
|
+
|
|
161
|
+
Context Environment (sandbox survives compaction):
|
|
162
|
+
- Load + analyze memories server-side: \`memory_context_load\` -> \`memory_context_query\` or \`memory_context_rlm\`
|
|
163
|
+
- Run computations on loaded data: \`memory_context_exec\` (Python sandbox)
|
|
164
|
+
- After compaction, call \`memory_context_inspect\` to re-orient with existing sandbox variables`;
|
|
165
|
+
sections.push(guidance);
|
|
166
|
+
if (sections.length === 1) {
|
|
167
|
+
// Only guidance, no prior context
|
|
168
|
+
return "MemoryLayer: No prior context found for this session.\n\n" + guidance;
|
|
169
|
+
}
|
|
170
|
+
return sections.join("\n\n");
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Format guidance for storing memories after tool use
|
|
174
|
+
*/
|
|
175
|
+
export function formatStorageGuidance(toolName, isSignificant) {
|
|
176
|
+
if (!isSignificant) {
|
|
177
|
+
return "";
|
|
178
|
+
}
|
|
179
|
+
const guidance = {
|
|
180
|
+
task: "Consider storing exploration/research findings with `memory_remember` (subtype: decision/problem/entity).",
|
|
181
|
+
bash: "If this was a significant git commit, test result, or build output, store with `memory_remember` (subtype: workflow).",
|
|
182
|
+
edit: "If this edit completes a milestone, store with `memory_remember` (type: working, tags: [active-file]).",
|
|
183
|
+
write: "If this file represents a significant deliverable, store with `memory_remember` (type: working, tags: [active-file]).",
|
|
184
|
+
};
|
|
185
|
+
return guidance[toolName] || "";
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=formatters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatters.js","sourceRoot":"","sources":["../../../src/shared/formatters.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observation extraction for auto-capture hooks.
|
|
3
|
+
* Extracts structured observation data from tool usage.
|
|
4
|
+
*
|
|
5
|
+
* Adapted from the CC plugin with OpenCode tool name mappings.
|
|
6
|
+
* OpenCode uses lowercase tool names: bash, edit, write, read, glob, grep,
|
|
7
|
+
* task, webfetch, websearch, multiedit, ls, apply_patch, codesearch, etc.
|
|
8
|
+
*/
|
|
9
|
+
export declare function shouldSkipTool(toolName: string): boolean;
|
|
10
|
+
export type ObservationType = "read" | "write" | "execute" | "search" | "other";
|
|
11
|
+
export interface ObservationData {
|
|
12
|
+
type: ObservationType;
|
|
13
|
+
title: string;
|
|
14
|
+
toolName: string;
|
|
15
|
+
filesRead: string[];
|
|
16
|
+
filesModified: string[];
|
|
17
|
+
facts: string[];
|
|
18
|
+
concepts: string[];
|
|
19
|
+
intent: string | null;
|
|
20
|
+
contentHash: string;
|
|
21
|
+
summary: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Classify tool as read/write/execute/search/other.
|
|
25
|
+
* Uses OpenCode's lowercase tool names.
|
|
26
|
+
*/
|
|
27
|
+
export declare function getObservationType(toolName: string): ObservationType;
|
|
28
|
+
/**
|
|
29
|
+
* Extract file paths from tool args, classified as read or modified
|
|
30
|
+
*/
|
|
31
|
+
export declare function extractFilePaths(toolName: string, toolArgs: Record<string, unknown>): {
|
|
32
|
+
filesRead: string[];
|
|
33
|
+
filesModified: string[];
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Generate observation title from tool usage
|
|
37
|
+
*/
|
|
38
|
+
export declare function generateTitle(toolName: string, toolArgs: Record<string, unknown>): string;
|
|
39
|
+
/**
|
|
40
|
+
* Extract facts from tool args/output
|
|
41
|
+
*/
|
|
42
|
+
export declare function extractFacts(toolName: string, toolArgs: Record<string, unknown>, toolOutput?: string): string[];
|
|
43
|
+
/**
|
|
44
|
+
* Extract concepts/topics from tool usage
|
|
45
|
+
*/
|
|
46
|
+
export declare function extractConcepts(toolName: string, toolArgs: Record<string, unknown>): string[];
|
|
47
|
+
/**
|
|
48
|
+
* Detect intent from tool usage and user prompt
|
|
49
|
+
*/
|
|
50
|
+
export declare function detectIntent(toolName: string, toolArgs: Record<string, unknown>, prompt?: string): string | null;
|
|
51
|
+
/**
|
|
52
|
+
* Compute content hash for deduplication
|
|
53
|
+
*/
|
|
54
|
+
export declare function computeContentHash(...parts: string[]): string;
|
|
55
|
+
/**
|
|
56
|
+
* Build observation from tool execution data
|
|
57
|
+
*/
|
|
58
|
+
export declare function buildObservation(toolName: string, toolArgs: Record<string, unknown>, toolOutput?: string, currentPrompt?: string): ObservationData | null;
|
|
59
|
+
//# sourceMappingURL=observation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observation.d.ts","sourceRoot":"","sources":["../../../src/shared/observation.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAoBH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGxD;AAGD,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEhF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,CAWpE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC;IAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAA;CAAE,CAgClD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAiCzF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,EAAE,CAsDV;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,EAAE,CAwF7F;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,MAAM,CAAC,EAAE,MAAM,GACd,MAAM,GAAG,IAAI,CA0Cf;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAK7D;AAUD;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,UAAU,CAAC,EAAE,MAAM,EACnB,aAAa,CAAC,EAAE,MAAM,GACrB,eAAe,GAAG,IAAI,CAiCxB"}
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observation extraction for auto-capture hooks.
|
|
3
|
+
* Extracts structured observation data from tool usage.
|
|
4
|
+
*
|
|
5
|
+
* Adapted from the CC plugin with OpenCode tool name mappings.
|
|
6
|
+
* OpenCode uses lowercase tool names: bash, edit, write, read, glob, grep,
|
|
7
|
+
* task, webfetch, websearch, multiedit, ls, apply_patch, codesearch, etc.
|
|
8
|
+
*/
|
|
9
|
+
import { createHash } from "crypto";
|
|
10
|
+
// Tools to skip (internal/noisy/self-referential)
|
|
11
|
+
const SKIP_TOOLS = new Set([
|
|
12
|
+
"todo",
|
|
13
|
+
"question",
|
|
14
|
+
"plan",
|
|
15
|
+
"skill",
|
|
16
|
+
"batch",
|
|
17
|
+
]);
|
|
18
|
+
// Also skip any tool matching these prefixes (memory tools)
|
|
19
|
+
const SKIP_PREFIXES = [
|
|
20
|
+
"memorylayer",
|
|
21
|
+
"memory_",
|
|
22
|
+
"mcp__memorylayer",
|
|
23
|
+
];
|
|
24
|
+
export function shouldSkipTool(toolName) {
|
|
25
|
+
if (SKIP_TOOLS.has(toolName))
|
|
26
|
+
return true;
|
|
27
|
+
return SKIP_PREFIXES.some(prefix => toolName.startsWith(prefix));
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Classify tool as read/write/execute/search/other.
|
|
31
|
+
* Uses OpenCode's lowercase tool names.
|
|
32
|
+
*/
|
|
33
|
+
export function getObservationType(toolName) {
|
|
34
|
+
const readTools = ["read", "glob", "grep", "ls", "codesearch"];
|
|
35
|
+
const writeTools = ["write", "edit", "multiedit", "apply_patch"];
|
|
36
|
+
const executeTools = ["bash", "task"];
|
|
37
|
+
const searchTools = ["websearch", "webfetch"];
|
|
38
|
+
if (readTools.includes(toolName))
|
|
39
|
+
return "read";
|
|
40
|
+
if (writeTools.includes(toolName))
|
|
41
|
+
return "write";
|
|
42
|
+
if (executeTools.includes(toolName))
|
|
43
|
+
return "execute";
|
|
44
|
+
if (searchTools.includes(toolName))
|
|
45
|
+
return "search";
|
|
46
|
+
return "other";
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Extract file paths from tool args, classified as read or modified
|
|
50
|
+
*/
|
|
51
|
+
export function extractFilePaths(toolName, toolArgs) {
|
|
52
|
+
const filesRead = [];
|
|
53
|
+
const filesModified = [];
|
|
54
|
+
try {
|
|
55
|
+
const filePath = (toolArgs.file_path || toolArgs.path || toolArgs.file || "");
|
|
56
|
+
if (!filePath) {
|
|
57
|
+
// Try to extract paths from bash commands
|
|
58
|
+
if (toolName === "bash") {
|
|
59
|
+
const command = (toolArgs.command || "");
|
|
60
|
+
const pathMatches = command.match(/(?:^|\s)([^\s]+\.(ts|js|py|json|yaml|yml|md|txt|go|rs|java|c|cpp|h))\b/gi);
|
|
61
|
+
if (pathMatches) {
|
|
62
|
+
filesRead.push(...pathMatches.map((p) => p.trim()));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { filesRead, filesModified };
|
|
66
|
+
}
|
|
67
|
+
const type = getObservationType(toolName);
|
|
68
|
+
if (type === "write") {
|
|
69
|
+
filesModified.push(filePath);
|
|
70
|
+
}
|
|
71
|
+
else if (type === "read") {
|
|
72
|
+
filesRead.push(filePath);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// Ignore parse errors
|
|
77
|
+
}
|
|
78
|
+
return { filesRead, filesModified };
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Generate observation title from tool usage
|
|
82
|
+
*/
|
|
83
|
+
export function generateTitle(toolName, toolArgs) {
|
|
84
|
+
try {
|
|
85
|
+
switch (toolName) {
|
|
86
|
+
case "read":
|
|
87
|
+
return `Read ${toolArgs.file_path || toolArgs.path || "file"}`;
|
|
88
|
+
case "write":
|
|
89
|
+
return `Write ${toolArgs.file_path || toolArgs.path || "file"}`;
|
|
90
|
+
case "edit":
|
|
91
|
+
case "multiedit":
|
|
92
|
+
return `Edit ${toolArgs.file_path || toolArgs.path || "file"}`;
|
|
93
|
+
case "apply_patch":
|
|
94
|
+
return `Patch ${toolArgs.file_path || toolArgs.path || "file"}`;
|
|
95
|
+
case "bash": {
|
|
96
|
+
const cmd = (toolArgs.command || "");
|
|
97
|
+
return `Run: ${cmd.substring(0, 50)}${cmd.length > 50 ? "..." : ""}`;
|
|
98
|
+
}
|
|
99
|
+
case "glob":
|
|
100
|
+
return `Find ${toolArgs.pattern || "files"}`;
|
|
101
|
+
case "grep":
|
|
102
|
+
case "codesearch":
|
|
103
|
+
return `Search "${toolArgs.pattern || ""}"`;
|
|
104
|
+
case "task":
|
|
105
|
+
return `Task: ${toolArgs.description || "agent"}`;
|
|
106
|
+
case "websearch":
|
|
107
|
+
return `Search: ${toolArgs.query || ""}`;
|
|
108
|
+
case "webfetch":
|
|
109
|
+
return `Fetch: ${toolArgs.url || ""}`;
|
|
110
|
+
default:
|
|
111
|
+
return toolName;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return toolName;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Extract facts from tool args/output
|
|
120
|
+
*/
|
|
121
|
+
export function extractFacts(toolName, toolArgs, toolOutput) {
|
|
122
|
+
const facts = [];
|
|
123
|
+
try {
|
|
124
|
+
const filePath = (toolArgs.file_path || toolArgs.path || toolArgs.file || "");
|
|
125
|
+
switch (toolName) {
|
|
126
|
+
case "read":
|
|
127
|
+
if (filePath)
|
|
128
|
+
facts.push(`File read: ${filePath}`);
|
|
129
|
+
break;
|
|
130
|
+
case "write":
|
|
131
|
+
if (filePath)
|
|
132
|
+
facts.push(`File created/updated: ${filePath}`);
|
|
133
|
+
break;
|
|
134
|
+
case "edit":
|
|
135
|
+
case "multiedit":
|
|
136
|
+
case "apply_patch":
|
|
137
|
+
if (filePath)
|
|
138
|
+
facts.push(`File modified: ${filePath}`);
|
|
139
|
+
if (toolArgs.old_string) {
|
|
140
|
+
facts.push(`Code replaced in ${filePath.split(/[/\\]/).pop() || "file"}`);
|
|
141
|
+
}
|
|
142
|
+
break;
|
|
143
|
+
case "bash": {
|
|
144
|
+
const cmd = (toolArgs.command || "");
|
|
145
|
+
facts.push(`Command executed: ${cmd.substring(0, 100)}`);
|
|
146
|
+
if (toolOutput) {
|
|
147
|
+
if (toolOutput.includes("passed") || toolOutput.includes("\u2713"))
|
|
148
|
+
facts.push("Tests passed");
|
|
149
|
+
if (toolOutput.includes("failed") || toolOutput.includes("\u2717"))
|
|
150
|
+
facts.push("Tests failed");
|
|
151
|
+
if (toolOutput.includes("error") || toolOutput.includes("Error"))
|
|
152
|
+
facts.push("Errors encountered");
|
|
153
|
+
}
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
case "glob":
|
|
157
|
+
if (toolArgs.pattern)
|
|
158
|
+
facts.push(`Pattern searched: ${toolArgs.pattern}`);
|
|
159
|
+
break;
|
|
160
|
+
case "grep":
|
|
161
|
+
case "codesearch":
|
|
162
|
+
if (toolArgs.pattern)
|
|
163
|
+
facts.push(`Code pattern searched: ${toolArgs.pattern}`);
|
|
164
|
+
if (toolArgs.path)
|
|
165
|
+
facts.push(`Search scope: ${toolArgs.path}`);
|
|
166
|
+
break;
|
|
167
|
+
case "websearch":
|
|
168
|
+
if (toolArgs.query)
|
|
169
|
+
facts.push(`Web search: ${toolArgs.query}`);
|
|
170
|
+
break;
|
|
171
|
+
case "webfetch":
|
|
172
|
+
if (toolArgs.url)
|
|
173
|
+
facts.push(`URL fetched: ${toolArgs.url}`);
|
|
174
|
+
break;
|
|
175
|
+
case "task":
|
|
176
|
+
if (toolArgs.description)
|
|
177
|
+
facts.push(`Sub-task: ${toolArgs.description}`);
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// Ignore parse errors
|
|
183
|
+
}
|
|
184
|
+
return facts;
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Extract concepts/topics from tool usage
|
|
188
|
+
*/
|
|
189
|
+
export function extractConcepts(toolName, toolArgs) {
|
|
190
|
+
const concepts = new Set();
|
|
191
|
+
try {
|
|
192
|
+
const filePath = (toolArgs.file_path || toolArgs.path || toolArgs.file || "");
|
|
193
|
+
// Extract concepts from file paths
|
|
194
|
+
if (filePath) {
|
|
195
|
+
const parts = filePath.split(/[/\\]/);
|
|
196
|
+
for (const part of parts) {
|
|
197
|
+
if (["src", "lib", "dist", "node_modules", ".", ".."].includes(part))
|
|
198
|
+
continue;
|
|
199
|
+
if (part.includes(".")) {
|
|
200
|
+
const ext = part.split(".").pop();
|
|
201
|
+
const extMap = {
|
|
202
|
+
ts: "typescript", tsx: "react", js: "javascript", jsx: "react",
|
|
203
|
+
py: "python", rs: "rust", go: "golang", css: "styling", scss: "styling",
|
|
204
|
+
html: "html", json: "configuration", yaml: "configuration", yml: "configuration",
|
|
205
|
+
md: "documentation", test: "testing", spec: "testing", sql: "database",
|
|
206
|
+
};
|
|
207
|
+
if (ext && extMap[ext])
|
|
208
|
+
concepts.add(extMap[ext]);
|
|
209
|
+
}
|
|
210
|
+
const dirMap = {
|
|
211
|
+
tests: "testing", __tests__: "testing", test: "testing", spec: "testing",
|
|
212
|
+
hooks: "hooks", api: "api", auth: "authentication", db: "database",
|
|
213
|
+
components: "components", pages: "pages", routes: "routing", utils: "utilities",
|
|
214
|
+
services: "services", middleware: "middleware", models: "models", types: "types",
|
|
215
|
+
cli: "cli", config: "configuration", migrations: "database", schemas: "schemas",
|
|
216
|
+
};
|
|
217
|
+
if (dirMap[part])
|
|
218
|
+
concepts.add(dirMap[part]);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Extract function/class names from edit tools
|
|
222
|
+
if (toolName === "edit" || toolName === "multiedit") {
|
|
223
|
+
const oldStr = (toolArgs.old_string || "");
|
|
224
|
+
const newStr = (toolArgs.new_string || "");
|
|
225
|
+
const combined = oldStr + "\n" + newStr;
|
|
226
|
+
const funcMatches = combined.match(/(?:function|async function|const|let|var)\s+(\w{3,})/g);
|
|
227
|
+
if (funcMatches) {
|
|
228
|
+
for (const m of funcMatches.slice(0, 3)) {
|
|
229
|
+
const name = m.replace(/(?:function|async function|const|let|var)\s+/, "");
|
|
230
|
+
concepts.add(`fn:${name}`);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const classMatches = combined.match(/class\s+(\w{3,})/g);
|
|
234
|
+
if (classMatches) {
|
|
235
|
+
for (const m of classMatches.slice(0, 2)) {
|
|
236
|
+
concepts.add(`class:${m.replace("class ", "")}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (/\bimport\b/.test(combined))
|
|
240
|
+
concepts.add("pattern:import");
|
|
241
|
+
if (/\bexport\b/.test(combined))
|
|
242
|
+
concepts.add("pattern:export");
|
|
243
|
+
if (/\binterface\b/.test(combined))
|
|
244
|
+
concepts.add("pattern:interface");
|
|
245
|
+
if (/\benum\b/.test(combined))
|
|
246
|
+
concepts.add("pattern:enum");
|
|
247
|
+
if (/\btry\s*\{/.test(combined))
|
|
248
|
+
concepts.add("pattern:error-handling");
|
|
249
|
+
if (/\basync\b/.test(combined))
|
|
250
|
+
concepts.add("pattern:async");
|
|
251
|
+
}
|
|
252
|
+
// Tool-based concepts
|
|
253
|
+
switch (toolName) {
|
|
254
|
+
case "bash": {
|
|
255
|
+
const cmd = (toolArgs.command || "");
|
|
256
|
+
if (cmd.includes("test") || cmd.includes("vitest") || cmd.includes("jest"))
|
|
257
|
+
concepts.add("testing");
|
|
258
|
+
if (cmd.includes("build") || cmd.includes("tsc"))
|
|
259
|
+
concepts.add("build");
|
|
260
|
+
if (cmd.includes("git"))
|
|
261
|
+
concepts.add("version-control");
|
|
262
|
+
if (cmd.includes("npm") || cmd.includes("yarn") || cmd.includes("pnpm") || cmd.includes("bun"))
|
|
263
|
+
concepts.add("package-management");
|
|
264
|
+
if (cmd.includes("docker"))
|
|
265
|
+
concepts.add("containerization");
|
|
266
|
+
if (cmd.includes("lint") || cmd.includes("eslint") || cmd.includes("biome"))
|
|
267
|
+
concepts.add("linting");
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
case "websearch":
|
|
271
|
+
concepts.add("research");
|
|
272
|
+
break;
|
|
273
|
+
case "webfetch":
|
|
274
|
+
concepts.add("web-content");
|
|
275
|
+
break;
|
|
276
|
+
case "task":
|
|
277
|
+
concepts.add("delegation");
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
// Ignore parse errors
|
|
283
|
+
}
|
|
284
|
+
return Array.from(concepts);
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Detect intent from tool usage and user prompt
|
|
288
|
+
*/
|
|
289
|
+
export function detectIntent(toolName, toolArgs, prompt) {
|
|
290
|
+
const intentPatterns = {
|
|
291
|
+
bugfix: /fix|bug|error|issue|broken|crash|repair/i,
|
|
292
|
+
feature: /add|feature|implement|create|new|build/i,
|
|
293
|
+
refactor: /refactor|clean|rename|reorganize|restructure/i,
|
|
294
|
+
testing: /test|spec|coverage|verify/i,
|
|
295
|
+
investigation: /find|search|investigate|debug|analyze|explore/i,
|
|
296
|
+
documentation: /document|comment|readme|doc|explain/i,
|
|
297
|
+
};
|
|
298
|
+
if (prompt) {
|
|
299
|
+
for (const [intent, pattern] of Object.entries(intentPatterns)) {
|
|
300
|
+
if (pattern.test(prompt)) {
|
|
301
|
+
return intent;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
try {
|
|
306
|
+
const inputStr = JSON.stringify(toolArgs);
|
|
307
|
+
for (const [intent, pattern] of Object.entries(intentPatterns)) {
|
|
308
|
+
if (pattern.test(inputStr)) {
|
|
309
|
+
return intent;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
// Ignore parse errors
|
|
315
|
+
}
|
|
316
|
+
switch (toolName) {
|
|
317
|
+
case "read":
|
|
318
|
+
case "glob":
|
|
319
|
+
case "grep":
|
|
320
|
+
case "codesearch":
|
|
321
|
+
return "investigation";
|
|
322
|
+
case "write":
|
|
323
|
+
return "feature";
|
|
324
|
+
case "edit":
|
|
325
|
+
return null; // Too ambiguous without context
|
|
326
|
+
default:
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Compute content hash for deduplication
|
|
332
|
+
*/
|
|
333
|
+
export function computeContentHash(...parts) {
|
|
334
|
+
return createHash("sha256")
|
|
335
|
+
.update(parts.join("|"))
|
|
336
|
+
.digest("hex")
|
|
337
|
+
.slice(0, 16);
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Truncate string to max length
|
|
341
|
+
*/
|
|
342
|
+
function truncate(str, maxLen = 500) {
|
|
343
|
+
if (str.length <= maxLen)
|
|
344
|
+
return str;
|
|
345
|
+
return str.substring(0, maxLen) + "...";
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Build observation from tool execution data
|
|
349
|
+
*/
|
|
350
|
+
export function buildObservation(toolName, toolArgs, toolOutput, currentPrompt) {
|
|
351
|
+
const type = getObservationType(toolName);
|
|
352
|
+
const title = generateTitle(toolName, toolArgs);
|
|
353
|
+
const { filesRead, filesModified } = extractFilePaths(toolName, toolArgs);
|
|
354
|
+
const facts = extractFacts(toolName, toolArgs, toolOutput);
|
|
355
|
+
const concepts = extractConcepts(toolName, toolArgs);
|
|
356
|
+
const intent = detectIntent(toolName, toolArgs, currentPrompt);
|
|
357
|
+
const summary = buildSummary(toolName, toolArgs, toolOutput, filesRead, filesModified);
|
|
358
|
+
const contentHash = computeContentHash(toolName, JSON.stringify(toolArgs), summary);
|
|
359
|
+
// Skip if empty observation (no meaningful data extracted)
|
|
360
|
+
if (filesRead.length === 0 && filesModified.length === 0 && facts.length === 0 && concepts.length === 0) {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
return {
|
|
364
|
+
type,
|
|
365
|
+
title,
|
|
366
|
+
toolName,
|
|
367
|
+
filesRead,
|
|
368
|
+
filesModified,
|
|
369
|
+
facts,
|
|
370
|
+
concepts,
|
|
371
|
+
intent,
|
|
372
|
+
contentHash,
|
|
373
|
+
summary,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Build human-readable summary
|
|
378
|
+
*/
|
|
379
|
+
function buildSummary(toolName, toolArgs, _toolOutput, filesRead, filesModified) {
|
|
380
|
+
const parts = [];
|
|
381
|
+
if (filesRead.length > 0) {
|
|
382
|
+
parts.push(`Read: ${filesRead.join(", ")}`);
|
|
383
|
+
}
|
|
384
|
+
if (filesModified.length > 0) {
|
|
385
|
+
parts.push(`Modified: ${filesModified.join(", ")}`);
|
|
386
|
+
}
|
|
387
|
+
try {
|
|
388
|
+
if (toolName === "bash") {
|
|
389
|
+
const cmd = (toolArgs.command || "");
|
|
390
|
+
parts.push(`Command: ${truncate(cmd, 100)}`);
|
|
391
|
+
}
|
|
392
|
+
else if (toolName === "grep" || toolName === "codesearch") {
|
|
393
|
+
const pattern = (toolArgs.pattern || "");
|
|
394
|
+
parts.push(`Pattern: ${pattern}`);
|
|
395
|
+
}
|
|
396
|
+
else if (toolName === "task") {
|
|
397
|
+
const desc = (toolArgs.description || "");
|
|
398
|
+
parts.push(`Task: ${truncate(desc, 100)}`);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
catch {
|
|
402
|
+
// Ignore
|
|
403
|
+
}
|
|
404
|
+
return parts.length > 0 ? parts.join("; ") : `Used ${toolName}`;
|
|
405
|
+
}
|
|
406
|
+
//# sourceMappingURL=observation.js.map
|