@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.
Files changed (41) hide show
  1. package/README.md +200 -8
  2. package/README.zh-CN.md +176 -8
  3. package/bin/cli.js +2 -2
  4. package/lib/init.js +18 -4
  5. package/lib/sandbox/commands/create.js +467 -240
  6. package/lib/sandbox/commands/enter.js +59 -26
  7. package/lib/sandbox/commands/ls.js +37 -6
  8. package/lib/sandbox/commands/rebuild.js +31 -15
  9. package/lib/sandbox/commands/refresh.js +119 -0
  10. package/lib/sandbox/commands/rm.js +59 -11
  11. package/lib/sandbox/commands/vm.js +50 -6
  12. package/lib/sandbox/config.js +9 -5
  13. package/lib/sandbox/constants.js +15 -3
  14. package/lib/sandbox/credentials.js +520 -0
  15. package/lib/sandbox/dotfiles.js +189 -0
  16. package/lib/sandbox/engine.js +135 -192
  17. package/lib/sandbox/engines/colima.js +79 -0
  18. package/lib/sandbox/engines/docker-desktop.js +34 -0
  19. package/lib/sandbox/engines/index.js +27 -0
  20. package/lib/sandbox/engines/native.js +112 -0
  21. package/lib/sandbox/engines/orbstack.js +76 -0
  22. package/lib/sandbox/engines/selinux.js +60 -0
  23. package/lib/sandbox/engines/wsl2-paths.js +59 -0
  24. package/lib/sandbox/engines/wsl2.js +72 -0
  25. package/lib/sandbox/index.js +10 -1
  26. package/lib/sandbox/runtimes/ai-tools.dockerfile +14 -1
  27. package/lib/sandbox/runtimes/base.dockerfile +116 -1
  28. package/lib/sandbox/shell.js +53 -2
  29. package/lib/sandbox/tools.js +5 -5
  30. package/package.json +6 -4
  31. package/templates/.agents/rules/create-issue.github.en.md +2 -4
  32. package/templates/.agents/rules/create-issue.github.zh-CN.md +2 -4
  33. package/templates/.agents/rules/issue-pr-commands.github.en.md +29 -0
  34. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +29 -0
  35. package/templates/.agents/scripts/{platform-adapters/find-existing-task.github.js → find-existing-task.js} +22 -79
  36. package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +26 -41
  37. package/templates/.agents/skills/create-task/SKILL.en.md +1 -1
  38. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +1 -1
  39. package/templates/.agents/skills/import-issue/SKILL.en.md +6 -8
  40. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +6 -8
  41. package/templates/.agents/scripts/platform-adapters/find-existing-task.js +0 -5
@@ -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.9",
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": ">=18"
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.2.0",
45
- "picocolors": "1.1.1"
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
- - Append to the `## 活动日志` / `## Activity Log` section:
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
- - 在 `## 活动日志` / `## Activity Log` 段落追加:
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 { spawnSync } from "node:child_process";
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/platform-adapters/find-existing-task.js --issue <number> [--repo <owner/name>] [--format json]"
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 (!args.issue) {
250
- console.error("Missing required argument: --issue");
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
- if (args.format !== "json") {
256
- console.error(`Unsupported format: ${args.format}`);
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
- const repo = resolveRepo(args.repo);
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 scan issue comments: ${error.stderr || error.message}`);
266
- process.exit(2);
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 { spawnSync } from "node:child_process";
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 = spawnSync("git", ["remote", "get-url", "origin"], {
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 command = resolveCommand("gh");
1101
- const result = spawnSync(command, args, commandOptions(command, {
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 command = resolveCommand("git");
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; get the current time and append an `Issue Creation Skipped` Activity Log entry with the reason
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;获取当前时间,追加 `Issue Creation Skipped` Activity Log,说明错误原因
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 Scan Issue comments for sync markers and look for a recoverable historical task ID:
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
- If `$upstream_repo` was not set in step 1, omit `--repo`; the script will infer it using the issue-sync.md detection rule.
33
+ This command depends on `$upstream_repo` being set in step 1.
34
34
 
35
- ```bash
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
- - Script outputs `found=false`: create a new task through the normal import flow
40
- - Script outputs `found=true`: reuse `task_id`
41
- - Script exits 2: treat it as network, authentication, or platform API degradation; show the failure reason from script stderr to the user, then continue with the new-task import flow without blocking
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 扫描 Issue 评论中的同步标记,查找可恢复的历史任务 ID
31
+ 2.2 `.agents/rules/issue-pr-commands.md` 的“历史任务评论扫描”命令扫描 Issue 评论中的同步标记,查找可恢复的历史任务 ID
32
32
 
33
- 如未在步骤 1 中已设置 `$upstream_repo`,可省略 `--repo`,脚本会自动按 issue-sync.md 的检测规则推断。
33
+ 该命令依赖步骤 1 已设置的 `$upstream_repo`。
34
34
 
35
- ```bash
36
- node .agents/scripts/platform-adapters/find-existing-task.js --issue <issue-number> --repo "$upstream_repo" --format json
37
- ```
35
+ 退出码处理(pipeline 整体):
38
36
 
39
- - 脚本输出 `found=false`:按新 Issue 导入流程创建新任务
40
- - 脚本输出 `found=true`:复用 `task_id`
41
- - 脚本退出码 2:视为网络、认证或 platform API 降级,向用户展示脚本 stderr 中的失败原因后,按新 Issue 导入流程继续,不阻塞导入
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
 
@@ -1,5 +0,0 @@
1
- import { pathToFileURL } from "node:url";
2
-
3
- if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
4
- console.log(JSON.stringify({ found: false }, null, 2));
5
- }