@entelligentsia/forgecli 0.10.0 → 0.11.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.
- package/CHANGELOG.md +95 -0
- package/README.md +21 -3
- package/dist/CHANGELOG-forge-plugin.md +90 -0
- package/dist/bin/config.js +6 -0
- package/dist/bin/config.js.map +1 -1
- package/dist/extensions/forgecli/add-pipeline.d.ts +19 -0
- package/dist/extensions/forgecli/add-pipeline.js +143 -0
- package/dist/extensions/forgecli/add-pipeline.js.map +1 -0
- package/dist/extensions/forgecli/add-task.d.ts +20 -0
- package/dist/extensions/forgecli/add-task.js +154 -0
- package/dist/extensions/forgecli/add-task.js.map +1 -0
- package/dist/extensions/forgecli/calibrate.d.ts +61 -0
- package/dist/extensions/forgecli/calibrate.js +488 -0
- package/dist/extensions/forgecli/calibrate.js.map +1 -0
- package/dist/extensions/forgecli/fix-bug.d.ts +9 -1
- package/dist/extensions/forgecli/fix-bug.js +155 -45
- package/dist/extensions/forgecli/fix-bug.js.map +1 -1
- package/dist/extensions/forgecli/forge-commands.js +15 -22
- package/dist/extensions/forgecli/forge-commands.js.map +1 -1
- package/dist/extensions/forgecli/forge-subagent.d.ts +16 -1
- package/dist/extensions/forgecli/forge-subagent.js +45 -8
- package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
- package/dist/extensions/forgecli/forge-update-command.d.ts +9 -0
- package/dist/extensions/forgecli/forge-update-command.js +106 -7
- package/dist/extensions/forgecli/forge-update-command.js.map +1 -1
- package/dist/extensions/forgecli/health-check.d.ts +22 -1
- package/dist/extensions/forgecli/health-check.js +177 -4
- package/dist/extensions/forgecli/health-check.js.map +1 -1
- package/dist/extensions/forgecli/hook-dispatcher.d.ts +25 -1
- package/dist/extensions/forgecli/hook-dispatcher.js +104 -9
- package/dist/extensions/forgecli/hook-dispatcher.js.map +1 -1
- package/dist/extensions/forgecli/hooks/check-update.d.ts +81 -0
- package/dist/extensions/forgecli/hooks/check-update.js +308 -0
- package/dist/extensions/forgecli/hooks/check-update.js.map +1 -0
- package/dist/extensions/forgecli/hooks/forge-permissions.d.ts +32 -0
- package/dist/extensions/forgecli/hooks/forge-permissions.js +119 -0
- package/dist/extensions/forgecli/hooks/forge-permissions.js.map +1 -0
- package/dist/extensions/forgecli/hooks/triage-error.d.ts +23 -0
- package/dist/extensions/forgecli/hooks/triage-error.js +62 -0
- package/dist/extensions/forgecli/hooks/triage-error.js.map +1 -0
- package/dist/extensions/forgecli/hooks/write-guard.d.ts +28 -0
- package/dist/extensions/forgecli/hooks/write-guard.js +225 -0
- package/dist/extensions/forgecli/hooks/write-guard.js.map +1 -0
- package/dist/extensions/forgecli/index.js +60 -0
- package/dist/extensions/forgecli/index.js.map +1 -1
- package/dist/extensions/forgecli/init-context.d.ts +1 -1
- package/dist/extensions/forgecli/init-context.js +21 -6
- package/dist/extensions/forgecli/init-context.js.map +1 -1
- package/dist/extensions/forgecli/materialize.d.ts +16 -0
- package/dist/extensions/forgecli/materialize.js +195 -0
- package/dist/extensions/forgecli/materialize.js.map +1 -0
- package/dist/extensions/forgecli/migrate.d.ts +19 -0
- package/dist/extensions/forgecli/migrate.js +258 -0
- package/dist/extensions/forgecli/migrate.js.map +1 -0
- package/dist/extensions/forgecli/migration-engine.d.ts +111 -0
- package/dist/extensions/forgecli/migration-engine.js +533 -0
- package/dist/extensions/forgecli/migration-engine.js.map +1 -0
- package/dist/extensions/forgecli/quiz-agent.d.ts +17 -0
- package/dist/extensions/forgecli/quiz-agent.js +98 -0
- package/dist/extensions/forgecli/quiz-agent.js.map +1 -0
- package/dist/extensions/forgecli/remove-command.d.ts +17 -0
- package/dist/extensions/forgecli/remove-command.js +124 -0
- package/dist/extensions/forgecli/remove-command.js.map +1 -0
- package/dist/extensions/forgecli/report-bug.d.ts +25 -0
- package/dist/extensions/forgecli/report-bug.js +159 -0
- package/dist/extensions/forgecli/report-bug.js.map +1 -0
- package/dist/extensions/forgecli/retrospective.d.ts +19 -0
- package/dist/extensions/forgecli/retrospective.js +156 -0
- package/dist/extensions/forgecli/retrospective.js.map +1 -0
- package/dist/extensions/forgecli/run-sprint.js +36 -3
- package/dist/extensions/forgecli/run-sprint.js.map +1 -1
- package/dist/extensions/forgecli/run-task.d.ts +9 -1
- package/dist/extensions/forgecli/run-task.js +66 -13
- package/dist/extensions/forgecli/run-task.js.map +1 -1
- package/dist/extensions/forgecli/session-registry.d.ts +40 -2
- package/dist/extensions/forgecli/session-registry.js +71 -1
- package/dist/extensions/forgecli/session-registry.js.map +1 -1
- package/dist/extensions/forgecli/status-command.d.ts +19 -0
- package/dist/extensions/forgecli/status-command.js +140 -0
- package/dist/extensions/forgecli/status-command.js.map +1 -0
- package/dist/extensions/forgecli/store-query.d.ts +22 -0
- package/dist/extensions/forgecli/store-query.js +107 -0
- package/dist/extensions/forgecli/store-query.js.map +1 -0
- package/dist/extensions/forgecli/store-repair.d.ts +17 -0
- package/dist/extensions/forgecli/store-repair.js +123 -0
- package/dist/extensions/forgecli/store-repair.js.map +1 -0
- package/dist/extensions/forgecli/test-orchestrate.js +1 -0
- package/dist/extensions/forgecli/test-orchestrate.js.map +1 -1
- package/dist/extensions/forgecli/thread-switcher.js +286 -41
- package/dist/extensions/forgecli/thread-switcher.js.map +1 -1
- package/dist/extensions/forgecli/transition-guard.js +7 -2
- package/dist/extensions/forgecli/transition-guard.js.map +1 -1
- package/dist/extensions/forgecli/update-tools.d.ts +23 -0
- package/dist/extensions/forgecli/update-tools.js +136 -0
- package/dist/extensions/forgecli/update-tools.js.map +1 -0
- package/dist/extensions/forgecli/viewport-events.js +10 -0
- package/dist/extensions/forgecli/viewport-events.js.map +1 -1
- package/dist/extensions/forgecli/viewport-renderer.d.ts +18 -0
- package/dist/extensions/forgecli/viewport-renderer.js +27 -0
- package/dist/extensions/forgecli/viewport-renderer.js.map +1 -1
- package/dist/extensions/forgecli/viewport-theme.js +4 -0
- package/dist/extensions/forgecli/viewport-theme.js.map +1 -1
- package/dist/extensions/forgecli/whats-new-widget.d.ts +13 -8
- package/dist/extensions/forgecli/whats-new-widget.js +111 -42
- package/dist/extensions/forgecli/whats-new-widget.js.map +1 -1
- package/dist/forge-payload/.base-pack/workflows/architect_approve.md +29 -3
- package/dist/forge-payload/.base-pack/workflows/commit_task.md +15 -8
- package/dist/forge-payload/.base-pack/workflows/fix_bug.md +327 -185
- package/dist/forge-payload/.base-pack/workflows/implement_plan.md +18 -10
- package/dist/forge-payload/.base-pack/workflows/plan_task.md +15 -9
- package/dist/forge-payload/.base-pack/workflows/review_code.md +14 -6
- package/dist/forge-payload/.base-pack/workflows/review_plan.md +18 -10
- package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
- package/dist/forge-payload/.schemas/bug.schema.json +3 -2
- package/dist/forge-payload/.schemas/config.schema.json +83 -0
- package/dist/forge-payload/.schemas/migrations.json +2049 -0
- package/dist/forge-payload/commands/regenerate.md +17 -1
- package/dist/forge-payload/meta/personas/README.md +16 -0
- package/dist/forge-payload/meta/personas/meta-architect.md +70 -0
- package/dist/forge-payload/meta/personas/meta-bug-fixer.md +73 -0
- package/dist/forge-payload/meta/personas/meta-collator.md +72 -0
- package/dist/forge-payload/meta/personas/meta-engineer.md +70 -0
- package/dist/forge-payload/meta/personas/meta-orchestrator.md +71 -0
- package/dist/forge-payload/meta/personas/meta-product-manager.md +82 -0
- package/dist/forge-payload/meta/personas/meta-qa-engineer.md +91 -0
- package/dist/forge-payload/meta/personas/meta-supervisor.md +92 -0
- package/dist/forge-payload/meta/skill-recommendations.md +154 -0
- package/dist/forge-payload/meta/skills/meta-architect-skills.md +43 -0
- package/dist/forge-payload/meta/skills/meta-bug-fixer-skills.md +43 -0
- package/dist/forge-payload/meta/skills/meta-collator-skills.md +41 -0
- package/dist/forge-payload/meta/skills/meta-engineer-skills.md +43 -0
- package/dist/forge-payload/meta/skills/meta-generic-skills.md +58 -0
- package/dist/forge-payload/meta/skills/meta-qa-engineer-skills.md +46 -0
- package/dist/forge-payload/meta/skills/meta-supervisor-skills.md +43 -0
- package/dist/forge-payload/meta/store-schema/bug.schema.md +71 -0
- package/dist/forge-payload/meta/store-schema/event.schema.md +76 -0
- package/dist/forge-payload/meta/store-schema/feature.schema.md +65 -0
- package/dist/forge-payload/meta/store-schema/sprint.schema.md +64 -0
- package/dist/forge-payload/meta/store-schema/task.schema.md +78 -0
- package/dist/forge-payload/meta/templates/meta-code-review.md +26 -0
- package/dist/forge-payload/meta/templates/meta-plan-review.md +28 -0
- package/dist/forge-payload/meta/templates/meta-plan.md +28 -0
- package/dist/forge-payload/meta/templates/meta-progress.md +25 -0
- package/dist/forge-payload/meta/templates/meta-retrospective.md +28 -0
- package/dist/forge-payload/meta/templates/meta-sprint-manifest.md +26 -0
- package/dist/forge-payload/meta/templates/meta-sprint-requirements.md +91 -0
- package/dist/forge-payload/meta/templates/meta-task-prompt.md +26 -0
- package/dist/forge-payload/meta/tool-specs/collate.spec.md +88 -0
- package/dist/forge-payload/meta/tool-specs/generation-manifest.spec.md +139 -0
- package/dist/forge-payload/meta/tool-specs/manage-config.spec.md +143 -0
- package/dist/forge-payload/meta/tool-specs/seed-store.spec.md +91 -0
- package/dist/forge-payload/meta/tool-specs/store-cli.spec.md +328 -0
- package/dist/forge-payload/meta/tool-specs/validate-store.spec.md +191 -0
- package/dist/forge-payload/meta/workflows/_fragments/context-injection.md +75 -0
- package/dist/forge-payload/meta/workflows/_fragments/event-emission-schema.md +73 -0
- package/dist/forge-payload/meta/workflows/_fragments/finalize.md +13 -0
- package/dist/forge-payload/meta/workflows/_fragments/friction-emit.md +73 -0
- package/dist/forge-payload/meta/workflows/_fragments/progress-reporting.md +38 -0
- package/dist/forge-payload/meta/workflows/_fragments/store-cli-verbs.md +39 -0
- package/dist/forge-payload/meta/workflows/meta-approve.md +119 -0
- package/dist/forge-payload/meta/workflows/meta-collate.md +89 -0
- package/dist/forge-payload/meta/workflows/meta-commit.md +93 -0
- package/dist/forge-payload/meta/workflows/meta-enhance.md +286 -0
- package/dist/forge-payload/meta/workflows/meta-fix-bug.md +501 -0
- package/dist/forge-payload/meta/workflows/meta-implement.md +132 -0
- package/dist/forge-payload/meta/workflows/meta-migrate.md +455 -0
- package/dist/forge-payload/meta/workflows/meta-orchestrate.md +993 -0
- package/dist/forge-payload/meta/workflows/meta-plan-task.md +133 -0
- package/dist/forge-payload/meta/workflows/meta-quiz-agent.md +135 -0
- package/dist/forge-payload/meta/workflows/meta-retrospective.md +65 -0
- package/dist/forge-payload/meta/workflows/meta-review-implementation.md +119 -0
- package/dist/forge-payload/meta/workflows/meta-review-plan.md +108 -0
- package/dist/forge-payload/meta/workflows/meta-review-sprint-completion.md +65 -0
- package/dist/forge-payload/meta/workflows/meta-sprint-intake.md +76 -0
- package/dist/forge-payload/meta/workflows/meta-sprint-plan.md +147 -0
- package/dist/forge-payload/meta/workflows/meta-update-implementation.md +76 -0
- package/dist/forge-payload/meta/workflows/meta-update-plan.md +76 -0
- package/dist/forge-payload/meta/workflows/meta-validate.md +111 -0
- package/dist/forge-payload/tools/check-structure.cjs +344 -0
- package/dist/forge-payload/tools/collate.cjs +34 -9
- package/dist/forge-payload/tools/list-skills.js +76 -0
- package/dist/forge-payload/tools/parse-gates.cjs +8 -2
- package/dist/forge-payload/tools/store-cli.cjs +56 -11
- package/dist/forge-payload/tools/store.cjs +61 -0
- package/dist/forge-payload/tools/substitute-placeholders.cjs +60 -8
- package/dist/forge-payload/tools/validate-store.cjs +6 -2
- package/dist/forge-payload/tools/verify-integrity.cjs +86 -0
- package/package.json +2 -2
|
@@ -56,6 +56,7 @@ class Store {
|
|
|
56
56
|
* @returns {{ purged: boolean, fileCount: number, files: string[] }}
|
|
57
57
|
*/
|
|
58
58
|
purgeEvents(sprintId, opts) { return this.impl.purgeEvents(sprintId, opts); }
|
|
59
|
+
purgeBugEvents(bugId, opts) { return this.impl.purgeBugEvents(bugId, opts); }
|
|
59
60
|
/**
|
|
60
61
|
* List event filenames for a sprint directory.
|
|
61
62
|
* @param {string} sprintId
|
|
@@ -287,6 +288,66 @@ class FSImpl {
|
|
|
287
288
|
return { purged: true, fileCount: files.length, files };
|
|
288
289
|
}
|
|
289
290
|
|
|
291
|
+
/**
|
|
292
|
+
* Purge only the events belonging to a specific bug from the shared
|
|
293
|
+
* `.forge/store/events/bugs/` virtual sprint dir.
|
|
294
|
+
*
|
|
295
|
+
* Bugs share a single events directory (`events/bugs/`) — see
|
|
296
|
+
* `meta-fix-bug.md § Event Emission` and `validate-store.spec.md`. Purging
|
|
297
|
+
* by bug therefore must filter primary events by `event.bugId === bugId`
|
|
298
|
+
* and sweep sidecars whose filename pattern `_{eventId}_usage.json`
|
|
299
|
+
* matches a purged primary. The `events/bugs/` directory itself is
|
|
300
|
+
* never removed — other bugs' events remain.
|
|
301
|
+
*
|
|
302
|
+
* Returns `{ purged, fileCount, files }` matching `purgeEvents`.
|
|
303
|
+
*/
|
|
304
|
+
purgeBugEvents(bugId, { dryRun = false } = {}) {
|
|
305
|
+
const eventsBugsDir = path.join(this.storeRoot, 'events', 'bugs');
|
|
306
|
+
if (!fs.existsSync(eventsBugsDir)) {
|
|
307
|
+
return { purged: false, fileCount: 0, files: [] };
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const all = fs.readdirSync(eventsBugsDir).filter(f => f.endsWith('.json'));
|
|
311
|
+
const primaries = all.filter(f => !f.startsWith('_'));
|
|
312
|
+
const sidecars = all.filter(f => f.startsWith('_') && f.endsWith('_usage.json'));
|
|
313
|
+
|
|
314
|
+
// Identify primaries whose payload.bugId matches the requested bug.
|
|
315
|
+
const matchedPrimaries = [];
|
|
316
|
+
const matchedEventIds = new Set();
|
|
317
|
+
for (const filename of primaries) {
|
|
318
|
+
const filePath = path.join(eventsBugsDir, filename);
|
|
319
|
+
let payload;
|
|
320
|
+
try {
|
|
321
|
+
payload = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
322
|
+
} catch (_) {
|
|
323
|
+
continue; // malformed file — skip
|
|
324
|
+
}
|
|
325
|
+
if (payload && payload.bugId === bugId) {
|
|
326
|
+
matchedPrimaries.push(filename);
|
|
327
|
+
if (payload.eventId) matchedEventIds.add(payload.eventId);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Match sidecars by filename pattern: `_{eventId}_usage.json`.
|
|
332
|
+
const matchedSidecars = sidecars.filter(filename => {
|
|
333
|
+
const m = filename.match(/^_(.+)_usage\.json$/);
|
|
334
|
+
return m && matchedEventIds.has(m[1]);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
const allMatches = [...matchedPrimaries, ...matchedSidecars];
|
|
338
|
+
if (dryRun) {
|
|
339
|
+
return { purged: false, fileCount: allMatches.length, files: allMatches };
|
|
340
|
+
}
|
|
341
|
+
for (const filename of allMatches) {
|
|
342
|
+
fs.unlinkSync(path.join(eventsBugsDir, filename));
|
|
343
|
+
}
|
|
344
|
+
return {
|
|
345
|
+
purged: allMatches.length > 0,
|
|
346
|
+
fileCount: allMatches.length,
|
|
347
|
+
files: allMatches,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
290
351
|
/**
|
|
291
352
|
* List all event filenames for a sprint directory.
|
|
292
353
|
* Returns { filename, id } objects for ALL .json files including
|
|
@@ -343,13 +343,14 @@ function renderDeploymentTable(envs) {
|
|
|
343
343
|
* Advisory Note 3: every output path is resolved and checked to be under outRoot
|
|
344
344
|
* to prevent path traversal via symlinks or '..' segments.
|
|
345
345
|
*
|
|
346
|
-
* @param {string} basePack
|
|
346
|
+
* @param {string} basePack — absolute path to the base-pack directory
|
|
347
347
|
* @param {Map<string, string>} map
|
|
348
|
-
* @param {string} outRoot
|
|
349
|
-
* @param {boolean} dryRun
|
|
350
|
-
* @param {{ warn: function }} io
|
|
348
|
+
* @param {string} outRoot — absolute project root (e.g. '/home/user/myproject')
|
|
349
|
+
* @param {boolean} dryRun — if true, perform no writes
|
|
350
|
+
* @param {{ warn: function }} io — pluggable stderr for warnings
|
|
351
|
+
* @param {Set<string>|null} categoryFilter — Defect E: if set, only walk matching subdirs
|
|
351
352
|
*/
|
|
352
|
-
function walkBasePack(basePack, map, outRoot, dryRun, io) {
|
|
353
|
+
function walkBasePack(basePack, map, outRoot, dryRun, io, categoryFilter) {
|
|
353
354
|
const warn = (io && io.warn) || ((msg) => process.stderr.write(msg + '\n'));
|
|
354
355
|
|
|
355
356
|
// Extract prefix from substitution map for commands path computation
|
|
@@ -363,6 +364,11 @@ function walkBasePack(basePack, map, outRoot, dryRun, io) {
|
|
|
363
364
|
const stat = fs.statSync(subdirPath);
|
|
364
365
|
if (!stat.isDirectory()) continue;
|
|
365
366
|
|
|
367
|
+
// Defect E: skip subdirs not in the category filter when one is provided
|
|
368
|
+
if (categoryFilter !== null && categoryFilter !== undefined && !categoryFilter.has(subdir)) {
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
|
|
366
372
|
let relOutputDir;
|
|
367
373
|
if (subdir === 'commands') {
|
|
368
374
|
relOutputDir = path.join('.claude', 'commands', commandsSubdir);
|
|
@@ -464,6 +470,32 @@ function walkBasePackPi(src, outRoot, dryRun, io) {
|
|
|
464
470
|
if (require.main === module) {
|
|
465
471
|
try {
|
|
466
472
|
const argv = process.argv.slice(2);
|
|
473
|
+
|
|
474
|
+
// ── Defect C fix: short-circuit --help/-h BEFORE any path resolution ────────
|
|
475
|
+
if (argv.includes('--help') || argv.includes('-h')) {
|
|
476
|
+
process.stdout.write([
|
|
477
|
+
'substitute-placeholders.cjs — Phase 3 (Materialize) engine.',
|
|
478
|
+
'',
|
|
479
|
+
'Usage:',
|
|
480
|
+
' node substitute-placeholders.cjs [options]',
|
|
481
|
+
'',
|
|
482
|
+
'Options:',
|
|
483
|
+
' --target <claude-code|pi> Output layout target (default: claude-code)',
|
|
484
|
+
' --src <path> Base-pack source dir for --target pi',
|
|
485
|
+
' --forge-root <path> Forge plugin root directory',
|
|
486
|
+
' --base-pack <path> Base-pack directory (probes .base-pack/ then init/base-pack/)',
|
|
487
|
+
' --config <path> Path to .forge/config.json',
|
|
488
|
+
' --context <path> Path to project-context.json',
|
|
489
|
+
' --rules <path> Path to build-base-pack-rules.json',
|
|
490
|
+
' --out <path> Output root directory (default: cwd)',
|
|
491
|
+
' --dry-run Preview without writing',
|
|
492
|
+
' --category <name[,name]> Limit materialisation to named subdirectories',
|
|
493
|
+
' (personas, skills, workflows, templates, commands)',
|
|
494
|
+
' --help, -h Show this message and exit',
|
|
495
|
+
].join('\n') + '\n');
|
|
496
|
+
process.exit(0);
|
|
497
|
+
}
|
|
498
|
+
|
|
467
499
|
const args = parseCliArgs(argv);
|
|
468
500
|
|
|
469
501
|
const dryRun = args.dryRun || false;
|
|
@@ -515,8 +547,20 @@ if (require.main === module) {
|
|
|
515
547
|
// Resolve forge root
|
|
516
548
|
const forgeRoot = args.forgeRoot || resolveForgeRoot();
|
|
517
549
|
|
|
518
|
-
// Resolve base-pack
|
|
519
|
-
|
|
550
|
+
// Resolve base-pack — Defect C fix: when no --base-pack flag given,
|
|
551
|
+
// probe .base-pack/ (bundled layout) before init/base-pack (source layout).
|
|
552
|
+
let basePack;
|
|
553
|
+
if (args.basePack) {
|
|
554
|
+
basePack = args.basePack;
|
|
555
|
+
} else {
|
|
556
|
+
const dotBasePack = path.resolve(process.cwd(), '.base-pack');
|
|
557
|
+
const initBasePack = path.join(forgeRoot, 'init', 'base-pack');
|
|
558
|
+
if (fs.existsSync(dotBasePack)) {
|
|
559
|
+
basePack = dotBasePack;
|
|
560
|
+
} else {
|
|
561
|
+
basePack = initBasePack;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
520
564
|
if (!fs.existsSync(basePack)) {
|
|
521
565
|
process.stderr.write(`substitute-placeholders: base-pack not found at ${basePack}\n`);
|
|
522
566
|
process.exit(1);
|
|
@@ -563,8 +607,15 @@ if (require.main === module) {
|
|
|
563
607
|
process.exit(1);
|
|
564
608
|
}
|
|
565
609
|
|
|
610
|
+
// Defect E: parse --category flag into a Set<string> filter (or null for all)
|
|
611
|
+
let categoryFilter = null;
|
|
612
|
+
if (args.category) {
|
|
613
|
+
const cats = args.category.split(',').map((c) => c.trim()).filter(Boolean);
|
|
614
|
+
categoryFilter = new Set(cats);
|
|
615
|
+
}
|
|
616
|
+
|
|
566
617
|
// Walk and materialise
|
|
567
|
-
walkBasePack(basePack, map, outRoot, dryRun, null);
|
|
618
|
+
walkBasePack(basePack, map, outRoot, dryRun, null, categoryFilter);
|
|
568
619
|
|
|
569
620
|
if (dryRun) {
|
|
570
621
|
process.stdout.write('substitute-placeholders: dry run complete (no files written)\n');
|
|
@@ -593,6 +644,7 @@ function parseCliArgs(argv) {
|
|
|
593
644
|
if (a === '--context' && argv[i + 1]) { args.context = argv[++i]; continue; }
|
|
594
645
|
if (a === '--rules' && argv[i + 1]) { args.rules = argv[++i]; continue; }
|
|
595
646
|
if (a === '--out' && argv[i + 1]) { args.out = argv[++i]; continue; }
|
|
647
|
+
if (a === '--category' && argv[i + 1]) { args.category = argv[++i]; continue; }
|
|
596
648
|
}
|
|
597
649
|
return args;
|
|
598
650
|
}
|
|
@@ -494,8 +494,12 @@ for (const sprint of allSprints) {
|
|
|
494
494
|
|
|
495
495
|
// --- Pass 2b: Orphan event directories ---
|
|
496
496
|
// Scan .forge/store/events/ for subdirectories whose name does not match any
|
|
497
|
-
// known sprintId and is not a reserved
|
|
498
|
-
|
|
497
|
+
// known sprintId and is not a reserved virtual dir.
|
|
498
|
+
// Reserved:
|
|
499
|
+
// - SYS-* — system-generated events that predate sprint records
|
|
500
|
+
// - bugs — virtual sprint dir for fix-bug phase events (see
|
|
501
|
+
// meta/tool-specs/validate-store.spec.md §"event.sprintId")
|
|
502
|
+
const RESERVED_EVENT_PREFIX = /^(SYS-|bugs$)/;
|
|
499
503
|
const eventsBaseDir = path.join(storeRootFromConfig, 'events');
|
|
500
504
|
if (fs.existsSync(eventsBaseDir)) {
|
|
501
505
|
let eventDirEntries;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// Forge tool: verify-integrity
|
|
5
|
+
// Runtime integrity verifier — reads integrity.json, re-hashes each file, reports drift.
|
|
6
|
+
// Usage: node verify-integrity.cjs [--forge-root <path>]
|
|
7
|
+
// Exit codes: 0 = all clean, 1 = one or more files modified or missing, 2 = manifest missing
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const crypto = require('crypto');
|
|
12
|
+
|
|
13
|
+
function computeHash(filePath) {
|
|
14
|
+
const content = fs.readFileSync(filePath);
|
|
15
|
+
return crypto.createHash('sha256').update(content).digest('hex');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function verifyIntegrity(forgeRoot) {
|
|
19
|
+
const manifestPath = path.join(forgeRoot, 'integrity.json');
|
|
20
|
+
|
|
21
|
+
if (!fs.existsSync(manifestPath)) {
|
|
22
|
+
const output = '× integrity.json not found — run /forge:update to restore';
|
|
23
|
+
return { exitCode: 1, output, modified: [], missing: [] };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let manifest;
|
|
27
|
+
try {
|
|
28
|
+
manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
29
|
+
} catch (e) {
|
|
30
|
+
const output = `× integrity.json is not valid JSON: ${e.message}`;
|
|
31
|
+
return { exitCode: 1, output, modified: [], missing: [] };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const { files } = manifest;
|
|
35
|
+
if (!files || typeof files !== 'object') {
|
|
36
|
+
const output = '× integrity.json has no "files" field';
|
|
37
|
+
return { exitCode: 1, output, modified: [], missing: [] };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const modified = [];
|
|
41
|
+
const missing = [];
|
|
42
|
+
|
|
43
|
+
for (const [rel, expectedHash] of Object.entries(files)) {
|
|
44
|
+
const abs = path.join(forgeRoot, rel);
|
|
45
|
+
if (!fs.existsSync(abs)) {
|
|
46
|
+
missing.push(rel);
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const actual = computeHash(abs);
|
|
50
|
+
if (actual !== expectedHash) {
|
|
51
|
+
modified.push(rel);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const totalFiles = Object.keys(files).length;
|
|
56
|
+
const problemCount = modified.length + missing.length;
|
|
57
|
+
|
|
58
|
+
if (problemCount === 0) {
|
|
59
|
+
const output = `〇 Plugin integrity — all ${totalFiles} files unmodified`;
|
|
60
|
+
return { exitCode: 0, output, modified: [], missing: [] };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const lines = [`△ Plugin integrity — ${problemCount} file${problemCount === 1 ? '' : 's'} modified or missing:`];
|
|
64
|
+
for (const f of modified) {
|
|
65
|
+
lines.push(` · ${f} (hash mismatch — run /forge:update to restore)`);
|
|
66
|
+
}
|
|
67
|
+
for (const f of missing) {
|
|
68
|
+
lines.push(` · ${f} (missing — run /forge:update to restore)`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return { exitCode: 1, output: lines.join('\n'), modified, missing };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = { verifyIntegrity };
|
|
75
|
+
|
|
76
|
+
if (require.main === module) {
|
|
77
|
+
const args = process.argv.slice(2);
|
|
78
|
+
const forgeRootIdx = args.indexOf('--forge-root');
|
|
79
|
+
const forgeRoot = forgeRootIdx !== -1
|
|
80
|
+
? path.resolve(args[forgeRootIdx + 1])
|
|
81
|
+
: path.resolve(__dirname, '..');
|
|
82
|
+
|
|
83
|
+
const result = verifyIntegrity(forgeRoot);
|
|
84
|
+
console.log(result.output);
|
|
85
|
+
process.exit(result.exitCode);
|
|
86
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@entelligentsia/forgecli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"description": "Forge SDLC ported onto @earendil-works/pi-coding-agent — production launcher with three bin aliases (forge/forgecli/4ge). Bundles a curated fork of pi-coding-agent vendored under earendil-works names.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Entelligentsia",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
]
|
|
50
50
|
},
|
|
51
51
|
"forge": {
|
|
52
|
-
"bundledVersion": "0.
|
|
52
|
+
"bundledVersion": "0.44.6",
|
|
53
53
|
"forgeRoot": "../forge/forge"
|
|
54
54
|
},
|
|
55
55
|
"scripts": {
|