specweave 1.0.461 → 1.0.463

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.d.ts +0 -1
  2. package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.d.ts.map +1 -1
  3. package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js +1 -7
  4. package/dist/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js.map +1 -1
  5. package/dist/plugins/specweave-github/lib/github-ac-checkbox-sync.d.ts +0 -1
  6. package/dist/plugins/specweave-github/lib/github-ac-checkbox-sync.d.ts.map +1 -1
  7. package/dist/plugins/specweave-github/lib/github-ac-checkbox-sync.js +3 -14
  8. package/dist/plugins/specweave-github/lib/github-ac-checkbox-sync.js.map +1 -1
  9. package/dist/plugins/specweave-github/lib/github-client-v2.js +1 -1
  10. package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
  11. package/dist/plugins/specweave-jira/lib/jira-ac-checkbox-sync.d.ts +0 -1
  12. package/dist/plugins/specweave-jira/lib/jira-ac-checkbox-sync.d.ts.map +1 -1
  13. package/dist/plugins/specweave-jira/lib/jira-ac-checkbox-sync.js +2 -9
  14. package/dist/plugins/specweave-jira/lib/jira-ac-checkbox-sync.js.map +1 -1
  15. package/package.json +1 -1
  16. package/plugins/specweave/lib/vendor/utils/clean-env.d.ts +47 -0
  17. package/plugins/specweave/lib/vendor/utils/clean-env.js +63 -0
  18. package/plugins/specweave/lib/vendor/utils/clean-env.js.map +1 -0
  19. package/plugins/specweave/lib/vendor/utils/execFileNoThrow.d.ts +99 -0
  20. package/plugins/specweave/lib/vendor/utils/execFileNoThrow.js +149 -0
  21. package/plugins/specweave/lib/vendor/utils/execFileNoThrow.js.map +1 -0
  22. package/plugins/specweave-ado/lib/ado-ac-checkbox-sync.js +0 -5
  23. package/plugins/specweave-ado/lib/ado-ac-checkbox-sync.ts +1 -7
  24. package/plugins/specweave-github/lib/github-ac-checkbox-sync.js +2 -12
  25. package/plugins/specweave-github/lib/github-ac-checkbox-sync.ts +3 -16
  26. package/plugins/specweave-github/lib/github-client-v2.js +1 -1
  27. package/plugins/specweave-github/lib/github-client-v2.ts +1 -1
  28. package/plugins/specweave-jira/lib/jira-ac-checkbox-sync.js +1 -7
  29. package/plugins/specweave-jira/lib/jira-ac-checkbox-sync.ts +2 -9
  30. package/plugins/specweave/lib/vendor/utils/project-detection.d.ts +0 -250
  31. package/plugins/specweave/lib/vendor/utils/project-detection.js +0 -560
  32. package/plugins/specweave/lib/vendor/utils/project-detection.js.map +0 -1
