specweave 0.26.3 → 0.26.5

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 (64) hide show
  1. package/CLAUDE.md +261 -1382
  2. package/dist/src/cli/commands/plan/plan-orchestrator.js +2 -2
  3. package/dist/src/cli/commands/plan/plan-orchestrator.js.map +1 -1
  4. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.d.ts.map +1 -1
  5. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js +147 -55
  6. package/dist/src/cli/helpers/issue-tracker/github-multi-repo.js.map +1 -1
  7. package/dist/src/config/types.d.ts +203 -1208
  8. package/dist/src/config/types.d.ts.map +1 -1
  9. package/dist/src/core/increment/completion-validator.d.ts +4 -0
  10. package/dist/src/core/increment/completion-validator.d.ts.map +1 -1
  11. package/dist/src/core/increment/completion-validator.js +36 -0
  12. package/dist/src/core/increment/completion-validator.js.map +1 -1
  13. package/dist/src/core/increment/increment-reopener.d.ts.map +1 -1
  14. package/dist/src/core/increment/increment-reopener.js +13 -14
  15. package/dist/src/core/increment/increment-reopener.js.map +1 -1
  16. package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
  17. package/dist/src/core/increment/metadata-manager.js +16 -0
  18. package/dist/src/core/increment/metadata-manager.js.map +1 -1
  19. package/dist/src/core/increment/status-change-sync-trigger.d.ts +85 -0
  20. package/dist/src/core/increment/status-change-sync-trigger.d.ts.map +1 -0
  21. package/dist/src/core/increment/status-change-sync-trigger.js +137 -0
  22. package/dist/src/core/increment/status-change-sync-trigger.js.map +1 -0
  23. package/dist/src/core/increment/sync-circuit-breaker.d.ts +64 -0
  24. package/dist/src/core/increment/sync-circuit-breaker.d.ts.map +1 -0
  25. package/dist/src/core/increment/sync-circuit-breaker.js +95 -0
  26. package/dist/src/core/increment/sync-circuit-breaker.js.map +1 -0
  27. package/dist/src/core/living-docs/living-docs-sync.d.ts +12 -0
  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 +161 -31
  30. package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
  31. package/dist/src/core/us-sync-throttle.d.ts +113 -0
  32. package/dist/src/core/us-sync-throttle.d.ts.map +1 -0
  33. package/dist/src/core/us-sync-throttle.js +195 -0
  34. package/dist/src/core/us-sync-throttle.js.map +1 -0
  35. package/dist/src/init/architecture/types.d.ts +33 -140
  36. package/dist/src/init/architecture/types.d.ts.map +1 -1
  37. package/dist/src/init/compliance/types.d.ts +30 -27
  38. package/dist/src/init/compliance/types.d.ts.map +1 -1
  39. package/dist/src/init/repo/types.d.ts +11 -34
  40. package/dist/src/init/repo/types.d.ts.map +1 -1
  41. package/dist/src/init/research/src/config/types.d.ts +15 -82
  42. package/dist/src/init/research/src/config/types.d.ts.map +1 -1
  43. package/dist/src/init/research/types.d.ts +38 -93
  44. package/dist/src/init/research/types.d.ts.map +1 -1
  45. package/dist/src/init/team/types.d.ts +4 -42
  46. package/dist/src/init/team/types.d.ts.map +1 -1
  47. package/dist/src/utils/external-tool-drift-detector.d.ts +76 -0
  48. package/dist/src/utils/external-tool-drift-detector.d.ts.map +1 -0
  49. package/dist/src/utils/external-tool-drift-detector.js +235 -0
  50. package/dist/src/utils/external-tool-drift-detector.js.map +1 -0
  51. package/package.json +5 -5
  52. package/plugins/specweave/hooks/post-task-completion.sh +6 -6
  53. package/plugins/specweave/hooks/pre-increment-start.sh +6 -1
  54. package/plugins/specweave/lib/hooks/us-completion-orchestrator.js +62 -89
  55. package/plugins/specweave/lib/hooks/us-completion-orchestrator.ts +215 -0
  56. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js +16 -0
  57. package/plugins/specweave/lib/vendor/core/increment/metadata-manager.js.map +1 -1
  58. package/plugins/specweave/skills/brownfield-analyzer/SKILL.md +267 -868
  59. package/plugins/specweave/skills/increment-planner/SKILL.md +337 -1253
  60. package/plugins/specweave/skills/role-orchestrator/SKILL.md +293 -969
  61. package/plugins/specweave-docs/skills/technical-writing/SKILL.md +333 -839
  62. package/plugins/specweave-release/hooks/post-task-completion.sh +5 -1
  63. package/plugins/specweave-testing/skills/tdd-expert/SKILL.md +269 -749
  64. package/plugins/specweave-testing/skills/unit-testing-expert/SKILL.md +318 -810
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/init/repo/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AACH,MAAM,MAAM,aAAa,GACrB,KAAK,GACL,QAAQ,GACR,OAAO,GACP,SAAS,GACT,UAAU,GACV,QAAQ,CAAC;AAEb;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,qBAAqB;IACrB,IAAI,EAAE,aAAa,CAAC;IAEpB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,yBAAyB;IACzB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;IAEb,0BAA0B;IAC1B,GAAG,EAAE,MAAM,CAAC;IAEZ,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IAEd,6BAA6B;IAC7B,WAAW,EAAE,MAAM,CAAC;IAEpB,uBAAuB;IACvB,QAAQ,EAAE,MAAM,CAAC;IAEjB,iBAAiB;IACjB,KAAK,EAAE,MAAM,CAAC;IAEd,6BAA6B;IAC7B,WAAW,EAAE,IAAI,CAAC;IAElB,iBAAiB;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,qBAAqB;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;EAKxC,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUnC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/init/repo/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AACH,MAAM,MAAM,aAAa,GACrB,KAAK,GACL,QAAQ,GACR,OAAO,GACP,SAAS,GACT,UAAU,GACV,QAAQ,CAAC;AAEb;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,qBAAqB;IACrB,IAAI,EAAE,aAAa,CAAC;IAEpB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,qBAAqB;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,yBAAyB;IACzB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;IAEb,0BAA0B;IAC1B,GAAG,EAAE,MAAM,CAAC;IAEZ,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IAEd,6BAA6B;IAC7B,WAAW,EAAE,MAAM,CAAC;IAEpB,uBAAuB;IACvB,QAAQ,EAAE,MAAM,CAAC;IAEjB,iBAAiB;IACjB,KAAK,EAAE,MAAM,CAAC;IAEd,6BAA6B;IAC7B,WAAW,EAAE,IAAI,CAAC;IAElB,iBAAiB;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,qBAAqB;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,eAAO,MAAM,6BAA6B;;;;;;;;;;;;iBAKxC,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;;;iBAUnC,CAAC"}
@@ -4,99 +4,32 @@
4
4
  import { z } from 'zod';
