tlc-claude-code 2.7.0 → 2.9.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/.claude/commands/tlc/audit.md +12 -0
- package/.claude/commands/tlc/autofix.md +12 -0
- package/.claude/commands/tlc/build.md +35 -4
- package/.claude/commands/tlc/cleanup.md +13 -1
- package/.claude/commands/tlc/coverage.md +12 -0
- package/.claude/commands/tlc/discuss.md +12 -0
- package/.claude/commands/tlc/docs.md +13 -1
- package/.claude/commands/tlc/edge-cases.md +13 -1
- package/.claude/commands/tlc/plan.md +32 -6
- package/.claude/commands/tlc/preflight.md +12 -0
- package/.claude/commands/tlc/progress.md +41 -15
- package/.claude/commands/tlc/refactor.md +12 -0
- package/.claude/commands/tlc/review-pr.md +32 -11
- package/.claude/commands/tlc/review.md +12 -0
- package/.claude/commands/tlc/security.md +13 -1
- package/.claude/commands/tlc/status.md +42 -3
- package/.claude/commands/tlc/tlc.md +32 -16
- package/.claude/commands/tlc/verify.md +12 -0
- package/.claude/commands/tlc/watchci.md +12 -0
- package/package.json +1 -1
- package/scripts/renumber-phases.js +283 -0
- package/scripts/renumber-phases.test.js +305 -0
- package/server/lib/orchestration/completion-checker.js +52 -2
- package/server/lib/orchestration/completion-checker.test.js +64 -0
- package/server/lib/orchestration/session-status.js +28 -4
- package/server/lib/orchestration/session-status.test.js +44 -1
- package/server/lib/orchestration/skill-dispatcher.js +270 -0
- package/server/lib/orchestration/skill-dispatcher.test.js +449 -0
- package/server/lib/workspace-manifest.js +138 -0
- package/server/lib/workspace-manifest.test.js +179 -0
|
@@ -198,6 +198,14 @@ If no roadmap exists:
|
|
|
198
198
|
|
|
199
199
|
### Step 2: Find The Active Phase
|
|
200
200
|
|
|
201
|
+
Before matching phase artifacts, determine the current repo prefix:
|
|
202
|
+
|
|
203
|
+
1. Try workspace-aware discovery first:
|
|
204
|
+
- Load `/workspace/Tools/.tlc-workspace.json` when available, or discover the nearest manifest via `server/lib/workspace-manifest.js`
|
|
205
|
+
- Use the current repo path plus the manifest to resolve the repo prefix (`TLC`, `CORE`, `SA`, etc.)
|
|
206
|
+
2. If no manifest is available, scan `.planning/phases/` for existing prefixed artifacts such as `TLC-108-PLAN.md` and infer the current repo prefix from those filenames
|
|
207
|
+
3. If neither manifest nor prefixed files are available, fall back to legacy unprefixed detection
|
|
208
|
+
|
|
201
209
|
From the roadmap, identify:
|
|
202
210
|
|
|
203
211
|
- The current phase marked as in progress, current, or active
|
|
@@ -206,18 +214,22 @@ From the roadmap, identify:
|
|
|
206
214
|
|
|
207
215
|
Extract:
|
|
208
216
|
|
|
209
|
-
- Phase number
|
|
217
|
+
- Phase number / phase ID
|
|
210
218
|
- Phase name
|
|
211
219
|
- Task count if the roadmap or plan exposes it
|
|
212
220
|
- Completed task count if available
|
|
213
221
|
|
|
214
222
|
### Step 3: Check Artifacts In The Same Pass
|
|
215
223
|
|
|
216
|
-
For the active phase `N
|
|
224
|
+
For the active phase, build the preferred prefixed ID `{PREFIX}-{N}` when a prefix is known. Check artifacts in this order:
|
|
217
225
|
|
|
218
|
-
- Discussion: `.planning/phases/{N}-DISCUSSION.md`
|
|
219
|
-
- Plan: `.planning/phases/{N}-PLAN.md`
|
|
220
|
-
- Verification: `.planning/phases/{N}-VERIFIED.md`
|
|
226
|
+
- Discussion: `.planning/phases/{PREFIX}-{N}-DISCUSSION.md`, then legacy `.planning/phases/{N}-DISCUSSION.md`
|
|
227
|
+
- Plan: `.planning/phases/{PREFIX}-{N}-PLAN.md`, then legacy `.planning/phases/{N}-PLAN.md`
|
|
228
|
+
- Verification: `.planning/phases/{PREFIX}-{N}-VERIFIED.md`, then legacy `.planning/phases/{N}-VERIFIED.md`
|
|
229
|
+
|
|
230
|
+
Phase file pattern matching must accept both:
|
|
231
|
+
- Prefixed: `{PREFIX}-{N}-PLAN.md`, `{PREFIX}-{N}-DISCUSSION.md`, `{PREFIX}-{N}-VERIFIED.md`
|
|
232
|
+
- Legacy: `{N}-PLAN.md`, `{N}-DISCUSSION.md`, `{N}-VERIFIED.md`
|
|
221
233
|
|
|
222
234
|
If the plan exists, inspect it to determine build state:
|
|
223
235
|
|
|
@@ -228,16 +240,20 @@ If the plan exists, inspect it to determine build state:
|
|
|
228
240
|
Use these state rules:
|
|
229
241
|
|
|
230
242
|
1. No discussion file:
|
|
231
|
-
Run `/tlc:discuss {
|
|
243
|
+
Run `/tlc:discuss {PHASE_ID}`
|
|
232
244
|
2. Discussion exists, no plan:
|
|
233
|
-
Run `/tlc:plan {
|
|
245
|
+
Run `/tlc:plan {PHASE_ID}`
|
|
234
246
|
3. Plan exists and phase is not fully built:
|
|
235
|
-
Run `/tlc:build {
|
|
247
|
+
Run `/tlc:build {PHASE_ID}`
|
|
236
248
|
4. Build complete, no verification file:
|
|
237
|
-
Run `/tlc:verify {
|
|
249
|
+
Run `/tlc:verify {PHASE_ID}`
|
|
238
250
|
5. Verification exists or all phases are complete:
|
|
239
251
|
Suggest release, but do not auto-run it
|
|
240
252
|
|
|
253
|
+
Backward compatibility:
|
|
254
|
+
- If prefixed artifacts exist, prefer them everywhere in status output and auto-run commands
|
|
255
|
+
- If no prefixed artifacts exist and no workspace manifest is found, continue using legacy unprefixed phase IDs
|
|
256
|
+
|
|
241
257
|
## Output Format
|
|
242
258
|
|
|
243
259
|
Normal output must stay within 3-5 lines.
|
|
@@ -245,9 +261,9 @@ Normal output must stay within 3-5 lines.
|
|
|
245
261
|
Use this pattern:
|
|
246
262
|
|
|
247
263
|
```text
|
|
248
|
-
TLC v{version} | Phase {
|
|
264
|
+
TLC v{version} | Phase {PHASE_ID}: {Name} | {done}/{total} tasks done
|
|
249
265
|
Next: {action summary}
|
|
250
|
-
Running /tlc:{command} {
|
|
266
|
+
Running /tlc:{command} {PHASE_ID}...
|
|
251
267
|
```
|
|
252
268
|
|
|
253
269
|
If there is nothing safe to auto-run:
|
|
@@ -288,7 +304,7 @@ Auto-run:
|
|
|
288
304
|
Auto-run:
|
|
289
305
|
|
|
290
306
|
```text
|
|
291
|
-
/tlc:plan {
|
|
307
|
+
/tlc:plan {PHASE_ID}
|
|
292
308
|
```
|
|
293
309
|
|
|
294
310
|
### Planned, Not Built
|
|
@@ -296,7 +312,7 @@ Auto-run:
|
|
|
296
312
|
Auto-run:
|
|
297
313
|
|
|
298
314
|
```text
|
|
299
|
-
/tlc:build {
|
|
315
|
+
/tlc:build {PHASE_ID}
|
|
300
316
|
```
|
|
301
317
|
|
|
302
318
|
Use the next incomplete task from the plan in the status line when available.
|
|
@@ -306,7 +322,7 @@ Use the next incomplete task from the plan in the status line when available.
|
|
|
306
322
|
Auto-run:
|
|
307
323
|
|
|
308
324
|
```text
|
|
309
|
-
/tlc:verify {
|
|
325
|
+
/tlc:verify {PHASE_ID}
|
|
310
326
|
```
|
|
311
327
|
|
|
312
328
|
### All Complete
|
|
@@ -322,9 +338,9 @@ Run /tlc:complete or /tlc:new-milestone
|
|
|
322
338
|
## Example
|
|
323
339
|
|
|
324
340
|
```text
|
|
325
|
-
TLC v2.4.2 | Phase 8: Auth System | 3/5 tasks done
|
|
341
|
+
TLC v2.4.2 | Phase TLC-8: Auth System | 3/5 tasks done
|
|
326
342
|
Next: build task 4 (JWT middleware)
|
|
327
|
-
Running /tlc:build 8...
|
|
343
|
+
Running /tlc:build TLC-8...
|
|
328
344
|
```
|
|
329
345
|
|
|
330
346
|
## Summary
|
|
@@ -17,6 +17,18 @@ Verify the phase automatically from its plan acceptance criteria, then leave onl
|
|
|
17
17
|
|
|
18
18
|
If no phase number, auto-detect current phase.
|
|
19
19
|
|
|
20
|
+
## Background Dispatch
|
|
21
|
+
|
|
22
|
+
When the orchestrator is available (`curl -sf http://localhost:3100/health`), dispatch this command to a background agent session instead of running inline.
|
|
23
|
+
|
|
24
|
+
1. Check orchestrator health
|
|
25
|
+
2. If available: package the full skill prompt with project context (branch, changed files, current phase) and dispatch via `skill-dispatcher.dispatch({ skill: 'verify', prompt, project })`
|
|
26
|
+
3. Report to user: "Dispatched verify to background session ses_XXXX. Results will appear when complete."
|
|
27
|
+
4. When session completes, capture result via `skill-dispatcher.captureResult()` and display summary
|
|
28
|
+
5. If orchestrator unavailable, fall back to inline execution (existing behavior below)
|
|
29
|
+
|
|
30
|
+
Override: `--inline` flag forces inline execution even when orchestrator is available.
|
|
31
|
+
|
|
20
32
|
## Process
|
|
21
33
|
|
|
22
34
|
### Step 1: Load the Phase Plan
|
|
@@ -19,6 +19,18 @@ This is the "I pushed, now babysit CI for me" command.
|
|
|
19
19
|
/tlc:watchci 3 # max 3 fix attempts (default: 5)
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
+
## Background Dispatch
|
|
23
|
+
|
|
24
|
+
When the orchestrator is available (`curl -sf http://localhost:3100/health`), dispatch this command to a background agent session instead of running inline.
|
|
25
|
+
|
|
26
|
+
1. Check orchestrator health
|
|
27
|
+
2. If available: package the full skill prompt with project context (branch, changed files, current phase) and dispatch via `skill-dispatcher.dispatch({ skill: 'watchci', prompt, project })`
|
|
28
|
+
3. Report to user: "Dispatched watchci to background session ses_XXXX. Results will appear when complete."
|
|
29
|
+
4. When session completes, capture result via `skill-dispatcher.captureResult()` and display summary
|
|
30
|
+
5. If orchestrator unavailable, fall back to inline execution (existing behavior below)
|
|
31
|
+
|
|
32
|
+
Override: `--inline` flag forces inline execution even when orchestrator is available.
|
|
33
|
+
|
|
22
34
|
## Process
|
|
23
35
|
|
|
24
36
|
### Step 1: Find the Active Run
|
package/package.json
CHANGED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
const ALLOWED_SUFFIXES = new Set([
|
|
5
|
+
'PLAN',
|
|
6
|
+
'DISCUSSION',
|
|
7
|
+
'TESTS',
|
|
8
|
+
'VERIFIED',
|
|
9
|
+
'TEST-PLAN',
|
|
10
|
+
'ARCHITECTURE',
|
|
11
|
+
'COMPLETION-PLAN',
|
|
12
|
+
'NOTE',
|
|
13
|
+
'AUDIT-REPORT',
|
|
14
|
+
'RESEARCH',
|
|
15
|
+
'DISCUSSION-addendum',
|
|
16
|
+
]);
|
|
17
|
+
|
|
18
|
+
const PHASE_FILE_PATTERN = /^([A-Za-z0-9]+)-([A-Za-z-]+)\.md(\.superseded)?$/;
|
|
19
|
+
|
|
20
|
+
function printUsage(stream = process.stderr) {
|
|
21
|
+
stream.write(
|
|
22
|
+
'Usage: node scripts/renumber-phases.js --repo-path <path> --prefix <PREFIX> [--dry-run]\n'
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function parseArgs(argv) {
|
|
27
|
+
const options = {
|
|
28
|
+
dryRun: false,
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
32
|
+
const arg = argv[index];
|
|
33
|
+
|
|
34
|
+
if (arg === '--dry-run') {
|
|
35
|
+
options.dryRun = true;
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (arg === '--repo-path' || arg === '--prefix') {
|
|
40
|
+
const value = argv[index + 1];
|
|
41
|
+
if (!value || value.startsWith('--')) {
|
|
42
|
+
throw new Error(`Missing value for ${arg}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (arg === '--repo-path') {
|
|
46
|
+
options.repoPath = value;
|
|
47
|
+
} else {
|
|
48
|
+
options.prefix = value;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
index += 1;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
throw new Error(`Unknown argument: ${arg}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!options.repoPath || !options.prefix) {
|
|
59
|
+
throw new Error('Missing required arguments');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return options;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function escapeRegExp(value) {
|
|
66
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function createPhaseReferenceRegex(prefix) {
|
|
70
|
+
return new RegExp(`\\bPhase\\s+(${escapeRegExp(prefix)}-)?(\\d+[A-Za-z]?)\\b`, 'g');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function updatePhaseReferences(content, prefix) {
|
|
74
|
+
const regex = createPhaseReferenceRegex(prefix);
|
|
75
|
+
let replacements = 0;
|
|
76
|
+
|
|
77
|
+
const updated = content.replace(regex, (match, existingPrefix, phaseId) => {
|
|
78
|
+
if (existingPrefix) {
|
|
79
|
+
return match;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
replacements += 1;
|
|
83
|
+
return `Phase ${prefix}-${phaseId}`;
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
content: updated,
|
|
88
|
+
replacements,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function isEligiblePhaseFile(name, prefix) {
|
|
93
|
+
const match = name.match(PHASE_FILE_PATTERN);
|
|
94
|
+
if (!match) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const [, phaseId, suffix, supersededExtension = ''] = match;
|
|
99
|
+
|
|
100
|
+
if (phaseId.startsWith(`${prefix}-`)) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!/^\d+[A-Za-z]?$/.test(phaseId)) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (!ALLOWED_SUFFIXES.has(suffix)) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
phaseId,
|
|
114
|
+
suffix,
|
|
115
|
+
supersededExtension,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function collectOperations(repoPath, prefix) {
|
|
120
|
+
const phasesDir = path.join(repoPath, '.planning', 'phases');
|
|
121
|
+
if (!fs.existsSync(phasesDir) || !fs.statSync(phasesDir).isDirectory()) {
|
|
122
|
+
throw new Error(`Missing phases directory: ${phasesDir}`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const roadmapPath = path.join(repoPath, '.planning', 'ROADMAP.md');
|
|
126
|
+
const operations = [];
|
|
127
|
+
const errors = [];
|
|
128
|
+
|
|
129
|
+
for (const entry of fs.readdirSync(phasesDir, { withFileTypes: true })) {
|
|
130
|
+
if (!entry.isFile()) {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const fileInfo = isEligiblePhaseFile(entry.name, prefix);
|
|
135
|
+
if (!fileInfo) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const nextName = `${prefix}-${fileInfo.phaseId}-${fileInfo.suffix}.md${fileInfo.supersededExtension}`;
|
|
140
|
+
operations.push({
|
|
141
|
+
type: 'rename-file',
|
|
142
|
+
from: path.join(phasesDir, entry.name),
|
|
143
|
+
to: path.join(phasesDir, nextName),
|
|
144
|
+
phaseId: fileInfo.phaseId,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (fs.existsSync(roadmapPath)) {
|
|
149
|
+
const roadmapContent = fs.readFileSync(roadmapPath, 'utf8');
|
|
150
|
+
const updatedRoadmap = updatePhaseReferences(roadmapContent, prefix);
|
|
151
|
+
|
|
152
|
+
if (updatedRoadmap.replacements > 0) {
|
|
153
|
+
operations.push({
|
|
154
|
+
type: 'update-file',
|
|
155
|
+
target: roadmapPath,
|
|
156
|
+
nextContent: updatedRoadmap.content,
|
|
157
|
+
replacements: updatedRoadmap.replacements,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
for (const renameOperation of operations.filter((operation) => operation.type === 'rename-file')) {
|
|
163
|
+
const content = fs.readFileSync(renameOperation.from, 'utf8');
|
|
164
|
+
const updated = updatePhaseReferences(content, prefix);
|
|
165
|
+
|
|
166
|
+
if (updated.replacements > 0) {
|
|
167
|
+
operations.push({
|
|
168
|
+
type: 'update-renamed-file',
|
|
169
|
+
target: renameOperation.to,
|
|
170
|
+
sourcePath: renameOperation.from,
|
|
171
|
+
nextContent: updated.content,
|
|
172
|
+
replacements: updated.replacements,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
for (const renameOperation of operations.filter((operation) => operation.type === 'rename-file')) {
|
|
178
|
+
if (!fs.existsSync(renameOperation.to)) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
errors.push(
|
|
183
|
+
`Cannot rename ${path.basename(renameOperation.from)} because ${path.basename(renameOperation.to)} already exists`
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
operations,
|
|
189
|
+
errors,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function applyOperations(operations, dryRun) {
|
|
194
|
+
const summary = {
|
|
195
|
+
renamed: 0,
|
|
196
|
+
updated: 0,
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
for (const operation of operations) {
|
|
200
|
+
if (operation.type === 'rename-file') {
|
|
201
|
+
if (dryRun) {
|
|
202
|
+
console.log(`DRY-RUN rename ${operation.from} -> ${operation.to}`);
|
|
203
|
+
} else {
|
|
204
|
+
fs.renameSync(operation.from, operation.to);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
summary.renamed += 1;
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (operation.type === 'update-file' || operation.type === 'update-renamed-file') {
|
|
212
|
+
if (dryRun) {
|
|
213
|
+
console.log(`DRY-RUN update ${operation.target} (${operation.replacements} references)`);
|
|
214
|
+
} else {
|
|
215
|
+
fs.writeFileSync(operation.target, operation.nextContent);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
summary.updated += operation.replacements;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return summary;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function renumberPhases({ repoPath, prefix, dryRun = false }) {
|
|
226
|
+
const resolvedRepoPath = path.resolve(repoPath);
|
|
227
|
+
const trimmedPrefix = prefix.trim();
|
|
228
|
+
|
|
229
|
+
if (!trimmedPrefix) {
|
|
230
|
+
throw new Error('Prefix must not be empty');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const { operations, errors } = collectOperations(resolvedRepoPath, trimmedPrefix);
|
|
234
|
+
const summary = applyOperations(operations, dryRun);
|
|
235
|
+
|
|
236
|
+
for (const error of errors) {
|
|
237
|
+
console.error(error);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const finalSummary = {
|
|
241
|
+
renamed: summary.renamed,
|
|
242
|
+
updated: summary.updated,
|
|
243
|
+
errors: errors.length,
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
console.log(
|
|
247
|
+
`Renamed: ${finalSummary.renamed} files, Updated: ${finalSummary.updated} references, Errors: ${finalSummary.errors}`
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
return finalSummary;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function main(argv = process.argv.slice(2)) {
|
|
254
|
+
try {
|
|
255
|
+
const options = parseArgs(argv);
|
|
256
|
+
renumberPhases(options);
|
|
257
|
+
return 0;
|
|
258
|
+
} catch (error) {
|
|
259
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
260
|
+
|
|
261
|
+
if (message.startsWith('Missing required arguments') || message.startsWith('Missing value for')) {
|
|
262
|
+
printUsage(process.stderr);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
console.error(message);
|
|
266
|
+
return 1;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (require.main === module) {
|
|
271
|
+
process.exit(main());
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
module.exports = {
|
|
275
|
+
ALLOWED_SUFFIXES,
|
|
276
|
+
PHASE_FILE_PATTERN,
|
|
277
|
+
collectOperations,
|
|
278
|
+
main,
|
|
279
|
+
parseArgs,
|
|
280
|
+
printUsage,
|
|
281
|
+
renumberPhases,
|
|
282
|
+
updatePhaseReferences,
|
|
283
|
+
};
|