specweave 0.28.67 → 0.29.0

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 (239) hide show
  1. package/CLAUDE.md +3 -2
  2. package/README.md +19 -2
  3. package/dist/src/cli/commands/discrepancies.d.ts +89 -0
  4. package/dist/src/cli/commands/discrepancies.d.ts.map +1 -0
  5. package/dist/src/cli/commands/discrepancies.js +385 -0
  6. package/dist/src/cli/commands/discrepancies.js.map +1 -0
  7. package/dist/src/cli/commands/notifications.d.ts +70 -0
  8. package/dist/src/cli/commands/notifications.d.ts.map +1 -0
  9. package/dist/src/cli/commands/notifications.js +236 -0
  10. package/dist/src/cli/commands/notifications.js.map +1 -0
  11. package/dist/src/cli/commands/sync-logs.d.ts +54 -0
  12. package/dist/src/cli/commands/sync-logs.d.ts.map +1 -0
  13. package/dist/src/cli/commands/sync-logs.js +240 -0
  14. package/dist/src/cli/commands/sync-logs.js.map +1 -0
  15. package/dist/src/cli/commands/sync-monitor.d.ts +42 -0
  16. package/dist/src/cli/commands/sync-monitor.d.ts.map +1 -0
  17. package/dist/src/cli/commands/sync-monitor.js +191 -0
  18. package/dist/src/cli/commands/sync-monitor.js.map +1 -0
  19. package/dist/src/cli/helpers/init/brownfield-analysis.d.ts +45 -0
  20. package/dist/src/cli/helpers/init/brownfield-analysis.d.ts.map +1 -0
  21. package/dist/src/cli/helpers/init/brownfield-analysis.js +431 -0
  22. package/dist/src/cli/helpers/init/brownfield-analysis.js.map +1 -0
  23. package/dist/src/cli/helpers/init/index.d.ts +1 -0
  24. package/dist/src/cli/helpers/init/index.d.ts.map +1 -1
  25. package/dist/src/cli/helpers/init/index.js +2 -0
  26. package/dist/src/cli/helpers/init/index.js.map +1 -1
  27. package/dist/src/cli/helpers/issue-tracker/ado.d.ts.map +1 -1
  28. package/dist/src/cli/helpers/issue-tracker/ado.js +14 -5
  29. package/dist/src/cli/helpers/issue-tracker/ado.js.map +1 -1
  30. package/dist/src/cli/workers/brownfield-worker.d.ts +66 -0
  31. package/dist/src/cli/workers/brownfield-worker.d.ts.map +1 -0
  32. package/dist/src/cli/workers/brownfield-worker.js +417 -0
  33. package/dist/src/cli/workers/brownfield-worker.js.map +1 -0
  34. package/dist/src/core/background/brownfield-launcher.d.ts +86 -0
  35. package/dist/src/core/background/brownfield-launcher.d.ts.map +1 -0
  36. package/dist/src/core/background/brownfield-launcher.js +295 -0
  37. package/dist/src/core/background/brownfield-launcher.js.map +1 -0
  38. package/dist/src/core/background/index.d.ts +2 -0
  39. package/dist/src/core/background/index.d.ts.map +1 -1
  40. package/dist/src/core/background/index.js +2 -0
  41. package/dist/src/core/background/index.js.map +1 -1
  42. package/dist/src/core/background/types.d.ts +23 -2
  43. package/dist/src/core/background/types.d.ts.map +1 -1
  44. package/dist/src/core/config/index.d.ts +1 -0
  45. package/dist/src/core/config/index.d.ts.map +1 -1
  46. package/dist/src/core/config/index.js +1 -0
  47. package/dist/src/core/config/index.js.map +1 -1
  48. package/dist/src/core/config/types.d.ts +6 -0
  49. package/dist/src/core/config/types.d.ts.map +1 -1
  50. package/dist/src/core/config/types.js.map +1 -1
  51. package/dist/src/core/dashboard/dashboard-data.d.ts +156 -0
  52. package/dist/src/core/dashboard/dashboard-data.d.ts.map +1 -0
  53. package/dist/src/core/dashboard/dashboard-data.js +191 -0
  54. package/dist/src/core/dashboard/dashboard-data.js.map +1 -0
  55. package/dist/src/core/dashboard/index.d.ts +9 -0
  56. package/dist/src/core/dashboard/index.d.ts.map +1 -0
  57. package/dist/src/core/dashboard/index.js +9 -0
  58. package/dist/src/core/dashboard/index.js.map +1 -0
  59. package/dist/src/core/discrepancy/analyzers/api-route-analyzer.d.ts +77 -0
  60. package/dist/src/core/discrepancy/analyzers/api-route-analyzer.d.ts.map +1 -0
  61. package/dist/src/core/discrepancy/analyzers/api-route-analyzer.js +286 -0
  62. package/dist/src/core/discrepancy/analyzers/api-route-analyzer.js.map +1 -0
  63. package/dist/src/core/discrepancy/analyzers/index.d.ts +8 -0
  64. package/dist/src/core/discrepancy/analyzers/index.d.ts.map +1 -0
  65. package/dist/src/core/discrepancy/analyzers/index.js +8 -0
  66. package/dist/src/core/discrepancy/analyzers/index.js.map +1 -0
  67. package/dist/src/core/discrepancy/analyzers/typescript-analyzer.d.ts +96 -0
  68. package/dist/src/core/discrepancy/analyzers/typescript-analyzer.d.ts.map +1 -0
  69. package/dist/src/core/discrepancy/analyzers/typescript-analyzer.js +247 -0
  70. package/dist/src/core/discrepancy/analyzers/typescript-analyzer.js.map +1 -0
  71. package/dist/src/core/discrepancy/brownfield-manager.d.ts +88 -0
  72. package/dist/src/core/discrepancy/brownfield-manager.d.ts.map +1 -0
  73. package/dist/src/core/discrepancy/brownfield-manager.js +520 -0
  74. package/dist/src/core/discrepancy/brownfield-manager.js.map +1 -0
  75. package/dist/src/core/discrepancy/brownfield-types.d.ts +174 -0
  76. package/dist/src/core/discrepancy/brownfield-types.d.ts.map +1 -0
  77. package/dist/src/core/discrepancy/brownfield-types.js +11 -0
  78. package/dist/src/core/discrepancy/brownfield-types.js.map +1 -0
  79. package/dist/src/core/discrepancy/detector.d.ts +92 -0
  80. package/dist/src/core/discrepancy/detector.d.ts.map +1 -0
  81. package/dist/src/core/discrepancy/detector.js +346 -0
  82. package/dist/src/core/discrepancy/detector.js.map +1 -0
  83. package/dist/src/core/discrepancy/increment-generator.d.ts +51 -0
  84. package/dist/src/core/discrepancy/increment-generator.d.ts.map +1 -0
  85. package/dist/src/core/discrepancy/increment-generator.js +234 -0
  86. package/dist/src/core/discrepancy/increment-generator.js.map +1 -0
  87. package/dist/src/core/discrepancy/index.d.ts +18 -0
  88. package/dist/src/core/discrepancy/index.d.ts.map +1 -0
  89. package/dist/src/core/discrepancy/index.js +24 -0
  90. package/dist/src/core/discrepancy/index.js.map +1 -0
  91. package/dist/src/core/discrepancy/severity-classifier.d.ts +81 -0
  92. package/dist/src/core/discrepancy/severity-classifier.d.ts.map +1 -0
  93. package/dist/src/core/discrepancy/severity-classifier.js +289 -0
  94. package/dist/src/core/discrepancy/severity-classifier.js.map +1 -0
  95. package/dist/src/core/discrepancy/spec-parser.d.ts +74 -0
  96. package/dist/src/core/discrepancy/spec-parser.d.ts.map +1 -0
  97. package/dist/src/core/discrepancy/spec-parser.js +213 -0
  98. package/dist/src/core/discrepancy/spec-parser.js.map +1 -0
  99. package/dist/src/core/discrepancy/update-recommender.d.ts +77 -0
  100. package/dist/src/core/discrepancy/update-recommender.d.ts.map +1 -0
  101. package/dist/src/core/discrepancy/update-recommender.js +323 -0
  102. package/dist/src/core/discrepancy/update-recommender.js.map +1 -0
  103. package/dist/src/core/living-docs/living-docs-sync.d.ts +13 -16
  104. package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
  105. package/dist/src/core/living-docs/living-docs-sync.js +31 -112
  106. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  107. package/dist/src/core/logs/index.d.ts +10 -0
  108. package/dist/src/core/logs/index.d.ts.map +1 -0
  109. package/dist/src/core/logs/index.js +10 -0
  110. package/dist/src/core/logs/index.js.map +1 -0
  111. package/dist/src/core/logs/log-aggregator.d.ts +130 -0
  112. package/dist/src/core/logs/log-aggregator.d.ts.map +1 -0
  113. package/dist/src/core/logs/log-aggregator.js +206 -0
  114. package/dist/src/core/logs/log-aggregator.js.map +1 -0
  115. package/dist/src/core/logs/log-exporter.d.ts +81 -0
  116. package/dist/src/core/logs/log-exporter.d.ts.map +1 -0
  117. package/dist/src/core/logs/log-exporter.js +141 -0
  118. package/dist/src/core/logs/log-exporter.js.map +1 -0
  119. package/dist/src/core/notifications/command-integration.d.ts +82 -0
  120. package/dist/src/core/notifications/command-integration.d.ts.map +1 -0
  121. package/dist/src/core/notifications/command-integration.js +80 -0
  122. package/dist/src/core/notifications/command-integration.js.map +1 -0
  123. package/dist/src/core/notifications/index.d.ts +12 -0
  124. package/dist/src/core/notifications/index.d.ts.map +1 -0
  125. package/dist/src/core/notifications/index.js +12 -0
  126. package/dist/src/core/notifications/index.js.map +1 -0
  127. package/dist/src/core/notifications/notification-display.d.ts +70 -0
  128. package/dist/src/core/notifications/notification-display.d.ts.map +1 -0
  129. package/dist/src/core/notifications/notification-display.js +177 -0
  130. package/dist/src/core/notifications/notification-display.js.map +1 -0
  131. package/dist/src/core/notifications/notification-manager.d.ts +126 -0
  132. package/dist/src/core/notifications/notification-manager.d.ts.map +1 -0
  133. package/dist/src/core/notifications/notification-manager.js +287 -0
  134. package/dist/src/core/notifications/notification-manager.js.map +1 -0
  135. package/dist/src/core/notifications/notification-types.d.ts +159 -0
  136. package/dist/src/core/notifications/notification-types.d.ts.map +1 -0
  137. package/dist/src/core/notifications/notification-types.js +93 -0
  138. package/dist/src/core/notifications/notification-types.js.map +1 -0
  139. package/dist/src/core/scheduler/index.d.ts +11 -0
  140. package/dist/src/core/scheduler/index.d.ts.map +1 -0
  141. package/dist/src/core/scheduler/index.js +11 -0
  142. package/dist/src/core/scheduler/index.js.map +1 -0
  143. package/dist/src/core/scheduler/job-scheduler.d.ts +179 -0
  144. package/dist/src/core/scheduler/job-scheduler.d.ts.map +1 -0
  145. package/dist/src/core/scheduler/job-scheduler.js +282 -0
  146. package/dist/src/core/scheduler/job-scheduler.js.map +1 -0
  147. package/dist/src/core/scheduler/schedule-persistence.d.ts +83 -0
  148. package/dist/src/core/scheduler/schedule-persistence.d.ts.map +1 -0
  149. package/dist/src/core/scheduler/schedule-persistence.js +180 -0
  150. package/dist/src/core/scheduler/schedule-persistence.js.map +1 -0
  151. package/dist/src/core/scheduler/scheduled-job.d.ts +188 -0
  152. package/dist/src/core/scheduler/scheduled-job.d.ts.map +1 -0
  153. package/dist/src/core/scheduler/scheduled-job.js +182 -0
  154. package/dist/src/core/scheduler/scheduled-job.js.map +1 -0
  155. package/dist/src/core/sync/permission-enforcer.d.ts +206 -0
  156. package/dist/src/core/sync/permission-enforcer.d.ts.map +1 -0
  157. package/dist/src/core/sync/permission-enforcer.js +268 -0
  158. package/dist/src/core/sync/permission-enforcer.js.map +1 -0
  159. package/dist/src/core/sync/sync-audit-logger.d.ts +217 -0
  160. package/dist/src/core/sync/sync-audit-logger.d.ts.map +1 -0
  161. package/dist/src/core/sync/sync-audit-logger.js +327 -0
  162. package/dist/src/core/sync/sync-audit-logger.js.map +1 -0
  163. package/dist/src/core/sync/sync-interceptor.d.ts +190 -0
  164. package/dist/src/core/sync/sync-interceptor.d.ts.map +1 -0
  165. package/dist/src/core/sync/sync-interceptor.js +224 -0
  166. package/dist/src/core/sync/sync-interceptor.js.map +1 -0
  167. package/dist/src/core/types/increment-metadata.d.ts +5 -2
  168. package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
  169. package/dist/src/core/types/sync-config.d.ts +267 -0
  170. package/dist/src/core/types/sync-config.d.ts.map +1 -0
  171. package/dist/src/core/types/sync-config.js +304 -0
  172. package/dist/src/core/types/sync-config.js.map +1 -0
  173. package/dist/src/hooks/index.d.ts +11 -0
  174. package/dist/src/hooks/index.d.ts.map +1 -0
  175. package/dist/src/hooks/index.js +11 -0
  176. package/dist/src/hooks/index.js.map +1 -0
  177. package/dist/src/hooks/platform.d.ts +125 -0
  178. package/dist/src/hooks/platform.d.ts.map +1 -0
  179. package/dist/src/hooks/platform.js +325 -0
  180. package/dist/src/hooks/platform.js.map +1 -0
  181. package/dist/src/hooks/processor.d.ts +20 -0
  182. package/dist/src/hooks/processor.d.ts.map +1 -0
  183. package/dist/src/hooks/processor.js +317 -0
  184. package/dist/src/hooks/processor.js.map +1 -0
  185. package/dist/src/hooks/scheduler-startup.d.ts +19 -0
  186. package/dist/src/hooks/scheduler-startup.d.ts.map +1 -0
  187. package/dist/src/hooks/scheduler-startup.js +92 -0
  188. package/dist/src/hooks/scheduler-startup.js.map +1 -0
  189. package/dist/src/hooks/session-start.d.ts +16 -0
  190. package/dist/src/hooks/session-start.d.ts.map +1 -0
  191. package/dist/src/hooks/session-start.js +92 -0
  192. package/dist/src/hooks/session-start.js.map +1 -0
  193. package/dist/src/importers/duplicate-detector.d.ts +13 -2
  194. package/dist/src/importers/duplicate-detector.d.ts.map +1 -1
  195. package/dist/src/importers/duplicate-detector.js +21 -2
  196. package/dist/src/importers/duplicate-detector.js.map +1 -1
  197. package/dist/src/importers/item-converter.d.ts +41 -2
  198. package/dist/src/importers/item-converter.d.ts.map +1 -1
  199. package/dist/src/importers/item-converter.js +231 -45
  200. package/dist/src/importers/item-converter.js.map +1 -1
  201. package/dist/src/living-docs/fs-id-allocator.d.ts +7 -0
  202. package/dist/src/living-docs/fs-id-allocator.d.ts.map +1 -1
  203. package/dist/src/living-docs/fs-id-allocator.js +30 -4
  204. package/dist/src/living-docs/fs-id-allocator.js.map +1 -1
  205. package/dist/src/sync/ado-sync-wrapper.d.ts +137 -0
  206. package/dist/src/sync/ado-sync-wrapper.d.ts.map +1 -0
  207. package/dist/src/sync/ado-sync-wrapper.js +148 -0
  208. package/dist/src/sync/ado-sync-wrapper.js.map +1 -0
  209. package/dist/src/sync/github-sync-wrapper.d.ts +195 -0
  210. package/dist/src/sync/github-sync-wrapper.d.ts.map +1 -0
  211. package/dist/src/sync/github-sync-wrapper.js +220 -0
  212. package/dist/src/sync/github-sync-wrapper.js.map +1 -0
  213. package/dist/src/sync/jira-sync-wrapper.d.ts +155 -0
  214. package/dist/src/sync/jira-sync-wrapper.d.ts.map +1 -0
  215. package/dist/src/sync/jira-sync-wrapper.js +175 -0
  216. package/dist/src/sync/jira-sync-wrapper.js.map +1 -0
  217. package/dist/src/utils/feature-id-derivation.d.ts +58 -0
  218. package/dist/src/utils/feature-id-derivation.d.ts.map +1 -0
  219. package/dist/src/utils/feature-id-derivation.js +77 -0
  220. package/dist/src/utils/feature-id-derivation.js.map +1 -0
  221. package/package.json +1 -1
  222. package/plugins/specweave/commands/specweave-discrepancies.md +141 -0
  223. package/plugins/specweave/commands/specweave-discrepancy-to-increment.md +160 -0
  224. package/plugins/specweave/commands/specweave-jobs.md +45 -2
  225. package/plugins/specweave/commands/specweave-notifications.md +92 -0
  226. package/plugins/specweave/commands/specweave-sync-logs.md +131 -0
  227. package/plugins/specweave/commands/specweave-sync-monitor.md +57 -0
  228. package/plugins/specweave/hooks/hooks.json +3 -3
  229. package/plugins/specweave/hooks/lib/scheduler-startup.sh +72 -0
  230. package/plugins/specweave/hooks/universal/dispatcher.mjs +246 -0
  231. package/plugins/specweave/hooks/universal/session-start.cmd +16 -0
  232. package/plugins/specweave/hooks/universal/session-start.ps1 +16 -0
  233. package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +14 -5
  234. package/plugins/specweave/lib/vendor/core/types/increment-metadata.d.ts +5 -2
  235. package/plugins/specweave/skills/discrepancy-viewer.md +154 -0
  236. package/plugins/specweave-ado/lib/ado-project-detector.js +11 -5
  237. package/plugins/specweave-ado/lib/ado-project-detector.ts +16 -5
  238. package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +34 -0
  239. package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +51 -0