5
5
  export declare const ResearchConfigSchema: z.ZodOptional<z.ZodObject<{
6
6
  vision: z.ZodOptional<z.ZodAny>;
7
- compliance: z.ZodOptional<z.ZodArray<z.ZodAny, "many">>;
8
- teams: z.ZodOptional<z.ZodArray<z.ZodAny, "many">>;
9
- repositories: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
7
+ compliance: z.ZodOptional<z.ZodArray<z.ZodAny>>;
8
+ teams: z.ZodOptional<z.ZodArray<z.ZodAny>>;
9
+ repositories: z.ZodOptional<z.ZodArray<z.ZodString>>;
10
10
  architecture: z.ZodOptional<z.ZodAny>;
11
- }, "strip", z.ZodTypeAny, {
12
- architecture?: any;
13
- repositories?: string[];
14
- teams?: any[];
15
- vision?: any;
16
- compliance?: any[];
17
- }, {
18
- architecture?: any;
19
- repositories?: string[];
20
- teams?: any[];
21
- vision?: any;
22
- compliance?: any[];
23
- }>>;
11
+ }, z.core.$strip>>;
24
12
  export declare const SpecWeaveConfigSchema: z.ZodObject<{
25
13
  version: z.ZodString;
26
14
  project: z.ZodObject<{
27
15
  name: z.ZodString;
28
- type: z.ZodEnum<["single", "multi"]>;
29
- }, "strip", z.ZodTypeAny, {
30
- name?: string;
31
- type?: "single" | "multi";
32
- }, {
33
- name?: string;
34
- type?: "single" | "multi";
35
- }>;
16
+ type: z.ZodEnum<{
17
+ single: "single";
18
+ multi: "multi";
19
+ }>;
20
+ }, z.core.$strip>;
36
21
  research: z.ZodOptional<z.ZodObject<{
37
22
  vision: z.ZodOptional<z.ZodAny>;
38
- compliance: z.ZodOptional<z.ZodArray<z.ZodAny, "many">>;
39
- teams: z.ZodOptional<z.ZodArray<z.ZodAny, "many">>;
40
- repositories: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
23
+ compliance: z.ZodOptional<z.ZodArray<z.ZodAny>>;
24
+ teams: z.ZodOptional<z.ZodArray<z.ZodAny>>;
25
+ repositories: z.ZodOptional<z.ZodArray<z.ZodString>>;
41
26
  architecture: z.ZodOptional<z.ZodAny>;
42
- }, "strip", z.ZodTypeAny, {
43
- architecture?: any;
44
- repositories?: string[];
45
- teams?: any[];
46
- vision?: any;
47
- compliance?: any[];
48
- }, {
49
- architecture?: any;
50
- repositories?: string[];
51
- teams?: any[];
52
- vision?: any;
53
- compliance?: any[];
54
- }>>;
27
+ }, z.core.$strip>>;
55
28
  livingDocs: z.ZodOptional<z.ZodObject<{
56
29
  enabled: z.ZodBoolean;
57
30
  baseDir: z.ZodString;
58
- }, "strip", z.ZodTypeAny, {
59
- enabled?: boolean;
60
- baseDir?: string;
61
- }, {
62
- enabled?: boolean;
63
- baseDir?: string;
64
- }>>;
65
- }, "strip", z.ZodTypeAny, {
66
- version?: string;
67
- project?: {
68
- name?: string;
69
- type?: "single" | "multi";
70
- };
71
- livingDocs?: {
72
- enabled?: boolean;
73
- baseDir?: string;
74
- };
75
- research?: {
76
- architecture?: any;
77
- repositories?: string[];
78
- teams?: any[];
79
- vision?: any;
80
- compliance?: any[];
81
- };
82
- }, {
83
- version?: string;
84
- project?: {
85
- name?: string;
86
- type?: "single" | "multi";
87
- };
88
- livingDocs?: {
89
- enabled?: boolean;
90
- baseDir?: string;
91
- };
92
- research?: {
93
- architecture?: any;
94
- repositories?: string[];
95
- teams?: any[];
96
- vision?: any;
97
- compliance?: any[];
98
- };
99
- }>;
31
+ }, z.core.$strip>>;
32
+ }, z.core.$strip>;
100
33
  export type ResearchConfig = z.infer<typeof ResearchConfigSchema>;
