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/setup.js
CHANGED
|
@@ -28,6 +28,7 @@ const VERSION = packageJson.version;
|
|
|
28
28
|
// packaged-ASSET read below goes through `getPackageRoot(packageDir)`; the
|
|
29
29
|
// module-`require` paths above keep `packageDir` (bundler handles those).
|
|
30
30
|
const { getPackageRoot } = require('../package-root');
|
|
31
|
+
const { cleanupDeprecatedSyncFiles } = require('../deprecated-sync-cleanup');
|
|
31
32
|
|
|
32
33
|
// Load PluginManager for discoverable agent architecture
|
|
33
34
|
const PluginManager = require('../plugin-manager');
|
|
@@ -53,9 +54,8 @@ const { askYesNo: _askYesNoBase } = require('../ui-utils');
|
|
|
53
54
|
const contextMerge = require('../context-merge');
|
|
54
55
|
const projectDiscovery = require('../project-discovery');
|
|
55
56
|
|
|
56
|
-
// Load lib modules for symlink
|
|
57
|
+
// Load lib modules for symlink setup
|
|
57
58
|
const { createSymlinkOrCopy: libCreateSymlinkOrCopy } = require('../symlink-utils');
|
|
58
|
-
const { scaffoldBeadsSync } = require('../beads-sync-scaffold');
|
|
59
59
|
const { resolveSyncBackend } = require('../sync-backend');
|
|
60
60
|
const { buildMigratedKernelIssueDeps } = require('../kernel/cli-broker-factory');
|
|
61
61
|
|
|
@@ -69,11 +69,13 @@ const { smartMergeAgentsMd } = require('../smart-merge');
|
|
|
69
69
|
const { checkLefthookStatus } = require('../lefthook-check');
|
|
70
70
|
const {
|
|
71
71
|
FORGE_USER_LEFTHOOK_YML,
|
|
72
|
+
FORGE_USER_LEFTHOOK_YML_NO_TDD,
|
|
72
73
|
forgeShouldWriteLefthookConfig,
|
|
73
74
|
installNativeGitHooks,
|
|
74
75
|
verifyHooksActive,
|
|
75
76
|
resolveGitHooksDir,
|
|
76
77
|
} = require('../lefthook-wiring');
|
|
78
|
+
const { detectExistingTddGate, describeExistingGateDeferral } = require('../existing-tdd-gate');
|
|
77
79
|
const { resolveShellRuntime } = require('../runtime-health');
|
|
78
80
|
const {
|
|
79
81
|
buildCodexSkillInstallPlan,
|
|
@@ -99,7 +101,6 @@ let FORCE_MODE = false;
|
|
|
99
101
|
let VERBOSE_MODE = false;
|
|
100
102
|
let NON_INTERACTIVE = false;
|
|
101
103
|
let SYMLINK_ONLY = false;
|
|
102
|
-
let SYNC_ENABLED = false;
|
|
103
104
|
let actionLog = new SetupActionLog();
|
|
104
105
|
let PKG_MANAGER = 'npm';
|
|
105
106
|
let SETUP_NOTES = [];
|
|
@@ -174,7 +175,6 @@ function detectPackageManager() {
|
|
|
174
175
|
|
|
175
176
|
|
|
176
177
|
const WORKFLOW_RUNTIME_ASSETS = Object.freeze([
|
|
177
|
-
'scripts/beads-context.sh',
|
|
178
178
|
'scripts/conflict-detect.sh',
|
|
179
179
|
'scripts/dep-guard-analyze.js',
|
|
180
180
|
'scripts/dep-guard.sh',
|
|
@@ -188,7 +188,6 @@ const WORKFLOW_RUNTIME_ASSETS = Object.freeze([
|
|
|
188
188
|
'scripts/forge-team/index.sh',
|
|
189
189
|
'scripts/forge-team/lib/agent-prompt.sh',
|
|
190
190
|
'scripts/forge-team/lib/claim.sh',
|
|
191
|
-
'scripts/forge-team/lib/dashboard.sh',
|
|
192
191
|
'scripts/forge-team/lib/epic.sh',
|
|
193
192
|
'scripts/forge-team/lib/hooks.sh',
|
|
194
193
|
'scripts/forge-team/lib/identity.sh',
|
|
@@ -219,7 +218,6 @@ function validateAgents(agentList) {
|
|
|
219
218
|
// Prerequisite check function
|
|
220
219
|
function checkPrerequisites(options = {}) {
|
|
221
220
|
const requireGithubCli = options.requireGithubCli !== false;
|
|
222
|
-
const requireBeadsCli = options.requireBeadsCli === true;
|
|
223
221
|
const requireJq = options.requireJq === true;
|
|
224
222
|
const commandRunner = options.commandRunner || safeExec;
|
|
225
223
|
const errors = [];
|
|
@@ -256,15 +254,13 @@ function checkPrerequisites(options = {}) {
|
|
|
256
254
|
}
|
|
257
255
|
}
|
|
258
256
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
console.log(' ✓ Kernel issue store');
|
|
267
|
-
}
|
|
257
|
+
// Issue tracking runs on the local Forge Kernel store, which auto-provisions
|
|
258
|
+
// on first use — there is no external CLI to install. Surface only whether
|
|
259
|
+
// team sync is wired up yet.
|
|
260
|
+
if (resolveSyncBackend({ projectRoot, env: options.env }) === 'local-noop') {
|
|
261
|
+
console.log(' ✓ Kernel issue store (local-noop sync: single-machine until a sync server is configured)');
|
|
262
|
+
} else {
|
|
263
|
+
console.log(' ✓ Kernel issue store');
|
|
268
264
|
}
|
|
269
265
|
|
|
270
266
|
// Check Node.js version
|
|
@@ -801,9 +797,7 @@ async function detectProjectStatus() {
|
|
|
801
797
|
claudeMdLines: 0,
|
|
802
798
|
// Project tools status — the Kernel issue store is always present (it
|
|
803
799
|
// auto-provisions on first use), so issue tracking needs no install probe.
|
|
804
|
-
hasBeads: true,
|
|
805
800
|
hasSkills: isSkillsInitialized(),
|
|
806
|
-
beadsInstallType: 'kernel',
|
|
807
801
|
skillsInstallType: checkForSkills(),
|
|
808
802
|
// Enhanced: Auto-detected project context
|
|
809
803
|
autoDetected: null
|
|
@@ -2064,7 +2058,8 @@ function createAgentLinkFile(agent, symlinkOnly = false) {
|
|
|
2064
2058
|
|
|
2065
2059
|
const result = createSymlinkOrCopy('AGENTS.md', agent.linkFile, { symlinkOnly });
|
|
2066
2060
|
if (result) {
|
|
2067
|
-
|
|
2061
|
+
const action = result === 'linked' ? 'Linked' : result === 'copied' ? 'Copied' : 'Preserved import';
|
|
2062
|
+
console.log(` ${action}: ${agent.linkFile}`);
|
|
2068
2063
|
}
|
|
2069
2064
|
}
|
|
2070
2065
|
|
|
@@ -2072,7 +2067,7 @@ function createAgentLinkFile(agent, symlinkOnly = false) {
|
|
|
2072
2067
|
|
|
2073
2068
|
|
|
2074
2069
|
// Setup specific agent
|
|
2075
|
-
async function setupAgent(agentKey, skipFiles = {}) {
|
|
2070
|
+
async function setupAgent(agentKey, skipFiles = {}, options = {}) {
|
|
2076
2071
|
const agent = AGENTS[agentKey];
|
|
2077
2072
|
if (!agent) return;
|
|
2078
2073
|
|
|
@@ -2112,11 +2107,13 @@ async function setupAgent(agentKey, skipFiles = {}) {
|
|
|
2112
2107
|
// Projects Forge's TDD-gate + protected-path enforcement onto each harness's
|
|
2113
2108
|
// native hook surface. Codex hooks are GLOBAL-config scope and intentionally
|
|
2114
2109
|
// not written at project setup (see lib/hook-renderer.js).
|
|
2115
|
-
if (
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2110
|
+
if (!options.skillsOnly) {
|
|
2111
|
+
if (agentKey === 'claude') {
|
|
2112
|
+
setupClaudeHooksConfig();
|
|
2113
|
+
}
|
|
2114
|
+
if (agent.customSetup === 'cursor') {
|
|
2115
|
+
setupCursorHooksConfig();
|
|
2116
|
+
}
|
|
2120
2117
|
}
|
|
2121
2118
|
|
|
2122
2119
|
// Create link file (SYMLINK_ONLY = --symlink flag disables copy fallback)
|
|
@@ -2757,6 +2754,48 @@ function resolveHookEnforcementState(root = projectRoot) {
|
|
|
2757
2754
|
}
|
|
2758
2755
|
}
|
|
2759
2756
|
|
|
2757
|
+
// True when .forge/config.yaml already states a rail.tdd_intent choice EXPLICITLY (either
|
|
2758
|
+
// config shape the resolver accepts). Setup must never overwrite that: otherwise the
|
|
2759
|
+
// `forge gate enable rail.tdd_intent` we advertise would be undone by the next `forge setup`.
|
|
2760
|
+
function hasExplicitTddRailChoice(root) {
|
|
2761
|
+
try {
|
|
2762
|
+
const { loadRawConfig } = require('../config-writer');
|
|
2763
|
+
const config = loadRawConfig(root) || {};
|
|
2764
|
+
const viaGates = config.workflow && config.workflow.gates && config.workflow.gates['rail.tdd_intent'];
|
|
2765
|
+
const viaRails = config.rails && config.rails['rail.tdd_intent'];
|
|
2766
|
+
return Boolean(
|
|
2767
|
+
(viaGates && viaGates.enabled !== undefined) || (viaRails && viaRails.enabled !== undefined)
|
|
2768
|
+
);
|
|
2769
|
+
} catch {
|
|
2770
|
+
return false; // unreadable config → treat as unset; setConfigOverride reports any real failure
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
|
|
2774
|
+
/**
|
|
2775
|
+
* Defer Forge's TDD gate to a pre-existing one: turn `rail.tdd_intent` off in
|
|
2776
|
+
* .forge/config.yaml so the installed hook scripts are genuinely inert, instead of stacking a
|
|
2777
|
+
* second gate on the same commit (kernel 5b425a85 / 2699b234). No-op when nothing was detected
|
|
2778
|
+
* (a repo with no gate keeps the default-ON rail) or when the user already chose explicitly.
|
|
2779
|
+
*
|
|
2780
|
+
* @returns {{ deferred: boolean, reason?: string }}
|
|
2781
|
+
*/
|
|
2782
|
+
function applyExistingGateDeferral(root, detection) {
|
|
2783
|
+
if (!detection || !detection.found) return { deferred: false, reason: 'no-existing-gate' };
|
|
2784
|
+
if (hasExplicitTddRailChoice(root)) return { deferred: false, reason: 'explicit-user-choice' };
|
|
2785
|
+
|
|
2786
|
+
const { setConfigOverride } = require('../config-writer');
|
|
2787
|
+
try {
|
|
2788
|
+
setConfigOverride(root, ['workflow', 'gates', 'rail.tdd_intent', 'enabled'], false);
|
|
2789
|
+
} catch (error) {
|
|
2790
|
+
// An unreadable/malformed .forge/config.yaml must not make `forge setup` fail outright
|
|
2791
|
+
// just because a pre-existing gate was detected. Report and DON'T defer: the caller keys
|
|
2792
|
+
// hook wiring off `deferred`, so falling back to the normal install keeps config and
|
|
2793
|
+
// wiring agreeing, instead of turning the gate off in neither place or in only one.
|
|
2794
|
+
return { deferred: false, reason: 'config-write-failed', error: error.message };
|
|
2795
|
+
}
|
|
2796
|
+
return { deferred: true };
|
|
2797
|
+
}
|
|
2798
|
+
|
|
2760
2799
|
function describeHookEnforcement(root = projectRoot) {
|
|
2761
2800
|
const state = resolveHookEnforcementState(root);
|
|
2762
2801
|
if (!state.resolved) {
|
|
@@ -2838,6 +2877,32 @@ function installGitHooks(options = {}) { // NOSONAR — Extracted as-is from bin
|
|
|
2838
2877
|
// init deliberately degrades to a warning rather than failing, and repair runs inside
|
|
2839
2878
|
// another command's flow.
|
|
2840
2879
|
const loud = options.loud === true;
|
|
2880
|
+
|
|
2881
|
+
// BEFORE wiring anything: does this repo already enforce TDD/source-test coupling on
|
|
2882
|
+
// pre-commit? If so, defer to it rather than silently stacking a second gate on the same
|
|
2883
|
+
// commit — the in-the-wild beta.3 report (kernel 5b425a85 / 2699b234). The deferral is
|
|
2884
|
+
// VISIBLE (report below) and reversible with one command.
|
|
2885
|
+
const existingGate = detectExistingTddGate(projectRoot);
|
|
2886
|
+
const deferral = applyExistingGateDeferral(projectRoot, existingGate);
|
|
2887
|
+
// Only claim a deferral when one actually happened. describeExistingGateDeferral() states
|
|
2888
|
+
// that Forge did NOT install its gate and that rail.tdd_intent is off — both false when we
|
|
2889
|
+
// declined to defer, so printing it unconditionally would misreport the installed state.
|
|
2890
|
+
if (deferral.deferred) {
|
|
2891
|
+
console.warn(describeExistingGateDeferral(existingGate));
|
|
2892
|
+
addSetupNote(`Deferred Forge's TDD gate to the existing pre-commit gate in ${existingGate.source}.`);
|
|
2893
|
+
} else if (existingGate.found) {
|
|
2894
|
+
const why = deferral.reason === 'explicit-user-choice'
|
|
2895
|
+
? 'rail.tdd_intent is set explicitly in .forge/config.yaml, so your choice stands'
|
|
2896
|
+
: `Forge could not update .forge/config.yaml (${deferral.error})`;
|
|
2897
|
+
console.warn(
|
|
2898
|
+
` ⚠ Detected an existing pre-commit TDD/coupling gate in ${existingGate.source}, but did NOT defer to it:\n` +
|
|
2899
|
+
` ${why}.\n` +
|
|
2900
|
+
' Forge\'s own TDD gate is installed as usual, so BOTH gates run on the same commit.\n' +
|
|
2901
|
+
' Want only yours? Run: forge gate disable rail.tdd_intent'
|
|
2902
|
+
);
|
|
2903
|
+
addSetupNote(`Existing pre-commit gate in ${existingGate.source} detected; Forge's TDD gate installed anyway (${deferral.reason}).`);
|
|
2904
|
+
}
|
|
2905
|
+
|
|
2841
2906
|
// Honest header: when the TDD gate is disabled in config the hooks install but are inert,
|
|
2842
2907
|
// so don't announce "(TDD enforcement)" for a state the user turned off (issue eda6d866).
|
|
2843
2908
|
console.log(
|
|
@@ -2877,7 +2942,18 @@ function installGitHooks(options = {}) { // NOSONAR — Extracted as-is from bin
|
|
|
2877
2942
|
// clobber a config that already has active jobs (kernel c713fce7).
|
|
2878
2943
|
const lefthookTarget = path.join(projectRoot, 'lefthook.yml');
|
|
2879
2944
|
if (forgeShouldWriteLefthookConfig(lefthookTarget)) {
|
|
2880
|
-
|
|
2945
|
+
// When we actually DEFERRED, write the variant WITHOUT Forge's forge-tdd job so the
|
|
2946
|
+
// commit is never blocked by two gates. Key this off `deferral.deferred`, not off
|
|
2947
|
+
// `existingGate.found`: applyExistingGateDeferral declines to defer when the user has
|
|
2948
|
+
// an explicit rail.tdd_intent choice, and suppressing the wiring anyway would leave
|
|
2949
|
+
// config saying "enabled" while the hook was never installed — making
|
|
2950
|
+
// `forge gate enable rail.tdd_intent` silently ineffective in exactly the repos this
|
|
2951
|
+
// feature targets.
|
|
2952
|
+
fs.writeFileSync(
|
|
2953
|
+
lefthookTarget,
|
|
2954
|
+
deferral.deferred ? FORGE_USER_LEFTHOOK_YML_NO_TDD : FORGE_USER_LEFTHOOK_YML,
|
|
2955
|
+
'utf8'
|
|
2956
|
+
);
|
|
2881
2957
|
actionLog.add('lefthook.yml', 'created');
|
|
2882
2958
|
console.log(' ✓ Created lefthook.yml');
|
|
2883
2959
|
}
|
|
@@ -2925,7 +3001,12 @@ function installGitHooks(options = {}) { // NOSONAR — Extracted as-is from bin
|
|
|
2925
3001
|
// inert (B3). This repair path only warns (never sets a failure exit code) since it can
|
|
2926
3002
|
// run mid-stage under enforce-stage's repairWorkflowRuntimeAssets().
|
|
2927
3003
|
if (!lefthookInstalled) {
|
|
2928
|
-
|
|
3004
|
+
// Skip the native pre-commit hook when the repo already has its own TDD/coupling gate —
|
|
3005
|
+
// pre-push is still wired, so only the double-gated surface is given up.
|
|
3006
|
+
const native = installNativeGitHooks(
|
|
3007
|
+
projectRoot,
|
|
3008
|
+
existingGate.found ? { skipHooks: ['pre-commit'] } : {}
|
|
3009
|
+
);
|
|
2929
3010
|
if (native.installed) {
|
|
2930
3011
|
console.log(` ✓ Native git hooks installed (${native.written.join(', ')}) - lefthook fallback`);
|
|
2931
3012
|
} else if (native.skipped && native.skipped.length > 0) {
|
|
@@ -3094,7 +3175,7 @@ function initializeSkills(installType) {
|
|
|
3094
3175
|
|
|
3095
3176
|
// Ensure the issue store during interactive setup. No prompt: the Forge Kernel
|
|
3096
3177
|
// ships with Forge and auto-provisions, so there is nothing to install or pick.
|
|
3097
|
-
async function
|
|
3178
|
+
async function promptIssueStoreSetup(_question) {
|
|
3098
3179
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
3099
3180
|
console.log('Issue Store (Forge Kernel)');
|
|
3100
3181
|
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
@@ -3220,17 +3301,14 @@ async function promptSkillsSetup(question) {
|
|
|
3220
3301
|
console.log('');
|
|
3221
3302
|
}
|
|
3222
3303
|
|
|
3223
|
-
// Interactive setup for
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
// Interactive setup for Beads and Skills
|
|
3304
|
+
// Interactive setup for the issue store and Skills
|
|
3227
3305
|
async function setupProjectTools(rl, question) {
|
|
3228
3306
|
console.log('');
|
|
3229
3307
|
console.log('═══════════════════════════════════════════════════════════');
|
|
3230
3308
|
console.log(' STEP 2: Project Tools (Recommended)');
|
|
3231
3309
|
console.log('═══════════════════════════════════════════════════════════');
|
|
3232
3310
|
console.log('');
|
|
3233
|
-
console.log('Forge recommends
|
|
3311
|
+
console.log('Forge recommends these tools for enhanced workflows:');
|
|
3234
3312
|
console.log('');
|
|
3235
3313
|
console.log('• Issue tracking (Forge Kernel) - zero-install, git-backed');
|
|
3236
3314
|
console.log(' Persists tasks across sessions, tracks dependencies.');
|
|
@@ -3242,7 +3320,7 @@ async function setupProjectTools(rl, question) {
|
|
|
3242
3320
|
console.log('');
|
|
3243
3321
|
|
|
3244
3322
|
// Use helper functions to reduce complexity
|
|
3245
|
-
await
|
|
3323
|
+
await promptIssueStoreSetup(question);
|
|
3246
3324
|
await promptSkillsSetup(question);
|
|
3247
3325
|
}
|
|
3248
3326
|
|
|
@@ -3396,7 +3474,8 @@ async function ensureGitHooksInstalled(targetRoot = projectRoot) {
|
|
|
3396
3474
|
|
|
3397
3475
|
|
|
3398
3476
|
// Quick setup with defaults
|
|
3399
|
-
async function quickSetup(selectedAgents, skipExternal) {
|
|
3477
|
+
async function quickSetup(selectedAgents, skipExternal, options = {}) {
|
|
3478
|
+
const skillsOnly = options.skillsOnly === true;
|
|
3400
3479
|
showBanner('Quick Setup');
|
|
3401
3480
|
console.log('');
|
|
3402
3481
|
console.log('Quick mode: Using defaults...');
|
|
@@ -3404,7 +3483,6 @@ async function quickSetup(selectedAgents, skipExternal) {
|
|
|
3404
3483
|
|
|
3405
3484
|
// Check prerequisites
|
|
3406
3485
|
checkPrerequisites({
|
|
3407
|
-
requireBeadsCli: true,
|
|
3408
3486
|
requireGithubCli: true,
|
|
3409
3487
|
requireJq: true,
|
|
3410
3488
|
});
|
|
@@ -3424,41 +3502,46 @@ async function quickSetup(selectedAgents, skipExternal) {
|
|
|
3424
3502
|
|
|
3425
3503
|
ensureWorkflowShellPolicy(selectedAgents);
|
|
3426
3504
|
|
|
3427
|
-
// Auto-install lefthook if missing
|
|
3428
|
-
|
|
3505
|
+
// Auto-install lefthook if missing unless the caller explicitly requested skills only.
|
|
3506
|
+
if (!skillsOnly) {
|
|
3507
|
+
autoInstallLefthook();
|
|
3508
|
+
}
|
|
3429
3509
|
|
|
3430
3510
|
// Auto-setup project tools (Kernel issue store, Skills)
|
|
3431
3511
|
await autoSetupToolsInQuickMode();
|
|
3432
3512
|
|
|
3433
3513
|
// Setup Claude first if selected, then setup remaining agents
|
|
3434
3514
|
if (selectedAgents.includes('claude')) {
|
|
3435
|
-
await setupAgent('claude');
|
|
3515
|
+
await setupAgent('claude', {}, { skillsOnly });
|
|
3436
3516
|
}
|
|
3437
|
-
await setupSelectedAgents(selectedAgents);
|
|
3517
|
+
await setupSelectedAgents(selectedAgents, undefined, { skillsOnly });
|
|
3438
3518
|
ensureWorkflowRuntimeAssets(selectedAgents);
|
|
3439
3519
|
|
|
3440
3520
|
// Detect Husky and migrate before installing Lefthook hooks
|
|
3441
|
-
|
|
3521
|
+
if (!skillsOnly) {
|
|
3522
|
+
await handleHuskyMigration();
|
|
3523
|
+
}
|
|
3442
3524
|
|
|
3443
3525
|
// Install git hooks for TDD enforcement. LOUD: this is the `forge setup` handler,
|
|
3444
3526
|
// so an inert-hooks result must fail non-zero (B3), not end green.
|
|
3445
3527
|
console.log('');
|
|
3446
|
-
|
|
3528
|
+
if (skillsOnly) {
|
|
3529
|
+
console.log('Skipping git and harness hooks (--skills-only).');
|
|
3530
|
+
} else {
|
|
3531
|
+
installGitHooks({ loud: true });
|
|
3532
|
+
}
|
|
3447
3533
|
|
|
3448
3534
|
// Configure external services with defaults (unless skipped)
|
|
3449
3535
|
configureDefaultExternalServices(skipExternal);
|
|
3450
3536
|
|
|
3451
|
-
|
|
3452
|
-
if (SYNC_ENABLED) {
|
|
3453
|
-
await handleSyncScaffold();
|
|
3454
|
-
}
|
|
3537
|
+
removeDeprecatedSyncFiles();
|
|
3455
3538
|
|
|
3456
3539
|
// Progressive setup summary
|
|
3457
3540
|
console.log('');
|
|
3458
3541
|
console.log(renderSetupSummary(actionLog, selectedAgents, VERBOSE_MODE, { status: getSetupSummaryStatus() }));
|
|
3459
3542
|
printSetupNotes();
|
|
3460
3543
|
// One-step onboarding: run `forge init` when config is still absent (ac0b38c7).
|
|
3461
|
-
// Hooks +
|
|
3544
|
+
// Hooks + issue store already handled above, so init skips those side effects.
|
|
3462
3545
|
await finalizeWorkflowConfig({ hooksAlreadyInstalled: true });
|
|
3463
3546
|
console.log('');
|
|
3464
3547
|
}
|
|
@@ -3618,13 +3701,13 @@ async function promptForOverwriteDecisions(question, projectStatus, flags = {})
|
|
|
3618
3701
|
|
|
3619
3702
|
|
|
3620
3703
|
// Helper: Setup all selected agents - extracted to reduce cognitive complexity
|
|
3621
|
-
async function setupSelectedAgents(selectedAgents, skipFiles) {
|
|
3704
|
+
async function setupSelectedAgents(selectedAgents, skipFiles, options = {}) {
|
|
3622
3705
|
const totalAgents = selectedAgents.length;
|
|
3623
3706
|
for (const [index, agentKey] of selectedAgents.entries()) {
|
|
3624
3707
|
const agent = AGENTS[agentKey];
|
|
3625
3708
|
console.log(`\n[${index + 1}/${totalAgents}] Setting up ${agent.name}...`);
|
|
3626
3709
|
if (agentKey !== 'claude') { // Claude already done above
|
|
3627
|
-
await setupAgent(agentKey, skipFiles);
|
|
3710
|
+
await setupAgent(agentKey, skipFiles, options);
|
|
3628
3711
|
}
|
|
3629
3712
|
}
|
|
3630
3713
|
|
|
@@ -3692,7 +3775,6 @@ async function interactiveSetupWithFlags(flags) {
|
|
|
3692
3775
|
|
|
3693
3776
|
// Check agent-independent prerequisites first
|
|
3694
3777
|
checkPrerequisites({
|
|
3695
|
-
requireBeadsCli: true,
|
|
3696
3778
|
requireGithubCli: false,
|
|
3697
3779
|
requireJq: true,
|
|
3698
3780
|
});
|
|
@@ -3741,11 +3823,11 @@ async function interactiveSetupWithFlags(flags) {
|
|
|
3741
3823
|
|
|
3742
3824
|
// Setup Claude first if selected (delegated to helper), then remaining agents
|
|
3743
3825
|
if (selectedAgents.includes('claude')) {
|
|
3744
|
-
await setupAgent('claude', skipFiles);
|
|
3826
|
+
await setupAgent('claude', skipFiles, flags);
|
|
3745
3827
|
}
|
|
3746
3828
|
|
|
3747
3829
|
// Setup each selected agent with progress indication (delegated to helper)
|
|
3748
|
-
await setupSelectedAgents(selectedAgents, skipFiles);
|
|
3830
|
+
await setupSelectedAgents(selectedAgents, skipFiles, flags);
|
|
3749
3831
|
ensureWorkflowRuntimeAssets(selectedAgents);
|
|
3750
3832
|
|
|
3751
3833
|
// Handle external services step (delegated to helper)
|
|
@@ -3759,7 +3841,7 @@ async function interactiveSetupWithFlags(flags) {
|
|
|
3759
3841
|
|
|
3760
3842
|
// One-step onboarding: this path never writes .forge/config.yaml itself, so
|
|
3761
3843
|
// run `forge init` when the config is still absent (ac0b38c7).
|
|
3762
|
-
await finalizeWorkflowConfig();
|
|
3844
|
+
await finalizeWorkflowConfig({ hooksAlreadyInstalled: flags.skillsOnly === true });
|
|
3763
3845
|
}
|
|
3764
3846
|
|
|
3765
3847
|
// Main
|
|
@@ -3832,7 +3914,7 @@ function determineSelectedAgents(flags) {
|
|
|
3832
3914
|
// Shared setup executor — used by handleSetupCommand
|
|
3833
3915
|
|
|
3834
3916
|
// Dry-run setup — enumerate planned actions without writing files
|
|
3835
|
-
function dryRunSetup(agents) { // NOSONAR — Extracted as-is from bin/forge.js; complexity reduction deferred
|
|
3917
|
+
function dryRunSetup(agents, options = {}) { // NOSONAR — Extracted as-is from bin/forge.js; complexity reduction deferred
|
|
3836
3918
|
const collector = new ActionCollector();
|
|
3837
3919
|
|
|
3838
3920
|
// Helper: add create or skip based on whether file exists
|
|
@@ -3902,10 +3984,12 @@ function dryRunSetup(agents) { // NOSONAR — Extracted as-is from bin/forge.js;
|
|
|
3902
3984
|
}
|
|
3903
3985
|
}
|
|
3904
3986
|
|
|
3905
|
-
// Git hooks
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
|
|
3987
|
+
// Git and harness hooks are opt-out for skills-only setup.
|
|
3988
|
+
if (options.skillsOnly !== true) {
|
|
3989
|
+
addFileAction('lefthook.yml', 'Git hook configuration');
|
|
3990
|
+
addFileAction('.forge/hooks/check-tdd.js', 'TDD enforcement hook');
|
|
3991
|
+
addFileAction('.forge/hooks/forge-native-hook.js', 'Native-hook enforcement adapter (Claude/Cursor)');
|
|
3992
|
+
}
|
|
3909
3993
|
|
|
3910
3994
|
// Print dry-run summary
|
|
3911
3995
|
console.log('');
|
|
@@ -3920,14 +4004,19 @@ function dryRunSetup(agents) { // NOSONAR — Extracted as-is from bin/forge.js;
|
|
|
3920
4004
|
|
|
3921
4005
|
|
|
3922
4006
|
async function executeSetup(config) {
|
|
3923
|
-
const {
|
|
4007
|
+
const {
|
|
4008
|
+
agents,
|
|
4009
|
+
skipExternal,
|
|
4010
|
+
keepExisting = false,
|
|
4011
|
+
commandRunner,
|
|
4012
|
+
skillsOnly = false,
|
|
4013
|
+
} = config;
|
|
3924
4014
|
|
|
3925
4015
|
showBanner('Installing for specified agents...');
|
|
3926
4016
|
console.log('');
|
|
3927
4017
|
|
|
3928
4018
|
// Check prerequisites
|
|
3929
4019
|
checkPrerequisites({
|
|
3930
|
-
requireBeadsCli: true,
|
|
3931
4020
|
requireGithubCli: requiresGithubCliForSetup(agents),
|
|
3932
4021
|
requireJq: true,
|
|
3933
4022
|
commandRunner,
|
|
@@ -3954,60 +4043,62 @@ async function executeSetup(config) {
|
|
|
3954
4043
|
|
|
3955
4044
|
// Setup Claude first if selected, then remaining agents
|
|
3956
4045
|
if (agents.includes('claude')) {
|
|
3957
|
-
await setupAgent('claude', skipFiles);
|
|
4046
|
+
await setupAgent('claude', skipFiles, { skillsOnly });
|
|
3958
4047
|
}
|
|
3959
|
-
await setupSelectedAgents(agents, skipFiles);
|
|
4048
|
+
await setupSelectedAgents(agents, skipFiles, { skillsOnly });
|
|
3960
4049
|
ensureWorkflowRuntimeAssets(agents);
|
|
3961
4050
|
ensureWorkflowShellPolicy(agents);
|
|
3962
|
-
|
|
4051
|
+
if (!skillsOnly) {
|
|
4052
|
+
repairDeclaredLefthookDependency(agents);
|
|
4053
|
+
}
|
|
3963
4054
|
|
|
3964
4055
|
// Detect Husky and migrate before installing Lefthook hooks
|
|
3965
|
-
|
|
4056
|
+
if (!skillsOnly) {
|
|
4057
|
+
await handleHuskyMigration();
|
|
4058
|
+
}
|
|
3966
4059
|
|
|
3967
4060
|
// Install git hooks for TDD enforcement. LOUD: this is the `forge setup` handler,
|
|
3968
4061
|
// so an inert-hooks result must fail non-zero (B3), not end green.
|
|
3969
4062
|
console.log('');
|
|
3970
|
-
|
|
4063
|
+
if (skillsOnly) {
|
|
4064
|
+
console.log('Skipping git and harness hooks (--skills-only).');
|
|
4065
|
+
} else {
|
|
4066
|
+
installGitHooks({ loud: true });
|
|
4067
|
+
}
|
|
3971
4068
|
|
|
3972
4069
|
// External services (unless skipped)
|
|
3973
4070
|
await handleExternalServices(skipExternal, agents);
|
|
3974
4071
|
|
|
3975
|
-
|
|
3976
|
-
if (SYNC_ENABLED) {
|
|
3977
|
-
await handleSyncScaffold();
|
|
3978
|
-
}
|
|
4072
|
+
removeDeprecatedSyncFiles();
|
|
3979
4073
|
|
|
3980
4074
|
// Progressive setup summary
|
|
3981
4075
|
console.log('');
|
|
3982
4076
|
console.log(renderSetupSummary(actionLog, agents, VERBOSE_MODE, { status: getSetupSummaryStatus() }));
|
|
3983
4077
|
printSetupNotes();
|
|
3984
4078
|
// One-step onboarding: run `forge init` when config is still absent (ac0b38c7).
|
|
3985
|
-
// Hooks +
|
|
4079
|
+
// Hooks + issue store already handled above, so init skips those side effects.
|
|
3986
4080
|
await finalizeWorkflowConfig({ hooksAlreadyInstalled: true });
|
|
3987
4081
|
console.log('');
|
|
3988
4082
|
}
|
|
3989
4083
|
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
4084
|
+
/**
|
|
4085
|
+
* Remove generated Beads/GitHub sync files left behind by older installs.
|
|
4086
|
+
*
|
|
4087
|
+
* Runs on every setup — there is no longer a flag for it. Only files whose
|
|
4088
|
+
* content matches a known generated template are removed, so user-owned files
|
|
4089
|
+
* at the same paths are preserved.
|
|
4090
|
+
*/
|
|
4091
|
+
function removeDeprecatedSyncFiles() {
|
|
3997
4092
|
try {
|
|
3998
|
-
const result =
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
console.log(` Removed deprecated sync file: ${f}`);
|
|
4093
|
+
const result = cleanupDeprecatedSyncFiles(projectRoot, { packageDir: getPackageRoot(packageDir) });
|
|
4094
|
+
for (const file of result.removed || []) {
|
|
4095
|
+
console.log(` Removed deprecated sync file: ${file}`);
|
|
4002
4096
|
}
|
|
4003
4097
|
} catch (err) {
|
|
4004
|
-
console.error(` Error
|
|
4098
|
+
console.error(` Error removing deprecated sync files: ${err.message}`);
|
|
4005
4099
|
}
|
|
4006
4100
|
}
|
|
4007
4101
|
|
|
4008
|
-
// Helper: Handle setup command in non-quick mode
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
4102
|
// Helper: Handle setup command in non-quick mode
|
|
4012
4103
|
async function handleSetupCommand(selectedAgents, flags) {
|
|
4013
4104
|
if (!Array.isArray(selectedAgents) || selectedAgents.length === 0) {
|
|
@@ -4025,6 +4116,7 @@ async function handleSetupCommand(selectedAgents, flags) {
|
|
|
4025
4116
|
skipExternal: flags.skipExternal,
|
|
4026
4117
|
keepExisting: flags.keep,
|
|
4027
4118
|
commandRunner: flags.commandRunner,
|
|
4119
|
+
skillsOnly: flags.skillsOnly,
|
|
4028
4120
|
});
|
|
4029
4121
|
} finally {
|
|
4030
4122
|
projectRoot = savedRoot;
|
|
@@ -4169,6 +4261,7 @@ const SETUP_FLAG_DEFAULTS = Object.freeze({
|
|
|
4169
4261
|
sync: false,
|
|
4170
4262
|
symlink: false,
|
|
4171
4263
|
nonInteractive: false,
|
|
4264
|
+
skillsOnly: false,
|
|
4172
4265
|
});
|
|
4173
4266
|
|
|
4174
4267
|
const SIMPLE_SETUP_FLAG_UPDATES = Object.freeze({
|
|
@@ -4187,6 +4280,8 @@ const SIMPLE_SETUP_FLAG_UPDATES = Object.freeze({
|
|
|
4187
4280
|
'--symlink': { symlink: true },
|
|
4188
4281
|
'--yes': { yes: true, nonInteractive: true },
|
|
4189
4282
|
'-y': { yes: true, nonInteractive: true },
|
|
4283
|
+
'--skills-only': { skillsOnly: true },
|
|
4284
|
+
'--no-hooks': { skillsOnly: true },
|
|
4190
4285
|
});
|
|
4191
4286
|
|
|
4192
4287
|
function parseAgentFlag(argv, currentIndex, isFlagToken) {
|
|
@@ -4263,9 +4358,9 @@ function mergeSetupFlags(flags, argv) {
|
|
|
4263
4358
|
standard: Boolean(flags.standard || setupFlags.standard),
|
|
4264
4359
|
full: Boolean(flags.full || setupFlags.full),
|
|
4265
4360
|
skipExternal: Boolean(flags.skipExternal || setupFlags.skipExternal),
|
|
4266
|
-
sync: Boolean(flags.sync || setupFlags.sync),
|
|
4267
4361
|
symlink: Boolean(flags.symlink || setupFlags.symlink),
|
|
4268
4362
|
nonInteractive: Boolean(flags.nonInteractive || setupFlags.nonInteractive),
|
|
4363
|
+
skillsOnly: Boolean(flags.skillsOnly || setupFlags.skillsOnly),
|
|
4269
4364
|
};
|
|
4270
4365
|
}
|
|
4271
4366
|
|
|
@@ -4310,7 +4405,6 @@ module.exports = {
|
|
|
4310
4405
|
if (flags.verbose) VERBOSE_MODE = true;
|
|
4311
4406
|
if (flags.nonInteractive || flags.yes) NON_INTERACTIVE = true;
|
|
4312
4407
|
if (flags.symlink) SYMLINK_ONLY = true;
|
|
4313
|
-
if (flags.sync) SYNC_ENABLED = true;
|
|
4314
4408
|
actionLog = new SetupActionLog();
|
|
4315
4409
|
resetSetupNotes();
|
|
4316
4410
|
PKG_MANAGER = detectPackageManager();
|
|
@@ -4328,6 +4422,12 @@ module.exports = {
|
|
|
4328
4422
|
}
|
|
4329
4423
|
|
|
4330
4424
|
const [profile] = selectedProfiles;
|
|
4425
|
+
if (flags.skillsOnly) {
|
|
4426
|
+
return {
|
|
4427
|
+
success: false,
|
|
4428
|
+
error: `--skills-only cannot be combined with --${profile}.`,
|
|
4429
|
+
};
|
|
4430
|
+
}
|
|
4331
4431
|
return initCommand.handler([`--profile=${profile}`, '--yes', ...(flags.force ? ['--force'] : [])], flags, projectRoot);
|
|
4332
4432
|
}
|
|
4333
4433
|
|
|
@@ -4352,7 +4452,7 @@ module.exports = {
|
|
|
4352
4452
|
|
|
4353
4453
|
if (flags.dryRun) {
|
|
4354
4454
|
if (selectedAgents.length === 0) selectedAgents = ['claude'];
|
|
4355
|
-
dryRunSetup(selectedAgents);
|
|
4455
|
+
dryRunSetup(selectedAgents, { skillsOnly: flags.skillsOnly });
|
|
4356
4456
|
return { success: true };
|
|
4357
4457
|
}
|
|
4358
4458
|
|
|
@@ -4361,12 +4461,7 @@ module.exports = {
|
|
|
4361
4461
|
if (selectedAgents.length === 0 || (flags.yes && !flags.agents)) {
|
|
4362
4462
|
selectedAgents = Object.keys(AGENTS);
|
|
4363
4463
|
}
|
|
4364
|
-
await quickSetup(selectedAgents, flags.skipExternal);
|
|
4365
|
-
return { success: true };
|
|
4366
|
-
}
|
|
4367
|
-
|
|
4368
|
-
if (flags.sync && selectedAgents.length === 0) {
|
|
4369
|
-
await handleSyncScaffold();
|
|
4464
|
+
await quickSetup(selectedAgents, flags.skipExternal, { skillsOnly: flags.skillsOnly });
|
|
4370
4465
|
return { success: true };
|
|
4371
4466
|
}
|
|
4372
4467
|
|
|
@@ -4407,6 +4502,8 @@ module.exports = {
|
|
|
4407
4502
|
ensureGitHooksInstalled,
|
|
4408
4503
|
forgeShouldWriteLefthookConfig,
|
|
4409
4504
|
FORGE_USER_LEFTHOOK_YML,
|
|
4505
|
+
applyExistingGateDeferral,
|
|
4506
|
+
installGitHooks,
|
|
4410
4507
|
setupClaudeMcpConfig,
|
|
4411
4508
|
setupCursorMcpConfig,
|
|
4412
4509
|
setupClaudePermissions,
|
|
@@ -4431,14 +4528,13 @@ module.exports = {
|
|
|
4431
4528
|
backupAndRemoveLegacyCursorRules,
|
|
4432
4529
|
|
|
4433
4530
|
// State accessors for testing
|
|
4434
|
-
_getState: () => ({ projectRoot, FORCE_MODE, VERBOSE_MODE, NON_INTERACTIVE, SYMLINK_ONLY,
|
|
4531
|
+
_getState: () => ({ projectRoot, FORCE_MODE, VERBOSE_MODE, NON_INTERACTIVE, SYMLINK_ONLY, PKG_MANAGER }),
|
|
4435
4532
|
_setState: (state) => {
|
|
4436
4533
|
if (state.projectRoot !== undefined) projectRoot = state.projectRoot;
|
|
4437
4534
|
if (state.FORCE_MODE !== undefined) FORCE_MODE = state.FORCE_MODE;
|
|
4438
4535
|
if (state.VERBOSE_MODE !== undefined) VERBOSE_MODE = state.VERBOSE_MODE;
|
|
4439
4536
|
if (state.NON_INTERACTIVE !== undefined) NON_INTERACTIVE = state.NON_INTERACTIVE;
|
|
4440
4537
|
if (state.SYMLINK_ONLY !== undefined) SYMLINK_ONLY = state.SYMLINK_ONLY;
|
|
4441
|
-
if (state.SYNC_ENABLED !== undefined) SYNC_ENABLED = state.SYNC_ENABLED;
|
|
4442
4538
|
if (state.PKG_MANAGER !== undefined) PKG_MANAGER = state.PKG_MANAGER;
|
|
4443
4539
|
},
|
|
4444
4540
|
};
|