@iinm/plain-agent 1.10.21 → 1.10.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 +10 -2
- package/config/config.predefined.json +9 -0
- package/package.json +1 -1
- package/src/config.d.ts +1 -0
- package/src/config.mjs +6 -0
- package/src/main.mjs +4 -4
- package/src/prompt.mjs +0 -16
- package/src/tools/tmuxCommand.mjs +11 -1
- package/src/utils/evalJSONConfig.mjs +17 -0
package/README.md
CHANGED
|
@@ -360,7 +360,7 @@ Files are loaded in the following order. Settings in later files override earlie
|
|
|
360
360
|
"action": "allow"
|
|
361
361
|
},
|
|
362
362
|
{
|
|
363
|
-
"toolName":
|
|
363
|
+
"toolName": "exec_command",
|
|
364
364
|
"action": "allow"
|
|
365
365
|
},
|
|
366
366
|
{
|
|
@@ -426,6 +426,14 @@ Files are loaded in the following order. Settings in later files override earlie
|
|
|
426
426
|
]
|
|
427
427
|
},
|
|
428
428
|
|
|
429
|
+
"tools": {
|
|
430
|
+
// Enable web tools. See Quick Start section.
|
|
431
|
+
"webSearch": {},
|
|
432
|
+
"webFetch": {},
|
|
433
|
+
// Enable the tmux tool
|
|
434
|
+
"tmux": { "enabled": true }
|
|
435
|
+
},
|
|
436
|
+
|
|
429
437
|
// Sandbox environment for the exec_command and tmux_command tools
|
|
430
438
|
"sandbox": {
|
|
431
439
|
// Commands are wrapped and executed with this command
|
|
@@ -489,7 +497,7 @@ The agent can use the following tools:
|
|
|
489
497
|
- **write_file**: Write a file.
|
|
490
498
|
- **patch_file**: Patch a file.
|
|
491
499
|
- **exec_command**: Run a command without shell interpretation.
|
|
492
|
-
- **tmux_command**: Run a tmux command.
|
|
500
|
+
- **tmux_command**: Run a tmux command. It is disabled by default.
|
|
493
501
|
- **web_search**: Search the web with one or more keyword sets and answer a question based on the combined results (requires Google API key, Vertex AI configuration, or the `command` provider with a local search command).
|
|
494
502
|
- **web_fetch**: Fetch the contents of a single URL and answer a question based on it (requires Google API key, Vertex AI configuration, or the `command` provider with a local fetch command such as `w3m`, `curl`, or `lynx`).
|
|
495
503
|
- **switch_to_subagent**: Switch to a subagent role within the same conversation, focusing on the specified goal.
|
|
@@ -9,6 +9,15 @@
|
|
|
9
9
|
"action": "deny",
|
|
10
10
|
"reason": "Use rg or fd instead"
|
|
11
11
|
},
|
|
12
|
+
{
|
|
13
|
+
"toolName": "exec_command",
|
|
14
|
+
"input": {
|
|
15
|
+
"command": "bash",
|
|
16
|
+
"args": ["-c", { "$not": { "$regex": "[|><&;$`]" } }]
|
|
17
|
+
},
|
|
18
|
+
"action": "deny",
|
|
19
|
+
"reason": "Use bash -c only when shell features (|, >, <, &, ;, $, `) are required"
|
|
20
|
+
},
|
|
12
21
|
{
|
|
13
22
|
"toolName": "exec_command",
|
|
14
23
|
"input": {
|
package/package.json
CHANGED
package/src/config.d.ts
CHANGED
package/src/config.mjs
CHANGED
|
@@ -95,6 +95,12 @@ export async function loadAppConfig(options = {}) {
|
|
|
95
95
|
...config.tools.webFetch,
|
|
96
96
|
}
|
|
97
97
|
: merged.tools?.webFetch,
|
|
98
|
+
tmux: config.tools?.tmux
|
|
99
|
+
? {
|
|
100
|
+
...(merged.tools?.tmux ?? {}),
|
|
101
|
+
...config.tools.tmux,
|
|
102
|
+
}
|
|
103
|
+
: merged.tools?.tmux,
|
|
98
104
|
},
|
|
99
105
|
mcpServers: {
|
|
100
106
|
...(merged.mcpServers ?? {}),
|
package/src/main.mjs
CHANGED
|
@@ -139,8 +139,6 @@ export async function main(argv = process.argv) {
|
|
|
139
139
|
? new Date(resumedState.startTime)
|
|
140
140
|
: new Date();
|
|
141
141
|
const sessionId = resumedState ? resumedState.sessionId : generateSessionId();
|
|
142
|
-
const tmuxSessionId = `agent-${sessionId}`;
|
|
143
|
-
|
|
144
142
|
const isBatchMode = cliArgs.subcommand.type === "batch";
|
|
145
143
|
/** @type {string[]} */
|
|
146
144
|
const configFiles =
|
|
@@ -294,7 +292,6 @@ export async function main(argv = process.argv) {
|
|
|
294
292
|
workingDir: process.cwd(),
|
|
295
293
|
today: new Date().toISOString().split("T")[0],
|
|
296
294
|
sessionId,
|
|
297
|
-
tmuxSessionId,
|
|
298
295
|
projectMetadataDir: AGENT_PROJECT_METADATA_DIR,
|
|
299
296
|
agentRoles,
|
|
300
297
|
skills: Array.from(prompts.values()).filter((p) => p.isSkill),
|
|
@@ -305,12 +302,15 @@ export async function main(argv = process.argv) {
|
|
|
305
302
|
readFileTool,
|
|
306
303
|
writeFileTool,
|
|
307
304
|
createPatchFileTool(),
|
|
308
|
-
createTmuxCommandTool({ sandbox: appConfig.sandbox }),
|
|
309
305
|
createCompactContextTool(),
|
|
310
306
|
createSwitchToSubagentTool(),
|
|
311
307
|
createSwitchToMainAgentTool(),
|
|
312
308
|
];
|
|
313
309
|
|
|
310
|
+
if (appConfig.tools?.tmux?.enabled) {
|
|
311
|
+
builtinTools.push(createTmuxCommandTool({ sandbox: appConfig.sandbox }));
|
|
312
|
+
}
|
|
313
|
+
|
|
314
314
|
if (appConfig.tools?.webSearch) {
|
|
315
315
|
const webSearchConfig = appConfig.tools.webSearch;
|
|
316
316
|
if (webSearchConfig.provider === "command") {
|
package/src/prompt.mjs
CHANGED
|
@@ -7,7 +7,6 @@ import { toOneLine } from "./utils/toOneLine.mjs";
|
|
|
7
7
|
* @property {string} workingDir - The current working directory.
|
|
8
8
|
* @property {string} today - Today's date in YYYY-MM-DD format.
|
|
9
9
|
* @property {string} sessionId
|
|
10
|
-
* @property {string} tmuxSessionId
|
|
11
10
|
* @property {string} projectMetadataDir - The directory where memory files are stored.
|
|
12
11
|
* @property {Map<string, import('./context/loadAgentRoles.mjs').AgentRole>} agentRoles - Available agent roles.
|
|
13
12
|
* @property {{filePath: string, description: string}[]} skills
|
|
@@ -22,7 +21,6 @@ export function createPrompt({
|
|
|
22
21
|
modelName,
|
|
23
22
|
sessionId,
|
|
24
23
|
today,
|
|
25
|
-
tmuxSessionId,
|
|
26
24
|
workingDir,
|
|
27
25
|
projectMetadataDir,
|
|
28
26
|
agentRoles,
|
|
@@ -75,7 +73,6 @@ Always read the target lines with \`read_file\` first to verify line numbers and
|
|
|
75
73
|
|
|
76
74
|
- Use relative paths for files inside the working directory, absolute paths for files outside.
|
|
77
75
|
- Use ${projectMetadataDir}/tmp/ for temporary files.
|
|
78
|
-
- Use bash -c only when pipes (|) or redirection (>, <) are required.
|
|
79
76
|
|
|
80
77
|
Examples:
|
|
81
78
|
- List directories or find files: fd [".", "./", "--max-depth", "3", "--type", "d", "--hidden"]
|
|
@@ -84,18 +81,6 @@ Examples:
|
|
|
84
81
|
Get PR details: gh ["pr", "view", "123", "--json", "title,body,url"]
|
|
85
82
|
Get PR comment: gh ["api", "--method", "GET", "repos/<owner>/<repo>/pulls/comments/<id>", "--jq", "{user: .user.login, path: .path, line: .line, body: .body}"]
|
|
86
83
|
|
|
87
|
-
## tmux_command
|
|
88
|
-
|
|
89
|
-
- Use only when the user explicitly requests it.
|
|
90
|
-
- Create a new session with the given tmux session id.
|
|
91
|
-
|
|
92
|
-
Examples:
|
|
93
|
-
- Start session: new-session ["-d", "-s", "<tmux-session-id>"]
|
|
94
|
-
- Detect window number to send keys: list-windows ["-t", "<tmux-session-id>"]
|
|
95
|
-
- Get output of window before sending keys: capture-pane ["-p", "-t", "<tmux-session-id>:<window>"]
|
|
96
|
-
- Send key to session: send-keys ["-t", "<tmux-session-id>:<window>", "echo hello", "Enter"]
|
|
97
|
-
- Delete line: send-keys ["-t", "<tmux-session-id>:<window>", "C-a", "C-k"]
|
|
98
|
-
|
|
99
84
|
# Project Rules and Skills
|
|
100
85
|
|
|
101
86
|
Discover and apply project-specific rules and reusable skills.
|
|
@@ -119,7 +104,6 @@ ${skillDescriptions}
|
|
|
119
104
|
- Current working directory: ${workingDir}
|
|
120
105
|
- Today's date: ${today}
|
|
121
106
|
- Session id: ${sessionId}
|
|
122
|
-
- Tmux session id: ${tmuxSessionId}
|
|
123
107
|
- Memory file path: ${projectMetadataDir}/memory/${sessionId}--<kebab-case-title>.md
|
|
124
108
|
|
|
125
109
|
Available subagents:
|
|
@@ -18,7 +18,17 @@ export function createTmuxCommandTool(config) {
|
|
|
18
18
|
return {
|
|
19
19
|
def: {
|
|
20
20
|
name: "tmux_command",
|
|
21
|
-
description:
|
|
21
|
+
description: [
|
|
22
|
+
"Run a tmux command.",
|
|
23
|
+
"The tmux session id is plain-agent-<session-id>.",
|
|
24
|
+
"",
|
|
25
|
+
"Examples:",
|
|
26
|
+
'- Start session: new-session ["-d", "-s", "<tmux-session-id>"]',
|
|
27
|
+
'- Detect window number to send keys: list-windows ["-t", "<tmux-session-id>"]',
|
|
28
|
+
'- Get output of window before sending keys: capture-pane ["-p", "-t", "<tmux-session-id>:<window>"]',
|
|
29
|
+
'- Send key to session: send-keys ["-t", "<tmux-session-id>:<window>", "echo hello", "Enter"]',
|
|
30
|
+
'- Delete line: send-keys ["-t", "<tmux-session-id>:<window>", "C-a", "C-k"]',
|
|
31
|
+
].join("\n"),
|
|
22
32
|
inputSchema: {
|
|
23
33
|
type: "object",
|
|
24
34
|
properties: {
|
|
@@ -40,6 +40,23 @@ export function evalJSONConfig(configItem) {
|
|
|
40
40
|
);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
if (Object.keys(configItem).length === 1 && "$not" in configItem) {
|
|
44
|
+
const pattern = evalJSONConfig(configItem.$not);
|
|
45
|
+
/** @param {unknown} value */
|
|
46
|
+
return (value) => {
|
|
47
|
+
if (typeof pattern === "string") {
|
|
48
|
+
return value !== pattern;
|
|
49
|
+
}
|
|
50
|
+
if (pattern instanceof RegExp) {
|
|
51
|
+
return typeof value !== "string" || !pattern.test(value);
|
|
52
|
+
}
|
|
53
|
+
if (typeof pattern === "function") {
|
|
54
|
+
return !pattern(value);
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
43
60
|
if (Object.keys(configItem).length === 1 && "$has" in configItem) {
|
|
44
61
|
const pattern = evalJSONConfig(configItem.$has);
|
|
45
62
|
/** @param {unknown} value */
|