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>
This commit is contained in:
58
tests/bug-1891-file-resolution.test.cjs
Normal file
58
tests/bug-1891-file-resolution.test.cjs
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* 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');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user