@mytechtoday/augment-extensions 1.2.2 → 1.3.1
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.
- package/LICENSE +22 -22
- package/augment-extensions/domain-rules/software-architecture/README.md +143 -143
- package/augment-extensions/domain-rules/software-architecture/examples/banking-layered.md +961 -961
- package/augment-extensions/domain-rules/software-architecture/examples/ecommerce-microservices.md +990 -990
- package/augment-extensions/domain-rules/software-architecture/examples/iot-eventdriven.md +882 -882
- package/augment-extensions/domain-rules/software-architecture/examples/monolith-to-microservices-migration.md +703 -703
- package/augment-extensions/domain-rules/software-architecture/examples/serverless-imageprocessing.md +957 -957
- package/augment-extensions/domain-rules/software-architecture/examples/trading-eventdriven.md +747 -747
- package/augment-extensions/domain-rules/software-architecture/module.json +119 -119
- package/augment-extensions/domain-rules/software-architecture/rules/challenges-solutions.md +763 -763
- package/augment-extensions/domain-rules/software-architecture/rules/definitions-terminology.md +409 -409
- package/augment-extensions/domain-rules/software-architecture/rules/design-principles.md +684 -684
- package/augment-extensions/domain-rules/software-architecture/rules/evaluation-testing.md +1381 -1381
- package/augment-extensions/domain-rules/software-architecture/rules/event-driven-architecture.md +616 -616
- package/augment-extensions/domain-rules/software-architecture/rules/fundamentals.md +306 -306
- package/augment-extensions/domain-rules/software-architecture/rules/industry-architectures.md +554 -554
- package/augment-extensions/domain-rules/software-architecture/rules/layered-architecture.md +776 -776
- package/augment-extensions/domain-rules/software-architecture/rules/microservices-architecture.md +503 -503
- package/augment-extensions/domain-rules/software-architecture/rules/modeling-documentation.md +1199 -1199
- package/augment-extensions/domain-rules/software-architecture/rules/monolithic-architecture.md +351 -351
- package/augment-extensions/domain-rules/software-architecture/rules/principles.md +556 -556
- package/augment-extensions/domain-rules/software-architecture/rules/quality-attributes.md +797 -797
- package/augment-extensions/domain-rules/software-architecture/rules/scalability-performance.md +1345 -1345
- package/augment-extensions/domain-rules/software-architecture/rules/security-architecture.md +1039 -1039
- package/augment-extensions/domain-rules/software-architecture/rules/serverless-architecture.md +711 -711
- package/augment-extensions/domain-rules/software-architecture/rules/skills-development.md +568 -568
- package/augment-extensions/domain-rules/software-architecture/rules/tools-methodologies.md +961 -961
- package/augment-extensions/visual-design/CHANGELOG.md +132 -0
- package/augment-extensions/visual-design/README.md +255 -0
- package/augment-extensions/visual-design/__tests__/README.md +119 -0
- package/augment-extensions/visual-design/__tests__/style-selector.test.ts +172 -0
- package/augment-extensions/visual-design/__tests__/vendor-styles.test.ts +214 -0
- package/augment-extensions/visual-design/domains/other/ai-prompt-helper.ts +157 -0
- package/augment-extensions/visual-design/domains/other/dotnet-application.ts +156 -0
- package/augment-extensions/visual-design/domains/other/linux-platform.ts +156 -0
- package/augment-extensions/visual-design/domains/other/mobile-application.ts +157 -0
- package/augment-extensions/visual-design/domains/other/motion-picture.ts +156 -0
- package/augment-extensions/visual-design/domains/other/os-application.ts +156 -0
- package/augment-extensions/visual-design/domains/other/print-campaigns.ts +158 -0
- package/augment-extensions/visual-design/domains/other/web-app.ts +157 -0
- package/augment-extensions/visual-design/domains/other/website.ts +161 -0
- package/augment-extensions/visual-design/domains/other/windows-platform.ts +156 -0
- package/augment-extensions/visual-design/domains/web-page-styles/amazon-cloudscape.ts +506 -0
- package/augment-extensions/visual-design/domains/web-page-styles/google-modern.ts +615 -0
- package/augment-extensions/visual-design/domains/web-page-styles/microsoft-fluent.ts +531 -0
- package/augment-extensions/visual-design/examples/README.md +97 -0
- package/augment-extensions/visual-design/examples/ai-prompt-generation.md +233 -0
- package/augment-extensions/visual-design/examples/basic-usage.md +216 -0
- package/augment-extensions/visual-design/examples/domain-workflows.md +257 -0
- package/augment-extensions/visual-design/examples/vendor-comparison.md +247 -0
- package/augment-extensions/visual-design/module.json +78 -0
- package/augment-extensions/visual-design/style-selector.ts +177 -0
- package/augment-extensions/visual-design/types.ts +302 -0
- package/augment-extensions/visual-design/visual-design-core.ts +469 -0
- package/augment-extensions/workflows/adr-support/README.md +227 -0
- package/augment-extensions/workflows/adr-support/__tests__/adr-validator.test.ts +203 -0
- package/augment-extensions/workflows/adr-support/adr-validator.ts +162 -0
- package/augment-extensions/workflows/adr-support/examples/complete-lifecycle-example.md +449 -0
- package/augment-extensions/workflows/adr-support/examples/integration-example.md +580 -0
- package/augment-extensions/workflows/adr-support/examples/superseding-example.md +436 -0
- package/augment-extensions/workflows/adr-support/module.json +112 -0
- package/augment-extensions/workflows/adr-support/rules/adr-creation.md +372 -0
- package/augment-extensions/workflows/adr-support/rules/beads-integration.md +443 -0
- package/augment-extensions/workflows/adr-support/rules/conflict-detection.md +486 -0
- package/augment-extensions/workflows/adr-support/rules/decision-detection.md +362 -0
- package/augment-extensions/workflows/adr-support/rules/lifecycle-management.md +427 -0
- package/augment-extensions/workflows/adr-support/rules/openspec-integration.md +465 -0
- package/augment-extensions/workflows/adr-support/rules/template-selection.md +405 -0
- package/augment-extensions/workflows/adr-support/rules/validation-rules.md +543 -0
- package/augment-extensions/workflows/adr-support/schemas/adr-config.json +191 -0
- package/augment-extensions/workflows/adr-support/schemas/adr-metadata.json +172 -0
- package/augment-extensions/workflows/adr-support/templates/business-case.md +235 -0
- package/augment-extensions/workflows/adr-support/templates/madr-elaborate.md +197 -0
- package/augment-extensions/workflows/adr-support/templates/madr-simple.md +68 -0
- package/augment-extensions/workflows/adr-support/templates/nygard.md +84 -0
- package/augment-extensions/writing-standards/screenplay/rules/file-organization.md +213 -213
- package/augment-extensions/writing-standards/screenplay/utils/__tests__/file-organization.test.ts +169 -169
- package/augment-extensions/writing-standards/screenplay/utils/file-organization.ts +165 -165
- package/cli/dist/utils/auto-sync.js +19 -19
- package/package.json +5 -3
- package/augment-extensions/workflows/openspec/README.md +0 -96
- package/augment-extensions/workflows/openspec/examples/complete-change-example.md +0 -244
- package/augment-extensions/workflows/openspec/module.json +0 -54
- package/augment-extensions/workflows/openspec/rules/best-practices.md +0 -272
- package/augment-extensions/workflows/openspec/rules/manual-setup.md +0 -231
- package/augment-extensions/workflows/openspec/rules/spec-format.md +0 -236
- package/augment-extensions/workflows/openspec/rules/workflow.md +0 -214
package/augment-extensions/writing-standards/screenplay/utils/__tests__/file-organization.test.ts
CHANGED
|
@@ -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
|
+
|