@kunlv/atdc 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +58 -0
- package/GO.md +245 -0
- package/bin/add-command.py +144 -0
- package/bin/atdc.js +460 -0
- package/bin/cls.js +98 -0
- package/central/link.sh +60 -0
- package/central/ticket_center_server/.handoffs/TASK-238389_QYinVipLimit.md +128 -0
- package/central/ticket_center_server/.test_plans/TASK-227254-SupportMembership-self-test-plan.md +566 -0
- package/central/ticket_center_server/.test_plans/task-238389-qyin-vip-limit-self-test-plan.md +210 -0
- package/commands/claude/atd.md +3 -0
- package/commands/claude/atdc/1-spec.md +5 -0
- package/commands/claude/atdc/1-update.md +5 -0
- package/commands/claude/atdc/2-qa.md +3 -0
- package/commands/claude/atdc/3-impl-skip-spec.md +5 -0
- package/commands/claude/atdc/3-impl.md +3 -0
- package/commands/claude/atdc/4-verify.md +3 -0
- package/commands/claude/atdc/5-done.md +3 -0
- package/commands/claude/atdc/add-command.md +4 -0
- package/commands/claude/atdc/add-to-hc.md +3 -0
- package/commands/claude/atdc/analysis-cls-log.md +4 -0
- package/commands/claude/atdc/article.md +6 -0
- package/commands/claude/atdc/bugfix.md +4 -0
- package/commands/claude/atdc/code-simplifier.md +8 -0
- package/commands/claude/atdc/commit.md +4 -0
- package/commands/claude/atdc/compound.md +5 -0
- package/commands/claude/atdc/context.md +3 -0
- package/commands/claude/atdc/diagnose.md +8 -0
- package/commands/claude/atdc/git-guardrails.md +18 -0
- package/commands/claude/atdc/grill-with-docs.md +8 -0
- package/commands/claude/atdc/handoff.md +8 -0
- package/commands/claude/atdc/health.md +3 -0
- package/commands/claude/atdc/learn.md +4 -0
- package/commands/claude/atdc/review.md +10 -0
- package/commands/claude/atdc/search.md +4 -0
- package/commands/claude/atdc/strategy.md +3 -0
- package/commands/claude/atdc/table2xlsx.md +4 -0
- package/commands/claude/atdc/test-plan.md +5 -0
- package/commands/claude/atdc/travel-planner.md +4 -0
- package/commands/claude/atdc/xlsx2sql-insert.md +4 -0
- package/commands/claude/atdc/xlsx2sql-update.md +4 -0
- package/commands/claude/atdc/zoom-out.md +8 -0
- package/commands/codex/atdc/SKILL.md +8 -0
- package/commands/codex/atdc-1-spec/SKILL.md +8 -0
- package/commands/codex/atdc-1-update/SKILL.md +8 -0
- package/commands/codex/atdc-2-qa/SKILL.md +8 -0
- package/commands/codex/atdc-3-impl/SKILL.md +8 -0
- package/commands/codex/atdc-3-impl-skip-spec/SKILL.md +8 -0
- package/commands/codex/atdc-4-verify/SKILL.md +8 -0
- package/commands/codex/atdc-5-done/SKILL.md +8 -0
- package/commands/codex/atdc-add-command/SKILL.md +7 -0
- package/commands/codex/atdc-add-to-hc/SKILL.md +8 -0
- package/commands/codex/atdc-analysis-cls-log/SKILL.md +5 -0
- package/commands/codex/atdc-article/SKILL.md +8 -0
- package/commands/codex/atdc-bugfix/SKILL.md +5 -0
- package/commands/codex/atdc-code-simplifier/SKILL.md +7 -0
- package/commands/codex/atdc-commit/SKILL.md +7 -0
- package/commands/codex/atdc-compound/SKILL.md +8 -0
- package/commands/codex/atdc-context/SKILL.md +8 -0
- package/commands/codex/atdc-diagnose/SKILL.md +9 -0
- package/commands/codex/atdc-git-guardrails/SKILL.md +19 -0
- package/commands/codex/atdc-grill-with-docs/SKILL.md +9 -0
- package/commands/codex/atdc-handoff/SKILL.md +9 -0
- package/commands/codex/atdc-health/SKILL.md +8 -0
- package/commands/codex/atdc-learn/SKILL.md +7 -0
- package/commands/codex/atdc-review/SKILL.md +49 -0
- package/commands/codex/atdc-search/SKILL.md +5 -0
- package/commands/codex/atdc-strategy/SKILL.md +8 -0
- package/commands/codex/atdc-table2xlsx/SKILL.md +8 -0
- package/commands/codex/atdc-test-plan/SKILL.md +13 -0
- package/commands/codex/atdc-travel-planner/SKILL.md +7 -0
- package/commands/codex/atdc-xlsx2sql-insert/SKILL.md +8 -0
- package/commands/codex/atdc-xlsx2sql-update/SKILL.md +8 -0
- package/commands/codex/atdc-zoom-out/SKILL.md +9 -0
- package/commands/cursor/atdc/SKILL.md +8 -0
- package/commands/cursor/atdc-1-spec/SKILL.md +8 -0
- package/commands/cursor/atdc-1-update/SKILL.md +8 -0
- package/commands/cursor/atdc-2-qa/SKILL.md +8 -0
- package/commands/cursor/atdc-3-impl/SKILL.md +8 -0
- package/commands/cursor/atdc-3-impl-skip-spec/SKILL.md +8 -0
- package/commands/cursor/atdc-4-verify/SKILL.md +8 -0
- package/commands/cursor/atdc-5-done/SKILL.md +8 -0
- package/commands/cursor/atdc-add-command/SKILL.md +7 -0
- package/commands/cursor/atdc-add-to-hc/SKILL.md +8 -0
- package/commands/cursor/atdc-analysis-cls-log/SKILL.md +5 -0
- package/commands/cursor/atdc-article/SKILL.md +8 -0
- package/commands/cursor/atdc-bugfix/SKILL.md +5 -0
- package/commands/cursor/atdc-code-simplifier/SKILL.md +7 -0
- package/commands/cursor/atdc-commit/SKILL.md +7 -0
- package/commands/cursor/atdc-compound/SKILL.md +8 -0
- package/commands/cursor/atdc-context/SKILL.md +8 -0
- package/commands/cursor/atdc-diagnose/SKILL.md +8 -0
- package/commands/cursor/atdc-git-guardrails/SKILL.md +18 -0
- package/commands/cursor/atdc-grill-with-docs/SKILL.md +8 -0
- package/commands/cursor/atdc-handoff/SKILL.md +8 -0
- package/commands/cursor/atdc-health/SKILL.md +8 -0
- package/commands/cursor/atdc-learn/SKILL.md +7 -0
- package/commands/cursor/atdc-review/SKILL.md +49 -0
- package/commands/cursor/atdc-search/SKILL.md +5 -0
- package/commands/cursor/atdc-strategy/SKILL.md +8 -0
- package/commands/cursor/atdc-table2xlsx/SKILL.md +8 -0
- package/commands/cursor/atdc-test-plan/SKILL.md +7 -0
- package/commands/cursor/atdc-travel-planner/SKILL.md +7 -0
- package/commands/cursor/atdc-xlsx2sql-insert/SKILL.md +8 -0
- package/commands/cursor/atdc-xlsx2sql-update/SKILL.md +8 -0
- package/commands/cursor/atdc-zoom-out/SKILL.md +8 -0
- package/commands/opencode/atd.md +6 -0
- package/commands/opencode/atdc/1-spec.md +8 -0
- package/commands/opencode/atdc/1-update.md +8 -0
- package/commands/opencode/atdc/2-qa.md +6 -0
- package/commands/opencode/atdc/3-impl-skip-spec.md +8 -0
- package/commands/opencode/atdc/3-impl.md +6 -0
- package/commands/opencode/atdc/4-verify.md +6 -0
- package/commands/opencode/atdc/5-done.md +6 -0
- package/commands/opencode/atdc/add-command.md +6 -0
- package/commands/opencode/atdc/add-to-hc.md +6 -0
- package/commands/opencode/atdc/analysis-cls-log.md +6 -0
- package/commands/opencode/atdc/article.md +6 -0
- package/commands/opencode/atdc/bugfix.md +6 -0
- package/commands/opencode/atdc/code-simplifier.md +8 -0
- package/commands/opencode/atdc/commit.md +6 -0
- package/commands/opencode/atdc/compound.md +8 -0
- package/commands/opencode/atdc/context.md +6 -0
- package/commands/opencode/atdc/diagnose.md +8 -0
- package/commands/opencode/atdc/git-guardrails.md +18 -0
- package/commands/opencode/atdc/grill-with-docs.md +8 -0
- package/commands/opencode/atdc/handoff.md +8 -0
- package/commands/opencode/atdc/health.md +6 -0
- package/commands/opencode/atdc/learn.md +6 -0
- package/commands/opencode/atdc/review.md +7 -0
- package/commands/opencode/atdc/search.md +6 -0
- package/commands/opencode/atdc/strategy.md +6 -0
- package/commands/opencode/atdc/table2xlsx.md +6 -0
- package/commands/opencode/atdc/test-plan.md +6 -0
- package/commands/opencode/atdc/travel-planner.md +6 -0
- package/commands/opencode/atdc/xlsx2sql-insert.md +6 -0
- package/commands/opencode/atdc/xlsx2sql-update.md +6 -0
- package/commands/opencode/atdc/zoom-out.md +8 -0
- package/config/cls-defaults.json +19 -0
- package/hooks/atd-archive.sh +86 -0
- package/hooks/atd-state.sh +165 -0
- package/hooks/evidence-hook.sh +88 -0
- package/hooks/evidence-log.sh +54 -0
- package/hooks/guardrails.sh +65 -0
- package/hooks/handoff-compress.sh +104 -0
- package/hooks/phase-guard.sh +134 -0
- package/hooks/phase-write-guard.sh +96 -0
- package/hooks/ponytail-fix.sh +179 -0
- package/hooks/ponytail-guard.sh +66 -0
- package/hooks/ponytail-scan.sh +62 -0
- package/install.sh +276 -0
- package/package.json +20 -0
- package/packages/cls/bin/cls.js +146 -0
- package/packages/cls/config/defaults.json +17 -0
- package/packages/cls/mcp/server.js +142 -0
- package/packages/cls/package.json +29 -0
- package/packages/cls/scripts/postinstall.js +19 -0
- package/packages/cls/src/client.js +262 -0
- package/prompts/1-spec.md +271 -0
- package/prompts/1-update.md +198 -0
- package/prompts/2-qa.md +164 -0
- package/prompts/3-impl-bugfix.md +271 -0
- package/prompts/3-impl.md +419 -0
- package/prompts/4-verify.md +160 -0
- package/prompts/5-done.md +227 -0
- package/prompts/6-article.md +571 -0
- package/prompts/X-add-command.md +60 -0
- package/prompts/X-add-to-hc.md +178 -0
- package/prompts/X-analysis-cls-log.md +68 -0
- package/prompts/X-bugfix.md +10 -0
- package/prompts/X-code-simplifier.md +38 -0
- package/prompts/X-commit.md +116 -0
- package/prompts/X-compound.md +188 -0
- package/prompts/X-context-scan.md +101 -0
- package/prompts/X-diagnose.md +116 -0
- package/prompts/X-grill-with-docs.md +120 -0
- package/prompts/X-handoff.md +80 -0
- package/prompts/X-health.md +78 -0
- package/prompts/X-learn.md +151 -0
- package/prompts/X-review.md +417 -0
- package/prompts/X-search.md +74 -0
- package/prompts/X-strategy.md +95 -0
- package/prompts/X-table2xlsx.md +46 -0
- package/prompts/X-test-plan.md +436 -0
- package/prompts/X-travel-planner.md +22 -0
- package/prompts/X-xlsx2sql-insert.md +34 -0
- package/prompts/X-xlsx2sql-update.md +40 -0
- package/prompts/X-zoom-out.md +119 -0
- package/reference/diagnose-loop.md +66 -0
- package/reference/gates.md +90 -0
- package/reference/git-guardrails.md +71 -0
- package/reference/guards.md +275 -0
- package/reference/learn.md +100 -0
- package/reference/memory.md +190 -0
- package/reference/risk.md +74 -0
- package/reference/tdd.md +40 -0
- package/scripts/svg2png.js +46 -0
- package/src/cls/client.js +244 -0
- package/src/constants.js +49 -0
- package/src/evidence/ai-draft-metrics.js +157 -0
- package/src/evidence/canvas.js +184 -0
- package/src/evidence/index.js +98 -0
- package/src/evidence/sink-events.js +165 -0
- package/src/evidence/stage-timing.js +112 -0
- package/src/installers/claude.js +79 -0
- package/src/installers/codex.js +62 -0
- package/src/installers/opencode.js +72 -0
- package/src/manifest/ponytail-patterns.json +185 -0
- package/src/mcp/cls-mcp-server.js +142 -0
- package/src/utils.js +79 -0
- package/src/validators/state-validate.js +123 -0
- package/templates/.phase-state.json +13 -0
- package/templates/adr.md +62 -0
- package/templates/changelog.md +21 -0
- package/templates/concepts.md +35 -0
- package/templates/design.md +47 -0
- package/templates/handoff.md +64 -0
- package/templates/learn-skill.md +43 -0
- package/templates/progress.json +17 -0
- package/templates/proposal.md +29 -0
- package/templates/solution.md +36 -0
- package/templates/strategy.md +39 -0
- package/templates/tasks.md +41 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const crypto = require('crypto');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const { execSync, exec } = require('child_process');
|
|
6
|
+
|
|
7
|
+
const CONFIG_DIR = path.join(os.homedir(), '.cls');
|
|
8
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
9
|
+
const CREDS_FILE = path.join(CONFIG_DIR, 'credentials.json');
|
|
10
|
+
const DEFAULT_TEMPLATE = path.join(__dirname, '..', 'config', 'defaults.json');
|
|
11
|
+
|
|
12
|
+
function _defaults() {
|
|
13
|
+
return {
|
|
14
|
+
skill_dir: '',
|
|
15
|
+
python: 'python3',
|
|
16
|
+
accounts: {},
|
|
17
|
+
defaults: { limit: 200, out_dir: '.', topic_type: 'error', regions: 'gz,bj', time_range: '15m' },
|
|
18
|
+
output: { format: 'jsonl', filename: '{account}_{query}_{ts}-{suffix}.jsonl' },
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function initConfig() {
|
|
23
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
24
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
25
|
+
return { ok: false, msg: `配置文件已存在: ${CONFIG_FILE}`, path: CONFIG_FILE };
|
|
26
|
+
}
|
|
27
|
+
if (!fs.existsSync(DEFAULT_TEMPLATE)) {
|
|
28
|
+
return { ok: false, msg: `模板文件不存在: ${DEFAULT_TEMPLATE}` };
|
|
29
|
+
}
|
|
30
|
+
const template = fs.readFileSync(DEFAULT_TEMPLATE, 'utf8');
|
|
31
|
+
fs.writeFileSync(CONFIG_FILE, template);
|
|
32
|
+
return { ok: true, msg: `配置文件已生成: ${CONFIG_FILE},请编辑 skill_dir 和 accounts`, path: CONFIG_FILE };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function loadConfig() {
|
|
36
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
37
|
+
throw new Error(`配置文件 ${CONFIG_FILE} 不存在。请运行: cls --init`);
|
|
38
|
+
}
|
|
39
|
+
const raw = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
40
|
+
return { ..._defaults(), ...raw, defaults: { ..._defaults().defaults, ...(raw.defaults || {}) }, output: { ..._defaults().output, ...(raw.output || {}) } };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function resolveAccount(config, alias) {
|
|
44
|
+
const entry = config.accounts[alias];
|
|
45
|
+
if (!entry) {
|
|
46
|
+
const available = Object.keys(config.accounts).join(', ');
|
|
47
|
+
throw new Error(`账号 "${alias}" 未在配置中找到。可用: ${available}`);
|
|
48
|
+
}
|
|
49
|
+
if (typeof entry === 'string') return { account: entry, project_id: null };
|
|
50
|
+
return { account: entry.account || alias, project_id: entry.project_id || null };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function detectPython(cmd) {
|
|
54
|
+
try {
|
|
55
|
+
execSync(`which "${cmd}" 2>/dev/null || command -v "${cmd}"`, { encoding: 'utf8' });
|
|
56
|
+
return true;
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function _relativeTime(range) {
|
|
63
|
+
const m = range.match(/^(\d+)(m|h|d|s)$/);
|
|
64
|
+
if (!m) throw new Error(`无效的时间范围: ${range}(支持 15m/1h/2d)`);
|
|
65
|
+
const val = parseInt(m[1]);
|
|
66
|
+
const unit = m[2];
|
|
67
|
+
const ms = val * { s: 1000, m: 60000, h: 3600000, d: 86400000 }[unit];
|
|
68
|
+
return new Date(Date.now() - ms).toISOString().replace('T', ' ').slice(0, 19);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function _now() {
|
|
72
|
+
return new Date().toISOString().replace('T', ' ').slice(0, 19);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function _safeQuery(query) {
|
|
76
|
+
return (query || 'log')
|
|
77
|
+
.replace(/[^a-zA-Z0-9\u4e00-\u9fff]+/g, '_')
|
|
78
|
+
.replace(/_+/g, '_')
|
|
79
|
+
.replace(/^_|_$/g, '')
|
|
80
|
+
.slice(0, 30);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function buildOutputPath(account, query, outDir) {
|
|
84
|
+
const ts = new Date().toISOString().replace(/[-:T]/g, '').slice(0, 15);
|
|
85
|
+
const suffix = crypto.randomBytes(2).toString('hex');
|
|
86
|
+
const safeQ = _safeQuery(query);
|
|
87
|
+
const ext = 'jsonl';
|
|
88
|
+
const name = `${account}_${safeQ}_${ts}-${suffix}.${ext}`;
|
|
89
|
+
const dir = (outDir || '.').replace(/^~/, os.homedir());
|
|
90
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
91
|
+
return path.join(dir, name);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function buildSearchArgs(config, account, query, opts = {}) {
|
|
95
|
+
const resolved = resolveAccount(config, account);
|
|
96
|
+
const script = path.join(config.skill_dir, 'scripts', 'cls_query_python.py');
|
|
97
|
+
const outputPath = buildOutputPath(account, query, opts.out_dir || config.defaults.out_dir);
|
|
98
|
+
const limit = opts.limit || config.defaults.limit;
|
|
99
|
+
const from = opts.from || _relativeTime(config.defaults.time_range);
|
|
100
|
+
const to = opts.to || _now();
|
|
101
|
+
const topicType = opts.type || config.defaults.topic_type;
|
|
102
|
+
const regions = opts.regions || config.defaults.regions;
|
|
103
|
+
|
|
104
|
+
const args = [
|
|
105
|
+
config.python,
|
|
106
|
+
script,
|
|
107
|
+
'--query', query,
|
|
108
|
+
'--limit', String(limit),
|
|
109
|
+
'--from', from,
|
|
110
|
+
'--to', to,
|
|
111
|
+
'--topic-type', topicType,
|
|
112
|
+
'--regions', regions,
|
|
113
|
+
'--output', outputPath,
|
|
114
|
+
'--pretty',
|
|
115
|
+
];
|
|
116
|
+
|
|
117
|
+
// credentials from ~/.cls/credentials.json (if exists)
|
|
118
|
+
if (fs.existsSync(CREDS_FILE)) {
|
|
119
|
+
const creds = JSON.parse(fs.readFileSync(CREDS_FILE, 'utf8'));
|
|
120
|
+
const cred = creds[resolved.account];
|
|
121
|
+
if (cred && cred.secret_id && cred.secret_key) {
|
|
122
|
+
args.push('--secret-id', cred.secret_id, '--secret-key', cred.secret_key);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// fallback: --account for inventory + credentials routing
|
|
126
|
+
args.push('--account', resolved.account);
|
|
127
|
+
|
|
128
|
+
if (resolved.project_id) {
|
|
129
|
+
args.push('--project-id', resolved.project_id);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return { args, outputPath };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function buildFetchArgs(config, account, query, opts = {}) {
|
|
136
|
+
const resolved = resolveAccount(config, account);
|
|
137
|
+
const script = path.join(config.skill_dir, 'scripts', 'cls_fetch_bulk.py');
|
|
138
|
+
const outputPath = buildOutputPath(account, query, opts.out_dir || config.defaults.out_dir);
|
|
139
|
+
const from = opts.from || _relativeTime(config.defaults.time_range);
|
|
140
|
+
const to = opts.to || _now();
|
|
141
|
+
const max = opts.max || 5000;
|
|
142
|
+
|
|
143
|
+
const args = [
|
|
144
|
+
config.python,
|
|
145
|
+
script,
|
|
146
|
+
'--query', query,
|
|
147
|
+
'--from', from,
|
|
148
|
+
'--to', to,
|
|
149
|
+
'--max', String(max),
|
|
150
|
+
'--out', outputPath,
|
|
151
|
+
'--content-only',
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
if (fs.existsSync(CREDS_FILE)) {
|
|
155
|
+
const creds = JSON.parse(fs.readFileSync(CREDS_FILE, 'utf8'));
|
|
156
|
+
const cred = creds[resolved.account];
|
|
157
|
+
if (cred && cred.secret_id && cred.secret_key) {
|
|
158
|
+
args.push('--secret-id', cred.secret_id, '--secret-key', cred.secret_key);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
args.push('--account', resolved.account);
|
|
162
|
+
if (resolved.project_id) {
|
|
163
|
+
args.push('--project-id', resolved.project_id);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return { args, outputPath };
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function listAccounts() {
|
|
170
|
+
const config = loadConfig();
|
|
171
|
+
const result = {};
|
|
172
|
+
for (const [alias, entry] of Object.entries(config.accounts)) {
|
|
173
|
+
if (typeof entry === 'string') {
|
|
174
|
+
result[alias] = entry;
|
|
175
|
+
} else {
|
|
176
|
+
result[alias] = `${entry.account} → project ${entry.project_id}`;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function searchSync(account, query, opts = {}) {
|
|
183
|
+
const config = loadConfig();
|
|
184
|
+
if (!detectPython(config.python)) throw new Error(`Python 解释器 "${config.python}" 未找到。请检查 ~/.cls/config.json 中 python 字段`);
|
|
185
|
+
|
|
186
|
+
const { args, outputPath } = buildSearchArgs(config, account, query, opts);
|
|
187
|
+
const cmd = args.map(a => a.includes(' ') ? `"${a}"` : a).join(' ');
|
|
188
|
+
|
|
189
|
+
if (!opts.quiet) console.error(`→ ${cmd}`);
|
|
190
|
+
|
|
191
|
+
execSync(cmd, { encoding: 'utf8', maxBuffer: 50 * 1024 * 1024, timeout: (opts.timeout || 60) * 1000 });
|
|
192
|
+
const raw = fs.readFileSync(outputPath, 'utf8');
|
|
193
|
+
const data = JSON.parse(raw);
|
|
194
|
+
const count = data?.Response?.Results?.length || data?.summary?.returned_count || 0;
|
|
195
|
+
|
|
196
|
+
return { output_file: outputPath, count, sample: (data?.Response?.Results || []).slice(0, 3) };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function searchAsync(account, query, opts = {}) {
|
|
200
|
+
return new Promise((resolve, reject) => {
|
|
201
|
+
const config = loadConfig();
|
|
202
|
+
if (!detectPython(config.python)) return reject(new Error(`Python "${config.python}" 未找到`));
|
|
203
|
+
|
|
204
|
+
const { args, outputPath } = buildSearchArgs(config, account, query, opts);
|
|
205
|
+
const cmd = args.map(a => a.includes(' ') ? `"${a}"` : a).join(' ');
|
|
206
|
+
|
|
207
|
+
exec(cmd, { encoding: 'utf8', maxBuffer: 50 * 1024 * 1024, timeout: (opts.timeout || 60) * 1000 }, (err, stdout, stderr) => {
|
|
208
|
+
if (err) return reject(new Error(stderr || err.message));
|
|
209
|
+
try {
|
|
210
|
+
const raw = fs.readFileSync(outputPath, 'utf8');
|
|
211
|
+
const data = JSON.parse(raw);
|
|
212
|
+
const count = data?.Response?.Results?.length || data?.summary?.returned_count || 0;
|
|
213
|
+
resolve({ output_file: outputPath, count, sample: (data?.Response?.Results || []).slice(0, 3) });
|
|
214
|
+
} catch (e) {
|
|
215
|
+
reject(e);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function fetchSync(account, query, opts = {}) {
|
|
222
|
+
const config = loadConfig();
|
|
223
|
+
if (!detectPython(config.python)) throw new Error(`Python "${config.python}" 未找到`);
|
|
224
|
+
|
|
225
|
+
const { args, outputPath } = buildFetchArgs(config, account, query, opts);
|
|
226
|
+
const cmd = args.map(a => a.includes(' ') ? `"${a}"` : a).join(' ');
|
|
227
|
+
|
|
228
|
+
if (!opts.quiet) console.error(`→ ${cmd}`);
|
|
229
|
+
|
|
230
|
+
execSync(cmd, { encoding: 'utf8', maxBuffer: 200 * 1024 * 1024, timeout: (opts.timeout || 120) * 1000 });
|
|
231
|
+
const lines = fs.readFileSync(outputPath, 'utf8').split('\n').filter(Boolean);
|
|
232
|
+
return { output_file: outputPath, total: lines.length };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function fetchAsync(account, query, opts = {}) {
|
|
236
|
+
return new Promise((resolve, reject) => {
|
|
237
|
+
const config = loadConfig();
|
|
238
|
+
if (!detectPython(config.python)) return reject(new Error(`Python "${config.python}" 未找到`));
|
|
239
|
+
|
|
240
|
+
const { args, outputPath } = buildFetchArgs(config, account, query, opts);
|
|
241
|
+
const cmd = args.map(a => a.includes(' ') ? `"${a}"` : a).join(' ');
|
|
242
|
+
|
|
243
|
+
exec(cmd, { encoding: 'utf8', maxBuffer: 200 * 1024 * 1024, timeout: (opts.timeout || 120) * 1000 }, (err, stdout, stderr) => {
|
|
244
|
+
if (err) return reject(new Error(stderr || err.message));
|
|
245
|
+
const lines = fs.readFileSync(outputPath, 'utf8').split('\n').filter(Boolean);
|
|
246
|
+
resolve({ output_file: outputPath, total: lines.length });
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
module.exports = {
|
|
252
|
+
initConfig,
|
|
253
|
+
loadConfig,
|
|
254
|
+
detectPython,
|
|
255
|
+
resolveAccount,
|
|
256
|
+
listAccounts,
|
|
257
|
+
searchSync,
|
|
258
|
+
searchAsync,
|
|
259
|
+
fetchSync,
|
|
260
|
+
fetchAsync,
|
|
261
|
+
buildOutputPath,
|
|
262
|
+
};
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
# prompts/1-spec · 需求澄清 + 规格生成
|
|
2
|
+
|
|
3
|
+
从此文件读取后,直接开始执行,无需用户再次确认启动。
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🔴 Step -1:规则加载(Phase 3 到达时必须执行)
|
|
8
|
+
|
|
9
|
+
> 1-spec 的 Phase 0-2 不依赖外部规则,可直接执行。
|
|
10
|
+
> 到达 Phase 3(复杂度评估)时,MUST Read 以下文件:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
Read @atdc/reference/risk.md → 风险评分算法 + blast radius 评估规则
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Phase 0:判断模式
|
|
19
|
+
|
|
20
|
+
### 0a. 读取产品策略(存在时自动注入)
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
cat STRATEGY.md 2>/dev/null || echo "[无 STRATEGY.md]"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
如果 STRATEGY.md 存在,将其作为 **方向约束** 注入后续所有 Phase:
|
|
27
|
+
- Phase 1 的提问要对照策略中的 Target Problem、Persona、Key Metrics
|
|
28
|
+
- Phase 2 的 proposal 要体现策略中的 Approach 和 Tracks
|
|
29
|
+
- 如果需求与 STRATEGY.md 的 Out of Scope 冲突,主动提示用户
|
|
30
|
+
|
|
31
|
+
不存在则跳过,不影响正常流程。
|
|
32
|
+
|
|
33
|
+
### 0b. 判断需求清晰度
|
|
34
|
+
|
|
35
|
+
检查用户输入是否模糊("我想…"/"有个想法"/"brainstorm" 等):
|
|
36
|
+
|
|
37
|
+
- **模糊** → 先执行 Phase 1(需求澄清)再到 Phase 2
|
|
38
|
+
- **明确需求** → 跳过 Phase 1,直接从 Phase 2 开始
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Phase 1:需求澄清(Brainstorm 模式)
|
|
43
|
+
|
|
44
|
+
逐一提问,每次只问一个问题,等用户回答后再问下一个:
|
|
45
|
+
|
|
46
|
+
1. 这个功能/改动是给谁用的?核心使用场景是什么?
|
|
47
|
+
2. 成功的标准是什么?用户做了什么操作、看到什么结果算完成?
|
|
48
|
+
3. 有哪些不能动的模块、性能要求或兼容性约束?
|
|
49
|
+
4. 有没有已经排除的方案?(避免重蹈)
|
|
50
|
+
|
|
51
|
+
问完后,提出 2-3 个技术方案 + 推荐理由,等用户选定后继续。
|
|
52
|
+
|
|
53
|
+
### Phase 1.5:UI v0 草稿(仅涉及新页面/新组件时触发)
|
|
54
|
+
|
|
55
|
+
确认本次变更涉及 UI 视觉层后,输出 v0 草稿供确认:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
📌 UI v0 草稿(30 秒扫完,方向对了就继续):
|
|
59
|
+
- 调性:<一个词>
|
|
60
|
+
- 主色:<颜色描述>
|
|
61
|
+
- 字体:display = <字体> / body = <字体>
|
|
62
|
+
- 缩略布局:
|
|
63
|
+
┌─────────────────┐
|
|
64
|
+
│ [示意] │
|
|
65
|
+
└─────────────────┘
|
|
66
|
+
|
|
67
|
+
我正在假设:
|
|
68
|
+
- 假设 <A>
|
|
69
|
+
- 假设 <B>
|
|
70
|
+
|
|
71
|
+
继续还是调整哪个维度?
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
用户确认后才继续。局部调整只改对应维度,不重写全部。
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Phase 1→2 过渡:文档输出提醒
|
|
79
|
+
|
|
80
|
+
> **💡 省钱提示**:接下来将生成 proposal.md + design.md + specs/ + tasks.md,预计输出 5-15K token。
|
|
81
|
+
> 如果当前使用昂贵模型,建议切到便宜模型(如 glm-4-flash)后再继续。
|
|
82
|
+
> 完成后切回即可。
|
|
83
|
+
>
|
|
84
|
+
> **模式 C(可选)**:如果你决定切模型,现在暂停,用 `/model <便宜模型>` 切换后回复"继续"。
|
|
85
|
+
> 不切模型则直接进入 Phase 2。
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Phase 2:生成 change 目录
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# 根据需求生成 change 名称(kebab-case,英文,描述功能)
|
|
93
|
+
CHANGE_NAME="<name>"
|
|
94
|
+
mkdir -p openspec/changes/$CHANGE_NAME/specs
|
|
95
|
+
|
|
96
|
+
# 🔴 初始化状态文件(触发 evidence PostToolUse hook)
|
|
97
|
+
cat > openspec/changes/$CHANGE_NAME/.phase-state.json << 'STATE'
|
|
98
|
+
{
|
|
99
|
+
"change": "<name>",
|
|
100
|
+
"phase": "open",
|
|
101
|
+
"mode": "full",
|
|
102
|
+
"tasks": { "total": 0, "completed": 0 },
|
|
103
|
+
"verifiedAt": null,
|
|
104
|
+
"archived": false
|
|
105
|
+
}
|
|
106
|
+
STATE
|
|
107
|
+
# PostToolUse hook 检测到写入后自动记录 phase_transition(init→open) 事件
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
生成以下文件:
|
|
111
|
+
|
|
112
|
+
### 2a. proposal.md
|
|
113
|
+
|
|
114
|
+
参考 `@atdc/templates/proposal.md`,填充:
|
|
115
|
+
- **背景与目标**:一句话(对谁/做什么/达到什么效果)
|
|
116
|
+
- **技术方案**:选定方案 + 核心理由
|
|
117
|
+
- **排除的方案**:被否定的选项及原因
|
|
118
|
+
- **实施范围**:涉及模块、文件列表、接口变更
|
|
119
|
+
|
|
120
|
+
### 2b. design.md
|
|
121
|
+
|
|
122
|
+
参考 `@atdc/templates/design.md`,填充:
|
|
123
|
+
- 模块划分与职责
|
|
124
|
+
- 关键接口定义(函数签名/API 路由)
|
|
125
|
+
- 数据流说明
|
|
126
|
+
- 关键决策与 ADR
|
|
127
|
+
|
|
128
|
+
### 2c. specs/<模块名>/spec.md
|
|
129
|
+
|
|
130
|
+
每个涉及模块一个 spec 文件(放在 `openspec/changes/<name>/specs/<模块名>/spec.md`),包含:
|
|
131
|
+
- 每条需求使用 `SHALL` 或 `MUST`(禁止用 should/might/可能)
|
|
132
|
+
- 每个场景有 Given/When/Then 或可量化预期结果
|
|
133
|
+
- 覆盖 happy path + sad path
|
|
134
|
+
- 覆盖边界场景(空值/超限/并发/降级)
|
|
135
|
+
|
|
136
|
+
### 2d. tasks.md
|
|
137
|
+
|
|
138
|
+
参考 `@atdc/templates/tasks.md`,每个任务格式:
|
|
139
|
+
|
|
140
|
+
```markdown
|
|
141
|
+
### N. <任务标题>
|
|
142
|
+
|
|
143
|
+
**目标**: <一句话说明完成后达到什么状态>
|
|
144
|
+
**Files**: [path/to/file1.go, path/to/file2.go]
|
|
145
|
+
**Dependencies**: <依赖的任务编号,或 none>
|
|
146
|
+
**估时**: <8-15 分钟>
|
|
147
|
+
|
|
148
|
+
实现步骤:
|
|
149
|
+
1. ...
|
|
150
|
+
2. ...
|
|
151
|
+
|
|
152
|
+
验证方式:
|
|
153
|
+
- [ ] <具体可执行的验证命令或检查项>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
任务颗粒度:每个任务 8-15 分钟可完成,可并行的任务标注 `Dependencies: none`。
|
|
157
|
+
|
|
158
|
+
### 2e. Verification Checklist
|
|
159
|
+
|
|
160
|
+
在 tasks.md 末尾生成:
|
|
161
|
+
|
|
162
|
+
```markdown
|
|
163
|
+
## Verification Checklist
|
|
164
|
+
|
|
165
|
+
从 `openspec/changes/<name>/specs/` 提取所有 SHALL/MUST,逐条列出:
|
|
166
|
+
- [ ] 系统 SHALL <需求描述> → Task N
|
|
167
|
+
- [ ] 系统 MUST <需求描述> → Task N
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Phase 3:复杂度评估 + C1 暂停
|
|
173
|
+
|
|
174
|
+
### 3a. 自动评估复杂度
|
|
175
|
+
|
|
176
|
+
tasks.md 生成完成后,统计以下维度:
|
|
177
|
+
|
|
178
|
+
| 维度 | 简单 | 复杂 |
|
|
179
|
+
|------|------|------|
|
|
180
|
+
| 任务数 | ≤ 4 | > 4 |
|
|
181
|
+
| 涉及模块数 | 1 | > 1 |
|
|
182
|
+
| 跨层改动 | 无(纯逻辑/工具函数) | 有(接口变更、DB、缓存、路由) |
|
|
183
|
+
| 任务间依赖 | Dependencies: none 居多 | 有依赖链(Task N → Task M) |
|
|
184
|
+
| 破坏性变更 | 无 | 有(改公共函数签名、删接口) |
|
|
185
|
+
|
|
186
|
+
### 3a-α. 预估 Blast Radius(可选但有条件触发)
|
|
187
|
+
|
|
188
|
+
当 Phase 3a 命中 ≥2 条复杂维度时,**自动执行预估影响分析**:
|
|
189
|
+
|
|
190
|
+
对 design.md 中"涉及文件"声明的关键符号,运行:
|
|
191
|
+
```
|
|
192
|
+
codegraph_impact "<SymbolName>" depth=1
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
将影响深度纳入复杂度评估:
|
|
196
|
+
|
|
197
|
+
| 预估影响 | 额外复杂度加分 |
|
|
198
|
+
|---------|-------------|
|
|
199
|
+
| blast radius ≤3 个调用方,单模块 | +0 |
|
|
200
|
+
| blast radius 4-10 个,跨 2 个模块 | +1 维度 |
|
|
201
|
+
| blast radius >10 个,跨 ≥3 个模块 | +2 维度 |
|
|
202
|
+
|
|
203
|
+
参考 `@atdc/reference/risk.md` 的快速评估规则。
|
|
204
|
+
|
|
205
|
+
**命中 2 条及以上 → 复杂,强制 grill**:直接读取 `@atdc/prompts/2-qa.md` 执行,grill 完成并用户确认后再暂停等待 C1。
|
|
206
|
+
|
|
207
|
+
**命中 0-1 条 → 简单**:直接暂停等待 C1,提示用户可选 grill。
|
|
208
|
+
|
|
209
|
+
### 3b. 输出 C1 暂停信息
|
|
210
|
+
|
|
211
|
+
**复杂(强制 grill 后):**
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
📋 规格生成完成,已完成 Grill 质疑检验,等待 C1 审核:
|
|
215
|
+
|
|
216
|
+
变更目录:openspec/changes/<name>/
|
|
217
|
+
├── proposal.md ✓
|
|
218
|
+
├── design.md ✓
|
|
219
|
+
├── tasks.md ✓(N 个任务)
|
|
220
|
+
└── specs/ ✓(N 个模块)
|
|
221
|
+
|
|
222
|
+
复杂度评估:命中 <N> 条复杂维度,已强制执行 Grill。
|
|
223
|
+
|
|
224
|
+
审核重点:
|
|
225
|
+
1. 需求完整、无歧义(openspec/changes/<name>/specs/ 中无 should/might)
|
|
226
|
+
2. 技术方案可行(design.md 接口定义合理)
|
|
227
|
+
3. 任务粒度合适(每个 8-15 分钟)
|
|
228
|
+
4. Verification Checklist 覆盖所有 SHALL/MUST
|
|
229
|
+
5. Grill 报告中的严重问题已处理
|
|
230
|
+
|
|
231
|
+
审核通过后说"执行"或"run",AI 必须读取 `atdc/prompts/3-impl.md` 进入执行阶段,禁止直接写代码。
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
**简单(可选 grill):**
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
📋 规格生成完成,等待 C1 审核:
|
|
238
|
+
|
|
239
|
+
变更目录:openspec/changes/<name>/
|
|
240
|
+
├── proposal.md ✓
|
|
241
|
+
├── design.md ✓
|
|
242
|
+
├── tasks.md ✓(N 个任务)
|
|
243
|
+
└── specs/ ✓(N 个模块)
|
|
244
|
+
|
|
245
|
+
审核重点:
|
|
246
|
+
1. 需求完整、无歧义(openspec/changes/<name>/specs/ 中无 should/might)
|
|
247
|
+
2. 技术方案可行(design.md 接口定义合理)
|
|
248
|
+
3. 任务粒度合适(每个 8-15 分钟)
|
|
249
|
+
4. Verification Checklist 覆盖所有 SHALL/MUST
|
|
250
|
+
|
|
251
|
+
审核通过后说"执行"或"run",AI 必须读取 `atdc/prompts/3-impl.md` 进入执行阶段,禁止直接写代码。
|
|
252
|
+
如需在执行前做一轮质疑检验,说"grill"。
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Phase 3(可选):深度拷问 + 文档沉淀
|
|
258
|
+
|
|
259
|
+
提案生成后,可用 `/atdc:grill-with-docs` 进行更深层的拷问,
|
|
260
|
+
与 2-qa 的区别:2-qa 重在找漏洞,grill-with-docs 重在建立共享语言并持久化。
|
|
261
|
+
|
|
262
|
+
> 💡 是否需要对方案进行深度拷问?
|
|
263
|
+
> - 说 `grill-docs` → 执行 `/atdc:grill-with-docs`:
|
|
264
|
+
> 1. 对照 CONTEXT.md 和现有 ADR 挑战方案假设
|
|
265
|
+
> 2. 更新 CONTEXT.md(领域术语、模块边界)
|
|
266
|
+
> 3. 重大决策生成 ADR(`docs/adr/<序号>-<标题>.md`)
|
|
267
|
+
> - 说 `继续` 或直接描述下一步 → 跳过,进入 C1 审核
|
|
268
|
+
|
|
269
|
+
> **触发条件**:仅当 CONTEXT.md 已存在时显示此提示,否则跳过 Phase 3。
|
|
270
|
+
|
|
271
|
+
**不得在用户确认前开始写任何代码。**
|