@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.
Files changed (222) hide show
  1. package/AGENTS.md +58 -0
  2. package/GO.md +245 -0
  3. package/bin/add-command.py +144 -0
  4. package/bin/atdc.js +460 -0
  5. package/bin/cls.js +98 -0
  6. package/central/link.sh +60 -0
  7. package/central/ticket_center_server/.handoffs/TASK-238389_QYinVipLimit.md +128 -0
  8. package/central/ticket_center_server/.test_plans/TASK-227254-SupportMembership-self-test-plan.md +566 -0
  9. package/central/ticket_center_server/.test_plans/task-238389-qyin-vip-limit-self-test-plan.md +210 -0
  10. package/commands/claude/atd.md +3 -0
  11. package/commands/claude/atdc/1-spec.md +5 -0
  12. package/commands/claude/atdc/1-update.md +5 -0
  13. package/commands/claude/atdc/2-qa.md +3 -0
  14. package/commands/claude/atdc/3-impl-skip-spec.md +5 -0
  15. package/commands/claude/atdc/3-impl.md +3 -0
  16. package/commands/claude/atdc/4-verify.md +3 -0
  17. package/commands/claude/atdc/5-done.md +3 -0
  18. package/commands/claude/atdc/add-command.md +4 -0
  19. package/commands/claude/atdc/add-to-hc.md +3 -0
  20. package/commands/claude/atdc/analysis-cls-log.md +4 -0
  21. package/commands/claude/atdc/article.md +6 -0
  22. package/commands/claude/atdc/bugfix.md +4 -0
  23. package/commands/claude/atdc/code-simplifier.md +8 -0
  24. package/commands/claude/atdc/commit.md +4 -0
  25. package/commands/claude/atdc/compound.md +5 -0
  26. package/commands/claude/atdc/context.md +3 -0
  27. package/commands/claude/atdc/diagnose.md +8 -0
  28. package/commands/claude/atdc/git-guardrails.md +18 -0
  29. package/commands/claude/atdc/grill-with-docs.md +8 -0
  30. package/commands/claude/atdc/handoff.md +8 -0
  31. package/commands/claude/atdc/health.md +3 -0
  32. package/commands/claude/atdc/learn.md +4 -0
  33. package/commands/claude/atdc/review.md +10 -0
  34. package/commands/claude/atdc/search.md +4 -0
  35. package/commands/claude/atdc/strategy.md +3 -0
  36. package/commands/claude/atdc/table2xlsx.md +4 -0
  37. package/commands/claude/atdc/test-plan.md +5 -0
  38. package/commands/claude/atdc/travel-planner.md +4 -0
  39. package/commands/claude/atdc/xlsx2sql-insert.md +4 -0
  40. package/commands/claude/atdc/xlsx2sql-update.md +4 -0
  41. package/commands/claude/atdc/zoom-out.md +8 -0
  42. package/commands/codex/atdc/SKILL.md +8 -0
  43. package/commands/codex/atdc-1-spec/SKILL.md +8 -0
  44. package/commands/codex/atdc-1-update/SKILL.md +8 -0
  45. package/commands/codex/atdc-2-qa/SKILL.md +8 -0
  46. package/commands/codex/atdc-3-impl/SKILL.md +8 -0
  47. package/commands/codex/atdc-3-impl-skip-spec/SKILL.md +8 -0
  48. package/commands/codex/atdc-4-verify/SKILL.md +8 -0
  49. package/commands/codex/atdc-5-done/SKILL.md +8 -0
  50. package/commands/codex/atdc-add-command/SKILL.md +7 -0
  51. package/commands/codex/atdc-add-to-hc/SKILL.md +8 -0
  52. package/commands/codex/atdc-analysis-cls-log/SKILL.md +5 -0
  53. package/commands/codex/atdc-article/SKILL.md +8 -0
  54. package/commands/codex/atdc-bugfix/SKILL.md +5 -0
  55. package/commands/codex/atdc-code-simplifier/SKILL.md +7 -0
  56. package/commands/codex/atdc-commit/SKILL.md +7 -0
  57. package/commands/codex/atdc-compound/SKILL.md +8 -0
  58. package/commands/codex/atdc-context/SKILL.md +8 -0
  59. package/commands/codex/atdc-diagnose/SKILL.md +9 -0
  60. package/commands/codex/atdc-git-guardrails/SKILL.md +19 -0
  61. package/commands/codex/atdc-grill-with-docs/SKILL.md +9 -0
  62. package/commands/codex/atdc-handoff/SKILL.md +9 -0
  63. package/commands/codex/atdc-health/SKILL.md +8 -0
  64. package/commands/codex/atdc-learn/SKILL.md +7 -0
  65. package/commands/codex/atdc-review/SKILL.md +49 -0
  66. package/commands/codex/atdc-search/SKILL.md +5 -0
  67. package/commands/codex/atdc-strategy/SKILL.md +8 -0
  68. package/commands/codex/atdc-table2xlsx/SKILL.md +8 -0
  69. package/commands/codex/atdc-test-plan/SKILL.md +13 -0
  70. package/commands/codex/atdc-travel-planner/SKILL.md +7 -0
  71. package/commands/codex/atdc-xlsx2sql-insert/SKILL.md +8 -0
  72. package/commands/codex/atdc-xlsx2sql-update/SKILL.md +8 -0
  73. package/commands/codex/atdc-zoom-out/SKILL.md +9 -0
  74. package/commands/cursor/atdc/SKILL.md +8 -0
  75. package/commands/cursor/atdc-1-spec/SKILL.md +8 -0
  76. package/commands/cursor/atdc-1-update/SKILL.md +8 -0
  77. package/commands/cursor/atdc-2-qa/SKILL.md +8 -0
  78. package/commands/cursor/atdc-3-impl/SKILL.md +8 -0
  79. package/commands/cursor/atdc-3-impl-skip-spec/SKILL.md +8 -0
  80. package/commands/cursor/atdc-4-verify/SKILL.md +8 -0
  81. package/commands/cursor/atdc-5-done/SKILL.md +8 -0
  82. package/commands/cursor/atdc-add-command/SKILL.md +7 -0
  83. package/commands/cursor/atdc-add-to-hc/SKILL.md +8 -0
  84. package/commands/cursor/atdc-analysis-cls-log/SKILL.md +5 -0
  85. package/commands/cursor/atdc-article/SKILL.md +8 -0
  86. package/commands/cursor/atdc-bugfix/SKILL.md +5 -0
  87. package/commands/cursor/atdc-code-simplifier/SKILL.md +7 -0
  88. package/commands/cursor/atdc-commit/SKILL.md +7 -0
  89. package/commands/cursor/atdc-compound/SKILL.md +8 -0
  90. package/commands/cursor/atdc-context/SKILL.md +8 -0
  91. package/commands/cursor/atdc-diagnose/SKILL.md +8 -0
  92. package/commands/cursor/atdc-git-guardrails/SKILL.md +18 -0
  93. package/commands/cursor/atdc-grill-with-docs/SKILL.md +8 -0
  94. package/commands/cursor/atdc-handoff/SKILL.md +8 -0
  95. package/commands/cursor/atdc-health/SKILL.md +8 -0
  96. package/commands/cursor/atdc-learn/SKILL.md +7 -0
  97. package/commands/cursor/atdc-review/SKILL.md +49 -0
  98. package/commands/cursor/atdc-search/SKILL.md +5 -0
  99. package/commands/cursor/atdc-strategy/SKILL.md +8 -0
  100. package/commands/cursor/atdc-table2xlsx/SKILL.md +8 -0
  101. package/commands/cursor/atdc-test-plan/SKILL.md +7 -0
  102. package/commands/cursor/atdc-travel-planner/SKILL.md +7 -0
  103. package/commands/cursor/atdc-xlsx2sql-insert/SKILL.md +8 -0
  104. package/commands/cursor/atdc-xlsx2sql-update/SKILL.md +8 -0
  105. package/commands/cursor/atdc-zoom-out/SKILL.md +8 -0
  106. package/commands/opencode/atd.md +6 -0
  107. package/commands/opencode/atdc/1-spec.md +8 -0
  108. package/commands/opencode/atdc/1-update.md +8 -0
  109. package/commands/opencode/atdc/2-qa.md +6 -0
  110. package/commands/opencode/atdc/3-impl-skip-spec.md +8 -0
  111. package/commands/opencode/atdc/3-impl.md +6 -0
  112. package/commands/opencode/atdc/4-verify.md +6 -0
  113. package/commands/opencode/atdc/5-done.md +6 -0
  114. package/commands/opencode/atdc/add-command.md +6 -0
  115. package/commands/opencode/atdc/add-to-hc.md +6 -0
  116. package/commands/opencode/atdc/analysis-cls-log.md +6 -0
  117. package/commands/opencode/atdc/article.md +6 -0
  118. package/commands/opencode/atdc/bugfix.md +6 -0
  119. package/commands/opencode/atdc/code-simplifier.md +8 -0
  120. package/commands/opencode/atdc/commit.md +6 -0
  121. package/commands/opencode/atdc/compound.md +8 -0
  122. package/commands/opencode/atdc/context.md +6 -0
  123. package/commands/opencode/atdc/diagnose.md +8 -0
  124. package/commands/opencode/atdc/git-guardrails.md +18 -0
  125. package/commands/opencode/atdc/grill-with-docs.md +8 -0
  126. package/commands/opencode/atdc/handoff.md +8 -0
  127. package/commands/opencode/atdc/health.md +6 -0
  128. package/commands/opencode/atdc/learn.md +6 -0
  129. package/commands/opencode/atdc/review.md +7 -0
  130. package/commands/opencode/atdc/search.md +6 -0
  131. package/commands/opencode/atdc/strategy.md +6 -0
  132. package/commands/opencode/atdc/table2xlsx.md +6 -0
  133. package/commands/opencode/atdc/test-plan.md +6 -0
  134. package/commands/opencode/atdc/travel-planner.md +6 -0
  135. package/commands/opencode/atdc/xlsx2sql-insert.md +6 -0
  136. package/commands/opencode/atdc/xlsx2sql-update.md +6 -0
  137. package/commands/opencode/atdc/zoom-out.md +8 -0
  138. package/config/cls-defaults.json +19 -0
  139. package/hooks/atd-archive.sh +86 -0
  140. package/hooks/atd-state.sh +165 -0
  141. package/hooks/evidence-hook.sh +88 -0
  142. package/hooks/evidence-log.sh +54 -0
  143. package/hooks/guardrails.sh +65 -0
  144. package/hooks/handoff-compress.sh +104 -0
  145. package/hooks/phase-guard.sh +134 -0
  146. package/hooks/phase-write-guard.sh +96 -0
  147. package/hooks/ponytail-fix.sh +179 -0
  148. package/hooks/ponytail-guard.sh +66 -0
  149. package/hooks/ponytail-scan.sh +62 -0
  150. package/install.sh +276 -0
  151. package/package.json +20 -0
  152. package/packages/cls/bin/cls.js +146 -0
  153. package/packages/cls/config/defaults.json +17 -0
  154. package/packages/cls/mcp/server.js +142 -0
  155. package/packages/cls/package.json +29 -0
  156. package/packages/cls/scripts/postinstall.js +19 -0
  157. package/packages/cls/src/client.js +262 -0
  158. package/prompts/1-spec.md +271 -0
  159. package/prompts/1-update.md +198 -0
  160. package/prompts/2-qa.md +164 -0
  161. package/prompts/3-impl-bugfix.md +271 -0
  162. package/prompts/3-impl.md +419 -0
  163. package/prompts/4-verify.md +160 -0
  164. package/prompts/5-done.md +227 -0
  165. package/prompts/6-article.md +571 -0
  166. package/prompts/X-add-command.md +60 -0
  167. package/prompts/X-add-to-hc.md +178 -0
  168. package/prompts/X-analysis-cls-log.md +68 -0
  169. package/prompts/X-bugfix.md +10 -0
  170. package/prompts/X-code-simplifier.md +38 -0
  171. package/prompts/X-commit.md +116 -0
  172. package/prompts/X-compound.md +188 -0
  173. package/prompts/X-context-scan.md +101 -0
  174. package/prompts/X-diagnose.md +116 -0
  175. package/prompts/X-grill-with-docs.md +120 -0
  176. package/prompts/X-handoff.md +80 -0
  177. package/prompts/X-health.md +78 -0
  178. package/prompts/X-learn.md +151 -0
  179. package/prompts/X-review.md +417 -0
  180. package/prompts/X-search.md +74 -0
  181. package/prompts/X-strategy.md +95 -0
  182. package/prompts/X-table2xlsx.md +46 -0
  183. package/prompts/X-test-plan.md +436 -0
  184. package/prompts/X-travel-planner.md +22 -0
  185. package/prompts/X-xlsx2sql-insert.md +34 -0
  186. package/prompts/X-xlsx2sql-update.md +40 -0
  187. package/prompts/X-zoom-out.md +119 -0
  188. package/reference/diagnose-loop.md +66 -0
  189. package/reference/gates.md +90 -0
  190. package/reference/git-guardrails.md +71 -0
  191. package/reference/guards.md +275 -0
  192. package/reference/learn.md +100 -0
  193. package/reference/memory.md +190 -0
  194. package/reference/risk.md +74 -0
  195. package/reference/tdd.md +40 -0
  196. package/scripts/svg2png.js +46 -0
  197. package/src/cls/client.js +244 -0
  198. package/src/constants.js +49 -0
  199. package/src/evidence/ai-draft-metrics.js +157 -0
  200. package/src/evidence/canvas.js +184 -0
  201. package/src/evidence/index.js +98 -0
  202. package/src/evidence/sink-events.js +165 -0
  203. package/src/evidence/stage-timing.js +112 -0
  204. package/src/installers/claude.js +79 -0
  205. package/src/installers/codex.js +62 -0
  206. package/src/installers/opencode.js +72 -0
  207. package/src/manifest/ponytail-patterns.json +185 -0
  208. package/src/mcp/cls-mcp-server.js +142 -0
  209. package/src/utils.js +79 -0
  210. package/src/validators/state-validate.js +123 -0
  211. package/templates/.phase-state.json +13 -0
  212. package/templates/adr.md +62 -0
  213. package/templates/changelog.md +21 -0
  214. package/templates/concepts.md +35 -0
  215. package/templates/design.md +47 -0
  216. package/templates/handoff.md +64 -0
  217. package/templates/learn-skill.md +43 -0
  218. package/templates/progress.json +17 -0
  219. package/templates/proposal.md +29 -0
  220. package/templates/solution.md +36 -0
  221. package/templates/strategy.md +39 -0
  222. package/templates/tasks.md +41 -0