101
34
  export type SpecWeaveConfig = z.infer<typeof SpecWeaveConfigSchema>;
102
35
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../src/init/research/src/config/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;GAMpB,CAAC;AAEd,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWhC,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAClE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../src/init/research/src/config/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,oBAAoB;;;;;;kBAMpB,CAAC;AAEd,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;iBAWhC,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAClE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
@@ -65,119 +65,64 @@ export interface VisionInsights {
65
65
  * Zod schema for VisionInsights validation
66
66
  */
67
67
  export declare const VisionInsightsSchema: z.ZodObject<{
68
- keywords: z.ZodArray<z.ZodString, "many">;
69
- market: z.ZodEnum<["productivity-saas", "healthcare", "fintech", "e-commerce", "education", "gaming", "social-network", "enterprise-b2b", "consumer-b2c", "marketplace", "iot", "blockchain", "ai-ml", "unknown"]>;
68
+ keywords: z.ZodArray<z.ZodString>;
69
+ market: z.ZodEnum<{
70
+ unknown: "unknown";
71
+ marketplace: "marketplace";
72
+ "productivity-saas": "productivity-saas";
73
+ healthcare: "healthcare";
74
+ fintech: "fintech";
75
+ "e-commerce": "e-commerce";
76
+ education: "education";
77
+ gaming: "gaming";
78
+ "social-network": "social-network";
79
+ "enterprise-b2b": "enterprise-b2b";
80
+ "consumer-b2c": "consumer-b2c";
81
+ iot: "iot";
82
+ blockchain: "blockchain";
83
+ "ai-ml": "ai-ml";
84
+ }>;
70
85
  competitors: z.ZodArray<z.ZodObject<{
71
86
  name: z.ZodString;
72
87
  url: z.ZodOptional<z.ZodString>;
73
- strengths: z.ZodArray<z.ZodString, "many">;
74
- weaknesses: z.ZodArray<z.ZodString, "many">;
75
- }, "strip", z.ZodTypeAny, {
76
- name?: string;
77
- url?: string;
78
- strengths?: string[];
79
- weaknesses?: string[];
80
- }, {
81
- name?: string;
82
- url?: string;
83
- strengths?: string[];
84
- weaknesses?: string[];
85
- }>, "many">;
88
+ strengths: z.ZodArray<z.ZodString>;
89
+ weaknesses: z.ZodArray<z.ZodString>;
90
+ }, z.core.$strip>>;
86
91
  opportunityScore: z.ZodNumber;
87
92
  viralPotential: z.ZodBoolean;
88
93
  followUpQuestions: z.ZodArray<z.ZodObject<{
89
94
  question: z.ZodString;
90
- type: z.ZodEnum<["open", "multiple-choice", "scale"]>;
91
- options: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
95
+ type: z.ZodEnum<{
96
+ open: "open";
97
+ "multiple-choice": "multiple-choice";
98
+ scale: "scale";
99
+ }>;
100
+ options: z.ZodOptional<z.ZodArray<z.ZodString>>;
92
101
  rationale: z.ZodString;
93
- }, "strip", z.ZodTypeAny, {
94
- type?: "open" | "multiple-choice" | "scale";
95
- options?: string[];
96
- question?: string;
97
- rationale?: string;
98
- }, {
99
- type?: "open" | "multiple-choice" | "scale";
100
- options?: string[];
101
- question?: string;
102
- rationale?: string;
103
- }>, "many">;
102
+ }, z.core.$strip>>;
104
103
  confidence: z.ZodOptional<z.ZodNumber>;
