specdo 1.0.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.
Files changed (161) hide show
  1. package/CHANGELOG.md +139 -0
  2. package/README.md +308 -0
  3. package/README.zh-CN.md +306 -0
  4. package/bin/specdo.js +3 -0
  5. package/dist/cli/index.d.ts +15 -0
  6. package/dist/cli/index.d.ts.map +1 -0
  7. package/dist/cli/index.js +297 -0
  8. package/dist/cli/index.js.map +1 -0
  9. package/dist/commands/_shared.d.ts +45 -0
  10. package/dist/commands/_shared.d.ts.map +1 -0
  11. package/dist/commands/_shared.js +124 -0
  12. package/dist/commands/_shared.js.map +1 -0
  13. package/dist/commands/apply.d.ts +30 -0
  14. package/dist/commands/apply.d.ts.map +1 -0
  15. package/dist/commands/apply.js +393 -0
  16. package/dist/commands/apply.js.map +1 -0
  17. package/dist/commands/archive.d.ts +25 -0
  18. package/dist/commands/archive.d.ts.map +1 -0
  19. package/dist/commands/archive.js +362 -0
  20. package/dist/commands/archive.js.map +1 -0
  21. package/dist/commands/doctor.d.ts +21 -0
  22. package/dist/commands/doctor.d.ts.map +1 -0
  23. package/dist/commands/doctor.js +180 -0
  24. package/dist/commands/doctor.js.map +1 -0
  25. package/dist/commands/domains.d.ts +14 -0
  26. package/dist/commands/domains.d.ts.map +1 -0
  27. package/dist/commands/domains.js +107 -0
  28. package/dist/commands/domains.js.map +1 -0
  29. package/dist/commands/explore.d.ts +48 -0
  30. package/dist/commands/explore.d.ts.map +1 -0
  31. package/dist/commands/explore.js +378 -0
  32. package/dist/commands/explore.js.map +1 -0
  33. package/dist/commands/init.d.ts +45 -0
  34. package/dist/commands/init.d.ts.map +1 -0
  35. package/dist/commands/init.js +243 -0
  36. package/dist/commands/init.js.map +1 -0
  37. package/dist/commands/list.d.ts +23 -0
  38. package/dist/commands/list.d.ts.map +1 -0
  39. package/dist/commands/list.js +135 -0
  40. package/dist/commands/list.js.map +1 -0
  41. package/dist/commands/propose.d.ts +22 -0
  42. package/dist/commands/propose.d.ts.map +1 -0
  43. package/dist/commands/propose.js +316 -0
  44. package/dist/commands/propose.js.map +1 -0
  45. package/dist/commands/show.d.ts +15 -0
  46. package/dist/commands/show.d.ts.map +1 -0
  47. package/dist/commands/show.js +214 -0
  48. package/dist/commands/show.js.map +1 -0
  49. package/dist/commands/status.d.ts +17 -0
  50. package/dist/commands/status.d.ts.map +1 -0
  51. package/dist/commands/status.js +146 -0
  52. package/dist/commands/status.js.map +1 -0
  53. package/dist/commands/sync.d.ts +21 -0
  54. package/dist/commands/sync.d.ts.map +1 -0
  55. package/dist/commands/sync.js +113 -0
  56. package/dist/commands/sync.js.map +1 -0
  57. package/dist/commands/validate.d.ts +117 -0
  58. package/dist/commands/validate.d.ts.map +1 -0
  59. package/dist/commands/validate.js +446 -0
  60. package/dist/commands/validate.js.map +1 -0
  61. package/dist/core/apply-brief-renderer.d.ts +35 -0
  62. package/dist/core/apply-brief-renderer.d.ts.map +1 -0
  63. package/dist/core/apply-brief-renderer.js +242 -0
  64. package/dist/core/apply-brief-renderer.js.map +1 -0
  65. package/dist/core/config-store.d.ts +190 -0
  66. package/dist/core/config-store.d.ts.map +1 -0
  67. package/dist/core/config-store.js +280 -0
  68. package/dist/core/config-store.js.map +1 -0
  69. package/dist/core/context-store.d.ts +96 -0
  70. package/dist/core/context-store.d.ts.map +1 -0
  71. package/dist/core/context-store.js +426 -0
  72. package/dist/core/context-store.js.map +1 -0
  73. package/dist/core/json-schemas.d.ts +349 -0
  74. package/dist/core/json-schemas.d.ts.map +1 -0
  75. package/dist/core/json-schemas.js +125 -0
  76. package/dist/core/json-schemas.js.map +1 -0
  77. package/dist/core/skill-content/cross-domain.d.ts +12 -0
  78. package/dist/core/skill-content/cross-domain.d.ts.map +1 -0
  79. package/dist/core/skill-content/cross-domain.js +291 -0
  80. package/dist/core/skill-content/cross-domain.js.map +1 -0
  81. package/dist/core/skill-content/protocol-examples.d.ts +13 -0
  82. package/dist/core/skill-content/protocol-examples.d.ts.map +1 -0
  83. package/dist/core/skill-content/protocol-examples.js +190 -0
  84. package/dist/core/skill-content/protocol-examples.js.map +1 -0
  85. package/dist/core/skill-content/workflow-content.d.ts +25 -0
  86. package/dist/core/skill-content/workflow-content.d.ts.map +1 -0
  87. package/dist/core/skill-content/workflow-content.js +1572 -0
  88. package/dist/core/skill-content/workflow-content.js.map +1 -0
  89. package/dist/core/skill-exporter.d.ts +186 -0
  90. package/dist/core/skill-exporter.d.ts.map +1 -0
  91. package/dist/core/skill-exporter.js +922 -0
  92. package/dist/core/skill-exporter.js.map +1 -0
  93. package/dist/core/spec-sync.d.ts +65 -0
  94. package/dist/core/spec-sync.d.ts.map +1 -0
  95. package/dist/core/spec-sync.js +226 -0
  96. package/dist/core/spec-sync.js.map +1 -0
  97. package/dist/core/task-parser.d.ts +58 -0
  98. package/dist/core/task-parser.d.ts.map +1 -0
  99. package/dist/core/task-parser.js +244 -0
  100. package/dist/core/task-parser.js.map +1 -0
  101. package/dist/core/template-renderer.d.ts +51 -0
  102. package/dist/core/template-renderer.d.ts.map +1 -0
  103. package/dist/core/template-renderer.js +362 -0
  104. package/dist/core/template-renderer.js.map +1 -0
  105. package/dist/domains/architecture.d.ts +34 -0
  106. package/dist/domains/architecture.d.ts.map +1 -0
  107. package/dist/domains/architecture.js +341 -0
  108. package/dist/domains/architecture.js.map +1 -0
  109. package/dist/domains/backend.d.ts +35 -0
  110. package/dist/domains/backend.d.ts.map +1 -0
  111. package/dist/domains/backend.js +367 -0
  112. package/dist/domains/backend.js.map +1 -0
  113. package/dist/domains/frontend.d.ts +36 -0
  114. package/dist/domains/frontend.d.ts.map +1 -0
  115. package/dist/domains/frontend.js +373 -0
  116. package/dist/domains/frontend.js.map +1 -0
  117. package/dist/domains/index.d.ts +49 -0
  118. package/dist/domains/index.d.ts.map +1 -0
  119. package/dist/domains/index.js +255 -0
  120. package/dist/domains/index.js.map +1 -0
  121. package/dist/domains/operations.d.ts +37 -0
  122. package/dist/domains/operations.d.ts.map +1 -0
  123. package/dist/domains/operations.js +344 -0
  124. package/dist/domains/operations.js.map +1 -0
  125. package/dist/domains/pool-ranking.d.ts +43 -0
  126. package/dist/domains/pool-ranking.d.ts.map +1 -0
  127. package/dist/domains/pool-ranking.js +153 -0
  128. package/dist/domains/pool-ranking.js.map +1 -0
  129. package/dist/domains/quality.d.ts +45 -0
  130. package/dist/domains/quality.d.ts.map +1 -0
  131. package/dist/domains/quality.js +368 -0
  132. package/dist/domains/quality.js.map +1 -0
  133. package/dist/domains/security.d.ts +19 -0
  134. package/dist/domains/security.d.ts.map +1 -0
  135. package/dist/domains/security.js +364 -0
  136. package/dist/domains/security.js.map +1 -0
  137. package/dist/domains/signal-match.d.ts +25 -0
  138. package/dist/domains/signal-match.d.ts.map +1 -0
  139. package/dist/domains/signal-match.js +67 -0
  140. package/dist/domains/signal-match.js.map +1 -0
  141. package/dist/domains/types.d.ts +354 -0
  142. package/dist/domains/types.d.ts.map +1 -0
  143. package/dist/domains/types.js +12 -0
  144. package/dist/domains/types.js.map +1 -0
  145. package/dist/index.d.ts +9 -0
  146. package/dist/index.d.ts.map +1 -0
  147. package/dist/index.js +9 -0
  148. package/dist/index.js.map +1 -0
  149. package/dist/protocols/index.d.ts +36 -0
  150. package/dist/protocols/index.d.ts.map +1 -0
  151. package/dist/protocols/index.js +85 -0
  152. package/dist/protocols/index.js.map +1 -0
  153. package/dist/protocols/review-to-solid.d.ts +32 -0
  154. package/dist/protocols/review-to-solid.d.ts.map +1 -0
  155. package/dist/protocols/review-to-solid.js +309 -0
  156. package/dist/protocols/review-to-solid.js.map +1 -0
  157. package/dist/utils/prompt.d.ts +37 -0
  158. package/dist/utils/prompt.d.ts.map +1 -0
  159. package/dist/utils/prompt.js +81 -0
  160. package/dist/utils/prompt.js.map +1 -0
  161. package/package.json +80 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * specdo status [--change <name>] [--json]
