forge-workflow 0.1.0-beta.3 → 0.1.0-beta.5
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/AGENTS.md +14 -7
- package/CHANGELOG.md +43 -1
- package/README.md +6 -2
- package/bin/forge-cmd.js +21 -1
- package/bin/forge.js +16 -369
- package/docs/INDEX.md +1 -1
- package/docs/guides/BEADS_GITHUB_SYNC.md +2 -31
- package/docs/guides/MIGRATION.md +4 -4
- package/docs/guides/SETUP.md +16 -16
- package/docs/reference/COMMANDS.md +9 -4
- package/docs/reference/INSIGHTS_RECAP.md +9 -20
- package/docs/reference/RELEASE.md +5 -3
- package/docs/reference/TOOLCHAIN.md +8 -0
- package/docs/reference/protected-state-surfaces.md +4 -4
- package/docs/reference/shepherd.md +117 -17
- package/lefthook.yml +12 -0
- package/lib/activation/ensure-forge-home.js +33 -15
- package/lib/adapters/greptile-review-adapter.js +1 -1
- package/lib/adapters/pr-state-adapter.js +397 -100
- package/lib/agents-config.js +5 -0
- package/lib/audit-evidence.js +71 -110
- package/lib/capped-jsonl-log.js +236 -0
- package/lib/commands/_issue.js +31 -46
- package/lib/commands/_manifest.js +1 -1
- package/lib/commands/_registry.js +2 -2
- package/lib/commands/_resolve-command-opts.js +36 -29
- package/lib/commands/claim.js +2 -4
- package/lib/commands/clean.js +196 -32
- package/lib/commands/dev.js +4 -33
- package/lib/commands/hooks.js +358 -13
- package/lib/commands/insights.js +8 -3
- package/lib/commands/merge.js +600 -40
- package/lib/commands/plan.js +23 -115
- package/lib/commands/pr.js +1 -1
- package/lib/commands/preflight.js +11 -2
- package/lib/commands/prime.js +23 -3
- package/lib/commands/push.js +41 -51
- package/lib/commands/recall.js +60 -16
- package/lib/commands/recap.js +6 -1
- package/lib/commands/release.js +18 -4
- package/lib/commands/serve.js +5 -2
- package/lib/commands/setup.js +191 -95
- package/lib/commands/shepherd.js +49 -4
- package/lib/commands/ship.js +22 -23
- package/lib/commands/skill.js +383 -0
- package/lib/commands/status.js +54 -33
- package/lib/commands/test.js +56 -34
- package/lib/commands/worktree.js +247 -43
- package/lib/core/runtime-graph.js +89 -15
- package/lib/doc-assertions.js +297 -0
- package/lib/existing-tdd-gate.js +253 -0
- package/lib/forge-context.js +1 -4
- package/lib/forge-issues.js +64 -491
- package/lib/git-defaults.js +56 -0
- package/lib/harness-capability-matrix.js +5 -5
- package/lib/hook-renderer.js +147 -16
- package/lib/insights.js +96 -80
- package/lib/issue-backend.js +42 -3
- package/lib/kernel/backing-issue.js +14 -2
- package/lib/kernel/broker.js +44 -0
- package/lib/kernel/cli-broker-factory.js +12 -1
- package/lib/kernel/close-on-merge.js +154 -0
- package/lib/kernel/fs-class.js +42 -25
- package/lib/kernel/migrations.js +30 -2
- package/lib/kernel/schema.js +35 -0
- package/lib/kernel/sqlite-driver.js +292 -18
- package/lib/lefthook-wiring.js +21 -1
- package/lib/memory/router.js +16 -1
- package/lib/memory-digest.js +47 -15
- package/lib/memory-recall-events.js +145 -0
- package/lib/memory-recall.js +212 -0
- package/lib/merge-rules.js +8 -4
- package/lib/npm-publish-workflow.js +272 -0
- package/lib/orientation.js +371 -49
- package/lib/plugin-catalog.js +14 -4
- package/lib/pr-bundle.js +9 -6
- package/lib/pr-monitor/journal.js +18 -2
- package/lib/pr-monitor/reconcile-executor.js +842 -0
- package/lib/pr-monitor/reconcile-tick.js +138 -0
- package/lib/pr-monitor/reconcile.js +0 -0
- package/lib/pr-monitor/render-summary.js +196 -0
- package/lib/pr-monitor/shepherd-lease.js +252 -0
- package/lib/pr-monitor/watch-lifecycle.js +14 -2
- package/lib/pr-pull.js +98 -24
- package/lib/pr-shepherd.js +34 -8
- package/lib/preflight/gates.js +65 -18
- package/lib/preflight/runner.js +5 -0
- package/lib/project-memory.js +40 -0
- package/lib/protected-state-authority.js +305 -0
- package/lib/protected-state-surfaces.js +64 -44
- package/lib/release-readiness.js +51 -4
- package/lib/rules-sync.js +4 -0
- package/lib/runtime-health.js +15 -46
- package/lib/shell-utils.js +1 -1
- package/lib/skill-eval.js +750 -0
- package/lib/skills-sync.js +6 -3
- package/lib/smart-merge.js +28 -4
- package/lib/status/identity.js +46 -0
- package/lib/status/presenter.js +0 -35
- package/lib/status/snapshot.js +11 -16
- package/lib/symlink-utils.js +74 -26
- package/lib/upgrade-safety.js +47 -9
- package/lib/using-forge.js +328 -0
- package/lib/workflow/enforce-stage.js +5 -5
- package/lib/workflow/state-manager.js +23 -23
- package/package.json +6 -7
- package/rules/using-forge.md +24 -0
- package/scripts/doc-asserting-tests.js +158 -0
- package/scripts/forge-team/index.sh +0 -5
- package/scripts/forge-team/tests/dispatcher.test.sh +1 -1
- package/scripts/forge-team/tests/workflow-integration.test.sh +0 -1
- package/scripts/lib/behavioral-eval-runner.js +310 -0
- package/scripts/lib/behavioral-eval-runtime.js +456 -0
- package/scripts/lib/eval-evidence.js +328 -0
- package/scripts/lib/eval-runner.js +81 -41
- package/scripts/lib/immutable-eval-corpus.js +309 -0
- package/scripts/lib/promotion-evidence-loader.js +94 -0
- package/scripts/lib/promotion-scorecard.js +314 -0
- package/scripts/npm-release-receipt.js +134 -0
- package/scripts/process-tree.js +761 -0
- package/scripts/protected-state-check.js +47 -22
- package/scripts/run-command-eval.js +29 -1
- package/scripts/sync-d20-audit.js +172 -0
- package/scripts/test-full-suite.js +249 -37
- package/scripts/test.js +184 -44
- package/skills/claim-safety/SKILL.md +4 -0
- package/skills/claim-safety/evals/scorecard.json +41 -0
- package/skills/coverage.json +83 -0
- package/skills/dev/SKILL.md +4 -0
- package/skills/dev/evals/scorecard.json +41 -0
- package/skills/gates/SKILL.md +80 -0
- package/skills/gates/evals/evals.json +38 -0
- package/skills/gates/evals/scorecard.json +41 -0
- package/skills/hermes-forge/SKILL.md +1 -0
- package/skills/hermes-forge/evals/scorecard.json +41 -0
- package/skills/issue-basics/SKILL.md +1 -0
- package/skills/issue-basics/evals/scorecard.json +41 -0
- package/skills/kernel/SKILL.md +38 -0
- package/skills/kernel/evals/scorecard.json +41 -0
- package/skills/memory/SKILL.md +16 -1
- package/skills/memory/evals/scorecard.json +41 -0
- package/skills/parallel-deep-research/SKILL.md +1 -0
- package/skills/parallel-deep-research/evals/scorecard.json +41 -0
- package/skills/plan/SKILL.md +6 -0
- package/skills/plan/evals/scorecard.json +41 -0
- package/skills/portability/SKILL.md +47 -0
- package/skills/portability/evals/evals.json +34 -0
- package/skills/portability/evals/scorecard.json +41 -0
- package/skills/research/SKILL.md +1 -0
- package/skills/research/evals/scorecard.json +41 -0
- package/skills/review/SKILL.md +10 -11
- package/skills/review/evals/scorecard.json +41 -0
- package/skills/rollback/SKILL.md +5 -11
- package/skills/rollback/evals/scorecard.json +41 -0
- package/skills/setup/SKILL.md +91 -0
- package/skills/setup/evals/evals.json +42 -0
- package/skills/setup/evals/scorecard.json +41 -0
- package/skills/shepherd/SKILL.md +84 -38
- package/skills/shepherd/evals/evals.json +21 -9
- package/skills/shepherd/evals/scorecard.json +41 -0
- package/skills/ship/SKILL.md +10 -12
- package/skills/ship/evals/scorecard.json +41 -0
- package/skills/smith/SKILL.md +8 -0
- package/skills/smith/evals/scorecard.json +41 -0
- package/skills/sonarcloud/SKILL.md +1 -0
- package/skills/sonarcloud/evals/scorecard.json +41 -0
- package/skills/sonarcloud-analysis/SKILL.md +1 -0
- package/skills/sonarcloud-analysis/evals/scorecard.json +41 -0
- package/skills/status/SKILL.md +3 -0
- package/skills/status/evals/scorecard.json +41 -0
- package/skills/triage-ready/SKILL.md +2 -0
- package/skills/triage-ready/evals/scorecard.json +41 -0
- package/skills/using-forge/SKILL.md +104 -0
- package/skills/using-forge/evals/scorecard.json +41 -0
- package/skills/validate/SKILL.md +4 -0
- package/skills/validate/evals/scorecard.json +41 -0
- package/skills/verify/SKILL.md +4 -0
- package/skills/verify/evals/scorecard.json +41 -0
- package/skills/worktree/SKILL.md +92 -0
- package/skills/worktree/evals/evals.json +38 -0
- package/skills/worktree/evals/scorecard.json +41 -0
- package/lib/adapters/beads-issue-adapter.js +0 -127
- package/lib/beads-nudge.js +0 -91
- package/lib/beads-setup.js +0 -538
- package/lib/beads-sync-scaffold.js +0 -189
- package/lib/commands/board.js +0 -64
- package/lib/pat-setup.js +0 -207
- package/lib/pr-monitor/render-sticky.js +0 -192
- package/lib/pr-monitor/upsert-sticky.js +0 -169
- package/lib/status/beads-snapshot.js +0 -145
- package/scripts/beads-context.sh +0 -577
- package/scripts/beads-migrate-to-dolt.sh +0 -7
- package/scripts/beads-upgrade-smoke.sh +0 -284
- package/scripts/forge-team/lib/dashboard.sh +0 -316
- package/scripts/forge-team/tests/dashboard.test.sh +0 -155
- package/scripts/lib/beads-migrate-to-dolt.mjs +0 -503
package/lib/commands/status.js
CHANGED
|
@@ -18,7 +18,7 @@ const {
|
|
|
18
18
|
const {
|
|
19
19
|
loadState,
|
|
20
20
|
extractWorkflowStateFromComments,
|
|
21
|
-
|
|
21
|
+
readWorkflowStateFromIssue,
|
|
22
22
|
} = require('../workflow/state-manager');
|
|
23
23
|
const { secureExecFileSync } = require('../shell-utils.js');
|
|
24
24
|
|
|
@@ -140,12 +140,12 @@ function analyzeChecks(context) {
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
/**
|
|
143
|
-
* Analyze
|
|
144
|
-
* @param {object}
|
|
145
|
-
* @returns {object}
|
|
143
|
+
* Analyze kernel issue state
|
|
144
|
+
* @param {object} issueRecord - kernel issue record
|
|
145
|
+
* @returns {object} issue analysis
|
|
146
146
|
*/
|
|
147
|
-
function
|
|
148
|
-
if (!
|
|
147
|
+
function analyzeIssue(issueRecord) {
|
|
148
|
+
if (!issueRecord) {
|
|
149
149
|
return {
|
|
150
150
|
hasActiveIssue: false,
|
|
151
151
|
issueStatus: null,
|
|
@@ -155,10 +155,10 @@ function analyzeBeads(beadsIssue) {
|
|
|
155
155
|
|
|
156
156
|
return {
|
|
157
157
|
hasActiveIssue: true,
|
|
158
|
-
issueStatus:
|
|
159
|
-
issueType:
|
|
160
|
-
isInProgress:
|
|
161
|
-
isClosed:
|
|
158
|
+
issueStatus: issueRecord.status,
|
|
159
|
+
issueType: issueRecord.type,
|
|
160
|
+
isInProgress: issueRecord.status === 'in_progress',
|
|
161
|
+
isClosed: issueRecord.status === 'closed',
|
|
162
162
|
};
|
|
163
163
|
}
|
|
164
164
|
|
|
@@ -178,7 +178,7 @@ function calculateConfidence(factors, stage) {
|
|
|
178
178
|
files: 30,
|
|
179
179
|
pr: 25,
|
|
180
180
|
checks: 15,
|
|
181
|
-
|
|
181
|
+
issue: 10,
|
|
182
182
|
};
|
|
183
183
|
|
|
184
184
|
// Branch state
|
|
@@ -235,16 +235,16 @@ function calculateConfidence(factors, stage) {
|
|
|
235
235
|
score += weights.checks / 2; // Partial match
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
-
//
|
|
239
|
-
maxScore += weights.
|
|
240
|
-
if (factors.
|
|
241
|
-
score += weights.
|
|
242
|
-
} else if (factors.
|
|
243
|
-
score += weights.
|
|
244
|
-
} else if (!factors.
|
|
245
|
-
score += weights.
|
|
238
|
+
// Issue state
|
|
239
|
+
maxScore += weights.issue;
|
|
240
|
+
if (factors.issue.isInProgress && stage >= 2 && stage <= 7) {
|
|
241
|
+
score += weights.issue; // Active work
|
|
242
|
+
} else if (factors.issue.isClosed && stage >= 8) {
|
|
243
|
+
score += weights.issue; // Closed for late stages
|
|
244
|
+
} else if (!factors.issue.hasActiveIssue && stage === 1) {
|
|
245
|
+
score += weights.issue; // No issue for fresh start
|
|
246
246
|
} else {
|
|
247
|
-
score += weights.
|
|
247
|
+
score += weights.issue / 2; // Partial match
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
const percentage = Math.round((score / maxScore) * 100);
|
|
@@ -275,21 +275,21 @@ function detectStage(context) {
|
|
|
275
275
|
const fileFactors = analyzeFiles(context);
|
|
276
276
|
const prFactors = analyzePR(context.pr);
|
|
277
277
|
const checkFactors = analyzeChecks(context);
|
|
278
|
-
const
|
|
278
|
+
const issueFactors = analyzeIssue(context.issueRecord);
|
|
279
279
|
|
|
280
280
|
const factors = {
|
|
281
281
|
branch: branchFactors,
|
|
282
282
|
files: fileFactors,
|
|
283
283
|
pr: prFactors,
|
|
284
284
|
checks: checkFactors,
|
|
285
|
-
|
|
285
|
+
issue: issueFactors,
|
|
286
286
|
};
|
|
287
287
|
|
|
288
288
|
// Stage detection logic
|
|
289
289
|
let stage;
|
|
290
290
|
|
|
291
291
|
// Stage 9: PR merged, verify docs
|
|
292
|
-
if (prFactors.prMerged &&
|
|
292
|
+
if (prFactors.prMerged && issueFactors.isClosed) {
|
|
293
293
|
stage = 9;
|
|
294
294
|
}
|
|
295
295
|
// Stage 8: PR approved, ready to merge
|
|
@@ -317,7 +317,7 @@ function detectStage(context) {
|
|
|
317
317
|
stage = 3;
|
|
318
318
|
}
|
|
319
319
|
// Stage 2: Research in progress
|
|
320
|
-
else if (
|
|
320
|
+
else if (issueFactors.issueType === 'research' && issueFactors.isInProgress) {
|
|
321
321
|
stage = 2;
|
|
322
322
|
}
|
|
323
323
|
// Stage 1: Fresh project (default)
|
|
@@ -612,7 +612,7 @@ function resolveWorkflowState(inputs) {
|
|
|
612
612
|
issueId: inputs.issueId,
|
|
613
613
|
issue: inputs.issue,
|
|
614
614
|
comments: inputs.bdComments,
|
|
615
|
-
|
|
615
|
+
preferIssueLookup: inputs.preferIssueLookup,
|
|
616
616
|
});
|
|
617
617
|
|
|
618
618
|
return { workflowState: state, fallbackReason: null };
|
|
@@ -649,7 +649,7 @@ function buildAuthoritativeStatus(workflowState) {
|
|
|
649
649
|
branch: {},
|
|
650
650
|
pr: {},
|
|
651
651
|
checks: { allChecksPass: workflowState.completedStages.includes('validate') },
|
|
652
|
-
|
|
652
|
+
issue: { hasActiveIssue: true },
|
|
653
653
|
},
|
|
654
654
|
};
|
|
655
655
|
}
|
|
@@ -751,15 +751,36 @@ function formatStatus(result) {
|
|
|
751
751
|
module.exports = {
|
|
752
752
|
name: 'status',
|
|
753
753
|
description: 'One-glance orientation: where you are, what to run next, and your work',
|
|
754
|
-
usage: 'forge status [--full] [--json]',
|
|
754
|
+
usage: 'forge status [-v|--verbose] [--full] [--json]',
|
|
755
755
|
flags: {
|
|
756
|
+
'-v, --verbose': 'Print the full session-entry briefing (replaces the deprecated `forge prime`)',
|
|
756
757
|
'--full': 'Also show blocked, stale, and recently completed issues',
|
|
757
758
|
'--json': 'Emit the machine-readable status envelope',
|
|
758
759
|
},
|
|
759
760
|
handler: async (args, flags, projectRoot) => {
|
|
761
|
+
// git-status-v semantics: bare status is the short pulse, -v is the long form.
|
|
762
|
+
// `--verbose` also arrives as flags.verbose from the global CLI flag parser.
|
|
763
|
+
const statusArgs = Array.isArray(args) ? args : [];
|
|
764
|
+
if (flags?.verbose === true || statusArgs.includes('-v') || statusArgs.includes('--verbose')) {
|
|
765
|
+
// Required lazily so the short pulse never loads the briefing's module graph.
|
|
766
|
+
const { renderBriefing } = require('./prime');
|
|
767
|
+
// The briefing reads --json from args (same as prime), so fold in the flag
|
|
768
|
+
// forms the short pulse accepts or `-v` would silently drop them.
|
|
769
|
+
const wantsJson = flags?.json === true || flags?.['--json'] === true;
|
|
770
|
+
const briefingArgs = wantsJson && !statusArgs.includes('--json')
|
|
771
|
+
? [...statusArgs, '--json']
|
|
772
|
+
: statusArgs;
|
|
773
|
+
return renderBriefing(briefingArgs, projectRoot || process.cwd());
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// Injectable snapshot read, same convention as setup.js's `options.commandRunner`
|
|
777
|
+
// (lib/commands/setup.js:223): a real default with a test-supplied override. Tests
|
|
778
|
+
// need a snapshot with controlled ids and orderings, which a seeded kernel cannot
|
|
779
|
+
// give them — the kernel stamps its own updated_at.
|
|
780
|
+
const readSnapshot = flags?.readStatusSnapshot || readStatusSnapshot;
|
|
760
781
|
const inputs = parseStatusInputs(args, flags);
|
|
761
782
|
const isZeroArgStatus = !inputs.issueId && !inputs.workflowState && !inputs.bdComments;
|
|
762
|
-
inputs.
|
|
783
|
+
inputs.preferIssueLookup = !isZeroArgStatus && Boolean(inputs.issueId || inputs.bdComments);
|
|
763
784
|
if (!inputs.projectRoot && projectRoot) {
|
|
764
785
|
inputs.projectRoot = projectRoot;
|
|
765
786
|
}
|
|
@@ -769,11 +790,11 @@ module.exports = {
|
|
|
769
790
|
|
|
770
791
|
if (isZeroArgStatus) {
|
|
771
792
|
context = detectRepoContext(effectiveProjectRoot);
|
|
772
|
-
// Read from the
|
|
773
|
-
//
|
|
793
|
+
// Read from the Kernel, the only issue backend. Previously hard-wired to
|
|
794
|
+
// the retired Beads
|
|
774
795
|
// issues.jsonl, which is empty on a kernel-default repo — so the flagship
|
|
775
796
|
// view showed "Ready: none" despite a populated kernel (bug 40f35797).
|
|
776
|
-
snapshot = await
|
|
797
|
+
snapshot = await readSnapshot(effectiveProjectRoot, {
|
|
777
798
|
now: inputs.now,
|
|
778
799
|
staleAfterDays: inputs.staleAfterDays,
|
|
779
800
|
issueBackend: inputs.issueBackend,
|
|
@@ -834,7 +855,7 @@ module.exports = {
|
|
|
834
855
|
extractWorkflowStateFromComments,
|
|
835
856
|
resolveWorkflowState,
|
|
836
857
|
parseStatusInputs,
|
|
837
|
-
|
|
858
|
+
readWorkflowStateFromIssue,
|
|
838
859
|
detectStage,
|
|
839
860
|
detectRepoContext,
|
|
840
861
|
discoverCurrentIssue,
|
|
@@ -842,7 +863,7 @@ module.exports = {
|
|
|
842
863
|
analyzeFiles,
|
|
843
864
|
analyzePR,
|
|
844
865
|
analyzeChecks,
|
|
845
|
-
|
|
866
|
+
analyzeIssue,
|
|
846
867
|
calculateConfidence,
|
|
847
868
|
formatStatus,
|
|
848
869
|
extractDesignSlugs,
|
package/lib/commands/test.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Wraps test execution with smart defaults:
|
|
7
7
|
* - Auto-detects package manager from lockfiles
|
|
8
|
-
* - Checks Beads (Dolt) connectivity, sets BEADS_SKIP_TESTS if unavailable
|
|
9
8
|
* - Supports --affected flag to run only tests for changed files
|
|
10
9
|
*
|
|
11
10
|
* Security: Uses execFileSync for subprocess calls (OWASP A03)
|
|
@@ -17,6 +16,8 @@ const { execFileSync: defaultExecFileSync, spawnSync: defaultSpawnSync } = requi
|
|
|
17
16
|
const defaultFs = require('node:fs');
|
|
18
17
|
const path = require('node:path');
|
|
19
18
|
|
|
19
|
+
const { selectDocAssertingTests } = require('../doc-assertions');
|
|
20
|
+
|
|
20
21
|
/** @type {Array<[string, string]>} Lockfile → package manager mapping (order matters) */
|
|
21
22
|
const LOCKFILE_MAP = [
|
|
22
23
|
['bun.lockb', 'bun'],
|
|
@@ -26,20 +27,53 @@ const LOCKFILE_MAP = [
|
|
|
26
27
|
];
|
|
27
28
|
|
|
28
29
|
const DEFAULT_TIMEOUT = 120000;
|
|
29
|
-
const BEADS_CHECK_TIMEOUT = 3000;
|
|
30
30
|
const DIRECT_TEST_CANDIDATES = Object.freeze({
|
|
31
31
|
'bin/forge.js': [
|
|
32
32
|
'test/cli-flags.test.js',
|
|
33
33
|
'test/forge-cli-registry.test.js',
|
|
34
|
-
'test/setup-github-sync.test.js',
|
|
35
34
|
'test/setup-runtime-flags.test.js',
|
|
36
35
|
],
|
|
36
|
+
// The second CLI surface (command descriptions + shepherd wiring). Without an
|
|
37
|
+
// entry here it resolves to zero tests and pushes fall back to the full suite.
|
|
38
|
+
'bin/forge-cmd.js': [
|
|
39
|
+
'test/cli/forge-cmd.test.js',
|
|
40
|
+
'test/forge-cmd-shepherd.test.js',
|
|
41
|
+
],
|
|
42
|
+
'bin/forge-preflight.js': ['test/bin/forge-preflight.test.js'],
|
|
43
|
+
// The CI entry point is covered by the doc-assertion regression suite; without
|
|
44
|
+
// this entry it resolves to zero tests and forces the full suite.
|
|
45
|
+
// (lib/doc-assertions.js resolves via the lib/ -> test/ convention below.)
|
|
46
|
+
'scripts/doc-asserting-tests.js': [
|
|
47
|
+
'test/doc-assertions.test.js',
|
|
48
|
+
'test/scripts/doc-asserting-tests.test.js',
|
|
49
|
+
],
|
|
37
50
|
'lib/lefthook-check.js': ['test/lefthook-check.test.js', 'test/runtime-health.test.js'],
|
|
38
51
|
'lib/runtime-health.js': ['test/runtime-health.test.js'],
|
|
52
|
+
// skill-eval hosts the accuracy-lint detectors (auditCommandDocumentation /
|
|
53
|
+
// auditRouterPrecision), so edits there must also run their detector suite.
|
|
54
|
+
'lib/skill-eval.js': ['test/skill-eval.test.js', 'test/skill-accuracy.test.js'],
|
|
39
55
|
'lib/upgrade-safety.js': ['test/commands/upgrade.test.js'],
|
|
40
56
|
'scripts/test.js': ['test/scripts/test-runner.test.js'],
|
|
41
57
|
});
|
|
42
58
|
|
|
59
|
+
// Skill sources (canonical `skills/**` and the committed `.agents/skills/**` mirror)
|
|
60
|
+
// map to the fast skill suite. This is what keeps a skills-only PR on the targeted
|
|
61
|
+
// lane instead of the full ~1500-test suite. `skills-sync-drift` guards the mirror.
|
|
62
|
+
const SKILL_TEST_TARGETS = Object.freeze([
|
|
63
|
+
'test/skills-structure.test.js',
|
|
64
|
+
'test/skill-coverage.test.js',
|
|
65
|
+
'test/skill-eval.test.js',
|
|
66
|
+
'test/skill-accuracy.test.js',
|
|
67
|
+
'test/skill-dispatch-parity.test.js',
|
|
68
|
+
'test/using-forge.test.js',
|
|
69
|
+
'test/structural/skills-sync-drift.test.js',
|
|
70
|
+
'test/skills/chain-integrity.test.js',
|
|
71
|
+
'test/skills/skills-sync.test.js',
|
|
72
|
+
'test/skills/stage-skills.test.js',
|
|
73
|
+
'test/skills/using-forge-skill.test.js',
|
|
74
|
+
'test/skills/context-cost.test.js',
|
|
75
|
+
]);
|
|
76
|
+
|
|
43
77
|
/**
|
|
44
78
|
* Detect the package manager by checking which lockfile exists.
|
|
45
79
|
*
|
|
@@ -56,22 +90,6 @@ function detectPackageManager(projectRoot, fs) {
|
|
|
56
90
|
return 'npm';
|
|
57
91
|
}
|
|
58
92
|
|
|
59
|
-
/**
|
|
60
|
-
* Check if Beads (bd CLI) is reachable.
|
|
61
|
-
*
|
|
62
|
-
* @param {Function} execFileSync - Injected execFileSync
|
|
63
|
-
* @returns {boolean} true if bd is available
|
|
64
|
-
*/
|
|
65
|
-
function checkBeadsConnectivity(execFileSync) {
|
|
66
|
-
try {
|
|
67
|
-
execFileSync('bd', ['list', '--limit=1'], { timeout: BEADS_CHECK_TIMEOUT });
|
|
68
|
-
return true;
|
|
69
|
-
} catch (_e) { // NOSONAR S2486
|
|
70
|
-
/* intentional: bd not installed or unreachable */
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
93
|
function resolveBaseBranch(execFileSync) {
|
|
76
94
|
let baseBranch = 'main';
|
|
77
95
|
try {
|
|
@@ -168,6 +186,16 @@ function getAffectedTestFiles(projectRoot, execFileSync, fs = defaultFs, options
|
|
|
168
186
|
}
|
|
169
187
|
}
|
|
170
188
|
|
|
189
|
+
// Markdown changes must also run the suites that assert on that markdown's
|
|
190
|
+
// CONTENT (README size badge, AGENTS.md convention, docs consistency). The
|
|
191
|
+
// static mapping above only knows the docs-consistency suite, so a docs PR
|
|
192
|
+
// used to merge green and break master for the next code PR (issue 63556816).
|
|
193
|
+
// The set is derived from the test sources, so new markdown-reading suites are
|
|
194
|
+
// picked up without editing a list here.
|
|
195
|
+
for (const candidate of selectDocAssertingTests(changedFiles, projectRoot, fs)) {
|
|
196
|
+
testFiles.add(candidate);
|
|
197
|
+
}
|
|
198
|
+
|
|
171
199
|
return Array.from(testFiles).sort((left, right) => left.localeCompare(right));
|
|
172
200
|
}
|
|
173
201
|
|
|
@@ -183,6 +211,10 @@ function getTestCandidatesForChangedFile(file) {
|
|
|
183
211
|
return [file];
|
|
184
212
|
}
|
|
185
213
|
|
|
214
|
+
if (file.startsWith('skills/') || file.startsWith('.agents/skills/')) {
|
|
215
|
+
return [...SKILL_TEST_TARGETS];
|
|
216
|
+
}
|
|
217
|
+
|
|
186
218
|
if (file === 'README.md'
|
|
187
219
|
|| file === 'QUICKSTART.md'
|
|
188
220
|
|| file === 'CHANGELOG.md'
|
|
@@ -259,7 +291,7 @@ module.exports = {
|
|
|
259
291
|
* @param {Object} [deps.fs] - fs module
|
|
260
292
|
* @param {Function} [deps.execFileSync] - child_process.execFileSync
|
|
261
293
|
* @param {Function} [deps.spawnSync] - child_process.spawnSync
|
|
262
|
-
* @returns {Promise<{ success: boolean, exitCode: number
|
|
294
|
+
* @returns {Promise<{ success: boolean, exitCode: number }>}
|
|
263
295
|
*/
|
|
264
296
|
async handler(_args, flags, projectRoot, deps = {}) {
|
|
265
297
|
const fs = deps.fs || defaultFs;
|
|
@@ -281,19 +313,10 @@ module.exports = {
|
|
|
281
313
|
}
|
|
282
314
|
} catch (_e) { /* intentional: package.json missing or unreadable, use default timeout */ } // NOSONAR S2486
|
|
283
315
|
|
|
284
|
-
// 3.
|
|
285
|
-
const beadsAvailable = checkBeadsConnectivity(execFileSync);
|
|
286
|
-
const beadsSkipped = !beadsAvailable;
|
|
287
|
-
|
|
288
|
-
const extraEnv = {};
|
|
289
|
-
if (beadsSkipped) {
|
|
290
|
-
extraEnv.BEADS_SKIP_TESTS = '1';
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// 4. Build test command args
|
|
316
|
+
// 3. Build test command args
|
|
294
317
|
let testArgs = ['run', 'test'];
|
|
295
318
|
|
|
296
|
-
//
|
|
319
|
+
// 4. --affected flag: find changed test files
|
|
297
320
|
if (flags['--affected'] || flags.affected) {
|
|
298
321
|
const affectedTests = getAffectedTestFiles(projectRoot, execFileSync, fs, {
|
|
299
322
|
sinceUpstream: flags.sinceUpstream || flags['--since-upstream'],
|
|
@@ -304,9 +327,9 @@ module.exports = {
|
|
|
304
327
|
// If no affected tests found, fall back to running all tests
|
|
305
328
|
}
|
|
306
329
|
|
|
307
|
-
//
|
|
330
|
+
// 5. Run tests
|
|
308
331
|
const result = spawnSync(pkgManager, testArgs, {
|
|
309
|
-
env: { ...process.env
|
|
332
|
+
env: { ...process.env },
|
|
310
333
|
timeout,
|
|
311
334
|
stdio: 'inherit',
|
|
312
335
|
shell: process.platform === 'win32',
|
|
@@ -317,7 +340,6 @@ module.exports = {
|
|
|
317
340
|
return {
|
|
318
341
|
success: exitCode === 0,
|
|
319
342
|
exitCode,
|
|
320
|
-
beadsSkipped,
|
|
321
343
|
};
|
|
322
344
|
},
|
|
323
345
|
};
|