@fitlab-ai/agent-infra 0.5.9 → 0.5.10
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 +200 -8
- package/README.zh-CN.md +176 -8
- package/bin/cli.js +2 -2
- package/lib/init.js +18 -4
- package/lib/sandbox/commands/create.js +467 -240
- package/lib/sandbox/commands/enter.js +59 -26
- package/lib/sandbox/commands/ls.js +37 -6
- package/lib/sandbox/commands/rebuild.js +31 -15
- package/lib/sandbox/commands/refresh.js +119 -0
- package/lib/sandbox/commands/rm.js +59 -11
- package/lib/sandbox/commands/vm.js +50 -6
- package/lib/sandbox/config.js +9 -5
- package/lib/sandbox/constants.js +15 -3
- package/lib/sandbox/credentials.js +520 -0
- package/lib/sandbox/dotfiles.js +189 -0
- package/lib/sandbox/engine.js +135 -192
- package/lib/sandbox/engines/colima.js +79 -0
- package/lib/sandbox/engines/docker-desktop.js +34 -0
- package/lib/sandbox/engines/index.js +27 -0
- package/lib/sandbox/engines/native.js +112 -0
- package/lib/sandbox/engines/orbstack.js +76 -0
- package/lib/sandbox/engines/selinux.js +60 -0
- package/lib/sandbox/engines/wsl2-paths.js +59 -0
- package/lib/sandbox/engines/wsl2.js +72 -0
- package/lib/sandbox/index.js +10 -1
- package/lib/sandbox/runtimes/ai-tools.dockerfile +14 -1
- package/lib/sandbox/runtimes/base.dockerfile +116 -1
- package/lib/sandbox/shell.js +53 -2
- package/lib/sandbox/tools.js +5 -5
- package/package.json +6 -4
- package/templates/.agents/rules/create-issue.github.en.md +2 -4
- package/templates/.agents/rules/create-issue.github.zh-CN.md +2 -4
- package/templates/.agents/rules/issue-pr-commands.github.en.md +29 -0
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +29 -0
- package/templates/.agents/scripts/{platform-adapters/find-existing-task.github.js → find-existing-task.js} +22 -79
- package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +26 -41
- package/templates/.agents/skills/create-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +6 -8
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +6 -8
- package/templates/.agents/scripts/platform-adapters/find-existing-task.js +0 -5
package/lib/sandbox/tools.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
1
|
import { safeNameCandidates, sanitizeBranchName } from './constants.js';
|
|
2
|
+
import { hostJoin } from './engines/wsl2-paths.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @typedef {Object} SandboxTool
|
|
@@ -18,10 +18,6 @@ import { safeNameCandidates, sanitizeBranchName } from './constants.js';
|
|
|
18
18
|
* @property {string[]=} postSetupCmds
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
function hostJoin(basePath, ...segments) {
|
|
22
|
-
return basePath.startsWith('/') ? path.posix.join(basePath, ...segments) : path.join(basePath, ...segments);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
21
|
function createBuiltinTools(home, project) {
|
|
26
22
|
/** @type {Record<string, SandboxTool>} */
|
|
27
23
|
return {
|
|
@@ -78,6 +74,10 @@ function createBuiltinTools(home, project) {
|
|
|
78
74
|
containerMount: '/home/devuser/.local/share/opencode',
|
|
79
75
|
versionCmd: 'opencode version',
|
|
80
76
|
setupHint: 'Configure OpenCode credentials inside the container before first use.',
|
|
77
|
+
// OpenCode reads opencode.json from $XDG_CONFIG_HOME/opencode by default,
|
|
78
|
+
// outside this tool mount. Pin the config file path so the inherited
|
|
79
|
+
// sandbox opencode.json is the one the TUI actually reads.
|
|
80
|
+
envVars: { OPENCODE_CONFIG: '/home/devuser/.local/share/opencode/opencode.json' },
|
|
81
81
|
hostLiveMounts: [
|
|
82
82
|
{
|
|
83
83
|
hostPath: hostJoin(home, '.local', 'share', 'opencode', 'auth.json'),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fitlab-ai/agent-infra",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.10",
|
|
4
4
|
"description": "Bootstrap tool for AI multi-tool collaboration infrastructure — works with Claude Code, Codex, Gemini CLI, and OpenCode",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"templates/"
|
|
28
28
|
],
|
|
29
29
|
"engines": {
|
|
30
|
-
"node": ">=
|
|
30
|
+
"node": ">=22"
|
|
31
31
|
},
|
|
32
32
|
"keywords": [
|
|
33
33
|
"ai",
|
|
@@ -41,8 +41,10 @@
|
|
|
41
41
|
"installer"
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@clack/prompts": "1.
|
|
45
|
-
"
|
|
44
|
+
"@clack/prompts": "1.4.0",
|
|
45
|
+
"cross-spawn": "^7.0.6",
|
|
46
|
+
"picocolors": "1.1.1",
|
|
47
|
+
"smol-toml": "^1.6.1"
|
|
46
48
|
},
|
|
47
49
|
"scripts": {
|
|
48
50
|
"build": "node scripts/build-inline.js",
|
|
@@ -155,10 +155,8 @@ Update task.md:
|
|
|
155
155
|
|
|
156
156
|
- Write `issue_number: {n}` into the frontmatter (replace if it exists; append at the end of the frontmatter otherwise)
|
|
157
157
|
- Update `updated_at` to the current time (command: `date "+%Y-%m-%d %H:%M:%S%:z"`)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
- {YYYY-MM-DD HH:mm:ss±HH:MM} — **Create Issue** by {agent} — Created GitHub Issue #{n}
|
|
161
|
-
```
|
|
158
|
+
|
|
159
|
+
> Do NOT append an Activity Log entry here. The Issue creation event is already captured by the GitHub Issue itself and by the frontmatter `issue_number` field; the Activity Log only records the single `create-task` skill execution anchor (`Task Created`), written by the caller SKILL step 3.
|
|
162
160
|
|
|
163
161
|
### 8. Return the Result
|
|
164
162
|
|
|
@@ -155,10 +155,8 @@ gh api "repos/$upstream_repo/issues/{issue-number}" -X PATCH \
|
|
|
155
155
|
|
|
156
156
|
- 把 `issue_number: {n}` 写入 frontmatter(已存在则替换;不存在则在 frontmatter 末尾追加)
|
|
157
157
|
- 更新 `updated_at` 为当前时间(命令:`date "+%Y-%m-%d %H:%M:%S%:z"`)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
- {YYYY-MM-DD HH:mm:ss±HH:MM} — **Create Issue** by {agent} — Created GitHub Issue #{n}
|
|
161
|
-
```
|
|
158
|
+
|
|
159
|
+
> 不要在此追加 Activity Log 条目。Issue 创建事件已由 GitHub Issue 自身和 frontmatter `issue_number` 承载;Activity Log 仅记录 `create-task` skill 一次执行的整体锚点(`Task Created`),由调用方 SKILL 步骤 3 写入。
|
|
162
160
|
|
|
163
161
|
### 8. 返回结果
|
|
164
162
|
|
|
@@ -120,6 +120,35 @@ Read Issue comments or search for existing hidden markers:
|
|
|
120
120
|
gh api "repos/$upstream_repo/issues/{issue-number}/comments" --paginate
|
|
121
121
|
```
|
|
122
122
|
|
|
123
|
+
## Historical Task Comment Scan
|
|
124
|
+
|
|
125
|
+
`find-existing-task.js` only consumes stdin and does not call `gh` directly. The AI selects the pipeline command for the host OS.
|
|
126
|
+
|
|
127
|
+
POSIX (bash / zsh):
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
set -o pipefail
|
|
131
|
+
gh api "repos/$upstream_repo/issues/{issue-number}/comments" \
|
|
132
|
+
--paginate --jq '.[] | @json' \
|
|
133
|
+
| node .agents/scripts/find-existing-task.js
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Windows (PowerShell 7+ / pwsh):
|
|
137
|
+
|
|
138
|
+
```powershell
|
|
139
|
+
$ErrorActionPreference = 'Stop'
|
|
140
|
+
gh api "repos/$upstream_repo/issues/{issue-number}/comments" `
|
|
141
|
+
--paginate --jq '.[] | @json' |
|
|
142
|
+
node .agents/scripts/find-existing-task.js
|
|
143
|
+
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
On PowerShell 5.1, explicitly enable UTF-8 stdio first; otherwise the pipe may corrupt multibyte characters:
|
|
147
|
+
|
|
148
|
+
```powershell
|
|
149
|
+
[Console]::OutputEncoding = $OutputEncoding = [System.Text.UTF8Encoding]::new()
|
|
150
|
+
```
|
|
151
|
+
|
|
123
152
|
## PR Template and Metadata Helpers
|
|
124
153
|
|
|
125
154
|
Read a repository PR template when present:
|
|
@@ -120,6 +120,35 @@ gh issue close {issue-number} -R "$upstream_repo" --reason "{reason}"
|
|
|
120
120
|
gh api "repos/$upstream_repo/issues/{issue-number}/comments" --paginate
|
|
121
121
|
```
|
|
122
122
|
|
|
123
|
+
## 历史任务评论扫描
|
|
124
|
+
|
|
125
|
+
`find-existing-task.js` 仅消费 stdin,不直接调用 `gh`。由 AI 按宿主 OS 选择下面的 pipeline 命令。
|
|
126
|
+
|
|
127
|
+
POSIX(bash / zsh):
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
set -o pipefail
|
|
131
|
+
gh api "repos/$upstream_repo/issues/{issue-number}/comments" \
|
|
132
|
+
--paginate --jq '.[] | @json' \
|
|
133
|
+
| node .agents/scripts/find-existing-task.js
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Windows(PowerShell 7+ / pwsh):
|
|
137
|
+
|
|
138
|
+
```powershell
|
|
139
|
+
$ErrorActionPreference = 'Stop'
|
|
140
|
+
gh api "repos/$upstream_repo/issues/{issue-number}/comments" `
|
|
141
|
+
--paginate --jq '.[] | @json' |
|
|
142
|
+
node .agents/scripts/find-existing-task.js
|
|
143
|
+
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
在 PowerShell 5.1 上需先显式启用 UTF-8 stdio,否则 pipe 可能损坏多字节字符:
|
|
147
|
+
|
|
148
|
+
```powershell
|
|
149
|
+
[Console]::OutputEncoding = $OutputEncoding = [System.Text.UTF8Encoding]::new()
|
|
150
|
+
```
|
|
151
|
+
|
|
123
152
|
## PR 模板与元数据辅助命令
|
|
124
153
|
|
|
125
154
|
存在仓库 PR 模板时读取:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { text } from "node:stream/consumers";
|
|
2
2
|
import { pathToFileURL } from "node:url";
|
|
3
3
|
|
|
4
4
|
const markerPattern = /^<!-- sync-issue:(TASK-\d{8}-\d{6}):([a-z][a-z0-9-]*) -->$/;
|
|
@@ -10,16 +10,6 @@ function parseArgs(argv) {
|
|
|
10
10
|
|
|
11
11
|
for (let index = 0; index < argv.length; index += 1) {
|
|
12
12
|
const arg = argv[index];
|
|
13
|
-
if (arg === "--issue") {
|
|
14
|
-
args.issue = argv[index + 1];
|
|
15
|
-
index += 1;
|
|
16
|
-
continue;
|
|
17
|
-
}
|
|
18
|
-
if (arg === "--repo") {
|
|
19
|
-
args.repo = argv[index + 1];
|
|
20
|
-
index += 1;
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
13
|
if (arg === "--format") {
|
|
24
14
|
args.format = argv[index + 1];
|
|
25
15
|
index += 1;
|
|
@@ -38,53 +28,12 @@ function parseArgs(argv) {
|
|
|
38
28
|
function usage() {
|
|
39
29
|
return [
|
|
40
30
|
"Usage:",
|
|
41
|
-
" node .agents/scripts/
|
|
31
|
+
" <issue comments JSON via stdin> | node .agents/scripts/find-existing-task.js [--format json]",
|
|
32
|
+
"",
|
|
33
|
+
"Reads issue comments JSON (JSON Lines or JSON Array) from stdin and prints {found, task_id, frontmatter?}."
|
|
42
34
|
].join("\n");
|
|
43
35
|
}
|
|
44
36
|
|
|
45
|
-
function runGh(args) {
|
|
46
|
-
const ghBin = process.env.IMPORT_ISSUE_GH_BIN || "gh";
|
|
47
|
-
const result = spawnSync(ghBin, args, {
|
|
48
|
-
encoding: "utf8",
|
|
49
|
-
maxBuffer: 10 * 1024 * 1024
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
if (result.error) {
|
|
53
|
-
const error = new Error(`gh command failed: ${result.error.message}`);
|
|
54
|
-
error.stderr = result.error.message;
|
|
55
|
-
throw error;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (result.status !== 0) {
|
|
59
|
-
const stderr = result.stderr.trim() || `gh exited with status ${result.status}`;
|
|
60
|
-
const error = new Error(stderr);
|
|
61
|
-
error.stderr = stderr;
|
|
62
|
-
throw error;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return result.stdout;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function resolveRepo(explicitRepo) {
|
|
69
|
-
if (explicitRepo) {
|
|
70
|
-
return explicitRepo;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const currentRepo = runGh(["repo", "view", "--json", "nameWithOwner", "-q", ".nameWithOwner"]).trim();
|
|
74
|
-
if (!currentRepo) {
|
|
75
|
-
throw new Error("Cannot detect current GitHub repository");
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const upstreamRepo = runGh([
|
|
79
|
-
"api",
|
|
80
|
-
`repos/${currentRepo}`,
|
|
81
|
-
"--jq",
|
|
82
|
-
"if .fork then .parent.full_name else .full_name end"
|
|
83
|
-
]).trim();
|
|
84
|
-
|
|
85
|
-
return upstreamRepo || currentRepo;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
37
|
function parseComments(output) {
|
|
89
38
|
const trimmed = output.trim();
|
|
90
39
|
if (!trimmed) {
|
|
@@ -102,18 +51,6 @@ function parseComments(output) {
|
|
|
102
51
|
}
|
|
103
52
|
}
|
|
104
53
|
|
|
105
|
-
function fetchComments(repo, issueNumber) {
|
|
106
|
-
const output = runGh([
|
|
107
|
-
"api",
|
|
108
|
-
`repos/${repo}/issues/${issueNumber}/comments`,
|
|
109
|
-
"--paginate",
|
|
110
|
-
"--jq",
|
|
111
|
-
".[] | @json"
|
|
112
|
-
]);
|
|
113
|
-
|
|
114
|
-
return parseComments(output);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
54
|
function firstLine(value) {
|
|
118
55
|
return String(value || "").split(/\r?\n/, 1)[0].trim();
|
|
119
56
|
}
|
|
@@ -231,7 +168,7 @@ function buildResult(comments) {
|
|
|
231
168
|
return result;
|
|
232
169
|
}
|
|
233
170
|
|
|
234
|
-
function main() {
|
|
171
|
+
async function main() {
|
|
235
172
|
let args;
|
|
236
173
|
try {
|
|
237
174
|
args = parseArgs(process.argv.slice(2));
|
|
@@ -246,27 +183,33 @@ function main() {
|
|
|
246
183
|
return;
|
|
247
184
|
}
|
|
248
185
|
|
|
249
|
-
if (
|
|
250
|
-
console.error(
|
|
251
|
-
console.error(usage());
|
|
186
|
+
if (args.format !== "json") {
|
|
187
|
+
console.error(`Unsupported format: ${args.format}`);
|
|
252
188
|
process.exit(1);
|
|
253
189
|
}
|
|
254
190
|
|
|
255
|
-
|
|
256
|
-
|
|
191
|
+
let raw;
|
|
192
|
+
try {
|
|
193
|
+
raw = await text(process.stdin);
|
|
194
|
+
} catch (error) {
|
|
195
|
+
console.error(`Cannot read stdin: ${error.message}`);
|
|
257
196
|
process.exit(1);
|
|
258
197
|
}
|
|
259
198
|
|
|
199
|
+
let comments;
|
|
260
200
|
try {
|
|
261
|
-
|
|
262
|
-
const comments = fetchComments(repo, args.issue);
|
|
263
|
-
console.log(JSON.stringify(buildResult(comments), null, 2));
|
|
201
|
+
comments = parseComments(raw);
|
|
264
202
|
} catch (error) {
|
|
265
|
-
console.error(`Cannot
|
|
266
|
-
process.exit(
|
|
203
|
+
console.error(`Cannot parse stdin as JSON: ${error.message}`);
|
|
204
|
+
process.exit(1);
|
|
267
205
|
}
|
|
206
|
+
|
|
207
|
+
console.log(JSON.stringify(buildResult(comments), null, 2));
|
|
268
208
|
}
|
|
269
209
|
|
|
270
210
|
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
271
|
-
main()
|
|
211
|
+
main().catch((error) => {
|
|
212
|
+
console.error(error.stack || error.message);
|
|
213
|
+
process.exit(1);
|
|
214
|
+
});
|
|
272
215
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import process from "node:process";
|
|
4
|
-
import
|
|
4
|
+
import spawn from "cross-spawn";
|
|
5
5
|
|
|
6
6
|
const CHECK_TYPE = "platform-sync";
|
|
7
7
|
const DEFAULT_RETRY_DELAYS_MS = [3000, 10000];
|
|
@@ -1034,7 +1034,7 @@ function resolveUpstreamRepo(taskDir) {
|
|
|
1034
1034
|
}
|
|
1035
1035
|
|
|
1036
1036
|
function resolveOwnerRepo(taskDir) {
|
|
1037
|
-
const gitResult =
|
|
1037
|
+
const gitResult = spawn.sync("git", ["remote", "get-url", "origin"], {
|
|
1038
1038
|
cwd: taskDir,
|
|
1039
1039
|
encoding: "utf8"
|
|
1040
1040
|
});
|
|
@@ -1097,12 +1097,12 @@ function ghText(args, cwd) {
|
|
|
1097
1097
|
}
|
|
1098
1098
|
|
|
1099
1099
|
function ghCommand(args, cwd) {
|
|
1100
|
-
const
|
|
1101
|
-
const result =
|
|
1100
|
+
const gh = resolveGhCommand();
|
|
1101
|
+
const result = spawn.sync(gh.command, [...gh.preArgs, ...args], {
|
|
1102
1102
|
cwd,
|
|
1103
1103
|
encoding: "utf8",
|
|
1104
1104
|
env: process.env
|
|
1105
|
-
})
|
|
1105
|
+
});
|
|
1106
1106
|
|
|
1107
1107
|
if (result.status !== 0) {
|
|
1108
1108
|
const stderr = `${result.stderr || ""}${result.stdout || ""}`.trim();
|
|
@@ -1113,17 +1113,35 @@ function ghCommand(args, cwd) {
|
|
|
1113
1113
|
return { ok: true, value: result.stdout };
|
|
1114
1114
|
}
|
|
1115
1115
|
|
|
1116
|
+
function resolveGhCommand() {
|
|
1117
|
+
const command = process.env.AGENT_INFRA_GH_BIN || "gh";
|
|
1118
|
+
const rawPreArgs = process.env.AGENT_INFRA_GH_ARGS_JSON;
|
|
1119
|
+
if (!rawPreArgs) {
|
|
1120
|
+
return { command, preArgs: [] };
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
try {
|
|
1124
|
+
const preArgs = JSON.parse(rawPreArgs);
|
|
1125
|
+
if (Array.isArray(preArgs) && preArgs.every((arg) => typeof arg === "string")) {
|
|
1126
|
+
return { command, preArgs };
|
|
1127
|
+
}
|
|
1128
|
+
} catch {
|
|
1129
|
+
return { command, preArgs: [] };
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
return { command, preArgs: [] };
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1116
1135
|
function ghPaginatedJson(args, cwd) {
|
|
1117
1136
|
return ghJson(args, cwd);
|
|
1118
1137
|
}
|
|
1119
1138
|
|
|
1120
1139
|
function gitText(args, cwd) {
|
|
1121
|
-
const
|
|
1122
|
-
const result = spawnSync(command, args, commandOptions(command, {
|
|
1140
|
+
const result = spawn.sync("git", args, {
|
|
1123
1141
|
cwd,
|
|
1124
1142
|
encoding: "utf8",
|
|
1125
1143
|
env: process.env
|
|
1126
|
-
})
|
|
1144
|
+
});
|
|
1127
1145
|
|
|
1128
1146
|
if (result.status !== 0) {
|
|
1129
1147
|
const stderr = `${result.stderr || ""}${result.stdout || ""}`.trim();
|
|
@@ -1237,39 +1255,6 @@ function sleep(delayMs) {
|
|
|
1237
1255
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delayMs);
|
|
1238
1256
|
}
|
|
1239
1257
|
|
|
1240
|
-
function resolveCommand(cmd) {
|
|
1241
|
-
if (process.platform !== "win32" || path.extname(cmd)) {
|
|
1242
|
-
return cmd;
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
const pathValue = process.env.Path || process.env.PATH || "";
|
|
1246
|
-
const extensions = (process.env.PATHEXT || ".COM;.EXE;.BAT;.CMD")
|
|
1247
|
-
.split(";")
|
|
1248
|
-
.filter(Boolean);
|
|
1249
|
-
|
|
1250
|
-
for (const dir of pathValue.split(path.delimiter).filter(Boolean)) {
|
|
1251
|
-
for (const extension of extensions) {
|
|
1252
|
-
const lowerCandidate = path.join(dir, `${cmd}${extension.toLowerCase()}`);
|
|
1253
|
-
if (fs.existsSync(lowerCandidate)) {
|
|
1254
|
-
return lowerCandidate;
|
|
1255
|
-
}
|
|
1256
|
-
const upperCandidate = path.join(dir, `${cmd}${extension.toUpperCase()}`);
|
|
1257
|
-
if (fs.existsSync(upperCandidate)) {
|
|
1258
|
-
return upperCandidate;
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
|
|
1263
|
-
return cmd;
|
|
1264
|
-
}
|
|
1265
|
-
|
|
1266
|
-
function commandOptions(cmd, options) {
|
|
1267
|
-
if (process.platform === "win32" && /\.(?:bat|cmd)$/i.test(cmd)) {
|
|
1268
|
-
return { ...options, shell: true };
|
|
1269
|
-
}
|
|
1270
|
-
return options;
|
|
1271
|
-
}
|
|
1272
|
-
|
|
1273
1258
|
function interpolate(template, taskDir, artifactFile) {
|
|
1274
1259
|
const artifactStem = artifactFile ? path.basename(artifactFile, path.extname(artifactFile)) : "";
|
|
1275
1260
|
return template
|
|
@@ -109,7 +109,7 @@ The rule's content is determined by the configured code platform:
|
|
|
109
109
|
|
|
110
110
|
Handle the result:
|
|
111
111
|
- Rule successfully created the Issue: `issue_number` has been written back to task.md per the rule; continue by reading `.agents/rules/issue-sync.md`, completing upstream repository and permission detection, then sync the task comment and set `status: waiting-for-triage` by rule
|
|
112
|
-
- Rule failed (auth / network / template parse / etc.): do not roll back task.md;
|
|
112
|
+
- Rule failed (auth / network / template parse / etc.): do not roll back task.md; do NOT append an extra Activity Log entry; follow "Scenario C: Issue creation failed" output to surface `error_code` and `error_message` to the user so they can decide whether to retry manually or write `issue_number` later
|
|
113
113
|
- Rule was a no-op (custom or empty platform): do not create comments, do not block the workflow, and do not write an Activity Log entry
|
|
114
114
|
- task.md already has `issue_number`: the rule's prerequisite check skips creation; `create-task` proceeds directly to step 5
|
|
115
115
|
|
|
@@ -109,7 +109,7 @@ date "+%Y-%m-%d %H:%M:%S%:z"
|
|
|
109
109
|
|
|
110
110
|
处理结果:
|
|
111
111
|
- 规则成功创建 Issue:`issue_number` 已按规则回写到 task.md;继续读取 `.agents/rules/issue-sync.md`,完成 upstream 仓库检测和权限检测,然后同步 task 评论并按规则设置 `status: waiting-for-triage`
|
|
112
|
-
- 规则失败(认证 / 网络 / 模板解析等):不回滚 task.md
|
|
112
|
+
- 规则失败(认证 / 网络 / 模板解析等):不回滚 task.md;不追加额外 Activity Log;按"场景 C:Issue 创建失败"输出向用户透出 `error_code` 与 `error_message`,让用户决定后续是否手动重试或写入 `issue_number`
|
|
113
113
|
- 规则为 no-op(自定义或空平台):不创建评论,不阻塞后续工作流,不写 Activity Log
|
|
114
114
|
- task.md 已存在 `issue_number`:规则中的前置检查会跳过;`create-task` 直接进入步骤 5
|
|
115
115
|
|
|
@@ -28,17 +28,15 @@ Use the Issue title as-is for the task title (preserve the Issue's original lang
|
|
|
28
28
|
- If found, ask the user whether to re-import or continue with the existing task
|
|
29
29
|
- If not found, continue to 2.2
|
|
30
30
|
|
|
31
|
-
2.2
|
|
31
|
+
2.2 Use the "historical task comment scan" command in `.agents/rules/issue-pr-commands.md` to scan Issue comments for sync markers and look for a recoverable historical task ID.
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
This command depends on `$upstream_repo` being set in step 1.
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
node .agents/scripts/platform-adapters/find-existing-task.js --issue <issue-number> --repo "$upstream_repo" --format json
|
|
37
|
-
```
|
|
35
|
+
Exit code handling for the whole pipeline:
|
|
38
36
|
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
37
|
+
- Exit 0 + output `found=false`: create a new task through the normal import flow
|
|
38
|
+
- Exit 0 + output `found=true`: reuse `task_id`
|
|
39
|
+
- Non-zero exit (platform API, authentication, JSON parsing, or any pipeline segment failure): treat it as platform API degradation; show stderr to the user, then continue with the new-task import flow without blocking
|
|
42
40
|
|
|
43
41
|
### 3. Create the Task Directory and File
|
|
44
42
|
|
|
@@ -28,17 +28,15 @@ description: "从 Issue 导入并创建任务"
|
|
|
28
28
|
- 如果找到,询问用户是重新导入还是继续使用现有任务
|
|
29
29
|
- 如果未找到,继续执行 2.2
|
|
30
30
|
|
|
31
|
-
2.2
|
|
31
|
+
2.2 按 `.agents/rules/issue-pr-commands.md` 的“历史任务评论扫描”命令扫描 Issue 评论中的同步标记,查找可恢复的历史任务 ID。
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
该命令依赖步骤 1 已设置的 `$upstream_repo`。
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
node .agents/scripts/platform-adapters/find-existing-task.js --issue <issue-number> --repo "$upstream_repo" --format json
|
|
37
|
-
```
|
|
35
|
+
退出码处理(pipeline 整体):
|
|
38
36
|
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
37
|
+
- 退出 0 + 输出 `found=false`:按新 Issue 导入流程创建新任务
|
|
38
|
+
- 退出 0 + 输出 `found=true`:复用 `task_id`
|
|
39
|
+
- 退出非 0(平台 API、认证、JSON 解析或任一段管道异常):视为 platform API 降级,向用户展示 stderr 后按新 Issue 导入流程继续,不阻塞导入
|
|
42
40
|
|
|
43
41
|
### 3. 创建任务目录和文件
|
|
44
42
|
|