@nlaprell/shipit 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/.cursor/commands/create_intent_from_issue.md +28 -0
- package/.cursor/commands/create_pr.md +28 -0
- package/.cursor/commands/dashboard.md +39 -0
- package/.cursor/commands/deploy.md +152 -0
- package/.cursor/commands/drift_check.md +36 -0
- package/.cursor/commands/fix.md +39 -0
- package/.cursor/commands/generate_release_plan.md +31 -0
- package/.cursor/commands/generate_roadmap.md +38 -0
- package/.cursor/commands/help.md +37 -0
- package/.cursor/commands/init_project.md +26 -0
- package/.cursor/commands/kill.md +72 -0
- package/.cursor/commands/new_intent.md +68 -0
- package/.cursor/commands/pr.md +77 -0
- package/.cursor/commands/revert-plan.md +58 -0
- package/.cursor/commands/risk.md +64 -0
- package/.cursor/commands/rollback.md +43 -0
- package/.cursor/commands/scope_project.md +53 -0
- package/.cursor/commands/ship.md +345 -0
- package/.cursor/commands/status.md +71 -0
- package/.cursor/commands/suggest.md +44 -0
- package/.cursor/commands/test_shipit.md +197 -0
- package/.cursor/commands/verify.md +50 -0
- package/.cursor/rules/architect.mdc +84 -0
- package/.cursor/rules/assumption-extractor.mdc +95 -0
- package/.cursor/rules/docs.mdc +66 -0
- package/.cursor/rules/implementer.mdc +112 -0
- package/.cursor/rules/pm.mdc +136 -0
- package/.cursor/rules/qa.mdc +97 -0
- package/.cursor/rules/security.mdc +90 -0
- package/.cursor/rules/steward.mdc +99 -0
- package/.cursor/rules/test-runner.mdc +196 -0
- package/AGENTS.md +121 -0
- package/README.md +264 -0
- package/_system/architecture/CANON.md +159 -0
- package/_system/architecture/invariants.yml +87 -0
- package/_system/architecture/project-schema.json +98 -0
- package/_system/architecture/workflow-state-layout.md +68 -0
- package/_system/artifacts/SYSTEM_STATE.md +43 -0
- package/_system/artifacts/confidence-calibration.json +16 -0
- package/_system/artifacts/dependencies.md +46 -0
- package/_system/artifacts/framework-files-manifest.json +179 -0
- package/_system/artifacts/usage.json +1 -0
- package/_system/behaviors/DO_RELEASE.md +371 -0
- package/_system/behaviors/DO_RELEASE_AI.md +329 -0
- package/_system/behaviors/PREPARE_RELEASE.md +373 -0
- package/_system/behaviors/PREPARE_RELEASE_AI.md +234 -0
- package/_system/behaviors/WORK_ROOT_PLATFORM_ISSUES.md +140 -0
- package/_system/behaviors/WORK_TEST_PLAN_ISSUES.md +380 -0
- package/_system/do-not-repeat/abandoned-designs.md +18 -0
- package/_system/do-not-repeat/bad-patterns.md +19 -0
- package/_system/do-not-repeat/failed-experiments.md +18 -0
- package/_system/do-not-repeat/rejected-libraries.md +19 -0
- package/_system/drift/baselines.md +49 -0
- package/_system/drift/metrics.md +33 -0
- package/_system/golden-data/.gitkeep +0 -0
- package/_system/golden-data/README.md +47 -0
- package/_system/reports/mutation/mutation.html +492 -0
- package/_system/security/audit-allowlist.json +4 -0
- package/bin/create-shipit-app +29 -0
- package/bin/shipit +183 -0
- package/cli/src/commands/check.js +82 -0
- package/cli/src/commands/create.js +195 -0
- package/cli/src/commands/init.js +267 -0
- package/cli/src/commands/upgrade.js +196 -0
- package/cli/src/utils/config.js +27 -0
- package/cli/src/utils/file-copy.js +144 -0
- package/cli/src/utils/gitignore-merge.js +44 -0
- package/cli/src/utils/manifest.js +105 -0
- package/cli/src/utils/package-json-merge.js +163 -0
- package/cli/src/utils/project-json-merge.js +57 -0
- package/cli/src/utils/prompts.js +30 -0
- package/cli/src/utils/stack-detection.js +56 -0
- package/cli/src/utils/stack-files.js +364 -0
- package/cli/src/utils/upgrade-backup.js +159 -0
- package/cli/src/utils/version.js +64 -0
- package/dashboard-app/README.md +73 -0
- package/dashboard-app/eslint.config.js +23 -0
- package/dashboard-app/index.html +13 -0
- package/dashboard-app/package.json +30 -0
- package/dashboard-app/pnpm-lock.yaml +2721 -0
- package/dashboard-app/public/dashboard.json +66 -0
- package/dashboard-app/public/vite.svg +1 -0
- package/dashboard-app/src/App.css +141 -0
- package/dashboard-app/src/App.tsx +155 -0
- package/dashboard-app/src/assets/react.svg +1 -0
- package/dashboard-app/src/index.css +68 -0
- package/dashboard-app/src/main.tsx +10 -0
- package/dashboard-app/tsconfig.app.json +28 -0
- package/dashboard-app/tsconfig.json +4 -0
- package/dashboard-app/tsconfig.node.json +26 -0
- package/dashboard-app/vite.config.ts +7 -0
- package/package.json +116 -0
- package/scripts/README.md +70 -0
- package/scripts/audit-check.sh +125 -0
- package/scripts/calibration-report.sh +198 -0
- package/scripts/check-readiness.sh +155 -0
- package/scripts/collect-metrics.sh +116 -0
- package/scripts/command-manifest.yml +131 -0
- package/scripts/create-test-plan-issue.sh +110 -0
- package/scripts/dashboard-start.sh +16 -0
- package/scripts/deploy.sh +170 -0
- package/scripts/drift-check.sh +93 -0
- package/scripts/execute-rollback.sh +177 -0
- package/scripts/export-dashboard-json.js +208 -0
- package/scripts/fix-intents.sh +239 -0
- package/scripts/generate-dashboard.sh +136 -0
- package/scripts/generate-docs.sh +279 -0
- package/scripts/generate-project-context.sh +142 -0
- package/scripts/generate-release-plan.sh +443 -0
- package/scripts/generate-roadmap.sh +189 -0
- package/scripts/generate-system-state.sh +95 -0
- package/scripts/gh/create-intent-from-issue.sh +82 -0
- package/scripts/gh/create-issue-from-intent.sh +59 -0
- package/scripts/gh/create-pr.sh +41 -0
- package/scripts/gh/link-issue.sh +44 -0
- package/scripts/gh/on-ship-update-issue.sh +42 -0
- package/scripts/headless/README.md +8 -0
- package/scripts/headless/call-llm.js +109 -0
- package/scripts/headless/run-phase.sh +99 -0
- package/scripts/help.sh +271 -0
- package/scripts/init-project.sh +976 -0
- package/scripts/kill-intent.sh +125 -0
- package/scripts/lib/common.sh +29 -0
- package/scripts/lib/intent.sh +61 -0
- package/scripts/lib/progress.sh +57 -0
- package/scripts/lib/suggest-next.sh +131 -0
- package/scripts/lib/validate-intents.sh +240 -0
- package/scripts/lib/verify-outputs.sh +55 -0
- package/scripts/lib/workflow_state.sh +201 -0
- package/scripts/new-intent.sh +271 -0
- package/scripts/publish-npm.sh +28 -0
- package/scripts/scope-project.sh +380 -0
- package/scripts/setup-worktrees.sh +125 -0
- package/scripts/status.sh +278 -0
- package/scripts/suggest.sh +173 -0
- package/scripts/test-headless.sh +47 -0
- package/scripts/test-shipit.sh +52 -0
- package/scripts/test-workflow-state.sh +49 -0
- package/scripts/usage-report.sh +47 -0
- package/scripts/usage.sh +58 -0
- package/scripts/validate-cursor.sh +151 -0
- package/scripts/validate-project.sh +71 -0
- package/scripts/validate-vscode.sh +146 -0
- package/scripts/verify.sh +153 -0
- package/scripts/workflow-orchestrator.sh +97 -0
- package/scripts/workflow-templates/01_analysis.md.tpl +25 -0
- package/scripts/workflow-templates/02_plan.md.tpl +30 -0
- package/scripts/workflow-templates/03_implementation.md.tpl +25 -0
- package/scripts/workflow-templates/04_verification.md.tpl +29 -0
- package/scripts/workflow-templates/05_release_notes.md.tpl +16 -0
- package/scripts/workflow-templates/05_verification_legacy.md.tpl +6 -0
- package/scripts/workflow-templates/active.md.tpl +18 -0
- package/scripts/workflow-templates/phases.yml +39 -0
- package/stryker.conf.json +8 -0
- package/work/intent/templates/api-endpoint.md +124 -0
- package/work/intent/templates/bugfix.md +116 -0
- package/work/intent/templates/frontend-feature.md +115 -0
- package/work/intent/templates/generic.md +122 -0
- package/work/intent/templates/infra-change.md +121 -0
- package/work/intent/templates/refactor.md +116 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* create-shipit-app - Create a new ShipIt project
|
|
5
|
+
*
|
|
6
|
+
* This is an alias for `shipit create <project-name>`
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { dirname, join } from 'path';
|
|
11
|
+
import { spawn } from 'child_process';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = dirname(__filename);
|
|
15
|
+
|
|
16
|
+
// Get shipit CLI path
|
|
17
|
+
const shipitPath = join(__dirname, 'shipit');
|
|
18
|
+
|
|
19
|
+
// Forward all arguments to shipit create
|
|
20
|
+
const args = ['create', ...process.argv.slice(2)];
|
|
21
|
+
|
|
22
|
+
const child = spawn('node', [shipitPath, ...args], {
|
|
23
|
+
stdio: 'inherit',
|
|
24
|
+
cwd: process.cwd()
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
child.on('exit', (code) => {
|
|
28
|
+
process.exit(code || 0);
|
|
29
|
+
});
|
package/bin/shipit
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ShipIt CLI - Main entry point
|
|
5
|
+
*
|
|
6
|
+
* Commands:
|
|
7
|
+
* shipit init - Attach ShipIt to existing project
|
|
8
|
+
* shipit upgrade - Upgrade ShipIt framework files
|
|
9
|
+
* shipit check - Validate ShipIt installation
|
|
10
|
+
* shipit create <name> - Create new ShipIt project (alias for create-shipit-app)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { program } from 'commander';
|
|
14
|
+
import { fileURLToPath } from 'url';
|
|
15
|
+
import { dirname, join } from 'path';
|
|
16
|
+
import { existsSync, readFileSync } from 'fs';
|
|
17
|
+
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = dirname(__filename);
|
|
20
|
+
|
|
21
|
+
// Import CLI modules - use dynamic imports to handle ESM
|
|
22
|
+
let initCommand, upgradeCommand, checkCommand, createCommand, listBackupsCommand, restoreCommand;
|
|
23
|
+
|
|
24
|
+
async function loadCommands() {
|
|
25
|
+
const init = await import('../cli/src/commands/init.js');
|
|
26
|
+
const upgrade = await import('../cli/src/commands/upgrade.js');
|
|
27
|
+
const check = await import('../cli/src/commands/check.js');
|
|
28
|
+
const create = await import('../cli/src/commands/create.js');
|
|
29
|
+
|
|
30
|
+
initCommand = init.initCommand;
|
|
31
|
+
upgradeCommand = upgrade.upgradeCommand;
|
|
32
|
+
checkCommand = check.checkCommand;
|
|
33
|
+
createCommand = create.createCommand;
|
|
34
|
+
listBackupsCommand = upgrade.listBackupsCommand;
|
|
35
|
+
restoreCommand = upgrade.restoreCommand;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Get version from package.json
|
|
39
|
+
const packageJsonPath = join(__dirname, '..', 'package.json');
|
|
40
|
+
let version = '0.6.0';
|
|
41
|
+
try {
|
|
42
|
+
const pkgContent = readFileSync(packageJsonPath, 'utf-8');
|
|
43
|
+
const pkg = JSON.parse(pkgContent);
|
|
44
|
+
version = pkg.version || version;
|
|
45
|
+
} catch (e) {
|
|
46
|
+
// Fallback to default version
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Load commands
|
|
50
|
+
await loadCommands();
|
|
51
|
+
|
|
52
|
+
program
|
|
53
|
+
.name('shipit')
|
|
54
|
+
.description('ShipIt - AI-native Software Development Life Cycle framework')
|
|
55
|
+
.version(version);
|
|
56
|
+
|
|
57
|
+
// Init command
|
|
58
|
+
program
|
|
59
|
+
.command('init')
|
|
60
|
+
.description('Attach ShipIt to an existing project')
|
|
61
|
+
.option('--path <dir>', 'Target directory (default: current directory)')
|
|
62
|
+
.option('--tech-stack <stack>', 'Tech stack (typescript-nodejs | python | other)')
|
|
63
|
+
.option('--description <text>', 'Project description')
|
|
64
|
+
.option('--high-risk <domains>', 'Comma-separated high-risk domains')
|
|
65
|
+
.option('--non-interactive', 'Use defaults/CLI args only, no prompts')
|
|
66
|
+
.option('--force', 'Overwrite existing ShipIt files')
|
|
67
|
+
.action(async (options) => {
|
|
68
|
+
try {
|
|
69
|
+
await initCommand(options);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error(`ERROR: ${error.message}`);
|
|
72
|
+
if (error.suggestedFix) {
|
|
73
|
+
console.error(` ${error.suggestedFix}`);
|
|
74
|
+
}
|
|
75
|
+
process.exit(error.exitCode || 2);
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Upgrade command
|
|
80
|
+
program
|
|
81
|
+
.command('upgrade')
|
|
82
|
+
.description('Upgrade ShipIt framework files to latest version')
|
|
83
|
+
.option('--path <dir>', 'Target directory (default: current directory)')
|
|
84
|
+
.option('--backup-dir <dir>', 'Backup directory (default: ._shipit_backup/)')
|
|
85
|
+
.option('--dry-run', 'Show what would be changed without making changes')
|
|
86
|
+
.option('--force', 'Skip prompts, overwrite all framework files')
|
|
87
|
+
.action(async (options) => {
|
|
88
|
+
try {
|
|
89
|
+
await upgradeCommand(options);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error(`ERROR: ${error.message}`);
|
|
92
|
+
if (error.suggestedFix) {
|
|
93
|
+
console.error(` ${error.suggestedFix}`);
|
|
94
|
+
}
|
|
95
|
+
process.exit(error.exitCode || 2);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// List backups
|
|
100
|
+
program
|
|
101
|
+
.command('list-backups')
|
|
102
|
+
.description('List ShipIt backup files from last upgrade')
|
|
103
|
+
.option('--path <dir>', 'Target directory (default: current directory)')
|
|
104
|
+
.option('--backup-dir <dir>', 'Backup directory (default: ._shipit_backup/)')
|
|
105
|
+
.action(async (options) => {
|
|
106
|
+
try {
|
|
107
|
+
await listBackupsCommand(options);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error(`ERROR: ${error.message}`);
|
|
110
|
+
process.exit(error.exitCode || 2);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Restore from backup
|
|
115
|
+
program
|
|
116
|
+
.command('restore <backup-path>')
|
|
117
|
+
.description('Restore a file from backup (path relative to project or ._shipit_backup/)')
|
|
118
|
+
.option('--path <dir>', 'Target directory (default: current directory)')
|
|
119
|
+
.option('--backup-dir <dir>', 'Backup directory (default: ._shipit_backup/)')
|
|
120
|
+
.option('--remove-backup', 'Remove backup file after restore')
|
|
121
|
+
.action(async (backupPath, options) => {
|
|
122
|
+
try {
|
|
123
|
+
await restoreCommand(backupPath, options);
|
|
124
|
+
} catch (error) {
|
|
125
|
+
console.error(`ERROR: ${error.message}`);
|
|
126
|
+
process.exit(error.exitCode || 2);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Check command
|
|
131
|
+
program
|
|
132
|
+
.command('check')
|
|
133
|
+
.description('Validate ShipIt installation and show project status')
|
|
134
|
+
.option('--path <dir>', 'Target directory (default: current directory)')
|
|
135
|
+
.option('--json', 'Output JSON instead of human-readable')
|
|
136
|
+
.action(async (options) => {
|
|
137
|
+
try {
|
|
138
|
+
await checkCommand(options);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
console.error(`ERROR: ${error.message}`);
|
|
141
|
+
if (error.suggestedFix) {
|
|
142
|
+
console.error(` ${error.suggestedFix}`);
|
|
143
|
+
}
|
|
144
|
+
process.exit(error.exitCode || 2);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Create command
|
|
149
|
+
program
|
|
150
|
+
.command('create <project-name>')
|
|
151
|
+
.description('Create a new ShipIt project')
|
|
152
|
+
.option('--tech-stack <stack>', 'Tech stack (typescript-nodejs | python | other)')
|
|
153
|
+
.option('--description <text>', 'Project description')
|
|
154
|
+
.option('--high-risk <domains>', 'Comma-separated high-risk domains')
|
|
155
|
+
.option('--non-interactive', 'Use defaults/CLI args only, no prompts')
|
|
156
|
+
.option('--skip-git', 'Don\'t initialize git repo')
|
|
157
|
+
.option('--skip-install', 'Don\'t run package manager install')
|
|
158
|
+
.action(async (projectName, options) => {
|
|
159
|
+
try {
|
|
160
|
+
await createCommand(projectName, options);
|
|
161
|
+
} catch (error) {
|
|
162
|
+
console.error(`ERROR: ${error.message}`);
|
|
163
|
+
if (error.suggestedFix) {
|
|
164
|
+
console.error(` ${error.suggestedFix}`);
|
|
165
|
+
}
|
|
166
|
+
process.exit(error.exitCode || 2);
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Global error handler
|
|
171
|
+
process.on('unhandledRejection', (error) => {
|
|
172
|
+
console.error('ERROR: Unhandled error:', error);
|
|
173
|
+
process.exit(2);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Parse arguments
|
|
177
|
+
program.parse(process.argv);
|
|
178
|
+
|
|
179
|
+
// Show help if no command provided
|
|
180
|
+
if (!process.argv.slice(2).length) {
|
|
181
|
+
program.outputHelp();
|
|
182
|
+
process.exit(0);
|
|
183
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shipit check command - Validate ShipIt installation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { existsSync, readFileSync } from 'fs';
|
|
6
|
+
import { join, resolve } from 'path';
|
|
7
|
+
import { readManifest } from '../utils/manifest.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Check command implementation
|
|
11
|
+
* @param {object} options - Command options
|
|
12
|
+
*/
|
|
13
|
+
export async function checkCommand(options) {
|
|
14
|
+
const projectPath = resolve(options.path || process.cwd());
|
|
15
|
+
const jsonOutput = options.json || false;
|
|
16
|
+
|
|
17
|
+
const manifest = readManifest();
|
|
18
|
+
const status = {
|
|
19
|
+
initialized: false,
|
|
20
|
+
projectJsonExists: false,
|
|
21
|
+
frameworkFiles: {
|
|
22
|
+
present: [],
|
|
23
|
+
missing: []
|
|
24
|
+
},
|
|
25
|
+
version: null,
|
|
26
|
+
userCustomizations: []
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// Check project.json
|
|
30
|
+
const projectJsonPath = join(projectPath, 'project.json');
|
|
31
|
+
if (existsSync(projectJsonPath)) {
|
|
32
|
+
status.projectJsonExists = true;
|
|
33
|
+
status.initialized = true;
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const projectJson = JSON.parse(readFileSync(projectJsonPath, 'utf-8'));
|
|
37
|
+
status.version = projectJson.shipitVersion || null;
|
|
38
|
+
} catch (e) {
|
|
39
|
+
// Ignore parse errors
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check framework files
|
|
44
|
+
for (const file of manifest.frameworkOwned.files || []) {
|
|
45
|
+
const filePath = join(projectPath, file);
|
|
46
|
+
if (existsSync(filePath)) {
|
|
47
|
+
status.frameworkFiles.present.push(file);
|
|
48
|
+
} else {
|
|
49
|
+
status.frameworkFiles.missing.push(file);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Check .override directory
|
|
54
|
+
const overridePath = join(projectPath, '.override');
|
|
55
|
+
if (existsSync(overridePath)) {
|
|
56
|
+
// List files in .override (user customizations)
|
|
57
|
+
// Simplified - just check if directory exists
|
|
58
|
+
status.userCustomizations.push('.override/');
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Output results
|
|
62
|
+
if (jsonOutput) {
|
|
63
|
+
console.log(JSON.stringify(status, null, 2));
|
|
64
|
+
} else {
|
|
65
|
+
console.log('ShipIt Installation Check');
|
|
66
|
+
console.log('========================');
|
|
67
|
+
console.log(`Initialized: ${status.initialized ? 'Yes' : 'No'}`);
|
|
68
|
+
if (status.version) {
|
|
69
|
+
console.log(`Version: ${status.version}`);
|
|
70
|
+
}
|
|
71
|
+
console.log(`Framework files present: ${status.frameworkFiles.present.length}`);
|
|
72
|
+
console.log(`Framework files missing: ${status.frameworkFiles.missing.length}`);
|
|
73
|
+
if (status.frameworkFiles.missing.length > 0) {
|
|
74
|
+
console.log('\nMissing files:');
|
|
75
|
+
status.frameworkFiles.missing.forEach(file => console.log(` - ${file}`));
|
|
76
|
+
}
|
|
77
|
+
if (status.userCustomizations.length > 0) {
|
|
78
|
+
console.log('\nUser customizations:');
|
|
79
|
+
status.userCustomizations.forEach(custom => console.log(` - ${custom}`));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shipit create command - Create new ShipIt project
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { existsSync, writeFileSync } from 'fs';
|
|
6
|
+
import fsExtra from 'fs-extra';
|
|
7
|
+
|
|
8
|
+
const { mkdirSync } = fsExtra;
|
|
9
|
+
import { join, resolve } from 'path';
|
|
10
|
+
import { readManifest, getFrameworkRoot } from '../utils/manifest.js';
|
|
11
|
+
import { isValidStack } from '../utils/stack-detection.js';
|
|
12
|
+
import { copyFrameworkFiles } from '../utils/file-copy.js';
|
|
13
|
+
import { createReadlineInterface, promptUser } from '../utils/prompts.js';
|
|
14
|
+
import { execSync } from 'child_process';
|
|
15
|
+
import { getShipitScripts, getShipitDevDependencies } from '../utils/package-json-merge.js';
|
|
16
|
+
import { createTypeScriptNodeFiles, createPythonFiles, createOtherStackFiles, createCIWorkflow } from '../utils/stack-files.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Create command implementation
|
|
20
|
+
* @param {string} projectName - Project name
|
|
21
|
+
* @param {object} options - Command options
|
|
22
|
+
*/
|
|
23
|
+
export async function createCommand(projectName, options) {
|
|
24
|
+
const nonInteractive = options.nonInteractive || process.env.SHIPIT_NON_INTERACTIVE === '1';
|
|
25
|
+
const skipGit = options.skipGit || false;
|
|
26
|
+
const skipInstall = options.skipInstall || false;
|
|
27
|
+
|
|
28
|
+
// Validate project name
|
|
29
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(projectName)) {
|
|
30
|
+
throw new Error('Project name must be alphanumeric with hyphens/underscores only');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const projectPath = resolve(projectName);
|
|
34
|
+
|
|
35
|
+
// Check if directory exists
|
|
36
|
+
if (existsSync(projectPath)) {
|
|
37
|
+
if (!options.force && !nonInteractive) {
|
|
38
|
+
const rl = createReadlineInterface();
|
|
39
|
+
const answer = await promptUser(rl, `Directory ${projectName} already exists. Continue? (y/N): `);
|
|
40
|
+
rl.close();
|
|
41
|
+
if (answer.toLowerCase() !== 'y') {
|
|
42
|
+
console.log('Aborted.');
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
} else {
|
|
47
|
+
mkdirSync(projectPath, { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Get tech stack
|
|
51
|
+
let techStack = options.techStack;
|
|
52
|
+
if (!techStack && !nonInteractive) {
|
|
53
|
+
const rl = createReadlineInterface();
|
|
54
|
+
console.log('Tech stack selection:');
|
|
55
|
+
console.log('1) TypeScript/Node.js (recommended)');
|
|
56
|
+
console.log('2) Python');
|
|
57
|
+
console.log('3) Other (manual setup)');
|
|
58
|
+
const choice = await promptUser(rl, 'Select tech stack [1=TS/Node, 2=Python, 3=Other]: ');
|
|
59
|
+
rl.close();
|
|
60
|
+
|
|
61
|
+
switch (choice.trim()) {
|
|
62
|
+
case '1': techStack = 'typescript-nodejs'; break;
|
|
63
|
+
case '2': techStack = 'python'; break;
|
|
64
|
+
case '3': techStack = 'other'; break;
|
|
65
|
+
default: techStack = 'typescript-nodejs';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
techStack = techStack || 'typescript-nodejs';
|
|
69
|
+
|
|
70
|
+
if (!isValidStack(techStack)) {
|
|
71
|
+
throw new Error(`Invalid tech stack: ${techStack}. Must be typescript-nodejs, python, or other.`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Get description
|
|
75
|
+
let description = options.description;
|
|
76
|
+
if (!description && !nonInteractive) {
|
|
77
|
+
const rl = createReadlineInterface();
|
|
78
|
+
description = await promptUser(rl, 'Project description (short): ');
|
|
79
|
+
rl.close();
|
|
80
|
+
}
|
|
81
|
+
description = description || projectName;
|
|
82
|
+
|
|
83
|
+
// Get high-risk domains
|
|
84
|
+
let highRisk = options.highRisk;
|
|
85
|
+
if (!highRisk && !nonInteractive) {
|
|
86
|
+
const rl = createReadlineInterface();
|
|
87
|
+
console.log('High-Risk Domains (comma-separated, or "none"):');
|
|
88
|
+
console.log('Examples: auth, payments, permissions, infrastructure, pii');
|
|
89
|
+
highRisk = await promptUser(rl, 'High-risk domains: ');
|
|
90
|
+
rl.close();
|
|
91
|
+
}
|
|
92
|
+
highRisk = highRisk || 'none';
|
|
93
|
+
const highRiskArray = highRisk === 'none' ? [] : highRisk.split(',').map(s => s.trim());
|
|
94
|
+
|
|
95
|
+
console.log(`Creating ShipIt project: ${projectName}`);
|
|
96
|
+
console.log(` Tech stack: ${techStack}`);
|
|
97
|
+
console.log(` Description: ${description}`);
|
|
98
|
+
|
|
99
|
+
// Read manifest
|
|
100
|
+
const manifest = readManifest();
|
|
101
|
+
const frameworkRoot = getFrameworkRoot();
|
|
102
|
+
|
|
103
|
+
// Copy framework files
|
|
104
|
+
console.log('Copying framework files...');
|
|
105
|
+
const copyResult = copyFrameworkFiles(frameworkRoot, projectPath, techStack, manifest, {
|
|
106
|
+
verbose: !nonInteractive
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (copyResult.errors.length > 0) {
|
|
110
|
+
console.error('Errors during file copy:');
|
|
111
|
+
copyResult.errors.forEach(err => console.error(` ${err}`));
|
|
112
|
+
throw new Error('Failed to copy some framework files');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Create stack-specific files (package.json, tsconfig.json, etc.)
|
|
116
|
+
if (techStack === 'typescript-nodejs') {
|
|
117
|
+
const shipitScripts = getShipitScripts();
|
|
118
|
+
const shipitDevDeps = getShipitDevDependencies();
|
|
119
|
+
createTypeScriptNodeFiles(projectPath, projectName, description, shipitScripts, shipitDevDeps);
|
|
120
|
+
} else if (techStack === 'python') {
|
|
121
|
+
createPythonFiles(projectPath, projectName, description);
|
|
122
|
+
} else {
|
|
123
|
+
createOtherStackFiles(projectPath);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Create stack-specific CI workflow
|
|
127
|
+
createCIWorkflow(projectPath, techStack);
|
|
128
|
+
|
|
129
|
+
// Create project.json
|
|
130
|
+
const projectJson = {
|
|
131
|
+
name: projectName,
|
|
132
|
+
description: description,
|
|
133
|
+
version: '0.1.0',
|
|
134
|
+
techStack: techStack,
|
|
135
|
+
created: new Date().toISOString(),
|
|
136
|
+
highRiskDomains: highRiskArray,
|
|
137
|
+
settings: {
|
|
138
|
+
humanResponseTime: 'minutes',
|
|
139
|
+
confidenceThreshold: 0.7,
|
|
140
|
+
testCoverageMinimum: 80
|
|
141
|
+
},
|
|
142
|
+
shipitVersion: '1.0.0'
|
|
143
|
+
};
|
|
144
|
+
writeFileSync(
|
|
145
|
+
join(projectPath, 'project.json'),
|
|
146
|
+
JSON.stringify(projectJson, null, 2) + '\n',
|
|
147
|
+
'utf-8'
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
// Create .override directory structure
|
|
151
|
+
const overrideDirs = [
|
|
152
|
+
'.override/rules',
|
|
153
|
+
'.override/commands',
|
|
154
|
+
'.override/scripts',
|
|
155
|
+
'.override/config'
|
|
156
|
+
];
|
|
157
|
+
for (const dir of overrideDirs) {
|
|
158
|
+
const dirPath = join(projectPath, dir);
|
|
159
|
+
if (!existsSync(dirPath)) {
|
|
160
|
+
mkdirSync(dirPath, { recursive: true });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Initialize git
|
|
165
|
+
if (!skipGit) {
|
|
166
|
+
const gitPath = join(projectPath, '.git');
|
|
167
|
+
if (!existsSync(gitPath)) {
|
|
168
|
+
console.log('Initializing git repository...');
|
|
169
|
+
try {
|
|
170
|
+
execSync('git init', { cwd: projectPath, stdio: 'inherit' });
|
|
171
|
+
execSync('git add .', { cwd: projectPath, stdio: 'inherit' });
|
|
172
|
+
execSync('git commit -m "Initial commit: ' + projectName + '"', { cwd: projectPath, stdio: 'inherit' });
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.warn('Warning: Git initialization failed (git may not be installed)');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Run package manager install
|
|
180
|
+
if (!skipInstall && techStack === 'typescript-nodejs') {
|
|
181
|
+
console.log('Installing dependencies...');
|
|
182
|
+
try {
|
|
183
|
+
execSync('pnpm install', { cwd: projectPath, stdio: 'inherit' });
|
|
184
|
+
} catch (error) {
|
|
185
|
+
console.warn('Warning: Package installation failed');
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
console.log('\n✓ Project created successfully!');
|
|
190
|
+
console.log(`\nNext steps:`);
|
|
191
|
+
console.log(`1. cd ${projectName}`);
|
|
192
|
+
console.log('2. Run /scope-project to break down into features');
|
|
193
|
+
console.log('3. Start creating intents with /new_intent');
|
|
194
|
+
}
|
|
195
|
+
|