claude-cli-advanced-starter-pack 1.0.16 ā 1.8.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/OVERVIEW.md +5 -1
- package/README.md +241 -132
- package/bin/gtask.js +53 -0
- package/package.json +1 -1
- package/src/cli/menu.js +27 -0
- package/src/commands/explore-mcp/mcp-registry.js +99 -0
- package/src/commands/init.js +309 -80
- package/src/commands/install-panel-hook.js +108 -0
- package/src/commands/install-scripts.js +232 -0
- package/src/commands/install-skill.js +220 -0
- package/src/commands/panel.js +297 -0
- package/src/commands/setup-wizard.js +4 -3
- package/src/commands/test-setup.js +4 -5
- package/src/data/releases.json +209 -0
- package/src/panel/queue.js +188 -0
- package/templates/commands/ask-claude.template.md +118 -0
- package/templates/commands/ccasp-panel.template.md +72 -0
- package/templates/commands/ccasp-setup.template.md +470 -79
- package/templates/commands/create-smoke-test.template.md +186 -0
- package/templates/commands/project-impl.template.md +9 -113
- package/templates/commands/refactor-check.template.md +112 -0
- package/templates/commands/refactor-cleanup.template.md +144 -0
- package/templates/commands/refactor-prep.template.md +192 -0
- package/templates/docs/AI_ARCHITECTURE_CONSTITUTION.template.md +198 -0
- package/templates/docs/DETAILED_GOTCHAS.template.md +347 -0
- package/templates/docs/PHASE-DEV-CHECKLIST.template.md +241 -0
- package/templates/docs/PROGRESS_JSON_TEMPLATE.json +117 -0
- package/templates/docs/background-agent.template.md +264 -0
- package/templates/hooks/autonomous-decision-logger.template.js +207 -0
- package/templates/hooks/branch-merge-checker.template.js +272 -0
- package/templates/hooks/context-injector.template.js +261 -0
- package/templates/hooks/git-commit-tracker.template.js +267 -0
- package/templates/hooks/happy-mode-detector.template.js +214 -0
- package/templates/hooks/happy-title-generator.template.js +260 -0
- package/templates/hooks/issue-completion-detector.template.js +205 -0
- package/templates/hooks/panel-queue-reader.template.js +83 -0
- package/templates/hooks/phase-validation-gates.template.js +307 -0
- package/templates/hooks/session-id-generator.template.js +236 -0
- package/templates/hooks/token-budget-loader.template.js +234 -0
- package/templates/hooks/token-usage-monitor.template.js +193 -0
- package/templates/hooks/tool-output-cacher.template.js +219 -0
- package/templates/patterns/README.md +129 -0
- package/templates/patterns/l1-l2-orchestration.md +189 -0
- package/templates/patterns/multi-phase-orchestration.md +258 -0
- package/templates/patterns/two-tier-query-pipeline.md +192 -0
- package/templates/scripts/README.md +109 -0
- package/templates/scripts/analyze-delegation-log.js +299 -0
- package/templates/scripts/autonomous-decision-logger.js +277 -0
- package/templates/scripts/git-history-analyzer.py +269 -0
- package/templates/scripts/phase-validation-gates.js +307 -0
- package/templates/scripts/poll-deployment-status.js +260 -0
- package/templates/scripts/roadmap-scanner.js +263 -0
- package/templates/scripts/validate-deployment.js +293 -0
- package/templates/skills/agent-creator/skill.json +18 -0
- package/templates/skills/agent-creator/skill.md +335 -0
- package/templates/skills/hook-creator/skill.json +18 -0
- package/templates/skills/hook-creator/skill.md +318 -0
- package/templates/skills/panel/skill.json +18 -0
- package/templates/skills/panel/skill.md +90 -0
- package/templates/skills/rag-agent-creator/skill.json +18 -0
- package/templates/skills/rag-agent-creator/skill.md +307 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Roadmap Scanner
|
|
4
|
+
*
|
|
5
|
+
* Scans for roadmap files and generates a progress dashboard.
|
|
6
|
+
* Detects phase status, completion percentages, and blockers.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node roadmap-scanner.js # Scan current directory
|
|
10
|
+
* node roadmap-scanner.js --path ./docs # Scan specific directory
|
|
11
|
+
* node roadmap-scanner.js --output json # JSON output
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { readdirSync, readFileSync, statSync } from 'fs';
|
|
15
|
+
import { join, basename } from 'path';
|
|
16
|
+
|
|
17
|
+
const ROADMAP_PATTERNS = [
|
|
18
|
+
/roadmap/i,
|
|
19
|
+
/phase[-_]?dev/i,
|
|
20
|
+
/implementation[-_]?plan/i,
|
|
21
|
+
/project[-_]?plan/i,
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
const STATUS_PATTERNS = {
|
|
25
|
+
complete: [/ā
/, /\[x\]/i, /complete/i, /done/i, /finished/i],
|
|
26
|
+
inProgress: [/šØ/, /\[ \]/, /in.?progress/i, /wip/i, /started/i],
|
|
27
|
+
blocked: [/ā/, /blocked/i, /stuck/i, /waiting/i],
|
|
28
|
+
notStarted: [/ā¬/, /not.?started/i, /pending/i, /todo/i],
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
class RoadmapScanner {
|
|
32
|
+
constructor(options = {}) {
|
|
33
|
+
this.path = options.path || process.cwd();
|
|
34
|
+
this.output = options.output || 'text';
|
|
35
|
+
this.recursive = options.recursive !== false;
|
|
36
|
+
this.roadmaps = [];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async scan() {
|
|
40
|
+
console.log(`\nš Scanning for roadmaps in: ${this.path}\n`);
|
|
41
|
+
|
|
42
|
+
await this.findRoadmaps(this.path);
|
|
43
|
+
|
|
44
|
+
if (this.roadmaps.length === 0) {
|
|
45
|
+
console.log('No roadmap files found.');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log(`Found ${this.roadmaps.length} roadmap(s)\n`);
|
|
50
|
+
|
|
51
|
+
for (const roadmap of this.roadmaps) {
|
|
52
|
+
await this.analyzeRoadmap(roadmap);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (this.output === 'json') {
|
|
56
|
+
this.outputJson();
|
|
57
|
+
} else {
|
|
58
|
+
this.outputText();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async findRoadmaps(dir) {
|
|
63
|
+
try {
|
|
64
|
+
const entries = readdirSync(dir);
|
|
65
|
+
|
|
66
|
+
for (const entry of entries) {
|
|
67
|
+
const fullPath = join(dir, entry);
|
|
68
|
+
const stat = statSync(fullPath);
|
|
69
|
+
|
|
70
|
+
if (stat.isDirectory() && this.recursive) {
|
|
71
|
+
if (!entry.startsWith('.') && entry !== 'node_modules') {
|
|
72
|
+
await this.findRoadmaps(fullPath);
|
|
73
|
+
}
|
|
74
|
+
} else if (stat.isFile() && entry.endsWith('.md')) {
|
|
75
|
+
if (ROADMAP_PATTERNS.some(pattern => pattern.test(entry))) {
|
|
76
|
+
this.roadmaps.push({
|
|
77
|
+
path: fullPath,
|
|
78
|
+
name: basename(entry, '.md'),
|
|
79
|
+
phases: [],
|
|
80
|
+
metrics: {},
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
} catch (e) {
|
|
86
|
+
// Ignore access errors
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async analyzeRoadmap(roadmap) {
|
|
91
|
+
const content = readFileSync(roadmap.path, 'utf8');
|
|
92
|
+
const lines = content.split('\n');
|
|
93
|
+
|
|
94
|
+
let currentPhase = null;
|
|
95
|
+
let totalTasks = 0;
|
|
96
|
+
let completedTasks = 0;
|
|
97
|
+
let blockedTasks = 0;
|
|
98
|
+
|
|
99
|
+
for (const line of lines) {
|
|
100
|
+
// Detect phase headers
|
|
101
|
+
const phaseMatch = line.match(/^#+\s*phase\s*(\d+)/i);
|
|
102
|
+
if (phaseMatch) {
|
|
103
|
+
if (currentPhase) {
|
|
104
|
+
roadmap.phases.push(currentPhase);
|
|
105
|
+
}
|
|
106
|
+
currentPhase = {
|
|
107
|
+
number: parseInt(phaseMatch[1]),
|
|
108
|
+
name: line.replace(/^#+\s*/, '').trim(),
|
|
109
|
+
status: 'not_started',
|
|
110
|
+
tasks: [],
|
|
111
|
+
notes: [],
|
|
112
|
+
};
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Detect status markers in phase headers or status tables
|
|
117
|
+
if (currentPhase) {
|
|
118
|
+
// Check for task markers
|
|
119
|
+
const taskMatch = line.match(/^\s*[-*]\s*\[([ x])\]/i);
|
|
120
|
+
if (taskMatch) {
|
|
121
|
+
totalTasks++;
|
|
122
|
+
const isComplete = taskMatch[1].toLowerCase() === 'x';
|
|
123
|
+
if (isComplete) completedTasks++;
|
|
124
|
+
currentPhase.tasks.push({
|
|
125
|
+
text: line.replace(/^\s*[-*]\s*\[[ x]\]\s*/i, ''),
|
|
126
|
+
complete: isComplete,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Check for status indicators
|
|
131
|
+
for (const [status, patterns] of Object.entries(STATUS_PATTERNS)) {
|
|
132
|
+
if (patterns.some(p => p.test(line))) {
|
|
133
|
+
if (status === 'blocked') {
|
|
134
|
+
blockedTasks++;
|
|
135
|
+
currentPhase.status = 'blocked';
|
|
136
|
+
} else if (status === 'complete' && currentPhase.status !== 'blocked') {
|
|
137
|
+
currentPhase.status = 'complete';
|
|
138
|
+
} else if (status === 'inProgress' && !['complete', 'blocked'].includes(currentPhase.status)) {
|
|
139
|
+
currentPhase.status = 'in_progress';
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Check for version info
|
|
145
|
+
const versionMatch = line.match(/v?(\d+\.\d+\.\d+)/);
|
|
146
|
+
if (versionMatch) {
|
|
147
|
+
currentPhase.version = versionMatch[1];
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Add last phase
|
|
153
|
+
if (currentPhase) {
|
|
154
|
+
roadmap.phases.push(currentPhase);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Calculate metrics
|
|
158
|
+
roadmap.metrics = {
|
|
159
|
+
totalPhases: roadmap.phases.length,
|
|
160
|
+
completedPhases: roadmap.phases.filter(p => p.status === 'complete').length,
|
|
161
|
+
inProgressPhases: roadmap.phases.filter(p => p.status === 'in_progress').length,
|
|
162
|
+
blockedPhases: roadmap.phases.filter(p => p.status === 'blocked').length,
|
|
163
|
+
totalTasks,
|
|
164
|
+
completedTasks,
|
|
165
|
+
blockedTasks,
|
|
166
|
+
progress: totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
outputText() {
|
|
171
|
+
console.log('='.repeat(70));
|
|
172
|
+
console.log(' ROADMAP DASHBOARD');
|
|
173
|
+
console.log('='.repeat(70));
|
|
174
|
+
|
|
175
|
+
for (const roadmap of this.roadmaps) {
|
|
176
|
+
console.log(`\nš ${roadmap.name}`);
|
|
177
|
+
console.log(` Path: ${roadmap.path}`);
|
|
178
|
+
console.log('-'.repeat(70));
|
|
179
|
+
|
|
180
|
+
// Progress bar
|
|
181
|
+
const progress = roadmap.metrics.progress;
|
|
182
|
+
const barLength = 40;
|
|
183
|
+
const filled = Math.round((progress / 100) * barLength);
|
|
184
|
+
const bar = 'ā'.repeat(filled) + 'ā'.repeat(barLength - filled);
|
|
185
|
+
console.log(`\n Progress: [${bar}] ${progress}%`);
|
|
186
|
+
|
|
187
|
+
// Metrics
|
|
188
|
+
const m = roadmap.metrics;
|
|
189
|
+
console.log(`\n Phases: ${m.completedPhases}/${m.totalPhases} complete`);
|
|
190
|
+
console.log(` Tasks: ${m.completedTasks}/${m.totalTasks} complete`);
|
|
191
|
+
|
|
192
|
+
if (m.blockedPhases > 0) {
|
|
193
|
+
console.log(` ā ļø ${m.blockedPhases} phase(s) blocked`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Phase details
|
|
197
|
+
console.log('\n Phase Status:');
|
|
198
|
+
for (const phase of roadmap.phases) {
|
|
199
|
+
const icon = phase.status === 'complete' ? 'ā
' :
|
|
200
|
+
phase.status === 'in_progress' ? 'š' :
|
|
201
|
+
phase.status === 'blocked' ? 'ā' : 'ā¬';
|
|
202
|
+
const version = phase.version ? ` (v${phase.version})` : '';
|
|
203
|
+
console.log(` ${icon} ${phase.name}${version}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Summary
|
|
208
|
+
console.log('\n' + '='.repeat(70));
|
|
209
|
+
console.log('SUMMARY');
|
|
210
|
+
console.log('='.repeat(70));
|
|
211
|
+
|
|
212
|
+
const totals = this.roadmaps.reduce((acc, r) => ({
|
|
213
|
+
phases: acc.phases + r.metrics.totalPhases,
|
|
214
|
+
completed: acc.completed + r.metrics.completedPhases,
|
|
215
|
+
tasks: acc.tasks + r.metrics.totalTasks,
|
|
216
|
+
doneTasks: acc.doneTasks + r.metrics.completedTasks,
|
|
217
|
+
}), { phases: 0, completed: 0, tasks: 0, doneTasks: 0 });
|
|
218
|
+
|
|
219
|
+
console.log(`\n Total Roadmaps: ${this.roadmaps.length}`);
|
|
220
|
+
console.log(` Total Phases: ${totals.completed}/${totals.phases} complete`);
|
|
221
|
+
console.log(` Total Tasks: ${totals.doneTasks}/${totals.tasks} complete`);
|
|
222
|
+
console.log(` Overall: ${totals.tasks > 0 ? Math.round((totals.doneTasks / totals.tasks) * 100) : 0}%`);
|
|
223
|
+
console.log('\n' + '='.repeat(70) + '\n');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
outputJson() {
|
|
227
|
+
const output = {
|
|
228
|
+
scannedAt: new Date().toISOString(),
|
|
229
|
+
path: this.path,
|
|
230
|
+
roadmaps: this.roadmaps,
|
|
231
|
+
summary: {
|
|
232
|
+
totalRoadmaps: this.roadmaps.length,
|
|
233
|
+
totalPhases: this.roadmaps.reduce((acc, r) => acc + r.metrics.totalPhases, 0),
|
|
234
|
+
completedPhases: this.roadmaps.reduce((acc, r) => acc + r.metrics.completedPhases, 0),
|
|
235
|
+
totalTasks: this.roadmaps.reduce((acc, r) => acc + r.metrics.totalTasks, 0),
|
|
236
|
+
completedTasks: this.roadmaps.reduce((acc, r) => acc + r.metrics.completedTasks, 0),
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
console.log(JSON.stringify(output, null, 2));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// CLI entry point
|
|
245
|
+
async function main() {
|
|
246
|
+
const args = process.argv.slice(2);
|
|
247
|
+
|
|
248
|
+
const getArg = (name) => {
|
|
249
|
+
const index = args.indexOf(`--${name}`);
|
|
250
|
+
return index >= 0 ? args[index + 1] : null;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const options = {
|
|
254
|
+
path: getArg('path') || process.cwd(),
|
|
255
|
+
output: getArg('output') || 'text',
|
|
256
|
+
recursive: !args.includes('--no-recursive'),
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const scanner = new RoadmapScanner(options);
|
|
260
|
+
await scanner.scan();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Validate Deployment Environment
|
|
4
|
+
*
|
|
5
|
+
* Pre-deployment validation for Railway, Cloudflare, Vercel, and other platforms.
|
|
6
|
+
* Checks environment variables, build artifacts, and configuration.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* node validate-deployment.js --platform railway
|
|
10
|
+
* node validate-deployment.js --platform cloudflare --project-name my-project
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { existsSync, readFileSync } from 'fs';
|
|
14
|
+
import { resolve } from 'path';
|
|
15
|
+
|
|
16
|
+
const PLATFORMS = {
|
|
17
|
+
railway: {
|
|
18
|
+
requiredEnv: ['RAILWAY_API_TOKEN'],
|
|
19
|
+
optionalEnv: ['RAILWAY_PROJECT_ID', 'RAILWAY_ENVIRONMENT_ID', 'RAILWAY_SERVICE_ID'],
|
|
20
|
+
buildArtifacts: ['package.json', 'Dockerfile', 'requirements.txt', 'go.mod'],
|
|
21
|
+
configFiles: ['railway.json', 'railway.toml'],
|
|
22
|
+
},
|
|
23
|
+
cloudflare: {
|
|
24
|
+
requiredEnv: ['CLOUDFLARE_API_TOKEN'],
|
|
25
|
+
optionalEnv: ['CLOUDFLARE_ACCOUNT_ID'],
|
|
26
|
+
buildArtifacts: ['dist/', 'build/', 'public/', '.next/'],
|
|
27
|
+
configFiles: ['wrangler.toml', 'wrangler.json'],
|
|
28
|
+
},
|
|
29
|
+
vercel: {
|
|
30
|
+
requiredEnv: ['VERCEL_TOKEN'],
|
|
31
|
+
optionalEnv: ['VERCEL_PROJECT_ID', 'VERCEL_ORG_ID'],
|
|
32
|
+
buildArtifacts: ['dist/', 'build/', '.next/', '.vercel/'],
|
|
33
|
+
configFiles: ['vercel.json'],
|
|
34
|
+
},
|
|
35
|
+
netlify: {
|
|
36
|
+
requiredEnv: ['NETLIFY_AUTH_TOKEN'],
|
|
37
|
+
optionalEnv: ['NETLIFY_SITE_ID'],
|
|
38
|
+
buildArtifacts: ['dist/', 'build/', 'public/'],
|
|
39
|
+
configFiles: ['netlify.toml'],
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
class DeploymentValidator {
|
|
44
|
+
constructor(platform, options = {}) {
|
|
45
|
+
this.platform = platform;
|
|
46
|
+
this.options = options;
|
|
47
|
+
this.config = PLATFORMS[platform];
|
|
48
|
+
this.errors = [];
|
|
49
|
+
this.warnings = [];
|
|
50
|
+
this.checks = [];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async validate() {
|
|
54
|
+
console.log(`\nš Validating ${this.platform} deployment...\n`);
|
|
55
|
+
|
|
56
|
+
await this.checkEnvironmentVariables();
|
|
57
|
+
await this.checkBuildArtifacts();
|
|
58
|
+
await this.checkConfigFiles();
|
|
59
|
+
await this.checkGitStatus();
|
|
60
|
+
await this.platformSpecificChecks();
|
|
61
|
+
|
|
62
|
+
this.printReport();
|
|
63
|
+
return this.errors.length === 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async checkEnvironmentVariables() {
|
|
67
|
+
console.log('š Checking environment variables...');
|
|
68
|
+
|
|
69
|
+
for (const envVar of this.config.requiredEnv) {
|
|
70
|
+
if (process.env[envVar]) {
|
|
71
|
+
this.checks.push({ name: envVar, status: 'pass', message: 'Set' });
|
|
72
|
+
} else {
|
|
73
|
+
this.errors.push(`Missing required: ${envVar}`);
|
|
74
|
+
this.checks.push({ name: envVar, status: 'fail', message: 'Missing' });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
for (const envVar of this.config.optionalEnv) {
|
|
79
|
+
if (process.env[envVar]) {
|
|
80
|
+
this.checks.push({ name: envVar, status: 'pass', message: 'Set (optional)' });
|
|
81
|
+
} else {
|
|
82
|
+
this.warnings.push(`Optional not set: ${envVar}`);
|
|
83
|
+
this.checks.push({ name: envVar, status: 'warn', message: 'Not set (optional)' });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async checkBuildArtifacts() {
|
|
89
|
+
console.log('š¦ Checking build artifacts...');
|
|
90
|
+
|
|
91
|
+
let foundArtifact = false;
|
|
92
|
+
for (const artifact of this.config.buildArtifacts) {
|
|
93
|
+
const path = resolve(process.cwd(), artifact);
|
|
94
|
+
if (existsSync(path)) {
|
|
95
|
+
foundArtifact = true;
|
|
96
|
+
this.checks.push({ name: artifact, status: 'pass', message: 'Found' });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!foundArtifact) {
|
|
101
|
+
this.warnings.push('No build artifacts found. Run build command first.');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async checkConfigFiles() {
|
|
106
|
+
console.log('āļø Checking configuration files...');
|
|
107
|
+
|
|
108
|
+
let foundConfig = false;
|
|
109
|
+
for (const configFile of this.config.configFiles) {
|
|
110
|
+
const path = resolve(process.cwd(), configFile);
|
|
111
|
+
if (existsSync(path)) {
|
|
112
|
+
foundConfig = true;
|
|
113
|
+
this.checks.push({ name: configFile, status: 'pass', message: 'Found' });
|
|
114
|
+
|
|
115
|
+
// Validate config content
|
|
116
|
+
try {
|
|
117
|
+
const content = readFileSync(path, 'utf8');
|
|
118
|
+
if (configFile.endsWith('.json')) {
|
|
119
|
+
JSON.parse(content);
|
|
120
|
+
this.checks.push({ name: `${configFile} syntax`, status: 'pass', message: 'Valid JSON' });
|
|
121
|
+
}
|
|
122
|
+
} catch (e) {
|
|
123
|
+
this.errors.push(`Invalid ${configFile}: ${e.message}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!foundConfig) {
|
|
129
|
+
this.warnings.push(`No ${this.platform} config file found. Using defaults.`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async checkGitStatus() {
|
|
134
|
+
console.log('š Checking git status...');
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
const { execSync } = await import('child_process');
|
|
138
|
+
|
|
139
|
+
// Check for uncommitted changes
|
|
140
|
+
const status = execSync('git status --porcelain', { encoding: 'utf8' });
|
|
141
|
+
if (status.trim()) {
|
|
142
|
+
this.warnings.push('Uncommitted changes detected. Consider committing first.');
|
|
143
|
+
this.checks.push({ name: 'Git status', status: 'warn', message: 'Uncommitted changes' });
|
|
144
|
+
} else {
|
|
145
|
+
this.checks.push({ name: 'Git status', status: 'pass', message: 'Clean' });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Check branch
|
|
149
|
+
const branch = execSync('git branch --show-current', { encoding: 'utf8' }).trim();
|
|
150
|
+
this.checks.push({ name: 'Current branch', status: 'info', message: branch });
|
|
151
|
+
|
|
152
|
+
} catch (e) {
|
|
153
|
+
this.warnings.push('Not a git repository or git not available');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async platformSpecificChecks() {
|
|
158
|
+
switch (this.platform) {
|
|
159
|
+
case 'railway':
|
|
160
|
+
await this.checkRailway();
|
|
161
|
+
break;
|
|
162
|
+
case 'cloudflare':
|
|
163
|
+
await this.checkCloudflare();
|
|
164
|
+
break;
|
|
165
|
+
case 'vercel':
|
|
166
|
+
await this.checkVercel();
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async checkRailway() {
|
|
172
|
+
// Check for Dockerfile or supported runtime
|
|
173
|
+
const hasDockerfile = existsSync(resolve(process.cwd(), 'Dockerfile'));
|
|
174
|
+
const hasPackageJson = existsSync(resolve(process.cwd(), 'package.json'));
|
|
175
|
+
const hasRequirements = existsSync(resolve(process.cwd(), 'requirements.txt'));
|
|
176
|
+
|
|
177
|
+
if (!hasDockerfile && !hasPackageJson && !hasRequirements) {
|
|
178
|
+
this.errors.push('No supported runtime detected (Dockerfile, package.json, or requirements.txt)');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Check for Railway-specific health check endpoint
|
|
182
|
+
if (hasPackageJson) {
|
|
183
|
+
try {
|
|
184
|
+
const pkg = JSON.parse(readFileSync('package.json', 'utf8'));
|
|
185
|
+
if (pkg.scripts?.start) {
|
|
186
|
+
this.checks.push({ name: 'Start script', status: 'pass', message: pkg.scripts.start });
|
|
187
|
+
} else {
|
|
188
|
+
this.warnings.push('No "start" script in package.json');
|
|
189
|
+
}
|
|
190
|
+
} catch (e) {
|
|
191
|
+
// Ignore
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async checkCloudflare() {
|
|
197
|
+
const { projectName } = this.options;
|
|
198
|
+
|
|
199
|
+
// Check dist folder
|
|
200
|
+
const distPath = resolve(process.cwd(), 'dist');
|
|
201
|
+
if (!existsSync(distPath)) {
|
|
202
|
+
this.errors.push('dist/ folder not found. Run "npm run build" first.');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Check for index.html in dist
|
|
206
|
+
const indexPath = resolve(distPath, 'index.html');
|
|
207
|
+
if (existsSync(distPath) && !existsSync(indexPath)) {
|
|
208
|
+
this.warnings.push('No index.html in dist/. Is this a static site?');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Validate wrangler.toml if exists
|
|
212
|
+
const wranglerPath = resolve(process.cwd(), 'wrangler.toml');
|
|
213
|
+
if (existsSync(wranglerPath)) {
|
|
214
|
+
const content = readFileSync(wranglerPath, 'utf8');
|
|
215
|
+
if (projectName && !content.includes(projectName)) {
|
|
216
|
+
this.warnings.push(`Project name "${projectName}" not found in wrangler.toml`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async checkVercel() {
|
|
222
|
+
// Check for .vercel folder
|
|
223
|
+
const vercelDir = resolve(process.cwd(), '.vercel');
|
|
224
|
+
if (!existsSync(vercelDir)) {
|
|
225
|
+
this.warnings.push('No .vercel folder. Run "vercel link" first.');
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
printReport() {
|
|
230
|
+
console.log('\n' + '='.repeat(60));
|
|
231
|
+
console.log(`š Validation Report: ${this.platform.toUpperCase()}`);
|
|
232
|
+
console.log('='.repeat(60));
|
|
233
|
+
|
|
234
|
+
// Print checks
|
|
235
|
+
console.log('\nChecks:');
|
|
236
|
+
for (const check of this.checks) {
|
|
237
|
+
const icon = check.status === 'pass' ? 'ā
' :
|
|
238
|
+
check.status === 'fail' ? 'ā' :
|
|
239
|
+
check.status === 'warn' ? 'ā ļø' : 'ā¹ļø';
|
|
240
|
+
console.log(` ${icon} ${check.name}: ${check.message}`);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Print errors
|
|
244
|
+
if (this.errors.length > 0) {
|
|
245
|
+
console.log('\nā Errors:');
|
|
246
|
+
for (const error of this.errors) {
|
|
247
|
+
console.log(` - ${error}`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Print warnings
|
|
252
|
+
if (this.warnings.length > 0) {
|
|
253
|
+
console.log('\nā ļø Warnings:');
|
|
254
|
+
for (const warning of this.warnings) {
|
|
255
|
+
console.log(` - ${warning}`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Summary
|
|
260
|
+
console.log('\n' + '-'.repeat(60));
|
|
261
|
+
if (this.errors.length === 0) {
|
|
262
|
+
console.log('ā
Validation PASSED - Ready to deploy');
|
|
263
|
+
} else {
|
|
264
|
+
console.log(`ā Validation FAILED - ${this.errors.length} error(s) found`);
|
|
265
|
+
}
|
|
266
|
+
console.log('-'.repeat(60) + '\n');
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// CLI entry point
|
|
271
|
+
async function main() {
|
|
272
|
+
const args = process.argv.slice(2);
|
|
273
|
+
const platformIndex = args.indexOf('--platform');
|
|
274
|
+
const platform = platformIndex >= 0 ? args[platformIndex + 1] : null;
|
|
275
|
+
|
|
276
|
+
if (!platform || !PLATFORMS[platform]) {
|
|
277
|
+
console.error('Usage: node validate-deployment.js --platform <railway|cloudflare|vercel|netlify>');
|
|
278
|
+
console.error('Available platforms:', Object.keys(PLATFORMS).join(', '));
|
|
279
|
+
process.exit(1);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const options = {};
|
|
283
|
+
const projectNameIndex = args.indexOf('--project-name');
|
|
284
|
+
if (projectNameIndex >= 0) {
|
|
285
|
+
options.projectName = args[projectNameIndex + 1];
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const validator = new DeploymentValidator(platform, options);
|
|
289
|
+
const success = await validator.validate();
|
|
290
|
+
process.exit(success ? 0 : 1);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "agent-creator",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Create Claude Code CLI agents (subagents) following best practices",
|
|
5
|
+
"category": "development",
|
|
6
|
+
"portability": 100,
|
|
7
|
+
"features": [
|
|
8
|
+
"Task tool configuration",
|
|
9
|
+
"Agent specialization patterns",
|
|
10
|
+
"Workflow integration",
|
|
11
|
+
"RAG-enhanced knowledge bases"
|
|
12
|
+
],
|
|
13
|
+
"entryPoint": "skill.md",
|
|
14
|
+
"context": [],
|
|
15
|
+
"workflows": [],
|
|
16
|
+
"requiredTools": ["Task", "Read", "Glob", "Grep"],
|
|
17
|
+
"suggestedModel": "sonnet"
|
|
18
|
+
}
|