@mytechtoday/augment-extensions 1.3.0 → 1.4.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 (232) hide show
  1. package/LICENSE +22 -22
  2. package/README.md +105 -6
  3. package/augment-extensions/domain-rules/software-architecture/README.md +143 -143
  4. package/augment-extensions/domain-rules/software-architecture/examples/banking-layered.md +961 -961
  5. package/augment-extensions/domain-rules/software-architecture/examples/ecommerce-microservices.md +990 -990
  6. package/augment-extensions/domain-rules/software-architecture/examples/iot-eventdriven.md +882 -882
  7. package/augment-extensions/domain-rules/software-architecture/examples/monolith-to-microservices-migration.md +703 -703
  8. package/augment-extensions/domain-rules/software-architecture/examples/serverless-imageprocessing.md +957 -957
  9. package/augment-extensions/domain-rules/software-architecture/examples/trading-eventdriven.md +747 -747
  10. package/augment-extensions/domain-rules/software-architecture/module.json +119 -119
  11. package/augment-extensions/domain-rules/software-architecture/rules/challenges-solutions.md +763 -763
  12. package/augment-extensions/domain-rules/software-architecture/rules/definitions-terminology.md +409 -409
  13. package/augment-extensions/domain-rules/software-architecture/rules/design-principles.md +684 -684
  14. package/augment-extensions/domain-rules/software-architecture/rules/evaluation-testing.md +1381 -1381
  15. package/augment-extensions/domain-rules/software-architecture/rules/event-driven-architecture.md +616 -616
  16. package/augment-extensions/domain-rules/software-architecture/rules/fundamentals.md +306 -306
  17. package/augment-extensions/domain-rules/software-architecture/rules/industry-architectures.md +554 -554
  18. package/augment-extensions/domain-rules/software-architecture/rules/layered-architecture.md +776 -776
  19. package/augment-extensions/domain-rules/software-architecture/rules/microservices-architecture.md +503 -503
  20. package/augment-extensions/domain-rules/software-architecture/rules/modeling-documentation.md +1199 -1199
  21. package/augment-extensions/domain-rules/software-architecture/rules/monolithic-architecture.md +351 -351
  22. package/augment-extensions/domain-rules/software-architecture/rules/principles.md +556 -556
  23. package/augment-extensions/domain-rules/software-architecture/rules/quality-attributes.md +797 -797
  24. package/augment-extensions/domain-rules/software-architecture/rules/scalability-performance.md +1345 -1345
  25. package/augment-extensions/domain-rules/software-architecture/rules/security-architecture.md +1039 -1039
  26. package/augment-extensions/domain-rules/software-architecture/rules/serverless-architecture.md +711 -711
  27. package/augment-extensions/domain-rules/software-architecture/rules/skills-development.md +568 -568
  28. package/augment-extensions/domain-rules/software-architecture/rules/tools-methodologies.md +961 -961
  29. package/augment-extensions/visual-design/CHANGELOG.md +132 -132
  30. package/augment-extensions/visual-design/README.md +255 -255
  31. package/augment-extensions/visual-design/__tests__/README.md +119 -119
  32. package/augment-extensions/visual-design/__tests__/style-selector.test.ts +172 -172
  33. package/augment-extensions/visual-design/__tests__/vendor-styles.test.ts +214 -214
  34. package/augment-extensions/visual-design/domains/other/ai-prompt-helper.ts +157 -157
  35. package/augment-extensions/visual-design/domains/other/dotnet-application.ts +156 -156
  36. package/augment-extensions/visual-design/domains/other/linux-platform.ts +156 -156
  37. package/augment-extensions/visual-design/domains/other/mobile-application.ts +157 -157
  38. package/augment-extensions/visual-design/domains/other/motion-picture.ts +156 -156
  39. package/augment-extensions/visual-design/domains/other/os-application.ts +156 -156
  40. package/augment-extensions/visual-design/domains/other/print-campaigns.ts +158 -158
  41. package/augment-extensions/visual-design/domains/other/web-app.ts +157 -157
  42. package/augment-extensions/visual-design/domains/other/website.ts +161 -161
  43. package/augment-extensions/visual-design/domains/other/windows-platform.ts +156 -156
  44. package/augment-extensions/visual-design/domains/web-page-styles/amazon-cloudscape.ts +506 -506
  45. package/augment-extensions/visual-design/domains/web-page-styles/google-modern.ts +615 -615
  46. package/augment-extensions/visual-design/domains/web-page-styles/microsoft-fluent.ts +531 -531
  47. package/augment-extensions/visual-design/examples/README.md +97 -97
  48. package/augment-extensions/visual-design/examples/ai-prompt-generation.md +233 -233
  49. package/augment-extensions/visual-design/examples/basic-usage.md +216 -216
  50. package/augment-extensions/visual-design/examples/domain-workflows.md +257 -257
  51. package/augment-extensions/visual-design/examples/vendor-comparison.md +247 -247
  52. package/augment-extensions/visual-design/module.json +78 -78
  53. package/augment-extensions/visual-design/style-selector.ts +177 -177
  54. package/augment-extensions/visual-design/types.ts +302 -302
  55. package/augment-extensions/visual-design/visual-design-core.ts +469 -469
  56. package/augment-extensions/workflows/adr-support/README.md +227 -227
  57. package/augment-extensions/workflows/adr-support/__tests__/adr-validator.test.ts +203 -203
  58. package/augment-extensions/workflows/adr-support/adr-validator.ts +162 -162
  59. package/augment-extensions/workflows/adr-support/examples/complete-lifecycle-example.md +449 -449
  60. package/augment-extensions/workflows/adr-support/examples/integration-example.md +580 -580
  61. package/augment-extensions/workflows/adr-support/examples/superseding-example.md +436 -436
  62. package/augment-extensions/workflows/adr-support/module.json +112 -112
  63. package/augment-extensions/workflows/adr-support/rules/adr-creation.md +372 -372
  64. package/augment-extensions/workflows/adr-support/rules/beads-integration.md +443 -443
  65. package/augment-extensions/workflows/adr-support/rules/conflict-detection.md +486 -486
  66. package/augment-extensions/workflows/adr-support/rules/decision-detection.md +362 -362
  67. package/augment-extensions/workflows/adr-support/rules/lifecycle-management.md +427 -427
  68. package/augment-extensions/workflows/adr-support/rules/openspec-integration.md +465 -465
  69. package/augment-extensions/workflows/adr-support/rules/template-selection.md +405 -405
  70. package/augment-extensions/workflows/adr-support/rules/validation-rules.md +543 -543
  71. package/augment-extensions/workflows/adr-support/schemas/adr-config.json +191 -191
  72. package/augment-extensions/workflows/adr-support/schemas/adr-metadata.json +172 -172
  73. package/augment-extensions/workflows/adr-support/templates/business-case.md +235 -235
  74. package/augment-extensions/workflows/adr-support/templates/madr-elaborate.md +197 -197
  75. package/augment-extensions/workflows/adr-support/templates/madr-simple.md +68 -68
  76. package/augment-extensions/workflows/adr-support/templates/nygard.md +84 -84
  77. package/augment-extensions/writing-standards/screenplay/rules/file-organization.md +213 -213
  78. package/augment-extensions/writing-standards/screenplay/utils/__tests__/file-organization.test.ts +169 -169
  79. package/augment-extensions/writing-standards/screenplay/utils/file-organization.ts +165 -165
  80. package/cli/dist/commands/agent.d.ts +37 -0
  81. package/cli/dist/commands/agent.d.ts.map +1 -0
  82. package/cli/dist/commands/agent.js +222 -0
  83. package/cli/dist/commands/agent.js.map +1 -0
  84. package/cli/dist/commands/beads.d.ts +64 -0
  85. package/cli/dist/commands/beads.d.ts.map +1 -0
  86. package/cli/dist/commands/beads.js +377 -0
  87. package/cli/dist/commands/beads.js.map +1 -0
  88. package/cli/dist/commands/change.d.ts +54 -0
  89. package/cli/dist/commands/change.d.ts.map +1 -0
  90. package/cli/dist/commands/change.js +243 -0
  91. package/cli/dist/commands/change.js.map +1 -0
  92. package/cli/dist/commands/clean.d.ts +15 -0
  93. package/cli/dist/commands/clean.d.ts.map +1 -0
  94. package/cli/dist/commands/clean.js +63 -0
  95. package/cli/dist/commands/clean.js.map +1 -0
  96. package/cli/dist/commands/clone.d.ts +15 -0
  97. package/cli/dist/commands/clone.d.ts.map +1 -0
  98. package/cli/dist/commands/clone.js +49 -0
  99. package/cli/dist/commands/clone.js.map +1 -0
  100. package/cli/dist/commands/config.d.ts +33 -0
  101. package/cli/dist/commands/config.d.ts.map +1 -0
  102. package/cli/dist/commands/config.js +166 -0
  103. package/cli/dist/commands/config.js.map +1 -0
  104. package/cli/dist/commands/context.d.ts +38 -0
  105. package/cli/dist/commands/context.d.ts.map +1 -0
  106. package/cli/dist/commands/context.js +205 -0
  107. package/cli/dist/commands/context.js.map +1 -0
  108. package/cli/dist/commands/create.d.ts +18 -0
  109. package/cli/dist/commands/create.d.ts.map +1 -0
  110. package/cli/dist/commands/create.js +178 -0
  111. package/cli/dist/commands/create.js.map +1 -0
  112. package/cli/dist/commands/diff.d.ts +19 -0
  113. package/cli/dist/commands/diff.d.ts.map +1 -0
  114. package/cli/dist/commands/diff.js +104 -0
  115. package/cli/dist/commands/diff.js.map +1 -0
  116. package/cli/dist/commands/doctor.d.ts +14 -0
  117. package/cli/dist/commands/doctor.d.ts.map +1 -0
  118. package/cli/dist/commands/doctor.js +62 -0
  119. package/cli/dist/commands/doctor.js.map +1 -0
  120. package/cli/dist/commands/export.d.ts +28 -0
  121. package/cli/dist/commands/export.d.ts.map +1 -0
  122. package/cli/dist/commands/export.js +135 -0
  123. package/cli/dist/commands/export.js.map +1 -0
  124. package/cli/dist/commands/import.d.ts +23 -0
  125. package/cli/dist/commands/import.d.ts.map +1 -0
  126. package/cli/dist/commands/import.js +118 -0
  127. package/cli/dist/commands/import.js.map +1 -0
  128. package/cli/dist/commands/prompt.d.ts +45 -0
  129. package/cli/dist/commands/prompt.d.ts.map +1 -0
  130. package/cli/dist/commands/prompt.js +223 -0
  131. package/cli/dist/commands/prompt.js.map +1 -0
  132. package/cli/dist/commands/spec.d.ts +57 -0
  133. package/cli/dist/commands/spec.d.ts.map +1 -0
  134. package/cli/dist/commands/spec.js +279 -0
  135. package/cli/dist/commands/spec.js.map +1 -0
  136. package/cli/dist/commands/stats.d.ts +18 -0
  137. package/cli/dist/commands/stats.d.ts.map +1 -0
  138. package/cli/dist/commands/stats.js +85 -0
  139. package/cli/dist/commands/stats.js.map +1 -0
  140. package/cli/dist/commands/task.d.ts +65 -0
  141. package/cli/dist/commands/task.d.ts.map +1 -0
  142. package/cli/dist/commands/task.js +282 -0
  143. package/cli/dist/commands/task.js.map +1 -0
  144. package/cli/dist/commands/template.d.ts +17 -0
  145. package/cli/dist/commands/template.d.ts.map +1 -0
  146. package/cli/dist/commands/template.js +55 -0
  147. package/cli/dist/commands/template.js.map +1 -0
  148. package/cli/dist/utils/agent-config.d.ts +129 -0
  149. package/cli/dist/utils/agent-config.d.ts.map +1 -0
  150. package/cli/dist/utils/agent-config.js +297 -0
  151. package/cli/dist/utils/agent-config.js.map +1 -0
  152. package/cli/dist/utils/auto-sync.js +19 -19
  153. package/cli/dist/utils/beads-graph.d.ts +17 -0
  154. package/cli/dist/utils/beads-graph.d.ts.map +1 -0
  155. package/cli/dist/utils/beads-graph.js +150 -0
  156. package/cli/dist/utils/beads-graph.js.map +1 -0
  157. package/cli/dist/utils/beads-integration.d.ts +112 -0
  158. package/cli/dist/utils/beads-integration.d.ts.map +1 -0
  159. package/cli/dist/utils/beads-integration.js +312 -0
  160. package/cli/dist/utils/beads-integration.js.map +1 -0
  161. package/cli/dist/utils/beads-reporter.d.ts +17 -0
  162. package/cli/dist/utils/beads-reporter.d.ts.map +1 -0
  163. package/cli/dist/utils/beads-reporter.js +160 -0
  164. package/cli/dist/utils/beads-reporter.js.map +1 -0
  165. package/cli/dist/utils/cache-manager.d.ts +55 -0
  166. package/cli/dist/utils/cache-manager.d.ts.map +1 -0
  167. package/cli/dist/utils/cache-manager.js +150 -0
  168. package/cli/dist/utils/cache-manager.js.map +1 -0
  169. package/cli/dist/utils/change-manager.d.ts +70 -0
  170. package/cli/dist/utils/change-manager.d.ts.map +1 -0
  171. package/cli/dist/utils/change-manager.js +412 -0
  172. package/cli/dist/utils/change-manager.js.map +1 -0
  173. package/cli/dist/utils/config-manager-enhanced.d.ts +66 -0
  174. package/cli/dist/utils/config-manager-enhanced.d.ts.map +1 -0
  175. package/cli/dist/utils/config-manager-enhanced.js +77 -0
  176. package/cli/dist/utils/config-manager-enhanced.js.map +1 -0
  177. package/cli/dist/utils/context-manager.d.ts +96 -0
  178. package/cli/dist/utils/context-manager.d.ts.map +1 -0
  179. package/cli/dist/utils/context-manager.js +258 -0
  180. package/cli/dist/utils/context-manager.js.map +1 -0
  181. package/cli/dist/utils/diff-engine.d.ts +78 -0
  182. package/cli/dist/utils/diff-engine.d.ts.map +1 -0
  183. package/cli/dist/utils/diff-engine.js +233 -0
  184. package/cli/dist/utils/diff-engine.js.map +1 -0
  185. package/cli/dist/utils/export-system.d.ts +101 -0
  186. package/cli/dist/utils/export-system.d.ts.map +1 -0
  187. package/cli/dist/utils/export-system.js +289 -0
  188. package/cli/dist/utils/export-system.js.map +1 -0
  189. package/cli/dist/utils/health-checker.d.ts +66 -0
  190. package/cli/dist/utils/health-checker.d.ts.map +1 -0
  191. package/cli/dist/utils/health-checker.js +285 -0
  192. package/cli/dist/utils/health-checker.js.map +1 -0
  193. package/cli/dist/utils/import-system.d.ts +74 -0
  194. package/cli/dist/utils/import-system.d.ts.map +1 -0
  195. package/cli/dist/utils/import-system.js +317 -0
  196. package/cli/dist/utils/import-system.js.map +1 -0
  197. package/cli/dist/utils/module-cloner.d.ts +40 -0
  198. package/cli/dist/utils/module-cloner.d.ts.map +1 -0
  199. package/cli/dist/utils/module-cloner.js +136 -0
  200. package/cli/dist/utils/module-cloner.js.map +1 -0
  201. package/cli/dist/utils/prompt-manager.d.ts +90 -0
  202. package/cli/dist/utils/prompt-manager.d.ts.map +1 -0
  203. package/cli/dist/utils/prompt-manager.js +302 -0
  204. package/cli/dist/utils/prompt-manager.js.map +1 -0
  205. package/cli/dist/utils/spec-manager.d.ts +65 -0
  206. package/cli/dist/utils/spec-manager.d.ts.map +1 -0
  207. package/cli/dist/utils/spec-manager.js +329 -0
  208. package/cli/dist/utils/spec-manager.js.map +1 -0
  209. package/cli/dist/utils/stats-collector.d.ts +74 -0
  210. package/cli/dist/utils/stats-collector.d.ts.map +1 -0
  211. package/cli/dist/utils/stats-collector.js +164 -0
  212. package/cli/dist/utils/stats-collector.js.map +1 -0
  213. package/cli/dist/utils/template-engine.d.ts +47 -0
  214. package/cli/dist/utils/template-engine.d.ts.map +1 -0
  215. package/cli/dist/utils/template-engine.js +204 -0
  216. package/cli/dist/utils/template-engine.js.map +1 -0
  217. package/package.json +12 -3
  218. package/augment-extensions/workflows/openspec/README.md +0 -96
  219. package/augment-extensions/workflows/openspec/examples/complete-change-example.md +0 -244
  220. package/augment-extensions/workflows/openspec/module.json +0 -54
  221. package/augment-extensions/workflows/openspec/rules/best-practices.md +0 -272
  222. package/augment-extensions/workflows/openspec/rules/manual-setup.md +0 -231
  223. package/augment-extensions/workflows/openspec/rules/spec-format.md +0 -236
  224. package/augment-extensions/workflows/openspec/rules/workflow.md +0 -214
  225. package/cli/dist/utils/__tests__/adr-validator.example.d.ts +0 -6
  226. package/cli/dist/utils/__tests__/adr-validator.example.d.ts.map +0 -1
  227. package/cli/dist/utils/__tests__/adr-validator.example.js +0 -148
  228. package/cli/dist/utils/__tests__/adr-validator.example.js.map +0 -1
  229. package/cli/dist/utils/adr-validator.d.ts +0 -65
  230. package/cli/dist/utils/adr-validator.d.ts.map +0 -1
  231. package/cli/dist/utils/adr-validator.js +0 -203
  232. package/cli/dist/utils/adr-validator.js.map +0 -1
