specweave 0.33.2 → 0.33.3

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 (56) hide show
  1. package/CLAUDE.md +56 -0
  2. package/dist/plugins/specweave-ado/lib/per-us-sync.d.ts +120 -0
  3. package/dist/plugins/specweave-ado/lib/per-us-sync.d.ts.map +1 -0
  4. package/dist/plugins/specweave-ado/lib/per-us-sync.js +276 -0
  5. package/dist/plugins/specweave-ado/lib/per-us-sync.js.map +1 -0
  6. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +4 -1
  7. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
  8. package/dist/plugins/specweave-github/lib/github-client-v2.js +13 -3
  9. package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
  10. package/dist/plugins/specweave-github/lib/per-us-sync.d.ts +97 -0
  11. package/dist/plugins/specweave-github/lib/per-us-sync.d.ts.map +1 -0
  12. package/dist/plugins/specweave-github/lib/per-us-sync.js +274 -0
  13. package/dist/plugins/specweave-github/lib/per-us-sync.js.map +1 -0
  14. package/dist/plugins/specweave-jira/lib/per-us-sync.d.ts +113 -0
  15. package/dist/plugins/specweave-jira/lib/per-us-sync.d.ts.map +1 -0
  16. package/dist/plugins/specweave-jira/lib/per-us-sync.js +254 -0
  17. package/dist/plugins/specweave-jira/lib/per-us-sync.js.map +1 -0
  18. package/dist/src/core/config/config-manager.d.ts.map +1 -1
  19. package/dist/src/core/config/config-manager.js +58 -0
  20. package/dist/src/core/config/config-manager.js.map +1 -1
  21. package/dist/src/core/config/types.d.ts +80 -0
  22. package/dist/src/core/config/types.d.ts.map +1 -1
  23. package/dist/src/core/config/types.js.map +1 -1
  24. package/dist/src/core/living-docs/cross-project-sync.d.ts +87 -15
  25. package/dist/src/core/living-docs/cross-project-sync.d.ts.map +1 -1
  26. package/dist/src/core/living-docs/cross-project-sync.js +147 -28
  27. package/dist/src/core/living-docs/cross-project-sync.js.map +1 -1
  28. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  29. package/dist/src/core/living-docs/living-docs-sync.js +26 -22
  30. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  31. package/dist/src/core/living-docs/types.d.ts +24 -3
  32. package/dist/src/core/living-docs/types.d.ts.map +1 -1
  33. package/dist/src/core/types/config.d.ts +79 -0
  34. package/dist/src/core/types/config.d.ts.map +1 -1
  35. package/dist/src/core/types/config.js.map +1 -1
  36. package/dist/src/sync/sync-coordinator.d.ts +20 -0
  37. package/dist/src/sync/sync-coordinator.d.ts.map +1 -1
  38. package/dist/src/sync/sync-coordinator.js +258 -33
  39. package/dist/src/sync/sync-coordinator.js.map +1 -1
  40. package/dist/src/utils/project-resolver.d.ts +156 -0
  41. package/dist/src/utils/project-resolver.d.ts.map +1 -0
  42. package/dist/src/utils/project-resolver.js +587 -0
  43. package/dist/src/utils/project-resolver.js.map +1 -0
  44. package/package.json +1 -1
  45. package/plugins/specweave/hooks/hooks.json +10 -0
  46. package/plugins/specweave/hooks/user-prompt-submit.sh +105 -3
  47. package/plugins/specweave/hooks/v2/guards/per-us-project-validator.sh +281 -0
  48. package/plugins/specweave/hooks/v2/handlers/living-specs-handler.sh +29 -0
  49. package/plugins/specweave-ado/lib/per-us-sync.js +247 -0
  50. package/plugins/specweave-ado/lib/per-us-sync.ts +410 -0
  51. package/plugins/specweave-github/lib/github-client-v2.js +10 -3
  52. package/plugins/specweave-github/lib/github-client-v2.ts +15 -3
  53. package/plugins/specweave-github/lib/per-us-sync.js +241 -0
  54. package/plugins/specweave-github/lib/per-us-sync.ts +375 -0
  55. package/plugins/specweave-jira/lib/per-us-sync.js +224 -0
  56. package/plugins/specweave-jira/lib/per-us-sync.ts +366 -0
@@ -5,8 +5,11 @@
5
5
  * Groups user stories by their target project and syncs each group
