@iinm/plain-agent 1.0.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/.config/agents.library/code-simplifier.md +5 -0
- package/.config/agents.library/qa-engineer.md +74 -0
- package/.config/agents.library/software-architect.md +278 -0
- package/.config/agents.predefined/worker.md +3 -0
- package/.config/config.predefined.json +825 -0
- package/.config/prompts.library/code-review.md +8 -0
- package/.config/prompts.library/feature-dev.md +6 -0
- package/.config/prompts.predefined/shortcuts/commit-by-user.md +9 -0
- package/.config/prompts.predefined/shortcuts/commit.md +10 -0
- package/.config/prompts.predefined/shortcuts/general-question.md +6 -0
- package/LICENSE +21 -0
- package/README.md +624 -0
- package/bin/plain +3 -0
- package/bin/plain-interrupt +6 -0
- package/bin/plain-notify-desktop +19 -0
- package/bin/plain-notify-terminal-bell +3 -0
- package/package.json +57 -0
- package/sandbox/bin/plain-sandbox +972 -0
- package/src/agent.d.ts +48 -0
- package/src/agent.mjs +159 -0
- package/src/agentLoop.mjs +369 -0
- package/src/agentState.mjs +41 -0
- package/src/cliArgs.mjs +45 -0
- package/src/cliFormatter.mjs +217 -0
- package/src/cliInteractive.mjs +739 -0
- package/src/config.d.ts +48 -0
- package/src/config.mjs +168 -0
- package/src/context/consumeInterruptMessage.mjs +30 -0
- package/src/context/loadAgentRoles.mjs +272 -0
- package/src/context/loadPrompts.mjs +312 -0
- package/src/context/loadUserMessageContext.mjs +147 -0
- package/src/env.mjs +46 -0
- package/src/main.mjs +202 -0
- package/src/mcp.mjs +202 -0
- package/src/model.d.ts +109 -0
- package/src/modelCaller.mjs +29 -0
- package/src/modelDefinition.d.ts +73 -0
- package/src/prompt.mjs +128 -0
- package/src/providers/anthropic.d.ts +248 -0
- package/src/providers/anthropic.mjs +596 -0
- package/src/providers/gemini.d.ts +208 -0
- package/src/providers/gemini.mjs +752 -0
- package/src/providers/openai.d.ts +281 -0
- package/src/providers/openai.mjs +551 -0
- package/src/providers/openaiCompatible.d.ts +147 -0
- package/src/providers/openaiCompatible.mjs +658 -0
- package/src/providers/platform/azure.mjs +42 -0
- package/src/providers/platform/bedrock.mjs +74 -0
- package/src/providers/platform/googleCloud.mjs +34 -0
- package/src/subagent.mjs +247 -0
- package/src/tmpfile.mjs +27 -0
- package/src/tool.d.ts +74 -0
- package/src/toolExecutor.mjs +236 -0
- package/src/toolInputValidator.mjs +183 -0
- package/src/toolUseApprover.mjs +98 -0
- package/src/tools/askGoogle.mjs +135 -0
- package/src/tools/delegateToSubagent.d.ts +4 -0
- package/src/tools/delegateToSubagent.mjs +48 -0
- package/src/tools/execCommand.d.ts +22 -0
- package/src/tools/execCommand.mjs +200 -0
- package/src/tools/fetchWebPage.mjs +96 -0
- package/src/tools/patchFile.d.ts +4 -0
- package/src/tools/patchFile.mjs +96 -0
- package/src/tools/reportAsSubagent.d.ts +3 -0
- package/src/tools/reportAsSubagent.mjs +44 -0
- package/src/tools/tavilySearch.d.ts +6 -0
- package/src/tools/tavilySearch.mjs +57 -0
- package/src/tools/tmuxCommand.d.ts +14 -0
- package/src/tools/tmuxCommand.mjs +194 -0
- package/src/tools/writeFile.d.ts +4 -0
- package/src/tools/writeFile.mjs +56 -0
- package/src/utils/evalJSONConfig.mjs +48 -0
- package/src/utils/matchValue.d.ts +6 -0
- package/src/utils/matchValue.mjs +40 -0
- package/src/utils/noThrow.mjs +31 -0
- package/src/utils/notify.mjs +28 -0
- package/src/utils/parseFileRange.mjs +18 -0
- package/src/utils/readFileRange.mjs +33 -0
- package/src/utils/retryOnError.mjs +41 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @template T
|
|
3
|
+
* @param {() => Promise<T>} task
|
|
4
|
+
* @returns {Promise<T | Error>}
|
|
5
|
+
*/
|
|
6
|
+
export async function noThrow(task) {
|
|
7
|
+
try {
|
|
8
|
+
return await task();
|
|
9
|
+
} catch (error) {
|
|
10
|
+
if (error instanceof Error) {
|
|
11
|
+
return error;
|
|
12
|
+
}
|
|
13
|
+
return new Error(`Non-Error thrown: ${error}`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @template T
|
|
19
|
+
* @param {() => T} task
|
|
20
|
+
* @returns {T | Error}
|
|
21
|
+
*/
|
|
22
|
+
export function noThrowSync(task) {
|
|
23
|
+
try {
|
|
24
|
+
return task();
|
|
25
|
+
} catch (error) {
|
|
26
|
+
if (error instanceof Error) {
|
|
27
|
+
return error;
|
|
28
|
+
}
|
|
29
|
+
return new Error(`Non-Error thrown: ${error}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { noThrowSync } from "./noThrow.mjs";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {string=} notifyCmd
|
|
6
|
+
* @returns {void | Error}
|
|
7
|
+
*/
|
|
8
|
+
export function notify(notifyCmd) {
|
|
9
|
+
if (!notifyCmd) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return noThrowSync(() => {
|
|
14
|
+
execFileSync(/** @type {string} */ (notifyCmd), [], {
|
|
15
|
+
shell: false,
|
|
16
|
+
stdio: ["ignore", "inherit", "pipe"],
|
|
17
|
+
env: {
|
|
18
|
+
PWD: process.env.PWD,
|
|
19
|
+
PATH: process.env.PATH,
|
|
20
|
+
HOME: process.env.HOME,
|
|
21
|
+
// for Linux
|
|
22
|
+
DISPLAY: process.env.DISPLAY,
|
|
23
|
+
DBUS_SESSION_BUS_ADDRESS: process.env.DBUS_SESSION_BUS_ADDRESS,
|
|
24
|
+
},
|
|
25
|
+
timeout: 10 * 1000,
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param {string} fileRange
|
|
3
|
+
* @returns {{filePath: string, startLine?: number, endLine?: number} | Error}
|
|
4
|
+
*/
|
|
5
|
+
export function parseFileRange(fileRange) {
|
|
6
|
+
const match = fileRange.match(/^([^:]+)(?::(\d+)(?:-(\d+))?)?$/);
|
|
7
|
+
if (!match) {
|
|
8
|
+
return new Error(
|
|
9
|
+
"Invalid format. Use: path/to/file[:line] or path/to/file[:start-end]",
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
const [, filePath, startLine, endLine] = match;
|
|
13
|
+
return {
|
|
14
|
+
filePath,
|
|
15
|
+
startLine: startLine ? Number.parseInt(startLine, 10) : undefined,
|
|
16
|
+
endLine: endLine ? Number.parseInt(endLine, 10) : undefined,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {{filePath: string, startLine?: number, endLine?: number}} fileRange
|
|
5
|
+
* @returns {Promise<string | Error>}
|
|
6
|
+
*/
|
|
7
|
+
export async function readFileRange({ filePath, startLine, endLine }) {
|
|
8
|
+
/** @type {string} */
|
|
9
|
+
let fileContent;
|
|
10
|
+
try {
|
|
11
|
+
fileContent = await fs.readFile(filePath, { encoding: "utf-8" });
|
|
12
|
+
} catch (error) {
|
|
13
|
+
return new Error(
|
|
14
|
+
`Error reading file: ${error instanceof Error ? error.message : String(error)}`,
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const lines = fileContent.split("\n");
|
|
19
|
+
|
|
20
|
+
if (startLine) {
|
|
21
|
+
const start = startLine;
|
|
22
|
+
const end = endLine ? endLine : start;
|
|
23
|
+
|
|
24
|
+
if (!(1 <= start && start <= end && end <= lines.length)) {
|
|
25
|
+
return new Error(
|
|
26
|
+
`Invalid line range. File ${filePath} has ${lines.length} lines.`,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return lines.slice(start - 1, end).join("\n");
|
|
31
|
+
}
|
|
32
|
+
return fileContent;
|
|
33
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} RetryOnErrorConfig
|
|
3
|
+
* @property {(err: unknown) => boolean} shouldRetry
|
|
4
|
+
* @property {(err: unknown, interval: number) => Promise<void>} [beforeRetry]
|
|
5
|
+
* @property {number} initialInterval
|
|
6
|
+
* @property {number} maxInterval
|
|
7
|
+
* @property {number} multiplier
|
|
8
|
+
* @property {number} maxAttempt
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @template T
|
|
13
|
+
* @param {() => Promise<T>} fn
|
|
14
|
+
* @param {RetryOnErrorConfig} config
|
|
15
|
+
* @returns {Promise<T>}
|
|
16
|
+
*/
|
|
17
|
+
export async function retryOnError(fn, config) {
|
|
18
|
+
let attempt = 0;
|
|
19
|
+
|
|
20
|
+
while (true) {
|
|
21
|
+
try {
|
|
22
|
+
attempt++;
|
|
23
|
+
return await fn();
|
|
24
|
+
} catch (err) {
|
|
25
|
+
if (attempt >= config.maxAttempt || !config.shouldRetry(err)) {
|
|
26
|
+
throw err;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const interval = Math.min(
|
|
30
|
+
config.initialInterval * config.multiplier ** (attempt - 1),
|
|
31
|
+
config.maxInterval,
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
if (config.beforeRetry) {
|
|
35
|
+
await config.beforeRetry(err, interval);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
await new Promise((resolve) => setTimeout(resolve, interval));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|