@@ -0,0 +1,99 @@
1
+ import { ExecFileException } from 'child_process';
2
+ /**
3
+ * Result from command execution
4
+ */
5
+ export interface ExecResult {
6
+ /** Standard output from command */
7
+ stdout: string;
8
+ /** Standard error from command */
9
+ stderr: string;
10
+ /** Exit code (0 = success) */
11
+ exitCode: number;
12
+ /** Whether command succeeded (exitCode === 0) */
13
+ success: boolean;
14
+ /** Error object if command failed */
15
+ error?: ExecFileException;
16
+ }
17
+ /**
18
+ * Safely execute a command without throwing errors
19
+ *
20
+ * This utility uses child_process.execFile instead of exec/execSync:
21
+ * - ✅ Prevents command injection vulnerabilities (no shell interpolation)
22
+ * - ✅ Cross-platform compatible (Windows, Mac, Linux)
23
+ * - ✅ Proper error handling (returns result instead of throwing)
24
+ * - ✅ Structured output (stdout, stderr, exitCode)
25
+ *
26
+ * @param command - Command to execute (must be in PATH or absolute path)
27
+ * @param args - Array of arguments (safely escaped automatically)
28
+ * @param options - Additional execution options
29
+ * @returns Promise resolving to execution result
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * // ✅ Safe: Arguments automatically escaped
34
+ * const result = await execFileNoThrow('git', ['add', userProvidedFilename]);
35
+ * if (result.success) {
36
+ * console.log('Git add succeeded:', result.stdout);
37
+ * } else {
38
+ * console.error('Git add failed:', result.stderr);
39
+ * }
40
+ *
41
+ * // ✅ Check if command exists
42
+ * const which = process.platform === 'win32' ? 'where' : 'which';
43
+ * const checkResult = await execFileNoThrow(which, ['claude']);
44
+ * if (checkResult.success) {
45
+ * console.log('Claude CLI found at:', checkResult.stdout.trim());
46
+ * }
47
+ * ```
48
+ */
49
+ export declare function execFileNoThrow(command: string, args?: string[], options?: {
50
+ cwd?: string;
51
+ env?: NodeJS.ProcessEnv;
52
+ timeout?: number;
53
+ maxBuffer?: number;
54
+ shell?: boolean;
55
+ }): Promise<ExecResult>;
56
+ /**
57
+ * Synchronous version of execFileNoThrow
58
+ *
59
+ * Use sparingly - prefer async version when possible.
60
+ * Useful for initialization code that needs to be synchronous.
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * const result = execFileNoThrowSync('claude', ['--version']);
65
+ * if (result.success) {
66
+ * console.log('Claude version:', result.stdout.trim());
67
+ * }
68
+ * ```
69
+ */
70
+ export declare function execFileNoThrowSync(command: string, args?: string[], options?: {
71
+ cwd?: string;
72
+ env?: NodeJS.ProcessEnv;
73
+ timeout?: number;
74
+ maxBuffer?: number;
75
+ shell?: boolean;
76
+ }): ExecResult;
77
+ /**
78
+ * Check if a command is available in PATH
79
+ *
80
+ * Cross-platform helper that uses 'which' (Unix) or 'where' (Windows)
81
+ *
82
+ * @param command - Command name to check (e.g., 'claude', 'git', 'node')
83
+ * @returns Promise resolving to true if command exists, false otherwise
84
+ *
85
+ * @example
86
+ * ```typescript
87
+ * if (await isCommandAvailable('claude')) {
88
+ * console.log('Claude CLI is installed');
89
+ * } else {
90
+ * console.log('Claude CLI not found - install from https://claude.com/code');
91
+ * }
92
+ * ```
93
+ */
94
+ export declare function isCommandAvailable(command: string): Promise<boolean>;
95
+ /**
96
+ * Synchronous version of isCommandAvailable
97
+ */
98
+ export declare function isCommandAvailableSync(command: string): boolean;
99
+ //# sourceMappingURL=execFileNoThrow.d.ts.map
@@ -0,0 +1,149 @@
1
+ import { execFile, execFileSync } from 'child_process';
2
+ import { promisify } from 'util';
3
+ import { getCleanEnv } from './clean-env.js';
4
+ const execFileAsync = promisify(execFile);
5
+ /**
6
+ * Safely execute a command without throwing errors
7
+ *
8
+ * This utility uses child_process.execFile instead of exec/execSync:
9
+ * - ✅ Prevents command injection vulnerabilities (no shell interpolation)
10
+ * - ✅ Cross-platform compatible (Windows, Mac, Linux)
11
+ * - ✅ Proper error handling (returns result instead of throwing)
12
+ * - ✅ Structured output (stdout, stderr, exitCode)
13
+ *
14
+ * @param command - Command to execute (must be in PATH or absolute path)
15
+ * @param args - Array of arguments (safely escaped automatically)
16
+ * @param options - Additional execution options
17
+ * @returns Promise resolving to execution result
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * // ✅ Safe: Arguments automatically escaped
22
+ * const result = await execFileNoThrow('git', ['add', userProvidedFilename]);
23
+ * if (result.success) {
24
+ * console.log('Git add succeeded:', result.stdout);
25
+ * } else {
26
+ * console.error('Git add failed:', result.stderr);
27
+ * }
28
+ *
29
+ * // ✅ Check if command exists
30
+ * const which = process.platform === 'win32' ? 'where' : 'which';
31
+ * const checkResult = await execFileNoThrow(which, ['claude']);
32
+ * if (checkResult.success) {
33
+ * console.log('Claude CLI found at:', checkResult.stdout.trim());
34
+ * }
35
+ * ```
36
+ */
37
+ export async function execFileNoThrow(command, args = [], options = {}) {
38
+ try {
39
+ // CRITICAL: On Windows, shell is needed for .cmd/.bat files
40
+ // Without shell, execFileAsync can't find 'claude.cmd' even if it's in PATH
41
+ const needsShell = process.platform === 'win32' && options.shell !== false;
42
+ // CRITICAL: Use clean environment to prevent debugger/instrumentation from breaking child processes
43
+ // If caller provides custom env, merge with clean env (caller's values take precedence)
44
+ const cleanEnv = getCleanEnv();
45
+ const mergedEnv = options.env ? { ...cleanEnv, ...options.env } : cleanEnv;
46
+ const { stdout, stderr } = await execFileAsync(command, args, {
47
+ ...options,
48
+ env: mergedEnv,
49
+ encoding: 'utf-8',
50
+ windowsHide: true, // Don't show console window on Windows
51
+ shell: needsShell,
52
+ });
53
+ return {
54
+ stdout: stdout || '',
55
+ stderr: stderr || '',
56
+ exitCode: 0,
57
+ success: true,
58
+ };
59
+ }
60
+ catch (error) {
61
+ const execError = error;
62
+ return {
63
+ stdout: execError.stdout || '',
64
+ stderr: execError.stderr || '',
65
+ exitCode: typeof execError.code === 'number' ? execError.code : 1,
66
+ success: false,
67
+ error: execError,
68
+ };
69
+ }
70
+ }
71
+ /**
72
+ * Synchronous version of execFileNoThrow
73
+ *
74
+ * Use sparingly - prefer async version when possible.
75
+ * Useful for initialization code that needs to be synchronous.
76
+ *
77
+ * @example
78
+ * ```typescript
79
+ * const result = execFileNoThrowSync('claude', ['--version']);
80
+ * if (result.success) {
81
+ * console.log('Claude version:', result.stdout.trim());
82
+ * }
83
+ * ```
84
+ */
85
+ export function execFileNoThrowSync(command, args = [], options = {}) {
86
+ try {
87
+ // CRITICAL: On Windows, shell is needed for .cmd/.bat files
88
+ // Without shell, execFileSync can't find 'claude.cmd' even if it's in PATH
89
+ const needsShell = process.platform === 'win32' && options.shell !== false;
90
+ // CRITICAL: Use clean environment to prevent debugger/instrumentation from breaking child processes
91
+ // If caller provides custom env, merge with clean env (caller's values take precedence)
92
+ const cleanEnv = getCleanEnv();
93
+ const mergedEnv = options.env ? { ...cleanEnv, ...options.env } : cleanEnv;
94
+ const stdout = execFileSync(command, args, {
95
+ ...options,
96
+ env: mergedEnv,
97
+ encoding: 'utf-8',
98
+ windowsHide: true,
99
+ shell: needsShell,
100
+ stdio: ['pipe', 'pipe', 'pipe'], // Capture all streams, don't leak to terminal
101
+ });
102
+ return {
103
+ stdout: stdout || '',
104
+ stderr: '',
105
+ exitCode: 0,
106
+ success: true,
107
+ };
108
+ }
109
+ catch (error) {
110
+ return {
111
+ stdout: error.stdout || '',
112
+ stderr: error.stderr || '',
113
+ exitCode: typeof error.status === 'number' ? error.status : 1,
114
+ success: false,
115
+ error: error,
116
+ };
117
+ }
118
+ }
119
+ /**
120
+ * Check if a command is available in PATH
121
+ *
122
+ * Cross-platform helper that uses 'which' (Unix) or 'where' (Windows)
123
+ *
124
+ * @param command - Command name to check (e.g., 'claude', 'git', 'node')
125
+ * @returns Promise resolving to true if command exists, false otherwise
126
+ *
127
+ * @example
128
+ * ```typescript
129
+ * if (await isCommandAvailable('claude')) {
130
+ * console.log('Claude CLI is installed');
131
+ * } else {
132
+ * console.log('Claude CLI not found - install from https://claude.com/code');
133
+ * }
134
+ * ```
135
+ */
136
+ export async function isCommandAvailable(command) {
137
+ const whichCommand = process.platform === 'win32' ? 'where' : 'which';
138
+ const result = await execFileNoThrow(whichCommand, [command]);
139
+ return result.success;
140
+ }
141
+ /**
142
+ * Synchronous version of isCommandAvailable
143
+ */
144
+ export function isCommandAvailableSync(command) {
145
+ const whichCommand = process.platform === 'win32' ? 'where' : 'which';
146
+ const result = execFileNoThrowSync(whichCommand, [command]);
147
+ return result.success;
148
+ }
149
+ //# sourceMappingURL=execFileNoThrow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"execFileNoThrow.js","sourceRoot":"","sources":["../../../src/utils/execFileNoThrow.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,6 @@ import path from "path";
3
3
  import yaml from "yaml";