6
6
  * to the appropriate project folder in living docs.
7
7
  *
8
+ * Supports both 1-level (project) and 2-level (project/board) structures.
9
+ *
8
10
  * @module core/living-docs/cross-project-sync
9
11
  * @since v0.33.0
12
+ * @updated v0.34.0 - Added 2-level structure support (project/board)
10
13
  */
11
14
  import type { UserStoryData } from './types.js';
12
15
  import { Logger } from '../../utils/logger.js';
@@ -45,50 +48,119 @@ export interface CrossProjectSyncOptions {
45
48
  export declare class CrossProjectSync {
46
49
  private projectRoot;
47
50
  private logger;
51
+ private structureConfig;
48
52
  constructor(projectRoot: string, options?: {
49
53
  logger?: Logger;
50
54
  });
51
55
  /**
52
- * Group user stories by their target project
56
+ * Get structure level configuration (cached)
57
+ */
58
+ private getStructureConfig;
59
+ /**
60
+ * Check if 2-level structure is detected
61
+ */
62
+ is2LevelStructure(): boolean;
63
+ /**
64
+ * Get full target path for a user story
65
+ *
66
+ * For 1-level: returns project
67
+ * For 2-level: returns project/board
68
+ *
69
+ * @param us - User story
70
+ * @param defaultProject - Default project if US doesn't specify
71
+ * @param defaultBoard - Default board if US doesn't specify (2-level only)
72
+ * @returns Full path like "project" or "project/board"
73
+ */
74
+ getUSTargetPath(us: UserStoryData, defaultProject: string, defaultBoard?: string): string;
75
+ /**
76
+ * Group user stories by their target path (project or project/board)
53
77
  *
54
78
  * @param userStories - User stories to group
55
79
  * @param defaultProject - Default project for USs without explicit project
56
- * @returns Map of project ID to user stories
80
+ * @param defaultBoard - Default board for USs without explicit board (2-level only)
81
+ * @returns Map of target path to user stories
82
+ */
83
+ groupByProject(userStories: UserStoryData[], defaultProject: string, defaultBoard?: string): Map<string, UserStoryData[]>;
84
+ /**
85
+ * Group user stories by project only (ignoring board)
86
+ * Useful for project-level operations like external sync
57
87
  */
58
- groupByProject(userStories: UserStoryData[], defaultProject: string): Map<string, UserStoryData[]>;
88
+ groupByProjectOnly(userStories: UserStoryData[], defaultProject: string): Map<string, UserStoryData[]>;
59
89
  /**
60
- * Check if an increment is cross-project (spans multiple projects)
90
+ * Check if an increment is cross-project (spans multiple projects/boards)
91
+ *
92
+ * For 1-level: checks if USs target different projects
93
+ * For 2-level: checks if USs target different project/board combinations
61
94
  */
62
- isCrossProject(userStories: UserStoryData[], defaultProject: string): boolean;
95
+ isCrossProject(userStories: UserStoryData[], defaultProject: string, defaultBoard?: string): boolean;
63
96
  /**
64
- * Get the specs folder path for a project
97
+ * Check if an increment spans multiple projects (ignoring boards)
98
+ * Useful for external sync which groups at project level
99
+ */
100
+ spansMultipleProjects(userStories: UserStoryData[], defaultProject: string): boolean;
101
+ /**
102
+ * Get the specs folder path for a target path
103
+ * Handles both 1-level (project) and 2-level (project/board)
104
+ *
105
+ * @param targetPath - Either "project" or "project/board"
106
+ */
107
+ getSpecsPath(targetPath: string): string;
108
+ /**
109
+ * Get the specs folder path for a project (legacy, 1-level only)
110
+ * @deprecated Use getSpecsPath() instead for 2-level support
65
111
  */
66
112
  getProjectSpecsPath(projectId: string): string;
67
113
  /**
68
- * Get the feature folder path for a project and feature
114
+ * Get the feature folder path for a target path and feature
115
+ * Handles both 1-level and 2-level structures
116
+ *
117
+ * @param targetPath - Either "project" or "project/board"
118
+ * @param featureId - Feature ID (e.g., "FS-125")
119
+ */
120
+ getFeaturePath(targetPath: string, featureId: string): string;
121
+ /**
122
+ * Ensure specs folder exists for a target path
123
+ * Handles both 1-level and 2-level structures
69
124
  */
70
- getFeaturePath(projectId: string, featureId: string): string;
125
+ ensureSpecsFolder(targetPath: string): Promise<string>;
71
126
  /**
72
- * Ensure project specs folder exists
127
+ * Ensure project specs folder exists (legacy, 1-level only)
128
+ * @deprecated Use ensureSpecsFolder() instead
73
129
  */
74
130
  ensureProjectFolder(projectId: string): Promise<string>;
75
131
  /**
76
- * Ensure feature folder exists within a project
132
+ * Ensure feature folder exists within a target path
133
+ * Handles both 1-level and 2-level structures
77
134
  */
78
- ensureFeatureFolder(projectId: string, featureId: string): Promise<string>;
135
+ ensureFeatureFolder(targetPath: string, featureId: string): Promise<string>;
79
136
  /**
80
137
  * Generate cross-references section for FEATURE.md
81
138
  *
82
139
  * @param featureId - The feature ID (e.g., "FS-125")
83
- * @param allProjects - All projects this feature spans
84
- * @param currentProject - The current project being generated
140
+ * @param allTargetPaths - All target paths this feature spans (can be "project" or "project/board")
141
+ * @param currentTargetPath - The current target path being generated
85
142
  * @returns Markdown content for "Related Projects" section
86
143
  */
87
- generateCrossReferences(featureId: string, allProjects: string[], currentProject: string): string;
144
+ generateCrossReferences(featureId: string, allTargetPaths: string[], currentTargetPath: string): string;
88
145
  /**
89
146
  * Generate cross-reference frontmatter for US files
147
+ *
148
+ * @param allTargetPaths - All target paths (can be "project" or "project/board")
149
+ * @param currentTargetPath - Current target path
150
+ */
151
+ generateRelatedProjectsFrontmatter(allTargetPaths: string[], currentTargetPath: string): string;
152
+ /**
153
+ * Extract project ID from target path
154
+ * "project" -> "project"
155
+ * "project/board" -> "project"
156
+ */
157
+ extractProjectId(targetPath: string): string;
158
+ /**
159
+ * Extract board ID from target path (for 2-level structures)
160
+ * "project" -> undefined
161
+ * "project/board" -> "board"
90
162
  */
91
- generateRelatedProjectsFrontmatter(allProjects: string[], currentProject: string): string;
163
+ extractBoardId(targetPath: string): string | undefined;
92
164
  /**
93
165
  * Log cross-project sync summary
94
166
  */
@@ -1 +1 @@
1
- {"version":3,"file":"cross-project-sync.d.ts","sourceRoot":"","sources":["../../../../src/core/living-docs/cross-project-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAiB,MAAM,uBAAuB,CAAC;AAG9D;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED;;GAEG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,MAAM,CAAS;gBAEX,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO;IAKlE;;;;;;OAMG;IACH,cAAc,CACZ,WAAW,EAAE,aAAa,EAAE,EAC5B,cAAc,EAAE,MAAM,GACrB,GAAG,CAAC,MAAM,EAAE,aAAa,EAAE,CAAC;IAe/B;;OAEG;IACH,cAAc,CACZ,WAAW,EAAE,aAAa,EAAE,EAC5B,cAAc,EAAE,MAAM,GACrB,OAAO;IAKV;;OAEG;IACH,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAQ9C;;OAEG;IACH,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IAO5D;;OAEG;IACG,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAM7D;;OAEG;IACG,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAMhF;;;;;;;OAOG;IACH,uBAAuB,CACrB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EAAE,EACrB,cAAc,EAAE,MAAM,GACrB,MAAM;IAyBT;;OAEG;IACH,kCAAkC,CAChC,WAAW,EAAE,MAAM,EAAE,EACrB,cAAc,EAAE,MAAM,GACrB,MAAM;IAUT;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,sBAAsB,GAAG,IAAI;CAqBrD"}
1
+ {"version":3,"file":"cross-project-sync.d.ts","sourceRoot":"","sources":["../../../../src/core/living-docs/cross-project-sync.ts"],"names":[],"mappings":"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"}
@@ -5,28 +5,83 @@
5
5
  * Groups user stories by their target project and syncs each group
6
6
  * to the appropriate project folder in living docs.
7
7
  *
8
+ * Supports both 1-level (project) and 2-level (project/board) structures.
9
+ *
8
10
  * @module core/living-docs/cross-project-sync
9
11
  * @since v0.33.0
12
+ * @updated v0.34.0 - Added 2-level structure support (project/board)
10
13
  */
11
14
  import * as path from 'node:path';
12
15
  import { consoleLogger } from '../../utils/logger.js';
13
16
  import { ensureDir } from '../../utils/fs-native.js';
17
+ import { detectStructureLevel } from '../../utils/structure-level-detector.js';
14
18
  /**
15
19
  * Cross-Project Sync Orchestrator
16
20
  */
17
21
  export class CrossProjectSync {
18
22
  constructor(projectRoot, options = {}) {
23
+ this.structureConfig = null;
19
24
  this.projectRoot = projectRoot;
20
25
  this.logger = options.logger ?? consoleLogger;
21
26
  }
22
27
  /**
23
- * Group user stories by their target project
28
+ * Get structure level configuration (cached)
29
+ */
30
+ getStructureConfig() {
31
+ if (!this.structureConfig) {
32
+ this.structureConfig = detectStructureLevel(this.projectRoot);
33
+ }
34
+ return this.structureConfig;
35
+ }
36
+ /**
37
+ * Check if 2-level structure is detected
38
+ */
39
+ is2LevelStructure() {
40
+ return this.getStructureConfig().level === 2;
41
+ }
42
+ /**
43
+ * Get full target path for a user story
44
+ *
45
+ * For 1-level: returns project
46
+ * For 2-level: returns project/board
47
+ *
48
+ * @param us - User story
49
+ * @param defaultProject - Default project if US doesn't specify
50
+ * @param defaultBoard - Default board if US doesn't specify (2-level only)
51
+ * @returns Full path like "project" or "project/board"
52
+ */
53
+ getUSTargetPath(us, defaultProject, defaultBoard) {
54
+ const project = us.project || defaultProject;
55
+ if (this.is2LevelStructure()) {
56
+ const board = us.board || defaultBoard || 'default';
57
+ return `${project}/${board}`;
58
+ }
59
+ return project;
60
+ }
61
+ /**
62
+ * Group user stories by their target path (project or project/board)
24
63
  *
25
64
  * @param userStories - User stories to group
26
65
  * @param defaultProject - Default project for USs without explicit project
27
- * @returns Map of project ID to user stories
66
+ * @param defaultBoard - Default board for USs without explicit board (2-level only)
67
+ * @returns Map of target path to user stories
68
+ */
69
+ groupByProject(userStories, defaultProject, defaultBoard) {
70
+ const groups = new Map();
71
+ for (const us of userStories) {
72
+ const targetPath = this.getUSTargetPath(us, defaultProject, defaultBoard);
73
+ if (!groups.has(targetPath)) {
74
+ groups.set(targetPath, []);
75
+ }
76
+ groups.get(targetPath).push(us);
77
+ }
78
+ return groups;
79
+ }
80
+ /**
81
+ * Group user stories by project only (ignoring board)
82
+ * Useful for project-level operations like external sync
28
83
  */
29
- groupByProject(userStories, defaultProject) {
84
+ groupByProjectOnly(userStories, defaultProject) {
30
85
  const groups = new Map();
31
86
  for (const us of userStories) {
32
87
  const project = us.project || defaultProject;
@@ -38,26 +93,61 @@ export class CrossProjectSync {
38
93
  return groups;
39
94
  }
40
95
  /**
41
- * Check if an increment is cross-project (spans multiple projects)
96
+ * Check if an increment is cross-project (spans multiple projects/boards)
97
+ *
98
+ * For 1-level: checks if USs target different projects
99
+ * For 2-level: checks if USs target different project/board combinations
100
+ */
101
+ isCrossProject(userStories, defaultProject, defaultBoard) {
102
+ const groups = this.groupByProject(userStories, defaultProject, defaultBoard);
103
+ return groups.size > 1;
104
+ }
105
+ /**
106
+ * Check if an increment spans multiple projects (ignoring boards)
107
+ * Useful for external sync which groups at project level
42
108
  */
43
- isCrossProject(userStories, defaultProject) {
44
- const groups = this.groupByProject(userStories, defaultProject);
109
+ spansMultipleProjects(userStories, defaultProject) {
110
+ const groups = this.groupByProjectOnly(userStories, defaultProject);
45
111
  return groups.size > 1;
46
112
  }
47
113
  /**
48
- * Get the specs folder path for a project
114
+ * Get the specs folder path for a target path
115
+ * Handles both 1-level (project) and 2-level (project/board)
116
+ *
117
+ * @param targetPath - Either "project" or "project/board"
118
+ */
119
+ getSpecsPath(targetPath) {
120
+ return path.join(this.projectRoot, '.specweave/docs/internal/specs', targetPath);
121
+ }
122
+ /**
123
+ * Get the specs folder path for a project (legacy, 1-level only)
124
+ * @deprecated Use getSpecsPath() instead for 2-level support
49
125
  */
50
126
  getProjectSpecsPath(projectId) {
51
127
  return path.join(this.projectRoot, '.specweave/docs/internal/specs', projectId);
52
128
  }
53
129
  /**
54
- * Get the feature folder path for a project and feature
130
+ * Get the feature folder path for a target path and feature
131
+ * Handles both 1-level and 2-level structures
132
+ *
133
+ * @param targetPath - Either "project" or "project/board"
134
+ * @param featureId - Feature ID (e.g., "FS-125")
135
+ */
136
+ getFeaturePath(targetPath, featureId) {
137
+ return path.join(this.getSpecsPath(targetPath), featureId);
138
+ }
139
+ /**
140
+ * Ensure specs folder exists for a target path
141
+ * Handles both 1-level and 2-level structures
55
142
  */
56
- getFeaturePath(projectId, featureId) {
57
- return path.join(this.getProjectSpecsPath(projectId), featureId);
143
+ async ensureSpecsFolder(targetPath) {
144
+ const folderPath = this.getSpecsPath(targetPath);
145
+ await ensureDir(folderPath);
146
+ return folderPath;
58
147
  }
59
148
  /**
60
- * Ensure project specs folder exists
149
+ * Ensure project specs folder exists (legacy, 1-level only)
150
+ * @deprecated Use ensureSpecsFolder() instead
61
151
  */
62
152
  async ensureProjectFolder(projectId) {
63
153
  const folderPath = this.getProjectSpecsPath(projectId);
@@ -65,10 +155,11 @@ export class CrossProjectSync {
65
155
  return folderPath;
66
156
  }
67
157
  /**
68
- * Ensure feature folder exists within a project
158
+ * Ensure feature folder exists within a target path
159
+ * Handles both 1-level and 2-level structures
69
160
  */
70
- async ensureFeatureFolder(projectId, featureId) {
71
- const folderPath = this.getFeaturePath(projectId, featureId);
161
+ async ensureFeatureFolder(targetPath, featureId) {
162
+ const folderPath = this.getFeaturePath(targetPath, featureId);
72
163
  await ensureDir(folderPath);
73
164
  return folderPath;
74
165
  }
@@ -76,39 +167,67 @@ export class CrossProjectSync {
76
167
  * Generate cross-references section for FEATURE.md
77
168
  *
78
169
  * @param featureId - The feature ID (e.g., "FS-125")
79
- * @param allProjects - All projects this feature spans
80
- * @param currentProject - The current project being generated
170
+ * @param allTargetPaths - All target paths this feature spans (can be "project" or "project/board")
171
+ * @param currentTargetPath - The current target path being generated
81
172
  * @returns Markdown content for "Related Projects" section
82
173
  */
83
- generateCrossReferences(featureId, allProjects, currentProject) {
84
- const otherProjects = allProjects.filter(p => p !== currentProject);
85
- if (otherProjects.length === 0) {
174
+ generateCrossReferences(featureId, allTargetPaths, currentTargetPath) {
175
+ const otherPaths = allTargetPaths.filter(p => p !== currentTargetPath);
176
+ if (otherPaths.length === 0) {
86
177
  return '';
87
178
  }
179
+ // Determine depth of current path for relative link calculation
180
+ const currentDepth = currentTargetPath.split('/').length;
88
181
  const lines = [
89
182
  '',
90
183
  '## Related Projects',
91
184
  '',
92
- 'This feature spans multiple projects:',
185
+ this.is2LevelStructure()
186
+ ? 'This feature spans multiple project/board combinations:'
187
+ : 'This feature spans multiple projects:',
93
188
  ''
94
189
  ];
95
- for (const projectId of otherProjects) {
96
- // Relative path from current project to other project
97
- const relativePath = `../../${projectId}/${featureId}/`;
98
- lines.push(`- [${projectId}](${relativePath})`);
190
+ for (const targetPath of otherPaths) {
191
+ // Calculate relative path based on depth
192
+ // From project/board/FS-XXX/FEATURE.md to ../../../other-project/other-board/FS-XXX/
193
+ const upLevels = '../'.repeat(currentDepth + 1); // +1 for featureId folder
194
+ const relativePath = `${upLevels}${targetPath}/${featureId}/`;
195
+ // Display label (show full path for clarity)
196
+ const label = targetPath;
197
+ lines.push(`- [${label}](${relativePath})`);
99
198
  }
100
199
  lines.push('');
101
200
  return lines.join('\n');
102
201
  }
103
202
  /**
104
203
  * Generate cross-reference frontmatter for US files
204
+ *
205
+ * @param allTargetPaths - All target paths (can be "project" or "project/board")
206
+ * @param currentTargetPath - Current target path
105
207
  */
106
- generateRelatedProjectsFrontmatter(allProjects, currentProject) {
107
- const otherProjects = allProjects.filter(p => p !== currentProject);
108
- if (otherProjects.length === 0) {
208
+ generateRelatedProjectsFrontmatter(allTargetPaths, currentTargetPath) {
209
+ const otherPaths = allTargetPaths.filter(p => p !== currentTargetPath);
210
+ if (otherPaths.length === 0) {
109
211
  return '';
110
212
  }
111
- return `related_projects: [${otherProjects.join(', ')}]`;
213
+ return `related_projects: [${otherPaths.join(', ')}]`;
214
+ }
215
+ /**
216
+ * Extract project ID from target path
217
+ * "project" -> "project"
218
+ * "project/board" -> "project"
219
+ */
220
+ extractProjectId(targetPath) {
221
+ return targetPath.split('/')[0];
222
+ }
223
+ /**
224
+ * Extract board ID from target path (for 2-level structures)
225
+ * "project" -> undefined
226
+ * "project/board" -> "board"
227
+ */
228
+ extractBoardId(targetPath) {
229
+ const parts = targetPath.split('/');
230
+ return parts.length > 1 ? parts[1] : undefined;
112
231
  }
113
232
  /**
114
233
  * Log cross-project sync summary
@@ -1 +1 @@
1
- {"version":3,"file":"cross-project-sync.js","sourceRoot":"","sources":["../../../../src/core/living-docs/cross-project-sync.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cross-project-sync.js","sourceRoot":"","sources":["../../../../src/core/living-docs/cross-project-sync.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"living-docs-sync.d.ts","sourceRoot":"","sources":["../../../../src/core/living-docs/living-docs-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAUH,OAAO,EAAE,MAAM,EAAiB,MAAM,uBAAuB,CAAC;AAO9D,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,UAAU,EACV,aAAa,EACb,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAoBpB,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,uBAAuB,EAAE,CAAC;AAE5F,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;gBAEd,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO;IAUlE;;;;;;;OAOG;IACH,YAAY,IAAI,MAAM;IAItB;;;;;OAKG;YACW,mBAAmB;IASjC;;OAEG;IACG,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IAiPxF;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;YACW,wBAAwB;IAwDtC;;;;;;;;;OASG;IACH,OAAO,CAAC,8BAA8B;IA+BtC;;;;;;;;;;;;;OAaG;YACW,2BAA2B;IAmEzC;;;;OAIG;IACH,OAAO,CAAC,wBAAwB;IA6BhC;;OAEG;YACW,sBAAsB;IAKpC;;;;;;;;;;;;;;OAcG;YACW,kBAAkB;IA6GhC;;OAEG;YACW,uBAAuB;IAcrC;;OAEG;YACW,wBAAwB;IAmFtC;;;OAGG;YACW,sBAAsB;IAqGpC;;OAEG;YACW,iBAAiB;IA0C/B;;;;;;;;;;;OAWG;YACW,oBAAoB;IAwClC;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,iBAAiB;IAwCzB;;OAEG;YACW,kBAAkB;IAqHhC;;;;OAIG;YACW,sBAAsB;IAkCpC;;OAEG;YACW,kBAAkB;IAmChC;;;;;;;;;;;;OAYG;YACW,mBAAmB;IA6EjC;;;;;;OAMG;IACH;;;;;;;;;;;OAWG;YACW,mBAAmB;IAyIjC;;;;;;;;;OASG;YACW,YAAY;IA0F1B;;;;;;;;;;;;OAYG;YACW,UAAU;IAyFxB;;;;;;;;;;;;OAYG;YACW,SAAS;IAyFvB;;;;;;;;;OASG;YACW,4BAA4B;CAmB3C"}
1
+ {"version":3,"file":"living-docs-sync.d.ts","sourceRoot":"","sources":["../../../../src/core/living-docs/living-docs-sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAUH,OAAO,EAAE,MAAM,EAAiB,MAAM,uBAAuB,CAAC;AAO9D,OAAO,KAAK,EACV,WAAW,EACX,UAAU,EACV,UAAU,EACV,aAAa,EACb,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAoBpB,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,uBAAuB,EAAE,CAAC;AAE5F,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,gBAAgB,CAAmB;IAC3C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,SAAS,CAAS;gBAEd,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAO;IAUlE;;;;;;;OAOG;IACH,YAAY,IAAI,MAAM;IAItB;;;;;OAKG;YACW,mBAAmB;IASjC;;OAEG;IACG,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;IA8PxF;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;YACW,wBAAwB;IAwDtC;;;;;;;;;OASG;IACH,OAAO,CAAC,8BAA8B;IA+BtC;;;;;;;;;;;;;OAaG;YACW,2BAA2B;IAmEzC;;;;OAIG;IACH,OAAO,CAAC,wBAAwB;IA6BhC;;OAEG;YACW,sBAAsB;IAKpC;;;;;;;;;;;;;;OAcG;YACW,kBAAkB;IA6GhC;;OAEG;YACW,uBAAuB;IAcrC;;OAEG;YACW,wBAAwB;IAmFtC;;;OAGG;YACW,sBAAsB;IAqGpC;;OAEG;YACW,iBAAiB;IA0C/B;;;;;;;;;;;OAWG;YACW,oBAAoB;IAwClC;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,iBAAiB;IAwCzB;;OAEG;YACW,kBAAkB;IAqHhC;;;;OAIG;YACW,sBAAsB;IAkCpC;;OAEG;YACW,kBAAkB;IAmChC;;;;;;;;;;;;OAYG;YACW,mBAAmB;IA6EjC;;;;;;OAMG;IACH;;;;;;;;;;;OAWG;YACW,mBAAmB;IAyIjC;;;;;;;;;OASG;YACW,YAAY;IA0F1B;;;;;;;;;;;;OAYG;YACW,UAAU;IAyFxB;;;;;;;;;;;;OAYG;YACW,SAAS;IAyFvB;;;;;;;;;OASG;YACW,4BAA4B;CAmB3C"}
@@ -124,27 +124,31 @@ export class LivingDocsSync {
124
124
  this.logger.log(`📚 Syncing ${incrementId} → ${featureId}...`);
125
125
  // Step 4: Parse increment spec
126
126
  const parsed = await this.parseIncrementSpec(incrementId);
127
- // Step 4b: Detect cross-project increments (v0.33.0+)
128
- // If USs target different projects, sync to multiple project folders
127
+ // Step 4b: Detect cross-project/cross-board increments (v0.33.0+, v0.34.0 2-level)
128
+ // If USs target different projects (or project/board combinations), sync to multiple folders
129
129
  const defaultProject = parsed.frontmatter.project || resolvedProjectPath;
130
- const isCrossProject = this.crossProjectSync.isCrossProject(parsed.userStories, defaultProject);
130
+ const defaultBoard = parsed.frontmatter.board;
131
+ const isCrossProject = this.crossProjectSync.isCrossProject(parsed.userStories, defaultProject, defaultBoard);
131
132
  if (isCrossProject) {
132
- this.logger.log(`📦 Cross-project increment detected`);
133
- const groups = this.crossProjectSync.groupByProject(parsed.userStories, defaultProject);
134
- this.logger.log(` ${groups.size} projects: ${[...groups.keys()].join(', ')}`);
135
- // For cross-project increments, create feature folder in each project
136
- // USs are synced to their respective project's feature folder
137
- // Cross-references are added to link related projects
138
- for (const [projectId, projectStories] of groups) {
139
- const crossProjectPath = path.join(basePath, projectId, featureId);
140
- this.logger.log(` 📁 Syncing ${projectStories.length} USs to ${projectId}/${featureId}/`);
133
+ const is2Level = this.crossProjectSync.is2LevelStructure();
134
+ this.logger.log(`📦 Cross-project increment detected${is2Level ? ' (2-level structure)' : ''}`);
135
+ // Group by full target path (project for 1-level, project/board for 2-level)
136
+ const groups = this.crossProjectSync.groupByProject(parsed.userStories, defaultProject, defaultBoard);
137
+ this.logger.log(` ${groups.size} target${is2Level ? ' paths' : ' projects'}: ${[...groups.keys()].join(', ')}`);
138
+ // For cross-project increments, create feature folder in each target path
139
+ // USs are synced to their respective target's feature folder
140
+ // Cross-references are added to link related projects/boards
141
+ for (const [targetPath, projectStories] of groups) {
142
+ // Use full target path (may be "project" or "project/board")
143
+ const crossProjectPath = path.join(basePath, targetPath, featureId);
144
+ this.logger.log(` 📁 Syncing ${projectStories.length} USs to ${targetPath}/${featureId}/`);
141
145
  if (!options.dryRun) {
142
146
  await ensureDir(crossProjectPath);
143
- // Generate FEATURE.md with cross-references
144
- const crossRefs = this.crossProjectSync.generateCrossReferences(featureId, [...groups.keys()], projectId);
147
+ // Generate FEATURE.md with cross-references (now uses target paths)
148
+ const crossRefs = this.crossProjectSync.generateCrossReferences(featureId, [...groups.keys()], targetPath);
145
149
  let featureContent = generateFeatureFile(featureId, {
146
150
  ...parsed,
147
- userStories: projectStories // Only this project's USs
151
+ userStories: projectStories // Only this target's USs
148
152
  }, incrementId);
149
153
  // Append cross-references section
150
154
  if (crossRefs) {
@@ -152,8 +156,8 @@ export class LivingDocsSync {
152
156
  }
153
157
  await fs.writeFile(path.join(crossProjectPath, 'FEATURE.md'), featureContent, 'utf-8');
154
158
  result.filesCreated.push(path.join(crossProjectPath, 'FEATURE.md'));
155
- // Create user story files for this project
156
- const allProjects = [...groups.keys()];
159
+ // Create user story files for this target path
160
+ const allTargetPaths = [...groups.keys()];
157
161
  for (const story of projectStories) {
158
162
  const existingFile = await findExistingUserStoryFile(crossProjectPath, story.id, this.logger);
159
163
  let storyFile;
@@ -164,15 +168,15 @@ export class LivingDocsSync {
164
168
  const storySlug = story.title.toLowerCase().replace(/[^a-z0-9]+/g, '-');
165
169
  storyFile = path.join(crossProjectPath, `${story.id.toLowerCase()}-${storySlug}.md`);
166
170
  }
167
- // Pass allProjects for related_projects frontmatter (v0.33.0+)
171
+ // Pass allProjects for related_projects frontmatter (v0.33.0+, v0.34.0 paths)
168
172
  const storyContent = generateUserStoryFile(story, featureId, incrementId, {
169
173
  ...parsed,
170
174
  userStories: projectStories
171
- }, { allProjects });
175
+ }, { allProjects: allTargetPaths });
172
176
  await fs.writeFile(storyFile, storyContent, 'utf-8');
173
177
  result.filesCreated.push(storyFile);
174
178
  }
175
- // Cleanup and sync tasks for this project's USs
179
+ // Cleanup and sync tasks for this target's USs
176
180
  await cleanupDuplicateFiles(crossProjectPath, this.logger);
177
181
  await cleanupTempFiles(crossProjectPath, this.logger);
178
182
  await this.syncTasksToUserStories(incrementId, featureId, projectStories, crossProjectPath);
@@ -182,8 +186,8 @@ export class LivingDocsSync {
182
186
  result.success = true;
183
187
  this.crossProjectSync.logSyncSummary({
184
188
  success: true,
185
- projects: [...groups.entries()].map(([projectId, stories]) => ({
186
- projectId,
189
+ projects: [...groups.entries()].map(([targetPath, stories]) => ({
190
+ projectId: targetPath, // Now contains full path (project or project/board)
187
191
  success: true,
188
192
  userStories: stories.map(s => s.id),
189
193
  filesCreated: [],