mirror of
https://github.com/glittercowboy/get-shit-done
synced 2026-04-25 17:25:23 +02:00
* chore: ignore .worktrees directory Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(install): remove marketing taglines from runtime selection prompt Closes #1654 The runtime selection menu had promotional copy appended to some entries ("open source, the #1 AI coding platform on OpenRouter", "open source, free models"). Replaced with just the name and path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * test(kilo): update test to assert marketing tagline is removed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(tests): use process.execPath so tests pass in shells without node on PATH Three test patterns called bare `node` via shell, which fails in Claude Code sessions where `node` is not on PATH: - helpers.cjs string branch: execSync(`node ...`) → execFileSync(process.execPath) with a shell-style tokenizer that handles quoted args and inner-quote stripping - hooks-opt-in.test.cjs: spawnSync('bash', ...) for hooks that call `node` internally → spawnHook() wrapper that injects process.execPath dir into PATH - concurrency-safety.test.cjs: exec(`node ...`) for concurrent patch test → exec(`"${process.execPath}" ...`) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: resolve #1656 and #1657 — bash hooks missing from dist, SDK install prompt #1656: Community bash hooks (gsd-session-state.sh, gsd-validate-commit.sh, gsd-phase-boundary.sh) were never included in HOOKS_TO_COPY in build-hooks.js, so hooks/dist/ never contained them and the installer could not copy them to user machines. Fixed by adding the three .sh files to the copy array with chmod +x preservation and skipping JS syntax validation for shell scripts. #1657: promptSdk() called installSdk() which ran `npm install -g @gsd-build/sdk` — a package that does not exist on npm, causing visible errors during interactive installs. Removed promptSdk(), installSdk(), --sdk flag, and all call sites. Regression tests in tests/bugs-1656-1657.test.cjs guard both fixes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: sort runtime list alphabetically after Claude Code - Claude Code stays pinned at position 1 - Remaining 10 runtimes sorted A-Z: Antigravity(2), Augment(3), Codex(4), Copilot(5), Cursor(6), Gemini(7), Kilo(8), OpenCode(9), Trae(10), Windsurf(11) - Updated runtimeMap, allRuntimes, and prompt display in promptRuntime() - Updated multi-runtime-select, kilo-install, copilot-install tests to match Also fix #1656 regression test: run build-hooks.js in before() hook so hooks/dist/ is populated on CI (directory is gitignored; build runs via prepublishOnly before publish, not during npm ci). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
111 lines
3.8 KiB
JavaScript
111 lines
3.8 KiB
JavaScript
/**
|
|
* GSD Tools Test Helpers
|
|
*/
|
|
|
|
const { execSync, execFileSync } = require('child_process');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const TOOLS_PATH = path.join(__dirname, '..', 'get-shit-done', 'bin', 'gsd-tools.cjs');
|
|
const TEST_ENV_BASE = {
|
|
GSD_SESSION_KEY: '',
|
|
CODEX_THREAD_ID: '',
|
|
CLAUDE_SESSION_ID: '',
|
|
CLAUDE_CODE_SSE_PORT: '',
|
|
OPENCODE_SESSION_ID: '',
|
|
GEMINI_SESSION_ID: '',
|
|
CURSOR_SESSION_ID: '',
|
|
WINDSURF_SESSION_ID: '',
|
|
TERM_SESSION_ID: '',
|
|
WT_SESSION: '',
|
|
TMUX_PANE: '',
|
|
ZELLIJ_SESSION_NAME: '',
|
|
TTY: '',
|
|
SSH_TTY: '',
|
|
};
|
|
|
|
/**
|
|
* Run gsd-tools command.
|
|
*
|
|
* @param {string|string[]} args - Command string (shell-interpreted) or array
|
|
* of arguments (shell-bypassed via execFileSync, safe for JSON and dollar signs).
|
|
* @param {string} cwd - Working directory.
|
|
* @param {object} [env] - Optional env overrides merged on top of process.env.
|
|
* Pass { HOME: cwd } to sandbox ~/.gsd/ lookups in tests that assert concrete
|
|
* config values that could be overridden by a developer's defaults.json.
|
|
*/
|
|
function runGsdTools(args, cwd = process.cwd(), env = {}) {
|
|
try {
|
|
let result;
|
|
const childEnv = { ...process.env, ...TEST_ENV_BASE, ...env };
|
|
if (Array.isArray(args)) {
|
|
result = execFileSync(process.execPath, [TOOLS_PATH, ...args], {
|
|
cwd,
|
|
encoding: 'utf-8',
|
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
env: childEnv,
|
|
});
|
|
} else {
|
|
// Split shell-style string into argv, stripping surrounding quotes, so we
|
|
// can invoke execFileSync with process.execPath instead of relying on
|
|
// `node` being on PATH (it isn't in Claude Code shell sessions).
|
|
// Apply shell-style quote removal: strip surrounding quotes from quoted
|
|
// sequences anywhere in a token (handles both "foo bar" and --"foo bar").
|
|
const argv = (args.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || [])
|
|
.map(t => t.replace(/"([^"]*)"/g, '$1').replace(/'([^']*)'/g, '$1'));
|
|
result = execFileSync(process.execPath, [TOOLS_PATH, ...argv], {
|
|
cwd,
|
|
encoding: 'utf-8',
|
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
env: childEnv,
|
|
});
|
|
}
|
|
return { success: true, output: result.trim() };
|
|
} catch (err) {
|
|
return {
|
|
success: false,
|
|
output: err.stdout?.toString().trim() || '',
|
|
error: err.stderr?.toString().trim() || err.message,
|
|
};
|
|
}
|
|
}
|
|
|
|
// Create a bare temp directory (no .planning/ structure)
|
|
function createTempDir(prefix = 'gsd-test-') {
|
|
return fs.mkdtempSync(path.join(require('os').tmpdir(), prefix));
|
|
}
|
|
|
|
// Create temp directory structure
|
|
function createTempProject(prefix = 'gsd-test-') {
|
|
const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), prefix));
|
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases'), { recursive: true });
|
|
return tmpDir;
|
|
}
|
|
|
|
// Create temp directory with initialized git repo and at least one commit
|
|
function createTempGitProject(prefix = 'gsd-test-') {
|
|
const tmpDir = fs.mkdtempSync(path.join(require('os').tmpdir(), prefix));
|
|
fs.mkdirSync(path.join(tmpDir, '.planning', 'phases'), { recursive: true });
|
|
|
|
execSync('git init', { cwd: tmpDir, stdio: 'pipe' });
|
|
execSync('git config user.email "test@test.com"', { cwd: tmpDir, stdio: 'pipe' });
|
|
execSync('git config user.name "Test"', { cwd: tmpDir, stdio: 'pipe' });
|
|
execSync('git config commit.gpgsign false', { cwd: tmpDir, stdio: 'pipe' });
|
|
|
|
fs.writeFileSync(
|
|
path.join(tmpDir, '.planning', 'PROJECT.md'),
|
|
'# Project\n\nTest project.\n'
|
|
);
|
|
|
|
execSync('git add -A', { cwd: tmpDir, stdio: 'pipe' });
|
|
execSync('git commit -m "initial commit"', { cwd: tmpDir, stdio: 'pipe' });
|
|
|
|
return tmpDir;
|
|
}
|
|
|
|
function cleanup(tmpDir) {
|
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
}
|
|
|
|
module.exports = { runGsdTools, createTempDir, createTempProject, createTempGitProject, cleanup, TOOLS_PATH };
|