105
104
  rawVision: z.ZodString;
106
- }, "strip", z.ZodTypeAny, {
107
- keywords?: string[];
108
- confidence?: number;
109
- market?: "unknown" | "marketplace" | "productivity-saas" | "healthcare" | "fintech" | "e-commerce" | "education" | "gaming" | "social-network" | "enterprise-b2b" | "consumer-b2c" | "iot" | "blockchain" | "ai-ml";
110
- competitors?: {
111
- name?: string;
112
- url?: string;
113
- strengths?: string[];
114
- weaknesses?: string[];
115
- }[];
116
- opportunityScore?: number;
117
- viralPotential?: boolean;
118
- followUpQuestions?: {
119
- type?: "open" | "multiple-choice" | "scale";
120
- options?: string[];
121
- question?: string;
122
- rationale?: string;
123
- }[];
124
- rawVision?: string;
125
- }, {
126
- keywords?: string[];
127
- confidence?: number;
128
- market?: "unknown" | "marketplace" | "productivity-saas" | "healthcare" | "fintech" | "e-commerce" | "education" | "gaming" | "social-network" | "enterprise-b2b" | "consumer-b2c" | "iot" | "blockchain" | "ai-ml";
129
- competitors?: {
130
- name?: string;
131
- url?: string;
132
- strengths?: string[];
133
- weaknesses?: string[];
134
- }[];
135
- opportunityScore?: number;
136
- viralPotential?: boolean;
137
- followUpQuestions?: {
138
- type?: "open" | "multiple-choice" | "scale";
139
- options?: string[];
140
- question?: string;
141
- rationale?: string;
142
- }[];
143
- rawVision?: string;
144
- }>;
105
+ }, z.core.$strip>;
145
106
  /**
146
107
  * Zod schema for Competitor validation
147
108
  */
148
109
  export declare const CompetitorSchema: z.ZodObject<{
149
110
  name: z.ZodString;
150
111
  url: z.ZodOptional<z.ZodString>;
151
- strengths: z.ZodArray<z.ZodString, "many">;
152
- weaknesses: z.ZodArray<z.ZodString, "many">;
153
- }, "strip", z.ZodTypeAny, {
154
- name?: string;
155
- url?: string;
156
- strengths?: string[];
157
- weaknesses?: string[];
158
- }, {
159
- name?: string;
160
- url?: string;
161
- strengths?: string[];
162
- weaknesses?: string[];
163
- }>;
112
+ strengths: z.ZodArray<z.ZodString>;
113
+ weaknesses: z.ZodArray<z.ZodString>;
114
+ }, z.core.$strip>;
164
115
  /**
165
116
  * Zod schema for Question validation
166
117
  */
