@iinm/plain-agent 1.8.3 → 1.8.4
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 +2 -2
- package/bin/plain +1 -1
- package/config/config.predefined.json +1 -1
- package/config/prompts.predefined/shortcuts/configure.md +1 -1
- package/dist/main.mjs +473 -0
- package/dist/main.mjs.map +7 -0
- package/package.json +5 -7
- package/src/agent.d.ts +0 -52
- package/src/agent.mjs +0 -204
- package/src/agentLoop.mjs +0 -419
- package/src/agentState.mjs +0 -41
- package/src/claudeCodePlugin.mjs +0 -164
- package/src/cliArgs.mjs +0 -175
- package/src/cliBatch.mjs +0 -147
- package/src/cliCommands.mjs +0 -283
- package/src/cliCompleter.mjs +0 -227
- package/src/cliCost.mjs +0 -309
- package/src/cliFormatter.mjs +0 -413
- package/src/cliInteractive.mjs +0 -529
- package/src/cliInterruptTransform.mjs +0 -51
- package/src/cliMuteTransform.mjs +0 -26
- package/src/cliPasteTransform.mjs +0 -183
- package/src/config.d.ts +0 -36
- package/src/config.mjs +0 -197
- package/src/context/loadAgentRoles.mjs +0 -283
- package/src/context/loadPrompts.mjs +0 -324
- package/src/context/loadUserMessageContext.mjs +0 -147
- package/src/costTracker.mjs +0 -210
- package/src/env.mjs +0 -44
- package/src/main.mjs +0 -279
- package/src/mcpClient.mjs +0 -351
- package/src/mcpIntegration.mjs +0 -160
- package/src/model.d.ts +0 -109
- package/src/modelCaller.mjs +0 -32
- package/src/modelDefinition.d.ts +0 -92
- package/src/prompt.mjs +0 -138
- package/src/providers/anthropic.d.ts +0 -248
- package/src/providers/anthropic.mjs +0 -587
- package/src/providers/bedrock.d.ts +0 -249
- package/src/providers/bedrock.mjs +0 -700
- package/src/providers/gemini.d.ts +0 -208
- package/src/providers/gemini.mjs +0 -754
- package/src/providers/openai.d.ts +0 -281
- package/src/providers/openai.mjs +0 -544
- package/src/providers/openaiCompatible.d.ts +0 -147
- package/src/providers/openaiCompatible.mjs +0 -652
- package/src/providers/platform/awsSigV4.mjs +0 -184
- package/src/providers/platform/azure.mjs +0 -42
- package/src/providers/platform/bedrock.mjs +0 -78
- package/src/providers/platform/googleCloud.mjs +0 -34
- package/src/subagent.mjs +0 -265
- package/src/tmpfile.mjs +0 -27
- package/src/tool.d.ts +0 -74
- package/src/toolExecutor.mjs +0 -236
- package/src/toolInputValidator.mjs +0 -183
- package/src/toolUseApprover.mjs +0 -99
- package/src/tools/askURL.mjs +0 -209
- package/src/tools/askWeb.mjs +0 -208
- package/src/tools/compactContext.d.ts +0 -4
- package/src/tools/compactContext.mjs +0 -87
- package/src/tools/delegateToSubagent.d.ts +0 -4
- package/src/tools/delegateToSubagent.mjs +0 -48
- package/src/tools/execCommand.d.ts +0 -22
- package/src/tools/execCommand.mjs +0 -200
- package/src/tools/patchFile.d.ts +0 -4
- package/src/tools/patchFile.mjs +0 -133
- package/src/tools/reportAsSubagent.d.ts +0 -3
- package/src/tools/reportAsSubagent.mjs +0 -44
- package/src/tools/tmuxCommand.d.ts +0 -14
- package/src/tools/tmuxCommand.mjs +0 -194
- package/src/tools/writeFile.d.ts +0 -4
- package/src/tools/writeFile.mjs +0 -56
- package/src/usageStore.mjs +0 -167
- package/src/utils/evalJSONConfig.mjs +0 -72
- package/src/utils/matchValue.d.ts +0 -6
- package/src/utils/matchValue.mjs +0 -40
- package/src/utils/noThrow.mjs +0 -31
- package/src/utils/notify.mjs +0 -29
- package/src/utils/parseFileRange.mjs +0 -18
- package/src/utils/readFileRange.mjs +0 -33
- package/src/utils/retryOnError.mjs +0 -41
- package/src/voiceInput.mjs +0 -61
- package/src/voiceInputGemini.mjs +0 -105
- package/src/voiceInputOpenAI.mjs +0 -104
- package/src/voiceInputSession.mjs +0 -543
- package/src/voiceToggleKey.mjs +0 -62
package/src/mcpIntegration.mjs
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @import { StructuredToolResultContent, Tool, ToolImplementation } from "./tool";
|
|
3
|
-
* @import { MCPServerConfig } from "./config";
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { mkdir } from "node:fs/promises";
|
|
7
|
-
import path from "node:path";
|
|
8
|
-
import { AGENT_PROJECT_METADATA_DIR } from "./env.mjs";
|
|
9
|
-
import { createMCPClient } from "./mcpClient.mjs";
|
|
10
|
-
import { writeTmpFile } from "./tmpfile.mjs";
|
|
11
|
-
import { noThrow } from "./utils/noThrow.mjs";
|
|
12
|
-
|
|
13
|
-
/** @typedef {import("./mcpClient.mjs").MCPClient} MCPClient */
|
|
14
|
-
|
|
15
|
-
const OUTPUT_MAX_LENGTH = 1024 * 8;
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @typedef {Object} SetupMCPServrResult
|
|
19
|
-
* @property {Tool[]} tools
|
|
20
|
-
* @property {string} stderrLogPath
|
|
21
|
-
* @property {() => Promise<void>} cleanup
|
|
22
|
-
*/
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* @param {string} serverName
|
|
26
|
-
* @param {MCPServerConfig} serverConfig
|
|
27
|
-
* @returns {Promise<SetupMCPServrResult>}
|
|
28
|
-
*/
|
|
29
|
-
export async function setupMCPServer(serverName, serverConfig) {
|
|
30
|
-
const { options, ...params } = serverConfig;
|
|
31
|
-
|
|
32
|
-
// Ensure log directory exists and open stderr log file
|
|
33
|
-
const logDir = path.join(AGENT_PROJECT_METADATA_DIR, "logs");
|
|
34
|
-
await mkdir(logDir, { recursive: true });
|
|
35
|
-
const logPath = path.join(logDir, `mcp--${serverName}.stderr`);
|
|
36
|
-
|
|
37
|
-
const client = await createMCPClient({
|
|
38
|
-
...params,
|
|
39
|
-
stderr: logPath,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const tools = (await createMCPTools(serverName, client)).filter(
|
|
43
|
-
(tool) =>
|
|
44
|
-
!options?.enabledTools ||
|
|
45
|
-
options.enabledTools.find((enabledToolName) =>
|
|
46
|
-
tool.def.name.endsWith(`__${enabledToolName}`),
|
|
47
|
-
),
|
|
48
|
-
);
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
tools,
|
|
52
|
-
stderrLogPath: logPath,
|
|
53
|
-
cleanup: async () => {
|
|
54
|
-
await client.close();
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* @param {string} serverName
|
|
61
|
-
* @param {MCPClient} client
|
|
62
|
-
* @returns {Promise<Tool[]>}
|
|
63
|
-
*/
|
|
64
|
-
async function createMCPTools(serverName, client) {
|
|
65
|
-
const { tools: mcpTools } = await client.listTools();
|
|
66
|
-
/** @type {Tool[]} */
|
|
67
|
-
const tools = mcpTools
|
|
68
|
-
.filter((tool) => {
|
|
69
|
-
// Remove unsupported tools
|
|
70
|
-
return ![""].includes(tool.name);
|
|
71
|
-
})
|
|
72
|
-
.map((tool) => {
|
|
73
|
-
return {
|
|
74
|
-
def: {
|
|
75
|
-
name: `mcp__${serverName}__${tool.name}`,
|
|
76
|
-
description: tool.description || `${tool.name} tool`,
|
|
77
|
-
inputSchema: tool.inputSchema,
|
|
78
|
-
},
|
|
79
|
-
|
|
80
|
-
/** @type {ToolImplementation} */
|
|
81
|
-
impl: async (input) =>
|
|
82
|
-
noThrow(async () => {
|
|
83
|
-
const result = await client.callTool({
|
|
84
|
-
name: tool.name,
|
|
85
|
-
arguments: input,
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
const resultStringRaw = JSON.stringify(result, null, 2);
|
|
89
|
-
|
|
90
|
-
/** @type {StructuredToolResultContent[]} */
|
|
91
|
-
const contentParts = [];
|
|
92
|
-
/** @type {string[]} */
|
|
93
|
-
const contentStrings = [];
|
|
94
|
-
let contentContainsImage = false;
|
|
95
|
-
if (Array.isArray(result.content)) {
|
|
96
|
-
for (const part of result.content) {
|
|
97
|
-
if ("text" in part && typeof part.text === "string") {
|
|
98
|
-
contentParts.push({
|
|
99
|
-
type: "text",
|
|
100
|
-
text: part.text,
|
|
101
|
-
});
|
|
102
|
-
contentStrings.push(part.text);
|
|
103
|
-
} else if (
|
|
104
|
-
part.type === "image" &&
|
|
105
|
-
typeof part.mimeType === "string" &&
|
|
106
|
-
typeof part.data === "string"
|
|
107
|
-
) {
|
|
108
|
-
contentParts.push({
|
|
109
|
-
type: "image",
|
|
110
|
-
data: part.data,
|
|
111
|
-
mimeType: part.mimeType,
|
|
112
|
-
});
|
|
113
|
-
contentContainsImage = true;
|
|
114
|
-
} else {
|
|
115
|
-
console.error(
|
|
116
|
-
`Unsupported content part from MCP: ${JSON.stringify(part)}`,
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (contentContainsImage) {
|
|
123
|
-
return contentParts;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const resultString = contentStrings.join("\n\n") || resultStringRaw;
|
|
127
|
-
|
|
128
|
-
/** @type {string} */
|
|
129
|
-
let formmatted = resultString;
|
|
130
|
-
let fileExtension = "txt";
|
|
131
|
-
|
|
132
|
-
try {
|
|
133
|
-
const parsed = JSON.parse(resultString);
|
|
134
|
-
formmatted = JSON.stringify(parsed, null, 2);
|
|
135
|
-
fileExtension = "json";
|
|
136
|
-
} catch {
|
|
137
|
-
// not JSON
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (formmatted.length <= OUTPUT_MAX_LENGTH) {
|
|
141
|
-
return formmatted;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
const filePath = await writeTmpFile(
|
|
145
|
-
formmatted,
|
|
146
|
-
tool.name,
|
|
147
|
-
fileExtension,
|
|
148
|
-
);
|
|
149
|
-
const lineCount = formmatted.split("\n").length;
|
|
150
|
-
|
|
151
|
-
return [
|
|
152
|
-
`Content is large (${resultString.length} characters, ${lineCount} lines) and saved to ${filePath}`,
|
|
153
|
-
"Use exec_command tool to find relevant parts.",
|
|
154
|
-
].join("\n");
|
|
155
|
-
}),
|
|
156
|
-
};
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
return tools;
|
|
160
|
-
}
|
package/src/model.d.ts
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import type { ToolDefinition } from "./tool";
|
|
2
|
-
|
|
3
|
-
export type CallModel = (input: ModelInput) => Promise<ModelOutput | Error>;
|
|
4
|
-
|
|
5
|
-
export type ModelInput = {
|
|
6
|
-
messages: Message[];
|
|
7
|
-
tools?: ToolDefinition[];
|
|
8
|
-
onPartialMessageContent?: (partialContent: PartialMessageContent) => void;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
export type ModelOutput = {
|
|
12
|
-
message: Message;
|
|
13
|
-
providerTokenUsage?: ProviderTokenUsage;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export type ProviderTokenUsage = Record<
|
|
17
|
-
string,
|
|
18
|
-
number | string | Record<string, number>
|
|
19
|
-
>;
|
|
20
|
-
|
|
21
|
-
export type PartialMessageContent = {
|
|
22
|
-
type: string;
|
|
23
|
-
position: "start" | "stop" | "delta";
|
|
24
|
-
content?: string;
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export type Message = SystemMessage | UserMessage | AssistantMessage;
|
|
28
|
-
|
|
29
|
-
export type SystemMessage = {
|
|
30
|
-
role: "system";
|
|
31
|
-
content: MessageContentText[];
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export type UserMessage = {
|
|
35
|
-
role: "user";
|
|
36
|
-
content: (
|
|
37
|
-
| MessageContentText
|
|
38
|
-
| MessageContentImage
|
|
39
|
-
| MessageContentToolResult
|
|
40
|
-
)[];
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
export type AssistantMessage = {
|
|
44
|
-
role: "assistant";
|
|
45
|
-
content: (
|
|
46
|
-
| MessageContentThinking
|
|
47
|
-
| MessageContentRedactedThinking
|
|
48
|
-
| MessageContentText
|
|
49
|
-
| MessageContentToolUse
|
|
50
|
-
)[];
|
|
51
|
-
provider?: MessageContentProvider;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
export type MessageContentThinking = {
|
|
55
|
-
type: "thinking";
|
|
56
|
-
thinking: string;
|
|
57
|
-
provider?: MessageContentProvider;
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export type MessageContentRedactedThinking = {
|
|
61
|
-
type: "redacted_thinking";
|
|
62
|
-
provider?: MessageContentProvider;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
export type MessageContentText = {
|
|
66
|
-
type: "text";
|
|
67
|
-
text: string;
|
|
68
|
-
provider?: MessageContentProvider;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
export type MessageContentImage = {
|
|
72
|
-
type: "image";
|
|
73
|
-
|
|
74
|
-
// base64 encoded image data
|
|
75
|
-
data: string;
|
|
76
|
-
|
|
77
|
-
// e.g., image/jpeg
|
|
78
|
-
mimeType: string;
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
export type MessageContentToolUse = {
|
|
82
|
-
type: "tool_use";
|
|
83
|
-
toolUseId: string;
|
|
84
|
-
toolName: string;
|
|
85
|
-
input: Record<string, unknown>;
|
|
86
|
-
provider?: MessageContentProvider;
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
export type MessageContentToolResult = {
|
|
90
|
-
type: "tool_result";
|
|
91
|
-
toolUseId: string;
|
|
92
|
-
toolName: string;
|
|
93
|
-
content: (MessageContentText | MessageContentImage)[];
|
|
94
|
-
isError?: boolean;
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
export type MessageContentProvider = {
|
|
98
|
-
/**
|
|
99
|
-
* Raw source data from the provider
|
|
100
|
-
* (original message, response, output items, etc.)
|
|
101
|
-
*/
|
|
102
|
-
source?: unknown;
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Provider-specific fields that are directly merged
|
|
106
|
-
* into the content part sent to the provider API.
|
|
107
|
-
*/
|
|
108
|
-
fields?: Record<string, unknown>;
|
|
109
|
-
};
|
package/src/modelCaller.mjs
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { callAnthropicModel } from "./providers/anthropic.mjs";
|
|
2
|
-
import { callBedrockConverseModel } from "./providers/bedrock.mjs";
|
|
3
|
-
import { createCacheEnabledGeminiModelCaller } from "./providers/gemini.mjs";
|
|
4
|
-
import { callOpenAIModel } from "./providers/openai.mjs";
|
|
5
|
-
import { callOpenAICompatibleModel } from "./providers/openaiCompatible.mjs";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @param {import("./modelDefinition").ModelDefinition} modelDef
|
|
9
|
-
* @returns {import("./model").CallModel}
|
|
10
|
-
*/
|
|
11
|
-
export function createModelCaller(modelDef) {
|
|
12
|
-
const { platform, model } = modelDef;
|
|
13
|
-
|
|
14
|
-
switch (model.format) {
|
|
15
|
-
case "anthropic":
|
|
16
|
-
return (input) => callAnthropicModel(platform, model.config, input);
|
|
17
|
-
case "gemini": {
|
|
18
|
-
const modelCaller = createCacheEnabledGeminiModelCaller(
|
|
19
|
-
platform,
|
|
20
|
-
model.config,
|
|
21
|
-
);
|
|
22
|
-
return (input) => modelCaller(model.config, input);
|
|
23
|
-
}
|
|
24
|
-
case "openai-responses":
|
|
25
|
-
return (input) => callOpenAIModel(platform, model.config, input);
|
|
26
|
-
case "openai-messages":
|
|
27
|
-
return (input) =>
|
|
28
|
-
callOpenAICompatibleModel(platform, model.config, input);
|
|
29
|
-
case "bedrock-converse":
|
|
30
|
-
return (input) => callBedrockConverseModel(platform, model.config, input);
|
|
31
|
-
}
|
|
32
|
-
}
|
package/src/modelDefinition.d.ts
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { AnthropicModelConfig } from "./providers/anthropic";
|
|
2
|
-
import { BedrockConverseModelConfig } from "./providers/bedrock";
|
|
3
|
-
import { GeminiModelConfig } from "./providers/gemini";
|
|
4
|
-
import { OpenAIModelConfig } from "./providers/openai";
|
|
5
|
-
import { OpenAICompatibleModelConfig } from "./providers/openaiCompatible";
|
|
6
|
-
|
|
7
|
-
export type ModelDefinition = {
|
|
8
|
-
name: string;
|
|
9
|
-
variant: string;
|
|
10
|
-
platform: PlatformConfig;
|
|
11
|
-
model: ModelConfig;
|
|
12
|
-
cost?: CostConfig;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
export type PlatformConfig =
|
|
16
|
-
| {
|
|
17
|
-
name: "anthropic";
|
|
18
|
-
variant: string;
|
|
19
|
-
baseURL: string;
|
|
20
|
-
customHeaders?: Record<string, string>;
|
|
21
|
-
apiKey: string;
|
|
22
|
-
}
|
|
23
|
-
| {
|
|
24
|
-
name: "gemini";
|
|
25
|
-
variant: string;
|
|
26
|
-
baseURL: string;
|
|
27
|
-
customHeaders?: Record<string, string>;
|
|
28
|
-
apiKey: string;
|
|
29
|
-
}
|
|
30
|
-
| {
|
|
31
|
-
name: "openai";
|
|
32
|
-
variant: string;
|
|
33
|
-
baseURL: string;
|
|
34
|
-
customHeaders?: Record<string, string>;
|
|
35
|
-
apiKey: string;
|
|
36
|
-
}
|
|
37
|
-
| {
|
|
38
|
-
name: "openai-compatible";
|
|
39
|
-
variant: string;
|
|
40
|
-
baseURL: string;
|
|
41
|
-
customHeaders?: Record<string, string>;
|
|
42
|
-
apiKey: string;
|
|
43
|
-
}
|
|
44
|
-
| {
|
|
45
|
-
name: "azure";
|
|
46
|
-
variant: string;
|
|
47
|
-
baseURL: string;
|
|
48
|
-
customHeaders?: Record<string, string>;
|
|
49
|
-
azureConfigDir?: string;
|
|
50
|
-
}
|
|
51
|
-
| {
|
|
52
|
-
name: "bedrock";
|
|
53
|
-
variant: string;
|
|
54
|
-
baseURL: string;
|
|
55
|
-
customHeaders?: Record<string, string>;
|
|
56
|
-
awsProfile: string;
|
|
57
|
-
}
|
|
58
|
-
| {
|
|
59
|
-
name: "vertex-ai";
|
|
60
|
-
variant: string;
|
|
61
|
-
baseURL: string;
|
|
62
|
-
customHeaders?: Record<string, string>;
|
|
63
|
-
account?: string;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
export type ModelConfig =
|
|
67
|
-
| {
|
|
68
|
-
format: "anthropic";
|
|
69
|
-
config: AnthropicModelConfig;
|
|
70
|
-
}
|
|
71
|
-
| {
|
|
72
|
-
format: "gemini";
|
|
73
|
-
config: GeminiModelConfig;
|
|
74
|
-
}
|
|
75
|
-
| {
|
|
76
|
-
format: "openai-responses";
|
|
77
|
-
config: OpenAIModelConfig;
|
|
78
|
-
}
|
|
79
|
-
| {
|
|
80
|
-
format: "openai-messages";
|
|
81
|
-
config: OpenAICompatibleModelConfig;
|
|
82
|
-
}
|
|
83
|
-
| {
|
|
84
|
-
format: "bedrock-converse";
|
|
85
|
-
config: BedrockConverseModelConfig;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
export type CostConfig = {
|
|
89
|
-
currency: string;
|
|
90
|
-
unit: string;
|
|
91
|
-
costs: Record<string, number>;
|
|
92
|
-
};
|
package/src/prompt.mjs
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {object} PromptConfig
|
|
3
|
-
* @property {string} username
|
|
4
|
-
* @property {string} modelName
|
|
5
|
-
* @property {string} workingDir - The current working directory.
|
|
6
|
-
* @property {string} today - Today's date in YYYY-MM-DD format.
|
|
7
|
-
* @property {string} sessionId
|
|
8
|
-
* @property {string} tmuxSessionId
|
|
9
|
-
* @property {string} projectMetadataDir - The directory where memory files are stored.
|
|
10
|
-
* @property {Map<string, import('./context/loadAgentRoles.mjs').AgentRole>} agentRoles - Available agent roles.
|
|
11
|
-
* @property {{filePath: string, description: string}[]} skills
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @param {PromptConfig} config
|
|
16
|
-
* @returns {string}
|
|
17
|
-
*/
|
|
18
|
-
export function createPrompt({
|
|
19
|
-
username,
|
|
20
|
-
modelName,
|
|
21
|
-
sessionId,
|
|
22
|
-
today,
|
|
23
|
-
tmuxSessionId,
|
|
24
|
-
workingDir,
|
|
25
|
-
projectMetadataDir,
|
|
26
|
-
agentRoles,
|
|
27
|
-
skills,
|
|
28
|
-
}) {
|
|
29
|
-
const agentRoleDescriptions = Array.from(agentRoles.entries())
|
|
30
|
-
.map(([id, role]) => {
|
|
31
|
-
const desc =
|
|
32
|
-
role.description.length > 100
|
|
33
|
-
? `${role.description.substring(0, 100)}...`
|
|
34
|
-
: role.description;
|
|
35
|
-
return `- ${id}: ${desc}`;
|
|
36
|
-
})
|
|
37
|
-
.join("\n");
|
|
38
|
-
|
|
39
|
-
const skillDescriptions = skills
|
|
40
|
-
.map((skill) => {
|
|
41
|
-
const desc =
|
|
42
|
-
skill.description.length > 100
|
|
43
|
-
? `${skill.description.substring(0, 100)}...`
|
|
44
|
-
: skill.description;
|
|
45
|
-
return `- ${skill.filePath}\n ${desc}`;
|
|
46
|
-
})
|
|
47
|
-
.join("\n");
|
|
48
|
-
|
|
49
|
-
return `
|
|
50
|
-
# Communication Style
|
|
51
|
-
|
|
52
|
-
- Respond in the user's language.
|
|
53
|
-
- Address the user by their name, rather than "user".
|
|
54
|
-
- Use emojis sparingly to highlight key points.
|
|
55
|
-
|
|
56
|
-
# Memory Files
|
|
57
|
-
|
|
58
|
-
- Create/Update memory files after creating/updating a plan, completing milestones, encountering issues, or making decisions.
|
|
59
|
-
- Update existing task memory when continuing the same task.
|
|
60
|
-
- Write the memory content in the user's language.
|
|
61
|
-
- Ensure self-containment: The file must be standalone. A reader should fully understand the task context, logic and progress without any other references.
|
|
62
|
-
|
|
63
|
-
Memory files should include:
|
|
64
|
-
- Task overview: What the task is, why it's being done, requirements and constraints
|
|
65
|
-
- Context: Relevant documentation, source files, commands, and resources referenced
|
|
66
|
-
- Progress tracking: Completed milestones with evidence, current status, and next steps
|
|
67
|
-
- Decision records: Important decisions made, alternatives considered, and rationale
|
|
68
|
-
- Findings and learnings: Key discoveries, challenges encountered, and solutions applied
|
|
69
|
-
- Future considerations: Known limitations, potential improvements, and follow-up items
|
|
70
|
-
|
|
71
|
-
# Tools
|
|
72
|
-
|
|
73
|
-
Call multiple tools at once when they don't depend on each other's results.
|
|
74
|
-
|
|
75
|
-
## exec_command
|
|
76
|
-
|
|
77
|
-
- Use relative paths.
|
|
78
|
-
- Avoid bash -c unless pipes (|) or redirection (>, <) are required.
|
|
79
|
-
|
|
80
|
-
Examples:
|
|
81
|
-
- List directories or find files: fd [".", "./", "--max-depth", "3", "--type", "d", "--hidden"]
|
|
82
|
-
- Search for strings: rg ["--heading", "--line-number", "pattern", "./"]
|
|
83
|
-
- Read specific line ranges (max 200 lines): sed ["-n", "1,200p", "file.txt"]
|
|
84
|
-
- Manage GitHub issues and PRs:
|
|
85
|
-
Get PR details: gh ["pr", "view", "123", "--json", "title,body,url"]
|
|
86
|
-
Get PR comment: gh ["api", "--method", "GET", "repos/<owner>/<repo>/pulls/comments/<id>", "--jq", "{user: .user.login, path: .path, line: .line, body: .body}"]
|
|
87
|
-
|
|
88
|
-
## tmux_command
|
|
89
|
-
|
|
90
|
-
- Only use when the user explicitly requests it.
|
|
91
|
-
- Create a new session with the given tmux session id.
|
|
92
|
-
- Use relative paths.
|
|
93
|
-
|
|
94
|
-
Examples:
|
|
95
|
-
- Start session: new-session ["-d", "-s", "<tmux-session-id>"]
|
|
96
|
-
- Detect window number to send keys: list-windows ["-t", "<tmux-session-id>"]
|
|
97
|
-
- Get output of window before sending keys: capture-pane ["-p", "-t", "<tmux-session-id>:<window>"]
|
|
98
|
-
- Send key to session: send-keys ["-t", "<tmux-session-id>:<window>", "echo hello", "Enter"]
|
|
99
|
-
- Delete line: send-keys ["-t", "<tmux-session-id>:<window>", "C-a", "C-k"]
|
|
100
|
-
|
|
101
|
-
# Project Rules and Skills
|
|
102
|
-
|
|
103
|
-
Discover and apply project-specific rules and reusable skills.
|
|
104
|
-
|
|
105
|
-
## AGENTS.md (falling back to CLAUDE.md if not found): Project-specific rules, conventions, and commands.
|
|
106
|
-
|
|
107
|
-
Find: fd ["^(AGENTS|CLAUDE)\\.md$", "./", "--hidden", "--max-depth", "5"]
|
|
108
|
-
Read from the project root to the directory you're working in: ./AGENTS.md → dir/AGENTS.md → dir/subdir/AGENTS.md
|
|
109
|
-
Apply rules when working in that directory
|
|
110
|
-
|
|
111
|
-
## SKILL.md: Reusable workflows with specialized knowledge
|
|
112
|
-
|
|
113
|
-
If skill matches task: read full file and apply the workflow
|
|
114
|
-
|
|
115
|
-
${skillDescriptions}
|
|
116
|
-
|
|
117
|
-
# Environment
|
|
118
|
-
|
|
119
|
-
- User name: ${username}
|
|
120
|
-
- Your model name: ${modelName}
|
|
121
|
-
- Current working directory: ${workingDir}
|
|
122
|
-
- Today's date: ${today}
|
|
123
|
-
- Session id: ${sessionId}
|
|
124
|
-
- Tmux session id: ${tmuxSessionId}
|
|
125
|
-
- Memory file path: ${projectMetadataDir}/memory/${sessionId}--<kebab-case-title>.md
|
|
126
|
-
|
|
127
|
-
Available subagents:
|
|
128
|
-
${agentRoleDescriptions}
|
|
129
|
-
- custom:<role-name>: Use this for ad-hoc roles not listed above (e.g., custom:explore, custom:plan).
|
|
130
|
-
`.trim();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
export const CLAUDE_CODE_COMPATIBILITY_NOTES = `# Environment Constraints
|
|
134
|
-
|
|
135
|
-
- Use memory file to manage todo list.
|
|
136
|
-
- Subagents cannot run in parallel. Delegate to them one at a time.
|
|
137
|
-
- Use AGENTS.md instead when CLAUDE.md is absent.
|
|
138
|
-
- If instructed to use "haiku agent", "sonnet agent", or "opus agent", use "worker" instead.`;
|