@fitlab-ai/agent-infra 0.7.0 → 0.7.2
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/bin/cli.ts +12 -1
- package/dist/bin/cli.js +13 -1
- package/dist/lib/builtin-tuis.js +45 -0
- package/dist/lib/defaults.json +3 -0
- package/dist/lib/init.js +62 -23
- package/dist/lib/prompt.js +49 -1
- package/dist/lib/sandbox/commands/create.js +10 -2
- package/dist/lib/sandbox/commands/enter.js +8 -7
- package/dist/lib/sandbox/commands/list-running.js +62 -28
- package/dist/lib/sandbox/commands/ls.js +20 -22
- package/dist/lib/sandbox/commands/rebuild.js +3 -11
- package/dist/lib/sandbox/commands/rm.js +2 -0
- package/dist/lib/sandbox/image-prune.js +18 -0
- package/dist/lib/sandbox/index.js +7 -3
- package/dist/lib/sandbox/task-resolver.js +18 -0
- package/dist/lib/sandbox/tools.js +1 -1
- package/dist/lib/table.js +29 -0
- package/dist/lib/task/commands/ls.js +122 -0
- package/dist/lib/task/commands/show.js +135 -0
- package/dist/lib/task/frontmatter.js +32 -0
- package/dist/lib/task/index.js +41 -0
- package/dist/lib/task/short-id.js +80 -0
- package/dist/lib/update.js +59 -18
- package/lib/builtin-tuis.ts +55 -0
- package/lib/defaults.json +3 -0
- package/lib/init.ts +87 -35
- package/lib/prompt.ts +54 -1
- package/lib/sandbox/commands/create.ts +11 -2
- package/lib/sandbox/commands/enter.ts +8 -7
- package/lib/sandbox/commands/list-running.ts +70 -31
- package/lib/sandbox/commands/ls.ts +25 -25
- package/lib/sandbox/commands/rebuild.ts +3 -12
- package/lib/sandbox/commands/rm.ts +3 -0
- package/lib/sandbox/image-prune.ts +23 -0
- package/lib/sandbox/index.ts +7 -3
- package/lib/sandbox/task-resolver.ts +23 -1
- package/lib/sandbox/tools.ts +1 -1
- package/lib/table.ts +32 -0
- package/lib/task/commands/ls.ts +138 -0
- package/lib/task/commands/show.ts +139 -0
- package/lib/task/frontmatter.ts +30 -0
- package/lib/task/index.ts +44 -0
- package/lib/task/short-id.ts +97 -0
- package/lib/update.ts +71 -30
- package/package.json +1 -1
- package/templates/.agents/README.en.md +32 -0
- package/templates/.agents/README.zh-CN.md +32 -0
- package/templates/.agents/hooks/auto-resume.sh +87 -0
- package/templates/.agents/rules/create-issue.github.en.md +1 -1
- package/templates/.agents/rules/create-issue.github.zh-CN.md +1 -1
- package/templates/.agents/rules/milestone-inference.github.en.md +4 -1
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +4 -1
- package/templates/.agents/rules/next-step-output.en.md +59 -0
- package/templates/.agents/rules/next-step-output.zh-CN.md +59 -0
- package/templates/.agents/rules/task-short-id.en.md +133 -0
- package/templates/.agents/rules/task-short-id.zh-CN.md +105 -0
- package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +17 -0
- package/templates/.agents/scripts/task-short-id.js +556 -0
- package/templates/.agents/skills/analyze-task/SKILL.en.md +13 -11
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +13 -12
- package/templates/.agents/skills/analyze-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/analyze-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/block-task/SKILL.en.md +17 -5
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +17 -6
- package/templates/.agents/skills/block-task/config/verify.json +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +17 -5
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +17 -6
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
- package/templates/.agents/skills/check-task/SKILL.en.md +15 -9
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +15 -10
- package/templates/.agents/skills/close-codescan/SKILL.en.md +16 -5
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +16 -5
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +16 -5
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +16 -5
- package/templates/.agents/skills/code-task/SKILL.en.md +13 -5
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +14 -6
- package/templates/.agents/skills/code-task/config/verify.en.json +2 -1
- package/templates/.agents/skills/code-task/config/verify.zh-CN.json +2 -1
- package/templates/.agents/skills/code-task/reference/fix-mode.en.md +10 -5
- package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +10 -5
- package/templates/.agents/skills/code-task/reference/output-template.en.md +3 -3
- package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +3 -3
- package/templates/.agents/skills/code-task/reference/report-template.en.md +8 -0
- package/templates/.agents/skills/code-task/reference/report-template.zh-CN.md +8 -0
- package/templates/.agents/skills/commit/SKILL.en.md +5 -1
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/commit/reference/task-status-update.en.md +9 -9
- package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +9 -9
- package/templates/.agents/skills/complete-task/SKILL.en.md +17 -1
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +17 -2
- package/templates/.agents/skills/complete-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/complete-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +9 -5
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +9 -5
- package/templates/.agents/skills/create-pr/config/verify.json +2 -1
- package/templates/.agents/skills/create-pr/reference/comment-publish.en.md +1 -1
- package/templates/.agents/skills/create-pr/reference/comment-publish.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +3 -3
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +3 -3
- package/templates/.agents/skills/create-task/SKILL.en.md +29 -15
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +29 -16
- package/templates/.agents/skills/create-task/config/verify.json +1 -1
- package/templates/.agents/skills/import-codescan/SKILL.en.md +20 -6
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +20 -6
- package/templates/.agents/skills/import-codescan/config/verify.json +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +20 -6
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +20 -6
- package/templates/.agents/skills/import-dependabot/config/verify.json +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +19 -5
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +19 -5
- package/templates/.agents/skills/plan-task/SKILL.en.md +13 -11
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +13 -12
- package/templates/.agents/skills/plan-task/config/verify.en.json +1 -1
- package/templates/.agents/skills/plan-task/config/verify.zh-CN.json +1 -1
- package/templates/.agents/skills/restore-task/SKILL.en.md +12 -0
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +12 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +7 -1
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +7 -2
- package/templates/.agents/skills/review-analysis/config/verify.en.json +3 -2
- package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +3 -2
- package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +15 -15
- package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +15 -15
- package/templates/.agents/skills/review-analysis/reference/report-template.en.md +7 -1
- package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +7 -1
- package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +2 -0
- package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +2 -0
- package/templates/.agents/skills/review-code/SKILL.en.md +8 -1
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +8 -2
- package/templates/.agents/skills/review-code/config/verify.en.json +3 -2
- package/templates/.agents/skills/review-code/config/verify.zh-CN.json +3 -2
- package/templates/.agents/skills/review-code/reference/output-templates.en.md +9 -9
- package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +9 -9
- package/templates/.agents/skills/review-code/reference/report-template.en.md +7 -1
- package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +7 -1
- package/templates/.agents/skills/review-code/reference/review-criteria.en.md +2 -0
- package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +2 -0
- package/templates/.agents/skills/review-plan/SKILL.en.md +7 -1
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +7 -2
- package/templates/.agents/skills/review-plan/config/verify.en.json +3 -2
- package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +3 -2
- package/templates/.agents/skills/review-plan/reference/output-templates.en.md +15 -15
- package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +15 -15
- package/templates/.agents/skills/review-plan/reference/report-template.en.md +7 -1
- package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +7 -1
- package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +2 -0
- package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +2 -0
- package/templates/.agents/skills/update-agent-infra/SKILL.en.md +1 -0
- package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +1 -0
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +112 -21
- package/templates/.agents/workflows/bug-fix.en.yaml +1 -1
- package/templates/.agents/workflows/bug-fix.zh-CN.yaml +1 -1
- package/templates/.agents/workflows/feature-development.en.yaml +1 -1
- package/templates/.agents/workflows/feature-development.zh-CN.yaml +1 -1
- package/templates/.agents/workflows/refactoring.en.yaml +1 -1
- package/templates/.agents/workflows/refactoring.zh-CN.yaml +1 -1
- package/templates/.claude/settings.json +11 -0
package/lib/update.ts
CHANGED
|
@@ -3,6 +3,8 @@ import path from 'node:path';
|
|
|
3
3
|
import { info, ok, err } from './log.ts';
|
|
4
4
|
import { resolveTemplateDir } from './paths.ts';
|
|
5
5
|
import { renderFile, copySkillDir, KNOWN_PLATFORMS } from './render.ts';
|
|
6
|
+
import { isPathOwnedByDisabledTUI, resolveEnabledTUIs } from './builtin-tuis.ts';
|
|
7
|
+
import type { BuiltinTUIId } from './builtin-tuis.ts';
|
|
6
8
|
|
|
7
9
|
type FileRegistry = {
|
|
8
10
|
managed: string[];
|
|
@@ -17,14 +19,17 @@ type UpdateConfig = {
|
|
|
17
19
|
platform?: { type?: string };
|
|
18
20
|
requiresPullRequest?: boolean;
|
|
19
21
|
sandbox?: Record<string, unknown>;
|
|
22
|
+
task?: { shortIdLength: number };
|
|
20
23
|
labels?: Record<string, unknown>;
|
|
21
24
|
files?: Partial<FileRegistry>;
|
|
25
|
+
tuis?: unknown;
|
|
22
26
|
};
|
|
23
27
|
|
|
24
28
|
type Defaults = {
|
|
25
29
|
platform: { type: string };
|
|
26
30
|
requiresPullRequest: boolean;
|
|
27
31
|
sandbox: Record<string, unknown>;
|
|
32
|
+
task: { shortIdLength: number };
|
|
28
33
|
labels: Record<string, unknown>;
|
|
29
34
|
files: FileRegistry;
|
|
30
35
|
};
|
|
@@ -45,7 +50,7 @@ function isPathOwnedByOtherPlatform(relativePath: string, platformType: string):
|
|
|
45
50
|
return candidate !== platformType;
|
|
46
51
|
}
|
|
47
52
|
|
|
48
|
-
function syncFileRegistry(config: UpdateConfig, platformType: string) {
|
|
53
|
+
function syncFileRegistry(config: UpdateConfig, platformType: string, enabledTUIs: Set<BuiltinTUIId>) {
|
|
49
54
|
config.files ||= {};
|
|
50
55
|
const before = JSON.stringify({
|
|
51
56
|
files: {
|
|
@@ -67,6 +72,7 @@ function syncFileRegistry(config: UpdateConfig, platformType: string) {
|
|
|
67
72
|
|
|
68
73
|
for (const entry of defaults.files.managed) {
|
|
69
74
|
if (isPathOwnedByOtherPlatform(entry, platformType)) continue;
|
|
75
|
+
if (isPathOwnedByDisabledTUI(entry, enabledTUIs)) continue;
|
|
70
76
|
if (!allExisting.includes(entry)) {
|
|
71
77
|
config.files.managed.push(entry);
|
|
72
78
|
added.managed.push(entry);
|
|
@@ -74,6 +80,7 @@ function syncFileRegistry(config: UpdateConfig, platformType: string) {
|
|
|
74
80
|
}
|
|
75
81
|
for (const entry of defaults.files.merged) {
|
|
76
82
|
if (isPathOwnedByOtherPlatform(entry, platformType)) continue;
|
|
83
|
+
if (isPathOwnedByDisabledTUI(entry, enabledTUIs)) continue;
|
|
77
84
|
if (!allExisting.includes(entry)) {
|
|
78
85
|
config.files.merged.push(entry);
|
|
79
86
|
added.merged.push(entry);
|
|
@@ -118,6 +125,7 @@ async function cmdUpdate(): Promise<void> {
|
|
|
118
125
|
const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')) as UpdateConfig;
|
|
119
126
|
const { project, org, language } = config;
|
|
120
127
|
const platformType = config.platform?.type || defaults.platform.type;
|
|
128
|
+
const enabledTUIs = resolveEnabledTUIs(config.tuis);
|
|
121
129
|
const replacements = { project, org };
|
|
122
130
|
|
|
123
131
|
info(`Updating seed files for: ${project}`);
|
|
@@ -150,35 +158,42 @@ async function cmdUpdate(): Promise<void> {
|
|
|
150
158
|
// Ignore missing legacy script from pre-ESM installs.
|
|
151
159
|
}
|
|
152
160
|
|
|
153
|
-
// update Claude command
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
161
|
+
// update Claude command (only if enabled)
|
|
162
|
+
if (enabledTUIs.has('claude-code')) {
|
|
163
|
+
renderFile(
|
|
164
|
+
path.join(templateDir, '.claude', 'commands', claudeSrc),
|
|
165
|
+
path.join('.claude', 'commands', 'update-agent-infra.md'),
|
|
166
|
+
replacements
|
|
167
|
+
);
|
|
168
|
+
ok('Updated .claude/commands/update-agent-infra.md');
|
|
169
|
+
}
|
|
160
170
|
|
|
161
|
-
// update Gemini command
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
171
|
+
// update Gemini command (only if enabled)
|
|
172
|
+
if (enabledTUIs.has('gemini-cli')) {
|
|
173
|
+
renderFile(
|
|
174
|
+
path.join(templateDir, '.gemini', 'commands', '_project_', geminiSrc),
|
|
175
|
+
path.join('.gemini', 'commands', project, 'update-agent-infra.toml'),
|
|
176
|
+
replacements
|
|
177
|
+
);
|
|
178
|
+
ok(`Updated .gemini/commands/${project}/update-agent-infra.toml`);
|
|
179
|
+
}
|
|
168
180
|
|
|
169
|
-
// update OpenCode command
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
181
|
+
// update OpenCode command (only if enabled)
|
|
182
|
+
if (enabledTUIs.has('opencode')) {
|
|
183
|
+
renderFile(
|
|
184
|
+
path.join(templateDir, '.opencode', 'commands', opencodeSrc),
|
|
185
|
+
path.join('.opencode', 'commands', 'update-agent-infra.md'),
|
|
186
|
+
replacements
|
|
187
|
+
);
|
|
188
|
+
ok('Updated .opencode/commands/update-agent-infra.md');
|
|
189
|
+
}
|
|
176
190
|
|
|
177
191
|
// sync file registry
|
|
178
|
-
const { added, changed } = syncFileRegistry(config, platformType);
|
|
192
|
+
const { added, changed } = syncFileRegistry(config, platformType, enabledTUIs);
|
|
179
193
|
const hasNewEntries = added.managed.length > 0 || added.merged.length > 0;
|
|
180
194
|
const platformAdded = !config.platform;
|
|
181
195
|
const sandboxAdded = !config.sandbox;
|
|
196
|
+
const taskAdded = !config.task;
|
|
182
197
|
const labelsAdded = !config.labels;
|
|
183
198
|
const requiresPullRequestAdded = config.requiresPullRequest === undefined;
|
|
184
199
|
let configChanged = changed;
|
|
@@ -193,6 +208,11 @@ async function cmdUpdate(): Promise<void> {
|
|
|
193
208
|
configChanged = true;
|
|
194
209
|
}
|
|
195
210
|
|
|
211
|
+
if (taskAdded) {
|
|
212
|
+
config.task = structuredClone(defaults.task);
|
|
213
|
+
configChanged = true;
|
|
214
|
+
}
|
|
215
|
+
|
|
196
216
|
if (labelsAdded) {
|
|
197
217
|
config.labels = structuredClone(defaults.labels);
|
|
198
218
|
configChanged = true;
|
|
@@ -213,13 +233,16 @@ async function cmdUpdate(): Promise<void> {
|
|
|
213
233
|
for (const entry of added.merged) {
|
|
214
234
|
ok(` merged: ${entry}`);
|
|
215
235
|
}
|
|
216
|
-
} else if (platformAdded || sandboxAdded || labelsAdded || requiresPullRequestAdded) {
|
|
236
|
+
} else if (platformAdded || sandboxAdded || taskAdded || labelsAdded || requiresPullRequestAdded) {
|
|
217
237
|
if (platformAdded) {
|
|
218
238
|
info(`Default platform config added to ${CONFIG_PATH}.`);
|
|
219
239
|
}
|
|
220
240
|
if (sandboxAdded) {
|
|
221
241
|
info(`Default sandbox config added to ${CONFIG_PATH}.`);
|
|
222
242
|
}
|
|
243
|
+
if (taskAdded) {
|
|
244
|
+
info(`Default task.shortIdLength=${defaults.task.shortIdLength} added to ${CONFIG_PATH}.`);
|
|
245
|
+
}
|
|
223
246
|
if (labelsAdded) {
|
|
224
247
|
info(`Default labels.in config added to ${CONFIG_PATH}.`);
|
|
225
248
|
}
|
|
@@ -232,6 +255,9 @@ async function cmdUpdate(): Promise<void> {
|
|
|
232
255
|
if (hasNewEntries && sandboxAdded) {
|
|
233
256
|
info(`Default sandbox config added to ${CONFIG_PATH}.`);
|
|
234
257
|
}
|
|
258
|
+
if (hasNewEntries && taskAdded) {
|
|
259
|
+
info(`Default task.shortIdLength=${defaults.task.shortIdLength} added to ${CONFIG_PATH}.`);
|
|
260
|
+
}
|
|
235
261
|
if (hasNewEntries && labelsAdded) {
|
|
236
262
|
info(`Default labels.in config added to ${CONFIG_PATH}.`);
|
|
237
263
|
}
|
|
@@ -249,12 +275,27 @@ async function cmdUpdate(): Promise<void> {
|
|
|
249
275
|
console.log('');
|
|
250
276
|
ok('Seed files updated successfully!');
|
|
251
277
|
console.log('');
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
278
|
+
if (enabledTUIs.size === 0) {
|
|
279
|
+
console.log(' No built-in TUI enabled (tuis: []).');
|
|
280
|
+
console.log(` Configure "customTUIs" in ${CONFIG_PATH} if needed.`);
|
|
281
|
+
console.log('');
|
|
282
|
+
} else {
|
|
283
|
+
console.log(' Next step: run the full update in your AI TUI:');
|
|
284
|
+
console.log('');
|
|
285
|
+
const claudeOrOpencode: string[] = [];
|
|
286
|
+
if (enabledTUIs.has('claude-code')) claudeOrOpencode.push('Claude Code');
|
|
287
|
+
if (enabledTUIs.has('opencode')) claudeOrOpencode.push('OpenCode');
|
|
288
|
+
if (claudeOrOpencode.length > 0) {
|
|
289
|
+
console.log(` ${claudeOrOpencode.join(' / ')}: /update-agent-infra`);
|
|
290
|
+
}
|
|
291
|
+
if (enabledTUIs.has('gemini-cli')) {
|
|
292
|
+
console.log(` Gemini CLI: /${project}:update-agent-infra`);
|
|
293
|
+
}
|
|
294
|
+
if (enabledTUIs.has('codex')) {
|
|
295
|
+
console.log(' Codex CLI: $update-agent-infra');
|
|
296
|
+
}
|
|
297
|
+
console.log('');
|
|
298
|
+
}
|
|
258
299
|
}
|
|
259
300
|
|
|
260
301
|
export { cmdUpdate };
|
package/package.json
CHANGED
|
@@ -210,6 +210,38 @@ The `files` field in `.agents/.airc.json` groups project files into three catego
|
|
|
210
210
|
|
|
211
211
|
`ejected` entries support literal paths or globs, using the same matching rules as `merged`.
|
|
212
212
|
|
|
213
|
+
## Built-in TUI Selection
|
|
214
|
+
|
|
215
|
+
Use the top-level `.agents/.airc.json` `tuis` array to pick which built-in TUIs (`claude-code`, `codex`, `gemini-cli`, `opencode`) agent-infra should install command files for and keep in sync.
|
|
216
|
+
|
|
217
|
+
| Value | Meaning |
|
|
218
|
+
|-------|---------|
|
|
219
|
+
| `tuis` missing or `null` | All four built-in TUIs are enabled (backward-compatible default for legacy `.airc.json` predating this field). |
|
|
220
|
+
| `tuis: []` | No built-in TUI is managed. Use this when the project only relies on `customTUIs` and does not need any built-in command files installed. |
|
|
221
|
+
| `tuis: [<subset>]` | Only the listed TUIs are managed. Unknown ids are ignored. |
|
|
222
|
+
|
|
223
|
+
`ai init` includes an interactive multi-select for this field:
|
|
224
|
+
|
|
225
|
+
- Press Enter to accept the default (all built-in TUIs enabled).
|
|
226
|
+
- Type comma-separated numbers or ids (e.g. `1,3` or `claude-code,opencode`) to keep a subset.
|
|
227
|
+
- Type `none` to explicitly disable every built-in TUI (typically combined with a `customTUIs` entry added later).
|
|
228
|
+
- Invalid input (duplicate, out-of-range, unknown id, whitespace-only) aborts init with a non-zero exit code.
|
|
229
|
+
|
|
230
|
+
### Side effects of disabling a TUI
|
|
231
|
+
|
|
232
|
+
When you disable a built-in TUI (either via `ai init` or by hand-editing `.airc.json`), the next `ai update` / `update-agent-infra` will:
|
|
233
|
+
|
|
234
|
+
- skip seed command writes for that TUI (e.g. `.gemini/commands/<project>/update-agent-infra.toml`);
|
|
235
|
+
- skip the TUI's owned default entries when backfilling `files.managed` / `files.merged`;
|
|
236
|
+
- **clean up existing files** under the TUI's owned path prefix (`.claude/`, `.codex/`, `.gemini/`, `.opencode/`) — these are listed in `report.managed.removed`, mirroring the cleanup behavior when switching `platform`.
|
|
237
|
+
|
|
238
|
+
To opt a specific file out of this cleanup, list it in `files.ejected`; ejected entries owned by disabled TUIs are preserved as-is and are not re-created by sync.
|
|
239
|
+
|
|
240
|
+
### Relation to other config fields
|
|
241
|
+
|
|
242
|
+
- `tuis` controls **which TUI command files agent-infra writes and maintains**. It is independent from `sandbox.tools`, which controls **which CLIs the sandbox image installs**. Toggling one does not affect the other; the README for `sandbox.tools` lives in the Sandbox section.
|
|
243
|
+
- `tuis` is independent from `customTUIs` (see below). CustomTUI command files are not removed when you disable a built-in TUI, even if the customTUI's `dir` falls under that TUI's owned prefix (e.g. a customTUI configured with `dir: ".codex/commands"` is preserved when `codex` is disabled).
|
|
244
|
+
|
|
213
245
|
## Custom TUI Configuration
|
|
214
246
|
|
|
215
247
|
Use the top-level `.agents/.airc.json` `customTUIs` array when your team uses an AI TUI that is not one of the built-in command targets. This config lets agent-infra show the correct next-step commands and generate command files for project custom skills by learning from an existing command in the custom TUI directory.
|
|
@@ -210,6 +210,38 @@ args: "<task-id>" # 可选
|
|
|
210
210
|
|
|
211
211
|
`ejected` 条目支持字面路径或 glob,匹配规则与 `merged` 相同。
|
|
212
212
|
|
|
213
|
+
## 内建 TUI 选择
|
|
214
|
+
|
|
215
|
+
`.agents/.airc.json` 顶层 `tuis` 数组用于决定 agent-infra 应当为哪些内建 TUI(`claude-code`、`codex`、`gemini-cli`、`opencode`)安装并维护命令文件。
|
|
216
|
+
|
|
217
|
+
| 取值 | 含义 |
|
|
218
|
+
|------|------|
|
|
219
|
+
| `tuis` 缺失或为 `null` | 启用全部四个内建 TUI(向后兼容默认,适用于本字段引入之前的 `.airc.json`) |
|
|
220
|
+
| `tuis: []` | 不维护任何内建 TUI。适用于只依赖 `customTUIs`、不需要安装任何内建命令文件的项目 |
|
|
221
|
+
| `tuis: [<子集>]` | 仅维护列出的 TUI;未知 id 会被忽略 |
|
|
222
|
+
|
|
223
|
+
`ai init` 会通过交互式多选询问该字段:
|
|
224
|
+
|
|
225
|
+
- 直接回车 = 接受默认值(全部内建 TUI 启用)。
|
|
226
|
+
- 输入逗号分隔的编号或 id(如 `1,3` 或 `claude-code,opencode`)= 只保留子集。
|
|
227
|
+
- 输入 `none` = 明确不启用任何内建 TUI(通常配合后续在 `customTUIs` 添加条目使用)。
|
|
228
|
+
- 非法输入(重复、超界、未知 id、纯空白)会让 init 以非零退出码终止。
|
|
229
|
+
|
|
230
|
+
### 取消某个 TUI 的副作用
|
|
231
|
+
|
|
232
|
+
通过 `ai init` 或手工编辑 `.airc.json` 取消某个内建 TUI 后,下一次 `ai update` / `update-agent-infra` 会:
|
|
233
|
+
|
|
234
|
+
- 跳过该 TUI 的 seed 命令文件写入(例如 `.gemini/commands/<project>/update-agent-infra.toml`);
|
|
235
|
+
- 在回填 `files.managed` / `files.merged` 时跳过该 TUI owned 的默认条目;
|
|
236
|
+
- **物理清理**该 TUI owned 路径前缀(`.claude/`、`.codex/`、`.gemini/`、`.opencode/`)下的已有文件——清理列表会出现在 `report.managed.removed`,与切换 `platform` 时的清理行为一致。
|
|
237
|
+
|
|
238
|
+
若希望保留某个具体文件,把它加入 `files.ejected`:被 ejected 的、属于已取消 TUI 的条目会保持原状,sync 不会重新创建也不会删除。
|
|
239
|
+
|
|
240
|
+
### 与其他配置字段的关系
|
|
241
|
+
|
|
242
|
+
- `tuis` 控制 **agent-infra 写入与维护哪些 TUI 的命令文件**,与 `sandbox.tools`(控制**沙箱镜像里安装哪些 CLI**)相互独立。两者互不影响;`sandbox.tools` 的说明见 Sandbox 一节。
|
|
243
|
+
- `tuis` 与 `customTUIs`(见下)相互独立。取消某个内建 TUI 时 customTUI 命令文件不会被清理,即便 customTUI 的 `dir` 落在该 TUI 的 owned 前缀下(例如 `dir: ".codex/commands"` 的 customTUI 在 `codex` 被取消时仍会保留)。
|
|
244
|
+
|
|
213
245
|
## 自定义 TUI 配置
|
|
214
246
|
|
|
215
247
|
当团队使用的 AI TUI 不属于内置命令目标时,可以在 `.agents/.airc.json` 顶层配置 `customTUIs` 数组。该配置用于让 agent-infra 输出正确的下一步命令,并通过学习自定义 TUI 目录中的既有命令文件,为项目自定义 skill 生成同格式命令。
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# StopFailure hook: auto-resume Claude Code after a recoverable API error.
|
|
3
|
+
#
|
|
4
|
+
# Fires when a turn ends due to an API error. Runs four gates and, if all pass,
|
|
5
|
+
# injects a "please continue" message into the current tmux pane via send-keys.
|
|
6
|
+
# StopFailure output and exit code are ignored by Claude Code, so recovery is
|
|
7
|
+
# delivered out-of-band through tmux; every exit path here returns 0 and the
|
|
8
|
+
# only observable trace is the log file.
|
|
9
|
+
#
|
|
10
|
+
# Intentionally NOT using `set -e`: the network probe, tmux and state-file
|
|
11
|
+
# writes may fail locally without warranting an abort of the whole script.
|
|
12
|
+
|
|
13
|
+
LOG="$HOME/.claude/auto-resume.log"
|
|
14
|
+
STATE_DIR="$HOME/.claude/auto-resume.state"
|
|
15
|
+
WHITELIST="unknown server_error overloaded"
|
|
16
|
+
WINDOW=1800
|
|
17
|
+
MAX=10
|
|
18
|
+
PROBE_URL="https://api.anthropic.com/"
|
|
19
|
+
PROBE_DEADLINE=60
|
|
20
|
+
RESUME_TEXT="Unexpected interruption. Please continue the unfinished operation."
|
|
21
|
+
|
|
22
|
+
log() {
|
|
23
|
+
mkdir -p "$HOME/.claude" 2>/dev/null
|
|
24
|
+
printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S%z')" "$1" >> "$LOG"
|
|
25
|
+
lines=$(wc -l < "$LOG" 2>/dev/null | tr -cd '0-9')
|
|
26
|
+
[ -z "$lines" ] && lines=0
|
|
27
|
+
if [ "$lines" -gt 5000 ]; then
|
|
28
|
+
tail -n 2500 "$LOG" > "$LOG.tmp" 2>/dev/null && mv "$LOG.tmp" "$LOG"
|
|
29
|
+
fi
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Read the StopFailure payload once, then extract session_id and error. The
|
|
33
|
+
# `error` field identifies the API error type and drives the whitelist gate.
|
|
34
|
+
payload=$(cat)
|
|
35
|
+
session_id=$(printf '%s' "$payload" | node -e 'let c=[];process.stdin.on("data",d=>c.push(d));process.stdin.on("end",()=>{try{const p=JSON.parse(Buffer.concat(c).toString());process.stdout.write(String(p.session_id||""))}catch{process.stdout.write("")}})' 2>/dev/null)
|
|
36
|
+
error=$(printf '%s' "$payload" | node -e 'let c=[];process.stdin.on("data",d=>c.push(d));process.stdin.on("end",()=>{try{const p=JSON.parse(Buffer.concat(c).toString());process.stdout.write(String(p.error||""))}catch{process.stdout.write("")}})' 2>/dev/null)
|
|
37
|
+
|
|
38
|
+
# Gate 1: only act inside a tmux pane; stay silent everywhere else.
|
|
39
|
+
if [ -z "$TMUX_PANE" ]; then
|
|
40
|
+
log "not in tmux, skip (error=$error)"
|
|
41
|
+
exit 0
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
# Gate 2: only recover from the whitelisted, retriable error types.
|
|
45
|
+
case " $WHITELIST " in
|
|
46
|
+
*" $error "*) : ;;
|
|
47
|
+
*) log "blocked: non-recoverable error=$error"; exit 0 ;;
|
|
48
|
+
esac
|
|
49
|
+
|
|
50
|
+
# Gate 3: back off after MAX fires within a WINDOW-second sliding window per session.
|
|
51
|
+
mkdir -p "$STATE_DIR" 2>/dev/null
|
|
52
|
+
# Treat the payload session_id as untrusted: sanitize to a safe filename so a
|
|
53
|
+
# value like "../outside" cannot write the state file outside STATE_DIR.
|
|
54
|
+
safe_session=$(printf '%s' "${session_id:-nosession}" | tr -c 'A-Za-z0-9._-' '_')
|
|
55
|
+
f="$STATE_DIR/$safe_session.count"
|
|
56
|
+
now=$(date +%s)
|
|
57
|
+
if [ -f "$f" ]; then
|
|
58
|
+
awk -v n="$now" -v w="$WINDOW" '$1 > n - w' "$f" > "$f.tmp" 2>/dev/null && mv "$f.tmp" "$f"
|
|
59
|
+
fi
|
|
60
|
+
# BSD `wc -l` (macOS) pads the count with leading spaces; strip to bare digits
|
|
61
|
+
# so the integer compare and the log line stay portable across GNU/BSD.
|
|
62
|
+
count=$( [ -f "$f" ] && wc -l < "$f" 2>/dev/null | tr -cd '0-9' || echo 0 )
|
|
63
|
+
[ -z "$count" ] && count=0
|
|
64
|
+
if [ "$count" -ge "$MAX" ]; then
|
|
65
|
+
log "backoff: $count fires in 30m, skip (error=$error)"
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
echo "$now" >> "$f"
|
|
69
|
+
|
|
70
|
+
# Gate 4: wait until the API is reachable again, up to PROBE_DEADLINE seconds.
|
|
71
|
+
# No --fail: any HTTP response (incl. 401/404) proves TLS/network connectivity.
|
|
72
|
+
waited=0
|
|
73
|
+
until curl -s -o /dev/null --max-time 3 "$PROBE_URL"; do
|
|
74
|
+
waited=$((waited + 3))
|
|
75
|
+
if [ "$waited" -ge "$PROBE_DEADLINE" ]; then
|
|
76
|
+
log "probe timeout after ${waited}s, skip (error=$error)"
|
|
77
|
+
exit 0
|
|
78
|
+
fi
|
|
79
|
+
sleep 3
|
|
80
|
+
done
|
|
81
|
+
log "probe ok after ${waited}s (error=$error)"
|
|
82
|
+
|
|
83
|
+
# Inject: Escape first to leave any non-input TUI state, then the resume text.
|
|
84
|
+
tmux send-keys -t "$TMUX_PANE" Escape 2>/dev/null
|
|
85
|
+
tmux send-keys -t "$TMUX_PANE" "$RESUME_TEXT" Enter 2>/dev/null
|
|
86
|
+
log "send-keys done (error=$error)"
|
|
87
|
+
exit 0
|
|
@@ -180,7 +180,7 @@ Update task.md:
|
|
|
180
180
|
- Write `issue_number: {n}` into the frontmatter (replace if it exists; append at the end of the frontmatter otherwise)
|
|
181
181
|
- Update `updated_at` to the current time (command: `date "+%Y-%m-%d %H:%M:%S%:z"`)
|
|
182
182
|
|
|
183
|
-
> 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
|
|
183
|
+
> 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 (`Create Task`), written by the caller SKILL step 3.
|
|
184
184
|
|
|
185
185
|
### 9. Return the Result
|
|
186
186
|
|
|
@@ -180,7 +180,7 @@ gh api "repos/$upstream_repo/issues/{issue-number}" -X PATCH \
|
|
|
180
180
|
- 把 `issue_number: {n}` 写入 frontmatter(已存在则替换;不存在则在 frontmatter 末尾追加)
|
|
181
181
|
- 更新 `updated_at` 为当前时间(命令:`date "+%Y-%m-%d %H:%M:%S%:z"`)
|
|
182
182
|
|
|
183
|
-
> 不要在此追加 Activity Log 条目。Issue 创建事件已由 GitHub Issue 自身和 frontmatter `issue_number` 承载;Activity Log 仅记录 `create-task` skill 一次执行的整体锚点(`Task
|
|
183
|
+
> 不要在此追加 Activity Log 条目。Issue 创建事件已由 GitHub Issue 自身和 frontmatter `issue_number` 承载;Activity Log 仅记录 `create-task` skill 一次执行的整体锚点(`Create Task`),由调用方 SKILL 步骤 3 写入。
|
|
184
184
|
|
|
185
185
|
### 9. 返回结果
|
|
186
186
|
|
|
@@ -81,7 +81,10 @@ if [ "$has_triage" = "true" ]; then
|
|
|
81
81
|
fi
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
6.
|
|
84
|
+
6. Keep the original milestone unchanged only in the following cases (otherwise narrow per step 5):
|
|
85
|
+
- Trunk mode with no open concrete version under the release line — the `code-task` / `create-pr` `verify_milestone_specific` gate will fail and prompt maintainers to create the missing concrete version
|
|
86
|
+
- Multi-release-line mode when both `git merge-base --is-ancestor` checks are unreliable or the remote refs are missing
|
|
87
|
+
- In any mode, `has_triage=false` (the bot will reconcile later)
|
|
85
88
|
|
|
86
89
|
Suggested concrete-version query:
|
|
87
90
|
|
|
@@ -81,7 +81,10 @@ if [ "$has_triage" = "true" ]; then
|
|
|
81
81
|
fi
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
6.
|
|
84
|
+
6. 仅在以下情况保持原 milestone 不变(其余情形必须按步骤 5 收窄):
|
|
85
|
+
- 主干模式下版本线下没有 open 具体版本 —— `code-task` / `create-pr` 的 `verify_milestone_specific` gate 会 FAIL,提醒维护者补建具体版本
|
|
86
|
+
- 多版本分支模式下 `git merge-base --is-ancestor` 两条判断都不可靠或远程引用缺失
|
|
87
|
+
- 任意模式下 `has_triage=false`(由 bot 后补)
|
|
85
88
|
|
|
86
89
|
具体版本查询建议:
|
|
87
90
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Next-Step Output Rule
|
|
2
|
+
|
|
3
|
+
When a skill renders "Next steps" commands and the "Task info" block in its notify-user step, present the task ID consistently per this rule. Read this file before rendering next steps.
|
|
4
|
+
|
|
5
|
+
## Placeholder semantics
|
|
6
|
+
|
|
7
|
+
| Placeholder | Meaning | Rendered form |
|
|
8
|
+
|-------------|---------|---------------|
|
|
9
|
+
| `{task-ref}` | Current task **short id** | `#`-prefixed, e.g. `#15`; falls back to the full `TASK-id` when unavailable |
|
|
10
|
+
| `{task-id}` | Current task **full id** | `TASK-YYYYMMDD-HHMMSS` |
|
|
11
|
+
|
|
12
|
+
## Scope
|
|
13
|
+
|
|
14
|
+
- **Next-step TUI commands** (`/analyze-task`, `/{{project}}:review-code`, `$create-pr`, etc., including commands inside Markdown table cells) → always use `{task-ref}` (short id).
|
|
15
|
+
- **"Task info" / "Task status" structured field lines** → show full id and short id together: `- Task ID: {task-id} (short id {task-ref})`.
|
|
16
|
+
- **Report titles** (`Task {task-id} ... completed`) and **artifact paths** (`.agents/workspace/active/{task-id}/...`) → keep the full `{task-id}` (physical path and archive key, must not change).
|
|
17
|
+
|
|
18
|
+
## Obtaining the short id (`{task-ref}`)
|
|
19
|
+
|
|
20
|
+
The single source of truth for short ids is the registry `.agents/workspace/active/.short-ids.json` (via `task-short-id.js`). **Never** read the `short_id` field from task.md frontmatter (that field is not authoritative).
|
|
21
|
+
|
|
22
|
+
Once the full `$task_id` is resolved, use the snippet below to look up the short id; it returns `#NN` on hit and falls back to the full `TASK-id` on miss:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
task_ref=$(node -e '
|
|
26
|
+
const cp=require("child_process");
|
|
27
|
+
const out=cp.execSync("node .agents/scripts/task-short-id.js list",{encoding:"utf8"});
|
|
28
|
+
const ids=(JSON.parse(out).ids)||{};
|
|
29
|
+
const full=process.argv[1];
|
|
30
|
+
const hit=Object.entries(ids).find(([,v])=>v===full);
|
|
31
|
+
process.stdout.write(hit?("#"+hit[0]):full);
|
|
32
|
+
' "$task_id")
|
|
33
|
+
# Example: $task_id=TASK-20260613-225809 -> task_ref=#15
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Fallback conditions
|
|
37
|
+
|
|
38
|
+
`{task-ref}` falls back to the full `TASK-id` in these cases (i.e. the registry has no matching short id):
|
|
39
|
+
|
|
40
|
+
- **Unallocated**: very early paths before `create-task` / `import-*` / `restore-task` has allocated a short id.
|
|
41
|
+
- **Released**: after a task is archived by `complete-task` / `cancel-task` / `block-task` / `close-codescan` / `close-dependabot`, its short id is immediately removed from the registry. The terminal/summary lines of these archival skills therefore fall back to the full `TASK-id` naturally, with no special-casing.
|
|
42
|
+
|
|
43
|
+
`restore-task` re-allocates a short id when restoring a task (possibly different from before); the snippet picks up the new short id.
|
|
44
|
+
|
|
45
|
+
## `#` prefix and shell quoting
|
|
46
|
+
|
|
47
|
+
Short ids are always rendered with a `#` prefix as `#NN`, matching how task.md frontmatter renders `short_id`. `#` starts a comment in bash, so pasting example commands depends on the TUI (both the bare numeric `NN` and `#NN` are accepted by `task-short-id.js resolve`).
|
|
48
|
+
|
|
49
|
+
## Completion timestamp line (Completed at)
|
|
50
|
+
|
|
51
|
+
Every skill that reads this rule and renders "Next steps / Inform user" output appends a single completion-time line as the **very last line** of its user-facing output, so users scanning across tmux windows can tell at a glance which agent finished most recently:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
Completed at: YYYY-MM-DD HH:mm:ss
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- Value command (local timezone, no offset): `date "+%Y-%m-%d %H:%M:%S"`
|
|
58
|
+
- Position: it must be the last line of the entire user-facing output, after all "Next steps" commands. If a scenario has a conditional reminder line after the commands (e.g. the env-blocked reminder), the completion line goes after that reminder.
|
|
59
|
+
- This line is for terminal scanning only; it is never written to any artifact file or Issue/PR comment. The single source of truth for completion time remains the Activity Log in task.md.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# 下一步输出规则
|
|
2
|
+
|
|
3
|
+
各 skill 在「告知用户」步骤渲染「下一步」命令与「任务信息」段时,统一按本规则呈现任务 ID 形态。渲染下一步前先读取本文件。
|
|
4
|
+
|
|
5
|
+
## 占位符语义
|
|
6
|
+
|
|
7
|
+
| 占位符 | 含义 | 渲染形态 |
|
|
8
|
+
|--------|------|----------|
|
|
9
|
+
| `{task-ref}` | 当前任务**短号** | 带 `#` 前缀,如 `#15`;取不到时回退完整 `TASK-id` |
|
|
10
|
+
| `{task-id}` | 当前任务**完整 ID** | `TASK-YYYYMMDD-HHMMSS` |
|
|
11
|
+
|
|
12
|
+
## 适用范围
|
|
13
|
+
|
|
14
|
+
- **下一步 TUI 命令**(`/analyze-task`、`/{{project}}:review-code`、`$create-pr` 等,含 Markdown 表格单元格内的命令)→ 一律用 `{task-ref}`(短号)。
|
|
15
|
+
- **「任务信息」/「任务状态」结构化字段行** → 完整 ID 与短号同显:`- 任务 ID:{task-id}(短号 {task-ref})`。
|
|
16
|
+
- **报告标题**(`任务 {task-id} ... 完成`)与**产出文件路径**(`.agents/workspace/active/{task-id}/...`)→ 保持完整 `{task-id}`(物理路径与归档键,不可改)。
|
|
17
|
+
|
|
18
|
+
## 取短号(`{task-ref}`)
|
|
19
|
+
|
|
20
|
+
短号唯一真源是注册表 `.agents/workspace/active/.short-ids.json`(经 `task-short-id.js`)。**禁止**读取 task.md frontmatter 的 `short_id` 字段(该字段不可信)。
|
|
21
|
+
|
|
22
|
+
在已解析出完整 `$task_id` 后,用以下片段反查短号;命中返回 `#NN`,未命中自动回退完整 `TASK-id`:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
task_ref=$(node -e '
|
|
26
|
+
const cp=require("child_process");
|
|
27
|
+
const out=cp.execSync("node .agents/scripts/task-short-id.js list",{encoding:"utf8"});
|
|
28
|
+
const ids=(JSON.parse(out).ids)||{};
|
|
29
|
+
const full=process.argv[1];
|
|
30
|
+
const hit=Object.entries(ids).find(([,v])=>v===full);
|
|
31
|
+
process.stdout.write(hit?("#"+hit[0]):full);
|
|
32
|
+
' "$task_id")
|
|
33
|
+
# 示例:$task_id=TASK-20260613-225809 -> task_ref=#15
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## 回退条件
|
|
37
|
+
|
|
38
|
+
`{task-ref}` 在以下情况回退为完整 `TASK-id`(即注册表查不到对应短号):
|
|
39
|
+
|
|
40
|
+
- **未分配**:任务尚未经 `create-task` / `import-*` / `restore-task` 分配短号的极早期路径。
|
|
41
|
+
- **已释放**:任务经 `complete-task` / `cancel-task` / `block-task` / `close-codescan` / `close-dependabot` 归档后,短号立即从注册表移除。这些归档类 skill 的终态/摘要行因此自然回退完整 `TASK-id`,无需特判。
|
|
42
|
+
|
|
43
|
+
`restore-task` 恢复任务时会重新分配短号(可能与历史不同),片段会取到新短号。
|
|
44
|
+
|
|
45
|
+
## `#` 前缀与 shell 引用
|
|
46
|
+
|
|
47
|
+
短号统一渲染为带 `#` 前缀的 `#NN`,与 task.md frontmatter 的 `short_id` 渲染一致。`#` 在 bash 中是注释起始符,示例命令若直接粘贴需视 TUI 而定(裸数字 `NN` 与 `#NN` 都被 `task-short-id.js resolve` 接受)。
|
|
48
|
+
|
|
49
|
+
## 完成时间收尾行(Completed at)
|
|
50
|
+
|
|
51
|
+
所有读取本规则、并向用户渲染「下一步 / 告知用户」输出的 skill,在面向用户输出的**绝对最后一行**统一追加一行完成时间,便于用户在 tmux 多窗口扫视时一眼判断各 Agent 的完成先后:
|
|
52
|
+
|
|
53
|
+
```text
|
|
54
|
+
Completed at: YYYY-MM-DD HH:mm:ss
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- 取值命令(本地时区、不带偏移):`date "+%Y-%m-%d %H:%M:%S"`
|
|
58
|
+
- 位置:必须是整段面向用户输出的最后一行,排在所有「下一步」命令之后。若某场景在命令之后还有条件性提醒行(如 env-blocked 提醒),收尾行排在该提醒行之后。
|
|
59
|
+
- 该行只用于终端扫视,不写入任何产物文件或 Issue/PR 评论;完成时刻的单一事实源仍是 task.md 的 Activity Log。
|