167
118
  export declare const QuestionSchema: z.ZodObject<{
168
119
  question: z.ZodString;
169
- type: z.ZodEnum<["open", "multiple-choice", "scale"]>;
170
- options: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
120
+ type: z.ZodEnum<{
121
+ open: "open";
122
+ "multiple-choice": "multiple-choice";
123
+ scale: "scale";
124
+ }>;
125
+ options: z.ZodOptional<z.ZodArray<z.ZodString>>;
171
126
  rationale: z.ZodString;
172
- }, "strip", z.ZodTypeAny, {
173
- type?: "open" | "multiple-choice" | "scale";
174
- options?: string[];
175
- question?: string;
176
- rationale?: string;
177
- }, {
178
- type?: "open" | "multiple-choice" | "scale";
179
- options?: string[];
180
- question?: string;
181
- rationale?: string;
182
- }>;
127
+ }, z.core.$strip>;
183
128
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/init/research/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,MAAM,MAAM,cAAc,GACtB,mBAAmB,GACnB,YAAY,GACZ,SAAS,GACT,YAAY,GACZ,WAAW,GACX,QAAQ,GACR,gBAAgB,GAChB,gBAAgB,GAChB,cAAc,GACd,aAAa,GACb,KAAK,GACL,YAAY,GACZ,OAAO,GACP,SAAS,CAAC;AAEd;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IAEb,6BAA6B;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,sCAAsC;IACtC,SAAS,EAAE,MAAM,EAAE,CAAC;IAEpB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,oBAAoB;IACpB,QAAQ,EAAE,MAAM,CAAC;IAEjB,wDAAwD;IACxD,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,OAAO,CAAC;IAE3C,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,EAAE,CAAC;IAEnB,+BAA+B;IAC/B,MAAM,EAAE,cAAc,CAAC;IAEvB,4CAA4C;IAC5C,WAAW,EAAE,UAAU,EAAE,CAAC;IAE1B,4CAA4C;IAC5C,gBAAgB,EAAE,MAAM,CAAC;IAEzB,iDAAiD;IACjD,cAAc,EAAE,OAAO,CAAC;IAExB,mDAAmD;IACnD,iBAAiB,EAAE,QAAQ,EAAE,CAAC;IAE9B,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsC/B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;EAK3B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;EAKzB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/init/research/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,MAAM,MAAM,cAAc,GACtB,mBAAmB,GACnB,YAAY,GACZ,SAAS,GACT,YAAY,GACZ,WAAW,GACX,QAAQ,GACR,gBAAgB,GAChB,gBAAgB,GAChB,cAAc,GACd,aAAa,GACb,KAAK,GACL,YAAY,GACZ,OAAO,GACP,SAAS,CAAC;AAEd;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IAEb,6BAA6B;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb,sCAAsC;IACtC,SAAS,EAAE,MAAM,EAAE,CAAC;IAEpB,6BAA6B;IAC7B,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,oBAAoB;IACpB,QAAQ,EAAE,MAAM,CAAC;IAEjB,wDAAwD;IACxD,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,OAAO,CAAC;IAE3C,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,0DAA0D;IAC1D,QAAQ,EAAE,MAAM,EAAE,CAAC;IAEnB,+BAA+B;IAC/B,MAAM,EAAE,cAAc,CAAC;IAEvB,4CAA4C;IAC5C,WAAW,EAAE,UAAU,EAAE,CAAC;IAE1B,4CAA4C;IAC5C,gBAAgB,EAAE,MAAM,CAAC;IAEzB,iDAAiD;IACjD,cAAc,EAAE,OAAO,CAAC;IAExB,mDAAmD;IACnD,iBAAiB,EAAE,QAAQ,EAAE,CAAC;IAE9B,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,uCAAuC;IACvC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsC/B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;;iBAK3B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,cAAc;;;;;;;;;iBAKzB,CAAC"}
@@ -45,51 +45,13 @@ export declare const TeamRecommendationSchema: z.ZodObject<{
45
45
  required: z.ZodBoolean;
46
46
  reason: z.ZodString;
47
47
  size: z.ZodString;
48
- skills: z.ZodArray<z.ZodString, "many">;
48
+ skills: z.ZodArray<z.ZodString>;
49
49
  serverlessAlternative: z.ZodOptional<z.ZodObject<{
50
50
  service: z.ZodString;
51
51
  costSavings: z.ZodNumber;
52
- tradeoffs: z.ZodArray<z.ZodString, "many">;
52
+ tradeoffs: z.ZodArray<z.ZodString>;
53
53
  pricingModel: z.ZodOptional<z.ZodString>;
54
- }, "strip", z.ZodTypeAny, {
55
- service?: string;
56
- costSavings?: number;
57
- tradeoffs?: string[];
58
- pricingModel?: string;
59
- }, {
60
- service?: string;
61
- costSavings?: number;
62
- tradeoffs?: string[];
63
- pricingModel?: string;
64
- }>>;
54
+ }, z.core.$strip>>;
65
55
  priority: z.ZodOptional<z.ZodNumber>;
