fix(query): thread workstream through all query handlers
All 18+ query handlers accepted _workstream but never forwarded it to planningPaths/loadConfig/getMilestoneInfo. Remove _ prefix and pass workstream to all internal helper calls so --ws flag actually scopes path resolution. Affected handlers: initNewProject, initProgress, initManager, configGet, configPath, configSet, configSetModelProfile, configNewProject, configEnsureSection, validateHealth, commit, checkCommit, commitToSubrepo. Also fixes validate.ts to use paths.* fields from planningPaths instead of hardcoded join(projectDir, '.planning') paths. Closes #2731
This commit is contained in:
@@ -95,7 +95,7 @@ export function sanitizeCommitMessage(text: string): string {
|
||||
* @param projectDir - Project root directory
|
||||
* @returns QueryResult with commit result
|
||||
*/
|
||||
export const commit: QueryHandler = async (args, projectDir, _workstream) => {
|
||||
export const commit: QueryHandler = async (args, projectDir, workstream) => {
|
||||
const allArgs = [...args];
|
||||
|
||||
// Extract flags
|
||||
@@ -117,7 +117,7 @@ export const commit: QueryHandler = async (args, projectDir, _workstream) => {
|
||||
|
||||
// Check commit_docs config unless --force
|
||||
if (!hasForce) {
|
||||
const paths = planningPaths(projectDir);
|
||||
const paths = planningPaths(projectDir, workstream);
|
||||
try {
|
||||
const raw = await readFile(paths.config, 'utf-8');
|
||||
const config = JSON.parse(raw) as Record<string, unknown>;
|
||||
@@ -180,8 +180,8 @@ export const commit: QueryHandler = async (args, projectDir, _workstream) => {
|
||||
* @param projectDir - Project root directory
|
||||
* @returns QueryResult with { can_commit, reason, commit_docs, staged_files }
|
||||
*/
|
||||
export const checkCommit: QueryHandler = async (_args, projectDir, _workstream) => {
|
||||
const paths = planningPaths(projectDir);
|
||||
export const checkCommit: QueryHandler = async (_args, projectDir, workstream) => {
|
||||
const paths = planningPaths(projectDir, workstream);
|
||||
|
||||
let commitDocs = true;
|
||||
try {
|
||||
@@ -227,7 +227,7 @@ export const checkCommit: QueryHandler = async (_args, projectDir, _workstream)
|
||||
|
||||
// ─── commitToSubrepo ─────────────────────────────────────────────────────
|
||||
|
||||
export const commitToSubrepo: QueryHandler = async (args, projectDir, _workstream) => {
|
||||
export const commitToSubrepo: QueryHandler = async (args, projectDir, workstream) => {
|
||||
const filesIdx = args.indexOf('--files');
|
||||
const endIdx = filesIdx >= 0 ? filesIdx : args.length;
|
||||
const knownFlags = new Set(['--force', '--amend', '--no-verify']);
|
||||
@@ -239,7 +239,7 @@ export const commitToSubrepo: QueryHandler = async (args, projectDir, _workstrea
|
||||
return { data: { committed: false, reason: 'commit message required' } };
|
||||
}
|
||||
|
||||
const paths = planningPaths(projectDir);
|
||||
const paths = planningPaths(projectDir, workstream);
|
||||
let config: Record<string, unknown> = {};
|
||||
try {
|
||||
const raw = await readFile(paths.config, 'utf-8');
|
||||
|
||||
@@ -186,7 +186,7 @@ function setConfigValue(obj: Record<string, unknown>, dotPath: string, value: un
|
||||
* @returns QueryResult matching gsd-tools `config-set` JSON: `{ updated, key, value, previousValue }`
|
||||
* @throws GSDError with Validation if key is invalid or args missing
|
||||
*/
|
||||
export const configSet: QueryHandler = async (args, projectDir, _workstream) => {
|
||||
export const configSet: QueryHandler = async (args, projectDir, workstream) => {
|
||||
const keyPath = args[0];
|
||||
const rawValue = args[1];
|
||||
if (!keyPath) {
|
||||
@@ -214,7 +214,7 @@ export const configSet: QueryHandler = async (args, projectDir, _workstream) =>
|
||||
}
|
||||
|
||||
// D6: Lock protection for read-modify-write (match CJS config.cjs:296)
|
||||
const paths = planningPaths(projectDir);
|
||||
const paths = planningPaths(projectDir, workstream);
|
||||
const lockPath = await acquireStateLock(paths.config);
|
||||
let previousValue: unknown;
|
||||
try {
|
||||
@@ -255,7 +255,7 @@ export const configSet: QueryHandler = async (args, projectDir, _workstream) =>
|
||||
* @returns QueryResult with { set: true, profile, agents }
|
||||
* @throws GSDError with Validation if profile is invalid
|
||||
*/
|
||||
export const configSetModelProfile: QueryHandler = async (args, projectDir, _workstream) => {
|
||||
export const configSetModelProfile: QueryHandler = async (args, projectDir, workstream) => {
|
||||
const profileName = args[0];
|
||||
if (!profileName) {
|
||||
throw new GSDError(
|
||||
@@ -273,7 +273,7 @@ export const configSetModelProfile: QueryHandler = async (args, projectDir, _wor
|
||||
}
|
||||
|
||||
// D6: Lock protection for read-modify-write
|
||||
const paths = planningPaths(projectDir);
|
||||
const paths = planningPaths(projectDir, workstream);
|
||||
const lockPath = await acquireStateLock(paths.config);
|
||||
let previousProfile = 'balanced';
|
||||
try {
|
||||
@@ -317,8 +317,8 @@ export const configSetModelProfile: QueryHandler = async (args, projectDir, _wor
|
||||
* @param projectDir - Project root directory
|
||||
* @returns QueryResult with { created: true, path } or { created: false, reason }
|
||||
*/
|
||||
export const configNewProject: QueryHandler = async (args, projectDir, _workstream) => {
|
||||
const paths = planningPaths(projectDir);
|
||||
export const configNewProject: QueryHandler = async (args, projectDir, workstream) => {
|
||||
const paths = planningPaths(projectDir, workstream);
|
||||
|
||||
// Idempotent: don't overwrite existing config
|
||||
if (existsSync(paths.config)) {
|
||||
@@ -443,13 +443,13 @@ export const configNewProject: QueryHandler = async (args, projectDir, _workstre
|
||||
* @param projectDir - Project root directory
|
||||
* @returns QueryResult with { ensured: true, section }
|
||||
*/
|
||||
export const configEnsureSection: QueryHandler = async (args, projectDir, _workstream) => {
|
||||
export const configEnsureSection: QueryHandler = async (args, projectDir, workstream) => {
|
||||
const sectionName = args[0];
|
||||
if (!sectionName) {
|
||||
throw new GSDError('Usage: config-ensure-section <section>', ErrorClassification.Validation);
|
||||
}
|
||||
|
||||
const paths = planningPaths(projectDir);
|
||||
const paths = planningPaths(projectDir, workstream);
|
||||
let config: Record<string, unknown> = {};
|
||||
try {
|
||||
const raw = await readFile(paths.config, 'utf-8');
|
||||
|
||||
@@ -79,13 +79,13 @@ export function getAgentToModelMapForProfile(normalizedProfile: string): Record<
|
||||
* @returns QueryResult with the config value at the given path
|
||||
* @throws GSDError with Validation classification if key missing or not found
|
||||
*/
|
||||
export const configGet: QueryHandler = async (args, projectDir, _workstream) => {
|
||||
export const configGet: QueryHandler = async (args, projectDir, workstream) => {
|
||||
const keyPath = args[0];
|
||||
if (!keyPath) {
|
||||
throw new GSDError('Usage: config-get <key.path>', ErrorClassification.Validation);
|
||||
}
|
||||
|
||||
const paths = planningPaths(projectDir);
|
||||
const paths = planningPaths(projectDir, workstream);
|
||||
let raw: string;
|
||||
try {
|
||||
raw = await readFile(paths.config, 'utf-8');
|
||||
@@ -129,8 +129,8 @@ export const configGet: QueryHandler = async (args, projectDir, _workstream) =>
|
||||
* @param projectDir - Project root directory
|
||||
* @returns QueryResult with `{ path: string }` absolute or project-relative resolution via planningPaths
|
||||
*/
|
||||
export const configPath: QueryHandler = async (_args, projectDir, _workstream) => {
|
||||
const paths = planningPaths(projectDir);
|
||||
export const configPath: QueryHandler = async (_args, projectDir, workstream) => {
|
||||
const paths = planningPaths(projectDir, workstream);
|
||||
return { data: { path: paths.config } };
|
||||
};
|
||||
|
||||
|
||||
@@ -93,8 +93,8 @@ function deriveStatusFromCheckbox(
|
||||
*
|
||||
* Port of cmdInitNewProject from init.cjs lines 296-399.
|
||||
*/
|
||||
export const initNewProject: QueryHandler = async (_args, projectDir, _workstream) => {
|
||||
const config = await loadConfig(projectDir);
|
||||
export const initNewProject: QueryHandler = async (_args, projectDir, workstream) => {
|
||||
const config = await loadConfig(projectDir, workstream);
|
||||
|
||||
// Detect search API key availability from env vars and ~/.gsd/ files
|
||||
const gsdHome = join(homedir(), '.gsd');
|
||||
@@ -209,10 +209,10 @@ export const initNewProject: QueryHandler = async (_args, projectDir, _workstrea
|
||||
*
|
||||
* Port of cmdInitProgress from init.cjs lines 1139-1284.
|
||||
*/
|
||||
export const initProgress: QueryHandler = async (_args, projectDir, _workstream) => {
|
||||
const config = await loadConfig(projectDir);
|
||||
const milestone = await getMilestoneInfo(projectDir);
|
||||
const paths = planningPaths(projectDir);
|
||||
export const initProgress: QueryHandler = async (_args, projectDir, workstream) => {
|
||||
const config = await loadConfig(projectDir, workstream);
|
||||
const milestone = await getMilestoneInfo(projectDir, workstream);
|
||||
const paths = planningPaths(projectDir, workstream);
|
||||
|
||||
const phases: Record<string, unknown>[] = [];
|
||||
let currentPhase: Record<string, unknown> | null = null;
|
||||
@@ -225,7 +225,7 @@ export const initProgress: QueryHandler = async (_args, projectDir, _workstream)
|
||||
|
||||
try {
|
||||
const rawRoadmap = await readFile(paths.roadmap, 'utf-8');
|
||||
const roadmapContent = await extractCurrentMilestone(rawRoadmap, projectDir);
|
||||
const roadmapContent = await extractCurrentMilestone(rawRoadmap, projectDir, workstream);
|
||||
const headingPattern = /#{2,4}\s*Phase\s+(\d+[A-Z]?(?:\.\d+)*)\s*:\s*([^\n]+)/gi;
|
||||
let hm: RegExpExecArray | null;
|
||||
while ((hm = headingPattern.exec(roadmapContent)) !== null) {
|
||||
@@ -372,10 +372,10 @@ export const initProgress: QueryHandler = async (_args, projectDir, _workstream)
|
||||
*
|
||||
* Port of cmdInitManager from init.cjs lines 854-1137.
|
||||
*/
|
||||
export const initManager: QueryHandler = async (_args, projectDir, _workstream) => {
|
||||
const config = await loadConfig(projectDir);
|
||||
const milestone = await getMilestoneInfo(projectDir);
|
||||
const paths = planningPaths(projectDir);
|
||||
export const initManager: QueryHandler = async (_args, projectDir, workstream) => {
|
||||
const config = await loadConfig(projectDir, workstream);
|
||||
const milestone = await getMilestoneInfo(projectDir, workstream);
|
||||
const paths = planningPaths(projectDir, workstream);
|
||||
|
||||
let rawContent: string;
|
||||
try {
|
||||
@@ -384,7 +384,7 @@ export const initManager: QueryHandler = async (_args, projectDir, _workstream)
|
||||
return { data: { error: 'No ROADMAP.md found. Run /gsd-new-milestone first.' } };
|
||||
}
|
||||
|
||||
const content = await extractCurrentMilestone(rawContent, projectDir);
|
||||
const content = await extractCurrentMilestone(rawContent, projectDir, workstream);
|
||||
|
||||
// Pre-compute directory listing once
|
||||
let phaseDirEntries: string[] = [];
|
||||
|
||||
@@ -344,7 +344,7 @@ export const validateConsistency: QueryHandler = async (_args, projectDir, works
|
||||
* @param projectDir - Project root directory
|
||||
* @returns QueryResult with { status, errors, warnings, info, repairable_count, repairs_performed? }
|
||||
*/
|
||||
export const validateHealth: QueryHandler = async (args, projectDir, _workstream) => {
|
||||
export const validateHealth: QueryHandler = async (args, projectDir, workstream) => {
|
||||
const doRepair = args.includes('--repair');
|
||||
|
||||
// T-12-09: Home directory guard
|
||||
@@ -365,13 +365,13 @@ export const validateHealth: QueryHandler = async (args, projectDir, _workstream
|
||||
};
|
||||
}
|
||||
|
||||
const paths = planningPaths(projectDir);
|
||||
const planBase = join(projectDir, '.planning');
|
||||
const paths = planningPaths(projectDir, workstream);
|
||||
const planBase = paths.planning;
|
||||
const projectPath = join(planBase, 'PROJECT.md');
|
||||
const roadmapPath = join(planBase, 'ROADMAP.md');
|
||||
const statePath = join(planBase, 'STATE.md');
|
||||
const configPath = join(planBase, 'config.json');
|
||||
const phasesDir = join(planBase, 'phases');
|
||||
const roadmapPath = paths.roadmap;
|
||||
const statePath = paths.state;
|
||||
const configPath = paths.config;
|
||||
const phasesDir = paths.phases;
|
||||
|
||||
interface Issue {
|
||||
code: string;
|
||||
|
||||
Reference in New Issue
Block a user