feat(sdk): Phase 1 typed query foundation (gsd-sdk query) (#2118)

* feat(sdk): add typed query foundation and gsd-sdk query (Phase 1)

Add sdk/src/query registry and handlers with tests, GSDQueryError, CLI query wiring, and supporting type/tool-scoping hooks. Update CHANGELOG. Vitest 4 constructor mock fixes in milestone-runner tests.

Made-with: Cursor

* chore: gitignore .cursor for local-only Cursor assets

Made-with: Cursor

* fix(sdk): harden query layer for PR review (paths, locks, CLI, ReDoS)

- resolvePathUnderProject: realpath + relative containment for frontmatter and key_links

- commitToSubrepo: path checks + sanitizeCommitMessage

- statePlannedPhase: readModifyWriteStateMd (lock); MUTATION_COMMANDS + events

- key_links: regexForKeyLinkPattern length/ReDoS guard; phase dirs: reject .. and separators

- gsd-sdk: strip --pick before parseArgs; strict parser; QueryRegistry.commands()

- progress: static GSDError import; tests updated

Made-with: Cursor

* feat(sdk): query follow-up — tests, QUERY-HANDLERS, registry, locks, intel depth

Made-with: Cursor

* docs(sdk): use ASCII punctuation in QUERY-HANDLERS.md

Made-with: Cursor
This commit is contained in:
Rezolv
2026-04-12 18:15:04 -04:00
committed by GitHub
parent 66a5f939b0
commit 6f79b1dd5e
36 changed files with 848 additions and 142 deletions

73
sdk/src/query/uat.test.ts Normal file
View File

@@ -0,0 +1,73 @@
/**
* Tests for UAT query handlers.
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { mkdtemp, writeFile, mkdir, rm } from 'node:fs/promises';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { uatRenderCheckpoint, auditUat } from './uat.js';
const SAMPLE_UAT = `---
status: draft
---
# UAT
## Current Test
number: 1
name: Login flow
expected: |
User can sign in
## Other
`;
describe('uatRenderCheckpoint', () => {
let tmpDir: string;
beforeEach(async () => {
tmpDir = await mkdtemp(join(tmpdir(), 'gsd-uat-'));
});
afterEach(async () => {
await rm(tmpDir, { recursive: true, force: true });
});
it('returns error when --file is missing', async () => {
const r = await uatRenderCheckpoint([], tmpDir);
const data = r.data as Record<string, unknown>;
expect(data.error).toBeDefined();
});
it('renders checkpoint for valid UAT file', async () => {
const f = join(tmpDir, '01-UAT.md');
await writeFile(f, SAMPLE_UAT, 'utf-8');
const r = await uatRenderCheckpoint(['--file', '01-UAT.md'], tmpDir);
const data = r.data as Record<string, unknown>;
expect(data.checkpoint).toBeDefined();
expect(String(data.checkpoint)).toContain('CHECKPOINT');
expect(data.test_number).toBe(1);
});
});
describe('auditUat', () => {
let tmpDir: string;
beforeEach(async () => {
tmpDir = await mkdtemp(join(tmpdir(), 'gsd-uat-audit-'));
await mkdir(join(tmpDir, '.planning', 'phases', '01-x'), { recursive: true });
});
afterEach(async () => {
await rm(tmpDir, { recursive: true, force: true });
});
it('returns empty results when no UAT files', async () => {
const r = await auditUat([], tmpDir);
const data = r.data as Record<string, unknown>;
expect(Array.isArray(data.results)).toBe(true);
expect((data.summary as Record<string, number>).total_files).toBe(0);
});
});