package/bin/atdc.js ADDED
@@ -0,0 +1,460 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const { HOME, MEMORY_DIR, PACKAGE_DIR, TERMINAL_CONFIGS } = require('../src/constants');
7
+ const { exists, mkdirp, resolvePackageDir, replaceAllInDir } = require('../src/utils');
8
+ const evidence = require('../src/evidence');
9
+
10
+ const installers = {
11
+ claude: require('../src/installers/claude'),
12
+ opencode: require('../src/installers/opencode'),
13
+ codex: require('../src/installers/codex'),
14
+ };
15
+
16
+ const ALL_TARGETS = ['claude', 'opencode', 'codex'];
17
+
18
+ function usage() {
19
+ console.log(`atdc — AI coding workflow orchestration kit
20
+
21
+ Usage:
22
+ atdc install [--claude|--opencode|--codex|--all] Register to AI terminals
23
+ atdc uninstall [--all] Remove registrations
24
+ atdc status Show installation status
25
+ atdc guardrails Install git pre-push hook
26
+ atdc guard <phase> Validate phase transition preconditions
27
+ atdc validate Validate .phase-state.json schema
28
+ atdc handoff <change> Generate compressed context package
29
+ atdc run [--hotfix|--tweak|--bugfix] Quick-run with preset workflow
30
+ atdc archive <change> [--dry-run] Archive a completed change
31
+ atdc hook install Install phase write guard to OpenCode
32
+ atdc ponytail scan [--diff] Scan project for over-engineering patterns
33
+ atdc ponytail guard [--strict|--warn] Enforce ponytail rules before build
34
+ atdc ponytail fix [--dry-run] --pattern=<id> Apply generic refactor (crud|rpc|di)
35
+ atdc evidence events <change> [--type=<t>] [--limit=<n>] List evidence events
36
+ atdc evidence stats <change> Event statistics
37
+ atdc evidence timing <change> Stage timing report
38
+ atdc evidence metrics <change> [--snapshot|--commit] AI draft metrics
39
+ atdc evidence canvas <change> [--out=<path>] Generate evidence dashboard
40
+ atdc evidence record <change> <type> Record an evidence event
41
+ atdc evidence all <change> Full evidence summary
42
+
43
+ Options:
44
+ --claude Claude Code only
45
+ --opencode OpenCode only
46
+ --codex Codex only
47
+ --all All detected terminals (default for install)
48
+ `);
49
+ }
50
+
51
+ function parseTargets(args) {
52
+ const targets = new Set();
53
+ for (const a of args) {
54
+ if (a === '--all') { ALL_TARGETS.forEach(t => targets.add(t)); }
55
+ else if (a === '--claude') targets.add('claude');
56
+ else if (a === '--opencode') targets.add('opencode');
57
+ else if (a === '--codex') targets.add('codex');
58
+ }
59
+ if (targets.size === 0) targets.add('all');
60
+ return targets;
61
+ }
62
+
63
+ function runForTargets(targets, fnName) {
64
+ const hasAll = targets.has('all');
65
+ const results = [];
66
+
67
+ for (const t of ALL_TARGETS) {
68
+ if (!hasAll && !targets.has(t)) continue;
69
+ const cfg = TERMINAL_CONFIGS[t];
70
+ console.log(`\n${cfg.name}:`);
71
+ const r = installers[t][fnName]();
72
+ if (!r.ok) {
73
+ console.log(` ⚠ ${r.error}`);
74
+ results.push({ target: t, ok: false, error: r.error });
75
+ } else if (r.results) {
76
+ r.results.forEach(line => console.log(line));
77
+ results.push({ target: t, ok: true });
78
+ } else if (r.details) {
79
+ for (const [k, v] of Object.entries(r.details)) {
80
+ const icon = v === 'ok' ? '✓' : v === 'conflict' ? '⚠' : '✗';
81
+ console.log(` ${icon} ${k}: ${v}`);
82
+ }
83
+ results.push({ target: t, ok: true });
84
+ }
85
+ }
86
+
87
+ return results;
88
+ }
89
+
90
+ function cmdInstall(args) {
91
+ const targets = parseTargets(args);
92
+ const pkgDir = resolvePackageDir();
93
+
94
+ replaceAllInDir(path.join(pkgDir, 'commands'), '{{ATDC_DIR}}', pkgDir);
95
+
96
+ mkdirp(MEMORY_DIR);
97
+
98
+ console.log(`atdc dir : ${pkgDir}`);
99
+ console.log(`memory dir : ${MEMORY_DIR}`);
100
+
101
+ runForTargets(targets, 'install');
102
+
103
+ console.log(`\nDone. Triggers:`);
104
+ console.log(` Claude Code /atdc:1-spec <需求>`);
105
+ console.log(` OpenCode /atdc:1-spec <需求>`);
106
+ console.log(` Codex atd:1-spec <需求>`);
107
+ console.log('');
108
+ }
109
+
110
+ function cmdUninstall(args) {
111
+ const targets = parseTargets(args);
112
+ const pkgDir = resolvePackageDir();
113
+
114
+ runForTargets(targets, 'uninstall');
115
+
116
+ replaceAllInDir(path.join(pkgDir, 'commands'), pkgDir, '{{ATDC_DIR}}');
117
+
118
+ console.log('\nUninstall complete. Run `npm uninstall -g atdc` to remove package files.');
119
+ }
120
+
121
+ function cmdStatus() {
122
+ console.log(`atdc dir : ${resolvePackageDir()}`);
123
+
124
+ for (const t of ALL_TARGETS) {
125
+ const cfg = TERMINAL_CONFIGS[t];
126
+ console.log(`\n${cfg.name}:`);
127
+ const r = installers[t].status();
128
+ if (!r.installed) {
129
+ console.log(` not detected`);
130
+ continue;
131
+ }
132
+ for (const [k, v] of Object.entries(r.details)) {
133
+ const icon = v === 'ok' ? '✓' : v === 'conflict' ? '⚠' : '✗';
134
+ console.log(` ${icon} ${k}: ${v}`);
135
+ }
136
+ }
137
+ console.log('');
138
+ }
139
+
140
+ function cmdGuardrails() {
141
+ const pkgDir = resolvePackageDir();
142
+ const hookSrc = path.join(pkgDir, 'hooks', 'guardrails.sh');
143
+
144
+ let gitDir;
145
+ try {
146
+ const { execSync } = require('child_process');
147
+ gitDir = execSync('git rev-parse --git-dir', { encoding: 'utf8' }).trim();
148
+ } catch {
149
+ console.log('Not a git repository. Run inside a git project.');
150
+ process.exit(1);
151
+ }
152
+
153
+ const hookPath = path.resolve(gitDir, 'hooks', 'pre-push');
154
+
155
+ if (exists(hookPath)) {
156
+ const { isSymlink, getSymlinkTarget } = require('../src/utils');
157
+ if (isSymlink(hookPath) && getSymlinkTarget(hookPath) === hookSrc) {
158
+ console.log('✓ git-guardrails already installed');
159
+ return;
160
+ }
161
+ console.log(`⚠ Existing pre-push hook found. Manual merge needed.`);
162
+ console.log(` Reference: ${hookSrc}`);
163
+ process.exit(1);
164
+ }
165
+
166
+ fs.symlinkSync(hookSrc, hookPath);
167
+ console.log(`✓ git-guardrails installed → .git/hooks/pre-push`);
168
+ }
169
+
170
+ function runScript(script, ...args) {
171
+ const { execSync } = require('child_process');
172
+ const pkgDir = resolvePackageDir();
173
+ const scriptPath = path.join(pkgDir, 'hooks', script);
174
+ if (!exists(scriptPath)) {
175
+ console.log(`Script not found: ${scriptPath}`);
176
+ process.exit(1);
177
+ }
178
+ try {
179
+ const result = execSync(`sh "${scriptPath}" ${args.join(' ')}`, { encoding: 'utf8' });
180
+ console.log(result.trim());
181
+ } catch (e) {
182
+ console.error(e.stderr || e.stdout || e.message);
183
+ process.exit(e.status || 1);
184
+ }
185
+ }
186
+
187
+ function findActiveChange() {
188
+ const changesDir = 'openspec/changes';
189
+ if (!exists(changesDir)) return null;
190
+ const entries = fs.readdirSync(changesDir).filter(e => {
191
+ if (e === 'archive') return false;
192
+ const stat = fs.statSync(path.join(changesDir, e));
193
+ return stat.isDirectory();
194
+ });
195
+ // prefer changes with .phase-state.json
196
+ const phaseState = entries.filter(e => exists(path.join(changesDir, e, '.phase-state.json')));
197
+ if (phaseState.length === 1) return phaseState[0];
198
+ if (phaseState.length > 1) return phaseState[0];
199
+ return entries.length === 1 ? entries[0] : (entries.length > 0 ? entries[0] : null);
200
+ }
201
+
202
+ function cmdGuard(args) {
203
+ const phase = args[0];
204
+ if (!phase) { console.log('Usage: atdc guard <phase>'); process.exit(1); }
205
+ const change = findActiveChange();
206
+ if (!change) { console.log('No active change found. Run atdc run first.'); process.exit(1); }
207
+ runScript('phase-guard.sh', change, phase);
208
+ }
209
+
210
+ function cmdValidate(args) {
211
+ const { execSync } = require('child_process');
212
+ const pkgDir = resolvePackageDir();
213
+ const validator = path.join(pkgDir, 'src', 'validators', 'state-validate.js');
214
+ if (!exists(validator)) { console.log('Validator not found. Install atdc first.'); process.exit(1); }
215
+ try {
216
+ const result = execSync(`node "${validator}"`, { encoding: 'utf8' });
217
+ console.log(result.trim());
218
+ } catch (e) {
219
+ console.error(e.stderr || e.stdout || e.message);
220
+ process.exit(e.status || 1);
221
+ }
222
+ }
223
+
224
+ function cmdHandoff(args) {
225
+ const change = args[0] || findActiveChange();
226
+ if (!change) { console.log('Usage: atdc handoff <change-name>'); process.exit(1); }
227
+ runScript('handoff-compress.sh', change);
228
+ }
229
+
230
+ function cmdRun(args) {
231
+ let mode = 'full';
232
+ if (args.includes('--bugfix')) mode = 'bugfix';
233
+ else if (args.includes('--hotfix')) mode = 'hotfix';
234
+ else if (args.includes('--tweak')) mode = 'tweak';
235
+
236
+ const { execSync } = require('child_process');
237
+ const ts = new Date().toISOString().slice(0, 10).replace(/-/g, '');
238
+ const changeName = `${mode}-${ts}`;
239
+ const changeDir = `openspec/changes/${changeName}`;
240
+
241
+ mkdirp(changeDir);
242
+ mkdirp(`${changeDir}/specs`);
243
+
244
+ if (mode === 'bugfix') {
245
+ console.log('🐛 Bugfix mode: feedback loop → root cause → fix → post-mortem');
246
+ runScript('atd-state.sh', 'init', changeName, 'bugfix');
247
+ runScript('atd-state.sh', 'set', changeName, 'phase', '"build"');
248
+ } else if (mode === 'hotfix') {
249
+ console.log('⚡ Hotfix mode: skipping brainstorm + design');
250
+ runScript('atd-state.sh', 'init', changeName, 'hotfix');
251
+ runScript('atd-state.sh', 'set', changeName, 'phase', '"build"');
252
+ } else if (mode === 'tweak') {
253
+ console.log('🔧 Tweak mode: skipping brainstorm + full plan');
254
+ runScript('atd-state.sh', 'init', changeName, 'tweak');
255
+ runScript('atd-state.sh', 'set', changeName, 'phase', '"build"');
256
+ } else {
257
+ runScript('atd-state.sh', 'init', changeName, 'full');
258
+ }
259
+
260
+ console.log(`Change created: ${changeName} (mode: ${mode})`);
261
+ }
262
+
263
+ function cmdArchive(args) {
264
+ const dryRun = args.includes('--dry-run');
265
+ const change = args.filter(a => a !== '--dry-run')[0] || findActiveChange();
266
+ if (!change) { console.log('Usage: atdc archive <change-name> [--dry-run]'); process.exit(1); }
267
+ const extra = dryRun ? '--dry-run' : '';
268
+ runScript('atd-archive.sh', extra, change);
269
+ }
270
+
271
+ function cmdHook(args) {
272
+ const sub = args[0];
273
+ if (sub !== 'install') { console.log('Usage: atdc hook install'); process.exit(1); }
274
+ const { execSync } = require('child_process');
275
+ const pkgDir = resolvePackageDir();
276
+ const guardPath = path.join(pkgDir, 'hooks', 'phase-write-guard.sh');
277
+ const evidencePath = path.join(pkgDir, 'hooks', 'evidence-hook.sh');
278
+
279
+ // Check OpenCode config paths
280
+ const configPaths = [
281
+ path.join(process.cwd(), '.opencode', 'config.json'),
282
+ path.join(process.cwd(), '.opencode', 'opencode.json'),
283
+ path.join(HOME, '.config', 'opencode', 'opencode.json'),
284
+ ];
285
+
286
+ const configFile = configPaths.find(p => exists(p));
287
+ if (!configFile) {
288
+ console.log('No OpenCode config found. Skipping hook install.');
289
+ console.log('Supported paths: .opencode/config.json, .opencode/opencode.json, ~/.config/opencode/opencode.json');
290
+ process.exit(1);
291
+ }
292
+
293
+ let config;
294
+ try {
295
+ config = JSON.parse(fs.readFileSync(configFile, 'utf8'));
296
+ } catch {
297
+ console.log('Failed to parse OpenCode config file.');
298
+ process.exit(1);
299
+ }
300
+
301
+ if (!config.hooks) config.hooks = {};
302
+ if (!config.hooks.PreToolUse) config.hooks.PreToolUse = [];
303
+
304
+ // phase-write-guard (PreToolUse)
305
+ if (!config.hooks.PreToolUse.some(h => typeof h === 'string' && h.includes('phase-write-guard'))) {
306
+ config.hooks.PreToolUse.push(`sh ${guardPath}`);
307
+ console.log('✓ phase-write-guard hook installed');
308
+ } else {
309
+ console.log('✓ phase-write-guard hook already installed');
310
+ }
311
+
312
+ // evidence-hook (PostToolUse — fires AFTER writes, detects state changes)
313
+ if (!config.hooks.PostToolUse) config.hooks.PostToolUse = [];
314
+ if (!config.hooks.PostToolUse.some(h => typeof h === 'string' && h.includes('evidence-hook'))) {
315
+ config.hooks.PostToolUse.push(`sh ${evidencePath}`);
316
+ console.log('✓ evidence-hook installed (auto-records state changes)');
317
+ } else {
318
+ console.log('✓ evidence-hook already installed');
319
+ }
320
+
321
+ fs.writeFileSync(configFile, JSON.stringify(config, null, 2) + '\n');
322
+ console.log(`✓ hooks updated → ${configFile}`);
323
+ }
324
+
325
+ function cmdEvidence(args) {
326
+ const sub = args[0];
327
+ const rest = args.slice(1);
328
+
329
+ if (!sub) {
330
+ console.log('Usage: atdc evidence {events|stats|timing|metrics|canvas|record|all} ...');
331
+ process.exit(1);
332
+ }
333
+
334
+ switch (sub) {
335
+ case 'events': {
336
+ const change = rest.filter(a => !a.startsWith('--'))[0] || findActiveChange();
337
+ if (!change) { console.log('No active change found.'); process.exit(1); }
338
+ const type = rest.find(a => a.startsWith('--type='))?.split('=')[1];
339
+ const limit = parseInt(rest.find(a => a.startsWith('--limit='))?.split('=')[1] || '50');
340
+ const dir = evidence.resolveChangeDir(change);
341
+ const events = evidence.listEvents(dir, { type, limit });
342
+ if (events.length === 0) {
343
+ console.log('No events found.');
344
+ } else {
345
+ events.forEach(({ event }) => {
346
+ console.log(`[${event.timestamp.slice(0, 19)}] ${event.event_type} · phase=${event.phase} · ${event.event_id.slice(0, 8)}`);
347
+ });
348
+ console.log(`\n${events.length} events total.`);
349
+ }
350
+ break;
351
+ }
352
+ case 'stats': {
353
+ const change = rest[0] || findActiveChange();
354
+ if (!change) { console.log('No active change found.'); process.exit(1); }
355
+ const dir = evidence.resolveChangeDir(change);
356
+ const stats = evidence.eventStats(dir);
357
+ console.log(JSON.stringify(stats, null, 2));
358
+ break;
359
+ }
360
+ case 'timing': {
361
+ const change = rest[0] || findActiveChange();
362
+ if (!change) { console.log('No active change found.'); process.exit(1); }
363
+ const dir = evidence.resolveChangeDir(change);
364
+ const report = evidence.stageReport(dir);
365
+ console.log(JSON.stringify(report, null, 2));
366
+ break;
367
+ }
368
+ case 'metrics': {
369
+ const change = rest.filter(a => !a.startsWith('--'))[0] || findActiveChange();
370
+ if (!change) { console.log('No active change found.'); process.exit(1); }
371
+ const dir = evidence.resolveChangeDir(change);
372
+ if (rest.includes('--snapshot')) {
373
+ const result = evidence.captureDraftSnapshot(dir);
374
+ console.log(JSON.stringify(result, null, 2));
375
+ } else if (rest.includes('--commit')) {
376
+ const result = evidence.captureCommitDiff(dir);
377
+ console.log(JSON.stringify(result, null, 2));
378
+ } else {
379
+ const report = evidence.metricsReport(dir);
380
+ console.log(JSON.stringify(report, null, 2));
381
+ }
382
+ break;
383
+ }
384
+ case 'canvas': {
385
+ const change = rest.filter(a => !a.startsWith('--'))[0] || findActiveChange();
386
+ if (!change) { console.log('No active change found.'); process.exit(1); }
387
+ const outArg = rest.find(a => a.startsWith('--out='));
388
+ const outPath = outArg ? outArg.split('=')[1] : null;
389
+ const dir = evidence.resolveChangeDir(change);
390
+ const dest = evidence.writeCanvas(dir, outPath);
391
+ console.log(`Canvas written to: ${dest}`);
392
+ break;
393
+ }
394
+ case 'record': {
395
+ const change = rest[0];
396
+ const type = rest[1];
397
+ if (!change || !type) {
398
+ console.log('Usage: atdc evidence record <change> <type> [--phase=<p>] [--key=value...]');
399
+ process.exit(1);
400
+ }
401
+ const dir = evidence.resolveChangeDir(change);
402
+ const phase = rest.find(a => a.startsWith('--phase='))?.split('=')[1] || 'unknown';
403
+ const metadata = {};
404
+ rest.forEach(a => {
405
+ if (a.startsWith('--') && a.includes('=') && !a.startsWith('--phase=')) {
406
+ const [k, v] = a.slice(2).split('=');
407
+ metadata[k] = v;
408
+ }
409
+ });
410
+ const result = evidence.writeEvent(dir, type, metadata, { change, phase });
411
+ console.log(`Event recorded: ${result.filepath}`);
412
+ break;
413
+ }
414
+ case 'all': {
415
+ const change = rest[0] || findActiveChange();
416
+ if (!change) { console.log('No active change found.'); process.exit(1); }
417
+ const dir = evidence.resolveChangeDir(change);
418
+ const result = evidence.allEvidence(change);
419
+ console.log(JSON.stringify(result, null, 2));
420
+ break;
421
+ }
422
+ case 'link':
423
+ cmdEvidenceLink();
424
+ break;
425
+ default:
426
+ console.log('Usage: atdc evidence {events|stats|timing|metrics|canvas|record|all|link} ...');
427
+ }
428
+ }
429
+
430
+ function cmdEvidenceLink() {
431
+ const linkScript = path.join(HOME, 'atdc', 'central', 'link.sh');
432
+ if (!exists(linkScript)) { console.log('~/atdc/central/link.sh not found'); process.exit(1); }
433
+ const { execSync } = require('child_process');
434
+ try {
435
+ const result = execSync(`sh "${linkScript}" --status`, { encoding: 'utf8' });
436
+ console.log(result.trim());
437
+ execSync(`sh "${linkScript}"`, { encoding: 'utf8', stdio: 'inherit' });
438
+ } catch (e) {
439
+ console.error(e.stderr || e.message);
440
+ process.exit(1);
441
+ }
442
+ }
443
+
444
+ const [,, cmd, ...args] = process.argv;
445
+
446
+ switch (cmd) {
447
+ case 'install': cmdInstall(args); break;
448
+ case 'uninstall': cmdUninstall(args); break;
449
+ case 'status': cmdStatus(); break;
450
+ case 'guardrails': cmdGuardrails(); break;
451
+ case 'guard': cmdGuard(args); break;
452
+ case 'validate': cmdValidate(args); break;
453
+ case 'handoff': cmdHandoff(args); break;
454
+ case 'run': cmdRun(args); break;
455
+ case 'archive': cmdArchive(args); break;
456
+ case 'hook': cmdHook(args); break;
457
+ case 'ponytail': cmdPonytail(args); break;
458
+ case 'evidence': cmdEvidence(args); break;
459
+ default: usage(); break;
460
+ }
package/bin/cls.js ADDED
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const client = require('../src/cls/client');
5
+
6
+ const HELP = `cls — CLS 日志查询 CLI
7
+
8
+ 用法:
9
+ cls --init 初始化配置文件
10
+ cls --list-accounts 列出可用账号
11
+ cls <account> <query> [options] 查询 CLS 日志
12
+
13
+ 选项:
14
+ --limit N 返回条数(默认 200,最大 1000)
15
+ --out DIR 输出目录(默认当前目录)
16
+ --from TIME 开始时间(默认 15m 前,支持 "2026-07-17 10:00")
17
+ --to TIME 结束时间(默认当前)
18
+ --type TYPE topic 类型(默认 error)
19
+ --regions R 地域(默认 gz,bj)
20
+ --format FORMAT 输出格式 json|jsonl(默认 jsonl)
21
+ --fetch 全量落地(调 cls_fetch_bulk.py)
22
+ --max N 全量最大条数(默认 5000)
23
+ --quiet 静默模式(不输出进度)
24
+ --help 显示此帮助
25
+
26
+ 示例:
27
+ cls tcs "callTencentMusicAPI AND done"
28
+ cls tcs "order AND pay*" --limit 500 --out /tmp/logs/
29
+ cls tcs "order AND pay*" --fetch --max 10000`;
30
+
31
+ function usage() {
32
+ console.log(HELP);
33
+ process.exit(0);
34
+ }
35
+
36
+ function fail(msg) {
37
+ console.error(`cls: ${msg}`);
38
+ process.exit(1);
39
+ }
40
+
41
+ const args = process.argv.slice(2);
42
+
43
+ if (args.length === 0 || args.includes('--help')) {
44
+ if (args.length === 0) {
45
+ try { client.loadConfig(); } catch { client.initConfig(); console.log('配置已生成: ~/.cls/config.json'); process.exit(0); }
46
+ }
47
+ usage();
48
+ }
49
+
50
+ if (args[0] === '--init') {
51
+ const r = client.initConfig();
52
+ console.log(r.msg);
53
+ process.exit(r.ok ? 0 : 1);
54
+ }
55
+
56
+ if (args[0] === '--list-accounts') {
57
+ try {
58
+ const accounts = client.listAccounts();
59
+ console.log('可用账号:');
60
+ Object.entries(accounts).forEach(([alias, name]) => console.log(` ${alias} → ${name}`));
61
+ } catch (e) { fail(e.message); }
62
+ process.exit(0);
63
+ }
64
+
65
+ const [account, query] = args;
66
+ if (!account || !query) fail('用法: cls <account> <query> [options]');
67
+
68
+ const opts = {};
69
+ for (let i = 2; i < args.length; i++) {
70
+ const a = args[i];
71
+ if (a === '--limit') opts.limit = parseInt(args[++i]);
72
+ else if (a === '--out') opts.out_dir = args[++i];
73
+ else if (a === '--from') opts.from = args[++i];
74
+ else if (a === '--to') opts.to = args[++i];
75
+ else if (a === '--type') opts.type = args[++i];
76
+ else if (a === '--regions') opts.regions = args[++i];
77
+ else if (a === '--format') opts.format = args[++i];
78
+ else if (a === '--max') opts.max = parseInt(args[++i]);
79
+ else if (a === '--fetch') opts.fetch = true;
80
+ else if (a === '--quiet') opts.quiet = true;
81
+ }
82
+
83
+ try {
84
+ if (opts.fetch) {
85
+ const result = client.fetchSync(account, query, opts);
86
+ console.log(`→ ${result.output_file} (${result.total} 条)`);
87
+ } else {
88
+ const result = client.searchSync(account, query, opts);
89
+ console.log(`→ ${result.output_file} (${result.count} 条)`);
90
+ }
91
+ } catch (e) {
92
+ if (e.message && e.message.includes('配置文件') && e.message.includes('不存在')) {
93
+ client.initConfig();
94
+ console.log('配置已生成: ~/.cls/config.json,请重试命令');
95
+ } else {
96
+ fail(e.stderr || e.message);
97
+ }
98
+ }
@@ -0,0 +1,60 @@
1
+ #!/bin/bash
2
+ # ~/atdc/central/link.sh — unified worktree symlink manager
3
+ # Called from GO.md Step 0b at every session start. Idempotent.
4
+ #
5
+ # Pattern:
6
+ # Main repo → hosts real dirs
7
+ # Worktree → symlink to main repo's dirs
8
+ # Already linked → skip
9
+
10
+ set -euo pipefail
11
+
12
+ DIRS=(
13
+ .code-review-graph
14
+ .codegraph
15
+ .hc
16
+ .test_plans
17
+ .handoffs
18
+ .evidence
19
+ )
20
+
21
+ # ── Find main worktree ──
22
+ MAIN=$(git worktree list --porcelain 2>/dev/null | awk '
23
+ /^worktree / { p=$2 }
24
+ /^bare / { bare=($2=="true") }
25
+ /^HEAD / { head=$2 }
26
+ /^$/ { if(bare!="true" && p!="") print p; p=""; bare=""; head="" }
27
+ ' | head -1)
28
+
29
+ [ -z "$MAIN" ] && MAIN=$(git worktree list 2>/dev/null | head -1 | awk '{print $1}')
30
+ [ -z "$MAIN" ] && exit 0
31
+
32
+ CURRENT="$(pwd)"
33
+
34
+ # ── Link each dir ──
35
+ for dir in "${DIRS[@]}"; do
36
+ # Already valid symlink → skip
37
+ if [ -L "$dir" ]; then
38
+ [ -d "$(readlink "$dir")" ] && continue
39
+ rm -f "$dir"
40
+ fi
41
+
42
+ # Main worktree: ensure real dir exists (hosting)
43
+ if [ "$CURRENT" = "$MAIN" ]; then
44
+ [ -d "$dir" ] || mkdir -p "$dir"
45
+ continue
46
+ fi
47
+
48
+ # Worktree: main must have dir, then symlink
49
+ [ -d "$MAIN/$dir" ] || mkdir -p "$MAIN/$dir"
50
+
51
+ # Migrate stray real dir → main
52
+ if [ -d "$dir" ] && [ ! -L "$dir" ]; then
53
+ mv "$dir"/* "$MAIN/$dir/" 2>/dev/null || true
54
+ rm -rf "$dir"
55
+ fi
56
+
57
+ ln -s "$MAIN/$dir" "$dir"
58
+ done
59
+
60
+ exit 0