@@ -0,0 +1,220 @@
1
+ /**
2
+ * GitHub Sync Wrapper
3
+ *
4
+ * Wraps GitHub sync operations with the SyncInterceptor for permission checks
5
+ * and audit logging. This provides a consistent interface for all GitHub sync
6
+ * operations while ensuring they go through the permission enforcement layer.
7
+ *
8
+ * Usage:
9
+ * ```typescript
10
+ * const wrapper = new GitHubSyncWrapper({
11
+ * client: GitHubClientV2.fromRepo(owner, repo),
12
+ * interceptor,
13
+ * logger
14
+ * });
15
+ *
16
+ * // All operations now go through permission enforcement
17
+ * const issue = await wrapper.createUserStoryIssue(params);
18
+ * ```
19
+ *
20
+ * @module sync/github-sync-wrapper
21
+ */
22
+ import { GitHubClientV2 } from '../../plugins/specweave-github/lib/github-client-v2.js';
23
+ import { consoleLogger } from '../utils/logger.js';
24
+ /**
25
+ * GitHubSyncWrapper - Permission-enforced GitHub sync operations
26
+ *
27
+ * All GitHub sync operations pass through the SyncInterceptor which:
28
+ * 1. Checks permissions before execution
29
+ * 2. Logs all attempts (success, denied, error)
30
+ * 3. Provides consistent error handling
31
+ *
32
+ * This wrapper does NOT modify the underlying GitHubClientV2 - it provides
33
+ * a parallel interface that can be used when permission checking is needed.
34
+ */
35
+ export class GitHubSyncWrapper {
36
+ constructor(options) {
37
+ this.client = options.client;
38
+ this.interceptor = options.interceptor;
39
+ this.logger = options.logger ?? consoleLogger;
40
+ }
41
+ /**
42
+ * Create a GitHub issue for a user story
43
+ *
44
+ * @param params - Issue creation parameters
45
+ * @returns Wrapped operation result
46
+ */
47
+ async createUserStoryIssue(params) {
48
+ const itemId = `${params.featureId}/${params.userStoryId}`;
49
+ const result = await this.interceptor.intercept('github', 'upsert-internal', itemId, 'internal', () => this.client.createUserStoryIssue({
50
+ featureId: params.featureId,
51
+ userStoryId: params.userStoryId,
52
+ title: params.title,
53
+ body: params.body,
54
+ labels: params.labels || [],
55
+ milestone: params.milestone ?? undefined,
56
+ }));
57
+ return this.toWrappedResult(result);
58
+ }
59
+ /**
60
+ * Create a generic epic/increment issue
61
+ *
62
+ * @param title - Issue title
63
+ * @param body - Issue body
64
+ * @param milestone - Optional milestone number or string
65
+ * @param labels - Issue labels
66
+ * @returns Wrapped operation result
67
+ */
68
+ async createEpicIssue(title, body, milestone, labels = []) {
69
+ // Extract item ID from title if possible
70
+ const match = title.match(/\[([^\]]+)\]/);
71
+ const itemId = match ? match[1] : 'unknown';
72
+ const result = await this.interceptor.intercept('github', 'upsert-internal', itemId, 'internal', () => this.client.createEpicIssue(title, body, milestone, labels));
73
+ return this.toWrappedResult(result);
74
+ }
75
+ /**
76
+ * Update a GitHub issue body
77
+ *
78
+ * @param issueNumber - Issue number
79
+ * @param body - New issue body
80
+ * @param origin - Item origin (internal or external)
81
+ * @returns Wrapped operation result
82
+ */
83
+ async updateIssueBody(issueNumber, body, origin = 'internal') {
84
+ const itemId = `issue-${issueNumber}`;
85
+ const operation = origin === 'external' ? 'upsert-external' : 'upsert-internal';
86
+ const result = await this.interceptor.intercept('github', operation, itemId, origin, () => this.client.updateIssueBody(issueNumber, body));
87
+ return this.toWrappedResult(result);
88
+ }
89
+ /**
90
+ * Close a GitHub issue
91
+ *
92
+ * @param issueNumber - Issue number
93
+ * @param comment - Optional closing comment
94
+ * @returns Wrapped operation result
95
+ */
96
+ async closeIssue(issueNumber, comment) {
97
+ const itemId = `issue-${issueNumber}`;
98
+ const result = await this.interceptor.interceptStatusUpdate('github', itemId, () => this.client.closeIssue(issueNumber, comment));
99
+ return this.toWrappedResult(result);
100
+ }
101
+ /**
102
+ * Reopen a GitHub issue
103
+ *
104
+ * @param issueNumber - Issue number
105
+ * @param comment - Optional reopening comment
106
+ * @returns Wrapped operation result
107
+ */
108
+ async reopenIssue(issueNumber, comment) {
109
+ const itemId = `issue-${issueNumber}`;
110
+ const result = await this.interceptor.interceptStatusUpdate('github', itemId, () => this.client.reopenIssue(issueNumber, comment));
111
+ return this.toWrappedResult(result);
112
+ }
113
+ /**
114
+ * Add a comment to a GitHub issue
115
+ *
116
+ * @param issueNumber - Issue number
117
+ * @param comment - Comment body
118
+ * @param origin - Item origin
119
+ * @returns Wrapped operation result
120
+ */
121
+ async addComment(issueNumber, comment, origin = 'internal') {
122
+ const itemId = `issue-${issueNumber}`;
123
+ const operation = origin === 'external' ? 'upsert-external' : 'upsert-internal';
124
+ const result = await this.interceptor.intercept('github', operation, itemId, origin, () => this.client.addComment(issueNumber, comment));
125
+ return this.toWrappedResult(result);
126
+ }
127
+ /**
128
+ * Add labels to a GitHub issue
129
+ *
130
+ * @param issueNumber - Issue number
131
+ * @param labels - Labels to add
132
+ * @returns Wrapped operation result
133
+ */
134
+ async addLabels(issueNumber, labels) {
135
+ const itemId = `issue-${issueNumber}`;
136
+ const result = await this.interceptor.interceptUpsert('github', itemId, () => this.client.addLabels(issueNumber, labels));
137
+ return this.toWrappedResult(result);
138
+ }
139
+ /**
140
+ * Get a GitHub issue (read-only, always allowed)
141
+ *
142
+ * @param issueNumber - Issue number
143
+ * @returns Wrapped operation result
144
+ */
145
+ async getIssue(issueNumber) {
146
+ const itemId = `issue-${issueNumber}`;
147
+ const result = await this.interceptor.interceptRead('github', itemId, () => this.client.getIssue(issueNumber));
148
+ return this.toWrappedResult(result);
149
+ }
150
+ /**
151
+ * Search for an issue by title (read-only, always allowed)
152
+ *
153
+ * @param title - Title to search for
154
+ * @returns Wrapped operation result
155
+ */
156
+ async searchIssueByTitle(title) {
157
+ const result = await this.interceptor.interceptRead('github', `search:${title}`, () => this.client.searchIssueByTitle(title));
158
+ return this.toWrappedResult(result);
159
+ }
160
+ /**
161
+ * Check if an operation would be allowed (pre-flight check)
162
+ *
163
+ * @param operation - Operation type
164
+ * @param itemId - Item ID
165
+ * @returns true if operation would be allowed
166
+ */
167
+ wouldAllow(operation, itemId) {
168
+ return this.interceptor.wouldAllow('github', operation, itemId);
169
+ }
170
+ /**
171
+ * Get the underlying client for operations that don't need interception
172
+ * (e.g., already-checked operations in bulk workflows)
173
+ */
174
+ getClient() {
175
+ return this.client;
176
+ }
177
+ /**
178
+ * Convert interceptor result to wrapped result
179
+ */
180
+ toWrappedResult(result) {
181
+ if (!result.allowed) {
182
+ return {
183
+ success: false,
184
+ result: null,
185
+ denialReason: result.permission.reason,
186
+ durationMs: result.durationMs,
187
+ };
188
+ }
189
+ if (result.error) {
190
+ return {
191
+ success: false,
192
+ result: null,
193
+ error: result.error.message,
194
+ durationMs: result.durationMs,
195
+ };
196
+ }
197
+ return {
198
+ success: true,
199
+ result: result.result,
200
+ durationMs: result.durationMs,
201
+ };
202
+ }
203
+ }
204
+ /**
205
+ * Factory function to create GitHubSyncWrapper
206
+ *
207
+ * @param owner - Repository owner
208
+ * @param repo - Repository name
209
+ * @param interceptor - Sync interceptor
210
+ * @param logger - Optional logger
211
+ */
212
+ export function createGitHubSyncWrapper(owner, repo, interceptor, logger) {
213
+ const client = GitHubClientV2.fromRepo(owner, repo);
214
+ return new GitHubSyncWrapper({
215
+ client,
216
+ interceptor,
217
+ logger,
218
+ });
219
+ }
220
+ //# sourceMappingURL=github-sync-wrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-sync-wrapper.js","sourceRoot":"","sources":["../../../src/sync/github-sync-wrapper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,155 @@
1
+ /**
2
+ * JIRA Sync Wrapper
3
+ *
4
+ * Wraps JIRA sync operations with the SyncInterceptor for permission checks
5
+ * and audit logging. This provides a consistent interface for all JIRA sync
6
+ * operations while ensuring they go through the permission enforcement layer.
7
+ *
8
+ * Usage:
9
+ * ```typescript
10
+ * const wrapper = new JiraSyncWrapper({
11
+ * client: new JiraClient({ baseUrl, credentials }),
12
+ * projectKey: 'PROJ',
13
+ * interceptor,
14
+ * logger
15
+ * });
16
+ *
17
+ * // All operations now go through permission enforcement
18
+ * const issue = await wrapper.createIssue(params);
19
+ * ```
20
+ *
21
+ * @module sync/jira-sync-wrapper
22
+ */
23
+ import { SyncInterceptor } from '../core/sync/sync-interceptor.js';
24
+ import { JiraClient, JiraIssue, JiraIssueCreate, JiraIssueUpdate } from '../integrations/jira/jira-client.js';
25
+ import { Logger } from '../utils/logger.js';
26
+ /**
27
+ * Wrapper options
28
+ */
29
+ export interface JiraSyncWrapperOptions {
30
+ /**
31
+ * JIRA client instance
32
+ */
33
+ client: JiraClient;
34
+ /**
35
+ * Default project key for operations
36
+ */
37
+ projectKey: string;
38
+ /**
39
+ * Sync interceptor for permission checking
40
+ */
41
+ interceptor: SyncInterceptor;
42
+ /**
43
+ * Logger instance
44
+ */
45
+ logger?: Logger;
46
+ }
47
+ /**
48
+ * Operation result with additional metadata
49
+ */
50
+ export interface WrappedOperationResult<T> {
51
+ /**
52
+ * Whether the operation was allowed and succeeded
53
+ */
54
+ success: boolean;
55
+ /**
56
+ * The result of the operation (null if denied or failed)
57
+ */
58
+ result: T | null;
59
+ /**
60
+ * Reason for denial (if denied)
61
+ */
62
+ denialReason?: string;
63
+ /**
64
+ * Error message (if failed)
65
+ */
66
+ error?: string;
67
+ /**
68
+ * Duration of the operation in milliseconds
69
+ */
70
+ durationMs: number;
71
+ }
72
+ /**
73
+ * JiraSyncWrapper - Permission-enforced JIRA sync operations
74
+ *
75
+ * All JIRA sync operations pass through the SyncInterceptor which:
76
+ * 1. Checks permissions before execution
77
+ * 2. Logs all attempts (success, denied, error)
78
+ * 3. Provides consistent error handling
79
+ */
80
+ export declare class JiraSyncWrapper {
81
+ private readonly client;
82
+ private readonly projectKey;
83
+ private readonly interceptor;
84
+ private readonly logger;
85
+ constructor(options: JiraSyncWrapperOptions);
86
+ /**
87
+ * Create a JIRA issue
88
+ *
89
+ * @param issue - Issue creation parameters
90
+ * @param origin - Item origin (internal or external)
91
+ * @returns Wrapped operation result
92
+ */
93
+ createIssue(issue: JiraIssueCreate, origin?: 'internal' | 'external'): Promise<WrappedOperationResult<JiraIssue>>;
94
+ /**
95
+ * Update a JIRA issue
96
+ *
97
+ * @param update - Issue update parameters
98
+ * @param origin - Item origin (internal or external)
99
+ * @returns Wrapped operation result
100
+ */
101
+ updateIssue(update: JiraIssueUpdate, origin?: 'internal' | 'external'): Promise<WrappedOperationResult<JiraIssue>>;
102
+ /**
103
+ * Update issue status (transition)
104
+ *
105
+ * @param issueKey - JIRA issue key
106
+ * @param targetStatus - Target status name
107
+ * @returns Wrapped operation result
108
+ */
109
+ updateStatus(issueKey: string, targetStatus: string): Promise<WrappedOperationResult<JiraIssue>>;
110
+ /**
111
+ * Get a JIRA issue (read-only, always allowed)
112
+ *
113
+ * @param issueKey - JIRA issue key
114
+ * @returns Wrapped operation result
115
+ */
116
+ getIssue(issueKey: string): Promise<WrappedOperationResult<JiraIssue>>;
117
+ /**
118
+ * Search issues with JQL filter (read-only, always allowed)
119
+ *
120
+ * @param jql - JQL query string
121
+ * @param maxResults - Maximum results to return
122
+ * @returns Wrapped operation result
123
+ */
124
+ searchIssues(jql: string, maxResults?: number): Promise<WrappedOperationResult<JiraIssue[]>>;
125
+ /**
126
+ * Add labels to a JIRA issue
127
+ *
128
+ * @param issueKey - JIRA issue key
129
+ * @param labels - Labels to add
130
+ * @param origin - Item origin
131
+ * @returns Wrapped operation result
132
+ */
133
+ addLabels(issueKey: string, labels: string[], origin?: 'internal' | 'external'): Promise<WrappedOperationResult<JiraIssue>>;
134
+ /**
135
+ * Check if an operation would be allowed (pre-flight check)
136
+ *
137
+ * @param operation - Operation type
138
+ * @param itemId - Item ID
139
+ * @returns true if operation would be allowed
140
+ */
141
+ wouldAllow(operation: 'upsert-internal' | 'upsert-external' | 'update-status' | 'read', itemId: string): boolean;
142
+ /**
143
+ * Get the underlying client for operations that don't need interception
144
+ */
145
+ getClient(): JiraClient;
146
+ /**
147
+ * Get the project key
148
+ */
149
+ getProjectKey(): string;
150
+ /**
151
+ * Convert interceptor result to wrapped result
152
+ */
153
+ private toWrappedResult;
154
+ }
155
+ //# sourceMappingURL=jira-sync-wrapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jira-sync-wrapper.d.ts","sourceRoot":"","sources":["../../../src/sync/jira-sync-wrapper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,eAAe,EAAqB,MAAM,kCAAkC,CAAC;AACtF,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAC9G,OAAO,EAAE,MAAM,EAAiB,MAAM,oBAAoB,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,MAAM,EAAE,UAAU,CAAC;IAEnB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,WAAW,EAAE,eAAe,CAAC;IAE7B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACvC;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC;IAEjB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkB;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,OAAO,EAAE,sBAAsB;IAO3C;;;;;;OAMG;IACG,WAAW,CACf,KAAK,EAAE,eAAe,EACtB,MAAM,GAAE,UAAU,GAAG,UAAuB,GAC3C,OAAO,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAe7C;;;;;;OAMG;IACG,WAAW,CACf,MAAM,EAAE,eAAe,EACvB,MAAM,GAAE,UAAU,GAAG,UAAuB,GAC3C,OAAO,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAe7C;;;;;;OAMG;IACG,YAAY,CAChB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAiB7C;;;;;OAKG;IACG,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAU5E;;;;;;OAMG;IACG,YAAY,CAChB,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAAW,GACtB,OAAO,CAAC,sBAAsB,CAAC,SAAS,EAAE,CAAC,CAAC;IAU/C;;;;;;;OAOG;IACG,SAAS,CACb,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EAAE,EAChB,MAAM,GAAE,UAAU,GAAG,UAAuB,GAC3C,OAAO,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAwB7C;;;;;;OAMG;IACH,UAAU,CACR,SAAS,EAAE,iBAAiB,GAAG,iBAAiB,GAAG,eAAe,GAAG,MAAM,EAC3E,MAAM,EAAE,MAAM,GACb,OAAO;IAIV;;OAEG;IACH,SAAS,IAAI,UAAU;IAIvB;;OAEG;IACH,aAAa,IAAI,MAAM;IAIvB;;OAEG;IACH,OAAO,CAAC,eAAe;CAyBxB"}
@@ -0,0 +1,175 @@
1
+ /**
2
+ * JIRA Sync Wrapper
3
+ *
4
+ * Wraps JIRA sync operations with the SyncInterceptor for permission checks
5
+ * and audit logging. This provides a consistent interface for all JIRA sync
6
+ * operations while ensuring they go through the permission enforcement layer.
7
+ *
8
+ * Usage:
9
+ * ```typescript
10
+ * const wrapper = new JiraSyncWrapper({
11
+ * client: new JiraClient({ baseUrl, credentials }),
12
+ * projectKey: 'PROJ',
13
+ * interceptor,
14
+ * logger
15
+ * });
16
+ *
17
+ * // All operations now go through permission enforcement
18
+ * const issue = await wrapper.createIssue(params);
19
+ * ```
20
+ *
21
+ * @module sync/jira-sync-wrapper
22
+ */
23
+ import { consoleLogger } from '../utils/logger.js';
24
+ /**
25
+ * JiraSyncWrapper - Permission-enforced JIRA sync operations
26
+ *
27
+ * All JIRA sync operations pass through the SyncInterceptor which:
28
+ * 1. Checks permissions before execution
29
+ * 2. Logs all attempts (success, denied, error)
30
+ * 3. Provides consistent error handling
31
+ */
32
+ export class JiraSyncWrapper {
33
+ constructor(options) {
34
+ this.client = options.client;
35
+ this.projectKey = options.projectKey;
36
+ this.interceptor = options.interceptor;
37
+ this.logger = options.logger ?? consoleLogger;
38
+ }
39
+ /**
40
+ * Create a JIRA issue
41
+ *
42
+ * @param issue - Issue creation parameters
43
+ * @param origin - Item origin (internal or external)
44
+ * @returns Wrapped operation result
45
+ */
46
+ async createIssue(issue, origin = 'internal') {
47
+ const itemId = `${this.projectKey}:${issue.summary.substring(0, 50)}`;
48
+ const operation = origin === 'external' ? 'upsert-external' : 'upsert-internal';
49
+ const result = await this.interceptor.intercept('jira', operation, itemId, origin, () => this.client.createIssue(issue, this.projectKey));
50
+ return this.toWrappedResult(result);
51
+ }
52
+ /**
53
+ * Update a JIRA issue
54
+ *
55
+ * @param update - Issue update parameters
56
+ * @param origin - Item origin (internal or external)
57
+ * @returns Wrapped operation result
58
+ */
59
+ async updateIssue(update, origin = 'internal') {
60
+ const itemId = update.key;
61
+ const operation = origin === 'external' ? 'upsert-external' : 'upsert-internal';
62
+ const result = await this.interceptor.intercept('jira', operation, itemId, origin, () => this.client.updateIssue(update));
63
+ return this.toWrappedResult(result);
64
+ }
65
+ /**
66
+ * Update issue status (transition)
67
+ *
68
+ * @param issueKey - JIRA issue key
69
+ * @param targetStatus - Target status name
70
+ * @returns Wrapped operation result
71
+ */
72
+ async updateStatus(issueKey, targetStatus) {
73
+ const result = await this.interceptor.interceptStatusUpdate('jira', issueKey, async () => {
74
+ // Update status via updateIssue which handles transitions
75
+ const updatedIssue = await this.client.updateIssue({
76
+ key: issueKey,
77
+ status: targetStatus,
78
+ });
79
+ return updatedIssue;
80
+ });
81
+ return this.toWrappedResult(result);
82
+ }
83
+ /**
84
+ * Get a JIRA issue (read-only, always allowed)
85
+ *
86
+ * @param issueKey - JIRA issue key
87
+ * @returns Wrapped operation result
88
+ */
89
+ async getIssue(issueKey) {
90
+ const result = await this.interceptor.interceptRead('jira', issueKey, () => this.client.getIssue(issueKey));
91
+ return this.toWrappedResult(result);
92
+ }
93
+ /**
94
+ * Search issues with JQL filter (read-only, always allowed)
95
+ *
96
+ * @param jql - JQL query string
97
+ * @param maxResults - Maximum results to return
98
+ * @returns Wrapped operation result
99
+ */
100
+ async searchIssues(jql, maxResults = 50) {
101
+ const result = await this.interceptor.interceptRead('jira', `jql:${jql.substring(0, 50)}`, () => this.client.searchIssues({ jql, maxResults }));
102
+ return this.toWrappedResult(result);
103
+ }
104
+ /**
105
+ * Add labels to a JIRA issue
106
+ *
107
+ * @param issueKey - JIRA issue key
108
+ * @param labels - Labels to add
109
+ * @param origin - Item origin
110
+ * @returns Wrapped operation result
111
+ */
112
+ async addLabels(issueKey, labels, origin = 'internal') {
113
+ const operation = origin === 'external' ? 'upsert-external' : 'upsert-internal';
114
+ const result = await this.interceptor.intercept('jira', operation, issueKey, origin, async () => {
115
+ // Get current labels and merge
116
+ const currentIssue = await this.client.getIssue(issueKey);
117
+ const currentLabels = currentIssue.fields.labels || [];
118
+ const mergedLabels = [...new Set([...currentLabels, ...labels])];
119
+ return this.client.updateIssue({
120
+ key: issueKey,
121
+ labels: mergedLabels,
122
+ });
123
+ });
124
+ return this.toWrappedResult(result);
125
+ }
126
+ /**
127
+ * Check if an operation would be allowed (pre-flight check)
128
+ *
129
+ * @param operation - Operation type
130
+ * @param itemId - Item ID
131
+ * @returns true if operation would be allowed
132
+ */
133
+ wouldAllow(operation, itemId) {
134
+ return this.interceptor.wouldAllow('jira', operation, itemId);
135
+ }
136
+ /**
137
+ * Get the underlying client for operations that don't need interception
138
+ */
139
+ getClient() {
140
+ return this.client;
141
+ }
142
+ /**
143
+ * Get the project key
144
+ */
145
+ getProjectKey() {
146
+ return this.projectKey;
147
+ }
148
+ /**
149
+ * Convert interceptor result to wrapped result
150
+ */
151
+ toWrappedResult(result) {
152
+ if (!result.allowed) {
153
+ return {
154
+ success: false,
155
+ result: null,
156
+ denialReason: result.permission.reason,
157
+ durationMs: result.durationMs,
158
+ };
159
+ }
160
+ if (result.error) {
161
+ return {
162
+ success: false,
163
+ result: null,
164
+ error: result.error.message,
165
+ durationMs: result.durationMs,
166
+ };
167
+ }
168
+ return {
169
+ success: true,
170
+ result: result.result,
171
+ durationMs: result.durationMs,
172
+ };
173
+ }
174
+ }
175
+ //# sourceMappingURL=jira-sync-wrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jira-sync-wrapper.js","sourceRoot":"","sources":["../../../src/sync/jira-sync-wrapper.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Feature ID Derivation Utility
3
+ *
4
+ * Derives feature ID from increment ID using the 1:1 mapping principle:
5
+ * - Each increment maps to exactly one feature folder
6
+ * - Feature ID = FS-{increment_number}
7
+ *
8
+ * @example
9
+ * deriveFeatureId('0081-ado-repo-cloning') → 'FS-081'
10
+ * deriveFeatureId('0100-some-feature') → 'FS-100'
11
+ * deriveFeatureId('1000-future-feature') → 'FS-1000'
12
+ *
13
+ * @see ADR-0140 for the decision rationale
14
+ */
15
+ /**
16
+ * Derive feature ID from increment ID
17
+ *
18
+ * The feature ID is derived directly from the increment number:
19
+ * - Extract leading digits from increment ID
20
+ * - Format as FS-XXX (minimum 3 digits, more if needed)
21
+ *
22
+ * This eliminates the need to store feature_id in metadata.json,
23
+ * as it's 100% derivable from the increment ID.
24
+ *
25
+ * @param incrementId - Increment ID (e.g., "0081-ado-repo-cloning")
26
+ * @returns Feature ID (e.g., "FS-081")
27
+ * @throws Error if increment ID format is invalid
28
+ */
29
+ export declare function deriveFeatureId(incrementId: string): string;
30
+ /**
31
+ * Extract increment number from increment ID
32
+ *
33
+ * @param incrementId - Increment ID (e.g., "0081-ado-repo-cloning")
34
+ * @returns Increment number (e.g., 81)
35
+ * @throws Error if increment ID format is invalid
36
+ */
37
+ export declare function extractIncrementNumber(incrementId: string): number;
38
+ /**
39
+ * Validate feature ID format
40
+ *
41
+ * Valid formats:
42
+ * - FS-XXX (internal, 3+ digits)
43
+ * - FS-XXXE (external, 3+ digits with E suffix)
44
+ *
45
+ * @param featureId - Feature ID to validate
46
+ * @returns true if valid, false otherwise
47
+ */
48
+ export declare function isValidFeatureId(featureId: string): boolean;
49
+ /**
50
+ * Check if feature ID is external (imported)
51
+ *
52
+ * External features have an 'E' suffix: FS-042E
53
+ *
54
+ * @param featureId - Feature ID to check
55
+ * @returns true if external, false if internal
56
+ */
57
+ export declare function isExternalFeatureId(featureId: string): boolean;
58
+ //# sourceMappingURL=feature-id-derivation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feature-id-derivation.d.ts","sourceRoot":"","sources":["../../../src/utils/feature-id-derivation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAU3D;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAMlE;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE9D"}