66
- }, "strip", z.ZodTypeAny, {
67
- skills?: string[];
68
- reason?: string;
69
- required?: boolean;
70
- priority?: number;
71
- teamName?: string;
72
- role?: string;
73
- size?: string;
74
- serverlessAlternative?: {
75
- service?: string;
76
- costSavings?: number;
77
- tradeoffs?: string[];
78
- pricingModel?: string;
79
- };
80
- }, {
81
- skills?: string[];
82
- reason?: string;
83
- required?: boolean;
84
- priority?: number;
85
- teamName?: string;
86
- role?: string;
87
- size?: string;
88
- serverlessAlternative?: {
89
- service?: string;
90
- costSavings?: number;
91
- tradeoffs?: string[];
92
- pricingModel?: string;
93
- };
94
- }>;
56
+ }, z.core.$strip>;
95
57
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/init/team/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAEhB,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;IAEpB,oCAAoC;IACpC,SAAS,EAAE,MAAM,EAAE,CAAC;IAEpB,oBAAoB;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAC;IAEjB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IAEb,+DAA+D;IAC/D,QAAQ,EAAE,OAAO,CAAC;IAElB,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IAEf,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IAEb,oCAAoC;IACpC,MAAM,EAAE,MAAM,EAAE,CAAC;IAEjB,6CAA6C;IAC7C,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;IAE9C,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAcnC,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/init/team/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,mDAAmD;IACnD,OAAO,EAAE,MAAM,CAAC;IAEhB,gDAAgD;IAChD,WAAW,EAAE,MAAM,CAAC;IAEpB,oCAAoC;IACpC,SAAS,EAAE,MAAM,EAAE,CAAC;IAEpB,oBAAoB;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAC;IAEjB,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IAEb,+DAA+D;IAC/D,QAAQ,EAAE,OAAO,CAAC;IAElB,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IAEf,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IAEb,oCAAoC;IACpC,MAAM,EAAE,MAAM,EAAE,CAAC;IAEjB,6CAA6C;IAC7C,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;IAE9C,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;iBAcnC,CAAC"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * External Tool Drift Detector
3
+ *
4
+ * Detects when external tools (GitHub/JIRA/ADO) haven't been synced recently,
5
+ * indicating potential drift between living docs and external systems.
6
+ *
7
+ * Used by:
8
+ * - /specweave:done (validation warning if drift > 24h)
9
+ * - Status line (show "sync pending" indicator)
10
+ * - /specweave:progress (show last sync time)
11
+ *
12
+ * See: ADR-0131 (External Tool Sync Context Detection)
13
+ */
14
+ import { Logger } from './logger.js';
15
+ export interface DriftStatus {
16
+ hasDrift: boolean;
17
+ lastSyncTime: Date | null;
18
+ hoursSinceSync: number | null;
19
+ externalToolsConfigured: boolean;
20
+ recommendation: string;
21
+ error?: string;
22
+ }
23
+ export declare class ExternalToolDriftDetector {
24
+ private projectRoot;
25
+ private logger;
26
+ constructor(projectRoot: string, options?: {
27
+ logger?: Logger;
28
+ });
29
+ /**
30
+ * Validate increment ID format to prevent path traversal attacks
31
+ * P0-1: Path traversal vulnerability fix
32
+ *
33
+ * @param incrementId - Increment ID to validate
34
+ * @throws Error if increment ID format is invalid
35
+ */
36
+ private validateIncrementId;
37
+ /**
38
+ * Safely parse and validate metadata.json
39
+ * P0-2: JSON injection protection
40
+ *
41
+ * @param metadataPath - Path to metadata.json
42
+ * @returns Parsed metadata object
43
+ * @throws Error if JSON is invalid or malformed
44
+ */
45
+ private readAndValidateMetadata;
46
+ /**
47
+ * Get most recent sync time across ALL external tools
48
+ * P1-1: Check all tools instead of just first configured tool
49
+ *
50
+ * @param metadata - Parsed metadata.json
51
+ * @returns Most recent sync date or null if no syncs found
52
+ */
53
+ private getMostRecentSync;
54
+ /**
55
+ * Safely parse date string with validation
56
+ *
57
+ * @param dateString - ISO date string
58
+ * @returns Date object or null if invalid
59
+ */
60
+ private parseSafeDate;
61
+ /**
62
+ * Detect drift for a specific increment
63
+ *
64
+ * @param incrementId - Increment ID (e.g., "0053-safe-feature-deletion")
65
+ * @returns Drift status with recommendations
66
+ */
67
+ detectDrift(incrementId: string): Promise<DriftStatus>;
68
+ /**
69
+ * Get human-readable drift message for display
70
+ *
71
+ * @param drift - Drift status from detectDrift()
72
+ * @returns Formatted message with emoji indicator
73
+ */
74
+ getDriftMessage(drift: DriftStatus): string;
75
+ }
76
+ //# sourceMappingURL=external-tool-drift-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"external-tool-drift-detector.d.ts","sourceRoot":"","sources":["../../../src/utils/external-tool-drift-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,EAAE,MAAM,EAAiB,MAAM,aAAa,CAAC;AAOpD,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,uBAAuB,EAAE,OAAO,CAAC;IACjC,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,yBAAyB;IACpC,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,OAAO,CAAC,mBAAmB;IAY3B;;;;;;;OAOG;YACW,uBAAuB;IAkBrC;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IA2BzB;;;;;OAKG;IACH,OAAO,CAAC,aAAa;IAcrB;;;;;OAKG;IACG,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAsG5D;;;;;OAKG;IACH,eAAe,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM;CAqB5C"}
@@ -0,0 +1,235 @@
1
+ /**
2
+ * External Tool Drift Detector
3
+ *
4
+ * Detects when external tools (GitHub/JIRA/ADO) haven't been synced recently,
5
+ * indicating potential drift between living docs and external systems.
6
+ *
7
+ * Used by:
8
+ * - /specweave:done (validation warning if drift > 24h)
9
+ * - Status line (show "sync pending" indicator)
10
+ * - /specweave:progress (show last sync time)
11
+ *
12
+ * See: ADR-0131 (External Tool Sync Context Detection)
13
+ */
14
+ import * as path from 'path';
15
+ import { promises as fs } from 'fs';
16
+ import { consoleLogger } from './logger.js';
17
+ // Drift thresholds (extracted constants for P0-5 fix)
18
+ const DRIFT_THRESHOLD_HOURS = 24; // Warning threshold
19
+ const WARNING_THRESHOLD_HOURS = 48; // Elevated warning
20
+ const CRITICAL_THRESHOLD_HOURS = 168; // 7 days - critical staleness
21
+ export class ExternalToolDriftDetector {
22
+ constructor(projectRoot, options = {}) {
23
+ this.projectRoot = projectRoot;
24
+ this.logger = options.logger ?? consoleLogger;
25
+ }
26
+ /**
27
+ * Validate increment ID format to prevent path traversal attacks
28
+ * P0-1: Path traversal vulnerability fix
29
+ *
30
+ * @param incrementId - Increment ID to validate
31
+ * @throws Error if increment ID format is invalid
32
+ */
33
+ validateIncrementId(incrementId) {
34
+ // Expected format: 4 digits + hyphen + kebab-case (e.g., "0053-safe-feature-deletion")
35
+ if (!/^\d{4}-[a-z0-9-]+$/.test(incrementId)) {
36
+ throw new Error(`Invalid increment ID format: ${incrementId}. Expected format: XXXX-kebab-case`);
37
+ }
38
+ // Additional safety: reject path traversal attempts
39
+ if (incrementId.includes('..') || incrementId.includes('/') || incrementId.includes('\\')) {
40
+ throw new Error(`Path traversal attempt detected in increment ID: ${incrementId}`);
41
+ }
42
+ }
43
+ /**
44
+ * Safely parse and validate metadata.json
45
+ * P0-2: JSON injection protection
46
+ *
47
+ * @param metadataPath - Path to metadata.json
48
+ * @returns Parsed metadata object
49
+ * @throws Error if JSON is invalid or malformed
50
+ */
51
+ async readAndValidateMetadata(metadataPath) {
52
+ const content = await fs.readFile(metadataPath, 'utf-8');
53
+ let metadata;
54
+ try {
55
+ metadata = JSON.parse(content);
56
+ }
57
+ catch (error) {
58
+ throw new Error(`Invalid JSON in metadata file: ${error instanceof Error ? error.message : 'Parse error'}`);
59
+ }
60
+ // Validate JSON structure
61
+ if (typeof metadata !== 'object' || metadata === null) {
62
+ throw new Error('Metadata must be a valid JSON object');
63
+ }
64
+ return metadata;
65
+ }
66
+ /**
67
+ * Get most recent sync time across ALL external tools
68
+ * P1-1: Check all tools instead of just first configured tool
69
+ *
70
+ * @param metadata - Parsed metadata.json
71
+ * @returns Most recent sync date or null if no syncs found
72
+ */
73
+ getMostRecentSync(metadata) {
74
+ const syncTimes = [];
75
+ // Collect all sync times from configured external tools
76
+ if (metadata.github?.lastSync) {
77
+ const date = this.parseSafeDate(metadata.github.lastSync);
78
+ if (date)
79
+ syncTimes.push(date);
80
+ }
81
+ if (metadata.jira?.lastSync) {
82
+ const date = this.parseSafeDate(metadata.jira.lastSync);
83
+ if (date)
84
+ syncTimes.push(date);
85
+ }
86
+ if (metadata.ado?.lastSync) {
87
+ const date = this.parseSafeDate(metadata.ado.lastSync);
88
+ if (date)
89
+ syncTimes.push(date);
90
+ }
91
+ if (syncTimes.length === 0) {
92
+ return null;
93
+ }
94
+ // Return most recent sync time (sort descending, take first)
95
+ return syncTimes.sort((a, b) => b.getTime() - a.getTime())[0];
96
+ }
97
+ /**
98
+ * Safely parse date string with validation
99
+ *
100
+ * @param dateString - ISO date string
101
+ * @returns Date object or null if invalid
102
+ */
103
+ parseSafeDate(dateString) {
104
+ if (typeof dateString !== 'string')
105
+ return null;
106
+ const date = new Date(dateString);
107
+ // Check if date is valid
108
+ if (isNaN(date.getTime())) {
109
+ this.logger.warn(`Invalid date format: ${dateString}`);
110
+ return null;
111
+ }
112
+ return date;
113
+ }
114
+ /**
115
+ * Detect drift for a specific increment
116
+ *
117
+ * @param incrementId - Increment ID (e.g., "0053-safe-feature-deletion")
118
+ * @returns Drift status with recommendations
119
+ */
120
+ async detectDrift(incrementId) {
121
+ try {
122
+ // P0-1: Validate increment ID to prevent path traversal
123
+ this.validateIncrementId(incrementId);
124
+ // P0-4: Use async fs operations (replaced existsSync/readFileSync)
125
+ const metadataPath = path.join(this.projectRoot, '.specweave/increments', incrementId, 'metadata.json');
126
+ // Check if metadata exists (async)
127
+ try {
128
+ await fs.access(metadataPath);
129
+ }
130
+ catch {
131
+ return {
132
+ hasDrift: false,
133
+ lastSyncTime: null,
134
+ hoursSinceSync: null,
135
+ externalToolsConfigured: false,
136
+ recommendation: 'No metadata found'
137
+ };
138
+ }
139
+ // P0-2: Safe JSON parsing with validation
140
+ const metadata = await this.readAndValidateMetadata(metadataPath);
141
+ // Check if external tools are configured
142
+ const hasGitHub = metadata.github && metadata.github.issues && metadata.github.issues.length > 0;
143
+ const hasJira = metadata.jira && metadata.jira.issues && metadata.jira.issues.length > 0;
144
+ const hasADO = metadata.ado && metadata.ado.workItems && metadata.ado.workItems.length > 0;
145
+ const externalToolsConfigured = hasGitHub || hasJira || hasADO;
146
+ if (!externalToolsConfigured) {
147
+ return {
148
+ hasDrift: false,
149
+ lastSyncTime: null,
150
+ hoursSinceSync: null,
151
+ externalToolsConfigured: false,
152
+ recommendation: 'No external tools configured'
153
+ };
154
+ }
155
+ // P1-1: Get most recent sync time across ALL external tools (not just first one)
156
+ const lastSyncTime = this.getMostRecentSync(metadata);
157
+ if (!lastSyncTime) {
158
+ return {
159
+ hasDrift: true,
160
+ lastSyncTime: null,
161
+ hoursSinceSync: null,
162
+ externalToolsConfigured: true,
163
+ recommendation: 'External tools never synced - run /specweave:sync-progress'
164
+ };
165
+ }
166
+ // Calculate hours since last sync
167
+ const now = new Date();
168
+ const hoursSinceSync = (now.getTime() - lastSyncTime.getTime()) / (1000 * 60 * 60);
169
+ // Use extracted constants (P0-5 fix)
170
+ const hasDrift = hoursSinceSync > DRIFT_THRESHOLD_HOURS;
171
+ let recommendation = '';
172
+ if (hasDrift) {
173
+ if (hoursSinceSync < WARNING_THRESHOLD_HOURS) {
174
+ recommendation = 'External tools synced 1-2 days ago - consider running /specweave:sync-progress';
175
+ }
176
+ else if (hoursSinceSync < CRITICAL_THRESHOLD_HOURS) {
177
+ recommendation = `External tools synced ${Math.floor(hoursSinceSync / 24)} days ago - run /specweave:sync-progress soon`;
178
+ }
179
+ else {
180
+ recommendation = `External tools synced ${Math.floor(hoursSinceSync / 168)} weeks ago - URGENT: run /specweave:sync-progress`;
181
+ }
182
+ }
183
+ else {
184
+ recommendation = `External tools synced ${Math.floor(hoursSinceSync)} hours ago - up to date`;
185
+ }
186
+ return {
187
+ hasDrift,
188
+ lastSyncTime,
189
+ hoursSinceSync,
190
+ externalToolsConfigured,
191
+ recommendation
192
+ };
193
+ }
194
+ catch (error) {
195
+ // P0-5: Don't mask errors - expose them in return value
196
+ this.logger.error(`Error detecting drift for ${incrementId}:`, error);
197
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
198
+ return {
199
+ hasDrift: false,
200
+ lastSyncTime: null,
201
+ hoursSinceSync: null,
202
+ externalToolsConfigured: false,
203
+ recommendation: `Error detecting drift: ${errorMessage}`,
204
+ error: errorMessage // Expose error for caller to handle
205
+ };
206
+ }
207
+ }
208
+ /**
209
+ * Get human-readable drift message for display
210
+ *
211
+ * @param drift - Drift status from detectDrift()
212
+ * @returns Formatted message with emoji indicator
213
+ */
214
+ getDriftMessage(drift) {
215
+ if (!drift.externalToolsConfigured) {
216
+ return '';
217
+ }
218
+ if (!drift.lastSyncTime) {
219
+ return '⚠️ External tools never synced';
220
+ }
221
+ if (drift.hasDrift) {
222
+ if (drift.hoursSinceSync && drift.hoursSinceSync < 48) {
223
+ return `⚠️ External sync: ${Math.floor(drift.hoursSinceSync)}h ago`;
224
+ }
225
+ else if (drift.hoursSinceSync && drift.hoursSinceSync < 168) {
226
+ return `🔴 External sync: ${Math.floor(drift.hoursSinceSync / 24)}d ago`;
227
+ }
228
+ else {
229
+ return `🔴 External sync: ${Math.floor((drift.hoursSinceSync || 0) / 168)}w ago (STALE)`;
230
+ }
231
+ }
232
+ return `✅ External sync: ${Math.floor(drift.hoursSinceSync || 0)}h ago`;
233
+ }
234
+ }
235
+ //# sourceMappingURL=external-tool-drift-detector.js.map