@loomhelm/multi-task-flow 0.2.6 → 0.2.7

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 (93) hide show
  1. package/README.md +3 -3
  2. package/package.json +1 -1
  3. package/toolkit/artifact-manifest.json +330 -26
  4. package/toolkit/package.json +1 -1
  5. package/toolkit/skills/docs-migrate/scripts/_runtime/compatibility_cli.ts +12 -0
  6. package/toolkit/skills/docs-migrate/scripts/_runtime/compatibility_docs.ts +115 -0
  7. package/toolkit/skills/docs-migrate/scripts/_runtime/compatibility_shared.ts +230 -0
  8. package/toolkit/skills/docs-migrate/scripts/_runtime/modules/checks/replay.ts +336 -0
  9. package/toolkit/skills/docs-migrate/scripts/_runtime/modules/docs/git.ts +54 -0
  10. package/toolkit/skills/docs-migrate/scripts/_runtime/modules/docs/hardware_ingest.ts +377 -0
  11. package/toolkit/skills/docs-migrate/scripts/_runtime/modules/docs/hardware_locks.ts +181 -0
  12. package/toolkit/skills/docs-migrate/scripts/_runtime/modules/docs/legacy_spec_id.ts +131 -0
  13. package/toolkit/skills/docs-migrate/scripts/_runtime/modules/docs/migration_common.ts +291 -0
  14. package/toolkit/skills/docs-migrate/scripts/_runtime/modules/docs/next_plan.ts +199 -0
  15. package/toolkit/skills/docs-migrate/scripts/_runtime/modules/docs/next_plan_support.ts +245 -0
  16. package/toolkit/skills/docs-migrate/scripts/_runtime/modules/docs/spec_contract.ts +299 -0
  17. package/toolkit/skills/docs-migrate/scripts/_runtime/modules/docs/spec_drift.ts +387 -0
  18. package/toolkit/skills/docs-migrate/scripts/_runtime/modules/docs/spec_drift_helpers.ts +381 -0
  19. package/toolkit/skills/docs-migrate/scripts/_runtime/modules/docs/spec_drift_legacy.ts +376 -0
  20. package/toolkit/skills/docs-migrate/scripts/_runtime/modules/docs/spec_drift_paths.ts +3 -0
  21. package/toolkit/skills/docs-migrate/scripts/_runtime/modules/docs/spec_drift_types.ts +27 -0
  22. package/toolkit/skills/docs-migrate/scripts/_runtime/modules/docs/spec_drift_validation.ts +251 -0
  23. package/toolkit/skills/docs-migrate/scripts/_runtime/modules/docs/spec_migration.ts +191 -0
  24. package/toolkit/skills/docs-migrate/scripts/gen_legacy_spec_id.ts +1 -1
  25. package/toolkit/skills/docs-migrate/scripts/specs_migrate_guard.ts +1 -1
  26. package/toolkit/skills/find-next-plan/scripts/_runtime/compatibility_cli.ts +12 -0
  27. package/toolkit/skills/find-next-plan/scripts/_runtime/compatibility_docs.ts +115 -0
  28. package/toolkit/skills/find-next-plan/scripts/_runtime/compatibility_shared.ts +230 -0
  29. package/toolkit/skills/find-next-plan/scripts/_runtime/modules/checks/replay.ts +336 -0
  30. package/toolkit/skills/find-next-plan/scripts/_runtime/modules/docs/git.ts +54 -0
  31. package/toolkit/skills/find-next-plan/scripts/_runtime/modules/docs/hardware_ingest.ts +377 -0
  32. package/toolkit/skills/find-next-plan/scripts/_runtime/modules/docs/hardware_locks.ts +181 -0
  33. package/toolkit/skills/find-next-plan/scripts/_runtime/modules/docs/legacy_spec_id.ts +131 -0
  34. package/toolkit/skills/find-next-plan/scripts/_runtime/modules/docs/migration_common.ts +291 -0
  35. package/toolkit/skills/find-next-plan/scripts/_runtime/modules/docs/next_plan.ts +199 -0
  36. package/toolkit/skills/find-next-plan/scripts/_runtime/modules/docs/next_plan_support.ts +245 -0
  37. package/toolkit/skills/find-next-plan/scripts/_runtime/modules/docs/spec_contract.ts +299 -0
  38. package/toolkit/skills/find-next-plan/scripts/_runtime/modules/docs/spec_drift.ts +387 -0
  39. package/toolkit/skills/find-next-plan/scripts/_runtime/modules/docs/spec_drift_helpers.ts +381 -0
  40. package/toolkit/skills/find-next-plan/scripts/_runtime/modules/docs/spec_drift_legacy.ts +376 -0
  41. package/toolkit/skills/find-next-plan/scripts/_runtime/modules/docs/spec_drift_paths.ts +3 -0
  42. package/toolkit/skills/find-next-plan/scripts/_runtime/modules/docs/spec_drift_types.ts +27 -0
  43. package/toolkit/skills/find-next-plan/scripts/_runtime/modules/docs/spec_drift_validation.ts +251 -0
  44. package/toolkit/skills/find-next-plan/scripts/_runtime/modules/docs/spec_migration.ts +191 -0
  45. package/toolkit/skills/find-next-plan/scripts/find_next_plan.ts +1 -1
  46. package/toolkit/skills/hardware-doc-ingest/scripts/_runtime/compatibility_cli.ts +12 -0
  47. package/toolkit/skills/hardware-doc-ingest/scripts/_runtime/compatibility_docs.ts +115 -0
  48. package/toolkit/skills/hardware-doc-ingest/scripts/_runtime/compatibility_shared.ts +230 -0
  49. package/toolkit/skills/hardware-doc-ingest/scripts/_runtime/modules/checks/replay.ts +336 -0
  50. package/toolkit/skills/hardware-doc-ingest/scripts/_runtime/modules/docs/git.ts +54 -0
  51. package/toolkit/skills/hardware-doc-ingest/scripts/_runtime/modules/docs/hardware_ingest.ts +377 -0
  52. package/toolkit/skills/hardware-doc-ingest/scripts/_runtime/modules/docs/hardware_locks.ts +181 -0
  53. package/toolkit/skills/hardware-doc-ingest/scripts/_runtime/modules/docs/legacy_spec_id.ts +131 -0
  54. package/toolkit/skills/hardware-doc-ingest/scripts/_runtime/modules/docs/migration_common.ts +291 -0
  55. package/toolkit/skills/hardware-doc-ingest/scripts/_runtime/modules/docs/next_plan.ts +199 -0
  56. package/toolkit/skills/hardware-doc-ingest/scripts/_runtime/modules/docs/next_plan_support.ts +245 -0
  57. package/toolkit/skills/hardware-doc-ingest/scripts/_runtime/modules/docs/spec_contract.ts +299 -0
  58. package/toolkit/skills/hardware-doc-ingest/scripts/_runtime/modules/docs/spec_drift.ts +387 -0
  59. package/toolkit/skills/hardware-doc-ingest/scripts/_runtime/modules/docs/spec_drift_helpers.ts +381 -0
  60. package/toolkit/skills/hardware-doc-ingest/scripts/_runtime/modules/docs/spec_drift_legacy.ts +376 -0
  61. package/toolkit/skills/hardware-doc-ingest/scripts/_runtime/modules/docs/spec_drift_paths.ts +3 -0
  62. package/toolkit/skills/hardware-doc-ingest/scripts/_runtime/modules/docs/spec_drift_types.ts +27 -0
  63. package/toolkit/skills/hardware-doc-ingest/scripts/_runtime/modules/docs/spec_drift_validation.ts +251 -0
  64. package/toolkit/skills/hardware-doc-ingest/scripts/_runtime/modules/docs/spec_migration.ts +191 -0
  65. package/toolkit/skills/hardware-doc-ingest/scripts/ingest_mineru_extract.ts +1 -1
  66. package/toolkit/skills/hardware-doc-ingest/scripts/verify_doc_locks.ts +1 -1
  67. package/toolkit/skills/multi-task-flow/dist/app.js +66 -66
  68. package/toolkit/skills/multi-task-flow/lib/view.ts +6 -2
  69. package/toolkit/skills/multi-task-flow/web/app.ts +2 -2
  70. package/toolkit/skills/multi-task-flow/web/graph-layout.ts +3 -3
  71. package/toolkit/skills/source-manifest.json +315 -11
  72. package/toolkit/skills/spec-sync/SKILL.md +1 -1
  73. package/toolkit/skills/spec-sync/scripts/_runtime/compatibility_cli.ts +12 -0
  74. package/toolkit/skills/spec-sync/scripts/_runtime/compatibility_docs.ts +115 -0
  75. package/toolkit/skills/spec-sync/scripts/_runtime/compatibility_shared.ts +230 -0
  76. package/toolkit/skills/spec-sync/scripts/_runtime/modules/checks/replay.ts +336 -0
  77. package/toolkit/skills/spec-sync/scripts/_runtime/modules/docs/git.ts +54 -0
  78. package/toolkit/skills/spec-sync/scripts/_runtime/modules/docs/hardware_ingest.ts +377 -0
  79. package/toolkit/skills/spec-sync/scripts/_runtime/modules/docs/hardware_locks.ts +181 -0
  80. package/toolkit/skills/spec-sync/scripts/_runtime/modules/docs/legacy_spec_id.ts +131 -0
  81. package/toolkit/skills/spec-sync/scripts/_runtime/modules/docs/migration_common.ts +291 -0
  82. package/toolkit/skills/spec-sync/scripts/_runtime/modules/docs/next_plan.ts +199 -0
  83. package/toolkit/skills/spec-sync/scripts/_runtime/modules/docs/next_plan_support.ts +245 -0
  84. package/toolkit/skills/spec-sync/scripts/_runtime/modules/docs/spec_contract.ts +299 -0
  85. package/toolkit/skills/spec-sync/scripts/_runtime/modules/docs/spec_drift.ts +387 -0
  86. package/toolkit/skills/spec-sync/scripts/_runtime/modules/docs/spec_drift_helpers.ts +381 -0
  87. package/toolkit/skills/spec-sync/scripts/_runtime/modules/docs/spec_drift_legacy.ts +376 -0
  88. package/toolkit/skills/spec-sync/scripts/_runtime/modules/docs/spec_drift_paths.ts +3 -0
  89. package/toolkit/skills/spec-sync/scripts/_runtime/modules/docs/spec_drift_types.ts +27 -0
  90. package/toolkit/skills/spec-sync/scripts/_runtime/modules/docs/spec_drift_validation.ts +251 -0
  91. package/toolkit/skills/spec-sync/scripts/_runtime/modules/docs/spec_migration.ts +191 -0
  92. package/toolkit/skills/spec-sync/scripts/spec_drift_check.ts +1 -1
  93. package/toolkit/skills/support-manifest.json +77 -1