4
4
  import axios from "axios";
5
5
  import { consoleLogger } from "../../specweave/lib/vendor/utils/logger.js";
6
- import { autoDetectProjectIdSync } from "../../specweave/lib/vendor/utils/project-detection.js";
7
6
  import { deriveFeatureId } from "../../specweave/lib/vendor/utils/feature-id-derivation.js";
8
7
  import { GitHubACCheckboxSync } from "../../specweave-github/lib/github-ac-checkbox-sync.js";
9
8
  class AdoACCheckboxSync {
@@ -11,7 +10,6 @@ class AdoACCheckboxSync {
11
10
  this.projectRoot = options.projectRoot;
12
11
  this.incrementId = options.incrementId;
13
12
  this.logger = options.logger ?? consoleLogger;
14
- this.projectId = autoDetectProjectIdSync(this.projectRoot) || "default";
15
13
  }
16
14
  async syncACCheckboxesToAdo(config) {
17
15
  const result = { success: true, updated: 0, issues: [] };
@@ -202,12 +200,9 @@ ${acLines}
202
200
  const specsRoot = path.join(this.projectRoot, ".specweave/docs/internal/specs");
203
201
  const usFiles = [];
204
202
  const projectDirs = [];
205
- const primary = path.join(specsRoot, this.projectId, featureId);
206
- if (existsSync(primary)) projectDirs.push(primary);
207
203
  if (existsSync(specsRoot)) {
208
204
  try {
209
205
  for (const proj of await fs.readdir(specsRoot)) {
210
- if (proj === this.projectId) continue;
211
206
  const p = path.join(specsRoot, proj, featureId);
212
207
  if (existsSync(p)) projectDirs.push(p);
213
208
  }
@@ -13,7 +13,6 @@ import path from 'path';
13
13
  import yaml from 'yaml';
14
14
  import axios, { AxiosInstance } from 'axios';
15
15
  import { Logger, consoleLogger } from '../../specweave/lib/vendor/utils/logger.js';
16
- import { autoDetectProjectIdSync } from '../../specweave/lib/vendor/utils/project-detection.js';
17
16
  import { deriveFeatureId } from '../../specweave/lib/vendor/utils/feature-id-derivation.js';
18
17
  import { GitHubACCheckboxSync } from '../../specweave-github/lib/github-ac-checkbox-sync.js';
19
18
  import type { SpecWeaveConfig } from '../../../src/core/config/types.js';
@@ -28,7 +27,6 @@ export interface AdoACCheckboxSyncResult {
28
27
  export class AdoACCheckboxSync {
29
28
  private projectRoot: string;
30
29
  private incrementId: string;
31
- private projectId: string;
32
30
  private logger: Logger;
33
31
 
34
32
  constructor(options: {
@@ -39,7 +37,6 @@ export class AdoACCheckboxSync {
39
37
  this.projectRoot = options.projectRoot;
40
38
  this.incrementId = options.incrementId;
41
39
  this.logger = options.logger ?? consoleLogger;
42
- this.projectId = autoDetectProjectIdSync(this.projectRoot) || 'default';
43
40
  }
44
41
 
45
42
  async syncACCheckboxesToAdo(config: SpecWeaveConfig): Promise<AdoACCheckboxSyncResult> {
@@ -271,13 +268,10 @@ ${acLines}
271
268
  const usFiles: LivingDocsUSFile[] = [];
272
269
  const projectDirs: string[] = [];
273
270
 
274
- const primary = path.join(specsRoot, this.projectId, featureId);
275
- if (existsSync(primary)) projectDirs.push(primary);
276
-
271
+ // Search all project directories for the feature
277
272
  if (existsSync(specsRoot)) {
278
273
  try {
279
274
  for (const proj of await fs.readdir(specsRoot)) {
280
- if (proj === this.projectId) continue;
281
275
  const p = path.join(specsRoot, proj, featureId);
282
276
  if (existsSync(p)) projectDirs.push(p);
283
277
  }
@@ -3,7 +3,6 @@ import path from "path";
3
3
  import yaml from "yaml";
4
4
  import { GitHubClientV2 } from "./github-client-v2.js";
5
5
  import { consoleLogger } from "../../specweave/lib/vendor/utils/logger.js";
6
- import { autoDetectProjectIdSync } from "../../specweave/lib/vendor/utils/project-detection.js";
7
6
  import { deriveFeatureId } from "../../specweave/lib/vendor/utils/feature-id-derivation.js";
8
7
  import {
9
8
  ProviderRouter
@@ -17,7 +16,6 @@ class GitHubACCheckboxSync {
17
16
  this.projectRoot = options.projectRoot;
18
17
  this.incrementId = options.incrementId;
19
18
  this.logger = options.logger ?? consoleLogger;
20
- this.projectId = autoDetectProjectIdSync(this.projectRoot) || "default";
21
19
  this.providerRouter = new ProviderRouter({ projectRoot: this.projectRoot, logger: this.logger });
22
20
  }
23
21
  /**
@@ -243,19 +241,11 @@ ${[...usAcStatus.entries()].map(
243
241
  const specsRoot = path.join(this.projectRoot, ".specweave/docs/internal/specs");
244
242
  const usFiles = [];
245
243
  const projectDirs = [];
246
- const primaryPath = path.join(specsRoot, this.projectId, featureId);
247
- if (existsSync(primaryPath)) {
248
- projectDirs.push(primaryPath);
249
- }
250
244
  if (existsSync(specsRoot)) {
251
245
  try {
252
- const allProjects = await fs.readdir(specsRoot);
253
- for (const proj of allProjects) {
254
- if (proj === this.projectId) continue;
246
+ for (const proj of await fs.readdir(specsRoot)) {
255
247
  const projFeaturePath = path.join(specsRoot, proj, featureId);
256
- if (existsSync(projFeaturePath)) {
257
- projectDirs.push(projFeaturePath);
258
- }
248
+ if (existsSync(projFeaturePath)) projectDirs.push(projFeaturePath);
259
249
  }
260
250
  } catch {
261
251
  }
@@ -13,7 +13,6 @@ import path from 'path';
13
13
  import yaml from 'yaml';
14
14
  import { GitHubClientV2 } from './github-client-v2.js';
15
15
  import { Logger, consoleLogger } from '../../specweave/lib/vendor/utils/logger.js';
16
- import { autoDetectProjectIdSync } from '../../specweave/lib/vendor/utils/project-detection.js';
17
16
  import { deriveFeatureId } from '../../specweave/lib/vendor/utils/feature-id-derivation.js';
18
17
  import {
19
18
  ProviderRouter,
@@ -35,7 +34,6 @@ export interface ACCheckboxSyncResult {
35
34
  export class GitHubACCheckboxSync {
36
35
  private projectRoot: string;
37
36
  private incrementId: string;
38
- private projectId: string;
39
37
  private logger: Logger;
40
38
  private providerRouter: ProviderRouter;
41
39
 
@@ -47,7 +45,6 @@ export class GitHubACCheckboxSync {
47
45
  this.projectRoot = options.projectRoot;
48
46
  this.incrementId = options.incrementId;
49
47
  this.logger = options.logger ?? consoleLogger;
50
- this.projectId = autoDetectProjectIdSync(this.projectRoot) || 'default';
51
48
  this.providerRouter = new ProviderRouter({ projectRoot: this.projectRoot, logger: this.logger });
52
49
  }
53
50
 
@@ -342,23 +339,13 @@ ${[...usAcStatus.entries()].map(([id, done]) =>
342
339
  const specsRoot = path.join(this.projectRoot, '.specweave/docs/internal/specs');
343
340
  const usFiles: LivingDocsUSFile[] = [];
344
341
 
345
- // Collect all project folders that have this feature
342
+ // Scan all project folders for this feature
346
343
  const projectDirs: string[] = [];
347
- const primaryPath = path.join(specsRoot, this.projectId, featureId);
348
- if (existsSync(primaryPath)) {
349
- projectDirs.push(primaryPath);
350
- }
351
-
352
- // For cross-project increments, also scan other project folders
353
344
  if (existsSync(specsRoot)) {
354
345
  try {
355
- const allProjects = await fs.readdir(specsRoot);
356
- for (const proj of allProjects) {
357
- if (proj === this.projectId) continue;
346
+ for (const proj of await fs.readdir(specsRoot)) {
358
347
  const projFeaturePath = path.join(specsRoot, proj, featureId);
359
- if (existsSync(projFeaturePath)) {
360
- projectDirs.push(projFeaturePath);
361
- }
348
+ if (existsSync(projFeaturePath)) projectDirs.push(projFeaturePath);
362
349
  }
363
350
  } catch {
364
351
  // Ignore readdir errors
@@ -1,4 +1,4 @@
1
- import { execFileNoThrow } from "../../../src/utils/execFileNoThrow.js";
1
+ import { execFileNoThrow } from "../../specweave/lib/vendor/utils/execFileNoThrow.js";
2
2
  class GitHubClientV2 {
3
3
  /**
4
4
  * Create GitHub client from sync profile
@@ -8,7 +8,7 @@
8
8
  * - Secure command execution (no shell injection)
9
9
  */
10
10
 
11
- import { execFileNoThrow } from '../../../src/utils/execFileNoThrow.js';
11
+ import { execFileNoThrow } from '../../specweave/lib/vendor/utils/execFileNoThrow.js';
12
12
  import { GitHubIssue, GitHubMilestone, GitHubExternalChange } from './types';
13
13
  import { SyncProfile, GitHubConfig, TimeRangePreset } from '../../../src/core/types/sync-profile';
14
14
 
@@ -3,7 +3,6 @@ import path from "path";
3
3
  import yaml from "yaml";
4
4
  import axios from "axios";
5
5
  import { consoleLogger } from "../../specweave/lib/vendor/utils/logger.js";
6
- import { autoDetectProjectIdSync } from "../../specweave/lib/vendor/utils/project-detection.js";
7
6
  import { deriveFeatureId } from "../../specweave/lib/vendor/utils/feature-id-derivation.js";
8
7
  import { getApiBaseUrl } from "./jira-deployment-detector.js";
9
8
  import { GitHubACCheckboxSync } from "../../specweave-github/lib/github-ac-checkbox-sync.js";
@@ -12,7 +11,6 @@ class JiraACCheckboxSync {
12
11
  this.projectRoot = options.projectRoot;
13
12
  this.incrementId = options.incrementId;
14
13
  this.logger = options.logger ?? consoleLogger;
15
- this.projectId = autoDetectProjectIdSync(this.projectRoot) || "default";
16
14
  }
17
15
  async syncACCheckboxesToJira(config) {
18
16
  const result = { success: true, updated: 0, issues: [] };
@@ -224,13 +222,9 @@ class JiraACCheckboxSync {
224
222
  const specsRoot = path.join(this.projectRoot, ".specweave/docs/internal/specs");
225
223
  const usFiles = [];
226
224
  const projectDirs = [];
227
- const primaryPath = path.join(specsRoot, this.projectId, featureId);
228
- if (existsSync(primaryPath)) projectDirs.push(primaryPath);
229
225
  if (existsSync(specsRoot)) {
230
226
  try {
231
- const allProjects = await fs.readdir(specsRoot);
232
- for (const proj of allProjects) {
233
- if (proj === this.projectId) continue;
227
+ for (const proj of await fs.readdir(specsRoot)) {
234
228
  const p = path.join(specsRoot, proj, featureId);
235
229
  if (existsSync(p)) projectDirs.push(p);
236
230
  }
@@ -16,7 +16,6 @@ import path from 'path';
16
16
  import yaml from 'yaml';
17
17
  import axios, { AxiosInstance } from 'axios';
18
18
  import { Logger, consoleLogger } from '../../specweave/lib/vendor/utils/logger.js';
19
- import { autoDetectProjectIdSync } from '../../specweave/lib/vendor/utils/project-detection.js';
20
19
  import { deriveFeatureId } from '../../specweave/lib/vendor/utils/feature-id-derivation.js';
21
20
  import { detectDeploymentType, getApiBaseUrl } from './jira-deployment-detector.js';
22
21
  import type { SpecWeaveConfig } from '../../../src/core/config/types.js';
@@ -32,7 +31,6 @@ export interface JiraACCheckboxSyncResult {
32
31
  export class JiraACCheckboxSync {
33
32
  private projectRoot: string;
34
33
  private incrementId: string;
35
- private projectId: string;
36
34
  private logger: Logger;
37
35
 
38
36
  constructor(options: {
@@ -43,7 +41,6 @@ export class JiraACCheckboxSync {
43
41
  this.projectRoot = options.projectRoot;
44
42
  this.incrementId = options.incrementId;
45
43
  this.logger = options.logger ?? consoleLogger;
46
- this.projectId = autoDetectProjectIdSync(this.projectRoot) || 'default';
47
44
  }
48
45
 
49
46
  async syncACCheckboxesToJira(config: SpecWeaveConfig): Promise<JiraACCheckboxSyncResult> {
@@ -299,15 +296,11 @@ export class JiraACCheckboxSync {
299
296
  const specsRoot = path.join(this.projectRoot, '.specweave/docs/internal/specs');
300
297
  const usFiles: LivingDocsUSFile[] = [];
301
298
 
299
+ // Search all project directories for the feature
302
300
  const projectDirs: string[] = [];
303
- const primaryPath = path.join(specsRoot, this.projectId, featureId);
304
- if (existsSync(primaryPath)) projectDirs.push(primaryPath);
305
-
306
301
  if (existsSync(specsRoot)) {
307
302
  try {
308
- const allProjects = await fs.readdir(specsRoot);
309
- for (const proj of allProjects) {
310
- if (proj === this.projectId) continue;
303
+ for (const proj of await fs.readdir(specsRoot)) {
311
304
  const p = path.join(specsRoot, proj, featureId);
312
305
  if (existsSync(p)) projectDirs.push(p);
313
306
  }