Files
get-shit-done/tests/bug-1891-file-resolution.test.cjs
Tom Boucher 14fd090e47 docs(config): document missing config keys in planning-config.md (#1947)
* fix(core): resolve @file: references in gsd-tools stdout (#1891)

Workflows used bash-specific `if [[ "$INIT" == @file:* ]]` to detect
when large JSON was written to a temp file. This syntax breaks on
PowerShell and other non-bash shells.

Intercept stdout in gsd-tools.cjs to transparently resolve @file:
references before they reach the caller, matching the existing --pick
path behavior. The bash checks in workflow files become harmless
no-ops and can be removed over time.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* docs(config): add missing config fields to planning-config.md (#1880)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Tibsfox <tibsfox@tibsfox.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 17:36:47 -04:00

59 lines
2.1 KiB
JavaScript

/**
* Regression tests for bug #1891
*
* gsd-tools.cjs must transparently resolve @file: references in stdout
* so that workflows never see the @file: prefix. This eliminates the
* bash-specific `if [[ "$INIT" == @file:* ]]` check that breaks on
* PowerShell and other non-bash shells.
*/
'use strict';
const { describe, test, before } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const GSD_TOOLS_SRC = path.join(__dirname, '..', 'get-shit-done', 'bin', 'gsd-tools.cjs');
describe('bug #1891: @file: resolution in gsd-tools.cjs', () => {
let src;
before(() => {
src = fs.readFileSync(GSD_TOOLS_SRC, 'utf-8');
});
test('main() intercepts stdout and resolves @file: references', () => {
// The non-pick path should have @file: resolution, just like the --pick path
assert.ok(
src.includes("captured.startsWith('@file:')") ||
src.includes('captured.startsWith(\'@file:\')'),
'main() should check for @file: prefix in captured output'
);
});
test('@file: resolution reads file content via readFileSync', () => {
// Verify the resolution reads the actual file
assert.ok(
src.includes("readFileSync(captured.slice(6)") ||
src.includes('readFileSync(captured.slice(6)'),
'@file: resolution should read file at the path after the prefix'
);
});
test('stdout interception wraps runCommand in the non-pick path', () => {
// The main function should intercept fs.writeSync for fd=1
// in BOTH the pick path AND the normal path
const mainFunc = src.slice(src.indexOf('async function main()'));
const pickInterception = mainFunc.indexOf('// When --pick is active');
const fileResolution = mainFunc.indexOf('@file:');
// There should be at least two @file: resolution points:
// one in the --pick path and one in the normal path
const firstAt = mainFunc.indexOf("'@file:'");
const secondAt = mainFunc.indexOf("'@file:'", firstAt + 1);
assert.ok(secondAt > firstAt,
'Both --pick and normal paths should resolve @file: references');
});
});