3
+ *
4
+ * 行为:
5
+ * - 无 --change:汇总所有 active changes(每个一行)
6
+ * - 有 --change:详细展示该 change 的 5 阶段状态 + tasks 进度 + sync 摘要
7
+ *
8
+ * 不写文件,仅读取。
9
+ */
10
+ import * as fs from 'node:fs';
11
+ import * as path from 'node:path';
12
+ import { ContextStore } from '../core/context-store.js';
13
+ import { parseTasks } from '../core/task-parser.js';
14
+ import { fail, findSpecdoConfig, getArchiveDir, getChangeDir, getSpecdoDir, isValidChangeName, ok, safeExists, safeIsDir, } from './_shared.js';
15
+ export async function handleStatus(options) {
16
+ const { cwd } = options;
17
+ if (!findSpecdoConfig(cwd)) {
18
+ return fail(1, ["Not initialized. Run 'specdo init' first."]);
19
+ }
20
+ if (options.change) {
21
+ if (!isValidChangeName(options.change)) {
22
+ return fail(1, [`Invalid --change name: "${options.change}".`]);
23
+ }
24
+ return statusForChange(cwd, options.change, options.json ?? false);
25
+ }
26
+ return statusOverview(cwd, options.json ?? false);
27
+ }
28
+ function statusForChange(cwd, name, asJson) {
29
+ const candidates = [
30
+ { dir: getChangeDir(cwd, name), location: 'active' },
31
+ { dir: getArchiveDir(cwd, name), location: 'archived' },
32
+ ];
33
+ const found = candidates.find((c) => safeIsDir(c.dir));
34
+ if (!found) {
35
+ return fail(1, [`Change '${name}' not found in specdo/changes/ or specdo/archive/.`]);
36
+ }
37
+ const summary = collectSummary(name, found.dir, found.location);
38
+ if (asJson) {
39
+ return ok([JSON.stringify({ kind: 'status', summary }, null, 2)]);
40
+ }
41
+ const ctx = new ContextStore({ changeDir: found.dir }).read();
42
+ const stdout = [];
43
+ stdout.push(`Change: ${name} (${found.location})`);
44
+ stdout.push('');
45
+ stdout.push(`Stage : ${summary.stage}`);
46
+ stdout.push(`Tasks : ${summary.tasksCompleted}/${summary.tasksTotal} complete${summary.evidenceMissing > 0 ? ` (${summary.evidenceMissing} missing evidence)` : ''}`);
47
+ stdout.push(`Updated : ${summary.updatedAt ?? '(unknown)'}`);
48
+ stdout.push('');
49
+ stdout.push('Pipeline:');
50
+ stdout.push(` explore : ${ctx.explore ? (ctx.explore.completedAt ? '✓' : '… in progress') : '(not run)'}`);
51
+ stdout.push(` propose : ${ctx.propose ? (ctx.propose.completedAt ? '✓' : '… in progress') : '(not run)'}`);
52
+ stdout.push(` apply : ${ctx.apply ? (ctx.apply.completedAt ? '✓' : `… ${ctx.apply.tasksCompleted}/${ctx.apply.tasksTotal}`) : '(not run)'}`);
53
+ stdout.push(` sync : ${ctx.sync ? (ctx.sync.completedAt ? '✓' + (summary.hasUnresolvedConflicts ? ' (with unresolved conflicts!)' : '') : '… in progress') : '(not run)'}`);
54
+ stdout.push(` archive : ${ctx.archive ? '✓' : '(not run)'}`);
55
+ if (summary.hasUnresolvedConflicts) {
56
+ stdout.push('');
57
+ stdout.push('⚠ Unresolved sync conflicts. Run `specdo sync --change ' + name + '` to inspect.');
58
+ }
59
+ if (summary.evidenceMissing > 0) {
60
+ stdout.push('');
61
+ stdout.push(`⚠ ${summary.evidenceMissing} completed task(s) lack evidence. Run validate for details.`);
62
+ }
63
+ return ok(stdout);
64
+ }
65
+ function statusOverview(cwd, asJson) {
66
+ const summaries = [];
67
+ const changesDir = path.join(getSpecdoDir(cwd), 'changes');
68
+ if (safeIsDir(changesDir)) {
69
+ for (const name of fs.readdirSync(changesDir)) {
70
+ const dir = path.join(changesDir, name);
71
+ if (!safeIsDir(dir))
72
+ continue;
73
+ summaries.push(collectSummary(name, dir, 'active'));
74
+ }
75
+ }
76
+ summaries.sort((a, b) => {
77
+ const ta = a.updatedAt ? new Date(a.updatedAt).getTime() : 0;
78
+ const tb = b.updatedAt ? new Date(b.updatedAt).getTime() : 0;
79
+ return tb - ta;
80
+ });
81
+ if (asJson) {
82
+ return ok([JSON.stringify({ kind: 'status-overview', changes: summaries }, null, 2)]);
83
+ }
84
+ const stdout = [];
85
+ if (summaries.length === 0) {
86
+ stdout.push('No active changes. Run `specdo explore "<idea>"` to start one.');
87
+ return ok(stdout);
88
+ }
89
+ stdout.push(`Active changes (${summaries.length}):`);
90
+ stdout.push('');
91
+ stdout.push(` ${'NAME'.padEnd(28)} ${'STAGE'.padEnd(10)} ${'TASKS'.padEnd(10)} ${'FLAGS'.padEnd(10)} UPDATED`);
92
+ for (const s of summaries) {
93
+ const tasks = s.tasksTotal > 0 ? `${s.tasksCompleted}/${s.tasksTotal}` : '-';
94
+ const flags = [
95
+ s.hasUnresolvedConflicts ? 'conflict' : null,
96
+ s.hasForcedConflicts ? 'forced' : null,
97
+ s.evidenceMissing > 0 ? `ev-${s.evidenceMissing}` : null,
98
+ ].filter(Boolean).join(',') || '-';
99
+ const updated = s.updatedAt ? s.updatedAt.replace('T', ' ').slice(0, 19) : '(unknown)';
100
+ stdout.push(` ${s.name.padEnd(28)} ${s.stage.padEnd(10)} ${tasks.padEnd(10)} ${flags.padEnd(10)} ${updated}`);
101
+ }
102
+ return ok(stdout);
103
+ }
104
+ function collectSummary(name, dir, location) {
105
+ const ctx = new ContextStore({ changeDir: dir }).read();
106
+ let total = 0;
107
+ let completed = 0;
108
+ let evidenceMissing = 0;
109
+ const tasksPath = path.join(dir, 'tasks.md');
110
+ if (safeExists(tasksPath)) {
111
+ try {
112
+ const parsed = parseTasks(fs.readFileSync(tasksPath, 'utf-8'));
113
+ total = parsed.tasks.length;
114
+ completed = parsed.tasks.filter((t) => t.completed).length;
115
+ evidenceMissing = parsed.tasks.filter((t) => t.completed && (!t.evidence || t.evidence.trim().length === 0)).length;
116
+ }
117
+ catch {
118
+ /* parse error → counts stay 0; validate command will surface */
119
+ }
120
+ }
121
+ return {
122
+ name,
123
+ location,
124
+ stage: deriveStage(ctx),
125
+ tasksCompleted: completed,
126
+ tasksTotal: total,
127
+ evidenceMissing,
128
+ hasUnresolvedConflicts: (ctx.sync?.conflicts ?? []).length > 0,
129
+ hasForcedConflicts: (ctx.sync?.forcedConflicts ?? []).length > 0,
130
+ updatedAt: ctx.change?.updatedAt ?? null,
131
+ };
132
+ }
133
+ function deriveStage(ctx) {
134
+ if (ctx.archive)
135
+ return 'archive';
136
+ if (ctx.sync?.completedAt)
137
+ return 'synced';
138
+ if (ctx.apply)
139
+ return ctx.apply.completedAt ? 'apply✓' : 'apply';
140
+ if (ctx.propose)
141
+ return 'propose';
142
+ if (ctx.explore)
143
+ return 'explore';
144
+ return 'init';
145
+ }
146
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * specdo sync [--change <name>] [--force]
3
+ *
4
+ * 行为:
5
+ * 1. 递归读取 specdo/changes/<name>/specs/ 下的全部 markdown spec 文件
6
+ * 2. 与 specdo/specs/ 下的对应相对路径文件做比对
7
+ * 3. 仅 added → 写入;含 conflict 且无 --force → exit 1,写 conflicts
8
+ * 4. 含 conflict 且 --force → 备份 + 覆盖 + 写 forcedConflicts/forcedConflictHashes
9
+ * 5. 写 context.sync.{syncedSpecs, conflicts, forcedConflicts, forcedConflictHashes, forced, completedAt}
10
+ *
11
+ * `.sync-backup/` 残留 → exit 3
12
+ */
13
+ import { CommandResult } from './_shared.js';
14
+ export interface SyncOptions {
15
+ cwd: string;
16
+ change?: string;
17
+ force?: boolean;
18
+ now?: () => string;
19
+ }
20
+ export declare function handleSync(options: SyncOptions): Promise<CommandResult>;
21
+ //# sourceMappingURL=sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,EACL,aAAa,EAQd,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,CAAC,CA0G7E"}
@@ -0,0 +1,113 @@
1
+ /**
2
+ * specdo sync [--change <name>] [--force]
3
+ *
4
+ * 行为:
5
+ * 1. 递归读取 specdo/changes/<name>/specs/ 下的全部 markdown spec 文件
6
+ * 2. 与 specdo/specs/ 下的对应相对路径文件做比对
7
+ * 3. 仅 added → 写入;含 conflict 且无 --force → exit 1,写 conflicts
8
+ * 4. 含 conflict 且 --force → 备份 + 覆盖 + 写 forcedConflicts/forcedConflictHashes
9
+ * 5. 写 context.sync.{syncedSpecs, conflicts, forcedConflicts, forcedConflictHashes, forced, completedAt}
10
+ *
11
+ * `.sync-backup/` 残留 → exit 3
12
+ */
13
+ import * as path from 'node:path';
14
+ import { ContextStore } from '../core/context-store.js';
15
+ import { executeSync, SyncError } from '../core/spec-sync.js';
16
+ import { fail, findSpecdoConfig, getChangeDir, getSpecdoDir, getSpecsDir, isValidChangeName, safeIsDir, } from './_shared.js';
17
+ export async function handleSync(options) {
18
+ const { cwd, force } = options;
19
+ if (!findSpecdoConfig(cwd)) {
20
+ return fail(1, ["Not initialized. Run 'specdo init' first."]);
21
+ }
22
+ if (!options.change) {
23
+ return fail(1, ['sync requires --change <name>.']);
24
+ }
25
+ if (!isValidChangeName(options.change)) {
26
+ return fail(1, [`Invalid --change name: "${options.change}".`]);
27
+ }
28
+ const changeName = options.change;
29
+ const changeDir = getChangeDir(cwd, changeName);
30
+ if (!safeIsDir(changeDir)) {
31
+ return fail(1, [`Change directory not found: ${changeDir}.`]);
32
+ }
33
+ const sourceSpecsDir = path.join(changeDir, 'specs');
34
+ if (!safeIsDir(sourceSpecsDir)) {
35
+ return fail(1, [
36
+ `Source specs directory not found at ${sourceSpecsDir}.`,
37
+ `Run 'specdo propose --change ${changeName}' first.`,
38
+ ]);
39
+ }
40
+ const specsDir = getSpecsDir(cwd);
41
+ const backupDir = path.join(getSpecdoDir(cwd), '.sync-backup');
42
+ const now = options.now ?? (() => new Date().toISOString());
43
+ const store = new ContextStore({ changeDir, now });
44
+ const ctx = store.read();
45
+ // 前置条件:apply 必须完成
46
+ if (!ctx.apply || ctx.apply.completedAt === null) {
47
+ return fail(1, [
48
+ `Cannot sync: apply is not complete.`,
49
+ `Run 'specdo apply --change ${changeName}' and complete all tasks first.`,
50
+ ]);
51
+ }
52
+ try {
53
+ const result = executeSync({
54
+ changesSpecsDir: sourceSpecsDir,
55
+ specsDir,
56
+ backupDir,
57
+ force: Boolean(force),
58
+ });
59
+ const stdout = [];
60
+ const stderr = [];
61
+ if (result.conflicts.length > 0) {
62
+ // 未使用 --force 且存在冲突
63
+ stderr.push(`Sync blocked: ${result.conflicts.length} conflict(s) detected.`);
64
+ for (const rel of result.conflicts) {
65
+ stderr.push(` ✗ ${rel}`);
66
+ }
67
+ stderr.push(`Run with --force to overwrite (a backup will be written to specdo/.sync-backup/).`);
68
+ store.writeDeep({
69
+ sync: {
70
+ syncedSpecs: result.syncedSpecs,
71
+ conflicts: result.conflicts,
72
+ forcedConflicts: [],
73
+ forcedConflictHashes: {},
74
+ forced: false,
75
+ completedAt: null,
76
+ },
77
+ });
78
+ return { exitCode: 1, stdout, stderr };
79
+ }
80
+ stdout.push(`# specdo sync --change ${changeName}`);
81
+ stdout.push('');
82
+ stdout.push(`Synced ${result.syncedSpecs.length} spec file(s) into ${path.relative(cwd, specsDir)}/.`);
83
+ for (const rel of result.syncedSpecs) {
84
+ const forcedTag = result.forcedConflicts.includes(rel) ? ' [forced]' : '';
85
+ stdout.push(` ✓ ${rel}${forcedTag}`);
86
+ }
87
+ if (result.forcedConflicts.length > 0) {
88
+ stdout.push('');
89
+ stdout.push(`Forced overwrites recorded in context.json.sync.forcedConflictHashes (${result.forcedConflicts.length} entries).`);
90
+ }
91
+ store.writeDeep({
92
+ sync: {
93
+ syncedSpecs: result.syncedSpecs,
94
+ conflicts: [],
95
+ forcedConflicts: result.forcedConflicts,
96
+ forcedConflictHashes: result.forcedConflictHashes,
97
+ forced: result.forced,
98
+ completedAt: now(),
99
+ },
100
+ });
101
+ stdout.push('');
102
+ stdout.push(`Next: specdo archive --change ${changeName}`);
103
+ return { exitCode: 0, stdout, stderr };
104
+ }
105
+ catch (err) {
106
+ if (err instanceof SyncError) {
107
+ const code = err.exitCode === 1 || err.exitCode === 3 ? err.exitCode : 3;
108
+ return fail(code, [err.message]);
109
+ }
110
+ return fail(3, [`Sync failed: ${err.message}`]);
111
+ }
112
+ }
113
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * specdo validate [--change <name>] [--json]
3
+ *
4
+ * 校验范围:
5
+ * 1. config.yaml schema + overrides (loadConfig)
6
+ * 2. 每个 change(active)的 context.json schema (Zod)
7
+ * 3. tasks.md 一致性:
8
+ * - 重复编号 / 缺口 → warn(缺口)/ error(重复)
9
+ * - 已完成任务缺 evidence → warning
10
+ * - context.apply.{tasksTotal, tasksCompleted, tasksEvidence} 与 tasks.md 一致
11
+ * 4. artifact 引用:proposal/design/tasks/specs 文件存在
12
+ * 5. context.json 与 propose.artifacts 路径自洽
13
+ *
14
+ * 输出:分组 (config / changes/<name>) 列出 issues;error 计数 > 0 → exit 1。
15
+ * 任意 change 失败时仍继续校验剩余 change。
16
+ */
17
+ import { z } from 'zod';
18
+ import { CommandResult } from './_shared.js';
19
+ export interface ValidateOptions {
20
+ cwd: string;
21
+ change?: string;
22
+ json?: boolean;
23
+ }
24
+ export interface ValidationIssue {
25
+ level: 'error' | 'warn';
26
+ scope: string;
27
+ path: string;
28
+ message: string;
29
+ }
30
+ export interface ChangeValidationSnapshot {
31
+ issues: ValidationIssue[];
32
+ context: z.infer<typeof contextSchema> | null;
33
+ tasks: {
34
+ total: number;
35
+ completed: number;
36
+ evidenceMap: Record<string, string>;
37
+ };
38
+ }
39
+ declare const contextSchema: z.ZodObject<{
40
+ version: z.ZodLiteral<2>;
41
+ change: z.ZodObject<{
42
+ name: z.ZodString;
43
+ createdAt: z.ZodString;
44
+ updatedAt: z.ZodString;
45
+ }, z.core.$strip>;
46
+ explore: z.ZodOptional<z.ZodObject<{
47
+ input: z.ZodString;
48
+ matchedDomains: z.ZodArray<z.ZodObject<{
49
+ name: z.ZodString;
50
+ score: z.ZodNumber;
51
+ reason: z.ZodString;
52
+ }, z.core.$strip>>;
53
+ answers: z.ZodRecord<z.ZodString, z.ZodString>;
54
+ flaggedConcerns: z.ZodArray<z.ZodString>;
55
+ completedAt: z.ZodNullable<z.ZodString>;
56
+ collectedContext: z.ZodOptional<z.ZodString>;
57
+ depth: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
58
+ shallow: "shallow";
59
+ standard: "standard";
60
+ deep: "deep";
61
+ }>>>;
62
+ llmQuestions: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
63
+ }, z.core.$strip>>;
64
+ propose: z.ZodOptional<z.ZodObject<{
65
+ artifacts: z.ZodObject<{
66
+ proposal: z.ZodString;
67
+ specs: z.ZodArray<z.ZodString>;
68
+ design: z.ZodString;
69
+ tasks: z.ZodString;
70
+ }, z.core.$strip>;
71
+ domainsUsed: z.ZodArray<z.ZodString>;
72
+ capabilities: z.ZodDefault<z.ZodArray<z.ZodObject<{
73
+ name: z.ZodString;
74
+ kind: z.ZodEnum<{
75
+ new: "new";
76
+ modified: "modified";
77
+ }>;
78
+ specPath: z.ZodString;
79
+ }, z.core.$strip>>>;
80
+ completedAt: z.ZodNullable<z.ZodString>;
81
+ }, z.core.$strip>>;
82
+ apply: z.ZodOptional<z.ZodObject<{
83
+ completedAt: z.ZodNullable<z.ZodString>;
84
+ tasksTotal: z.ZodNumber;
85
+ tasksCompleted: z.ZodNumber;
86
+ tasksEvidence: z.ZodRecord<z.ZodString, z.ZodString>;
87
+ applyBriefGenerated: z.ZodBoolean;
88
+ }, z.core.$strip>>;
89
+ sync: z.ZodOptional<z.ZodObject<{
90
+ syncedSpecs: z.ZodArray<z.ZodString>;
91
+ conflicts: z.ZodArray<z.ZodString>;
92
+ forcedConflicts: z.ZodOptional<z.ZodArray<z.ZodString>>;
93
+ forcedConflictHashes: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
94
+ before: z.ZodString;
95
+ after: z.ZodString;
96
+ }, z.core.$strip>>>;
97
+ forced: z.ZodBoolean;
98
+ completedAt: z.ZodNullable<z.ZodString>;
99
+ }, z.core.$strip>>;
100
+ archive: z.ZodOptional<z.ZodObject<{
101
+ archivedAt: z.ZodString;
102
+ archivePath: z.ZodString;
103
+ summaryPath: z.ZodString;
104
+ skippedSyncCheck: z.ZodOptional<z.ZodBoolean>;
105
+ skipSyncReason: z.ZodOptional<z.ZodString>;
106
+ syncSnapshot: z.ZodOptional<z.ZodObject<{
107
+ completedAt: z.ZodNullable<z.ZodString>;
108
+ unresolvedConflicts: z.ZodArray<z.ZodString>;
109
+ forcedConflicts: z.ZodArray<z.ZodString>;
110
+ }, z.core.$strip>>;
111
+ riskNotes: z.ZodOptional<z.ZodArray<z.ZodString>>;
112
+ }, z.core.$strip>>;
113
+ }, z.core.$strip>;
114
+ export declare function handleValidate(options: ValidateOptions): Promise<CommandResult>;
115
+ export declare function validateChange(cwd: string, name: string): ChangeValidationSnapshot;
116
+ export {};
117
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EACL,aAAa,EASd,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,GAAG,IAAI,CAAC;IAC9C,KAAK,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACrC,CAAC;CACH;AA6ED,QAAA,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAcjB,CAAC;AAEH,wBAAsB,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC,CA4ErF;AAYD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,wBAAwB,CAkNlF"}