* enh(#2876): retire the dead and pass-through exports from bin/install.js The installer exported 197 names and had zero production consumers - every non-test require of it repo-wide sits inside a comment. Its interface was shaped by test access, not by callers. Removes 9 dead exports and 61 pass-throughs, repointing their tests onto the extracted modules' own interfaces. 197 down to 127. Every count in the issue was wrong: 197 exports not 188, 9 dead not 12, 61 pass-throughs not 49, 44 test files not 42 - and the audit itself then missed 7 more consumer files. restoreUserArtifacts was on the dead list but ceased to exist in phase 6, and two _GSD_EFFORT_MANIFEST_* names listed as dead are now genuinely asserted, so acting on that list would have deleted live exports. 7 of the 9 dead names collide with an independent declaration that install.js delegates TO. Each removal was justified by which declaration a reference resolves to, never by whether the name appears somewhere. Coverage parity was the gate rather than test greenness: per-file counts were captured before any edit and diffed after. 44 of 45 files are byte-identical; the single delta is one added assertion, not a loss. The sweep for scattered require sites found two forms static grep misses - require(VARIABLE) and multi-line require() - plus tests asserting that install.js re-exports the SAME object, which now assert retirement instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(#2876): close review findings — restore the duplicate-body guard, sweep orphaned code Both review engines found real defects in the first cut. The DEFECT.GENERATIVE-FIX single-owner guard from #1511 had been repointed from a reference-identity check to install.X === undefined. Those are not equivalent: the guard exists to catch a duplicate function body reintroduced into install.js, and the replacement passes cleanly if that duplicate is used internally and never exported. It now walks bin/install.js's real top-level bindings, so it catches a duplicate under either shape, exported or not - strictly stronger than the check it replaced. Proved by injecting a duplicate and watching it go red. That weakening survived the coverage-parity gate because the assertion count never moved. The gate compares counts, so an assertion that changes meaning rather than number is invisible to it. Removing the exports had orphaned their wrapper bodies: 14 dead wrappers, 9 consts and 9 destructure entries, several pre-existing and found by the same sweep. Dead code left in the file this phase exists to shrink. Three more comments claimed re-exports this phase removed, and tests were reading Cursor and Windsurf hook constants from install.js's local copy while calling functions from the hooks surface - equal today, with nothing holding them equal. The local consts now reference the owning module. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(#2876): backfill changeset pr number Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: sim <sim@local> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,7 +15,7 @@ const {
|
||||
convertClaudeCommandToAugmentSkill,
|
||||
convertClaudeAgentToAugmentAgent,
|
||||
convertClaudeToAugmentMarkdown,
|
||||
} = require('../bin/install.js');
|
||||
} = require('../gsd-core/bin/lib/runtime-artifact-conversion.cjs');
|
||||
|
||||
describe('convertClaudeCommandToAugmentSkill', () => {
|
||||
test('writes unquoted Augment skill name in frontmatter', () => {
|
||||
|
||||
@@ -34,21 +34,24 @@ const { PROBE_TIMEOUT_MS, INSTALL_TIMEOUT_MS } = require('./helpers/timeouts.cjs
|
||||
const INSTALL_SCRIPT = path.join(__dirname, '..', 'bin', 'install.js');
|
||||
|
||||
const {
|
||||
getDirName,
|
||||
getConfigDirFromHome,
|
||||
convertClaudeToCliineMarkdown,
|
||||
install,
|
||||
finishInstall,
|
||||
uninstall,
|
||||
buildClineRulesBody,
|
||||
buildClinePreToolUseHook,
|
||||
buildClineAgentsMdBody,
|
||||
mergeGsdAgentsMd,
|
||||
stripGsdFromAgentsMd,
|
||||
GSD_AGENTS_MD_MARKER,
|
||||
GSD_AGENTS_MD_CLOSE_MARKER,
|
||||
} = require('../bin/install.js');
|
||||
|
||||
const { getDirName } = require('../gsd-core/bin/lib/runtime-name-policy.cjs');
|
||||
const {
|
||||
buildClineRulesBody,
|
||||
buildClinePreToolUseHook,
|
||||
buildClineAgentsMdBody,
|
||||
mergeGsdAgentsMd,
|
||||
} = require('../gsd-core/bin/lib/runtime-hooks-surface.cjs');
|
||||
|
||||
const { getGlobalConfigDir } = require('../gsd-core/bin/lib/runtime-homes.cjs');
|
||||
|
||||
describe('Cline runtime directory mapping', () => {
|
||||
|
||||
@@ -13,7 +13,6 @@ const os = require('node:os');
|
||||
const { createTempDir, cleanup } = require('./helpers.cjs');
|
||||
|
||||
const {
|
||||
getDirName,
|
||||
getConfigDirFromHome,
|
||||
convertClaudeToCodebuddyMarkdown,
|
||||
convertClaudeCommandToCodebuddySkill,
|
||||
@@ -24,6 +23,7 @@ const {
|
||||
} = require('../bin/install.js');
|
||||
|
||||
const { installRuntimeArtifacts } = require('../gsd-core/bin/lib/install-engine.cjs');
|
||||
const { getDirName } = require('../gsd-core/bin/lib/runtime-name-policy.cjs');
|
||||
|
||||
const { getGlobalConfigDir } = require('../gsd-core/bin/lib/runtime-homes.cjs');
|
||||
|
||||
|
||||
@@ -69,10 +69,10 @@ const {
|
||||
GSD_CODEX_MARKER,
|
||||
CODEX_AGENT_SANDBOX,
|
||||
parseTomlToObject,
|
||||
resolveNodeRunner,
|
||||
validateCodexConfigSchema,
|
||||
} = require('../bin/install.js');
|
||||
|
||||
const { resolveNodeRunner } = require('../gsd-core/bin/lib/runtime-hooks-surface.cjs');
|
||||
const { resolveInstallPlan } = require('../gsd-core/bin/lib/runtime-config-adapter-registry.cjs');
|
||||
|
||||
function runCodexInstall(codexHome, cwd = path.join(__dirname, '..')) {
|
||||
@@ -5392,9 +5392,9 @@ const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const path = require('node:path');
|
||||
|
||||
const INSTALL = require(path.join(__dirname, '..', 'bin', 'install.js'));
|
||||
const HOOKS_SURFACE = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'runtime-hooks-surface.cjs'));
|
||||
const projection = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'shell-command-projection.cjs'));
|
||||
const { buildCodexHookBlock, rewriteLegacyCodexHookBlock, resolveNodeRunner } = INSTALL;
|
||||
const { buildCodexHookBlock, rewriteLegacyCodexHookBlock, resolveNodeRunner } = HOOKS_SURFACE;
|
||||
const { projectCodexHookTomlCommand } = projection;
|
||||
|
||||
/**
|
||||
@@ -6959,15 +6959,19 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const INSTALL = require('../bin/install.js');
|
||||
const HOOKS_SURFACE = require('../gsd-core/bin/lib/runtime-hooks-surface.cjs');
|
||||
const PROJECTION = require('../gsd-core/bin/lib/shell-command-projection.cjs');
|
||||
const { createTempDir, cleanup } = require('./helpers.cjs');
|
||||
|
||||
const {
|
||||
uninstall,
|
||||
} = INSTALL;
|
||||
|
||||
const {
|
||||
buildCodexHookWindowsShimIR,
|
||||
ensureCodexHooksJsonSessionStart,
|
||||
resolveNodeRunner,
|
||||
uninstall,
|
||||
} = INSTALL;
|
||||
} = HOOKS_SURFACE;
|
||||
|
||||
const { projectManagedHookCommand } = PROJECTION;
|
||||
|
||||
@@ -6991,12 +6995,12 @@ function hookHandlersForEvent(hooksJson, eventName) {
|
||||
describe('#3426 — export surface: buildCodexHookWindowsShimIR must be exported', () => {
|
||||
test('buildCodexHookWindowsShimIR is a function', () => {
|
||||
assert.equal(typeof buildCodexHookWindowsShimIR, 'function',
|
||||
'buildCodexHookWindowsShimIR must be exported from bin/install.js');
|
||||
'buildCodexHookWindowsShimIR must be exported from runtime-hooks-surface.cjs');
|
||||
});
|
||||
|
||||
test('ensureCodexHooksJsonSessionStart is a function', () => {
|
||||
assert.equal(typeof ensureCodexHooksJsonSessionStart, 'function',
|
||||
'ensureCodexHooksJsonSessionStart must be exported from bin/install.js');
|
||||
'ensureCodexHooksJsonSessionStart must be exported from runtime-hooks-surface.cjs');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9382,13 +9386,12 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const INSTALL = require('../bin/install.js');
|
||||
const {
|
||||
ensureCodexHooksJsonSessionStart,
|
||||
ensureCodexHooksJsonEvent,
|
||||
removeCodexHooksJsonEvent,
|
||||
reconcileCodexHooksJsonEvent,
|
||||
} = INSTALL;
|
||||
} = require('../gsd-core/bin/lib/runtime-hooks-surface.cjs');
|
||||
const { createTempDir, cleanup } = require('./helpers.cjs');
|
||||
|
||||
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
||||
@@ -9430,17 +9433,17 @@ function stubHookFile(targetDir, hookName) {
|
||||
describe('enh-772: export surface — new functions are exported', () => {
|
||||
test('ensureCodexHooksJsonEvent is a function', () => {
|
||||
assert.strictEqual(typeof ensureCodexHooksJsonEvent, 'function',
|
||||
'ensureCodexHooksJsonEvent must be exported from bin/install.js');
|
||||
'ensureCodexHooksJsonEvent must be exported from runtime-hooks-surface.cjs');
|
||||
});
|
||||
|
||||
test('removeCodexHooksJsonEvent is a function', () => {
|
||||
assert.strictEqual(typeof removeCodexHooksJsonEvent, 'function',
|
||||
'removeCodexHooksJsonEvent must be exported from bin/install.js');
|
||||
'removeCodexHooksJsonEvent must be exported from runtime-hooks-surface.cjs');
|
||||
});
|
||||
|
||||
test('reconcileCodexHooksJsonEvent is a function', () => {
|
||||
assert.strictEqual(typeof reconcileCodexHooksJsonEvent, 'function',
|
||||
'reconcileCodexHooksJsonEvent must be exported from bin/install.js');
|
||||
'reconcileCodexHooksJsonEvent must be exported from runtime-hooks-surface.cjs');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ const {
|
||||
} = require('../gsd-core/bin/lib/host-integration.cjs');
|
||||
|
||||
const install = require('../bin/install.js');
|
||||
const hooksSurface = require('../gsd-core/bin/lib/runtime-hooks-surface.cjs');
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
|
||||
const CODEX_CAP = JSON.parse(
|
||||
@@ -201,9 +202,9 @@ test('upgrade 1 — codex registers all documented hooks.json lifecycle events,
|
||||
fs.writeFileSync(path.join(tmp, 'hooks', 'gsd-context-monitor.js'), '// stub');
|
||||
fs.writeFileSync(path.join(tmp, 'hooks', 'gsd-check-update.js'), '// stub');
|
||||
for (const ev of install.CODEX_EXTENDED_HOOK_EVENTS) {
|
||||
install.ensureCodexHooksJsonEvent(tmp, ev, { absoluteRunner: '/usr/bin/node', platform: 'linux' });
|
||||
hooksSurface.ensureCodexHooksJsonEvent(tmp, ev, { absoluteRunner: '/usr/bin/node', platform: 'linux' });
|
||||
}
|
||||
install.ensureCodexHooksJsonSessionStart(tmp, { absoluteRunner: '/usr/bin/node', platform: 'linux' });
|
||||
hooksSurface.ensureCodexHooksJsonSessionStart(tmp, { absoluteRunner: '/usr/bin/node', platform: 'linux' });
|
||||
const hooksJson = JSON.parse(fs.readFileSync(path.join(tmp, 'hooks.json'), 'utf8'));
|
||||
const registered = Object.keys(hooksJson.hooks || hooksJson || {});
|
||||
for (const ev of ['PreToolUse', 'PermissionRequest', 'PreCompact', 'PostCompact', 'SubagentStop', 'UserPromptSubmit']) {
|
||||
|
||||
@@ -28,7 +28,6 @@ const { parseFrontmatter, createTempDir, cleanup } = require('./helpers.cjs');
|
||||
const { listAgentFiles } = require('./helpers/agent-roster.cjs');
|
||||
|
||||
const {
|
||||
getDirName,
|
||||
getConfigDirFromHome,
|
||||
claudeToCopilotTools,
|
||||
convertCopilotToolName,
|
||||
@@ -40,8 +39,6 @@ const {
|
||||
mergeCopilotInstructions,
|
||||
stripGsdFromCopilotInstructions,
|
||||
GSD_COPILOT_HOOK_FILE,
|
||||
buildCopilotHookConfig,
|
||||
writeCopilotHookConfig,
|
||||
writeManifest,
|
||||
reportLocalPatches,
|
||||
runtimeMap,
|
||||
@@ -51,6 +48,11 @@ const {
|
||||
} = require('../bin/install.js');
|
||||
|
||||
const { installRuntimeArtifacts } = require('../gsd-core/bin/lib/install-engine.cjs');
|
||||
const { getDirName } = require('../gsd-core/bin/lib/runtime-name-policy.cjs');
|
||||
const {
|
||||
buildCopilotHookConfig,
|
||||
writeCopilotHookConfig,
|
||||
} = require('../gsd-core/bin/lib/runtime-hooks-surface.cjs');
|
||||
|
||||
const { getGlobalConfigDir } = require('../gsd-core/bin/lib/runtime-homes.cjs');
|
||||
|
||||
|
||||
@@ -14,8 +14,10 @@ const assert = require('node:assert/strict');
|
||||
const {
|
||||
convertClaudeCommandToCursorSkill,
|
||||
convertClaudeAgentToCursorAgent,
|
||||
_applyRuntimeRewrites,
|
||||
} = require('../bin/install.js');
|
||||
const {
|
||||
_applyRuntimeRewrites,
|
||||
} = require('../gsd-core/bin/lib/runtime-artifact-conversion.cjs');
|
||||
|
||||
describe('convertClaudeCommandToCursorSkill', () => {
|
||||
test('writes unquoted Cursor skill name in frontmatter', () => {
|
||||
|
||||
@@ -25,10 +25,13 @@ const {
|
||||
} = require('../gsd-core/bin/lib/host-integration-adapters/imperative-hook-bus.cjs');
|
||||
|
||||
const {
|
||||
reconcileCursorHooksJson,
|
||||
GSD_CURSOR_HOOK_MARKER,
|
||||
} = require('../bin/install.js');
|
||||
|
||||
const {
|
||||
reconcileCursorHooksJson,
|
||||
} = require('../gsd-core/bin/lib/runtime-hooks-surface.cjs');
|
||||
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
|
||||
const CUR_CAP = JSON.parse(
|
||||
|
||||
@@ -43,15 +43,18 @@ const os = require('node:os');
|
||||
const { createTempDir, cleanup } = require('./helpers.cjs');
|
||||
|
||||
const {
|
||||
reconcileCursorHooksJson,
|
||||
isManagedCursorHookEntry,
|
||||
buildCursorHookEntry,
|
||||
removeCursorHooksJson,
|
||||
GSD_CURSOR_HOOK_MARKER,
|
||||
GSD_CURSOR_SESSION_HOOK_SCRIPT,
|
||||
GSD_CURSOR_POST_TOOL_HOOK_SCRIPT,
|
||||
} = require('../bin/install.js');
|
||||
|
||||
const {
|
||||
reconcileCursorHooksJson,
|
||||
isManagedCursorHookEntry,
|
||||
buildCursorHookEntry,
|
||||
removeCursorHooksJson,
|
||||
} = require('../gsd-core/bin/lib/runtime-hooks-surface.cjs');
|
||||
|
||||
const {
|
||||
resolveRuntimeConfigIntent,
|
||||
INSTALL_SURFACES,
|
||||
|
||||
@@ -29,13 +29,15 @@ const {
|
||||
maskStringLiterals,
|
||||
findDispatchCallSpans,
|
||||
_assertProjectionComplete,
|
||||
applyClaudeCodeBrandSwap,
|
||||
convertClaudeToWindsurfMarkdown,
|
||||
install,
|
||||
uninstall,
|
||||
} = require('../bin/install.js');
|
||||
|
||||
const { installRuntimeArtifacts } = require('../gsd-core/bin/lib/install-engine.cjs');
|
||||
const {
|
||||
applyClaudeCodeBrandSwap,
|
||||
convertClaudeToWindsurfMarkdown,
|
||||
} = require('../gsd-core/bin/lib/runtime-artifact-conversion.cjs');
|
||||
const { parseFrontmatter, cleanup } = require('./helpers.cjs');
|
||||
const { nestedSkillPath } = require('./helpers/nested-layout.cjs');
|
||||
const pkg = require('../package.json');
|
||||
|
||||
@@ -124,9 +124,12 @@ describe('hook file validation', () => {
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
// Typed import — no source-grep needed (#455)
|
||||
const { GSD_UNINSTALL_HOOKS, buildHookCommand } = require(
|
||||
const { GSD_UNINSTALL_HOOKS } = require(
|
||||
path.join(__dirname, '..', 'bin', 'install.js')
|
||||
);
|
||||
const { buildHookCommand } = require(
|
||||
path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'runtime-hooks-surface.cjs')
|
||||
);
|
||||
|
||||
describe('installer hook registration', () => {
|
||||
test('GSD_UNINSTALL_HOOKS includes all 3 opt-in bash hooks', () => {
|
||||
|
||||
@@ -1044,7 +1044,7 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { createTempDir, cleanup, captureConsole } = require('./helpers.cjs');
|
||||
const { applySettingsJsonHooks } = require('../bin/install.js');
|
||||
const { applySettingsJsonHooks } = require('../gsd-core/bin/lib/runtime-hooks-surface.cjs');
|
||||
|
||||
const JS_HOOKS = [
|
||||
'gsd-check-update.js',
|
||||
@@ -1167,7 +1167,7 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { createTempDir, cleanup, captureConsole } = require('./helpers.cjs');
|
||||
const { applySettingsJsonHooks } = require('../bin/install.js');
|
||||
const { applySettingsJsonHooks } = require('../gsd-core/bin/lib/runtime-hooks-surface.cjs');
|
||||
|
||||
const SH_HOOKS = [
|
||||
'gsd-validate-commit.sh',
|
||||
@@ -1292,7 +1292,7 @@ before(() => {
|
||||
ensureHooksDist();
|
||||
});
|
||||
|
||||
const { applySettingsJsonHooks } = require('../bin/install.js');
|
||||
const { applySettingsJsonHooks } = require('../gsd-core/bin/lib/runtime-hooks-surface.cjs');
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
|
||||
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -38,13 +38,19 @@ try {
|
||||
else process.env.GSD_TEST_MODE = savedTestMode;
|
||||
}
|
||||
|
||||
const { install, mergeClaudePermissions, GSD_CLAUDE_ALLOW_PERMISSIONS, GSD_CLAUDE_LEGACY_ALLOW_PERMISSIONS, GSD_CLAUDE_DENY_PERMISSIONS, rewriteLegacyManagedNodeHookCommands, resolveNodeRunner, copyWithPathReplacement } = installExports || {};
|
||||
const { install, mergeClaudePermissions, GSD_CLAUDE_ALLOW_PERMISSIONS, GSD_CLAUDE_LEGACY_ALLOW_PERMISSIONS, GSD_CLAUDE_DENY_PERMISSIONS, copyWithPathReplacement } = installExports || {};
|
||||
|
||||
const {
|
||||
installRuntimeArtifacts,
|
||||
uninstallRuntimeArtifacts,
|
||||
} = require('../gsd-core/bin/lib/install-engine.cjs');
|
||||
|
||||
const {
|
||||
rewriteLegacyManagedNodeHookCommands,
|
||||
resolveNodeRunner,
|
||||
reconcileManagedShellHookCommands,
|
||||
} = require('../gsd-core/bin/lib/runtime-hooks-surface.cjs');
|
||||
|
||||
const INSTALL_SCRIPT = path.join(__dirname, '..', 'bin', 'install.js');
|
||||
// #3145: class-norm timeout, not a per-suite value — see helpers/timeouts.cjs.
|
||||
const { INSTALL_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
|
||||
@@ -120,7 +126,7 @@ describe('Defect #1 regression (#3664 reversed by #947): bare-stem dirs removed,
|
||||
t.after(() => cleanup(configDir));
|
||||
|
||||
assert.strictEqual(typeof installRuntimeArtifacts, 'function',
|
||||
'installRuntimeArtifacts must be exported from bin/install.js');
|
||||
'installRuntimeArtifacts must be exported from install-engine.cjs');
|
||||
|
||||
// Pre-create #3664-era bare-stem Hermes layout (no gsd- prefix, now stale).
|
||||
// Use real GSD command stems (help, quick) that readGsdCommandNames() knows about.
|
||||
@@ -307,7 +313,7 @@ describe('U1 (#2973): uninstallRuntimeArtifacts qwen migrates dev-preferences
|
||||
t.after(() => cleanup(configDir));
|
||||
|
||||
assert.strictEqual(typeof uninstallRuntimeArtifacts, 'function',
|
||||
'uninstallRuntimeArtifacts must be exported from bin/install.js');
|
||||
'uninstallRuntimeArtifacts must be exported from install-engine.cjs');
|
||||
|
||||
const legacyDir = path.join(configDir, 'commands', 'gsd');
|
||||
fs.mkdirSync(legacyDir, { recursive: true });
|
||||
@@ -925,7 +931,7 @@ describe('#976 regression: installer does not duplicate managed hooks when regis
|
||||
|
||||
test('rewriteLegacyManagedNodeHookCommands leaves args-form launcher entries unchanged', () => {
|
||||
assert.strictEqual(typeof rewriteLegacyManagedNodeHookCommands, 'function',
|
||||
'rewriteLegacyManagedNodeHookCommands must be exported from bin/install.js');
|
||||
'rewriteLegacyManagedNodeHookCommands must be exported from runtime-hooks-surface.cjs');
|
||||
|
||||
const launcherCommand = '/usr/local/bin/node-launcher';
|
||||
const hookPath = '/Users/user/.claude/hooks/gsd-check-update.js';
|
||||
@@ -1343,27 +1349,15 @@ describe('#1924: profile-user.md backup path must be outside gsd-core/', () => {
|
||||
// guarantee than the retired helper ever gave, and one a plain in-process
|
||||
// round-trip would not prove.
|
||||
|
||||
describe('#1924: user artifacts survive a wipe via the durable staging path (install.js exports)', () => {
|
||||
test('install.js exports the staging primitives, and content staged through them survives a destDir wipe + crash', (t) => {
|
||||
// Set GSD_TEST_MODE so require() reaches the module.exports block
|
||||
const origMode = process.env.GSD_TEST_MODE;
|
||||
process.env.GSD_TEST_MODE = '1';
|
||||
let mod;
|
||||
try {
|
||||
mod = require(INSTALL_SCRIPT);
|
||||
} finally {
|
||||
if (origMode === undefined) {
|
||||
delete process.env.GSD_TEST_MODE;
|
||||
} else {
|
||||
process.env.GSD_TEST_MODE = origMode;
|
||||
}
|
||||
}
|
||||
describe('#1924: user artifacts survive a wipe via the durable staging path (user-artifact-staging.cjs exports)', () => {
|
||||
test('user-artifact-staging.cjs exports the staging primitives, and content staged through them survives a destDir wipe + crash', (t) => {
|
||||
const mod = require('../gsd-core/bin/lib/user-artifact-staging.cjs');
|
||||
|
||||
for (const name of ['stageUserArtifacts', 'restoreStagedUserArtifacts', 'discardStagedUserArtifacts', 'recoverOrphanedUserArtifacts']) {
|
||||
assert.strictEqual(
|
||||
typeof mod[name],
|
||||
'function',
|
||||
`install.js must export ${name} — the mechanism that now makes the #1924 durability property testable`,
|
||||
`user-artifact-staging.cjs must export ${name} — the mechanism that now makes the #1924 durability property testable`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1519,8 +1513,6 @@ describe('#3333 regression: copyWithPathReplacement tolerates a source file vani
|
||||
// user-authored hooks are untouched.
|
||||
|
||||
describe('#3329 regression: stale managed .sh hook commands are reconciled on install/update', () => {
|
||||
const { reconcileManagedShellHookCommands } = installExports || {};
|
||||
|
||||
const GLOBAL_EXPECTED = {
|
||||
'gsd-validate-commit.sh': '"C:/Users/u/.claude/hooks/gsd-validate-commit.sh"',
|
||||
'gsd-graphify-update.sh': '"C:/Users/u/.claude/hooks/gsd-graphify-update.sh"',
|
||||
@@ -1555,9 +1547,9 @@ describe('#3329 regression: stale managed .sh hook commands are reconciled on in
|
||||
return out;
|
||||
}
|
||||
|
||||
test('reconcileManagedShellHookCommands is exported from bin/install.js', () => {
|
||||
test('reconcileManagedShellHookCommands is exported from runtime-hooks-surface.cjs', () => {
|
||||
assert.strictEqual(typeof reconcileManagedShellHookCommands, 'function',
|
||||
'reconcileManagedShellHookCommands must be exported from bin/install.js (#3329)');
|
||||
'reconcileManagedShellHookCommands must be exported from runtime-hooks-surface.cjs (#3329)');
|
||||
});
|
||||
|
||||
test('win32+claude: bare `bash`-prefixed global entries are rewritten to the bare script path', () => {
|
||||
|
||||
@@ -25,6 +25,8 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const crypto = require('node:crypto');
|
||||
const os = require('node:os');
|
||||
const espree = require('espree');
|
||||
const { splitLines, joinLines } = require('../gsd-core/bin/lib/text-lines.cjs');
|
||||
|
||||
const { createTempDir, cleanup } = require('./helpers.cjs');
|
||||
const { runNode } = require('./helpers/process-seam.cjs');
|
||||
@@ -40,6 +42,7 @@ const {
|
||||
const {
|
||||
installRuntimeArtifacts,
|
||||
installOpencodeFamilySkills,
|
||||
uninstallRuntimeArtifacts,
|
||||
_resolveUserArtifactStagingRoot,
|
||||
} = require('../gsd-core/bin/lib/install-engine.cjs');
|
||||
|
||||
@@ -110,7 +113,7 @@ describe('installRuntimeArtifacts — consumes Runtime Artifact Install Plan Mod
|
||||
fs.writeFileSync(path.join(sourceDir, 'proof.md'), '# proof\n');
|
||||
fs.writeFileSync(path.join(cleanupDir, 'temp.md'), '# cleanup\n');
|
||||
let planArgs;
|
||||
const { installer, restore } = loadFreshInstallerWithInstallPlanStub((args) => {
|
||||
const { restore } = loadFreshInstallerWithInstallPlanStub((args) => {
|
||||
planArgs = args;
|
||||
return {
|
||||
ok: true,
|
||||
@@ -125,7 +128,12 @@ describe('installRuntimeArtifacts — consumes Runtime Artifact Install Plan Mod
|
||||
t.after(restore);
|
||||
|
||||
// Augment has a flat commands kind and prefixes proof.md as gsd-proof.md.
|
||||
installer.installRuntimeArtifacts('augment', configDir, 'global', RESOLVED_CORE);
|
||||
// `installer` above only reloads bin/install.js (needed for OTHER stubs in
|
||||
// this file); installRuntimeArtifacts itself always lived in install-engine.cjs
|
||||
// (bin/install.js's #2876-retired re-export was an alias to the same
|
||||
// singleton), so calling the direct import exercises the identical,
|
||||
// already-monkeypatched module instance.
|
||||
installRuntimeArtifacts('augment', configDir, 'global', RESOLVED_CORE);
|
||||
|
||||
assert.strictEqual(planArgs.layout.runtime, 'augment');
|
||||
assert.strictEqual(planArgs.layout.configDir, configDir);
|
||||
@@ -145,7 +153,7 @@ describe('installRuntimeArtifacts — consumes Runtime Artifact Install Plan Mod
|
||||
});
|
||||
|
||||
fs.writeFileSync(path.join(cleanupDir, 'temp.md'), '# cleanup\n');
|
||||
const { installer, restore } = loadFreshInstallerWithInstallPlanStub(() => ({
|
||||
const { restore } = loadFreshInstallerWithInstallPlanStub(() => ({
|
||||
ok: false,
|
||||
kind: 'rewrite_failed',
|
||||
failedKind: 'commands',
|
||||
@@ -155,7 +163,7 @@ describe('installRuntimeArtifacts — consumes Runtime Artifact Install Plan Mod
|
||||
t.after(restore);
|
||||
|
||||
assert.throws(
|
||||
() => installer.installRuntimeArtifacts('claude', configDir, 'global', RESOLVED_CORE),
|
||||
() => installRuntimeArtifacts('claude', configDir, 'global', RESOLVED_CORE),
|
||||
/planned failure/,
|
||||
);
|
||||
assert.ok(!fs.existsSync(cleanupDir), 'failure cleanup dir must be removed');
|
||||
@@ -745,7 +753,7 @@ describe('uninstallRuntimeArtifacts — consumes Runtime Artifact Uninstall Plan
|
||||
fs.writeFileSync(path.join(commandsDir, 'user-custom.md'), '# keep\n');
|
||||
|
||||
let planLayout;
|
||||
const { installer, restore } = loadFreshInstallerWithPlanStubs({
|
||||
const { restore } = loadFreshInstallerWithPlanStubs({
|
||||
uninstallStub(layout) {
|
||||
planLayout = layout;
|
||||
return {
|
||||
@@ -757,7 +765,10 @@ describe('uninstallRuntimeArtifacts — consumes Runtime Artifact Uninstall Plan
|
||||
});
|
||||
t.after(restore);
|
||||
|
||||
installer.uninstallRuntimeArtifacts('augment', configDir, 'global');
|
||||
// uninstallRuntimeArtifacts always lived in install-engine.cjs (bin/install.js's
|
||||
// #2876-retired re-export was an alias to the same singleton) — the direct
|
||||
// top-level import exercises the identical, already-monkeypatched instance.
|
||||
uninstallRuntimeArtifacts('augment', configDir, 'global');
|
||||
|
||||
assert.strictEqual(planLayout.runtime, 'augment');
|
||||
assert.strictEqual(planLayout.configDir, configDir);
|
||||
@@ -774,7 +785,7 @@ describe('uninstallRuntimeArtifacts — removes gsd-owned entries, preserves for
|
||||
t.after(() => cleanup(configDir));
|
||||
sandboxHome(t, configDir);
|
||||
|
||||
const { uninstallRuntimeArtifacts } = require('../bin/install.js');
|
||||
const { uninstallRuntimeArtifacts } = require('../gsd-core/bin/lib/install-engine.cjs');
|
||||
assert.strictEqual(typeof uninstallRuntimeArtifacts, 'function');
|
||||
|
||||
const layout = resolveRuntimeArtifactLayout(runtime, configDir, 'global');
|
||||
@@ -928,7 +939,7 @@ describe('installRuntimeArtifacts — legacy migrations run before layout copy',
|
||||
|
||||
describe('uninstallRuntimeArtifacts — legacy cleanup runs before layout removal', () => {
|
||||
test('hermes: both flat and nested layouts removed (#947: bare-stem dirs cleaned on uninstall)', (t) => {
|
||||
const { uninstallRuntimeArtifacts } = require('../bin/install.js');
|
||||
const { uninstallRuntimeArtifacts } = require('../gsd-core/bin/lib/install-engine.cjs');
|
||||
const configDir = createTempDir('gsd-legacy-uninstall-hermes-');
|
||||
t.after(() => cleanup(configDir));
|
||||
|
||||
@@ -957,7 +968,7 @@ describe('uninstallRuntimeArtifacts — legacy cleanup runs before layout remova
|
||||
});
|
||||
|
||||
test('claude: legacy commands/gsd/ cleaned AND new skills/ entries removed', (t) => {
|
||||
const { uninstallRuntimeArtifacts } = require('../bin/install.js');
|
||||
const { uninstallRuntimeArtifacts } = require('../gsd-core/bin/lib/install-engine.cjs');
|
||||
const configDir = createTempDir('gsd-legacy-uninstall-claude-');
|
||||
t.after(() => cleanup(configDir));
|
||||
|
||||
@@ -1283,11 +1294,14 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const {
|
||||
convertClaudeToWindsurfMarkdown,
|
||||
convertClaudeToTraeMarkdown,
|
||||
_applyRuntimeRewrites,
|
||||
} = require('../bin/install.js');
|
||||
|
||||
const {
|
||||
convertClaudeToWindsurfMarkdown,
|
||||
_applyRuntimeRewrites,
|
||||
} = require('../gsd-core/bin/lib/runtime-artifact-conversion.cjs');
|
||||
|
||||
// ─── Windsurf converter bare-form tests ─────────────────────────────────────
|
||||
|
||||
describe('convertClaudeToWindsurfMarkdown — bare ~/.claude and CLAUDE_CONFIG_DIR (#983)', () => {
|
||||
@@ -1634,10 +1648,10 @@ const {
|
||||
convertClaudeCommandToClineSkill,
|
||||
convertClaudeToCliineMarkdown,
|
||||
install,
|
||||
_applyRuntimeRewrites,
|
||||
} = require('../bin/install.js');
|
||||
|
||||
const { installRuntimeArtifacts } = require('../gsd-core/bin/lib/install-engine.cjs');
|
||||
const { _applyRuntimeRewrites } = require('../gsd-core/bin/lib/runtime-artifact-conversion.cjs');
|
||||
|
||||
const {
|
||||
resolveRuntimeArtifactLayout,
|
||||
@@ -2694,9 +2708,9 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const {
|
||||
readGsdRuntimeProfileResolver,
|
||||
install,
|
||||
} = require('../bin/install.js');
|
||||
const { readGsdRuntimeProfileResolver } = require('../gsd-core/bin/lib/install-model-override-resolver.cjs');
|
||||
|
||||
const { createTempDir, cleanup } = require('./helpers.cjs');
|
||||
const makeTmp = (prefix) => createTempDir(`gsd-2794-${prefix}-`);
|
||||
@@ -4232,8 +4246,10 @@ describe('#443 Source purity: agents/gsd-planner.md has no effort: key', () => {
|
||||
// convertClaudeToAugmentMarkdown duplicate dedup is deferred to Phase 2's cleanup
|
||||
// (entangled converter cluster; not required to unblock Phase 2).
|
||||
// These tests exercise the REAL relocated functions at their new home (the
|
||||
// generated .cjs) and assert install.js re-exports the SAME references
|
||||
// (Hyrum: existing consumers import these names from bin/install.js).
|
||||
// generated .cjs). #2876 (epic #2866 Phase 7) retired install.js's re-export
|
||||
// of both names — a repo-wide audit found zero production consumers of the
|
||||
// pass-through, so the "existing consumers" beneficiary ADR-1508 cited was
|
||||
// the test suite alone. The re-export absence is asserted below instead.
|
||||
|
||||
const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
@@ -4276,8 +4292,8 @@ describe('getDirName (relocated to runtime-name-policy)', () => {
|
||||
assert.strictEqual(runtimeNamePolicy.getDirName(''), '.claude');
|
||||
});
|
||||
|
||||
test('bin/install.js re-exports the SAME getDirName reference (no drift)', () => {
|
||||
assert.strictEqual(installer.getDirName, runtimeNamePolicy.getDirName);
|
||||
test('bin/install.js no longer re-exports getDirName (#2876 retired the pass-through)', () => {
|
||||
assert.strictEqual(installer.getDirName, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4323,10 +4339,8 @@ describe('processAttribution (relocated to runtime-artifact-conversion)', () =>
|
||||
);
|
||||
});
|
||||
|
||||
test('bin/install.js re-exports the SAME processAttribution reference (no drift)', () => {
|
||||
// processAttribution remains an explicit installer compatibility relay, so
|
||||
// the export must keep pointing at the conversion module's implementation.
|
||||
assert.strictEqual(installer.processAttribution, conversion.processAttribution);
|
||||
test('bin/install.js no longer re-exports processAttribution (#2876 retired the pass-through)', () => {
|
||||
assert.strictEqual(installer.processAttribution, undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -4681,124 +4695,151 @@ describe('layout module no longer exports getInstallExports', () => {
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DEFECT.GENERATIVE-FIX: single-owner reference-identity guard (#1511)
|
||||
// Proves install.js binds to the conversion module's implementation, not a
|
||||
// duplicate local copy. If these fail, a duplicate body was re-introduced.
|
||||
// DEFECT.GENERATIVE-FIX: single-owner duplicate-body guard (#1511),
|
||||
// re-armed AST-based after #2876 (epic #2866 Phase 7) retired the exports
|
||||
// the original reference-identity check compared against.
|
||||
//
|
||||
// #1511 proved install.js bound to the conversion module's implementation
|
||||
// rather than a duplicate local copy — the single-owner property held via
|
||||
// `assert.strictEqual(install.X, conversion.X)` (reference identity). #2876
|
||||
// retired these names from bin/install.js's module.exports entirely because
|
||||
// nothing consumed the re-export. That broke the reference-identity form
|
||||
// (there is no `install.X` left to compare), and a same-shaped
|
||||
// `install.X === undefined` replacement is NOT an equivalent guard: it only
|
||||
// inspects the EXPORT surface, so a duplicate, unexported, re-implemented
|
||||
// copy of one of these functions/consts added directly to bin/install.js
|
||||
// would satisfy `install.X === undefined` trivially while still being
|
||||
// exactly the drift-hazard duplicate this guard exists to catch.
|
||||
//
|
||||
// Fix: parse bin/install.js's own top-level AST (espree) and assert directly
|
||||
// on what is actually declared there, instead of on what is exported.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
describe('single-owner reference-identity guard (ADR-1508 / #1511 Phase 2)', () => {
|
||||
describe('single-owner duplicate-body guard (ADR-1508 / #1511 Phase 2, AST-based since #2876)', () => {
|
||||
let install;
|
||||
let conversionCjs;
|
||||
let installTopLevel;
|
||||
|
||||
// Collect every top-level `function <name>(...) {}` declaration and every
|
||||
// top-level `const/let/var <name> = <init>;` declarator in bin/install.js,
|
||||
// keyed by name. A duplicate body re-introduced under EITHER shape (a real
|
||||
// function, or a const bound to something other than a bare
|
||||
// runtimeArtifactConversion.<Y> reference) is visible here regardless of
|
||||
// whether it is ever exported.
|
||||
function collectTopLevelBindings(ast) {
|
||||
const functionNames = new Set();
|
||||
const variableInits = new Map();
|
||||
for (const node of ast.body) {
|
||||
if (node.type === 'FunctionDeclaration' && node.id) {
|
||||
functionNames.add(node.id.name);
|
||||
}
|
||||
if (node.type === 'VariableDeclaration') {
|
||||
for (const decl of node.declarations) {
|
||||
if (decl.type === 'VariableDeclarator' && decl.id && decl.id.type === 'Identifier') {
|
||||
variableInits.set(decl.id.name, decl.init);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return { functionNames, variableInits };
|
||||
}
|
||||
|
||||
// True iff `node` is exactly `<objectName>.<propertyName>` — a bare
|
||||
// single-hop member-expression reference, not a call, not a duplicated
|
||||
// function/object body.
|
||||
function isMemberReferenceTo(node, objectName, propertyName) {
|
||||
return (
|
||||
!!node &&
|
||||
node.type === 'MemberExpression' &&
|
||||
!node.computed &&
|
||||
node.object &&
|
||||
node.object.type === 'Identifier' &&
|
||||
node.object.name === objectName &&
|
||||
node.property &&
|
||||
node.property.type === 'Identifier' &&
|
||||
node.property.name === propertyName
|
||||
);
|
||||
}
|
||||
|
||||
before(() => {
|
||||
process.env['GSD_TEST_MODE'] = '1';
|
||||
install = require('../bin/install.js');
|
||||
conversionCjs = require('../gsd-core/bin/lib/runtime-artifact-conversion.cjs');
|
||||
// bin/install.js opens with a `#!/usr/bin/env node` shebang line, which
|
||||
// is not valid top-level JS syntax for espree's parser — strip it before
|
||||
// parsing (mirrors how Node's own module loader strips it at runtime).
|
||||
// Uses splitLines() (src/text-lines.cts, the repo's sole `\r?\n` split
|
||||
// seam) rather than a readFileSync-content regex, per
|
||||
// local/no-unbounded-quantifier and local/no-crlf-fragile-split.
|
||||
const installSrcRaw = fs.readFileSync(INSTALL_SCRIPT, 'utf8');
|
||||
const installSrcLines = splitLines(installSrcRaw);
|
||||
if (installSrcLines[0] && installSrcLines[0].startsWith('#!')) {
|
||||
installSrcLines[0] = '';
|
||||
}
|
||||
const installSrc = joinLines(installSrcLines, '\n');
|
||||
const ast = espree.parse(installSrc, { ecmaVersion: 2022, sourceType: 'script', range: true, loc: true });
|
||||
installTopLevel = collectTopLevelBindings(ast);
|
||||
});
|
||||
|
||||
test('install.computePathPrefix === conversion._computePathPrefix (single implementation)', () => {
|
||||
assert.strictEqual(
|
||||
install.computePathPrefix,
|
||||
conversionCjs._computePathPrefix,
|
||||
'install.js must bind computePathPrefix from conversion (not a duplicate body)',
|
||||
);
|
||||
});
|
||||
// Names #2876 retired from bin/install.js entirely (no export, no internal
|
||||
// caller). Regression shape this guards against: someone re-adds one of
|
||||
// these as either a real `function NAME(...) {...}` OR a
|
||||
// `const NAME = <anything>;` — under a plain `install.X === undefined`
|
||||
// check, an unexported re-add of either shape passes silently.
|
||||
const RETIRED_NAMES = [
|
||||
['applyRuntimeContentRewritesInPlace', 'applyRuntimeContentRewritesInPlace'],
|
||||
['applyRuntimeContentRewritesForCommandsInPlace', 'applyRuntimeContentRewritesForCommandsInPlace'],
|
||||
['convertClaudeToAugmentMarkdown', 'convertClaudeToAugmentMarkdown'],
|
||||
['convertClaudeCommandToAugmentSkill', 'convertClaudeCommandToAugmentSkill'],
|
||||
['convertClaudeAgentToAugmentAgent', 'convertClaudeAgentToAugmentAgent'],
|
||||
['convertClaudeCommandToWindsurfSkill', 'convertClaudeCommandToWindsurfSkill'],
|
||||
['convertClaudeCommandToWindsurfWorkflow', 'convertClaudeCommandToWindsurfWorkflow'],
|
||||
['convertClaudeAgentToWindsurfAgent', 'convertClaudeAgentToWindsurfAgent'],
|
||||
];
|
||||
|
||||
test('install.applyRuntimeContentRewritesInPlace === conversion.applyRuntimeContentRewritesInPlace (single walk loop)', () => {
|
||||
assert.strictEqual(
|
||||
install.applyRuntimeContentRewritesInPlace,
|
||||
conversionCjs.applyRuntimeContentRewritesInPlace,
|
||||
'install.js must bind applyRuntimeContentRewritesInPlace from conversion (not a duplicate walk loop)',
|
||||
);
|
||||
});
|
||||
for (const [name, conversionProp] of RETIRED_NAMES) {
|
||||
test(`${name}: retired from bin/install.js with zero top-level presence (#2876); conversion.${conversionProp} remains the single implementation`, () => {
|
||||
assert.strictEqual(install[name], undefined, `bin/install.js must no longer export ${name}`);
|
||||
assert.strictEqual(
|
||||
installTopLevel.functionNames.has(name),
|
||||
false,
|
||||
`bin/install.js must not declare a top-level function named ${name} — a re-added unexported copy is the #1511/#2876 duplicate-body regression this guard exists to catch`
|
||||
);
|
||||
assert.strictEqual(
|
||||
installTopLevel.variableInits.has(name),
|
||||
false,
|
||||
`bin/install.js must not declare a top-level const/let/var named ${name} — a re-added unexported copy is the #1511/#2876 duplicate-body regression this guard exists to catch`
|
||||
);
|
||||
assert.strictEqual(typeof conversionCjs[conversionProp], 'function', `${conversionProp} remains available from the conversion module`);
|
||||
});
|
||||
}
|
||||
|
||||
test('install.applyRuntimeContentRewritesForCommandsInPlace === conversion.applyRuntimeContentRewritesForCommandsInPlace (single copy+rewrite loop)', () => {
|
||||
assert.strictEqual(
|
||||
install.applyRuntimeContentRewritesForCommandsInPlace,
|
||||
conversionCjs.applyRuntimeContentRewritesForCommandsInPlace,
|
||||
'install.js must bind applyRuntimeContentRewritesForCommandsInPlace from conversion (not a duplicate copy+rewrite loop)',
|
||||
);
|
||||
});
|
||||
// Names #2876 kept as a bare single-hop reference (`const X =
|
||||
// runtimeArtifactConversion.<Y>;`) because bin/install.js still calls them
|
||||
// internally (computePathPrefix, _applyRuntimeRewrites,
|
||||
// convertClaudeToWindsurfMarkdown, applyClaudeCodeBrandSwap — #2931). The
|
||||
// regression this half guards against: the reference gets replaced with an
|
||||
// actual re-implemented body instead of staying a pointer to the single
|
||||
// conversion-module owner.
|
||||
const REFERENCE_ONLY_NAMES = [
|
||||
['computePathPrefix', '_computePathPrefix'],
|
||||
['_applyRuntimeRewrites', '_applyRuntimeRewrites'],
|
||||
['convertClaudeToWindsurfMarkdown', 'convertClaudeToWindsurfMarkdown'],
|
||||
['applyClaudeCodeBrandSwap', 'applyClaudeCodeBrandSwap'],
|
||||
];
|
||||
|
||||
test('install._applyRuntimeRewrites === conversion._applyRuntimeRewrites (single switch engine)', () => {
|
||||
assert.strictEqual(
|
||||
install._applyRuntimeRewrites,
|
||||
conversionCjs._applyRuntimeRewrites,
|
||||
'install.js must bind _applyRuntimeRewrites from conversion (not a local shim)',
|
||||
);
|
||||
});
|
||||
|
||||
// #1675 (ADR-1508): the augment converter family is single-sourced in the
|
||||
// conversion module. install.js must re-bind (not re-define) these so there
|
||||
// is exactly one body — the generative-drift hazard the dedup removes.
|
||||
test('install.convertClaudeToAugmentMarkdown === conversion.convertClaudeToAugmentMarkdown (single converter)', () => {
|
||||
assert.strictEqual(
|
||||
install.convertClaudeToAugmentMarkdown,
|
||||
conversionCjs.convertClaudeToAugmentMarkdown,
|
||||
'install.js must bind convertClaudeToAugmentMarkdown from conversion (not a duplicate body)',
|
||||
);
|
||||
});
|
||||
|
||||
test('install.convertClaudeCommandToAugmentSkill === conversion.convertClaudeCommandToAugmentSkill (single converter)', () => {
|
||||
assert.strictEqual(
|
||||
install.convertClaudeCommandToAugmentSkill,
|
||||
conversionCjs.convertClaudeCommandToAugmentSkill,
|
||||
'install.js must bind convertClaudeCommandToAugmentSkill from conversion (not a duplicate body)',
|
||||
);
|
||||
});
|
||||
|
||||
test('install.convertClaudeAgentToAugmentAgent === conversion.convertClaudeAgentToAugmentAgent (single converter)', () => {
|
||||
assert.strictEqual(
|
||||
install.convertClaudeAgentToAugmentAgent,
|
||||
conversionCjs.convertClaudeAgentToAugmentAgent,
|
||||
'install.js must bind convertClaudeAgentToAugmentAgent from conversion (not a duplicate body)',
|
||||
);
|
||||
});
|
||||
|
||||
// #2931 (ADR-1508): the windsurf converter family is single-sourced in the
|
||||
// conversion module, same pattern as the #1675 Augment dedup above.
|
||||
test('installJsBindsWindsurfConvertersByReference — convertClaudeCommandToWindsurfWorkflow (single converter)', () => {
|
||||
assert.strictEqual(
|
||||
install.convertClaudeCommandToWindsurfWorkflow,
|
||||
conversionCjs.convertClaudeCommandToWindsurfWorkflow,
|
||||
'install.js must bind convertClaudeCommandToWindsurfWorkflow from conversion (not a duplicate body)',
|
||||
);
|
||||
});
|
||||
|
||||
test('installJsBindsEntireWindsurfFamilyByReference — every windsurf converter member', () => {
|
||||
assert.strictEqual(
|
||||
install.convertClaudeToWindsurfMarkdown,
|
||||
conversionCjs.convertClaudeToWindsurfMarkdown,
|
||||
'install.js must bind convertClaudeToWindsurfMarkdown from conversion (not a duplicate body)',
|
||||
);
|
||||
assert.strictEqual(
|
||||
install.convertClaudeCommandToWindsurfSkill,
|
||||
conversionCjs.convertClaudeCommandToWindsurfSkill,
|
||||
'install.js must bind convertClaudeCommandToWindsurfSkill from conversion (not a duplicate body)',
|
||||
);
|
||||
assert.strictEqual(
|
||||
install.convertClaudeCommandToWindsurfWorkflow,
|
||||
conversionCjs.convertClaudeCommandToWindsurfWorkflow,
|
||||
'install.js must bind convertClaudeCommandToWindsurfWorkflow from conversion (not a duplicate body)',
|
||||
);
|
||||
assert.strictEqual(
|
||||
install.convertClaudeAgentToWindsurfAgent,
|
||||
conversionCjs.convertClaudeAgentToWindsurfAgent,
|
||||
'install.js must bind convertClaudeAgentToWindsurfAgent from conversion (not a duplicate body)',
|
||||
);
|
||||
});
|
||||
|
||||
// #2931 (ADR-1508): applyClaudeCodeBrandSwap + RUNTIME_COMPATIBILITY_BLOCK_RE
|
||||
// were duplicated verbatim in install.js (used by the local Cursor/Trae/
|
||||
// CodeBuddy/Cline converters) alongside the conversion module's copy —
|
||||
// exactly the unlinked-duplicate-implementation class this guard exists to
|
||||
// catch. install.js now re-binds (does not re-define) it.
|
||||
test('install.applyClaudeCodeBrandSwap === conversion.applyClaudeCodeBrandSwap (single implementation)', () => {
|
||||
assert.strictEqual(
|
||||
install.applyClaudeCodeBrandSwap,
|
||||
conversionCjs.applyClaudeCodeBrandSwap,
|
||||
'install.js must bind applyClaudeCodeBrandSwap from conversion (not a duplicate body)',
|
||||
);
|
||||
});
|
||||
for (const [local, conversionProp] of REFERENCE_ONLY_NAMES) {
|
||||
test(`${local}: still a bare runtimeArtifactConversion.${conversionProp} reference, not a re-implemented body`, () => {
|
||||
assert.strictEqual(install[local], undefined, `bin/install.js must no longer export ${local}`);
|
||||
const init = installTopLevel.variableInits.get(local);
|
||||
assert.ok(init, `bin/install.js must still declare a top-level const named ${local} (has an internal caller)`);
|
||||
assert.ok(
|
||||
isMemberReferenceTo(init, 'runtimeArtifactConversion', conversionProp),
|
||||
`bin/install.js's ${local} binding must be a bare \`runtimeArtifactConversion.${conversionProp}\` reference — anything else (a real function/object body) is the #1511/#2876 duplicate-body regression this guard exists to catch`
|
||||
);
|
||||
assert.strictEqual(typeof conversionCjs[conversionProp], 'function');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -4995,7 +5036,7 @@ describe('Bug #2973: installer migrates existing legacy dev-preferences.md to sk
|
||||
// documented signature. End-to-end install testing is covered by
|
||||
// tests/install-*.test.cjs which already exercise legacy preservation.
|
||||
assert.equal(typeof inst.migrateLegacyDevPreferencesToSkill, 'function',
|
||||
'expected migrateLegacyDevPreferencesToSkill in install.js exports (#2973)');
|
||||
'expected migrateLegacyDevPreferencesToSkill in install-engine.cjs exports (#2973)');
|
||||
});
|
||||
|
||||
test('migration writes to skills/gsd-dev-preferences/SKILL.md when no skill exists yet', () => {
|
||||
@@ -6804,7 +6845,8 @@ describe('#2873 C1-C6 — install-time shadow report (spawned installer wiring)'
|
||||
// - Kilo mirrors OpenCode (static-frontmatter twin, #2093).
|
||||
{
|
||||
const { describe: __d3543, test: __t3543, beforeEach: __be3543, afterEach: __ae3543 } = require('node:test');
|
||||
const { install: __install3543, readGsdRuntimeProfileResolver: __resolver3543 } = require('../bin/install.js');
|
||||
const { install: __install3543 } = require('../bin/install.js');
|
||||
const { readGsdRuntimeProfileResolver: __resolver3543 } = require('../gsd-core/bin/lib/install-model-override-resolver.cjs');
|
||||
const { captureConsole: __capture3543 } = require('./helpers.cjs');
|
||||
|
||||
// Installer-written shape for a non-Claude runtime (writeNonClaudeDefaults).
|
||||
|
||||
@@ -22,12 +22,14 @@ process.env['GSD_TEST_MODE'] = '1';
|
||||
const {
|
||||
copyWithPathReplacement,
|
||||
installCodexConfig,
|
||||
_copyStaged,
|
||||
_resolveUserArtifactStagingRoot: _installJsResolveUserArtifactStagingRoot,
|
||||
_tryResolveUserArtifactStagingRoot: _installJsTryResolveUserArtifactStagingRoot,
|
||||
install,
|
||||
uninstall,
|
||||
} = require('../bin/install.js');
|
||||
const {
|
||||
_copyStaged,
|
||||
} = require('../gsd-core/bin/lib/install-engine.cjs');
|
||||
|
||||
// #2875 (epic #2866 Phase 6): user-artifact-staging confinement rows (E1-E5).
|
||||
// Top-level (not inside any of the folded `__foldDescribe` sections below,
|
||||
|
||||
@@ -37,7 +37,6 @@ const pkg = require('../package.json');
|
||||
const { PROBE_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
|
||||
|
||||
const {
|
||||
getDirName,
|
||||
getConfigDirFromHome,
|
||||
install,
|
||||
uninstall,
|
||||
@@ -48,12 +47,14 @@ const {
|
||||
resolveKiloConfigPath,
|
||||
configureKiloPermissions,
|
||||
selectRuntimesFromArgs,
|
||||
normalizeNodePath,
|
||||
GSD_CHANGESET_FILES,
|
||||
GSD_SCRIPTS_LIB_FILES,
|
||||
installRuntimeArtifacts,
|
||||
} = require('../bin/install.js');
|
||||
|
||||
const { getDirName } = require('../gsd-core/bin/lib/runtime-name-policy.cjs');
|
||||
const { normalizeNodePath } = require('../gsd-core/bin/lib/runtime-hooks-surface.cjs');
|
||||
const { installRuntimeArtifacts } = require('../gsd-core/bin/lib/install-engine.cjs');
|
||||
|
||||
const { getGlobalConfigDir } = require('../gsd-core/bin/lib/runtime-homes.cjs');
|
||||
// #2874 AC3 exemplar (see the qwen install/uninstall group below): resolves
|
||||
// the SAME 'full' profile install(false, <runtime>) resolves by default
|
||||
@@ -2031,11 +2032,11 @@ const path = require('path');
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
const {
|
||||
readGsdEffectiveModelOverrides,
|
||||
generateCodexAgentToml,
|
||||
convertClaudeToOpencodeFrontmatter,
|
||||
getCodexSkillAdapterHeader,
|
||||
} = require('../bin/install.js');
|
||||
const { readGsdEffectiveModelOverrides } = require('../gsd-core/bin/lib/install-model-override-resolver.cjs');
|
||||
|
||||
const { createTempDir, cleanup } = require('./helpers.cjs');
|
||||
const makeTmp = (prefix) => createTempDir(`gsd-2256-${prefix}-`);
|
||||
@@ -2203,8 +2204,8 @@ const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const path = require('node:path');
|
||||
|
||||
const INSTALL = require(path.join(__dirname, '..', 'bin', 'install.js'));
|
||||
const { normalizeNodePath, resolveNodeRunner, rewriteLegacyManagedNodeHookCommands } = INSTALL;
|
||||
const HOOKS_SURFACE = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'runtime-hooks-surface.cjs'));
|
||||
const { normalizeNodePath, resolveNodeRunner, rewriteLegacyManagedNodeHookCommands } = HOOKS_SURFACE;
|
||||
|
||||
// ─── normalizeNodePath ────────────────────────────────────────────────────────
|
||||
|
||||
@@ -2499,8 +2500,7 @@ const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const path = require('node:path');
|
||||
|
||||
const INSTALL = require(path.join(__dirname, '..', 'bin', 'install.js'));
|
||||
const { normalizeNodePath, resolveNodeRunner } = INSTALL;
|
||||
const { normalizeNodePath, resolveNodeRunner } = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'runtime-hooks-surface.cjs'));
|
||||
|
||||
// ─── Synthetic paths used across tests ───────────────────────────────────────
|
||||
|
||||
@@ -2709,8 +2709,7 @@ const { test, describe } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const path = require('node:path');
|
||||
|
||||
const INSTALL = require(path.join(__dirname, '..', 'bin', 'install.js'));
|
||||
const { buildHookCommand, resolveNodeRunner } = INSTALL;
|
||||
const { buildHookCommand, resolveNodeRunner, rewriteLegacyManagedNodeHookCommands } = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'runtime-hooks-surface.cjs'));
|
||||
|
||||
/**
|
||||
* Parse a hook command string into { runner, hookPath } structured
|
||||
@@ -2876,8 +2875,6 @@ describe('Bug #2979: buildHookCommand for .sh hooks still uses bare "bash" (POSI
|
||||
|
||||
// ─── #3002 CR follow-up: legacy-bare-node migration ─────────────────────────
|
||||
|
||||
const { rewriteLegacyManagedNodeHookCommands } = INSTALL;
|
||||
|
||||
describe('Bug #2979 (#3002 CR): rewriteLegacyManagedNodeHookCommands rewrites bare-node managed hooks on reinstall', () => {
|
||||
test('exported as a function', () => {
|
||||
assert.equal(typeof rewriteLegacyManagedNodeHookCommands, 'function');
|
||||
@@ -3146,7 +3143,7 @@ describe('Bug #2979 (#3002 CR): resolveNodeRunner returns null when execPath una
|
||||
|
||||
// ─── #3002 CR follow-up #2: null-command guards in settings.json ──────────
|
||||
|
||||
const { validateHookFields } = INSTALL;
|
||||
const { validateHookFields } = require('../bin/install.js');
|
||||
|
||||
describe('Bug #2979 (#3002 CR follow-up): no command:null hook entries survive serialization', () => {
|
||||
// CR feedback: assert structurally on the resulting settings object, not by
|
||||
@@ -3369,13 +3366,12 @@ before(() => {
|
||||
conversion = require('../gsd-core/bin/lib/runtime-artifact-conversion.cjs');
|
||||
});
|
||||
|
||||
describe('bin/install.js compatibility export audit (#1559)', () => {
|
||||
test('retains audited compatibility relays for shared rewrite helpers', () => {
|
||||
assert.strictEqual(installer.processAttribution, conversion.processAttribution);
|
||||
assert.strictEqual(
|
||||
installer.applyRuntimeContentRewritesForCommandsInPlace,
|
||||
conversion.applyRuntimeContentRewritesForCommandsInPlace,
|
||||
);
|
||||
describe('bin/install.js compatibility export audit (#1559, retired by #2876)', () => {
|
||||
test('the two previously-audited compatibility relays are retired — test consumers now import runtime-artifact-conversion.cjs directly', () => {
|
||||
assert.equal(typeof conversion.processAttribution, 'function', 'processAttribution remains available from the conversion module');
|
||||
assert.equal(installer.processAttribution, undefined, 'processAttribution is no longer re-exported by bin/install.js (#2876)');
|
||||
assert.equal(typeof conversion.applyRuntimeContentRewritesForCommandsInPlace, 'function', 'applyRuntimeContentRewritesForCommandsInPlace remains available from the conversion module');
|
||||
assert.equal(installer.applyRuntimeContentRewritesForCommandsInPlace, undefined, 'applyRuntimeContentRewritesForCommandsInPlace is no longer re-exported by bin/install.js (#2876)');
|
||||
});
|
||||
|
||||
test('does not leak unaudited conversion-module helpers through the installer', () => {
|
||||
@@ -3759,25 +3755,29 @@ describe('#2771: legacy manifest entries for USER_OWNED_ARTIFACTS are normalized
|
||||
// ─── Test 4: shared constant exists and is used by both call sites ────────────
|
||||
|
||||
describe('#2771: USER_OWNED_ARTIFACTS is a single source of truth', () => {
|
||||
test('install.js exports USER_OWNED_ARTIFACTS containing USER-PROFILE.md', () => {
|
||||
test('install-engine.cjs exports USER_OWNED_ARTIFACTS containing USER-PROFILE.md; bin/install.js no longer re-exports it (#2876)', () => {
|
||||
const origMode = process.env.GSD_TEST_MODE;
|
||||
process.env.GSD_TEST_MODE = '1';
|
||||
let mod;
|
||||
let engine;
|
||||
try {
|
||||
delete require.cache[require.resolve(INSTALL_SCRIPT)];
|
||||
mod = require(INSTALL_SCRIPT);
|
||||
engine = require('../gsd-core/bin/lib/install-engine.cjs');
|
||||
} finally {
|
||||
if (origMode === undefined) delete process.env.GSD_TEST_MODE;
|
||||
else process.env.GSD_TEST_MODE = origMode;
|
||||
}
|
||||
|
||||
assert.strictEqual(mod.USER_OWNED_ARTIFACTS, undefined, 'bin/install.js must no longer export USER_OWNED_ARTIFACTS (#2876)');
|
||||
|
||||
assert.ok(
|
||||
Array.isArray(mod.USER_OWNED_ARTIFACTS) || mod.USER_OWNED_ARTIFACTS instanceof Set,
|
||||
'install.js must export USER_OWNED_ARTIFACTS as a single source of truth'
|
||||
Array.isArray(engine.USER_OWNED_ARTIFACTS) || engine.USER_OWNED_ARTIFACTS instanceof Set,
|
||||
'install-engine.cjs must export USER_OWNED_ARTIFACTS as a single source of truth'
|
||||
);
|
||||
const list = Array.isArray(mod.USER_OWNED_ARTIFACTS)
|
||||
? mod.USER_OWNED_ARTIFACTS
|
||||
: Array.from(mod.USER_OWNED_ARTIFACTS);
|
||||
const list = Array.isArray(engine.USER_OWNED_ARTIFACTS)
|
||||
? engine.USER_OWNED_ARTIFACTS
|
||||
: Array.from(engine.USER_OWNED_ARTIFACTS);
|
||||
assert.ok(
|
||||
list.includes('USER-PROFILE.md'),
|
||||
'USER_OWNED_ARTIFACTS must include USER-PROFILE.md'
|
||||
@@ -6001,52 +6001,63 @@ const manifestPath = path.join(
|
||||
);
|
||||
const manifest = require(manifestPath);
|
||||
|
||||
// Load install.js exported values (executes the module, not text inspection)
|
||||
// Load install-effort-resolver.cjs's catalog (executes the module, not text
|
||||
// inspection). #2876 (epic #2866 Phase 7) retired bin/install.js's
|
||||
// `_GSD_EFFORT_MANIFEST_TIER_DEFAULTS` / `_GSD_EFFORT_MANIFEST_DEFAULT`
|
||||
// getter re-exports — install-effort-resolver.cjs's `_getGsdEffortCatalog()`
|
||||
// was always the single source; bin/install.js's getters merely relayed it.
|
||||
const installPath = path.join(__dirname, '..', 'bin', 'install.js');
|
||||
const {
|
||||
_GSD_EFFORT_MANIFEST_TIER_DEFAULTS,
|
||||
_GSD_EFFORT_MANIFEST_DEFAULT,
|
||||
} = require(installPath);
|
||||
const { _getGsdEffortCatalog } = require(
|
||||
path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'install-effort-resolver.cjs')
|
||||
);
|
||||
const _GSD_EFFORT_MANIFEST_TIER_DEFAULTS = _getGsdEffortCatalog().EFFORT_MANIFEST_TIER_DEFAULTS;
|
||||
const _GSD_EFFORT_MANIFEST_DEFAULT = _getGsdEffortCatalog().EFFORT_MANIFEST_DEFAULT;
|
||||
|
||||
test('install.js _GSD_EFFORT_MANIFEST_TIER_DEFAULTS.light matches manifest effort.routing_tier_defaults.light', () => {
|
||||
test('bin/install.js no longer re-exports _GSD_EFFORT_MANIFEST_TIER_DEFAULTS / _GSD_EFFORT_MANIFEST_DEFAULT (#2876)', () => {
|
||||
const installExports = require(installPath);
|
||||
assert.strictEqual(installExports._GSD_EFFORT_MANIFEST_TIER_DEFAULTS, undefined);
|
||||
assert.strictEqual(installExports._GSD_EFFORT_MANIFEST_DEFAULT, undefined);
|
||||
});
|
||||
|
||||
test('install-effort-resolver.cjs _GSD_EFFORT_MANIFEST_TIER_DEFAULTS.light matches manifest effort.routing_tier_defaults.light', () => {
|
||||
assert.strictEqual(
|
||||
_GSD_EFFORT_MANIFEST_TIER_DEFAULTS.light,
|
||||
manifest.effort.routing_tier_defaults.light,
|
||||
`install.js tier default for "light" (${_GSD_EFFORT_MANIFEST_TIER_DEFAULTS.light}) differs from manifest (${manifest.effort.routing_tier_defaults.light})`
|
||||
`install-effort-resolver.cjs tier default for "light" (${_GSD_EFFORT_MANIFEST_TIER_DEFAULTS.light}) differs from manifest (${manifest.effort.routing_tier_defaults.light})`
|
||||
);
|
||||
});
|
||||
|
||||
test('install.js _GSD_EFFORT_MANIFEST_TIER_DEFAULTS.standard matches manifest effort.routing_tier_defaults.standard', () => {
|
||||
test('install-effort-resolver.cjs _GSD_EFFORT_MANIFEST_TIER_DEFAULTS.standard matches manifest effort.routing_tier_defaults.standard', () => {
|
||||
assert.strictEqual(
|
||||
_GSD_EFFORT_MANIFEST_TIER_DEFAULTS.standard,
|
||||
manifest.effort.routing_tier_defaults.standard,
|
||||
`install.js tier default for "standard" (${_GSD_EFFORT_MANIFEST_TIER_DEFAULTS.standard}) differs from manifest (${manifest.effort.routing_tier_defaults.standard})`
|
||||
`install-effort-resolver.cjs tier default for "standard" (${_GSD_EFFORT_MANIFEST_TIER_DEFAULTS.standard}) differs from manifest (${manifest.effort.routing_tier_defaults.standard})`
|
||||
);
|
||||
});
|
||||
|
||||
test('install.js _GSD_EFFORT_MANIFEST_TIER_DEFAULTS.heavy matches manifest effort.routing_tier_defaults.heavy', () => {
|
||||
test('install-effort-resolver.cjs _GSD_EFFORT_MANIFEST_TIER_DEFAULTS.heavy matches manifest effort.routing_tier_defaults.heavy', () => {
|
||||
assert.strictEqual(
|
||||
_GSD_EFFORT_MANIFEST_TIER_DEFAULTS.heavy,
|
||||
manifest.effort.routing_tier_defaults.heavy,
|
||||
`install.js tier default for "heavy" (${_GSD_EFFORT_MANIFEST_TIER_DEFAULTS.heavy}) differs from manifest (${manifest.effort.routing_tier_defaults.heavy})`
|
||||
`install-effort-resolver.cjs tier default for "heavy" (${_GSD_EFFORT_MANIFEST_TIER_DEFAULTS.heavy}) differs from manifest (${manifest.effort.routing_tier_defaults.heavy})`
|
||||
);
|
||||
});
|
||||
|
||||
test('install.js _GSD_EFFORT_MANIFEST_DEFAULT matches manifest effort.default', () => {
|
||||
test('install-effort-resolver.cjs _GSD_EFFORT_MANIFEST_DEFAULT matches manifest effort.default', () => {
|
||||
assert.strictEqual(
|
||||
_GSD_EFFORT_MANIFEST_DEFAULT,
|
||||
manifest.effort.default,
|
||||
`install.js effort default (${_GSD_EFFORT_MANIFEST_DEFAULT}) differs from manifest (${manifest.effort.default})`
|
||||
`install-effort-resolver.cjs effort default (${_GSD_EFFORT_MANIFEST_DEFAULT}) differs from manifest (${manifest.effort.default})`
|
||||
);
|
||||
});
|
||||
|
||||
test('install.js tier-defaults object has exactly the same keys as manifest effort.routing_tier_defaults', () => {
|
||||
test('install-effort-resolver.cjs tier-defaults object has exactly the same keys as manifest effort.routing_tier_defaults', () => {
|
||||
const installKeys = Object.keys(_GSD_EFFORT_MANIFEST_TIER_DEFAULTS).sort();
|
||||
const manifestKeys = Object.keys(manifest.effort.routing_tier_defaults).sort();
|
||||
assert.deepStrictEqual(
|
||||
installKeys,
|
||||
manifestKeys,
|
||||
`Key mismatch — install.js: [${installKeys.join(', ')}], manifest: [${manifestKeys.join(', ')}]`
|
||||
`Key mismatch — install-effort-resolver.cjs: [${installKeys.join(', ')}], manifest: [${manifestKeys.join(', ')}]`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2087,7 +2087,8 @@ const { throwIfFailed } = require('./helpers/git-fixture.cjs');
|
||||
|
||||
const installModule = require('../bin/install.js');
|
||||
const { readInstallState } = require('../gsd-core/bin/lib/installer-migrations.cjs');
|
||||
const { install, parseTomlToObject, reconcileCodexHooksJsonEvent } = installModule;
|
||||
const { install, parseTomlToObject } = installModule;
|
||||
const { reconcileCodexHooksJsonEvent } = require('../gsd-core/bin/lib/runtime-hooks-surface.cjs');
|
||||
const { createTempDir, cleanup } = require('./helpers.cjs');
|
||||
const HOOKS_DIST = path.join(__dirname, '..', 'hooks', 'dist');
|
||||
const BUILD_HOOKS_SCRIPT = path.join(__dirname, '..', 'scripts', 'build-hooks.js');
|
||||
|
||||
@@ -2354,7 +2354,7 @@ const {
|
||||
|
||||
const {
|
||||
injectEffortFrontmatter,
|
||||
} = require('../bin/install.js');
|
||||
} = require('../gsd-core/bin/lib/runtime-artifact-conversion.cjs');
|
||||
|
||||
function writeConfig(dir, config) {
|
||||
const planningDir = path.join(dir, '.planning');
|
||||
@@ -5512,9 +5512,10 @@ describe('issue #2517: install end-to-end — per-project config reaches Codex T
|
||||
const prevTestMode = process.env.GSD_TEST_MODE;
|
||||
process.env.GSD_TEST_MODE = '1';
|
||||
const installMod = require('../bin/install.js');
|
||||
const { readGsdRuntimeProfileResolver } = require('../gsd-core/bin/lib/install-model-override-resolver.cjs');
|
||||
if (prevTestMode === undefined) delete process.env.GSD_TEST_MODE;
|
||||
else process.env.GSD_TEST_MODE = prevTestMode;
|
||||
const { readGsdRuntimeProfileResolver, generateCodexAgentToml } = installMod;
|
||||
const { generateCodexAgentToml } = installMod;
|
||||
|
||||
let tmpDir;
|
||||
beforeEach(() => { isolateHome(); tmpDir = createTempProject(); resetRuntimeWarningCaches(); });
|
||||
|
||||
@@ -235,8 +235,8 @@ let computePathPrefix;
|
||||
before(() => {
|
||||
process.env.GSD_TEST_MODE = '1';
|
||||
// Re-require fresh in case other tests already loaded it.
|
||||
delete require.cache[require.resolve('../bin/install.js')];
|
||||
({ computePathPrefix } = require('../bin/install.js'));
|
||||
delete require.cache[require.resolve('../gsd-core/bin/lib/runtime-artifact-conversion.cjs')];
|
||||
({ _computePathPrefix: computePathPrefix } = require('../gsd-core/bin/lib/runtime-artifact-conversion.cjs'));
|
||||
});
|
||||
|
||||
after(() => {
|
||||
@@ -244,7 +244,7 @@ after(() => {
|
||||
});
|
||||
|
||||
describe('bug-2376: OpenCode on Windows must use absolute path, not $HOME', () => {
|
||||
test('computePathPrefix is exported by install.js', () => {
|
||||
test('computePathPrefix (_computePathPrefix) is exported by runtime-artifact-conversion.cjs', () => {
|
||||
assert.equal(typeof computePathPrefix, 'function');
|
||||
});
|
||||
|
||||
@@ -316,8 +316,8 @@ let computePathPrefix;
|
||||
|
||||
before(() => {
|
||||
process.env.GSD_TEST_MODE = '1';
|
||||
delete require.cache[require.resolve('../bin/install.js')];
|
||||
({ computePathPrefix } = require('../bin/install.js'));
|
||||
delete require.cache[require.resolve('../gsd-core/bin/lib/runtime-artifact-conversion.cjs')];
|
||||
({ _computePathPrefix: computePathPrefix } = require('../gsd-core/bin/lib/runtime-artifact-conversion.cjs'));
|
||||
});
|
||||
|
||||
after(() => {
|
||||
@@ -325,7 +325,7 @@ after(() => {
|
||||
});
|
||||
|
||||
describe('bug-2831: OpenCode pathPrefix uses absolute path on all platforms', () => {
|
||||
test('computePathPrefix is exported by install.js', () => {
|
||||
test('computePathPrefix (_computePathPrefix) is exported by runtime-artifact-conversion.cjs', () => {
|
||||
assert.equal(typeof computePathPrefix, 'function');
|
||||
});
|
||||
|
||||
|
||||
@@ -392,7 +392,7 @@ describe('applySurface', () => {
|
||||
// both paths must produce identical sets.
|
||||
test('applySurface command-dir filenames match a fresh install for every command runtime (#816 parity)', async (t) => {
|
||||
process.env.GSD_TEST_MODE = '1';
|
||||
const { installRuntimeArtifacts } = require('../bin/install.js');
|
||||
const { installRuntimeArtifacts } = require('../gsd-core/bin/lib/install-engine.cjs');
|
||||
|
||||
const manifest = loadSkillsManifest(REAL_COMMANDS_DIR);
|
||||
// Build the resolved profile once. Both install and surface sides must use
|
||||
|
||||
@@ -17,14 +17,17 @@ const {
|
||||
convertClaudeToOpencodeFrontmatter,
|
||||
convertClaudeToKiloFrontmatter,
|
||||
convertClaudeAgentToAntigravityAgent,
|
||||
convertClaudeCommandToOpencodeSkill,
|
||||
convertClaudeCommandToKiloSkill,
|
||||
convertClaudeCommandToTraeSkill,
|
||||
convertClaudeCommandToKimiSkill,
|
||||
buildKimiAgentArtifacts,
|
||||
neutralizeAgentReferences,
|
||||
} = require('../bin/install.js');
|
||||
|
||||
const {
|
||||
convertClaudeCommandToOpencodeSkill,
|
||||
convertClaudeCommandToKiloSkill,
|
||||
} = require('../gsd-core/bin/lib/install-engine.cjs');
|
||||
|
||||
// Sample Claude agent frontmatter (matches actual GSD agent format)
|
||||
const SAMPLE_AGENT = `---
|
||||
name: gsd-executor
|
||||
|
||||
@@ -27,10 +27,10 @@ const assert = require('node:assert/strict');
|
||||
const path = require('path');
|
||||
|
||||
// buildHookCommand was extracted to gsd-core/bin/lib/runtime-hooks-surface.cjs
|
||||
// (ADR-857 phase 5f-1) and re-exported via install.js. Import through install.js
|
||||
// so the test exercises the same public surface that the rest of the codebase uses.
|
||||
const INSTALL = require(path.join(__dirname, '..', 'bin', 'install.js'));
|
||||
const { buildHookCommand } = INSTALL;
|
||||
// (ADR-857 phase 5f-1). #2876 (epic #2866 Phase 7) retired install.js's
|
||||
// pass-through re-export (zero production consumers) — import the module
|
||||
// directly.
|
||||
const { buildHookCommand } = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'runtime-hooks-surface.cjs'));
|
||||
|
||||
const SH_HOOKS = [
|
||||
{ name: 'gsd-validate-commit.sh' },
|
||||
|
||||
@@ -524,7 +524,7 @@ const assert = require('node:assert/strict');
|
||||
const path = require('node:path');
|
||||
|
||||
const projection = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'shell-command-projection.cjs'));
|
||||
const install = require(path.join(__dirname, '..', 'bin', 'install.js'));
|
||||
const hooksSurface = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'runtime-hooks-surface.cjs'));
|
||||
|
||||
const {
|
||||
hookCommandNeedsPowerShellCallOperator,
|
||||
@@ -535,7 +535,7 @@ const {
|
||||
projectLegacySettingsHookCommand,
|
||||
projectPortableHookBaseDir,
|
||||
} = projection;
|
||||
const { buildHookCommand, rewriteLegacyManagedNodeHookCommands } = install;
|
||||
const { buildHookCommand, rewriteLegacyManagedNodeHookCommands } = hooksSurface;
|
||||
|
||||
describe('bug #3413: Shell Command Projection Module uses runtime-aware hook policy', () => {
|
||||
test('#1928: no current runtime needs the PowerShell call operator (seam inert after gemini removal)', () => {
|
||||
|
||||
@@ -478,7 +478,8 @@ describe('stale-bake-guard parity with bin/install.js bake paths', () => {
|
||||
path.join(tmp, '.planning', 'config.json'),
|
||||
JSON.stringify({ model_overrides: { 'gsd-executor': 'opencode-go/flash', 'gsd-planner': 'openai/gpt-5' } }),
|
||||
);
|
||||
const resolved = install.readGsdEffectiveModelOverrides(tmp, { homedir: () => sandboxHome });
|
||||
const { readGsdEffectiveModelOverrides } = require(path.join(REPO_ROOT, 'gsd-core', 'bin', 'lib', 'install-model-override-resolver.cjs'));
|
||||
const resolved = readGsdEffectiveModelOverrides(tmp, { homedir: () => sandboxHome });
|
||||
assert.deepEqual(resolved, { 'gsd-executor': 'opencode-go/flash', 'gsd-planner': 'openai/gpt-5' });
|
||||
} finally {
|
||||
cleanup(tmp);
|
||||
|
||||
@@ -18,7 +18,7 @@ const {
|
||||
convertClaudeCommandToWindsurfWorkflow,
|
||||
convertClaudeAgentToWindsurfAgent,
|
||||
convertClaudeToWindsurfMarkdown,
|
||||
} = require('../bin/install.js');
|
||||
} = require('../gsd-core/bin/lib/runtime-artifact-conversion.cjs');
|
||||
|
||||
// Mirrors WINDSURF_WORKFLOW_DESCRIPTION_MAX in
|
||||
// src/runtime-artifact-conversion.cts (not exported — the module's own
|
||||
|
||||
@@ -184,7 +184,7 @@ describe('atomic write temp-tracking — shared Set parity guard', () => {
|
||||
assert.ok(tmpsSet instanceof Set, '__atomicWrittenTmps must be a Set');
|
||||
const sizeBefore = tmpsSet.size;
|
||||
|
||||
const { writeCursorHooksJson } = require('../bin/install.js');
|
||||
const { writeCursorHooksJson } = require('../gsd-core/bin/lib/runtime-hooks-surface.cjs');
|
||||
writeCursorHooksJson(targetDir, srcDir, {});
|
||||
|
||||
// After the write, the Set must have grown: the hooks.json temp path
|
||||
@@ -227,7 +227,8 @@ describe('cursor hook registration — behavioral guard (module-owned hooks)', (
|
||||
fs.writeFileSync(path.join(srcHooksDir, 'gsd-cursor-session-start.js'), '// stub\n');
|
||||
fs.writeFileSync(path.join(srcHooksDir, 'gsd-cursor-post-tool.js'), '// stub\n');
|
||||
|
||||
const { writeCursorHooksJson, GSD_CURSOR_HOOK_MARKER } = require('../bin/install.js');
|
||||
const { GSD_CURSOR_HOOK_MARKER } = require('../bin/install.js');
|
||||
const { writeCursorHooksJson } = require('../gsd-core/bin/lib/runtime-hooks-surface.cjs');
|
||||
const result = writeCursorHooksJson(targetDir, srcDir, {});
|
||||
|
||||
assert.ok(result, 'writeCursorHooksJson must return a result');
|
||||
|
||||
Reference in New Issue
Block a user