create-foundry-workspace 1.0.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 +170 -0
- package/bin/index.js +2 -0
- package/dist/adapters/adapter.interface.d.ts +13 -0
- package/dist/adapters/adapter.interface.js +2 -0
- package/dist/adapters/agents.adapter.d.ts +10 -0
- package/dist/adapters/agents.adapter.js +16 -0
- package/dist/adapters/claude.adapter.d.ts +10 -0
- package/dist/adapters/claude.adapter.js +30 -0
- package/dist/adapters/codex.adapter.d.ts +10 -0
- package/dist/adapters/codex.adapter.js +33 -0
- package/dist/adapters/copilot.adapter.d.ts +10 -0
- package/dist/adapters/copilot.adapter.js +40 -0
- package/dist/adapters/cursor.adapter.d.ts +10 -0
- package/dist/adapters/cursor.adapter.js +41 -0
- package/dist/adapters/gemini.adapter.d.ts +10 -0
- package/dist/adapters/gemini.adapter.js +34 -0
- package/dist/adapters/generic.adapter.d.ts +10 -0
- package/dist/adapters/generic.adapter.js +34 -0
- package/dist/adapters/registry.d.ts +12 -0
- package/dist/adapters/registry.js +38 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.js +7 -0
- package/dist/cli/command.d.ts +2 -0
- package/dist/cli/command.js +96 -0
- package/dist/cli/formatters.d.ts +5 -0
- package/dist/cli/formatters.js +30 -0
- package/dist/cli/prompts.d.ts +11 -0
- package/dist/cli/prompts.js +140 -0
- package/dist/config/defaults.d.ts +8 -0
- package/dist/config/defaults.js +7 -0
- package/dist/config/schema.d.ts +95 -0
- package/dist/config/schema.js +51 -0
- package/dist/core/context.d.ts +3 -0
- package/dist/core/context.js +16 -0
- package/dist/core/generator.d.ts +10 -0
- package/dist/core/generator.js +97 -0
- package/dist/core/interpolator.d.ts +6 -0
- package/dist/core/interpolator.js +13 -0
- package/dist/detection/detector.d.ts +13 -0
- package/dist/detection/detector.js +67 -0
- package/dist/detection/rules/go.d.ts +3 -0
- package/dist/detection/rules/go.js +19 -0
- package/dist/detection/rules/java.d.ts +3 -0
- package/dist/detection/rules/java.js +30 -0
- package/dist/detection/rules/node.d.ts +8 -0
- package/dist/detection/rules/node.js +36 -0
- package/dist/detection/rules/python.d.ts +3 -0
- package/dist/detection/rules/python.js +30 -0
- package/dist/detection/rules/rust.d.ts +3 -0
- package/dist/detection/rules/rust.js +19 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +12 -0
- package/dist/io/paths.d.ts +11 -0
- package/dist/io/paths.js +21 -0
- package/dist/io/writer.d.ts +23 -0
- package/dist/io/writer.js +56 -0
- package/dist/templates/agents.d.ts +3 -0
- package/dist/templates/agents.js +41 -0
- package/dist/templates/ai-context.d.ts +3 -0
- package/dist/templates/ai-context.js +28 -0
- package/dist/templates/ai-dir/architecture.d.ts +3 -0
- package/dist/templates/ai-dir/architecture.js +13 -0
- package/dist/templates/ai-dir/context.d.ts +3 -0
- package/dist/templates/ai-dir/context.js +14 -0
- package/dist/templates/ai-dir/decisions.d.ts +3 -0
- package/dist/templates/ai-dir/decisions.js +21 -0
- package/dist/templates/ai-dir/rules.d.ts +3 -0
- package/dist/templates/ai-dir/rules.js +12 -0
- package/dist/templates/ai-dir/tasks.d.ts +3 -0
- package/dist/templates/ai-dir/tasks.js +31 -0
- package/dist/templates/app-types/api.d.ts +2 -0
- package/dist/templates/app-types/api.js +5 -0
- package/dist/templates/app-types/cli.d.ts +2 -0
- package/dist/templates/app-types/cli.js +5 -0
- package/dist/templates/app-types/data-ml.d.ts +2 -0
- package/dist/templates/app-types/data-ml.js +5 -0
- package/dist/templates/app-types/fullstack.d.ts +2 -0
- package/dist/templates/app-types/fullstack.js +5 -0
- package/dist/templates/app-types/index.d.ts +3 -0
- package/dist/templates/app-types/index.js +30 -0
- package/dist/templates/app-types/library.d.ts +2 -0
- package/dist/templates/app-types/library.js +5 -0
- package/dist/templates/app-types/mobile.d.ts +2 -0
- package/dist/templates/app-types/mobile.js +5 -0
- package/dist/templates/app-types/other.d.ts +2 -0
- package/dist/templates/app-types/other.js +5 -0
- package/dist/templates/app-types/web.d.ts +2 -0
- package/dist/templates/app-types/web.js +5 -0
- package/dist/templates/base/guardrails.d.ts +3 -0
- package/dist/templates/base/guardrails.js +20 -0
- package/dist/templates/contributing.d.ts +3 -0
- package/dist/templates/contributing.js +19 -0
- package/dist/templates/docs/architecture-doc.d.ts +3 -0
- package/dist/templates/docs/architecture-doc.js +22 -0
- package/dist/templates/docs/decisions-doc.d.ts +3 -0
- package/dist/templates/docs/decisions-doc.js +19 -0
- package/dist/templates/docs/development-doc.d.ts +3 -0
- package/dist/templates/docs/development-doc.js +21 -0
- package/dist/templates/docs/docs-readme.d.ts +3 -0
- package/dist/templates/docs/docs-readme.js +15 -0
- package/dist/templates/gitignore.d.ts +3 -0
- package/dist/templates/gitignore.js +109 -0
- package/dist/templates/guardrails-doc.d.ts +3 -0
- package/dist/templates/guardrails-doc.js +15 -0
- package/dist/templates/languages/go.d.ts +4 -0
- package/dist/templates/languages/go.js +11 -0
- package/dist/templates/languages/index.d.ts +7 -0
- package/dist/templates/languages/index.js +27 -0
- package/dist/templates/languages/java.d.ts +4 -0
- package/dist/templates/languages/java.js +11 -0
- package/dist/templates/languages/javascript.d.ts +4 -0
- package/dist/templates/languages/javascript.js +11 -0
- package/dist/templates/languages/other.d.ts +4 -0
- package/dist/templates/languages/other.js +11 -0
- package/dist/templates/languages/python.d.ts +4 -0
- package/dist/templates/languages/python.js +11 -0
- package/dist/templates/languages/rust.d.ts +4 -0
- package/dist/templates/languages/rust.js +11 -0
- package/dist/templates/languages/typescript.d.ts +4 -0
- package/dist/templates/languages/typescript.js +11 -0
- package/dist/templates/manifest.d.ts +3 -0
- package/dist/templates/manifest.js +17 -0
- package/dist/templates/readme.d.ts +3 -0
- package/dist/templates/readme.js +35 -0
- package/package.json +64 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { defaultRegistry } from '../adapters/registry.js';
|
|
4
|
+
import { CliOptionsSchema, WorkspaceConfigSchema } from '../config/schema.js';
|
|
5
|
+
import { buildResolvedContext } from '../core/context.js';
|
|
6
|
+
import { generateWorkspace } from '../core/generator.js';
|
|
7
|
+
import { detectProjectEnvironment } from '../detection/detector.js';
|
|
8
|
+
import { writeWorkspace } from '../io/writer.js';
|
|
9
|
+
import { formatDryRunBanner, formatSuccessSummary } from './formatters.js';
|
|
10
|
+
import { runInteractivePrompts } from './prompts.js';
|
|
11
|
+
export async function runCli(argv = process.argv) {
|
|
12
|
+
const program = new Command();
|
|
13
|
+
program
|
|
14
|
+
.name('create-foundry-workspace')
|
|
15
|
+
.description('Deterministic, zero-LLM CLI for initializing AI-ready software development workspaces')
|
|
16
|
+
.version('1.0.0')
|
|
17
|
+
.argument('[directory]', 'Target directory to initialize')
|
|
18
|
+
.option('-n, --name <string>', 'Project name')
|
|
19
|
+
.option('-t, --type <type>', 'Application type (web | api | fullstack | cli | library | data-ml | mobile | custom)')
|
|
20
|
+
.option('-l, --lang <language>', 'Primary language (typescript | javascript | python | java | go | rust | other)')
|
|
21
|
+
.option('-r, --repo <repository>', 'Repository hosting (github | gitlab | none)', 'github')
|
|
22
|
+
.option('-s, --strictness <level>', 'Guardrail level (standard | strict)', 'standard')
|
|
23
|
+
.option('-a, --ai <adapters>', 'Comma-separated adapter IDs (e.g. claude,cursor,codex,gemini,agents,generic)')
|
|
24
|
+
.option('--dry-run', 'Preview generated files in memory without writing to disk', false)
|
|
25
|
+
.option('-f, --force', 'Overwrite existing files in target directory', false)
|
|
26
|
+
.option('-y, --yes', 'Accept defaults non-interactively', false);
|
|
27
|
+
program.parse(argv);
|
|
28
|
+
const rawOpts = program.opts();
|
|
29
|
+
const opts = CliOptionsSchema.parse(rawOpts);
|
|
30
|
+
// Check if directory argument was explicitly provided by the user
|
|
31
|
+
const explicitDirArg = program.args[0];
|
|
32
|
+
const initialTargetDir = explicitDirArg ? path.resolve(explicitDirArg) : process.cwd();
|
|
33
|
+
// 1. Detect project environment from filesystem markers
|
|
34
|
+
const detected = await detectProjectEnvironment(initialTargetDir);
|
|
35
|
+
const defaultAdapters = opts.ai
|
|
36
|
+
? opts.ai
|
|
37
|
+
.split(',')
|
|
38
|
+
.map((s) => s.trim().toLowerCase())
|
|
39
|
+
.filter((s) => defaultRegistry.has(s))
|
|
40
|
+
: ['claude', 'cursor', 'agents'];
|
|
41
|
+
let config;
|
|
42
|
+
// 2. Determine interactive vs. non-interactive flow
|
|
43
|
+
const isNonInteractive = opts.yes || !process.stdout.isTTY || !!opts.name || !!opts.type || !!opts.lang;
|
|
44
|
+
if (isNonInteractive) {
|
|
45
|
+
const rawConfig = {
|
|
46
|
+
version: '1',
|
|
47
|
+
project: {
|
|
48
|
+
name: opts.name || detected.name,
|
|
49
|
+
type: (opts.type || detected.type),
|
|
50
|
+
language: (opts.lang || detected.language),
|
|
51
|
+
},
|
|
52
|
+
repository: (opts.repo || 'github'),
|
|
53
|
+
strictness: (opts.strictness || 'standard'),
|
|
54
|
+
adapters: defaultAdapters.length > 0 ? defaultAdapters : ['claude', 'cursor', 'agents'],
|
|
55
|
+
};
|
|
56
|
+
config = WorkspaceConfigSchema.parse(rawConfig);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
config = await runInteractivePrompts(detected, {
|
|
60
|
+
name: opts.name,
|
|
61
|
+
type: opts.type,
|
|
62
|
+
language: opts.lang,
|
|
63
|
+
repository: opts.repo,
|
|
64
|
+
strictness: opts.strictness,
|
|
65
|
+
adapters: defaultAdapters,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
// 3. Resolve target directory:
|
|
69
|
+
// - If an explicit directory argument was passed (e.g. `create-foundry-workspace .` or `create-foundry-workspace my-dir`), resolve that.
|
|
70
|
+
// - If detected in an existing project (e.g. package.json exists in cwd) or project name matches current directory name, initialize in cwd (`.`).
|
|
71
|
+
// - Otherwise, create a new subfolder named after the project in cwd.
|
|
72
|
+
let targetDir;
|
|
73
|
+
if (explicitDirArg) {
|
|
74
|
+
targetDir = path.resolve(explicitDirArg);
|
|
75
|
+
}
|
|
76
|
+
else if (detected.isDetected || config.project.name === path.basename(process.cwd())) {
|
|
77
|
+
targetDir = process.cwd();
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
targetDir = path.resolve(process.cwd(), config.project.name);
|
|
81
|
+
}
|
|
82
|
+
// 4. Resolve context and generate files (PURE IN-MEMORY)
|
|
83
|
+
const context = buildResolvedContext(config);
|
|
84
|
+
const generatedFiles = generateWorkspace(context, defaultRegistry);
|
|
85
|
+
// 5. Handle dry-run or write to disk (I/O BOUNDARY)
|
|
86
|
+
if (opts.dryRun) {
|
|
87
|
+
formatDryRunBanner(config.project.name, targetDir, generatedFiles);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const writeResult = await writeWorkspace(targetDir, generatedFiles, {
|
|
91
|
+
dryRun: false,
|
|
92
|
+
force: opts.force,
|
|
93
|
+
});
|
|
94
|
+
formatSuccessSummary(targetDir, writeResult);
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=command.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { GeneratedFile } from '../adapters/adapter.interface.js';
|
|
2
|
+
import { WriteResult } from '../io/writer.js';
|
|
3
|
+
export declare function formatDryRunBanner(name: string, targetDir: string, files: GeneratedFile[]): void;
|
|
4
|
+
export declare function formatSuccessSummary(targetDir: string, result: WriteResult): void;
|
|
5
|
+
//# sourceMappingURL=formatters.d.ts.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export function formatDryRunBanner(name, targetDir, files) {
|
|
2
|
+
console.log(`\n============================================================`);
|
|
3
|
+
console.log(` Foundry (create-foundry-workspace) — DRY RUN PREVIEW`);
|
|
4
|
+
console.log(`============================================================`);
|
|
5
|
+
console.log(`Project: ${name}`);
|
|
6
|
+
console.log(`Target Directory: ${targetDir}`);
|
|
7
|
+
console.log(`Files to create: ${files.length}`);
|
|
8
|
+
console.log(`Mode: No disk writes performed (--dry-run enabled)\n`);
|
|
9
|
+
console.log(`Generated Workspace File Tree:`);
|
|
10
|
+
for (const file of files) {
|
|
11
|
+
console.log(` + ${file.relativePath}`);
|
|
12
|
+
}
|
|
13
|
+
console.log(`============================================================\n`);
|
|
14
|
+
}
|
|
15
|
+
export function formatSuccessSummary(targetDir, result) {
|
|
16
|
+
console.log(`\n✔ AI Workspace initialized successfully in ${targetDir}`);
|
|
17
|
+
console.log(` Created: ${result.written.length} files`);
|
|
18
|
+
if (result.skipped.length > 0) {
|
|
19
|
+
console.log(` Skipped (existing): ${result.skipped.length} files (use --force to overwrite)`);
|
|
20
|
+
}
|
|
21
|
+
console.log(`\nGenerated files:`);
|
|
22
|
+
for (const path of result.written) {
|
|
23
|
+
console.log(` + ${path}`);
|
|
24
|
+
}
|
|
25
|
+
console.log(`\nNext steps:`);
|
|
26
|
+
console.log(` 1. Open the project in Cursor, VS Code (Copilot), or Claude Code.`);
|
|
27
|
+
console.log(` 2. Review AGENTS.md and README.md.`);
|
|
28
|
+
console.log(` 3. Start developing!\n`);
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=formatters.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AppType, Language, Repository, Strictness, WorkspaceConfig } from '../config/schema.js';
|
|
2
|
+
import { DetectedEnvironment } from '../detection/detector.js';
|
|
3
|
+
export declare function runInteractivePrompts(detected: DetectedEnvironment, defaults: {
|
|
4
|
+
name?: string;
|
|
5
|
+
type?: AppType;
|
|
6
|
+
language?: Language;
|
|
7
|
+
repository?: Repository;
|
|
8
|
+
strictness?: Strictness;
|
|
9
|
+
adapters?: string[];
|
|
10
|
+
}): Promise<WorkspaceConfig>;
|
|
11
|
+
//# sourceMappingURL=prompts.d.ts.map
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import * as p from '@clack/prompts';
|
|
2
|
+
import { defaultRegistry } from '../adapters/registry.js';
|
|
3
|
+
export async function runInteractivePrompts(detected, defaults) {
|
|
4
|
+
// Guard against headless/non-interactive terminals
|
|
5
|
+
if (!process.stdout.isTTY) {
|
|
6
|
+
return {
|
|
7
|
+
version: '1',
|
|
8
|
+
project: {
|
|
9
|
+
name: defaults.name || detected.name,
|
|
10
|
+
type: defaults.type || detected.type,
|
|
11
|
+
language: defaults.language || detected.language,
|
|
12
|
+
},
|
|
13
|
+
repository: defaults.repository || 'github',
|
|
14
|
+
strictness: defaults.strictness || 'standard',
|
|
15
|
+
adapters: defaults.adapters || defaultRegistry.getDefaultIds(),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
p.intro('Foundry (create-foundry-workspace) v1.0.0');
|
|
19
|
+
// Step 1: Project Name
|
|
20
|
+
const initialName = defaults.name || (detected.name !== 'my-workspace' && detected.name !== 'Extentions' ? detected.name : 'my-app');
|
|
21
|
+
const nameInput = await p.text({
|
|
22
|
+
message: 'Project name:',
|
|
23
|
+
defaultValue: initialName,
|
|
24
|
+
placeholder: initialName,
|
|
25
|
+
validate: (val) => {
|
|
26
|
+
if (!val || !/^[a-zA-Z0-9_\-.]+$/.test(val)) {
|
|
27
|
+
return 'Project name must be alphanumeric with dashes/underscores/dots.';
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
if (p.isCancel(nameInput)) {
|
|
32
|
+
p.cancel('Initialization cancelled.');
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
// Step 2: Project type
|
|
36
|
+
const selectedType = await p.select({
|
|
37
|
+
message: 'Project type:',
|
|
38
|
+
options: [
|
|
39
|
+
{ value: 'web', label: 'Web App', hint: 'Frontend / SPA' },
|
|
40
|
+
{ value: 'api', label: 'Backend/API', hint: 'REST / GraphQL / RPC services' },
|
|
41
|
+
{ value: 'fullstack', label: 'Full Stack', hint: 'Client + Server application' },
|
|
42
|
+
{ value: 'cli', label: 'CLI', hint: 'Command-line tool or terminal binary' },
|
|
43
|
+
{ value: 'library', label: 'Library/Package', hint: 'Reusable package' },
|
|
44
|
+
{ value: 'data-ml', label: 'Data/ML', hint: 'Data pipelines & machine learning' },
|
|
45
|
+
{ value: 'mobile', label: 'Mobile', hint: 'Native or cross-platform mobile app' },
|
|
46
|
+
{ value: 'custom', label: 'Custom', hint: 'Custom architecture' },
|
|
47
|
+
],
|
|
48
|
+
initialValue: defaults.type || detected.type,
|
|
49
|
+
});
|
|
50
|
+
if (p.isCancel(selectedType)) {
|
|
51
|
+
p.cancel('Initialization cancelled.');
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
// Step 3: Language
|
|
55
|
+
const selectedLang = await p.select({
|
|
56
|
+
message: 'Language:',
|
|
57
|
+
options: [
|
|
58
|
+
{ value: 'typescript', label: 'TypeScript' },
|
|
59
|
+
{ value: 'javascript', label: 'JavaScript' },
|
|
60
|
+
{ value: 'python', label: 'Python' },
|
|
61
|
+
{ value: 'java', label: 'Java' },
|
|
62
|
+
{ value: 'go', label: 'Go' },
|
|
63
|
+
{ value: 'rust', label: 'Rust' },
|
|
64
|
+
{ value: 'other', label: 'Other' },
|
|
65
|
+
],
|
|
66
|
+
initialValue: defaults.language || detected.language,
|
|
67
|
+
});
|
|
68
|
+
if (p.isCancel(selectedLang)) {
|
|
69
|
+
p.cancel('Initialization cancelled.');
|
|
70
|
+
process.exit(0);
|
|
71
|
+
}
|
|
72
|
+
// Step 4: AI workflow
|
|
73
|
+
const selectedAdapters = await p.multiselect({
|
|
74
|
+
message: 'AI workflow (Press Enter to default to Multi-agent / Generic):',
|
|
75
|
+
options: [
|
|
76
|
+
{ value: 'agents', label: 'Multi-agent', hint: 'AGENTS.md open standard' },
|
|
77
|
+
{ value: 'claude', label: 'Claude', hint: 'CLAUDE.md for Claude Code' },
|
|
78
|
+
{ value: 'cursor', label: 'Cursor', hint: '.cursor/rules/ for Cursor IDE' },
|
|
79
|
+
{ value: 'copilot', label: 'Copilot', hint: '.github/copilot-instructions.md for GitHub Copilot' },
|
|
80
|
+
{ value: 'codex', label: 'Codex', hint: 'CODEX.md for OpenAI Codex' },
|
|
81
|
+
{ value: 'gemini', label: 'Gemini', hint: 'GEMINI.md for Google Gemini CLI' },
|
|
82
|
+
{ value: 'generic', label: 'Generic', hint: 'AI_INSTRUCTIONS.md for any tool' },
|
|
83
|
+
],
|
|
84
|
+
required: false,
|
|
85
|
+
});
|
|
86
|
+
if (p.isCancel(selectedAdapters)) {
|
|
87
|
+
p.cancel('Initialization cancelled.');
|
|
88
|
+
process.exit(0);
|
|
89
|
+
}
|
|
90
|
+
const finalAdapters = Array.isArray(selectedAdapters) && selectedAdapters.length > 0
|
|
91
|
+
? selectedAdapters
|
|
92
|
+
: ['agents'];
|
|
93
|
+
// Step 5: Repository
|
|
94
|
+
const selectedRepo = await p.select({
|
|
95
|
+
message: 'Repository:',
|
|
96
|
+
options: [
|
|
97
|
+
{ value: 'github', label: 'GitHub', hint: '.github/ configuration' },
|
|
98
|
+
{ value: 'gitlab', label: 'GitLab', hint: '.gitlab/ configuration' },
|
|
99
|
+
{ value: 'none', label: 'None', hint: 'No remote repository config' },
|
|
100
|
+
],
|
|
101
|
+
initialValue: defaults.repository || 'github',
|
|
102
|
+
});
|
|
103
|
+
if (p.isCancel(selectedRepo)) {
|
|
104
|
+
p.cancel('Initialization cancelled.');
|
|
105
|
+
process.exit(0);
|
|
106
|
+
}
|
|
107
|
+
// Step 6: Guardrail Strictness
|
|
108
|
+
const selectedStrictness = await p.select({
|
|
109
|
+
message: 'Guardrail Strictness:',
|
|
110
|
+
options: [
|
|
111
|
+
{
|
|
112
|
+
value: 'standard',
|
|
113
|
+
label: 'Standard',
|
|
114
|
+
hint: 'Modular architecture, build/test guidelines, basic security',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
value: 'strict',
|
|
118
|
+
label: 'Strict',
|
|
119
|
+
hint: 'Mandatory tests, strict typing, destructive command restriction',
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
initialValue: defaults.strictness || 'standard',
|
|
123
|
+
});
|
|
124
|
+
if (p.isCancel(selectedStrictness)) {
|
|
125
|
+
p.cancel('Initialization cancelled.');
|
|
126
|
+
process.exit(0);
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
version: '1',
|
|
130
|
+
project: {
|
|
131
|
+
name: nameInput,
|
|
132
|
+
type: selectedType,
|
|
133
|
+
language: selectedLang,
|
|
134
|
+
},
|
|
135
|
+
repository: selectedRepo,
|
|
136
|
+
strictness: selectedStrictness,
|
|
137
|
+
adapters: finalAdapters,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=prompts.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { AppType, Language, Strictness } from './schema.js';
|
|
2
|
+
export declare const DEFAULT_APP_TYPE: AppType;
|
|
3
|
+
export declare const DEFAULT_LANGUAGE: Language;
|
|
4
|
+
export declare const DEFAULT_STRICTNESS: Strictness;
|
|
5
|
+
export declare const DEFAULT_ADAPTERS: string[];
|
|
6
|
+
export declare const SPEC_VERSION = "1";
|
|
7
|
+
export declare const GENERATOR_NAME = "create-foundry-workspace";
|
|
8
|
+
//# sourceMappingURL=defaults.d.ts.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const DEFAULT_APP_TYPE = 'fullstack';
|
|
2
|
+
export const DEFAULT_LANGUAGE = 'typescript';
|
|
3
|
+
export const DEFAULT_STRICTNESS = 'standard';
|
|
4
|
+
export const DEFAULT_ADAPTERS = ['agents', 'cursor', 'claude', 'copilot'];
|
|
5
|
+
export const SPEC_VERSION = '1';
|
|
6
|
+
export const GENERATOR_NAME = 'create-foundry-workspace';
|
|
7
|
+
//# sourceMappingURL=defaults.js.map
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const AppTypeSchema: z.ZodEnum<["web", "api", "fullstack", "cli", "library", "data-ml", "mobile", "custom"]>;
|
|
3
|
+
export type AppType = z.infer<typeof AppTypeSchema>;
|
|
4
|
+
export declare const LanguageSchema: z.ZodEnum<["typescript", "javascript", "python", "java", "go", "rust", "other"]>;
|
|
5
|
+
export type Language = z.infer<typeof LanguageSchema>;
|
|
6
|
+
export declare const RepositorySchema: z.ZodEnum<["github", "gitlab", "none"]>;
|
|
7
|
+
export type Repository = z.infer<typeof RepositorySchema>;
|
|
8
|
+
export declare const StrictnessSchema: z.ZodEnum<["standard", "strict"]>;
|
|
9
|
+
export type Strictness = z.infer<typeof StrictnessSchema>;
|
|
10
|
+
export declare const WorkspaceConfigSchema: z.ZodObject<{
|
|
11
|
+
version: z.ZodLiteral<"1">;
|
|
12
|
+
project: z.ZodObject<{
|
|
13
|
+
name: z.ZodString;
|
|
14
|
+
type: z.ZodEnum<["web", "api", "fullstack", "cli", "library", "data-ml", "mobile", "custom"]>;
|
|
15
|
+
language: z.ZodEnum<["typescript", "javascript", "python", "java", "go", "rust", "other"]>;
|
|
16
|
+
}, "strip", z.ZodTypeAny, {
|
|
17
|
+
type: "web" | "api" | "fullstack" | "cli" | "library" | "data-ml" | "mobile" | "custom";
|
|
18
|
+
name: string;
|
|
19
|
+
language: "typescript" | "javascript" | "python" | "java" | "go" | "rust" | "other";
|
|
20
|
+
}, {
|
|
21
|
+
type: "web" | "api" | "fullstack" | "cli" | "library" | "data-ml" | "mobile" | "custom";
|
|
22
|
+
name: string;
|
|
23
|
+
language: "typescript" | "javascript" | "python" | "java" | "go" | "rust" | "other";
|
|
24
|
+
}>;
|
|
25
|
+
repository: z.ZodDefault<z.ZodEnum<["github", "gitlab", "none"]>>;
|
|
26
|
+
strictness: z.ZodDefault<z.ZodEnum<["standard", "strict"]>>;
|
|
27
|
+
adapters: z.ZodEffects<z.ZodDefault<z.ZodArray<z.ZodString, "many">>, string[], string[] | undefined>;
|
|
28
|
+
}, "strip", z.ZodTypeAny, {
|
|
29
|
+
version: "1";
|
|
30
|
+
project: {
|
|
31
|
+
type: "web" | "api" | "fullstack" | "cli" | "library" | "data-ml" | "mobile" | "custom";
|
|
32
|
+
name: string;
|
|
33
|
+
language: "typescript" | "javascript" | "python" | "java" | "go" | "rust" | "other";
|
|
34
|
+
};
|
|
35
|
+
repository: "github" | "gitlab" | "none";
|
|
36
|
+
strictness: "standard" | "strict";
|
|
37
|
+
adapters: string[];
|
|
38
|
+
}, {
|
|
39
|
+
version: "1";
|
|
40
|
+
project: {
|
|
41
|
+
type: "web" | "api" | "fullstack" | "cli" | "library" | "data-ml" | "mobile" | "custom";
|
|
42
|
+
name: string;
|
|
43
|
+
language: "typescript" | "javascript" | "python" | "java" | "go" | "rust" | "other";
|
|
44
|
+
};
|
|
45
|
+
repository?: "github" | "gitlab" | "none" | undefined;
|
|
46
|
+
strictness?: "standard" | "strict" | undefined;
|
|
47
|
+
adapters?: string[] | undefined;
|
|
48
|
+
}>;
|
|
49
|
+
export type WorkspaceConfig = z.infer<typeof WorkspaceConfigSchema>;
|
|
50
|
+
export interface CommandSet {
|
|
51
|
+
readonly build: string;
|
|
52
|
+
readonly test: string;
|
|
53
|
+
readonly dev: string;
|
|
54
|
+
readonly lint: string;
|
|
55
|
+
}
|
|
56
|
+
export interface ResolvedWorkspaceContext {
|
|
57
|
+
readonly config: WorkspaceConfig;
|
|
58
|
+
readonly commands: CommandSet;
|
|
59
|
+
readonly languageRules: string;
|
|
60
|
+
readonly appTypeRules: string;
|
|
61
|
+
readonly baseGuardrails: string;
|
|
62
|
+
}
|
|
63
|
+
export declare const CliOptionsSchema: z.ZodObject<{
|
|
64
|
+
name: z.ZodOptional<z.ZodString>;
|
|
65
|
+
type: z.ZodOptional<z.ZodEnum<["web", "api", "fullstack", "cli", "library", "data-ml", "mobile", "custom"]>>;
|
|
66
|
+
lang: z.ZodOptional<z.ZodEnum<["typescript", "javascript", "python", "java", "go", "rust", "other"]>>;
|
|
67
|
+
repo: z.ZodOptional<z.ZodEnum<["github", "gitlab", "none"]>>;
|
|
68
|
+
strictness: z.ZodOptional<z.ZodEnum<["standard", "strict"]>>;
|
|
69
|
+
ai: z.ZodOptional<z.ZodString>;
|
|
70
|
+
dryRun: z.ZodDefault<z.ZodBoolean>;
|
|
71
|
+
force: z.ZodDefault<z.ZodBoolean>;
|
|
72
|
+
yes: z.ZodDefault<z.ZodBoolean>;
|
|
73
|
+
}, "strip", z.ZodTypeAny, {
|
|
74
|
+
dryRun: boolean;
|
|
75
|
+
force: boolean;
|
|
76
|
+
yes: boolean;
|
|
77
|
+
type?: "web" | "api" | "fullstack" | "cli" | "library" | "data-ml" | "mobile" | "custom" | undefined;
|
|
78
|
+
name?: string | undefined;
|
|
79
|
+
strictness?: "standard" | "strict" | undefined;
|
|
80
|
+
lang?: "typescript" | "javascript" | "python" | "java" | "go" | "rust" | "other" | undefined;
|
|
81
|
+
repo?: "github" | "gitlab" | "none" | undefined;
|
|
82
|
+
ai?: string | undefined;
|
|
83
|
+
}, {
|
|
84
|
+
type?: "web" | "api" | "fullstack" | "cli" | "library" | "data-ml" | "mobile" | "custom" | undefined;
|
|
85
|
+
name?: string | undefined;
|
|
86
|
+
strictness?: "standard" | "strict" | undefined;
|
|
87
|
+
lang?: "typescript" | "javascript" | "python" | "java" | "go" | "rust" | "other" | undefined;
|
|
88
|
+
repo?: "github" | "gitlab" | "none" | undefined;
|
|
89
|
+
ai?: string | undefined;
|
|
90
|
+
dryRun?: boolean | undefined;
|
|
91
|
+
force?: boolean | undefined;
|
|
92
|
+
yes?: boolean | undefined;
|
|
93
|
+
}>;
|
|
94
|
+
export type CliOptions = z.infer<typeof CliOptionsSchema>;
|
|
95
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export const AppTypeSchema = z.enum([
|
|
3
|
+
'web',
|
|
4
|
+
'api',
|
|
5
|
+
'fullstack',
|
|
6
|
+
'cli',
|
|
7
|
+
'library',
|
|
8
|
+
'data-ml',
|
|
9
|
+
'mobile',
|
|
10
|
+
'custom',
|
|
11
|
+
]);
|
|
12
|
+
export const LanguageSchema = z.enum([
|
|
13
|
+
'typescript',
|
|
14
|
+
'javascript',
|
|
15
|
+
'python',
|
|
16
|
+
'java',
|
|
17
|
+
'go',
|
|
18
|
+
'rust',
|
|
19
|
+
'other',
|
|
20
|
+
]);
|
|
21
|
+
export const RepositorySchema = z.enum(['github', 'gitlab', 'none']);
|
|
22
|
+
export const StrictnessSchema = z.enum(['standard', 'strict']);
|
|
23
|
+
export const WorkspaceConfigSchema = z.object({
|
|
24
|
+
version: z.literal('1'),
|
|
25
|
+
project: z.object({
|
|
26
|
+
name: z
|
|
27
|
+
.string()
|
|
28
|
+
.min(1, 'Project name is required')
|
|
29
|
+
.regex(/^[a-zA-Z0-9_\-.]+$/, 'Project name must be alphanumeric with dashes/underscores/dots'),
|
|
30
|
+
type: AppTypeSchema,
|
|
31
|
+
language: LanguageSchema,
|
|
32
|
+
}),
|
|
33
|
+
repository: RepositorySchema.default('github'),
|
|
34
|
+
strictness: StrictnessSchema.default('standard'),
|
|
35
|
+
adapters: z
|
|
36
|
+
.array(z.string().min(1))
|
|
37
|
+
.default(['agents'])
|
|
38
|
+
.transform((val) => (val.length === 0 ? ['agents'] : val)),
|
|
39
|
+
});
|
|
40
|
+
export const CliOptionsSchema = z.object({
|
|
41
|
+
name: z.string().optional(),
|
|
42
|
+
type: AppTypeSchema.optional(),
|
|
43
|
+
lang: LanguageSchema.optional(),
|
|
44
|
+
repo: RepositorySchema.optional(),
|
|
45
|
+
strictness: StrictnessSchema.optional(),
|
|
46
|
+
ai: z.string().optional(),
|
|
47
|
+
dryRun: z.boolean().default(false),
|
|
48
|
+
force: z.boolean().default(false),
|
|
49
|
+
yes: z.boolean().default(false),
|
|
50
|
+
});
|
|
51
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { getAppTypeRules } from '../templates/app-types/index.js';
|
|
2
|
+
import { BASE_GUARDRAILS } from '../templates/base/guardrails.js';
|
|
3
|
+
import { getLanguageTemplate } from '../templates/languages/index.js';
|
|
4
|
+
export function buildResolvedContext(config) {
|
|
5
|
+
const langTemplate = getLanguageTemplate(config.project.language);
|
|
6
|
+
const appRules = getAppTypeRules(config.project.type);
|
|
7
|
+
const guardrails = BASE_GUARDRAILS[config.strictness];
|
|
8
|
+
return {
|
|
9
|
+
config,
|
|
10
|
+
commands: langTemplate.commands,
|
|
11
|
+
languageRules: langTemplate.rules,
|
|
12
|
+
appTypeRules: appRules,
|
|
13
|
+
baseGuardrails: guardrails,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { AdapterRegistry } from '../adapters/registry.js';
|
|
2
|
+
import { GeneratedFile } from '../adapters/adapter.interface.js';
|
|
3
|
+
import { ResolvedWorkspaceContext } from '../config/schema.js';
|
|
4
|
+
export { GeneratedFile };
|
|
5
|
+
/**
|
|
6
|
+
* Pure generator function: maps ResolvedWorkspaceContext to GeneratedFile[].
|
|
7
|
+
* Does NOT execute any disk I/O, network requests, or model inferences.
|
|
8
|
+
*/
|
|
9
|
+
export declare function generateWorkspace(context: ResolvedWorkspaceContext, registry?: AdapterRegistry): GeneratedFile[];
|
|
10
|
+
//# sourceMappingURL=generator.d.ts.map
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { defaultRegistry } from '../adapters/registry.js';
|
|
2
|
+
import { renderAiContext } from '../templates/ai-context.js';
|
|
3
|
+
import { renderAiDirArchitecture } from '../templates/ai-dir/architecture.js';
|
|
4
|
+
import { renderAiDirContext } from '../templates/ai-dir/context.js';
|
|
5
|
+
import { renderAiDirDecisions } from '../templates/ai-dir/decisions.js';
|
|
6
|
+
import { renderAiDirRules } from '../templates/ai-dir/rules.js';
|
|
7
|
+
import { renderAiDirTasks } from '../templates/ai-dir/tasks.js';
|
|
8
|
+
import { renderContributing } from '../templates/contributing.js';
|
|
9
|
+
import { renderArchitectureDoc } from '../templates/docs/architecture-doc.js';
|
|
10
|
+
import { renderDecisionsDoc } from '../templates/docs/decisions-doc.js';
|
|
11
|
+
import { renderDevelopmentDoc } from '../templates/docs/development-doc.js';
|
|
12
|
+
import { renderDocsReadme } from '../templates/docs/docs-readme.js';
|
|
13
|
+
import { renderGitignore } from '../templates/gitignore.js';
|
|
14
|
+
import { renderProjectGuardrails } from '../templates/guardrails-doc.js';
|
|
15
|
+
import { renderManifest } from '../templates/manifest.js';
|
|
16
|
+
import { renderProjectReadme } from '../templates/readme.js';
|
|
17
|
+
/**
|
|
18
|
+
* Pure generator function: maps ResolvedWorkspaceContext to GeneratedFile[].
|
|
19
|
+
* Does NOT execute any disk I/O, network requests, or model inferences.
|
|
20
|
+
*/
|
|
21
|
+
export function generateWorkspace(context, registry = defaultRegistry) {
|
|
22
|
+
const files = [];
|
|
23
|
+
// 1. Canonical machine contract
|
|
24
|
+
files.push({
|
|
25
|
+
relativePath: 'ai-workspace.yaml',
|
|
26
|
+
content: renderManifest(context.config),
|
|
27
|
+
});
|
|
28
|
+
// 2. Root documentation and guardrails
|
|
29
|
+
files.push({
|
|
30
|
+
relativePath: 'README.md',
|
|
31
|
+
content: renderProjectReadme(context),
|
|
32
|
+
});
|
|
33
|
+
files.push({
|
|
34
|
+
relativePath: 'AI_CONTEXT.md',
|
|
35
|
+
content: renderAiContext(context),
|
|
36
|
+
});
|
|
37
|
+
files.push({
|
|
38
|
+
relativePath: 'PROJECT_GUARDRAILS.md',
|
|
39
|
+
content: renderProjectGuardrails(context),
|
|
40
|
+
});
|
|
41
|
+
files.push({
|
|
42
|
+
relativePath: 'CONTRIBUTING.md',
|
|
43
|
+
content: renderContributing(context),
|
|
44
|
+
});
|
|
45
|
+
files.push({
|
|
46
|
+
relativePath: '.gitignore',
|
|
47
|
+
content: renderGitignore(context.config.project.language),
|
|
48
|
+
});
|
|
49
|
+
// 3. docs/ directory structure
|
|
50
|
+
files.push({
|
|
51
|
+
relativePath: 'docs/README.md',
|
|
52
|
+
content: renderDocsReadme(context),
|
|
53
|
+
});
|
|
54
|
+
files.push({
|
|
55
|
+
relativePath: 'docs/ARCHITECTURE.md',
|
|
56
|
+
content: renderArchitectureDoc(context),
|
|
57
|
+
});
|
|
58
|
+
files.push({
|
|
59
|
+
relativePath: 'docs/DEVELOPMENT.md',
|
|
60
|
+
content: renderDevelopmentDoc(context),
|
|
61
|
+
});
|
|
62
|
+
files.push({
|
|
63
|
+
relativePath: 'docs/DECISIONS.md',
|
|
64
|
+
content: renderDecisionsDoc(context),
|
|
65
|
+
});
|
|
66
|
+
// 4. .ai/ directory structure (context, rules, architecture, decisions, tasks)
|
|
67
|
+
files.push({
|
|
68
|
+
relativePath: '.ai/context/project-context.md',
|
|
69
|
+
content: renderAiDirContext(context),
|
|
70
|
+
});
|
|
71
|
+
files.push({
|
|
72
|
+
relativePath: '.ai/rules/00-core-rules.md',
|
|
73
|
+
content: renderAiDirRules(context),
|
|
74
|
+
});
|
|
75
|
+
files.push({
|
|
76
|
+
relativePath: '.ai/architecture/system-design.md',
|
|
77
|
+
content: renderAiDirArchitecture(context),
|
|
78
|
+
});
|
|
79
|
+
files.push({
|
|
80
|
+
relativePath: '.ai/decisions/0001-project-init.md',
|
|
81
|
+
content: renderAiDirDecisions(context),
|
|
82
|
+
});
|
|
83
|
+
files.push({
|
|
84
|
+
relativePath: '.ai/tasks/task-backlog.md',
|
|
85
|
+
content: renderAiDirTasks(context),
|
|
86
|
+
});
|
|
87
|
+
// 5. Render enabled AI tool adapters
|
|
88
|
+
for (const adapterId of context.config.adapters) {
|
|
89
|
+
const adapter = registry.get(adapterId);
|
|
90
|
+
if (adapter) {
|
|
91
|
+
const adapterFiles = adapter.render(context);
|
|
92
|
+
files.push(...adapterFiles);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return files;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=generator.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure, deterministic string substitution engine.
|
|
3
|
+
* Replaces {{KEY}} occurrences with string data values.
|
|
4
|
+
*/
|
|
5
|
+
export function interpolate(template, data) {
|
|
6
|
+
return template.replace(/\{\{([A-Z0-9_]+)\}\}/g, (match, key) => {
|
|
7
|
+
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
|
8
|
+
return data[key];
|
|
9
|
+
}
|
|
10
|
+
return match;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=interpolator.js.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AppType, Language } from '../config/schema.js';
|
|
2
|
+
export interface DetectedEnvironment {
|
|
3
|
+
readonly name: string;
|
|
4
|
+
readonly language: Language;
|
|
5
|
+
readonly type: AppType;
|
|
6
|
+
readonly isDetected: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Deterministically inspects target directory markers to infer project defaults.
|
|
10
|
+
* Completely decoupled from CLI prompt parsing.
|
|
11
|
+
*/
|
|
12
|
+
export declare function detectProjectEnvironment(targetDir: string): Promise<DetectedEnvironment>;
|
|
13
|
+
//# sourceMappingURL=detector.d.ts.map
|