godpowers 3.11.0 → 3.13.1

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.
@@ -25,9 +25,74 @@ const COMMANDS = new Set([
25
25
  'import-ledger'
26
26
  ]);
27
27
 
28
- function parseArgs(argv, cwd = process.cwd()) {
29
- const args = argv.slice(2);
30
- const opts = {
28
+ // Per-command positional slots, filled in order into the first empty slot.
29
+ const POSITIONALS = {
30
+ state: ['stateAction'],
31
+ verify: ['verifyCommand'],
32
+ route: ['routePrompt'],
33
+ memory: ['memoryAction', 'memoryKey', 'memoryValue'],
34
+ lesson: ['lessonAction', 'lessonText'],
35
+ outcome: ['outcomeAction', 'outcomeSlug']
36
+ };
37
+
38
+ // Boolean flags (and their aliases) -> opts field set to true.
39
+ const BOOL_FLAGS = {
40
+ '--json': 'json',
41
+ '--brief': 'brief',
42
+ '--full': 'full',
43
+ '--apply': 'apply',
44
+ '--dry-run': 'dryRun',
45
+ '--attest': 'attest',
46
+ '--peek': 'peek',
47
+ '--all': 'all',
48
+ '-g': 'global',
49
+ '--global': 'global',
50
+ '-l': 'local',
51
+ '--local': 'local',
52
+ '-h': 'help',
53
+ '--help': 'help',
54
+ '-u': 'uninstall',
55
+ '--uninstall': 'uninstall'
56
+ };
57
+
58
+ // Value flags -> opts field. Each accepts both "--flag value" and "--flag=value".
59
+ const VALUE_FLAGS = {
60
+ '--tier': 'tier',
61
+ '--step': 'step',
62
+ '--status': 'status',
63
+ '--substep': 'substep',
64
+ '--claim': 'claim',
65
+ '--timeout': 'timeout',
66
+ '--evidence': 'evidence',
67
+ '--since': 'since',
68
+ '--action': 'reflectAction',
69
+ '--outcome': 'outcome',
70
+ '--observation': 'observation',
71
+ '--root-cause': 'rootCause',
72
+ '--next': 'nextAction',
73
+ '--lesson': 'lesson',
74
+ '--category': 'category',
75
+ '--tags': 'tags',
76
+ '--scope': 'scope',
77
+ '--goal': 'outcomeGoal',
78
+ '--verify': 'outcomeVerify',
79
+ '--budget': 'budget',
80
+ '--reason': 'reason',
81
+ '--from': 'importFrom',
82
+ '--profile': 'profile',
83
+ '--project': 'project',
84
+ '--name': 'extensionName',
85
+ '--output': 'extensionOutput',
86
+ '--skill': 'extensionSkill',
87
+ '--agent': 'extensionAgent',
88
+ '--workflow': 'extensionWorkflow'
89
+ };
90
+
91
+ // Value flags whose value is resolved to an absolute path.
92
+ const PATH_FLAGS = new Set(['--project', '--output']);
93
+
94
+ function defaultOpts(cwd) {
95
+ return {
31
96
  command: null,
32
97
  project: cwd,
33
98
  json: false,
@@ -80,301 +145,86 @@ function parseArgs(argv, cwd = process.cwd()) {
80
145
  all: false,
81
146
  help: false,
82
147
  uninstall: false,
83
- profile: 'core',
148
+ profile: 'core'
84
149
  };
150
+ }
85
151
 
86
- for (let i = 0; i < args.length; i++) {
87
- const arg = args[i];
88
- if (COMMANDS.has(arg)) {
89
- opts.command = arg;
90
- continue;
91
- }
92
- if (opts.command === 'state' && !opts.stateAction && !arg.startsWith('-')) {
93
- opts.stateAction = arg;
94
- continue;
95
- }
96
- if (opts.command === 'verify' && opts.verifyCommand === null && !arg.startsWith('-')) {
97
- opts.verifyCommand = arg;
98
- continue;
99
- }
100
- if (opts.command === 'route' && opts.routePrompt === null && !arg.startsWith('-')) {
101
- opts.routePrompt = arg;
102
- continue;
152
+ function setValue(opts, flag, value) {
153
+ opts[VALUE_FLAGS[flag]] = PATH_FLAGS.has(flag) ? path.resolve(value) : value;
154
+ }
155
+
156
+ // Fill the first empty positional slot for the active command. Returns true when
157
+ // the arg was consumed as a positional.
158
+ function consumePositional(opts, arg) {
159
+ if (arg.startsWith('-')) return false;
160
+ const slots = POSITIONALS[opts.command];
161
+ if (!slots) return false;
162
+ for (const slot of slots) {
163
+ if (opts[slot] === null) {
164
+ opts[slot] = arg;
165
+ return true;
103
166
  }
104
- if (opts.command === 'memory' && !arg.startsWith('-')) {
105
- if (opts.memoryAction === null) { opts.memoryAction = arg; continue; }
106
- if (opts.memoryKey === null) { opts.memoryKey = arg; continue; }
107
- if (opts.memoryValue === null) { opts.memoryValue = arg; continue; }
167
+ }
168
+ return false;
169
+ }
170
+
171
+ // Handle one flag at args[i]. Returns the index to continue from (advanced past
172
+ // a consumed value for space-form flags).
173
+ function consumeFlag(opts, args, i) {
174
+ const arg = args[i];
175
+
176
+ if (arg in BOOL_FLAGS) {
177
+ opts[BOOL_FLAGS[arg]] = true;
178
+ return i;
179
+ }
180
+ if (arg === '--minimal') {
181
+ opts.profile = 'core';
182
+ return i;
183
+ }
184
+ if (arg === '--runtime') {
185
+ if (args[i + 1]) {
186
+ opts.runtimes.push(args[i + 1]);
187
+ return i + 1;
108
188
  }
109
- if (opts.command === 'lesson' && !arg.startsWith('-')) {
110
- if (opts.lessonAction === null) { opts.lessonAction = arg; continue; }
111
- if (opts.lessonText === null) { opts.lessonText = arg; continue; }
189
+ return i;
190
+ }
191
+ if (arg.startsWith('--runtime=')) {
192
+ opts.runtimes.push(arg.slice('--runtime='.length));
193
+ return i;
194
+ }
195
+ if (arg in VALUE_FLAGS) {
196
+ if (args[i + 1]) {
197
+ setValue(opts, arg, args[i + 1]);
198
+ return i + 1;
112
199
  }
113
- if (opts.command === 'outcome' && !arg.startsWith('-')) {
114
- if (opts.outcomeAction === null) { opts.outcomeAction = arg; continue; }
115
- if (opts.outcomeSlug === null) { opts.outcomeSlug = arg; continue; }
200
+ return i;
201
+ }
202
+ const eq = arg.indexOf('=');
203
+ if (arg.startsWith('--') && eq > 0) {
204
+ const name = arg.slice(0, eq);
205
+ if (name in VALUE_FLAGS) {
206
+ setValue(opts, name, arg.slice(eq + 1));
207
+ return i;
116
208
  }
209
+ }
210
+ if (arg.startsWith('--') && RUNTIMES[arg.slice(2)]) {
211
+ opts.runtimes.push(arg.slice(2));
212
+ }
213
+ return i;
214
+ }
215
+
216
+ function parseArgs(argv, cwd = process.cwd()) {
217
+ const args = argv.slice(2);
218
+ const opts = defaultOpts(cwd);
117
219
 
118
- switch (arg) {
119
- case '--json':
120
- opts.json = true;
121
- break;
122
- case '--brief':
123
- opts.brief = true;
124
- break;
125
- case '--full':
126
- opts.full = true;
127
- break;
128
- case '--apply':
129
- opts.apply = true;
130
- break;
131
- case '--dry-run':
132
- opts.dryRun = true;
133
- break;
134
- case '--runtime':
135
- if (args[i + 1]) {
136
- opts.runtimes.push(args[i + 1]);
137
- i++;
138
- }
139
- break;
140
- case '--tier':
141
- if (args[i + 1]) {
142
- opts.tier = args[i + 1];
143
- i++;
144
- }
145
- break;
146
- case '--step':
147
- if (args[i + 1]) {
148
- opts.step = args[i + 1];
149
- i++;
150
- }
151
- break;
152
- case '--status':
153
- if (args[i + 1]) {
154
- opts.status = args[i + 1];
155
- i++;
156
- }
157
- break;
158
- case '--substep':
159
- if (args[i + 1]) {
160
- opts.substep = args[i + 1];
161
- i++;
162
- }
163
- break;
164
- case '--claim':
165
- if (args[i + 1]) {
166
- opts.claim = args[i + 1];
167
- i++;
168
- }
169
- break;
170
- case '--timeout':
171
- if (args[i + 1]) {
172
- opts.timeout = args[i + 1];
173
- i++;
174
- }
175
- break;
176
- case '--evidence':
177
- if (args[i + 1]) {
178
- opts.evidence = args[i + 1];
179
- i++;
180
- }
181
- break;
182
- case '--attest':
183
- opts.attest = true;
184
- break;
185
- case '--peek':
186
- opts.peek = true;
187
- break;
188
- case '--since':
189
- if (args[i + 1]) {
190
- opts.since = args[i + 1];
191
- i++;
192
- }
193
- break;
194
- case '--action':
195
- if (args[i + 1]) {
196
- opts.reflectAction = args[i + 1];
197
- i++;
198
- }
199
- break;
200
- case '--outcome':
201
- if (args[i + 1]) {
202
- opts.outcome = args[i + 1];
203
- i++;
204
- }
205
- break;
206
- case '--observation':
207
- if (args[i + 1]) {
208
- opts.observation = args[i + 1];
209
- i++;
210
- }
211
- break;
212
- case '--root-cause':
213
- if (args[i + 1]) {
214
- opts.rootCause = args[i + 1];
215
- i++;
216
- }
217
- break;
218
- case '--next':
219
- if (args[i + 1]) {
220
- opts.nextAction = args[i + 1];
221
- i++;
222
- }
223
- break;
224
- case '--lesson':
225
- if (args[i + 1]) {
226
- opts.lesson = args[i + 1];
227
- i++;
228
- }
229
- break;
230
- case '--category':
231
- if (args[i + 1]) {
232
- opts.category = args[i + 1];
233
- i++;
234
- }
235
- break;
236
- case '--tags':
237
- if (args[i + 1]) {
238
- opts.tags = args[i + 1];
239
- i++;
240
- }
241
- break;
242
- case '--scope':
243
- if (args[i + 1]) {
244
- opts.scope = args[i + 1];
245
- i++;
246
- }
247
- break;
248
- case '--goal':
249
- if (args[i + 1]) {
250
- opts.outcomeGoal = args[i + 1];
251
- i++;
252
- }
253
- break;
254
- case '--verify':
255
- if (args[i + 1]) {
256
- opts.outcomeVerify = args[i + 1];
257
- i++;
258
- }
259
- break;
260
- case '--budget':
261
- if (args[i + 1]) {
262
- opts.budget = args[i + 1];
263
- i++;
264
- }
265
- break;
266
- case '--reason':
267
- if (args[i + 1]) {
268
- opts.reason = args[i + 1];
269
- i++;
270
- }
271
- break;
272
- case '--from':
273
- if (args[i + 1]) {
274
- opts.importFrom = args[i + 1];
275
- i++;
276
- }
277
- break;
278
- case '--project':
279
- if (args[i + 1]) {
280
- opts.project = path.resolve(args[i + 1]);
281
- i++;
282
- }
283
- break;
284
- case '-g':
285
- case '--global':
286
- opts.global = true;
287
- break;
288
- case '-l':
289
- case '--local':
290
- opts.local = true;
291
- break;
292
- case '--all':
293
- opts.all = true;
294
- break;
295
- case '--minimal':
296
- opts.profile = 'core';
297
- break;
298
- case '--profile':
299
- if (args[i + 1]) {
300
- opts.profile = args[i + 1];
301
- i++;
302
- }
303
- break;
304
- case '-h':
305
- case '--help':
306
- opts.help = true;
307
- break;
308
- case '-u':
309
- case '--uninstall':
310
- opts.uninstall = true;
311
- break;
312
- default:
313
- if (arg.startsWith('--project=')) {
314
- opts.project = path.resolve(arg.slice('--project='.length));
315
- } else if (arg.startsWith('--name=')) {
316
- opts.extensionName = arg.slice('--name='.length);
317
- } else if (arg.startsWith('--output=')) {
318
- opts.extensionOutput = path.resolve(arg.slice('--output='.length));
319
- } else if (arg.startsWith('--skill=')) {
320
- opts.extensionSkill = arg.slice('--skill='.length);
321
- } else if (arg.startsWith('--agent=')) {
322
- opts.extensionAgent = arg.slice('--agent='.length);
323
- } else if (arg.startsWith('--workflow=')) {
324
- opts.extensionWorkflow = arg.slice('--workflow='.length);
325
- } else if (arg.startsWith('--runtime=')) {
326
- opts.runtimes.push(arg.slice('--runtime='.length));
327
- } else if (arg.startsWith('--tier=')) {
328
- opts.tier = arg.slice('--tier='.length);
329
- } else if (arg.startsWith('--step=')) {
330
- opts.step = arg.slice('--step='.length);
331
- } else if (arg.startsWith('--status=')) {
332
- opts.status = arg.slice('--status='.length);
333
- } else if (arg.startsWith('--substep=')) {
334
- opts.substep = arg.slice('--substep='.length);
335
- } else if (arg.startsWith('--claim=')) {
336
- opts.claim = arg.slice('--claim='.length);
337
- } else if (arg.startsWith('--timeout=')) {
338
- opts.timeout = arg.slice('--timeout='.length);
339
- } else if (arg.startsWith('--evidence=')) {
340
- opts.evidence = arg.slice('--evidence='.length);
341
- } else if (arg.startsWith('--since=')) {
342
- opts.since = arg.slice('--since='.length);
343
- } else if (arg.startsWith('--action=')) {
344
- opts.reflectAction = arg.slice('--action='.length);
345
- } else if (arg.startsWith('--outcome=')) {
346
- opts.outcome = arg.slice('--outcome='.length);
347
- } else if (arg.startsWith('--observation=')) {
348
- opts.observation = arg.slice('--observation='.length);
349
- } else if (arg.startsWith('--root-cause=')) {
350
- opts.rootCause = arg.slice('--root-cause='.length);
351
- } else if (arg.startsWith('--next=')) {
352
- opts.nextAction = arg.slice('--next='.length);
353
- } else if (arg.startsWith('--lesson=')) {
354
- opts.lesson = arg.slice('--lesson='.length);
355
- } else if (arg.startsWith('--category=')) {
356
- opts.category = arg.slice('--category='.length);
357
- } else if (arg.startsWith('--tags=')) {
358
- opts.tags = arg.slice('--tags='.length);
359
- } else if (arg.startsWith('--scope=')) {
360
- opts.scope = arg.slice('--scope='.length);
361
- } else if (arg.startsWith('--goal=')) {
362
- opts.outcomeGoal = arg.slice('--goal='.length);
363
- } else if (arg.startsWith('--verify=')) {
364
- opts.outcomeVerify = arg.slice('--verify='.length);
365
- } else if (arg.startsWith('--budget=')) {
366
- opts.budget = arg.slice('--budget='.length);
367
- } else if (arg.startsWith('--reason=')) {
368
- opts.reason = arg.slice('--reason='.length);
369
- } else if (arg.startsWith('--from=')) {
370
- opts.importFrom = arg.slice('--from='.length);
371
- } else if (arg.startsWith('--profile=')) {
372
- opts.profile = arg.slice('--profile='.length);
373
- } else if (arg.startsWith('--') && RUNTIMES[arg.slice(2)]) {
374
- opts.runtimes.push(arg.slice(2));
375
- }
376
- break;
220
+ for (let i = 0; i < args.length; i++) {
221
+ const arg = args[i];
222
+ if (COMMANDS.has(arg)) {
223
+ opts.command = arg;
224
+ continue;
377
225
  }
226
+ if (consumePositional(opts, arg)) continue;
227
+ i = consumeFlag(opts, args, i);
378
228
  }
379
229
 
380
230
  return opts;
@@ -6,6 +6,7 @@ const { resolveRuntime } = require('./installer-runtimes');
6
6
  const { selectedSkillNames, normalizeProfiles } = require('./install-profiles');
7
7
  const identity = require('./package-identity');
8
8
  const frontmatter = require('./frontmatter');
9
+ const { log, success, error } = require('./cli-log');
9
10
 
10
11
  const VERSION = identity.PACKAGE_VERSION;
11
12
 
@@ -21,18 +22,6 @@ const VERSION = identity.PACKAGE_VERSION;
21
22
  * @property {number} agents Number of specialist agent files.
22
23
  */
23
24
 
24
- function log(msg) {
25
- console.log(` ${msg}`);
26
- }
27
-
28
- function success(msg) {
29
- console.log(` \x1b[32m+\x1b[0m ${msg}`);
30
- }
31
-
32
- function error(msg) {
33
- console.error(` \x1b[31mx\x1b[0m ${msg}`);
34
- }
35
-
36
25
  function installSkillFile(srcFile, skillsDest, runtimeKey, targetName = null) {
37
26
  const baseName = targetName || path.basename(srcFile, '.md');
38
27
  if (runtimeKey === 'codex') {
@@ -11,6 +11,7 @@ const path = require('path');
11
11
  const crypto = require('crypto');
12
12
 
13
13
  const state = require('./state');
14
+ const { exists } = require('./sync-fs');
14
15
 
15
16
  const MAX_FILE_BYTES = 80 * 1024;
16
17
  const MAX_SYSTEM_FILES = 80;
@@ -95,10 +96,6 @@ function rel(projectRoot, absPath) {
95
96
  return path.relative(projectRoot, absPath).split(path.sep).join('/');
96
97
  }
97
98
 
98
- function exists(projectRoot, relPath) {
99
- return fs.existsSync(path.join(projectRoot, relPath));
100
- }
101
-
102
99
  function ensureDir(filePath) {
103
100
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
104
101
  }
@@ -9,6 +9,7 @@ const fs = require('fs');
9
9
  const path = require('path');
10
10
 
11
11
  const recipes = require('./recipes');
12
+ const { read, write } = require('./sync-fs');
12
13
 
13
14
  const LOG_PATH = '.godpowers/surface/RECIPE-COVERAGE-SYNC.md';
14
15
 
@@ -40,17 +41,6 @@ const REQUIRED_COVERAGE = [
40
41
  }
41
42
  ];
42
43
 
43
- function read(projectRoot, relPath) {
44
- const file = path.join(projectRoot, relPath);
45
- if (!fs.existsSync(file)) return '';
46
- return fs.readFileSync(file, 'utf8');
47
- }
48
-
49
- function write(projectRoot, relPath, content) {
50
- const file = path.join(projectRoot, relPath);
51
- fs.mkdirSync(path.dirname(file), { recursive: true });
52
- fs.writeFileSync(file, content);
53
- }
54
44
 
55
45
  function addCheck(checks, id, status, relPath, message, opts = {}) {
56
46
  checks.push({
@@ -9,6 +9,8 @@
9
9
  const fs = require('fs');
10
10
  const path = require('path');
11
11
 
12
+ const { read, write, readJson } = require('./sync-fs');
13
+
12
14
  const LOG_PATH = '.godpowers/surface/RELEASE-SURFACE-SYNC.md';
13
15
 
14
16
  const REQUIRED_PACKAGE_GUARDS = [
@@ -37,26 +39,6 @@ const REQUIRED_RELEASE_TESTS = [
37
39
  'scripts/test-install-smoke.js'
38
40
  ];
39
41
 
40
- function read(projectRoot, relPath) {
41
- const file = path.join(projectRoot, relPath);
42
- if (!fs.existsSync(file)) return '';
43
- return fs.readFileSync(file, 'utf8');
44
- }
45
-
46
- function write(projectRoot, relPath, content) {
47
- const file = path.join(projectRoot, relPath);
48
- fs.mkdirSync(path.dirname(file), { recursive: true });
49
- fs.writeFileSync(file, content);
50
- }
51
-
52
- function readJson(projectRoot, relPath) {
53
- try {
54
- return JSON.parse(read(projectRoot, relPath));
55
- } catch (err) {
56
- return null;
57
- }
58
- }
59
-
60
42
  function releaseGateText(projectRoot, pkg) {
61
43
  return [
62
44
  JSON.stringify((pkg && pkg.scripts) || {}),
@@ -10,25 +10,10 @@ const path = require('path');
10
10
  const crypto = require('crypto');
11
11
 
12
12
  const pillars = require('./pillars');
13
+ const { read, write, exists } = require('./sync-fs');
13
14
 
14
15
  const LOG_PATH = '.godpowers/docs/REPO-DOC-SYNC.md';
15
16
 
16
- function read(projectRoot, relPath) {
17
- const file = path.join(projectRoot, relPath);
18
- if (!fs.existsSync(file)) return '';
19
- return fs.readFileSync(file, 'utf8');
20
- }
21
-
22
- function write(projectRoot, relPath, text) {
23
- const file = path.join(projectRoot, relPath);
24
- fs.mkdirSync(path.dirname(file), { recursive: true });
25
- fs.writeFileSync(file, text);
26
- }
27
-
28
- function exists(projectRoot, relPath) {
29
- return fs.existsSync(path.join(projectRoot, relPath));
30
- }
31
-
32
17
  function countFiles(projectRoot, dir, pattern) {
33
18
  const full = path.join(projectRoot, dir);
34
19
  if (!fs.existsSync(full)) return 0;
@@ -11,6 +11,7 @@ const fs = require('fs');
11
11
  const path = require('path');
12
12
 
13
13
  const { parseSimpleYaml } = require('./intent');
14
+ const { read, write, exists, readJson } = require('./sync-fs');
14
15
  const extensions = require('./extensions');
15
16
  const repoDocSync = require('./repo-doc-sync');
16
17
  const routeQualitySync = require('./route-quality-sync');
@@ -55,22 +56,6 @@ function rel(projectRoot, absPath) {
55
56
  return path.relative(projectRoot, absPath).split(path.sep).join('/');
56
57
  }
57
58
 
58
- function exists(projectRoot, relPath) {
59
- return fs.existsSync(path.join(projectRoot, relPath));
60
- }
61
-
62
- function read(projectRoot, relPath) {
63
- const file = path.join(projectRoot, relPath);
64
- if (!fs.existsSync(file)) return '';
65
- return fs.readFileSync(file, 'utf8');
66
- }
67
-
68
- function write(projectRoot, relPath, content) {
69
- const file = path.join(projectRoot, relPath);
70
- fs.mkdirSync(path.dirname(file), { recursive: true });
71
- fs.writeFileSync(file, content);
72
- }
73
-
74
59
  function listFiles(projectRoot, relDir, pattern) {
75
60
  const dir = path.join(projectRoot, relDir);
76
61
  if (!fs.existsSync(dir)) return [];
@@ -80,14 +65,6 @@ function listFiles(projectRoot, relDir, pattern) {
80
65
  .map((name) => `${relDir}/${name}`.replace(/\\/g, '/'));
81
66
  }
82
67
 
83
- function readJson(projectRoot, relPath) {
84
- try {
85
- return JSON.parse(read(projectRoot, relPath));
86
- } catch (err) {
87
- return null;
88
- }
89
- }
90
-
91
68
  function releaseGateText(projectRoot, pkg) {
92
69
  return [
93
70
  JSON.stringify((pkg && pkg.scripts) || {}),
@@ -32,6 +32,7 @@ const path = require('path');
32
32
  const linkage = require('./linkage');
33
33
  const state = require('./state');
34
34
  const atomic = require('./atomic-write');
35
+ const textUtil = require('./text-util');
35
36
 
36
37
  const PRD_PATH = '.godpowers/prd/PRD.md';
37
38
  const ROADMAP_PATH = '.godpowers/roadmap/ROADMAP.md';
@@ -58,11 +59,7 @@ function pad2(n) {
58
59
  }
59
60
 
60
61
  function slugify(text) {
61
- return String(text)
62
- .toLowerCase()
63
- .replace(/[^a-z0-9]+/g, '-')
64
- .replace(/^-+|-+$/g, '')
65
- .slice(0, 40) || 'increment';
62
+ return textUtil.slugify(text, 'increment');
66
63
  }
67
64
 
68
65
  // ============================================================================
@@ -10,6 +10,7 @@ const fs = require('fs');
10
10
  const path = require('path');
11
11
 
12
12
  const { parseSimpleYaml } = require('./intent');
13
+ const { read, write } = require('./sync-fs');
13
14
 
14
15
  const LOG_PATH = '.godpowers/surface/ROUTE-QUALITY-SYNC.md';
15
16
  const CONTEXTUAL_NEXT_VALUES = new Set([
@@ -100,18 +101,6 @@ const TIER_GATE_COMMANDS = new Set([
100
101
  '/god-harden'
101
102
  ]);
102
103
 
103
- function read(projectRoot, relPath) {
104
- const file = path.join(projectRoot, relPath);
105
- if (!fs.existsSync(file)) return '';
106
- return fs.readFileSync(file, 'utf8');
107
- }
108
-
109
- function write(projectRoot, relPath, content) {
110
- const file = path.join(projectRoot, relPath);
111
- fs.mkdirSync(path.dirname(file), { recursive: true });
112
- fs.writeFileSync(file, content);
113
- }
114
-
115
104
  function listFiles(projectRoot, relDir, pattern) {
116
105
  const dir = path.join(projectRoot, relDir);
117
106
  if (!fs.existsSync(dir)) return [];