@fitlab-ai/agent-infra 0.7.1 → 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 +11 -0
- package/dist/bin/cli.js +12 -0
- 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 +21 -32
- package/dist/lib/sandbox/commands/ls.js +20 -22
- package/dist/lib/sandbox/index.js +7 -3
- package/dist/lib/sandbox/task-resolver.js +1 -1
- 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/lib/sandbox/commands/create.ts +11 -2
- package/lib/sandbox/commands/enter.ts +8 -7
- package/lib/sandbox/commands/list-running.ts +23 -37
- package/lib/sandbox/commands/ls.ts +25 -25
- package/lib/sandbox/index.ts +7 -3
- package/lib/sandbox/task-resolver.ts +1 -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/package.json +1 -1
- 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 +54 -62
- package/templates/.agents/rules/task-short-id.zh-CN.md +35 -54
- package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +17 -0
- package/templates/.agents/scripts/task-short-id.js +32 -189
- package/templates/.agents/skills/analyze-task/SKILL.en.md +10 -12
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +10 -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 +6 -6
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/block-task/config/verify.json +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +6 -6
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
- package/templates/.agents/skills/check-task/SKILL.en.md +12 -10
- package/templates/.agents/skills/check-task/SKILL.zh-CN.md +12 -10
- package/templates/.agents/skills/close-codescan/SKILL.en.md +6 -6
- package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/close-dependabot/SKILL.en.md +6 -6
- package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/code-task/SKILL.en.md +10 -6
- package/templates/.agents/skills/code-task/SKILL.zh-CN.md +11 -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 +2 -2
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +2 -2
- 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 +6 -2
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +6 -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 +6 -6
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +6 -6
- 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 +17 -17
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +17 -17
- package/templates/.agents/skills/create-task/config/verify.json +1 -1
- package/templates/.agents/skills/import-codescan/SKILL.en.md +8 -8
- package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/import-codescan/config/verify.json +1 -1
- package/templates/.agents/skills/import-dependabot/SKILL.en.md +8 -8
- package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +8 -8
- package/templates/.agents/skills/import-dependabot/config/verify.json +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +7 -7
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +7 -7
- package/templates/.agents/skills/plan-task/SKILL.en.md +10 -12
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +10 -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 +1 -1
- package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-analysis/SKILL.en.md +4 -2
- package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +4 -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 +5 -2
- package/templates/.agents/skills/review-code/SKILL.zh-CN.md +5 -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 +4 -2
- package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +4 -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/templates/task.en.md +0 -1
- package/templates/.agents/templates/task.zh-CN.md +0 -1
- 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
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
type Frontmatter = Record<string, string>;
|
|
2
|
+
|
|
3
|
+
function parseTaskFrontmatter(content: string): Frontmatter {
|
|
4
|
+
const result: Frontmatter = {};
|
|
5
|
+
if (!content.startsWith('---')) return result;
|
|
6
|
+
const end = content.indexOf('\n---', 3);
|
|
7
|
+
if (end === -1) return result;
|
|
8
|
+
const body = content.slice(3, end);
|
|
9
|
+
for (const rawLine of body.split('\n')) {
|
|
10
|
+
const line = rawLine.replace(/\r$/, '');
|
|
11
|
+
if (!line.trim()) continue;
|
|
12
|
+
const colon = line.indexOf(':');
|
|
13
|
+
if (colon === -1) continue;
|
|
14
|
+
const key = line.slice(0, colon).trim();
|
|
15
|
+
const value = line.slice(colon + 1).trim();
|
|
16
|
+
if (key) result[key] = value;
|
|
17
|
+
}
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function extractTitle(content: string): string {
|
|
22
|
+
for (const line of content.split('\n')) {
|
|
23
|
+
const m = /^#\s+(?:任务[::]?\s*)?(.+)$/.exec(line.trim());
|
|
24
|
+
if (m && m[1]) return m[1].trim();
|
|
25
|
+
}
|
|
26
|
+
return '';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export { parseTaskFrontmatter, extractTitle };
|
|
30
|
+
export type { Frontmatter };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const USAGE = `Usage: ai task <command> [options]
|
|
2
|
+
|
|
3
|
+
Commands:
|
|
4
|
+
ls [--all | --blocked | --completed] List tasks (default: active)
|
|
5
|
+
show <N | #N | TASK-id> Print a task.md
|
|
6
|
+
|
|
7
|
+
Examples:
|
|
8
|
+
ai task ls
|
|
9
|
+
ai task show 11
|
|
10
|
+
ai task show TASK-20260612-162737
|
|
11
|
+
|
|
12
|
+
Run 'ai task <command> --help' for details.`;
|
|
13
|
+
|
|
14
|
+
export async function runTask(args: string[]): Promise<void> {
|
|
15
|
+
const [subcommand, ...rest] = args;
|
|
16
|
+
|
|
17
|
+
if (!subcommand) {
|
|
18
|
+
process.stdout.write(`${USAGE}\n`);
|
|
19
|
+
process.exitCode = 1;
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (subcommand === '--help' || subcommand === '-h' || subcommand === 'help') {
|
|
24
|
+
process.stdout.write(`${USAGE}\n`);
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
switch (subcommand) {
|
|
29
|
+
case 'ls': {
|
|
30
|
+
const { ls } = await import('./commands/ls.ts');
|
|
31
|
+
ls(rest);
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
case 'show': {
|
|
35
|
+
const { show } = await import('./commands/show.ts');
|
|
36
|
+
show(rest);
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
default:
|
|
40
|
+
process.stderr.write(`Unknown task command: ${subcommand}\n\n`);
|
|
41
|
+
process.stdout.write(`${USAGE}\n`);
|
|
42
|
+
process.exitCode = 1;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
const REGISTRY_NAME = '.short-ids.json';
|
|
5
|
+
|
|
6
|
+
type NormalizeResult =
|
|
7
|
+
| { kind: 'shortId'; value: string }
|
|
8
|
+
| { kind: 'pass'; value: string }
|
|
9
|
+
| { kind: 'error'; message: string };
|
|
10
|
+
|
|
11
|
+
type NormalizeOpts = { shortIdLength: number };
|
|
12
|
+
|
|
13
|
+
function normalizeShortIdInput(input: string, opts: NormalizeOpts): NormalizeResult {
|
|
14
|
+
const L = opts.shortIdLength;
|
|
15
|
+
const m = /^#?(\d+)$/.exec(input);
|
|
16
|
+
if (!m) {
|
|
17
|
+
return { kind: 'pass', value: input };
|
|
18
|
+
}
|
|
19
|
+
const n = Number(m[1]);
|
|
20
|
+
if (n === 0) {
|
|
21
|
+
return {
|
|
22
|
+
kind: 'error',
|
|
23
|
+
message: `short id '${input}' is invalid (#${'0'.repeat(L)} is reserved)`
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const max = Math.pow(10, L) - 1;
|
|
27
|
+
if (n > max) {
|
|
28
|
+
return {
|
|
29
|
+
kind: 'error',
|
|
30
|
+
message: `short id ${n} exceeds shortIdLength=${L} capacity (max=${max}); archive tasks or raise task.shortIdLength in .agents/.airc.json`
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return { kind: 'shortId', value: `#${String(n).padStart(L, '0')}` };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
type RegistrySchema = {
|
|
37
|
+
version: number;
|
|
38
|
+
ids: Record<string, string>;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
function readRegistry(repoRoot: string): RegistrySchema | null {
|
|
42
|
+
const registryPath = path.join(repoRoot, '.agents', 'workspace', 'active', REGISTRY_NAME);
|
|
43
|
+
if (!fs.existsSync(registryPath)) return null;
|
|
44
|
+
try {
|
|
45
|
+
const raw = fs.readFileSync(registryPath, 'utf8');
|
|
46
|
+
const data = JSON.parse(raw) as RegistrySchema;
|
|
47
|
+
if (!data || typeof data !== 'object' || !data.ids) return null;
|
|
48
|
+
return data;
|
|
49
|
+
} catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function readBranchFromTaskMd(repoRoot: string, taskId: string): string | null {
|
|
55
|
+
const taskMdPath = path.join(repoRoot, '.agents', 'workspace', 'active', taskId, 'task.md');
|
|
56
|
+
if (!fs.existsSync(taskMdPath)) return null;
|
|
57
|
+
const content = fs.readFileSync(taskMdPath, 'utf8');
|
|
58
|
+
const m = content.match(/^branch:\s*(.+)$/m);
|
|
59
|
+
if (!m || !m[1]) return null;
|
|
60
|
+
return m[1].trim().replace(/^(["'])(.*)\1$/, '$2');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function loadShortIdByTaskId(repoRoot: string): Map<string, string> {
|
|
64
|
+
const registry = readRegistry(repoRoot);
|
|
65
|
+
const map = new Map<string, string>();
|
|
66
|
+
if (!registry) return map;
|
|
67
|
+
for (const [key, taskId] of Object.entries(registry.ids)) {
|
|
68
|
+
map.set(taskId, `#${key}`);
|
|
69
|
+
}
|
|
70
|
+
return map;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function lookupShortIdByBranch(
|
|
74
|
+
branch: string,
|
|
75
|
+
repoRoot: string,
|
|
76
|
+
_opts?: { shortIdLength?: number }
|
|
77
|
+
): string | null {
|
|
78
|
+
const registry = readRegistry(repoRoot);
|
|
79
|
+
if (!registry) return null;
|
|
80
|
+
const matches: string[] = [];
|
|
81
|
+
for (const [key, taskId] of Object.entries(registry.ids)) {
|
|
82
|
+
const taskBranch = readBranchFromTaskMd(repoRoot, taskId);
|
|
83
|
+
if (taskBranch && taskBranch === branch) {
|
|
84
|
+
matches.push(`#${key}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (matches.length === 0) return null;
|
|
88
|
+
if (matches.length > 1) {
|
|
89
|
+
process.stderr.write(
|
|
90
|
+
`Warning: branch '${branch}' is bound to multiple active tasks: ${matches.join(', ')}; using ${matches[0]}\n`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
return matches[0]!;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export { normalizeShortIdInput, lookupShortIdByBranch, loadShortIdByTaskId };
|
|
97
|
+
export type { NormalizeResult, NormalizeOpts };
|
package/package.json
CHANGED
|
@@ -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。
|
|
@@ -1,27 +1,33 @@
|
|
|
1
1
|
# Task short id
|
|
2
2
|
|
|
3
3
|
Task short ids let mobile-style SKILL invocations replace the full 22-char
|
|
4
|
-
`TASK-YYYYMMDD-HHMMSS` with `#NN` while a
|
|
4
|
+
`TASK-YYYYMMDD-HHMMSS` with bare numeric `N` (recommended) or `#NN` while a
|
|
5
|
+
task is active.
|
|
5
6
|
|
|
6
7
|
## Syntax
|
|
7
8
|
|
|
8
|
-
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
|
|
9
|
+
- Two equivalent literal forms are accepted:
|
|
10
|
+
- **Bare numeric `N`** (recommended; no shell quoting needed): e.g. `1`, `7`, `42`.
|
|
11
|
+
- **`#`-prefixed `#N` / `#NN`** (also accepted; bash needs `'...'` quoting): e.g. `#1`, `#01`, `#42`.
|
|
12
|
+
- Resolution: drop leading zeros and take the numeric value `n`; if `n == 0`,
|
|
13
|
+
reject (reserved); if `n > 10^shortIdLength - 1`, reject (over capacity);
|
|
14
|
+
otherwise canonicalize to `#${n.padStart(shortIdLength, '0')}` as the
|
|
15
|
+
registry key.
|
|
16
|
+
- With the default `shortIdLength=2`, capacity is `n ∈ [1, 99]`; registry keys
|
|
17
|
+
look like `01`, `07`, `42`.
|
|
12
18
|
- `#00` (or `#0` when `shortIdLength=1`) is reserved and never allocated; digits
|
|
13
19
|
only, no letters.
|
|
14
|
-
- The plain `TASK-…` form keeps working everywhere; `#NN` is an
|
|
15
|
-
persisted task id.
|
|
20
|
+
- The plain `TASK-…` form keeps working everywhere; bare numeric / `#NN` is an
|
|
21
|
+
alias, not the persisted task id.
|
|
16
22
|
|
|
17
23
|
## Lifecycle
|
|
18
24
|
|
|
19
|
-
| Action | When | Effect on registry
|
|
25
|
+
| Action | When | Effect on registry |
|
|
20
26
|
|------------|-----------------------------------------------------------------------|---------------------------------------------------------------|
|
|
21
|
-
| alloc | `create-task`, `import-issue`, `import-codescan`, `import-dependabot` | Assigns lowest free `#NN
|
|
27
|
+
| alloc | `create-task`, `import-issue`, `import-codescan`, `import-dependabot` | Assigns lowest free `#NN` in the registry. |
|
|
22
28
|
| resolve | Lifecycle SKILLs (`analyze-task`, `plan-task`, `code-task`, …) | Looks up `#NN` → full task id. Does not allocate. |
|
|
23
|
-
| release | `complete-task`, `cancel-task`, `block-task`, `close-codescan`, `close-dependabot` | Removes the registry entry
|
|
24
|
-
| re-alloc | `restore-task` | Re-allocates a (possibly new) `#NN`
|
|
29
|
+
| release | `complete-task`, `cancel-task`, `block-task`, `close-codescan`, `close-dependabot` | Removes the registry entry. |
|
|
30
|
+
| re-alloc | `restore-task` | Re-allocates a (possibly new) `#NN` in the registry. |
|
|
25
31
|
|
|
26
32
|
Short ids are valid only while a task lives in `.agents/workspace/active/`.
|
|
27
33
|
Once it is moved to `completed/`, `blocked/`, or `archive/`, the `#NN` slot is
|
|
@@ -48,22 +54,23 @@ archiving all active tasks first (the registry key width depends on it).
|
|
|
48
54
|
| Entrypoint | Hit | Miss |
|
|
49
55
|
|-------------------------------------------------------------|----------------------|------------------------------------------------------|
|
|
50
56
|
| SKILL parameter resolver (lifecycle SKILLs) | resolve to full id | **strict error** — short id not found / invalid |
|
|
51
|
-
| `ai sandbox
|
|
57
|
+
| `ai sandbox exec <N \| '#N'>` / `ai sandbox create <N \| '#N'>` | resolve to full id, then read `branch` from task.md | **strict error** — no ls-index fallback, no literal-branch fallback; hint the user to pass a short id / `TASK-id` / branch name |
|
|
52
58
|
|
|
53
|
-
`list --verify` is strictly read-only: it reports discrepancies between
|
|
54
|
-
dir
|
|
59
|
+
`list --verify` is strictly read-only: it reports discrepancies between the
|
|
60
|
+
active dir and the registry, but never writes.
|
|
55
61
|
|
|
56
62
|
## SKILL parameter resolver
|
|
57
63
|
|
|
58
64
|
Any SKILL (alloc / resolve / release / re-alloc lifecycle entry-points) that
|
|
59
65
|
receives a `{task-id}` argument must follow this contract:
|
|
60
66
|
|
|
61
|
-
1. If `{task-id}`
|
|
67
|
+
1. If `{task-id}` matches `^[#]?[0-9]+$` (bare numeric `N` or `#`-prefixed `#N`):
|
|
62
68
|
|
|
63
69
|
```bash
|
|
64
|
-
if [[ "{task-id}"
|
|
65
|
-
# The script writes the full error message (
|
|
66
|
-
#
|
|
70
|
+
if [[ "{task-id}" =~ ^[#]?[0-9]+$ ]]; then
|
|
71
|
+
# The script writes the full error message (covering reserved / exceeds
|
|
72
|
+
# shortIdLength capacity / malformed input) to stderr; callers only forward
|
|
73
|
+
# the exit.
|
|
67
74
|
task_id=$(node .agents/scripts/task-short-id.js resolve "{task-id}") || exit 1
|
|
68
75
|
else
|
|
69
76
|
task_id="{task-id}"
|
|
@@ -77,14 +84,8 @@ fi
|
|
|
77
84
|
|
|
78
85
|
## Storage
|
|
79
86
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
| Location | Written by | Read by | Removed by |
|
|
83
|
-
|---|---|---|---|
|
|
84
|
-
| `.agents/workspace/active/.short-ids.json` (registry) | `alloc` / cold-start migration | `resolve` (authoritative) / `list` / `list --verify` | `release` / cold-start stale cleanup |
|
|
85
|
-
| `short_id` frontmatter field in each task.md | `alloc` / cold-start migration | `list --verify` (consistency check) | **never** (kept as historical value after archive) |
|
|
86
|
-
|
|
87
|
-
**Registry**:
|
|
87
|
+
Short ids are pure local state, persisted only in the registry
|
|
88
|
+
`.agents/workspace/active/.short-ids.json`; task.md does not hold the short id:
|
|
88
89
|
|
|
89
90
|
- Path: `<repo-root>/.agents/workspace/active/.short-ids.json`
|
|
90
91
|
- Schema: `{ "version": 1, "ids": { "01": "TASK-20260609-192644", "02": "TASK-…" } }`
|
|
@@ -92,50 +93,41 @@ The short id system persists state in two places that stay in sync at rest:
|
|
|
92
93
|
full `TASK-…` task ids.
|
|
93
94
|
- Automatically git-ignored (the whole active workspace is ignored; no new
|
|
94
95
|
ignore entry needed).
|
|
95
|
-
- Created on demand by the first `alloc
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
- Lives in frontmatter, immediately after `id`; formatted `short_id: #01`.
|
|
101
|
-
- Matches the registry key byte-for-byte (including the `#` prefix).
|
|
96
|
+
- Created on demand by the first `alloc`; an absent file is treated as an empty
|
|
97
|
+
registry.
|
|
98
|
+
- Short ids are assigned only by an explicit `alloc` (`create-task` /
|
|
99
|
+
`import-*` / `restore-task`); `resolve` / `list` / `release` never allocate —
|
|
100
|
+
they only clean up stale entries pointing at non-active tasks.
|
|
102
101
|
- After archive (complete-task / cancel-task / block-task / close-*) the
|
|
103
|
-
registry entry is deleted immediately
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
② look up `NN` directly as the registry `ids` key → ③ return full task id on
|
|
113
|
-
hit; on miss, exit 1 with the `list --verify` repair hint.
|
|
102
|
+
registry entry is deleted immediately and the short id may be reused; archived
|
|
103
|
+
tasks are referenced by their full `TASK-…` id.
|
|
104
|
+
|
|
105
|
+
`resolve(<N|'#N'>)` workflow: ① validate arg matches `^[#]?[0-9]+$` →
|
|
106
|
+
② strip leading zeros and take the numeric value `n`; classify as reserved
|
|
107
|
+
(`n == 0`) / over capacity (`n > 10^shortIdLength - 1`) / normal → ③ on
|
|
108
|
+
normal, use `n.padStart(shortIdLength, '0')` as the registry `ids` key
|
|
109
|
+
→ ④ return full task id on hit; on miss, exit 1 with the `list --verify`
|
|
110
|
+
repair hint.
|
|
114
111
|
|
|
115
112
|
## Error scenarios
|
|
116
113
|
|
|
117
|
-
- **Short id not found**: the registry has no entry for
|
|
118
|
-
was archived (release freed the slot) or the input is
|
|
114
|
+
- **Short id not found**: the registry has no entry for the resolved key.
|
|
115
|
+
Either the task was archived (release freed the slot) or the input is
|
|
116
|
+
wrong. Exit code 1.
|
|
119
117
|
- **Registry corruption** (duplicate registry entries for the same task id, or
|
|
120
118
|
the JSON is unparsable): exit code 2; manual cleanup required.
|
|
121
|
-
- **
|
|
122
|
-
|
|
119
|
+
- **Reserved key**: the resolved `n == 0` (inputs like `0`, `#0`, `#00`). Exit code 1.
|
|
120
|
+
- **Over capacity**: the resolved `n > 10^shortIdLength - 1` (e.g. `100` or
|
|
121
|
+
`#100` when `shortIdLength=2`). Exit code 1.
|
|
122
|
+
- **Parameter format error**: input matches neither `^[#]?[0-9]+$` nor a
|
|
123
|
+
`TASK-id` (e.g. `#abc`, `#`, `5.5`). Exit code 1.
|
|
123
124
|
|
|
124
125
|
## Cross-TUI quoting
|
|
125
126
|
|
|
126
|
-
|
|
127
|
+
Bare numeric `N` is safe in every shell and TUI without quoting (recommended):
|
|
128
|
+
`ai sandbox exec 11 'npm test'`, `/review-analysis 11`.
|
|
129
|
+
|
|
130
|
+
The `#N` / `#NN` form is also accepted; but bash treats `#` as a comment
|
|
131
|
+
marker, so it must be single-quoted: `ai sandbox exec '#03' 'npm test'`.
|
|
127
132
|
Claude Code / Codex / Gemini CLI / OpenCode all forward `#NN` to SKILL
|
|
128
133
|
`ARGUMENTS` literally when quoted.
|
|
129
|
-
|
|
130
|
-
## Cold-start migration
|
|
131
|
-
|
|
132
|
-
When a project upgrades to a version with this feature, the first call to
|
|
133
|
-
`alloc` / `resolve` runs the cold-start path:
|
|
134
|
-
|
|
135
|
-
- Active tasks whose `task.md` lacks `short_id` get one allocated and written
|
|
136
|
-
back (the only frontmatter mutation; `updated_at` / `agent_infra_version`
|
|
137
|
-
are **not** refreshed and Activity Log is **not** appended).
|
|
138
|
-
- If active task count exceeds `shortIdLength` capacity, the migration aborts
|
|
139
|
-
**before any write** with a capacity error.
|
|
140
|
-
- If a partial write fails midway, `tx.commit()` rolls all task.md files back to
|
|
141
|
-
their original content (including `mtime` / `atime`).
|