@@ -1,169 +1,169 @@
1
- /**
2
- * Integration tests for screenplay file organization utilities
3
- */
4
-
5
- import * as fs from 'fs';
6
- import * as path from 'path';
7
- import * as os from 'os';
8
- import {
9
- getScreenplaysDir,
10
- ensureScreenplaysDir,
11
- getProjectNameFromOpenSpec,
12
- getProjectNameFromBeads,
13
- getProjectName,
14
- createProjectDir,
15
- ScreenplayProjectInfo
16
- } from '../file-organization';
17
-
18
- describe('Screenplay File Organization', () => {
19
- let testDir: string;
20
-
21
- beforeEach(() => {
22
- // Create a temporary test directory
23
- testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'screenplay-test-'));
24
- });
25
-
26
- afterEach(() => {
27
- // Clean up test directory
28
- if (fs.existsSync(testDir)) {
29
- fs.rmSync(testDir, { recursive: true, force: true });
30
- }
31
- });
32
-
33
- describe('getScreenplaysDir', () => {
34
- it('should return screenplays directory path', () => {
35
- const result = getScreenplaysDir(testDir);
36
- expect(result).toBe(path.join(testDir, 'screenplays'));
37
- });
38
- });
39
-
40
- describe('ensureScreenplaysDir', () => {
41
- it('should create screenplays directory if it does not exist', () => {
42
- const screenplaysDir = ensureScreenplaysDir(testDir);
43
- expect(fs.existsSync(screenplaysDir)).toBe(true);
44
- expect(fs.statSync(screenplaysDir).isDirectory()).toBe(true);
45
- });
46
-
47
- it('should not fail if screenplays directory already exists', () => {
48
- const screenplaysDir = path.join(testDir, 'screenplays');
49
- fs.mkdirSync(screenplaysDir);
50
-
51
- const result = ensureScreenplaysDir(testDir);
52
- expect(result).toBe(screenplaysDir);
53
- expect(fs.existsSync(screenplaysDir)).toBe(true);
54
- });
55
- });
56
-
57
- describe('getProjectNameFromOpenSpec', () => {
58
- it('should return null if openspec directory does not exist', () => {
59
- const result = getProjectNameFromOpenSpec(testDir);
60
- expect(result).toBeNull();
61
- });
62
-
63
- it('should return project info from OpenSpec changes', () => {
64
- // Create mock OpenSpec structure
65
- const changesDir = path.join(testDir, 'openspec', 'changes', 'heist-movie');
66
- fs.mkdirSync(changesDir, { recursive: true });
67
-
68
- const result = getProjectNameFromOpenSpec(testDir);
69
- expect(result).not.toBeNull();
70
- expect(result?.name).toBe('heist-movie');
71
- expect(result?.source).toBe('openspec');
72
- expect(result?.specId).toBe('heist-movie');
73
- });
74
- });
75
-
76
- describe('getProjectNameFromBeads', () => {
77
- it('should return null if .beads directory does not exist', () => {
78
- const result = getProjectNameFromBeads(testDir);
79
- expect(result).toBeNull();
80
- });
81
-
82
- it('should return project info from Beads epic', () => {
83
- // Create mock Beads structure
84
- const beadsDir = path.join(testDir, '.beads');
85
- fs.mkdirSync(beadsDir);
86
-
87
- const issue = {
88
- id: 'bd-scr-test',
89
- title: 'Test Screenplay',
90
- issue_type: 'epic',
91
- status: 'open',
92
- labels: ['screenplay', 'writing-standards']
93
- };
94
-
95
- fs.writeFileSync(
96
- path.join(beadsDir, 'issues.jsonl'),
97
- JSON.stringify(issue) + '\n'
98
- );
99
-
100
- const result = getProjectNameFromBeads(testDir);
101
- expect(result).not.toBeNull();
102
- expect(result?.name).toBe('bd-scr-test');
103
- expect(result?.source).toBe('beads');
104
- expect(result?.epicId).toBe('bd-scr-test');
105
- });
106
- });
107
-
108
- describe('getProjectName', () => {
109
- it('should prefer OpenSpec over Beads', () => {
110
- // Create both OpenSpec and Beads structures
111
- const changesDir = path.join(testDir, 'openspec', 'changes', 'openspec-project');
112
- fs.mkdirSync(changesDir, { recursive: true });
113
-
114
- const beadsDir = path.join(testDir, '.beads');
115
- fs.mkdirSync(beadsDir);
116
- fs.writeFileSync(
117
- path.join(beadsDir, 'issues.jsonl'),
118
- JSON.stringify({
119
- id: 'bd-beads-project',
120
- issue_type: 'epic',
121
- status: 'open',
122
- labels: ['screenplay']
123
- }) + '\n'
124
- );
125
-
126
- const result = getProjectName(testDir);
127
- expect(result.name).toBe('openspec-project');
128
- expect(result.source).toBe('openspec');
129
- });
130
-
131
- it('should use timestamp fallback if no context available', () => {
132
- const result = getProjectName(testDir);
133
- expect(result.source).toBe('manual');
134
- expect(result.name).toMatch(/^screenplay-\d{4}-\d{2}-\d{2}$/);
135
- });
136
- });
137
-
138
- describe('createProjectDir', () => {
139
- it('should create project directory', () => {
140
- const projectInfo: ScreenplayProjectInfo = {
141
- name: 'test-project',
142
- source: 'manual'
143
- };
144
-
145
- const projectDir = createProjectDir(projectInfo, { rootDir: testDir });
146
- expect(fs.existsSync(projectDir)).toBe(true);
147
- expect(projectDir).toBe(path.join(testDir, 'screenplays', 'test-project'));
148
- });
149
-
150
- it('should handle conflicts with append-number strategy', () => {
151
- const projectInfo: ScreenplayProjectInfo = {
152
- name: 'conflict-test',
153
- source: 'manual'
154
- };
155
-
156
- // Create first project
157
- const firstDir = createProjectDir(projectInfo, { rootDir: testDir });
158
- expect(firstDir).toBe(path.join(testDir, 'screenplays', 'conflict-test'));
159
-
160
- // Create second project with same name
161
- const secondDir = createProjectDir(projectInfo, {
162
- rootDir: testDir,
163
- handleConflicts: 'append-number'
164
- });
165
- expect(secondDir).toBe(path.join(testDir, 'screenplays', 'conflict-test-1'));
166
- });
167
- });
168
- });
169
-
1
+ /**
2
+ * Integration tests for screenplay file organization utilities
3
+ */
4
+
5
+ import * as fs from 'fs';
6
+ import * as path from 'path';
7
+ import * as os from 'os';
8
+ import {
9
+ getScreenplaysDir,
10
+ ensureScreenplaysDir,
11
+ getProjectNameFromOpenSpec,
12
+ getProjectNameFromBeads,
13
+ getProjectName,
14
+ createProjectDir,
15
+ ScreenplayProjectInfo
16
+ } from '../file-organization';
17
+
18
+ describe('Screenplay File Organization', () => {
19
+ let testDir: string;
20
+
21
+ beforeEach(() => {
22
+ // Create a temporary test directory
23
+ testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'screenplay-test-'));
24
+ });
25
+
26
+ afterEach(() => {
27
+ // Clean up test directory
28
+ if (fs.existsSync(testDir)) {
29
+ fs.rmSync(testDir, { recursive: true, force: true });
30
+ }
31
+ });
32
+
33
+ describe('getScreenplaysDir', () => {
34
+ it('should return screenplays directory path', () => {
35
+ const result = getScreenplaysDir(testDir);
36
+ expect(result).toBe(path.join(testDir, 'screenplays'));
37
+ });
38
+ });
39
+
40
+ describe('ensureScreenplaysDir', () => {
41
+ it('should create screenplays directory if it does not exist', () => {
42
+ const screenplaysDir = ensureScreenplaysDir(testDir);
43
+ expect(fs.existsSync(screenplaysDir)).toBe(true);
44
+ expect(fs.statSync(screenplaysDir).isDirectory()).toBe(true);
45
+ });
46
+
47
+ it('should not fail if screenplays directory already exists', () => {
48
+ const screenplaysDir = path.join(testDir, 'screenplays');
49
+ fs.mkdirSync(screenplaysDir);
50
+
51
+ const result = ensureScreenplaysDir(testDir);
52
+ expect(result).toBe(screenplaysDir);
53
+ expect(fs.existsSync(screenplaysDir)).toBe(true);
54
+ });
55
+ });
56
+
57
+ describe('getProjectNameFromOpenSpec', () => {
58
+ it('should return null if openspec directory does not exist', () => {
59
+ const result = getProjectNameFromOpenSpec(testDir);
60
+ expect(result).toBeNull();
61
+ });
62
+
63
+ it('should return project info from OpenSpec changes', () => {
64
+ // Create mock OpenSpec structure
65
+ const changesDir = path.join(testDir, 'openspec', 'changes', 'heist-movie');
66
+ fs.mkdirSync(changesDir, { recursive: true });
67
+
68
+ const result = getProjectNameFromOpenSpec(testDir);
69
+ expect(result).not.toBeNull();
70
+ expect(result?.name).toBe('heist-movie');
71
+ expect(result?.source).toBe('openspec');
72
+ expect(result?.specId).toBe('heist-movie');
73
+ });
74
+ });
75
+
76
+ describe('getProjectNameFromBeads', () => {
77
+ it('should return null if .beads directory does not exist', () => {
78
+ const result = getProjectNameFromBeads(testDir);
79
+ expect(result).toBeNull();
80
+ });
81
+
82
+ it('should return project info from Beads epic', () => {
83
+ // Create mock Beads structure
84
+ const beadsDir = path.join(testDir, '.beads');
85
+ fs.mkdirSync(beadsDir);
86
+
87
+ const issue = {
88
+ id: 'bd-scr-test',
89
+ title: 'Test Screenplay',
90
+ issue_type: 'epic',
91
+ status: 'open',
92
+ labels: ['screenplay', 'writing-standards']
93
+ };
94
+
95
+ fs.writeFileSync(
96
+ path.join(beadsDir, 'issues.jsonl'),
97
+ JSON.stringify(issue) + '\n'
98
+ );
99
+
100
+ const result = getProjectNameFromBeads(testDir);
101
+ expect(result).not.toBeNull();
102
+ expect(result?.name).toBe('bd-scr-test');
103
+ expect(result?.source).toBe('beads');
104
+ expect(result?.epicId).toBe('bd-scr-test');
105
+ });
106
+ });
107
+
108
+ describe('getProjectName', () => {
109
+ it('should prefer OpenSpec over Beads', () => {
110
+ // Create both OpenSpec and Beads structures
111
+ const changesDir = path.join(testDir, 'openspec', 'changes', 'openspec-project');
112
+ fs.mkdirSync(changesDir, { recursive: true });
113
+
114
+ const beadsDir = path.join(testDir, '.beads');
115
+ fs.mkdirSync(beadsDir);
116
+ fs.writeFileSync(
117
+ path.join(beadsDir, 'issues.jsonl'),
118
+ JSON.stringify({
119
+ id: 'bd-beads-project',
120
+ issue_type: 'epic',
121
+ status: 'open',
122
+ labels: ['screenplay']
123
+ }) + '\n'
124
+ );
125
+
126
+ const result = getProjectName(testDir);
127
+ expect(result.name).toBe('openspec-project');
128
+ expect(result.source).toBe('openspec');
129
+ });
130
+
131
+ it('should use timestamp fallback if no context available', () => {
132
+ const result = getProjectName(testDir);
133
+ expect(result.source).toBe('manual');
134
+ expect(result.name).toMatch(/^screenplay-\d{4}-\d{2}-\d{2}$/);
135
+ });
136
+ });
137
+
138
+ describe('createProjectDir', () => {
139
+ it('should create project directory', () => {
140
+ const projectInfo: ScreenplayProjectInfo = {
141
+ name: 'test-project',
142
+ source: 'manual'
143
+ };
144
+
145
+ const projectDir = createProjectDir(projectInfo, { rootDir: testDir });
146
+ expect(fs.existsSync(projectDir)).toBe(true);
147
+ expect(projectDir).toBe(path.join(testDir, 'screenplays', 'test-project'));
148
+ });
149
+
150
+ it('should handle conflicts with append-number strategy', () => {
151
+ const projectInfo: ScreenplayProjectInfo = {
152
+ name: 'conflict-test',
153
+ source: 'manual'
154
+ };
155
+
156
+ // Create first project
157
+ const firstDir = createProjectDir(projectInfo, { rootDir: testDir });
158
+ expect(firstDir).toBe(path.join(testDir, 'screenplays', 'conflict-test'));
159
+
160
+ // Create second project with same name
161
+ const secondDir = createProjectDir(projectInfo, {
162
+ rootDir: testDir,
163
+ handleConflicts: 'append-number'
164
+ });
165
+ expect(secondDir).toBe(path.join(testDir, 'screenplays', 'conflict-test-1'));
166
+ });
167
+ });
168
+ });
169
+
@@ -1,165 +1,165 @@
1
- /**
2
- * File Organization Utilities for Screenplay Module
3
- *
4
- * Provides utilities for organizing screenplay files into structured directories
5
- * based on OpenSpec specs or Beads epics.
6
- */
7
-
8
- import * as fs from 'fs';
9
- import * as path from 'path';
10
-
11
- export interface ScreenplayProjectInfo {
12
- name: string;
13
- source: 'openspec' | 'beads' | 'manual';
14
- specId?: string;
15
- epicId?: string;
16
- timestamp?: string;
17
- }
18
-
19
- export interface OrganizationOptions {
20
- rootDir?: string;
21
- createIfMissing?: boolean;
22
- handleConflicts?: 'append-timestamp' | 'append-number' | 'error';
23
- }
24
-
25
- /**
26
- * Get the screenplays directory path
27
- */
28
- export function getScreenplaysDir(rootDir: string = process.cwd()): string {
29
- return path.join(rootDir, 'screenplays');
30
- }
31
-
32
- /**
33
- * Ensure the screenplays directory exists
34
- */
35
- export function ensureScreenplaysDir(rootDir: string = process.cwd()): string {
36
- const screenplaysDir = getScreenplaysDir(rootDir);
37
-
38
- if (!fs.existsSync(screenplaysDir)) {
39
- fs.mkdirSync(screenplaysDir, { recursive: true });
40
- }
41
-
42
- return screenplaysDir;
43
- }
44
-
45
- /**
46
- * Get project name from OpenSpec spec
47
- */
48
- export function getProjectNameFromOpenSpec(rootDir: string = process.cwd()): ScreenplayProjectInfo | null {
49
- const openspecDir = path.join(rootDir, 'openspec');
50
-
51
- if (!fs.existsSync(openspecDir)) {
52
- return null;
53
- }
54
-
55
- // Check for active changes
56
- const changesDir = path.join(openspecDir, 'changes');
57
- if (fs.existsSync(changesDir)) {
58
- const changes = fs.readdirSync(changesDir, { withFileTypes: true })
59
- .filter(dirent => dirent.isDirectory())
60
- .map(dirent => dirent.name);
61
-
62
- if (changes.length > 0) {
63
- // Use the first active change as the project name
64
- return {
65
- name: changes[0],
66
- source: 'openspec',
67
- specId: changes[0]
68
- };
69
- }
70
- }
71
-
72
- return null;
73
- }
74
-
75
- /**
76
- * Get project name from Beads epic
77
- */
78
- export function getProjectNameFromBeads(rootDir: string = process.cwd()): ScreenplayProjectInfo | null {
79
- const beadsFile = path.join(rootDir, '.beads', 'issues.jsonl');
80
-
81
- if (!fs.existsSync(beadsFile)) {
82
- return null;
83
- }
84
-
85
- try {
86
- const lines = fs.readFileSync(beadsFile, 'utf-8').split('\n').filter(line => line.trim());
87
-
88
- // Find the most recent open epic with screenplay labels
89
- for (let i = lines.length - 1; i >= 0; i--) {
90
- const issue = JSON.parse(lines[i]);
91
-
92
- if (issue.issue_type === 'epic' &&
93
- issue.status === 'open' &&
94
- (issue.labels?.includes('screenplay') || issue.labels?.includes('writing-standards'))) {
95
- return {
96
- name: issue.id,
97
- source: 'beads',
98
- epicId: issue.id
99
- };
100
- }
101
- }
102
- } catch (error) {
103
- console.warn('Error reading Beads issues:', error);
104
- }
105
-
106
- return null;
107
- }
108
-
109
- /**
110
- * Get project name with fallback logic
111
- */
112
- export function getProjectName(rootDir: string = process.cwd()): ScreenplayProjectInfo {
113
- // Try OpenSpec first
114
- const openspecInfo = getProjectNameFromOpenSpec(rootDir);
115
- if (openspecInfo) {
116
- return openspecInfo;
117
- }
118
-
119
- // Try Beads second
120
- const beadsInfo = getProjectNameFromBeads(rootDir);
121
- if (beadsInfo) {
122
- return beadsInfo;
123
- }
124
-
125
- // Fallback to timestamp
126
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0];
127
- return {
128
- name: `screenplay-${timestamp}`,
129
- source: 'manual',
130
- timestamp
131
- };
132
- }
133
-
134
- /**
135
- * Create project directory with conflict resolution
136
- */
137
- export function createProjectDir(
138
- projectInfo: ScreenplayProjectInfo,
139
- options: OrganizationOptions = {}
140
- ): string {
141
- const { rootDir = process.cwd(), handleConflicts = 'append-number' } = options;
142
-
143
- const screenplaysDir = ensureScreenplaysDir(rootDir);
144
- let projectDir = path.join(screenplaysDir, projectInfo.name);
145
-
146
- // Handle conflicts
147
- if (fs.existsSync(projectDir)) {
148
- if (handleConflicts === 'error') {
149
- throw new Error(`Project directory already exists: ${projectDir}`);
150
- } else if (handleConflicts === 'append-timestamp') {
151
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
152
- projectDir = path.join(screenplaysDir, `${projectInfo.name}-${timestamp}`);
153
- } else if (handleConflicts === 'append-number') {
154
- let counter = 1;
155
- while (fs.existsSync(projectDir)) {
156
- projectDir = path.join(screenplaysDir, `${projectInfo.name}-${counter}`);
157
- counter++;
158
- }
159
- }
160
- }
161
-
162
- fs.mkdirSync(projectDir, { recursive: true });
163
- return projectDir;
164
- }
165
-
1
+ /**
2
+ * File Organization Utilities for Screenplay Module
3
+ *
4
+ * Provides utilities for organizing screenplay files into structured directories
5
+ * based on OpenSpec specs or Beads epics.
6
+ */
7
+
8
+ import * as fs from 'fs';
9
+ import * as path from 'path';
10
+
11
+ export interface ScreenplayProjectInfo {
12
+ name: string;
13
+ source: 'openspec' | 'beads' | 'manual';
14
+ specId?: string;
15
+ epicId?: string;
16
+ timestamp?: string;
17
+ }
18
+
19
+ export interface OrganizationOptions {
20
+ rootDir?: string;
21
+ createIfMissing?: boolean;
22
+ handleConflicts?: 'append-timestamp' | 'append-number' | 'error';
23
+ }
24
+
25
+ /**
26
+ * Get the screenplays directory path
27
+ */
28
+ export function getScreenplaysDir(rootDir: string = process.cwd()): string {
29
+ return path.join(rootDir, 'screenplays');
30
+ }
31
+
32
+ /**
33
+ * Ensure the screenplays directory exists
34
+ */
35
+ export function ensureScreenplaysDir(rootDir: string = process.cwd()): string {
36
+ const screenplaysDir = getScreenplaysDir(rootDir);
37
+
38
+ if (!fs.existsSync(screenplaysDir)) {
39
+ fs.mkdirSync(screenplaysDir, { recursive: true });
40
+ }
41
+
42
+ return screenplaysDir;
43
+ }
44
+
45
+ /**
46
+ * Get project name from OpenSpec spec
47
+ */
48
+ export function getProjectNameFromOpenSpec(rootDir: string = process.cwd()): ScreenplayProjectInfo | null {
49
+ const openspecDir = path.join(rootDir, 'openspec');
50
+
51
+ if (!fs.existsSync(openspecDir)) {
52
+ return null;
53
+ }
54
+
55
+ // Check for active changes
56
+ const changesDir = path.join(openspecDir, 'changes');
57
+ if (fs.existsSync(changesDir)) {
58
+ const changes = fs.readdirSync(changesDir, { withFileTypes: true })
59
+ .filter(dirent => dirent.isDirectory())
60
+ .map(dirent => dirent.name);
61
+
62
+ if (changes.length > 0) {
63
+ // Use the first active change as the project name
64
+ return {
65
+ name: changes[0],
66
+ source: 'openspec',
67
+ specId: changes[0]
68
+ };
69
+ }
70
+ }
71
+
72
+ return null;
73
+ }
74
+
75
+ /**
76
+ * Get project name from Beads epic
77
+ */
78
+ export function getProjectNameFromBeads(rootDir: string = process.cwd()): ScreenplayProjectInfo | null {
79
+ const beadsFile = path.join(rootDir, '.beads', 'issues.jsonl');
80
+
81
+ if (!fs.existsSync(beadsFile)) {
82
+ return null;
83
+ }
84
+
85
+ try {
86
+ const lines = fs.readFileSync(beadsFile, 'utf-8').split('\n').filter(line => line.trim());
87
+
88
+ // Find the most recent open epic with screenplay labels
89
+ for (let i = lines.length - 1; i >= 0; i--) {
90
+ const issue = JSON.parse(lines[i]);
91
+
92
+ if (issue.issue_type === 'epic' &&
93
+ issue.status === 'open' &&
94
+ (issue.labels?.includes('screenplay') || issue.labels?.includes('writing-standards'))) {
95
+ return {
96
+ name: issue.id,
97
+ source: 'beads',
98
+ epicId: issue.id
99
+ };
100
+ }
101
+ }
102
+ } catch (error) {
103
+ console.warn('Error reading Beads issues:', error);
104
+ }
105
+
106
+ return null;
107
+ }
108
+
109
+ /**
110
+ * Get project name with fallback logic
111
+ */
112
+ export function getProjectName(rootDir: string = process.cwd()): ScreenplayProjectInfo {
113
+ // Try OpenSpec first
114
+ const openspecInfo = getProjectNameFromOpenSpec(rootDir);
115
+ if (openspecInfo) {
116
+ return openspecInfo;
117
+ }
118
+
119
+ // Try Beads second
120
+ const beadsInfo = getProjectNameFromBeads(rootDir);
121
+ if (beadsInfo) {
122
+ return beadsInfo;
123
+ }
124
+
125
+ // Fallback to timestamp
126
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0];
127
+ return {
128
+ name: `screenplay-${timestamp}`,
129
+ source: 'manual',
130
+ timestamp
131
+ };
132
+ }
133
+
134
+ /**
135
+ * Create project directory with conflict resolution
136
+ */
137
+ export function createProjectDir(
138
+ projectInfo: ScreenplayProjectInfo,
139
+ options: OrganizationOptions = {}
140
+ ): string {
141
+ const { rootDir = process.cwd(), handleConflicts = 'append-number' } = options;
142
+
143
+ const screenplaysDir = ensureScreenplaysDir(rootDir);
144
+ let projectDir = path.join(screenplaysDir, projectInfo.name);
145
+
146
+ // Handle conflicts
147
+ if (fs.existsSync(projectDir)) {
148
+ if (handleConflicts === 'error') {
149
+ throw new Error(`Project directory already exists: ${projectDir}`);
150
+ } else if (handleConflicts === 'append-timestamp') {
151
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
152
+ projectDir = path.join(screenplaysDir, `${projectInfo.name}-${timestamp}`);
153
+ } else if (handleConflicts === 'append-number') {
154
+ let counter = 1;
155
+ while (fs.existsSync(projectDir)) {
156
+ projectDir = path.join(screenplaysDir, `${projectInfo.name}-${counter}`);
157
+ counter++;
158
+ }
159
+ }
160
+ }
161
+
162
+ fs.mkdirSync(projectDir, { recursive: true });
163
+ return projectDir;
164
+ }
165
+