@smartmemory/compose 0.1.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.
- package/LICENSE +21 -0
- package/README.md +1014 -0
- package/bin/compose.js +1515 -0
- package/dist/assets/_baseUniq-CQwX6VLz.js +1 -0
- package/dist/assets/arc-SxJ2J1sh.js +1 -0
- package/dist/assets/architectureDiagram-Q4EWVU46-BykunY1F.js +36 -0
- package/dist/assets/blockDiagram-DXYQGD6D-ohAKBOUw.js +132 -0
- package/dist/assets/c4Diagram-AHTNJAMY-DBDC3ENB.js +10 -0
- package/dist/assets/channel-DGElom1e.js +1 -0
- package/dist/assets/chunk-4BX2VUAB-Cv93Z7uM.js +1 -0
- package/dist/assets/chunk-4TB4RGXK-DE0WBDkj.js +206 -0
- package/dist/assets/chunk-55IACEB6-CE1EXenG.js +1 -0
- package/dist/assets/chunk-EDXVE4YY-DA7Ana6H.js +1 -0
- package/dist/assets/chunk-FMBD7UC4-CTDIPA3p.js +15 -0
- package/dist/assets/chunk-OYMX7WX6-uGBaPaTX.js +231 -0
- package/dist/assets/chunk-QZHKN3VN-CYlnXuUO.js +1 -0
- package/dist/assets/chunk-YZCP3GAM-ojGkzcZK.js +1 -0
- package/dist/assets/classDiagram-6PBFFD2Q-KqWP9wWZ.js +1 -0
- package/dist/assets/classDiagram-v2-HSJHXN6E-KqWP9wWZ.js +1 -0
- package/dist/assets/clone-DUJKJXd7.js +1 -0
- package/dist/assets/cose-bilkent-S5V4N54A-Bktn9hL-.js +1 -0
- package/dist/assets/dagre-KV5264BT-DFaSzuRF.js +4 -0
- package/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/assets/diagram-5BDNPKRD-DnfmDzEm.js +10 -0
- package/dist/assets/diagram-G4DWMVQ6-Bm8W9YnG.js +24 -0
- package/dist/assets/diagram-MMDJMWI5-B5-TSKvp.js +43 -0
- package/dist/assets/diagram-TYMM5635-ls4rqlky.js +24 -0
- package/dist/assets/erDiagram-SMLLAGMA-giG6WO-r.js +85 -0
- package/dist/assets/flowDiagram-DWJPFMVM-XvlUuz-7.js +162 -0
- package/dist/assets/ganttDiagram-T4ZO3ILL-hLBV57oV.js +292 -0
- package/dist/assets/gitGraphDiagram-UUTBAWPF-BHu3s_Gn.js +106 -0
- package/dist/assets/graph-D0Cfv00Y.js +1 -0
- package/dist/assets/index-CUd6pFGF.css +1 -0
- package/dist/assets/index-DReRlzZI.js +1144 -0
- package/dist/assets/infoDiagram-42DDH7IO-DbqRsOo3.js +2 -0
- package/dist/assets/init-Gi6I4Gst.js +1 -0
- package/dist/assets/ishikawaDiagram-UXIWVN3A-DnCdx7zb.js +70 -0
- package/dist/assets/journeyDiagram-VCZTEJTY-CfD7eNcP.js +139 -0
- package/dist/assets/kanban-definition-6JOO6SKY-BYaO9-mK.js +89 -0
- package/dist/assets/katex-DkKDou_j.js +257 -0
- package/dist/assets/layout-Bj72wOEB.js +1 -0
- package/dist/assets/linear-BRFo114D.js +1 -0
- package/dist/assets/min-GCHnKlJS.js +1 -0
- package/dist/assets/mindmap-definition-QFDTVHPH-n0PMebY4.js +96 -0
- package/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/dist/assets/pieDiagram-DEJITSTG-pN4CljHF.js +30 -0
- package/dist/assets/quadrantDiagram-34T5L4WZ-DNoAy8-D.js +7 -0
- package/dist/assets/requirementDiagram-MS252O5E-BhtY05PT.js +84 -0
- package/dist/assets/sankeyDiagram-XADWPNL6-B6AD-16A.js +10 -0
- package/dist/assets/sequenceDiagram-FGHM5R23-DShHM-uk.js +157 -0
- package/dist/assets/stateDiagram-FHFEXIEX-DMxn7HTo.js +1 -0
- package/dist/assets/stateDiagram-v2-QKLJ7IA2-o6PnCs4e.js +1 -0
- package/dist/assets/timeline-definition-GMOUNBTQ-Cdu6uq52.js +120 -0
- package/dist/assets/vennDiagram-DHZGUBPP-CpK29iRe.js +34 -0
- package/dist/assets/wardley-RL74JXVD-BQgSkdcO.js +162 -0
- package/dist/assets/wardleyDiagram-NUSXRM2D-DJHYev6O.js +20 -0
- package/dist/assets/xychartDiagram-5P7HB3ND-1d75pbaO.js +7 -0
- package/dist/index.html +30 -0
- package/lib/agent-chains.js +65 -0
- package/lib/agent-string.js +86 -0
- package/lib/budget-ledger.js +86 -0
- package/lib/build-all.js +162 -0
- package/lib/build-dag.js +120 -0
- package/lib/build-stream-writer.js +190 -0
- package/lib/build.js +2997 -0
- package/lib/capability-checker.js +53 -0
- package/lib/cert-inject.js +38 -0
- package/lib/cli-progress.js +483 -0
- package/lib/constants.js +69 -0
- package/lib/cross-layer-audit.js +84 -0
- package/lib/debug-discipline.js +173 -0
- package/lib/feature-json.js +106 -0
- package/lib/gate-prompt.js +291 -0
- package/lib/gate-tiers.js +194 -0
- package/lib/health-history.js +119 -0
- package/lib/health-score.js +227 -0
- package/lib/ideabox.js +570 -0
- package/lib/import.js +244 -0
- package/lib/migrate-roadmap.js +94 -0
- package/lib/model-pricing.js +67 -0
- package/lib/new.js +413 -0
- package/lib/pipeline-cli.js +489 -0
- package/lib/plan-parser.js +103 -0
- package/lib/qa-scoping.js +474 -0
- package/lib/questionnaire.js +200 -0
- package/lib/resolve-port.js +7 -0
- package/lib/result-normalizer.js +349 -0
- package/lib/review-lenses.js +166 -0
- package/lib/roadmap-gen.js +210 -0
- package/lib/roadmap-parser.js +176 -0
- package/lib/server-probe.js +23 -0
- package/lib/staleness.js +87 -0
- package/lib/step-prompt.js +260 -0
- package/lib/step-validator.js +49 -0
- package/lib/stratum-mcp-client.js +365 -0
- package/lib/team-flag.js +46 -0
- package/lib/test-bootstrap.js +401 -0
- package/lib/triage.js +274 -0
- package/lib/vision-writer.js +391 -0
- package/package.json +111 -0
- package/pipelines/bug-fix.stratum.yaml +230 -0
- package/pipelines/build.stratum.yaml +498 -0
- package/pipelines/content.stratum.yaml +112 -0
- package/pipelines/coverage-sweep.stratum.yaml +52 -0
- package/pipelines/refactor.stratum.yaml +169 -0
- package/pipelines/research.stratum.yaml +88 -0
- package/pipelines/review-fix.stratum.yaml +109 -0
- package/presets/team-feature.stratum.yaml +105 -0
- package/presets/team-research.stratum.yaml +108 -0
- package/presets/team-review.stratum.yaml +106 -0
- package/scripts/agent-activity-hook.sh +31 -0
- package/scripts/agent-error-hook.sh +28 -0
- package/scripts/analyze-orphans.mjs +50 -0
- package/scripts/find-orphans.mjs +26 -0
- package/scripts/fix-phases.mjs +49 -0
- package/scripts/generate-stratum-spec.mjs +137 -0
- package/scripts/import-roadmap.mjs +116 -0
- package/scripts/phase-audit.mjs +33 -0
- package/scripts/run-pipeline.mjs +314 -0
- package/scripts/session-end-hook.sh +18 -0
- package/scripts/session-start-hook.sh +38 -0
- package/scripts/vision-hook.sh +104 -0
- package/scripts/vision-track.mjs +554 -0
- package/scripts/wire-all-orphans.mjs +108 -0
- package/scripts/wire-orphans.mjs +164 -0
- package/server/activity-routes.js +123 -0
- package/server/agent-health.js +197 -0
- package/server/agent-hooks.js +102 -0
- package/server/agent-mcp.js +10 -0
- package/server/agent-registry.js +95 -0
- package/server/agent-server.js +290 -0
- package/server/agent-spawn.js +251 -0
- package/server/agent-templates.js +77 -0
- package/server/artifact-manager.js +247 -0
- package/server/artifact-templates/architecture.md +28 -0
- package/server/artifact-templates/blueprint.md +21 -0
- package/server/artifact-templates/design.md +36 -0
- package/server/artifact-templates/plan.md +25 -0
- package/server/artifact-templates/prd.md +43 -0
- package/server/artifact-templates/report.md +40 -0
- package/server/block-tracker.js +90 -0
- package/server/build-stream-bridge.js +502 -0
- package/server/coalescing-buffer.js +46 -0
- package/server/compose-mcp-tools.js +479 -0
- package/server/compose-mcp.js +324 -0
- package/server/connectors/agent-connector.js +78 -0
- package/server/connectors/claude-sdk-connector.js +198 -0
- package/server/connectors/codex-connector.js +240 -0
- package/server/connectors/connector-discovery.js +18 -0
- package/server/connectors/connector-runtime.js +13 -0
- package/server/connectors/opencode-connector.js +200 -0
- package/server/design-routes.js +540 -0
- package/server/design-session.js +161 -0
- package/server/feature-scan.js +593 -0
- package/server/file-watcher.js +284 -0
- package/server/find-root.js +29 -0
- package/server/graph-export.js +343 -0
- package/server/ideabox-cache.js +77 -0
- package/server/ideabox-routes.js +294 -0
- package/server/index.js +156 -0
- package/server/model-tiers.js +49 -0
- package/server/pipeline-routes.js +288 -0
- package/server/policy-evaluator.js +36 -0
- package/server/project-root.js +122 -0
- package/server/security.js +23 -0
- package/server/session-manager.js +403 -0
- package/server/session-routes.js +190 -0
- package/server/session-store.js +107 -0
- package/server/settings-routes.js +35 -0
- package/server/settings-store.js +234 -0
- package/server/stratum-api.js +102 -0
- package/server/stratum-client.js +192 -0
- package/server/stratum-sync.js +193 -0
- package/server/summarizer.js +139 -0
- package/server/supervisor.js +196 -0
- package/server/vision-routes.js +668 -0
- package/server/vision-server.js +393 -0
- package/server/vision-store.js +360 -0
- package/server/vision-utils.js +179 -0
- package/server/worktree-gc.js +137 -0
- package/templates/ROADMAP.md +46 -0
package/lib/team-flag.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* team-flag.js — --team CLI flag parsing for COMP-TEAMS.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from compose.js for testability (compose.js has top-level
|
|
5
|
+
* side effects that prevent clean import in test files).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export const KNOWN_TEAMS = ['review', 'research', 'feature'];
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Parse and validate the --team flag from CLI args.
|
|
12
|
+
* Rewrites --team <name> into a template name and strips it from args.
|
|
13
|
+
*
|
|
14
|
+
* @param {string[]} args - CLI arguments (after subcommand is stripped)
|
|
15
|
+
* @returns {{ template: string|null, args: string[] }}
|
|
16
|
+
* @throws {Error} On invalid usage
|
|
17
|
+
*/
|
|
18
|
+
export function parseTeamFlag(args) {
|
|
19
|
+
const teamIdx = args.indexOf('--team');
|
|
20
|
+
if (teamIdx === -1) return { template: null, args: [...args] };
|
|
21
|
+
|
|
22
|
+
const teamName = args[teamIdx + 1];
|
|
23
|
+
if (!teamName || teamName.startsWith('-')) {
|
|
24
|
+
throw new Error('--team requires a team name (available: ' + KNOWN_TEAMS.join(', ') + ')');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (!KNOWN_TEAMS.includes(teamName)) {
|
|
28
|
+
throw new Error(`Unknown team "${teamName}". Available: ${KNOWN_TEAMS.join(', ')}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (args.includes('--template')) {
|
|
32
|
+
throw new Error('--team cannot be used with --template');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const cleaned = args.filter((_, i) => i !== teamIdx && i !== teamIdx + 1);
|
|
36
|
+
|
|
37
|
+
if (cleaned.includes('--all')) {
|
|
38
|
+
throw new Error('--team cannot be used with batch builds (--all or multiple features)');
|
|
39
|
+
}
|
|
40
|
+
const featureCodes = cleaned.filter(a => !a.startsWith('-'));
|
|
41
|
+
if (featureCodes.length > 1) {
|
|
42
|
+
throw new Error('--team cannot be used with batch builds (--all or multiple features)');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return { template: `team-${teamName}`, args: cleaned };
|
|
46
|
+
}
|
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* test-bootstrap.js — Framework detection and test scaffold generation.
|
|
3
|
+
*
|
|
4
|
+
* Item 125: detectTestFramework(cwd) — inspect config files, package.json devDeps,
|
|
5
|
+
* and pyproject.toml to identify the active test framework.
|
|
6
|
+
*
|
|
7
|
+
* Item 126: scaffoldTestFramework(cwd, language) — create minimal test scaffolding
|
|
8
|
+
* when no framework is detected.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync, readdirSync } from 'node:fs';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Internal helpers
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
function _readJSON(filePath) {
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
21
|
+
} catch {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function _readText(filePath) {
|
|
27
|
+
try {
|
|
28
|
+
return readFileSync(filePath, 'utf-8');
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Item 125: detectTestFramework
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Detect which test framework is configured in the given directory.
|
|
40
|
+
*
|
|
41
|
+
* Checks (in order):
|
|
42
|
+
* 1. Well-known config file names on disk
|
|
43
|
+
* 2. package.json devDependencies / dependencies
|
|
44
|
+
* 3. pyproject.toml [tool.pytest]
|
|
45
|
+
*
|
|
46
|
+
* @param {string} cwd - Directory to inspect
|
|
47
|
+
* @returns {{ framework: string, runner: string, configFile: string|null, command: string }|null}
|
|
48
|
+
*/
|
|
49
|
+
export function detectTestFramework(cwd) {
|
|
50
|
+
// --- Config-file checks ---
|
|
51
|
+
|
|
52
|
+
const configChecks = [
|
|
53
|
+
// Vitest
|
|
54
|
+
{
|
|
55
|
+
files: ['vitest.config.js', 'vitest.config.ts', 'vitest.config.mjs', 'vitest.config.cjs'],
|
|
56
|
+
framework: 'vitest',
|
|
57
|
+
runner: 'vitest',
|
|
58
|
+
command: 'npx vitest run',
|
|
59
|
+
},
|
|
60
|
+
// Jest
|
|
61
|
+
{
|
|
62
|
+
files: [
|
|
63
|
+
'jest.config.js', 'jest.config.ts', 'jest.config.mjs', 'jest.config.cjs',
|
|
64
|
+
'jest.config.json',
|
|
65
|
+
],
|
|
66
|
+
framework: 'jest',
|
|
67
|
+
runner: 'jest',
|
|
68
|
+
command: 'npx jest',
|
|
69
|
+
},
|
|
70
|
+
// Mocha
|
|
71
|
+
{
|
|
72
|
+
files: ['.mocharc.js', '.mocharc.cjs', '.mocharc.yaml', '.mocharc.yml', '.mocharc.json'],
|
|
73
|
+
framework: 'mocha',
|
|
74
|
+
runner: 'mocha',
|
|
75
|
+
command: 'npx mocha',
|
|
76
|
+
},
|
|
77
|
+
// pytest — config file forms
|
|
78
|
+
{
|
|
79
|
+
files: ['pytest.ini', 'conftest.py'],
|
|
80
|
+
framework: 'pytest',
|
|
81
|
+
runner: 'pytest',
|
|
82
|
+
command: 'pytest',
|
|
83
|
+
},
|
|
84
|
+
// setup.cfg with [tool:pytest]
|
|
85
|
+
{
|
|
86
|
+
files: ['setup.cfg'],
|
|
87
|
+
framework: 'pytest',
|
|
88
|
+
runner: 'pytest',
|
|
89
|
+
command: 'pytest',
|
|
90
|
+
// validated below — only if [tool:pytest] section present
|
|
91
|
+
contentCheck: (content) => content.includes('[tool:pytest]'),
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
for (const check of configChecks) {
|
|
96
|
+
for (const filename of check.files) {
|
|
97
|
+
const fullPath = join(cwd, filename);
|
|
98
|
+
if (!existsSync(fullPath)) continue;
|
|
99
|
+
|
|
100
|
+
if (check.contentCheck) {
|
|
101
|
+
const content = _readText(fullPath);
|
|
102
|
+
if (!content || !check.contentCheck(content)) continue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
framework: check.framework,
|
|
107
|
+
runner: check.runner,
|
|
108
|
+
configFile: filename,
|
|
109
|
+
command: check.command,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// --- Go: *_test.go files ---
|
|
115
|
+
{
|
|
116
|
+
const goMod = join(cwd, 'go.mod');
|
|
117
|
+
if (existsSync(goMod)) {
|
|
118
|
+
return {
|
|
119
|
+
framework: 'go-test',
|
|
120
|
+
runner: 'go',
|
|
121
|
+
configFile: 'go.mod',
|
|
122
|
+
command: 'go test ./...',
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// --- Rust: Cargo.toml ---
|
|
128
|
+
{
|
|
129
|
+
const cargoToml = join(cwd, 'Cargo.toml');
|
|
130
|
+
if (existsSync(cargoToml)) {
|
|
131
|
+
const content = _readText(cargoToml);
|
|
132
|
+
if (content) {
|
|
133
|
+
return {
|
|
134
|
+
framework: 'cargo-test',
|
|
135
|
+
runner: 'cargo',
|
|
136
|
+
configFile: 'Cargo.toml',
|
|
137
|
+
command: 'cargo test',
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// --- pyproject.toml with [tool.pytest] ---
|
|
144
|
+
{
|
|
145
|
+
const pyproject = join(cwd, 'pyproject.toml');
|
|
146
|
+
const content = _readText(pyproject);
|
|
147
|
+
if (content && content.includes('[tool.pytest')) {
|
|
148
|
+
return {
|
|
149
|
+
framework: 'pytest',
|
|
150
|
+
runner: 'pytest',
|
|
151
|
+
configFile: 'pyproject.toml',
|
|
152
|
+
command: 'pytest',
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// --- package.json devDependencies / dependencies ---
|
|
158
|
+
{
|
|
159
|
+
const pkgPath = join(cwd, 'package.json');
|
|
160
|
+
const pkg = _readJSON(pkgPath);
|
|
161
|
+
if (pkg) {
|
|
162
|
+
const allDeps = {
|
|
163
|
+
...(pkg.dependencies ?? {}),
|
|
164
|
+
...(pkg.devDependencies ?? {}),
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// Ordered: prefer vitest > jest > mocha > ava > tap
|
|
168
|
+
if (allDeps['vitest']) {
|
|
169
|
+
return {
|
|
170
|
+
framework: 'vitest',
|
|
171
|
+
runner: 'vitest',
|
|
172
|
+
configFile: 'package.json',
|
|
173
|
+
command: 'npx vitest run',
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
if (allDeps['jest'] || allDeps['ts-jest'] || allDeps['babel-jest']) {
|
|
177
|
+
return {
|
|
178
|
+
framework: 'jest',
|
|
179
|
+
runner: 'jest',
|
|
180
|
+
configFile: 'package.json',
|
|
181
|
+
command: 'npx jest',
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
if (allDeps['mocha']) {
|
|
185
|
+
return {
|
|
186
|
+
framework: 'mocha',
|
|
187
|
+
runner: 'mocha',
|
|
188
|
+
configFile: 'package.json',
|
|
189
|
+
command: 'npx mocha',
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
if (allDeps['ava']) {
|
|
193
|
+
return {
|
|
194
|
+
framework: 'ava',
|
|
195
|
+
runner: 'ava',
|
|
196
|
+
configFile: 'package.json',
|
|
197
|
+
command: 'npx ava',
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
if (allDeps['tap']) {
|
|
201
|
+
return {
|
|
202
|
+
framework: 'tap',
|
|
203
|
+
runner: 'tap',
|
|
204
|
+
configFile: 'package.json',
|
|
205
|
+
command: 'npx tap',
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// ---------------------------------------------------------------------------
|
|
215
|
+
// Item 126: scaffoldTestFramework
|
|
216
|
+
// ---------------------------------------------------------------------------
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Create a minimal test scaffold if no framework is detected.
|
|
220
|
+
* Only runs when detectTestFramework(cwd) returns null.
|
|
221
|
+
*
|
|
222
|
+
* @param {string} cwd - Project root directory
|
|
223
|
+
* @param {string} language - 'node' | 'python' | 'go' | 'rust'
|
|
224
|
+
* @returns {{ framework: string, configFile: string, testDir: string, command: string }}
|
|
225
|
+
*/
|
|
226
|
+
export function scaffoldTestFramework(cwd, language) {
|
|
227
|
+
const existing = detectTestFramework(cwd);
|
|
228
|
+
if (existing) return existing;
|
|
229
|
+
|
|
230
|
+
switch (language) {
|
|
231
|
+
case 'python': {
|
|
232
|
+
const testDir = join(cwd, 'tests');
|
|
233
|
+
mkdirSync(testDir, { recursive: true });
|
|
234
|
+
|
|
235
|
+
const conftest = join(cwd, 'conftest.py');
|
|
236
|
+
if (!existsSync(conftest)) {
|
|
237
|
+
writeFileSync(conftest, [
|
|
238
|
+
'# conftest.py — pytest configuration and shared fixtures',
|
|
239
|
+
'import pytest',
|
|
240
|
+
'',
|
|
241
|
+
'# Add project root to sys.path so tests can import the package.',
|
|
242
|
+
'import sys, os',
|
|
243
|
+
'sys.path.insert(0, os.path.dirname(__file__))',
|
|
244
|
+
'',
|
|
245
|
+
].join('\n'), 'utf-8');
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const initTest = join(testDir, 'test_golden.py');
|
|
249
|
+
if (!existsSync(initTest)) {
|
|
250
|
+
writeFileSync(initTest, [
|
|
251
|
+
'"""Golden flow placeholder — replace with real lifecycle tests."""',
|
|
252
|
+
'',
|
|
253
|
+
'def test_placeholder():',
|
|
254
|
+
' """Minimal placeholder that passes until real tests are written."""',
|
|
255
|
+
' assert True',
|
|
256
|
+
'',
|
|
257
|
+
].join('\n'), 'utf-8');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return {
|
|
261
|
+
framework: 'pytest',
|
|
262
|
+
configFile: 'conftest.py',
|
|
263
|
+
testDir: 'tests',
|
|
264
|
+
command: 'pytest',
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
case 'go': {
|
|
269
|
+
// Find first .go file to derive the package name, defaulting to 'main'
|
|
270
|
+
let packageName = 'main';
|
|
271
|
+
try {
|
|
272
|
+
const goFiles = readdirSync(cwd).filter(
|
|
273
|
+
f => f.endsWith('.go') && !f.endsWith('_test.go')
|
|
274
|
+
);
|
|
275
|
+
if (goFiles.length > 0) {
|
|
276
|
+
const src = _readText(join(cwd, goFiles[0]));
|
|
277
|
+
const m = src?.match(/^package\s+(\w+)/m);
|
|
278
|
+
if (m) packageName = m[1];
|
|
279
|
+
}
|
|
280
|
+
} catch { /* use default */ }
|
|
281
|
+
|
|
282
|
+
const testFile = join(cwd, 'golden_test.go');
|
|
283
|
+
if (!existsSync(testFile)) {
|
|
284
|
+
writeFileSync(testFile, [
|
|
285
|
+
`package ${packageName}`,
|
|
286
|
+
'',
|
|
287
|
+
'import "testing"',
|
|
288
|
+
'',
|
|
289
|
+
'// TestGoldenFlow is a placeholder — replace with real lifecycle tests.',
|
|
290
|
+
'func TestGoldenFlow(t *testing.T) {',
|
|
291
|
+
' // TODO: implement golden flow test',
|
|
292
|
+
'}',
|
|
293
|
+
'',
|
|
294
|
+
].join('\n'), 'utf-8');
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return {
|
|
298
|
+
framework: 'go-test',
|
|
299
|
+
configFile: 'golden_test.go',
|
|
300
|
+
testDir: '.',
|
|
301
|
+
command: 'go test ./...',
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
case 'rust': {
|
|
306
|
+
const testDir = join(cwd, 'tests');
|
|
307
|
+
mkdirSync(testDir, { recursive: true });
|
|
308
|
+
|
|
309
|
+
const testFile = join(testDir, 'golden.rs');
|
|
310
|
+
if (!existsSync(testFile)) {
|
|
311
|
+
writeFileSync(testFile, [
|
|
312
|
+
'//! Golden flow placeholder — replace with real lifecycle tests.',
|
|
313
|
+
'',
|
|
314
|
+
'#[test]',
|
|
315
|
+
'fn placeholder() {',
|
|
316
|
+
' // TODO: implement golden flow test',
|
|
317
|
+
'}',
|
|
318
|
+
'',
|
|
319
|
+
].join('\n'), 'utf-8');
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
return {
|
|
323
|
+
framework: 'cargo-test',
|
|
324
|
+
configFile: 'tests/golden.rs',
|
|
325
|
+
testDir: 'tests',
|
|
326
|
+
command: 'cargo test',
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
case 'node':
|
|
331
|
+
default: {
|
|
332
|
+
const testDir = join(cwd, 'test');
|
|
333
|
+
mkdirSync(testDir, { recursive: true });
|
|
334
|
+
|
|
335
|
+
// Prefer vitest if package.json exists; otherwise scaffold bare vitest config
|
|
336
|
+
const pkgPath = join(cwd, 'package.json');
|
|
337
|
+
const pkg = _readJSON(pkgPath);
|
|
338
|
+
const useVitest = !pkg?.devDependencies?.jest && !pkg?.dependencies?.jest;
|
|
339
|
+
|
|
340
|
+
if (useVitest) {
|
|
341
|
+
const configPath = join(cwd, 'vitest.config.js');
|
|
342
|
+
if (!existsSync(configPath)) {
|
|
343
|
+
writeFileSync(configPath, [
|
|
344
|
+
'// vitest.config.js — generated by Compose test-bootstrap',
|
|
345
|
+
"import { defineConfig } from 'vitest/config';",
|
|
346
|
+
'',
|
|
347
|
+
'export default defineConfig({',
|
|
348
|
+
' test: {',
|
|
349
|
+
" include: ['test/**/*.test.{js,ts}'],",
|
|
350
|
+
' },',
|
|
351
|
+
'});',
|
|
352
|
+
'',
|
|
353
|
+
].join('\n'), 'utf-8');
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const setupPath = join(testDir, 'setup.js');
|
|
357
|
+
if (!existsSync(setupPath)) {
|
|
358
|
+
writeFileSync(setupPath, [
|
|
359
|
+
'// test/setup.js — shared test helpers',
|
|
360
|
+
"// Import this in tests via: import './setup.js'",
|
|
361
|
+
'',
|
|
362
|
+
'// Example: assert helpers, fixtures, shared state',
|
|
363
|
+
'',
|
|
364
|
+
].join('\n'), 'utf-8');
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return {
|
|
368
|
+
framework: 'vitest',
|
|
369
|
+
configFile: 'vitest.config.js',
|
|
370
|
+
testDir: 'test',
|
|
371
|
+
command: 'npx vitest run',
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Jest fallback
|
|
376
|
+
const configPath = join(cwd, 'jest.config.js');
|
|
377
|
+
if (!existsSync(configPath)) {
|
|
378
|
+
writeFileSync(configPath, [
|
|
379
|
+
'// jest.config.js — generated by Compose test-bootstrap',
|
|
380
|
+
"export default { testMatch: ['**/test/**/*.test.js'] };",
|
|
381
|
+
'',
|
|
382
|
+
].join('\n'), 'utf-8');
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const setupPath = join(testDir, 'setup.js');
|
|
386
|
+
if (!existsSync(setupPath)) {
|
|
387
|
+
writeFileSync(setupPath, [
|
|
388
|
+
'// test/setup.js — shared test helpers',
|
|
389
|
+
'',
|
|
390
|
+
].join('\n'), 'utf-8');
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return {
|
|
394
|
+
framework: 'jest',
|
|
395
|
+
configFile: 'jest.config.js',
|
|
396
|
+
testDir: 'test',
|
|
397
|
+
command: 'npx jest',
|
|
398
|
+
};
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|