wave-agent-sdk 0.0.17-alpha.0 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent.d.ts +2 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +23 -2
- package/dist/constants/prompts.d.ts +1 -0
- package/dist/constants/prompts.d.ts.map +1 -1
- package/dist/constants/prompts.js +21 -0
- package/dist/managers/MemoryRuleManager.d.ts +31 -0
- package/dist/managers/MemoryRuleManager.d.ts.map +1 -0
- package/dist/managers/MemoryRuleManager.js +110 -0
- package/dist/managers/messageManager.d.ts +10 -0
- package/dist/managers/messageManager.d.ts.map +1 -1
- package/dist/managers/messageManager.js +61 -0
- package/dist/managers/slashCommandManager.d.ts.map +1 -1
- package/dist/managers/slashCommandManager.js +16 -0
- package/dist/services/MemoryRuleService.d.ts +12 -0
- package/dist/services/MemoryRuleService.d.ts.map +1 -0
- package/dist/services/MemoryRuleService.js +44 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/memoryRule.d.ts +31 -0
- package/dist/types/memoryRule.d.ts.map +1 -0
- package/dist/types/memoryRule.js +1 -0
- package/dist/utils/constants.d.ts +1 -1
- package/dist/utils/constants.js +1 -1
- package/dist/utils/markdownParser.d.ts +7 -0
- package/dist/utils/markdownParser.d.ts.map +1 -1
- package/dist/utils/markdownParser.js +13 -3
- package/package.json +1 -1
- package/src/agent.ts +27 -2
- package/src/constants/prompts.ts +22 -0
- package/src/managers/MemoryRuleManager.ts +148 -0
- package/src/managers/messageManager.ts +67 -0
- package/src/managers/slashCommandManager.ts +19 -0
- package/src/services/MemoryRuleService.ts +59 -0
- package/src/types/index.ts +1 -0
- package/src/types/memoryRule.ts +31 -0
- package/src/utils/constants.ts +1 -1
- package/src/utils/markdownParser.ts +16 -3
package/dist/agent.d.ts
CHANGED
|
@@ -61,6 +61,7 @@ export declare class Agent {
|
|
|
61
61
|
private pluginManager;
|
|
62
62
|
private skillManager;
|
|
63
63
|
private hookManager;
|
|
64
|
+
private memoryRuleManager;
|
|
64
65
|
private liveConfigManager;
|
|
65
66
|
private configurationService;
|
|
66
67
|
private workdir;
|
|
@@ -116,7 +117,7 @@ export declare class Agent {
|
|
|
116
117
|
get projectMemory(): string;
|
|
117
118
|
/** Get user memory content */
|
|
118
119
|
get userMemory(): string;
|
|
119
|
-
/** Get combined memory content (project + user) */
|
|
120
|
+
/** Get combined memory content (project + user + modular rules) */
|
|
120
121
|
get combinedMemory(): string;
|
|
121
122
|
/** Get AI loading status */
|
|
122
123
|
get isLoading(): boolean;
|
package/dist/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAEL,KAAK,wBAAwB,EAC9B,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAAc,KAAK,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAGhF,OAAO,EAEL,KAAK,8BAA8B,EACpC,MAAM,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
|
package/dist/agent.js
CHANGED
|
@@ -9,9 +9,10 @@ import { BashManager } from "./managers/bashManager.js";
|
|
|
9
9
|
import { BackgroundBashManager, } from "./managers/backgroundBashManager.js";
|
|
10
10
|
import { SlashCommandManager } from "./managers/slashCommandManager.js";
|
|
11
11
|
import { PluginManager } from "./managers/pluginManager.js";
|
|
12
|
+
import { HookManager } from "./managers/hookManager.js";
|
|
12
13
|
import { PermissionManager } from "./managers/permissionManager.js";
|
|
13
14
|
import { PlanManager } from "./managers/planManager.js";
|
|
14
|
-
import {
|
|
15
|
+
import { MemoryRuleManager } from "./managers/MemoryRuleManager.js";
|
|
15
16
|
import { LiveConfigManager } from "./managers/liveConfigManager.js";
|
|
16
17
|
import { configValidator } from "./utils/configValidator.js";
|
|
17
18
|
import { SkillManager } from "./managers/skillManager.js";
|
|
@@ -118,6 +119,10 @@ export class Agent {
|
|
|
118
119
|
workdir: this.workdir,
|
|
119
120
|
logger: this.logger,
|
|
120
121
|
});
|
|
122
|
+
// Initialize memory rule manager
|
|
123
|
+
this.memoryRuleManager = new MemoryRuleManager({
|
|
124
|
+
workdir: this.workdir,
|
|
125
|
+
});
|
|
121
126
|
// Create a wrapper for canUseTool that triggers notification hooks
|
|
122
127
|
const canUseToolWithNotification = options.canUseTool
|
|
123
128
|
? async (context) => {
|
|
@@ -295,7 +300,7 @@ export class Agent {
|
|
|
295
300
|
get userMemory() {
|
|
296
301
|
return this._userMemoryContent;
|
|
297
302
|
}
|
|
298
|
-
/** Get combined memory content (project + user) */
|
|
303
|
+
/** Get combined memory content (project + user + modular rules) */
|
|
299
304
|
get combinedMemory() {
|
|
300
305
|
let combined = "";
|
|
301
306
|
if (this._projectMemoryContent.trim()) {
|
|
@@ -307,6 +312,15 @@ export class Agent {
|
|
|
307
312
|
}
|
|
308
313
|
combined += this._userMemoryContent;
|
|
309
314
|
}
|
|
315
|
+
// Add modular memory rules
|
|
316
|
+
const filesInContext = this.messageManager.getFilesInContext();
|
|
317
|
+
const activeRules = this.memoryRuleManager.getActiveRules(filesInContext);
|
|
318
|
+
if (activeRules.length > 0) {
|
|
319
|
+
if (combined) {
|
|
320
|
+
combined += "\n\n";
|
|
321
|
+
}
|
|
322
|
+
combined += activeRules.map((r) => r.content).join("\n\n");
|
|
323
|
+
}
|
|
310
324
|
return combined;
|
|
311
325
|
}
|
|
312
326
|
/** Get AI loading status */
|
|
@@ -443,6 +457,13 @@ export class Agent {
|
|
|
443
457
|
}
|
|
444
458
|
// Resolve and validate configuration after loading settings.json
|
|
445
459
|
this.resolveAndValidateConfig();
|
|
460
|
+
// Discover modular memory rules
|
|
461
|
+
try {
|
|
462
|
+
await this.memoryRuleManager.discoverRules();
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
this.logger?.error("Failed to discover memory rules:", error);
|
|
466
|
+
}
|
|
446
467
|
// Set global logger for SDK-wide access after validation
|
|
447
468
|
setGlobalLogger(this.logger || null);
|
|
448
469
|
// Initialize live configuration reload
|
|
@@ -5,6 +5,7 @@ export declare const SUBAGENT_POLICY = "\n- When doing file search, prefer to us
|
|
|
5
5
|
export declare const FILE_TOOL_POLICY = "\n- Use specialized tools instead of bash commands when possible, as this provides a better user experience. For file operations, use dedicated tools: Read for reading files instead of cat/head/tail, Edit/MultiEdit for editing instead of sed/awk, Write for creating files instead of cat with heredoc or echo redirection, and LS/Glob/Grep for searching and listing files instead of find/ls/grep.";
|
|
6
6
|
export declare const BASH_POLICY = "\n- Reserve bash tools exclusively for actual system commands and terminal operations that require shell execution. NEVER use bash echo or other command-line tools to communicate thoughts, explanations, or instructions to the user. Output all communication directly in your response text instead.\n- When making multiple bash tool calls, you MUST send a single message with multiple tools calls to run the calls in parallel. For example, if you need to run \"git status\" and \"git diff\", send a single message with two tool calls to run the calls in parallel.";
|
|
7
7
|
export declare const DEFAULT_SYSTEM_PROMPT = "You are an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.\n\n# Doing tasks\nThe user will primarily request you perform software engineering tasks. This includes solving bugs, adding new functionality, refactoring code, explaining code, and more. For these tasks the following steps are recommended:\n- NEVER propose changes to code you haven't read. If a user asks about or wants you to modify a file, read it first. Understand existing code before suggesting modifications.\n- Be careful not to introduce security vulnerabilities such as command injection, XSS, SQL injection, and other OWASP top 10 vulnerabilities. If you notice that you wrote insecure code, immediately fix it.\n- Avoid over-engineering. Only make changes that are directly requested or clearly necessary. Keep solutions simple and focused.\n - Don't add features, refactor code, or make \"improvements\" beyond what was asked. A bug fix doesn't need surrounding code cleaned up. A simple feature doesn't need extra configurability. Don't add docstrings, comments, or type annotations to code you didn't change. Only add comments where the logic isn't self-evident.\n - Don't add error handling, fallbacks, or validation for scenarios that can't happen. Trust internal code and framework guarantees. Only validate at system boundaries (user input, external APIs). Don't use feature flags or backwards-compatibility shims when you can just change the code.\n - Don't create helpers, utilities, or abstractions for one-time operations. Don't design for hypothetical future requirements. The right amount of complexity is the minimum needed for the current task\u2014three similar lines of code is better than a premature abstraction.\n- Avoid backwards-compatibility hacks like renaming unused `_vars`, re-exporting types, adding `// removed` comments for removed code, etc. If something is unused, delete it completely.\n\n# Tool usage policy\n- You can call multiple tools in a single response. If you intend to call multiple tools and there are no dependencies between them, make all independent tool calls in parallel. Maximize use of parallel tool calls where possible to increase efficiency.\n- However, if some tool calls depend on previous calls to inform dependent values, do NOT call these tools in parallel and instead call them sequentially. For instance, if one operation must complete before another starts, run these operations sequentially instead. Never use placeholders or guess missing parameters in tool calls.\n- If the user specifies that they want you to run tools \"in parallel\", you MUST send a single message with multiple tool use content blocks.";
|
|
8
|
+
export declare const INIT_PROMPT = "Please analyze this codebase and create a AGENTS.md file, which will be given to future instances of Wave Code to operate in this repository.\n\nWhat to add:\n1. Commands that will be commonly used, such as how to build, lint, and run tests. Include the necessary commands to develop in this codebase, such as how to run a single test.\n2. High-level code architecture and structure so that future instances can be productive more quickly. Focus on the \"big picture\" architecture that requires reading multiple files to understand.\n\nUsage notes:\n- If there's already a AGENTS.md, suggest improvements to it.\n- When you make the initial AGENTS.md, do not repeat yourself and do not include obvious instructions like \"Provide helpful error messages to users\", \"Write unit tests for all new utilities\", \"Never include sensitive information (API keys, tokens) in code or commits\".\n- Avoid listing every component or file structure that can be easily discovered.\n- Don't include generic development practices.\n- If there are Cursor rules (in .cursor/rules/ or .cursorrules) or Copilot rules (in .github/copilot-instructions.md), make sure to include the important parts.\n- If there is a README.md, make sure to include the important parts.\n- Do not make up information such as \"Common Development Tasks\", \"Tips for Development\", \"Support and Documentation\" unless this is expressly included in other files that you read.\n- Be sure to prefix the file with the following text:\n\n```\n# AGENTS.md\n\nThis file provides guidance to Wave Code when working with code in this repository.\n```";
|
|
8
9
|
export declare function buildSystemPrompt(basePrompt: string, tools: {
|
|
9
10
|
name?: string;
|
|
10
11
|
function?: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/constants/prompts.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,kBAAkB,ksFAe8G,CAAC;AAE9I,eAAO,MAAM,sBAAsB,+lBAImH,CAAC;AAEvJ,eAAO,MAAM,eAAe,mVAEgS,CAAC;AAE7T,eAAO,MAAM,eAAe,meAG0P,CAAC;AAEvR,eAAO,MAAM,gBAAgB,+YACwc,CAAC;AAEte,eAAO,MAAM,WAAW,sjBAE4O,CAAC;AAErQ,eAAO,MAAM,qBAAqB,ksFAAqB,CAAC;AAExD,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,EAAE,GACtD,MAAM,CAkCR"}
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/constants/prompts.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,kBAAkB,ksFAe8G,CAAC;AAE9I,eAAO,MAAM,sBAAsB,+lBAImH,CAAC;AAEvJ,eAAO,MAAM,eAAe,mVAEgS,CAAC;AAE7T,eAAO,MAAM,eAAe,meAG0P,CAAC;AAEvR,eAAO,MAAM,gBAAgB,+YACwc,CAAC;AAEte,eAAO,MAAM,WAAW,sjBAE4O,CAAC;AAErQ,eAAO,MAAM,qBAAqB,ksFAAqB,CAAC;AAExD,eAAO,MAAM,WAAW,0kDAoBjB,CAAC;AAER,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,EAAE,GACtD,MAAM,CAkCR"}
|
|
@@ -33,6 +33,27 @@ export const BASH_POLICY = `
|
|
|
33
33
|
- Reserve bash tools exclusively for actual system commands and terminal operations that require shell execution. NEVER use bash echo or other command-line tools to communicate thoughts, explanations, or instructions to the user. Output all communication directly in your response text instead.
|
|
34
34
|
- When making multiple bash tool calls, you MUST send a single message with multiple tools calls to run the calls in parallel. For example, if you need to run "git status" and "git diff", send a single message with two tool calls to run the calls in parallel.`;
|
|
35
35
|
export const DEFAULT_SYSTEM_PROMPT = BASE_SYSTEM_PROMPT;
|
|
36
|
+
export const INIT_PROMPT = `Please analyze this codebase and create a AGENTS.md file, which will be given to future instances of Wave Code to operate in this repository.
|
|
37
|
+
|
|
38
|
+
What to add:
|
|
39
|
+
1. Commands that will be commonly used, such as how to build, lint, and run tests. Include the necessary commands to develop in this codebase, such as how to run a single test.
|
|
40
|
+
2. High-level code architecture and structure so that future instances can be productive more quickly. Focus on the "big picture" architecture that requires reading multiple files to understand.
|
|
41
|
+
|
|
42
|
+
Usage notes:
|
|
43
|
+
- If there's already a AGENTS.md, suggest improvements to it.
|
|
44
|
+
- When you make the initial AGENTS.md, do not repeat yourself and do not include obvious instructions like "Provide helpful error messages to users", "Write unit tests for all new utilities", "Never include sensitive information (API keys, tokens) in code or commits".
|
|
45
|
+
- Avoid listing every component or file structure that can be easily discovered.
|
|
46
|
+
- Don't include generic development practices.
|
|
47
|
+
- If there are Cursor rules (in .cursor/rules/ or .cursorrules) or Copilot rules (in .github/copilot-instructions.md), make sure to include the important parts.
|
|
48
|
+
- If there is a README.md, make sure to include the important parts.
|
|
49
|
+
- Do not make up information such as "Common Development Tasks", "Tips for Development", "Support and Documentation" unless this is expressly included in other files that you read.
|
|
50
|
+
- Be sure to prefix the file with the following text:
|
|
51
|
+
|
|
52
|
+
\`\`\`
|
|
53
|
+
# AGENTS.md
|
|
54
|
+
|
|
55
|
+
This file provides guidance to Wave Code when working with code in this repository.
|
|
56
|
+
\`\`\``;
|
|
36
57
|
export function buildSystemPrompt(basePrompt, tools) {
|
|
37
58
|
let prompt = basePrompt;
|
|
38
59
|
const toolNames = new Set(tools.map((t) => t.function?.name || t.name).filter(Boolean));
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { MemoryRule } from "../types/memoryRule.js";
|
|
2
|
+
export interface MemoryRuleRegistryState {
|
|
3
|
+
/** All discovered rules, indexed by ID */
|
|
4
|
+
rules: Record<string, MemoryRule>;
|
|
5
|
+
/** Set of active rule IDs based on the current context */
|
|
6
|
+
activeRuleIds: Set<string>;
|
|
7
|
+
}
|
|
8
|
+
export interface MemoryRuleManagerOptions {
|
|
9
|
+
workdir: string;
|
|
10
|
+
}
|
|
11
|
+
export declare class MemoryRuleManager {
|
|
12
|
+
private state;
|
|
13
|
+
private workdir;
|
|
14
|
+
private service;
|
|
15
|
+
constructor(options: MemoryRuleManagerOptions);
|
|
16
|
+
/**
|
|
17
|
+
* Scans .wave/rules and ~/.wave/rules for memory rule files.
|
|
18
|
+
*/
|
|
19
|
+
discoverRules(): Promise<void>;
|
|
20
|
+
private scanDirectory;
|
|
21
|
+
private loadRuleFile;
|
|
22
|
+
/**
|
|
23
|
+
* Returns the union of all active memory rules based on the provided file paths.
|
|
24
|
+
*/
|
|
25
|
+
getActiveRules(filesInContext: string[]): MemoryRule[];
|
|
26
|
+
/**
|
|
27
|
+
* Reloads rules from disk.
|
|
28
|
+
*/
|
|
29
|
+
reload(): Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=MemoryRuleManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MemoryRuleManager.d.ts","sourceRoot":"","sources":["../../src/managers/MemoryRuleManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAOzD,MAAM,WAAW,uBAAuB;IACtC,0CAA0C;IAC1C,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClC,0DAA0D;IAC1D,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,KAAK,CAGX;IAEF,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAoB;gBAEvB,OAAO,EAAE,wBAAwB;IAK7C;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;YAetB,aAAa;YAyDb,YAAY;IAqB1B;;OAEG;IACH,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,UAAU,EAAE;IAUtD;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;CAG9B"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { MemoryRuleService } from "../services/MemoryRuleService.js";
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import * as os from "node:os";
|
|
5
|
+
import { logger } from "../utils/globalLogger.js";
|
|
6
|
+
export class MemoryRuleManager {
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.state = {
|
|
9
|
+
rules: {},
|
|
10
|
+
activeRuleIds: new Set(),
|
|
11
|
+
};
|
|
12
|
+
this.workdir = options.workdir;
|
|
13
|
+
this.service = new MemoryRuleService();
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Scans .wave/rules and ~/.wave/rules for memory rule files.
|
|
17
|
+
*/
|
|
18
|
+
async discoverRules() {
|
|
19
|
+
const projectRulesDir = path.join(this.workdir, ".wave", "rules");
|
|
20
|
+
const userRulesDir = path.join(os.homedir(), ".wave", "rules");
|
|
21
|
+
const newRules = {};
|
|
22
|
+
// Discover user rules first, then project rules so project rules can override if needed
|
|
23
|
+
// (though IDs are based on file path, so they shouldn't collide unless same path)
|
|
24
|
+
await this.scanDirectory(userRulesDir, "user", newRules);
|
|
25
|
+
await this.scanDirectory(projectRulesDir, "project", newRules);
|
|
26
|
+
this.state.rules = newRules;
|
|
27
|
+
logger.debug(`Discovered ${Object.keys(newRules).length} memory rules`);
|
|
28
|
+
}
|
|
29
|
+
async scanDirectory(dir, source, registry, visited = new Set()) {
|
|
30
|
+
const realDir = await fs.realpath(dir).catch(() => dir);
|
|
31
|
+
if (visited.has(realDir)) {
|
|
32
|
+
logger.warn(`Circular symlink detected or directory already visited: ${dir}`);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
visited.add(realDir);
|
|
36
|
+
try {
|
|
37
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
38
|
+
for (const entry of entries) {
|
|
39
|
+
const fullPath = path.join(dir, entry.name);
|
|
40
|
+
const isDirectory = typeof entry.isDirectory === "function" ? entry.isDirectory() : false;
|
|
41
|
+
const isSymbolicLink = typeof entry.isSymbolicLink === "function"
|
|
42
|
+
? entry.isSymbolicLink()
|
|
43
|
+
: false;
|
|
44
|
+
const isFile = typeof entry.isFile === "function" ? entry.isFile() : false;
|
|
45
|
+
if (isDirectory) {
|
|
46
|
+
await this.scanDirectory(fullPath, source, registry, visited);
|
|
47
|
+
}
|
|
48
|
+
else if (isSymbolicLink) {
|
|
49
|
+
const stats = await fs.stat(fullPath);
|
|
50
|
+
if (stats.isDirectory()) {
|
|
51
|
+
await this.scanDirectory(fullPath, source, registry, visited);
|
|
52
|
+
}
|
|
53
|
+
else if (stats.isFile() && entry.name.endsWith(".md")) {
|
|
54
|
+
await this.loadRuleFile(fullPath, source, registry);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else if (isFile && entry.name.endsWith(".md")) {
|
|
58
|
+
await this.loadRuleFile(fullPath, source, registry);
|
|
59
|
+
}
|
|
60
|
+
else if (!isDirectory &&
|
|
61
|
+
!isSymbolicLink &&
|
|
62
|
+
!isFile &&
|
|
63
|
+
entry.name.endsWith(".md")) {
|
|
64
|
+
// Fallback for simple string arrays or incomplete mocks
|
|
65
|
+
await this.loadRuleFile(fullPath, source, registry);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
// Ignore if directory doesn't exist
|
|
71
|
+
if (error.code !== "ENOENT") {
|
|
72
|
+
logger.error(`Error scanning memory rules directory ${dir}:`, error);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async loadRuleFile(filePath, source, registry) {
|
|
77
|
+
try {
|
|
78
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
79
|
+
const rule = this.service.parseRule(content, filePath, source);
|
|
80
|
+
// Use relative path from rules root as ID to allow project rules to override user rules
|
|
81
|
+
const rulesRoot = source === "project"
|
|
82
|
+
? path.join(this.workdir, ".wave", "rules")
|
|
83
|
+
: path.join(os.homedir(), ".wave", "rules");
|
|
84
|
+
const relativeId = path.relative(rulesRoot, filePath);
|
|
85
|
+
rule.id = relativeId;
|
|
86
|
+
registry[rule.id] = rule;
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
logger.error(`Failed to parse memory rule at ${filePath}:`, error);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Returns the union of all active memory rules based on the provided file paths.
|
|
94
|
+
*/
|
|
95
|
+
getActiveRules(filesInContext) {
|
|
96
|
+
const activeRules = [];
|
|
97
|
+
for (const rule of Object.values(this.state.rules)) {
|
|
98
|
+
if (this.service.isRuleActive(rule, filesInContext)) {
|
|
99
|
+
activeRules.push(rule);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return activeRules;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Reloads rules from disk.
|
|
106
|
+
*/
|
|
107
|
+
async reload() {
|
|
108
|
+
await this.discoverRules();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -46,6 +46,7 @@ export declare class MessageManager {
|
|
|
46
46
|
private callbacks;
|
|
47
47
|
private transcriptPath;
|
|
48
48
|
private savedMessageCount;
|
|
49
|
+
private filesInContext;
|
|
49
50
|
private sessionType;
|
|
50
51
|
private subagentType?;
|
|
51
52
|
constructor(options: MessageManagerOptions);
|
|
@@ -54,6 +55,10 @@ export declare class MessageManager {
|
|
|
54
55
|
getlatestTotalTokens(): number;
|
|
55
56
|
getUserInputHistory(): string[];
|
|
56
57
|
getWorkdir(): string;
|
|
58
|
+
/**
|
|
59
|
+
* Returns all files mentioned in the current conversation context.
|
|
60
|
+
*/
|
|
61
|
+
getFilesInContext(): string[];
|
|
57
62
|
getSessionDir(): string;
|
|
58
63
|
getTranscriptPath(): string;
|
|
59
64
|
/**
|
|
@@ -122,5 +127,10 @@ export declare class MessageManager {
|
|
|
122
127
|
* Used for hook error handling when the user prompt needs to be erased
|
|
123
128
|
*/
|
|
124
129
|
removeLastUserMessage(): void;
|
|
130
|
+
/**
|
|
131
|
+
* Updates the set of files mentioned in the conversation.
|
|
132
|
+
*/
|
|
133
|
+
private updateFilesInContext;
|
|
134
|
+
private extractPathsFromParams;
|
|
125
135
|
}
|
|
126
136
|
//# sourceMappingURL=messageManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messageManager.d.ts","sourceRoot":"","sources":["../../src/managers/messageManager.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"messageManager.d.ts","sourceRoot":"","sources":["../../src/managers/messageManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,6 +4,7 @@ import { appendMessages, createSession, generateSessionId, SESSION_DIR, } from "
|
|
|
4
4
|
import { pathEncoder } from "../utils/pathEncoder.js";
|
|
5
5
|
export class MessageManager {
|
|
6
6
|
constructor(options) {
|
|
7
|
+
this.filesInContext = new Set(); // Track files mentioned in the conversation
|
|
7
8
|
this.sessionId = generateSessionId();
|
|
8
9
|
this.messages = [];
|
|
9
10
|
this.latestTotalTokens = 0;
|
|
@@ -34,6 +35,12 @@ export class MessageManager {
|
|
|
34
35
|
getWorkdir() {
|
|
35
36
|
return this.workdir;
|
|
36
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Returns all files mentioned in the current conversation context.
|
|
40
|
+
*/
|
|
41
|
+
getFilesInContext() {
|
|
42
|
+
return Array.from(this.filesInContext);
|
|
43
|
+
}
|
|
37
44
|
getSessionDir() {
|
|
38
45
|
return SESSION_DIR;
|
|
39
46
|
}
|
|
@@ -74,6 +81,7 @@ export class MessageManager {
|
|
|
74
81
|
}
|
|
75
82
|
setMessages(messages) {
|
|
76
83
|
this.messages = [...messages];
|
|
84
|
+
this.updateFilesInContext(messages);
|
|
77
85
|
this.callbacks.onMessagesChange?.([...messages]);
|
|
78
86
|
}
|
|
79
87
|
/**
|
|
@@ -126,6 +134,7 @@ export class MessageManager {
|
|
|
126
134
|
initializeFromSession(sessionData) {
|
|
127
135
|
this.setSessionId(sessionData.id);
|
|
128
136
|
this.setMessages([...sessionData.messages]);
|
|
137
|
+
this.updateFilesInContext(sessionData.messages);
|
|
129
138
|
this.setlatestTotalTokens(sessionData.metadata.latestTotalTokens);
|
|
130
139
|
// Extract user input history from session messages
|
|
131
140
|
this.setUserInputHistory(extractUserInputHistory(sessionData.messages));
|
|
@@ -404,4 +413,56 @@ export class MessageManager {
|
|
|
404
413
|
const newMessages = removeLastUserMessage(this.messages);
|
|
405
414
|
this.setMessages(newMessages);
|
|
406
415
|
}
|
|
416
|
+
/**
|
|
417
|
+
* Updates the set of files mentioned in the conversation.
|
|
418
|
+
*/
|
|
419
|
+
updateFilesInContext(messages) {
|
|
420
|
+
this.filesInContext.clear();
|
|
421
|
+
for (const message of messages) {
|
|
422
|
+
for (const block of message.blocks) {
|
|
423
|
+
if (block.type === "tool") {
|
|
424
|
+
// Extract file paths from common tool parameters
|
|
425
|
+
if (block.parameters) {
|
|
426
|
+
try {
|
|
427
|
+
const params = JSON.parse(block.parameters);
|
|
428
|
+
const paths = this.extractPathsFromParams(params);
|
|
429
|
+
for (const p of paths) {
|
|
430
|
+
this.filesInContext.add(p);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
catch {
|
|
434
|
+
// Ignore parse errors
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
extractPathsFromParams(params) {
|
|
442
|
+
const paths = [];
|
|
443
|
+
if (typeof params !== "object" || params === null)
|
|
444
|
+
return paths;
|
|
445
|
+
// Common parameter names for file paths
|
|
446
|
+
const pathKeys = [
|
|
447
|
+
"path",
|
|
448
|
+
"filePath",
|
|
449
|
+
"file_path",
|
|
450
|
+
"target_file",
|
|
451
|
+
"targetFile",
|
|
452
|
+
];
|
|
453
|
+
for (const key of pathKeys) {
|
|
454
|
+
if (typeof params[key] === "string") {
|
|
455
|
+
paths.push(params[key]);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
// Handle arrays of paths (e.g. in Glob or Grep results if we ever track those,
|
|
459
|
+
// but here we track inputs to tools)
|
|
460
|
+
if (Array.isArray(params.files)) {
|
|
461
|
+
for (const f of params.files) {
|
|
462
|
+
if (typeof f === "string")
|
|
463
|
+
paths.push(f);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
return paths;
|
|
467
|
+
}
|
|
407
468
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slashCommandManager.d.ts","sourceRoot":"","sources":["../../src/managers/slashCommandManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,MAAM,EACP,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"slashCommandManager.d.ts","sourceRoot":"","sources":["../../src/managers/slashCommandManager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,MAAM,EACP,MAAM,mBAAmB,CAAC;AAmB3B,MAAM,WAAW,0BAA0B;IACzC,cAAc,EAAE,cAAc,CAAC;IAC/B,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAmC;IACnD,OAAO,CAAC,cAAc,CAAyC;IAC/D,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAC,CAAS;gBAEZ,OAAO,EAAE,0BAA0B;IAU/C,OAAO,CAAC,yBAAyB;IAiCjC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAgD1B;;OAEG;IACI,sBAAsB,CAC3B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,kBAAkB,EAAE,GAC7B,IAAI;IA+CP;;OAEG;IACI,oBAAoB,IAAI,IAAI;IAWnC;;OAEG;IACH,OAAO,CAAC,eAAe;IAIvB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAIzB;;OAEG;IACI,WAAW,IAAI,YAAY,EAAE;IAIpC;;OAEG;IACI,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAI9D;;OAEG;IACU,cAAc,CACzB,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,OAAO,CAAC;IAenB;;;OAGG;IACI,4BAA4B,CAAC,KAAK,EAAE,MAAM,GAAG;QAClD,OAAO,EAAE,OAAO,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf;IAeD;;OAEG;IACI,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAI7C;;OAEG;IACI,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAI1E;;OAEG;IACI,iBAAiB,IAAI,kBAAkB,EAAE;IAIhD;;OAEG;YACW,+BAA+B;IA0E7C;;OAEG;IACI,mBAAmB,IAAI,IAAI;CAInC"}
|
|
@@ -3,6 +3,7 @@ import { substituteCommandParameters, parseSlashCommandInput, hasParameterPlaceh
|
|
|
3
3
|
import { parseBashCommands, replaceBashCommandsWithOutput, } from "../utils/markdownParser.js";
|
|
4
4
|
import { exec } from "child_process";
|
|
5
5
|
import { promisify } from "util";
|
|
6
|
+
import { INIT_PROMPT } from "../constants/prompts.js";
|
|
6
7
|
const execAsync = promisify(exec);
|
|
7
8
|
export class SlashCommandManager {
|
|
8
9
|
constructor(options) {
|
|
@@ -28,6 +29,21 @@ export class SlashCommandManager {
|
|
|
28
29
|
process.stdout.write("\x1Bc");
|
|
29
30
|
},
|
|
30
31
|
});
|
|
32
|
+
// Register built-in init command
|
|
33
|
+
this.registerCommand({
|
|
34
|
+
id: "init",
|
|
35
|
+
name: "init",
|
|
36
|
+
description: "Initialize repository for AI agents by generating AGENTS.md",
|
|
37
|
+
handler: async () => {
|
|
38
|
+
// Add custom command message to show the command being executed
|
|
39
|
+
this.messageManager.addUserMessage({
|
|
40
|
+
content: "/init",
|
|
41
|
+
customCommandContent: INIT_PROMPT,
|
|
42
|
+
});
|
|
43
|
+
// Execute the AI conversation with the init prompt
|
|
44
|
+
await this.aiManager.sendAIMessage();
|
|
45
|
+
},
|
|
46
|
+
});
|
|
31
47
|
}
|
|
32
48
|
/**
|
|
33
49
|
* Load custom commands from filesystem
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { MemoryRule } from "../types/memoryRule.js";
|
|
2
|
+
export declare class MemoryRuleService {
|
|
3
|
+
/**
|
|
4
|
+
* Parses a markdown file into a MemoryRule object.
|
|
5
|
+
*/
|
|
6
|
+
parseRule(content: string, filePath: string, source: "project" | "user"): MemoryRule;
|
|
7
|
+
/**
|
|
8
|
+
* Determines if a rule matches any of the given file paths using minimatch.
|
|
9
|
+
*/
|
|
10
|
+
isRuleActive(rule: MemoryRule, filesInContext: string[]): boolean;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=MemoryRuleService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MemoryRuleService.d.ts","sourceRoot":"","sources":["../../src/services/MemoryRuleService.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAsB,MAAM,wBAAwB,CAAC;AAE7E,qBAAa,iBAAiB;IAC5B;;OAEG;IACH,SAAS,CACP,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,SAAS,GAAG,MAAM,GACzB,UAAU;IAgCb;;OAEG;IACH,YAAY,CAAC,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO;CAWlE"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { minimatch } from "minimatch";
|
|
2
|
+
import { parseFrontmatter } from "../utils/markdownParser.js";
|
|
3
|
+
export class MemoryRuleService {
|
|
4
|
+
/**
|
|
5
|
+
* Parses a markdown file into a MemoryRule object.
|
|
6
|
+
*/
|
|
7
|
+
parseRule(content, filePath, source) {
|
|
8
|
+
const { frontmatter, content: bodyContent } = parseFrontmatter(content);
|
|
9
|
+
const metadata = {};
|
|
10
|
+
if (frontmatter) {
|
|
11
|
+
if (Array.isArray(frontmatter.paths)) {
|
|
12
|
+
metadata.paths = frontmatter.paths.filter((p) => typeof p === "string");
|
|
13
|
+
}
|
|
14
|
+
else if (typeof frontmatter.paths === "string") {
|
|
15
|
+
metadata.paths = [frontmatter.paths];
|
|
16
|
+
}
|
|
17
|
+
if (typeof frontmatter.priority === "number") {
|
|
18
|
+
metadata.priority = frontmatter.priority;
|
|
19
|
+
}
|
|
20
|
+
else if (typeof frontmatter.priority === "string") {
|
|
21
|
+
const parsed = parseInt(frontmatter.priority, 10);
|
|
22
|
+
if (!isNaN(parsed)) {
|
|
23
|
+
metadata.priority = parsed;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
id: filePath, // Use absolute path as ID for now
|
|
29
|
+
content: bodyContent.trim(),
|
|
30
|
+
metadata,
|
|
31
|
+
source,
|
|
32
|
+
filePath,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Determines if a rule matches any of the given file paths using minimatch.
|
|
37
|
+
*/
|
|
38
|
+
isRuleActive(rule, filesInContext) {
|
|
39
|
+
if (!rule.metadata.paths || rule.metadata.paths.length === 0) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
return filesInContext.some((filePath) => rule.metadata.paths.some((pattern) => minimatch(filePath, pattern, { dot: true })));
|
|
43
|
+
}
|
|
44
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,cAAc,WAAW,CAAC;AAG1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,cAAc,WAAW,CAAC;AAG1B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC"}
|
package/dist/types/index.js
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metadata extracted from the YAML frontmatter of a memory rule file.
|
|
3
|
+
*/
|
|
4
|
+
export interface MemoryRuleMetadata {
|
|
5
|
+
/**
|
|
6
|
+
* Glob patterns that determine when this rule is active.
|
|
7
|
+
* If undefined or empty, the rule is always active.
|
|
8
|
+
*/
|
|
9
|
+
paths?: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Optional priority override.
|
|
12
|
+
* Higher numbers take precedence if there are conflicting instructions.
|
|
13
|
+
*/
|
|
14
|
+
priority?: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Represents a single memory rule discovered from the filesystem.
|
|
18
|
+
*/
|
|
19
|
+
export interface MemoryRule {
|
|
20
|
+
/** Unique identifier, typically the relative path from the rules root */
|
|
21
|
+
id: string;
|
|
22
|
+
/** The raw content of the markdown file (excluding frontmatter) */
|
|
23
|
+
content: string;
|
|
24
|
+
/** Metadata parsed from YAML frontmatter */
|
|
25
|
+
metadata: MemoryRuleMetadata;
|
|
26
|
+
/** Source of the rule (project-level or user-level) */
|
|
27
|
+
source: "project" | "user";
|
|
28
|
+
/** Absolute path to the file on disk */
|
|
29
|
+
filePath: string;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=memoryRule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memoryRule.d.ts","sourceRoot":"","sources":["../../src/types/memoryRule.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,yEAAyE;IACzE,EAAE,EAAE,MAAM,CAAC;IACX,mEAAmE;IACnE,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,uDAAuD;IACvD,MAAM,EAAE,SAAS,GAAG,MAAM,CAAC;IAC3B,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;CAClB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -22,7 +22,7 @@ export declare const USER_MEMORY_FILE: string;
|
|
|
22
22
|
* AI related constants
|
|
23
23
|
*/
|
|
24
24
|
export declare const DEFAULT_WAVE_MAX_INPUT_TOKENS = 96000;
|
|
25
|
-
export declare const DEFAULT_WAVE_MAX_OUTPUT_TOKENS =
|
|
25
|
+
export declare const DEFAULT_WAVE_MAX_OUTPUT_TOKENS = 8192;
|
|
26
26
|
/**
|
|
27
27
|
* Default number of messages to keep uncompressed
|
|
28
28
|
*/
|
package/dist/utils/constants.js
CHANGED
|
@@ -24,7 +24,7 @@ export const USER_MEMORY_FILE = path.join(DATA_DIRECTORY, "AGENTS.md");
|
|
|
24
24
|
* AI related constants
|
|
25
25
|
*/
|
|
26
26
|
export const DEFAULT_WAVE_MAX_INPUT_TOKENS = 96000; // Default token limit
|
|
27
|
-
export const DEFAULT_WAVE_MAX_OUTPUT_TOKENS =
|
|
27
|
+
export const DEFAULT_WAVE_MAX_OUTPUT_TOKENS = 8192; // Default output token limit
|
|
28
28
|
/**
|
|
29
29
|
* Default number of messages to keep uncompressed
|
|
30
30
|
*/
|
|
@@ -3,6 +3,13 @@ interface ParsedMarkdownFile {
|
|
|
3
3
|
content: string;
|
|
4
4
|
config?: CustomSlashCommandConfig;
|
|
5
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* Parse YAML frontmatter from markdown content
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseFrontmatter(content: string): {
|
|
10
|
+
frontmatter?: Record<string, unknown>;
|
|
11
|
+
content: string;
|
|
12
|
+
};
|
|
6
13
|
/**
|
|
7
14
|
* Parse markdown file and extract config and content
|
|
8
15
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdownParser.d.ts","sourceRoot":"","sources":["../../src/utils/markdownParser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAElE,UAAU,kBAAkB;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,wBAAwB,CAAC;CACnC;
|
|
1
|
+
{"version":3,"file":"markdownParser.d.ts","sourceRoot":"","sources":["../../src/utils/markdownParser.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAC;AAElE,UAAU,kBAAkB;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,wBAAwB,CAAC;CACnC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;CACjB,CA8DA;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CA4CtE;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG;IAClD,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAgBA;AAED;;GAEG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,iBAAiB,EAAE,GAC3B,MAAM,CAcR"}
|
|
@@ -2,7 +2,7 @@ import { readFileSync } from "fs";
|
|
|
2
2
|
/**
|
|
3
3
|
* Parse YAML frontmatter from markdown content
|
|
4
4
|
*/
|
|
5
|
-
function parseFrontmatter(content) {
|
|
5
|
+
export function parseFrontmatter(content) {
|
|
6
6
|
const frontmatterRegex = /^---\s*\n([\s\S]*?)\n---\s*\n([\s\S]*)$/;
|
|
7
7
|
const match = content.match(frontmatterRegex);
|
|
8
8
|
if (!match) {
|
|
@@ -20,8 +20,13 @@ function parseFrontmatter(content) {
|
|
|
20
20
|
continue;
|
|
21
21
|
// Check if it's a list item
|
|
22
22
|
if (trimmedLine.startsWith("-") && currentKey) {
|
|
23
|
-
|
|
23
|
+
let value = trimmedLine.slice(1).trim();
|
|
24
24
|
if (value) {
|
|
25
|
+
// Remove surrounding quotes if present
|
|
26
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
27
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
28
|
+
value = value.slice(1, -1);
|
|
29
|
+
}
|
|
25
30
|
if (!Array.isArray(frontmatter[currentKey])) {
|
|
26
31
|
frontmatter[currentKey] = [];
|
|
27
32
|
}
|
|
@@ -36,7 +41,12 @@ function parseFrontmatter(content) {
|
|
|
36
41
|
const value = trimmedLine.slice(colonIndex + 1).trim();
|
|
37
42
|
currentKey = key;
|
|
38
43
|
if (value) {
|
|
39
|
-
|
|
44
|
+
// Remove surrounding quotes if present
|
|
45
|
+
const unquotedValue = (value.startsWith('"') && value.endsWith('"')) ||
|
|
46
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
47
|
+
? value.slice(1, -1)
|
|
48
|
+
: value;
|
|
49
|
+
frontmatter[key] = unquotedValue;
|
|
40
50
|
}
|
|
41
51
|
}
|
|
42
52
|
return { frontmatter, content: bodyContent };
|
package/package.json
CHANGED
package/src/agent.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
} from "./managers/backgroundBashManager.js";
|
|
19
19
|
import { SlashCommandManager } from "./managers/slashCommandManager.js";
|
|
20
20
|
import { PluginManager } from "./managers/pluginManager.js";
|
|
21
|
+
import { HookManager } from "./managers/hookManager.js";
|
|
21
22
|
import { PermissionManager } from "./managers/permissionManager.js";
|
|
22
23
|
import { PlanManager } from "./managers/planManager.js";
|
|
23
24
|
import type {
|
|
@@ -36,7 +37,7 @@ import type {
|
|
|
36
37
|
PermissionMode,
|
|
37
38
|
PermissionCallback,
|
|
38
39
|
} from "./types/index.js";
|
|
39
|
-
import {
|
|
40
|
+
import { MemoryRuleManager } from "./managers/MemoryRuleManager.js";
|
|
40
41
|
import { LiveConfigManager } from "./managers/liveConfigManager.js";
|
|
41
42
|
import { configValidator } from "./utils/configValidator.js";
|
|
42
43
|
import { SkillManager } from "./managers/skillManager.js";
|
|
@@ -118,6 +119,7 @@ export class Agent {
|
|
|
118
119
|
private pluginManager: PluginManager; // Add plugin manager instance
|
|
119
120
|
private skillManager: SkillManager; // Add skill manager instance
|
|
120
121
|
private hookManager: HookManager; // Add hooks manager instance
|
|
122
|
+
private memoryRuleManager: MemoryRuleManager; // Add memory rule manager instance
|
|
121
123
|
private liveConfigManager: LiveConfigManager; // Add live configuration manager
|
|
122
124
|
private configurationService: ConfigurationService; // Add configuration service
|
|
123
125
|
private workdir: string; // Working directory
|
|
@@ -266,6 +268,11 @@ export class Agent {
|
|
|
266
268
|
logger: this.logger,
|
|
267
269
|
});
|
|
268
270
|
|
|
271
|
+
// Initialize memory rule manager
|
|
272
|
+
this.memoryRuleManager = new MemoryRuleManager({
|
|
273
|
+
workdir: this.workdir,
|
|
274
|
+
});
|
|
275
|
+
|
|
269
276
|
// Create a wrapper for canUseTool that triggers notification hooks
|
|
270
277
|
const canUseToolWithNotification: PermissionCallback | undefined =
|
|
271
278
|
options.canUseTool
|
|
@@ -470,7 +477,7 @@ export class Agent {
|
|
|
470
477
|
return this._userMemoryContent;
|
|
471
478
|
}
|
|
472
479
|
|
|
473
|
-
/** Get combined memory content (project + user) */
|
|
480
|
+
/** Get combined memory content (project + user + modular rules) */
|
|
474
481
|
public get combinedMemory(): string {
|
|
475
482
|
let combined = "";
|
|
476
483
|
if (this._projectMemoryContent.trim()) {
|
|
@@ -482,6 +489,17 @@ export class Agent {
|
|
|
482
489
|
}
|
|
483
490
|
combined += this._userMemoryContent;
|
|
484
491
|
}
|
|
492
|
+
|
|
493
|
+
// Add modular memory rules
|
|
494
|
+
const filesInContext = this.messageManager.getFilesInContext();
|
|
495
|
+
const activeRules = this.memoryRuleManager.getActiveRules(filesInContext);
|
|
496
|
+
if (activeRules.length > 0) {
|
|
497
|
+
if (combined) {
|
|
498
|
+
combined += "\n\n";
|
|
499
|
+
}
|
|
500
|
+
combined += activeRules.map((r) => r.content).join("\n\n");
|
|
501
|
+
}
|
|
502
|
+
|
|
485
503
|
return combined;
|
|
486
504
|
}
|
|
487
505
|
|
|
@@ -649,6 +667,13 @@ export class Agent {
|
|
|
649
667
|
// Resolve and validate configuration after loading settings.json
|
|
650
668
|
this.resolveAndValidateConfig();
|
|
651
669
|
|
|
670
|
+
// Discover modular memory rules
|
|
671
|
+
try {
|
|
672
|
+
await this.memoryRuleManager.discoverRules();
|
|
673
|
+
} catch (error) {
|
|
674
|
+
this.logger?.error("Failed to discover memory rules:", error);
|
|
675
|
+
}
|
|
676
|
+
|
|
652
677
|
// Set global logger for SDK-wide access after validation
|
|
653
678
|
setGlobalLogger(this.logger || null);
|
|
654
679
|
|
package/src/constants/prompts.ts
CHANGED
|
@@ -53,6 +53,28 @@ export const BASH_POLICY = `
|
|
|
53
53
|
|
|
54
54
|
export const DEFAULT_SYSTEM_PROMPT = BASE_SYSTEM_PROMPT;
|
|
55
55
|
|
|
56
|
+
export const INIT_PROMPT = `Please analyze this codebase and create a AGENTS.md file, which will be given to future instances of Wave Code to operate in this repository.
|
|
57
|
+
|
|
58
|
+
What to add:
|
|
59
|
+
1. Commands that will be commonly used, such as how to build, lint, and run tests. Include the necessary commands to develop in this codebase, such as how to run a single test.
|
|
60
|
+
2. High-level code architecture and structure so that future instances can be productive more quickly. Focus on the "big picture" architecture that requires reading multiple files to understand.
|
|
61
|
+
|
|
62
|
+
Usage notes:
|
|
63
|
+
- If there's already a AGENTS.md, suggest improvements to it.
|
|
64
|
+
- When you make the initial AGENTS.md, do not repeat yourself and do not include obvious instructions like "Provide helpful error messages to users", "Write unit tests for all new utilities", "Never include sensitive information (API keys, tokens) in code or commits".
|
|
65
|
+
- Avoid listing every component or file structure that can be easily discovered.
|
|
66
|
+
- Don't include generic development practices.
|
|
67
|
+
- If there are Cursor rules (in .cursor/rules/ or .cursorrules) or Copilot rules (in .github/copilot-instructions.md), make sure to include the important parts.
|
|
68
|
+
- If there is a README.md, make sure to include the important parts.
|
|
69
|
+
- Do not make up information such as "Common Development Tasks", "Tips for Development", "Support and Documentation" unless this is expressly included in other files that you read.
|
|
70
|
+
- Be sure to prefix the file with the following text:
|
|
71
|
+
|
|
72
|
+
\`\`\`
|
|
73
|
+
# AGENTS.md
|
|
74
|
+
|
|
75
|
+
This file provides guidance to Wave Code when working with code in this repository.
|
|
76
|
+
\`\`\``;
|
|
77
|
+
|
|
56
78
|
export function buildSystemPrompt(
|
|
57
79
|
basePrompt: string,
|
|
58
80
|
tools: { name?: string; function?: { name: string } }[],
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { MemoryRule } from "../types/memoryRule.js";
|
|
2
|
+
import { MemoryRuleService } from "../services/MemoryRuleService.js";
|
|
3
|
+
import * as fs from "node:fs/promises";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
import * as os from "node:os";
|
|
6
|
+
import { logger } from "../utils/globalLogger.js";
|
|
7
|
+
|
|
8
|
+
export interface MemoryRuleRegistryState {
|
|
9
|
+
/** All discovered rules, indexed by ID */
|
|
10
|
+
rules: Record<string, MemoryRule>;
|
|
11
|
+
/** Set of active rule IDs based on the current context */
|
|
12
|
+
activeRuleIds: Set<string>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface MemoryRuleManagerOptions {
|
|
16
|
+
workdir: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class MemoryRuleManager {
|
|
20
|
+
private state: MemoryRuleRegistryState = {
|
|
21
|
+
rules: {},
|
|
22
|
+
activeRuleIds: new Set(),
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
private workdir: string;
|
|
26
|
+
private service: MemoryRuleService;
|
|
27
|
+
|
|
28
|
+
constructor(options: MemoryRuleManagerOptions) {
|
|
29
|
+
this.workdir = options.workdir;
|
|
30
|
+
this.service = new MemoryRuleService();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Scans .wave/rules and ~/.wave/rules for memory rule files.
|
|
35
|
+
*/
|
|
36
|
+
async discoverRules(): Promise<void> {
|
|
37
|
+
const projectRulesDir = path.join(this.workdir, ".wave", "rules");
|
|
38
|
+
const userRulesDir = path.join(os.homedir(), ".wave", "rules");
|
|
39
|
+
|
|
40
|
+
const newRules: Record<string, MemoryRule> = {};
|
|
41
|
+
|
|
42
|
+
// Discover user rules first, then project rules so project rules can override if needed
|
|
43
|
+
// (though IDs are based on file path, so they shouldn't collide unless same path)
|
|
44
|
+
await this.scanDirectory(userRulesDir, "user", newRules);
|
|
45
|
+
await this.scanDirectory(projectRulesDir, "project", newRules);
|
|
46
|
+
|
|
47
|
+
this.state.rules = newRules;
|
|
48
|
+
logger.debug(`Discovered ${Object.keys(newRules).length} memory rules`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private async scanDirectory(
|
|
52
|
+
dir: string,
|
|
53
|
+
source: "project" | "user",
|
|
54
|
+
registry: Record<string, MemoryRule>,
|
|
55
|
+
visited: Set<string> = new Set(),
|
|
56
|
+
): Promise<void> {
|
|
57
|
+
const realDir = await fs.realpath(dir).catch(() => dir);
|
|
58
|
+
if (visited.has(realDir)) {
|
|
59
|
+
logger.warn(
|
|
60
|
+
`Circular symlink detected or directory already visited: ${dir}`,
|
|
61
|
+
);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
visited.add(realDir);
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
68
|
+
for (const entry of entries) {
|
|
69
|
+
const fullPath = path.join(dir, entry.name);
|
|
70
|
+
const isDirectory =
|
|
71
|
+
typeof entry.isDirectory === "function" ? entry.isDirectory() : false;
|
|
72
|
+
const isSymbolicLink =
|
|
73
|
+
typeof entry.isSymbolicLink === "function"
|
|
74
|
+
? entry.isSymbolicLink()
|
|
75
|
+
: false;
|
|
76
|
+
const isFile =
|
|
77
|
+
typeof entry.isFile === "function" ? entry.isFile() : false;
|
|
78
|
+
|
|
79
|
+
if (isDirectory) {
|
|
80
|
+
await this.scanDirectory(fullPath, source, registry, visited);
|
|
81
|
+
} else if (isSymbolicLink) {
|
|
82
|
+
const stats = await fs.stat(fullPath);
|
|
83
|
+
if (stats.isDirectory()) {
|
|
84
|
+
await this.scanDirectory(fullPath, source, registry, visited);
|
|
85
|
+
} else if (stats.isFile() && entry.name.endsWith(".md")) {
|
|
86
|
+
await this.loadRuleFile(fullPath, source, registry);
|
|
87
|
+
}
|
|
88
|
+
} else if (isFile && entry.name.endsWith(".md")) {
|
|
89
|
+
await this.loadRuleFile(fullPath, source, registry);
|
|
90
|
+
} else if (
|
|
91
|
+
!isDirectory &&
|
|
92
|
+
!isSymbolicLink &&
|
|
93
|
+
!isFile &&
|
|
94
|
+
entry.name.endsWith(".md")
|
|
95
|
+
) {
|
|
96
|
+
// Fallback for simple string arrays or incomplete mocks
|
|
97
|
+
await this.loadRuleFile(fullPath, source, registry);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} catch (error) {
|
|
101
|
+
// Ignore if directory doesn't exist
|
|
102
|
+
if ((error as NodeJS.ErrnoException).code !== "ENOENT") {
|
|
103
|
+
logger.error(`Error scanning memory rules directory ${dir}:`, error);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private async loadRuleFile(
|
|
109
|
+
filePath: string,
|
|
110
|
+
source: "project" | "user",
|
|
111
|
+
registry: Record<string, MemoryRule>,
|
|
112
|
+
): Promise<void> {
|
|
113
|
+
try {
|
|
114
|
+
const content = await fs.readFile(filePath, "utf-8");
|
|
115
|
+
const rule = this.service.parseRule(content, filePath, source);
|
|
116
|
+
// Use relative path from rules root as ID to allow project rules to override user rules
|
|
117
|
+
const rulesRoot =
|
|
118
|
+
source === "project"
|
|
119
|
+
? path.join(this.workdir, ".wave", "rules")
|
|
120
|
+
: path.join(os.homedir(), ".wave", "rules");
|
|
121
|
+
const relativeId = path.relative(rulesRoot, filePath);
|
|
122
|
+
rule.id = relativeId;
|
|
123
|
+
registry[rule.id] = rule;
|
|
124
|
+
} catch (error) {
|
|
125
|
+
logger.error(`Failed to parse memory rule at ${filePath}:`, error);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Returns the union of all active memory rules based on the provided file paths.
|
|
131
|
+
*/
|
|
132
|
+
getActiveRules(filesInContext: string[]): MemoryRule[] {
|
|
133
|
+
const activeRules: MemoryRule[] = [];
|
|
134
|
+
for (const rule of Object.values(this.state.rules)) {
|
|
135
|
+
if (this.service.isRuleActive(rule, filesInContext)) {
|
|
136
|
+
activeRules.push(rule);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return activeRules;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Reloads rules from disk.
|
|
144
|
+
*/
|
|
145
|
+
async reload(): Promise<void> {
|
|
146
|
+
await this.discoverRules();
|
|
147
|
+
}
|
|
148
|
+
}
|
|
@@ -92,6 +92,7 @@ export class MessageManager {
|
|
|
92
92
|
private callbacks: MessageManagerCallbacks;
|
|
93
93
|
private transcriptPath: string; // Cached transcript path
|
|
94
94
|
private savedMessageCount: number; // Track how many messages have been saved to prevent duplication
|
|
95
|
+
private filesInContext: Set<string> = new Set(); // Track files mentioned in the conversation
|
|
95
96
|
private sessionType: "main" | "subagent";
|
|
96
97
|
private subagentType?: string;
|
|
97
98
|
|
|
@@ -133,6 +134,13 @@ export class MessageManager {
|
|
|
133
134
|
return this.workdir;
|
|
134
135
|
}
|
|
135
136
|
|
|
137
|
+
/**
|
|
138
|
+
* Returns all files mentioned in the current conversation context.
|
|
139
|
+
*/
|
|
140
|
+
public getFilesInContext(): string[] {
|
|
141
|
+
return Array.from(this.filesInContext);
|
|
142
|
+
}
|
|
143
|
+
|
|
136
144
|
public getSessionDir(): string {
|
|
137
145
|
return SESSION_DIR;
|
|
138
146
|
}
|
|
@@ -179,6 +187,7 @@ export class MessageManager {
|
|
|
179
187
|
|
|
180
188
|
public setMessages(messages: Message[]): void {
|
|
181
189
|
this.messages = [...messages];
|
|
190
|
+
this.updateFilesInContext(messages);
|
|
182
191
|
this.callbacks.onMessagesChange?.([...messages]);
|
|
183
192
|
}
|
|
184
193
|
|
|
@@ -244,6 +253,7 @@ export class MessageManager {
|
|
|
244
253
|
public initializeFromSession(sessionData: SessionData): void {
|
|
245
254
|
this.setSessionId(sessionData.id);
|
|
246
255
|
this.setMessages([...sessionData.messages]);
|
|
256
|
+
this.updateFilesInContext(sessionData.messages);
|
|
247
257
|
this.setlatestTotalTokens(sessionData.metadata.latestTotalTokens);
|
|
248
258
|
|
|
249
259
|
// Extract user input history from session messages
|
|
@@ -632,4 +642,61 @@ export class MessageManager {
|
|
|
632
642
|
const newMessages = removeLastUserMessage(this.messages);
|
|
633
643
|
this.setMessages(newMessages);
|
|
634
644
|
}
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Updates the set of files mentioned in the conversation.
|
|
648
|
+
*/
|
|
649
|
+
private updateFilesInContext(messages: Message[]): void {
|
|
650
|
+
this.filesInContext.clear();
|
|
651
|
+
for (const message of messages) {
|
|
652
|
+
for (const block of message.blocks) {
|
|
653
|
+
if (block.type === "tool") {
|
|
654
|
+
// Extract file paths from common tool parameters
|
|
655
|
+
if (block.parameters) {
|
|
656
|
+
try {
|
|
657
|
+
const params = JSON.parse(block.parameters) as Record<
|
|
658
|
+
string,
|
|
659
|
+
unknown
|
|
660
|
+
>;
|
|
661
|
+
const paths = this.extractPathsFromParams(params);
|
|
662
|
+
for (const p of paths) {
|
|
663
|
+
this.filesInContext.add(p);
|
|
664
|
+
}
|
|
665
|
+
} catch {
|
|
666
|
+
// Ignore parse errors
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
private extractPathsFromParams(params: Record<string, unknown>): string[] {
|
|
675
|
+
const paths: string[] = [];
|
|
676
|
+
if (typeof params !== "object" || params === null) return paths;
|
|
677
|
+
|
|
678
|
+
// Common parameter names for file paths
|
|
679
|
+
const pathKeys = [
|
|
680
|
+
"path",
|
|
681
|
+
"filePath",
|
|
682
|
+
"file_path",
|
|
683
|
+
"target_file",
|
|
684
|
+
"targetFile",
|
|
685
|
+
];
|
|
686
|
+
for (const key of pathKeys) {
|
|
687
|
+
if (typeof params[key] === "string") {
|
|
688
|
+
paths.push(params[key]);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// Handle arrays of paths (e.g. in Glob or Grep results if we ever track those,
|
|
693
|
+
// but here we track inputs to tools)
|
|
694
|
+
if (Array.isArray(params.files)) {
|
|
695
|
+
for (const f of params.files) {
|
|
696
|
+
if (typeof f === "string") paths.push(f);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
return paths;
|
|
701
|
+
}
|
|
635
702
|
}
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
} from "../utils/markdownParser.js";
|
|
20
20
|
import { exec } from "child_process";
|
|
21
21
|
import { promisify } from "util";
|
|
22
|
+
import { INIT_PROMPT } from "../constants/prompts.js";
|
|
22
23
|
|
|
23
24
|
const execAsync = promisify(exec);
|
|
24
25
|
|
|
@@ -60,6 +61,24 @@ export class SlashCommandManager {
|
|
|
60
61
|
process.stdout.write("\x1Bc");
|
|
61
62
|
},
|
|
62
63
|
});
|
|
64
|
+
|
|
65
|
+
// Register built-in init command
|
|
66
|
+
this.registerCommand({
|
|
67
|
+
id: "init",
|
|
68
|
+
name: "init",
|
|
69
|
+
description:
|
|
70
|
+
"Initialize repository for AI agents by generating AGENTS.md",
|
|
71
|
+
handler: async () => {
|
|
72
|
+
// Add custom command message to show the command being executed
|
|
73
|
+
this.messageManager.addUserMessage({
|
|
74
|
+
content: "/init",
|
|
75
|
+
customCommandContent: INIT_PROMPT,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Execute the AI conversation with the init prompt
|
|
79
|
+
await this.aiManager.sendAIMessage();
|
|
80
|
+
},
|
|
81
|
+
});
|
|
63
82
|
}
|
|
64
83
|
|
|
65
84
|
/**
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { minimatch } from "minimatch";
|
|
2
|
+
import { parseFrontmatter } from "../utils/markdownParser.js";
|
|
3
|
+
import type { MemoryRule, MemoryRuleMetadata } from "../types/memoryRule.js";
|
|
4
|
+
|
|
5
|
+
export class MemoryRuleService {
|
|
6
|
+
/**
|
|
7
|
+
* Parses a markdown file into a MemoryRule object.
|
|
8
|
+
*/
|
|
9
|
+
parseRule(
|
|
10
|
+
content: string,
|
|
11
|
+
filePath: string,
|
|
12
|
+
source: "project" | "user",
|
|
13
|
+
): MemoryRule {
|
|
14
|
+
const { frontmatter, content: bodyContent } = parseFrontmatter(content);
|
|
15
|
+
|
|
16
|
+
const metadata: MemoryRuleMetadata = {};
|
|
17
|
+
if (frontmatter) {
|
|
18
|
+
if (Array.isArray(frontmatter.paths)) {
|
|
19
|
+
metadata.paths = frontmatter.paths.filter(
|
|
20
|
+
(p): p is string => typeof p === "string",
|
|
21
|
+
);
|
|
22
|
+
} else if (typeof frontmatter.paths === "string") {
|
|
23
|
+
metadata.paths = [frontmatter.paths];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (typeof frontmatter.priority === "number") {
|
|
27
|
+
metadata.priority = frontmatter.priority;
|
|
28
|
+
} else if (typeof frontmatter.priority === "string") {
|
|
29
|
+
const parsed = parseInt(frontmatter.priority, 10);
|
|
30
|
+
if (!isNaN(parsed)) {
|
|
31
|
+
metadata.priority = parsed;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
id: filePath, // Use absolute path as ID for now
|
|
38
|
+
content: bodyContent.trim(),
|
|
39
|
+
metadata,
|
|
40
|
+
source,
|
|
41
|
+
filePath,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Determines if a rule matches any of the given file paths using minimatch.
|
|
47
|
+
*/
|
|
48
|
+
isRuleActive(rule: MemoryRule, filesInContext: string[]): boolean {
|
|
49
|
+
if (!rule.metadata.paths || rule.metadata.paths.length === 0) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return filesInContext.some((filePath) =>
|
|
54
|
+
rule.metadata.paths!.some((pattern) =>
|
|
55
|
+
minimatch(filePath, pattern, { dot: true }),
|
|
56
|
+
),
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
package/src/types/index.ts
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Metadata extracted from the YAML frontmatter of a memory rule file.
|
|
3
|
+
*/
|
|
4
|
+
export interface MemoryRuleMetadata {
|
|
5
|
+
/**
|
|
6
|
+
* Glob patterns that determine when this rule is active.
|
|
7
|
+
* If undefined or empty, the rule is always active.
|
|
8
|
+
*/
|
|
9
|
+
paths?: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Optional priority override.
|
|
12
|
+
* Higher numbers take precedence if there are conflicting instructions.
|
|
13
|
+
*/
|
|
14
|
+
priority?: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Represents a single memory rule discovered from the filesystem.
|
|
19
|
+
*/
|
|
20
|
+
export interface MemoryRule {
|
|
21
|
+
/** Unique identifier, typically the relative path from the rules root */
|
|
22
|
+
id: string;
|
|
23
|
+
/** The raw content of the markdown file (excluding frontmatter) */
|
|
24
|
+
content: string;
|
|
25
|
+
/** Metadata parsed from YAML frontmatter */
|
|
26
|
+
metadata: MemoryRuleMetadata;
|
|
27
|
+
/** Source of the rule (project-level or user-level) */
|
|
28
|
+
source: "project" | "user";
|
|
29
|
+
/** Absolute path to the file on disk */
|
|
30
|
+
filePath: string;
|
|
31
|
+
}
|
package/src/utils/constants.ts
CHANGED
|
@@ -30,7 +30,7 @@ export const USER_MEMORY_FILE = path.join(DATA_DIRECTORY, "AGENTS.md");
|
|
|
30
30
|
* AI related constants
|
|
31
31
|
*/
|
|
32
32
|
export const DEFAULT_WAVE_MAX_INPUT_TOKENS = 96000; // Default token limit
|
|
33
|
-
export const DEFAULT_WAVE_MAX_OUTPUT_TOKENS =
|
|
33
|
+
export const DEFAULT_WAVE_MAX_OUTPUT_TOKENS = 8192; // Default output token limit
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* Default number of messages to keep uncompressed
|
|
@@ -9,7 +9,7 @@ interface ParsedMarkdownFile {
|
|
|
9
9
|
/**
|
|
10
10
|
* Parse YAML frontmatter from markdown content
|
|
11
11
|
*/
|
|
12
|
-
function parseFrontmatter(content: string): {
|
|
12
|
+
export function parseFrontmatter(content: string): {
|
|
13
13
|
frontmatter?: Record<string, unknown>;
|
|
14
14
|
content: string;
|
|
15
15
|
} {
|
|
@@ -34,8 +34,15 @@ function parseFrontmatter(content: string): {
|
|
|
34
34
|
|
|
35
35
|
// Check if it's a list item
|
|
36
36
|
if (trimmedLine.startsWith("-") && currentKey) {
|
|
37
|
-
|
|
37
|
+
let value = trimmedLine.slice(1).trim();
|
|
38
38
|
if (value) {
|
|
39
|
+
// Remove surrounding quotes if present
|
|
40
|
+
if (
|
|
41
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
42
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
43
|
+
) {
|
|
44
|
+
value = value.slice(1, -1);
|
|
45
|
+
}
|
|
39
46
|
if (!Array.isArray(frontmatter[currentKey])) {
|
|
40
47
|
frontmatter[currentKey] = [];
|
|
41
48
|
}
|
|
@@ -52,7 +59,13 @@ function parseFrontmatter(content: string): {
|
|
|
52
59
|
|
|
53
60
|
currentKey = key;
|
|
54
61
|
if (value) {
|
|
55
|
-
|
|
62
|
+
// Remove surrounding quotes if present
|
|
63
|
+
const unquotedValue =
|
|
64
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
65
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
66
|
+
? value.slice(1, -1)
|
|
67
|
+
: value;
|
|
68
|
+
frontmatter[key] = unquotedValue;
|
|
56
69
|
}
|
|
57
70
|
}
|
|
58
71
|
|