@@ -0,0 +1,387 @@
1
+ import { existsSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import { gitText, runGit } from "./git";
4
+ import {
5
+ isImplementationPath,
6
+ pathCountsAsSpecSync,
7
+ requiredSpecFiles,
8
+ resolveAssociatedSpecPath,
9
+ specAllowsLegacyRelatedAdrsNone,
10
+ sourceRequiresRelatedAdrs,
11
+ specTargetFromChangedPath,
12
+ validateRelatedAdrs,
13
+ type DriftPathContext,
14
+ } from "./spec_drift_paths";
15
+ import {
16
+ collectChanges,
17
+ commitCoverage,
18
+ evidence,
19
+ frozenAdrCheck,
20
+ normalizeRepoPath,
21
+ relatedAdrTargets,
22
+ resolveBase,
23
+ result,
24
+ strictCheck,
25
+ } from "./spec_drift_helpers";
26
+ import type { DriftResult } from "./spec_drift_types";
27
+ export type { DriftResult } from "./spec_drift_types";
28
+
29
+ export interface DriftOptions {
30
+ repoRoot?: string;
31
+ cwd?: string;
32
+ baseRef?: string;
33
+ specPaths?: string[];
34
+ specFree?: boolean;
35
+ env?: Record<string, string | undefined>;
36
+ }
37
+
38
+ function specPaths(options: DriftOptions): string[] {
39
+ return options.specPaths ?? [];
40
+ }
41
+
42
+ export function runSpecDrift(options: DriftOptions = {}): DriftResult {
43
+ const cwd = options.cwd ?? process.cwd();
44
+ const repoRoot =
45
+ options.repoRoot ?? gitText(cwd, ["rev-parse", "--show-toplevel"])?.trim();
46
+ if (!repoRoot)
47
+ return result(3, "未检查", "未检查", "未检查", "not in git repository");
48
+ const baseRef = resolveBase(repoRoot, options.baseRef);
49
+ if (!baseRef)
50
+ return result(3, "未检查", "未检查", "未检查", "base ref not resolvable");
51
+ if (runGit(repoRoot, ["merge-base", baseRef, "HEAD"]).exitCode !== 0)
52
+ return result(
53
+ 3,
54
+ "未检查",
55
+ "未检查",
56
+ "未检查",
57
+ `no merge base between ${baseRef} and HEAD`,
58
+ );
59
+ const changes = collectChanges(repoRoot, baseRef);
60
+ const context: DriftPathContext = {
61
+ repoRoot,
62
+ baseRef,
63
+ changedFiles: changes.changed,
64
+ deletedFiles: changes.deleted,
65
+ renamePairs: changes.renames,
66
+ };
67
+ const requested = specPaths(options);
68
+ const requestedNormalized = requested
69
+ .map((path) => normalizeRepoPath(repoRoot, path))
70
+ .filter((path): path is string => path !== null);
71
+ const specFree =
72
+ options.specFree || options.env?.SPEC_DISPOSITION === "not_needed";
73
+ const frozenError = frozenAdrCheck(
74
+ context,
75
+ requestedNormalized,
76
+ options.env?.ADR_REFS ?? process.env.ADR_REFS,
77
+ );
78
+ if (frozenError) return result(2, "阻断", "存在", "ADR_REFS", frozenError);
79
+ if (!changes.changed.length)
80
+ return result(0, "通过", "不存在", "未检查", "no changes in diff");
81
+ const specChanges = changes.changed.filter(
82
+ (path) => path.startsWith("docs/specs/") || path.startsWith("docs/plan/"),
83
+ );
84
+ const implementationChanged = changes.changed.some(isImplementationPath);
85
+ if (specFree && specChanges.length)
86
+ return result(
87
+ 2,
88
+ "阻断",
89
+ "存在",
90
+ evidence(repoRoot, specChanges),
91
+ "spec-free run touched spec files",
92
+ );
93
+ if (!implementationChanged) {
94
+ const checked = new Set<string>();
95
+ let incomplete = false;
96
+ const incompleteEvidence: string[] = [];
97
+ let relatedError: string | null = null;
98
+ for (const changed of specChanges) {
99
+ const target = specTargetFromChangedPath(changed);
100
+ if (!target || checked.has(target)) continue;
101
+ checked.add(target);
102
+ const resolved = resolveAssociatedSpecPath(context, target);
103
+ if (resolved === "ambiguous")
104
+ return result(
105
+ 3,
106
+ "未检查",
107
+ "未检查",
108
+ "未检查",
109
+ "ambiguous legacy spec mapping; provide canonical --spec-path",
110
+ );
111
+ const normalized = resolved ?? target;
112
+ for (const required of requiredSpecFiles(context, normalized))
113
+ if (!existsSync(resolve(repoRoot, required))) {
114
+ incomplete = true;
115
+ incompleteEvidence.push(required);
116
+ }
117
+ const related = validateRelatedAdrs(
118
+ context,
119
+ normalized,
120
+ sourceRequiresRelatedAdrs(context, changed, normalized),
121
+ specAllowsLegacyRelatedAdrsNone(context, normalized),
122
+ );
123
+ if (!related.ok)
124
+ relatedError = `invalid Related ADRs in ${normalized}: ${related.error}`;
125
+ const strictError = strictCheck(repoRoot, normalized);
126
+ if (strictError)
127
+ return result(
128
+ 2,
129
+ "阻断",
130
+ "存在",
131
+ evidence(repoRoot, [normalized]),
132
+ strictError,
133
+ );
134
+ }
135
+ for (const requestedPath of requestedNormalized) {
136
+ const resolved = resolveAssociatedSpecPath(context, requestedPath);
137
+ if (resolved === "ambiguous")
138
+ return result(
139
+ 3,
140
+ "未检查",
141
+ "未检查",
142
+ "未检查",
143
+ "ambiguous legacy spec mapping; provide canonical --spec-path",
144
+ );
145
+ if (!resolved) continue;
146
+ const related = validateRelatedAdrs(
147
+ context,
148
+ resolved,
149
+ sourceRequiresRelatedAdrs(context, requestedPath, resolved),
150
+ specAllowsLegacyRelatedAdrsNone(context, resolved),
151
+ );
152
+ if (!related.ok)
153
+ relatedError = `invalid Related ADRs in ${resolved}: ${related.error}`;
154
+ }
155
+ for (const target of relatedAdrTargets(
156
+ repoRoot,
157
+ changes.changed.filter((path) => /^docs\/adr\//.test(path)),
158
+ )) {
159
+ const related = validateRelatedAdrs(context, target, false, false);
160
+ if (!related.ok)
161
+ relatedError = `invalid Related ADRs in ${target}: ${related.error}`;
162
+ }
163
+ if (incomplete)
164
+ return result(
165
+ 2,
166
+ "阻断",
167
+ "存在",
168
+ evidence(repoRoot, [...specChanges, ...incompleteEvidence]),
169
+ "changed spec directories are incomplete",
170
+ );
171
+ if (relatedError)
172
+ return result(
173
+ 2,
174
+ "阻断",
175
+ "存在",
176
+ evidence(repoRoot, specChanges),
177
+ relatedError,
178
+ );
179
+ return result(
180
+ 0,
181
+ "通过",
182
+ "不存在",
183
+ specChanges.length ? evidence(repoRoot, specChanges) : "未检查",
184
+ "docs/workflow-only changes",
185
+ );
186
+ }
187
+ if (specFree && requestedNormalized.length === 0)
188
+ return result(
189
+ 0,
190
+ "通过",
191
+ "不存在",
192
+ "none(spec-free)",
193
+ "spec-free run (spec_disposition=not_needed)",
194
+ );
195
+ if (requestedNormalized.length === 0)
196
+ return result(
197
+ 3,
198
+ "未检查",
199
+ "未检查",
200
+ specChanges.length ? evidence(repoRoot, specChanges) : "未检查",
201
+ "impl changes found but no SPEC_PATHS provided",
202
+ );
203
+ const normalized: string[] = [];
204
+ for (const relativeRequested of requestedNormalized) {
205
+ const resolved = resolveAssociatedSpecPath(context, relativeRequested);
206
+ if (resolved === "ambiguous")
207
+ return result(
208
+ 3,
209
+ "未检查",
210
+ "未检查",
211
+ "未检查",
212
+ "ambiguous legacy spec mapping; provide canonical --spec-path",
213
+ );
214
+ if (resolved && !normalized.includes(resolved)) normalized.push(resolved);
215
+ }
216
+ if (!normalized.length)
217
+ return result(
218
+ 3,
219
+ "未检查",
220
+ "未检查",
221
+ "未检查",
222
+ "no valid SPEC_PATHS after normalization",
223
+ );
224
+ const specEvidence: string[] = [];
225
+ const worktreeSpec = new Set<string>();
226
+ let incomplete = false;
227
+ let tracked = false;
228
+ let associatedDeleted = false;
229
+ for (const spec of normalized) {
230
+ const root = dirname(spec);
231
+ for (const required of requiredSpecFiles(context, spec))
232
+ if (!existsSync(resolve(repoRoot, required))) {
233
+ incomplete = true;
234
+ specEvidence.push(required);
235
+ }
236
+ for (const changed of changes.changed)
237
+ if (pathCountsAsSpecSync(changed, root, spec)) {
238
+ tracked = true;
239
+ specEvidence.push(changed);
240
+ if (changes.deleted.includes(changed) && changed === spec)
241
+ associatedDeleted = true;
242
+ }
243
+ if (
244
+ changes.worktreeChanged.some((path) =>
245
+ pathCountsAsSpecSync(path, root, spec),
246
+ )
247
+ )
248
+ worktreeSpec.add(spec);
249
+ const related = validateRelatedAdrs(
250
+ context,
251
+ spec,
252
+ sourceRequiresRelatedAdrs(context, spec, spec),
253
+ specAllowsLegacyRelatedAdrsNone(context, spec),
254
+ );
255
+ if (!related.ok)
256
+ return result(
257
+ 2,
258
+ "阻断",
259
+ "存在",
260
+ evidence(repoRoot, [spec]),
261
+ `invalid Related ADRs in ${spec}: ${related.error}`,
262
+ );
263
+ const strictError = strictCheck(repoRoot, spec);
264
+ if (strictError)
265
+ return result(2, "阻断", "存在", evidence(repoRoot, [spec]), strictError);
266
+ }
267
+ const validated = new Set(normalized);
268
+ for (const changed of specChanges) {
269
+ const target = specTargetFromChangedPath(changed);
270
+ if (!target || validated.has(target)) continue;
271
+ const related = validateRelatedAdrs(
272
+ context,
273
+ target,
274
+ sourceRequiresRelatedAdrs(context, changed, target),
275
+ specAllowsLegacyRelatedAdrsNone(context, target),
276
+ );
277
+ if (!related.ok)
278
+ return result(
279
+ 2,
280
+ "阻断",
281
+ "存在",
282
+ evidence(repoRoot, [target]),
283
+ `invalid Related ADRs in ${target}: ${related.error}`,
284
+ );
285
+ validated.add(target);
286
+ }
287
+ for (const target of relatedAdrTargets(
288
+ repoRoot,
289
+ changes.changed.filter((path) => /^docs\/adr\//.test(path)),
290
+ )) {
291
+ const related = validateRelatedAdrs(
292
+ context,
293
+ target,
294
+ false,
295
+ specAllowsLegacyRelatedAdrsNone(context, target),
296
+ );
297
+ if (!related.ok)
298
+ return result(
299
+ 2,
300
+ "阻断",
301
+ "存在",
302
+ evidence(repoRoot, [target]),
303
+ `invalid Related ADRs in ${target}: ${related.error}`,
304
+ );
305
+ }
306
+ if (incomplete || associatedDeleted)
307
+ return result(
308
+ 2,
309
+ "阻断",
310
+ "存在",
311
+ evidence(repoRoot, specEvidence),
312
+ associatedDeleted
313
+ ? "associated spec was deleted"
314
+ : "associated spec files are missing",
315
+ );
316
+ if (
317
+ implementationChanged &&
318
+ changes.worktreeChanged.some(isImplementationPath) &&
319
+ worktreeSpec.size === 0
320
+ )
321
+ return result(
322
+ 2,
323
+ "阻断",
324
+ "存在",
325
+ "未检查",
326
+ "worktree implementation changes without associated spec sync",
327
+ );
328
+ if (!tracked && worktreeSpec.size === 0)
329
+ return result(
330
+ 2,
331
+ "阻断",
332
+ "存在",
333
+ "未检查",
334
+ "implementation changes without spec sync",
335
+ );
336
+ const coverage = commitCoverage(context, normalized, worktreeSpec);
337
+ if (coverage)
338
+ return result(
339
+ 2,
340
+ "阻断",
341
+ "存在",
342
+ evidence(repoRoot, specEvidence),
343
+ coverage,
344
+ );
345
+ return result(
346
+ 0,
347
+ "通过",
348
+ "不存在",
349
+ evidence(repoRoot, specEvidence),
350
+ "associated spec updated with implementation changes",
351
+ );
352
+ }
353
+
354
+ export function parseSpecDriftArgs(args: string[]): {
355
+ options?: DriftOptions;
356
+ help?: boolean;
357
+ error?: string;
358
+ } {
359
+ const options: DriftOptions = { specPaths: [] };
360
+ for (let index = 0; index < args.length; index += 1) {
361
+ const arg = args[index];
362
+ if (arg === "--base-ref") {
363
+ const value = args[index + 1];
364
+ if (!value) return { error: "missing value for --base-ref" };
365
+ options.baseRef = value;
366
+ index += 1;
367
+ continue;
368
+ }
369
+ if (arg === "--spec-path") {
370
+ const value = args[index + 1];
371
+ if (!value) return { error: "missing value for --spec-path" };
372
+ options.specPaths?.push(value);
373
+ index += 1;
374
+ continue;
375
+ }
376
+ if (arg === "--spec-free") {
377
+ options.specFree = true;
378
+ continue;
379
+ }
380
+ if (arg === "-h" || arg === "--help") return { help: true };
381
+ return { error: `unknown argument: ${arg}` };
382
+ }
383
+ return { options };
384
+ }
385
+
386
+ export const SPEC_DRIFT_USAGE =
387
+ "Usage: spec_drift_check.ts [--base-ref <ref>] [--spec-path <path>]... [--spec-free]";