@specsafe/cli 0.1.0 → 0.3.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/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +72 -6
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/rules.d.ts +6 -0
- package/dist/commands/rules.d.ts.map +1 -0
- package/dist/commands/rules.js +147 -0
- package/dist/commands/rules.js.map +1 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/rules/downloader.d.ts +40 -0
- package/dist/rules/downloader.d.ts.map +1 -0
- package/dist/rules/downloader.js +253 -0
- package/dist/rules/downloader.js.map +1 -0
- package/dist/rules/index.d.ts +8 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +8 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/registry.d.ts +45 -0
- package/dist/rules/registry.d.ts.map +1 -0
- package/dist/rules/registry.js +158 -0
- package/dist/rules/registry.js.map +1 -0
- package/dist/rules/types.d.ts +86 -0
- package/dist/rules/types.d.ts.map +1 -0
- package/dist/rules/types.js +6 -0
- package/dist/rules/types.js.map +1 -0
- package/dist/utils/detectTools.d.ts +15 -0
- package/dist/utils/detectTools.d.ts.map +1 -0
- package/dist/utils/detectTools.js +54 -0
- package/dist/utils/detectTools.js.map +1 -0
- package/dist/utils/generateToolConfig.d.ts +12 -0
- package/dist/utils/generateToolConfig.d.ts.map +1 -0
- package/dist/utils/generateToolConfig.js +200 -0
- package/dist/utils/generateToolConfig.js.map +1 -0
- package/package.json +4 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,eAAO,MAAM,WAAW,SAyJpB,CAAC"}
|
package/dist/commands/init.js
CHANGED
|
@@ -3,12 +3,49 @@ import chalk from 'chalk';
|
|
|
3
3
|
import ora from 'ora';
|
|
4
4
|
import { writeFile, mkdir } from 'fs/promises';
|
|
5
5
|
import { ProjectTracker } from '@specsafe/core';
|
|
6
|
+
import { checkbox, select, confirm, input } from '@inquirer/prompts';
|
|
7
|
+
import { detectInstalledTools, availableTools } from '../utils/detectTools.js';
|
|
8
|
+
import { generateToolConfig, generateGitHooks } from '../utils/generateToolConfig.js';
|
|
6
9
|
export const initCommand = new Command('init')
|
|
7
10
|
.description('Initialize a new SpecSafe project')
|
|
8
11
|
.argument('[name]', 'Project name', 'my-project')
|
|
9
|
-
.action(async (
|
|
12
|
+
.action(async (defaultName) => {
|
|
10
13
|
const spinner = ora('Initializing SpecSafe project...').start();
|
|
11
14
|
try {
|
|
15
|
+
// Interactive prompts for project name
|
|
16
|
+
spinner.stop();
|
|
17
|
+
const name = await input({
|
|
18
|
+
message: 'Project name:',
|
|
19
|
+
default: defaultName,
|
|
20
|
+
});
|
|
21
|
+
// 1. Detect existing tools
|
|
22
|
+
const detectedTools = detectInstalledTools();
|
|
23
|
+
// 2. Prompt user to confirm/select tools
|
|
24
|
+
const toolChoices = availableTools.map((tool) => ({
|
|
25
|
+
name: tool.displayName + (detectedTools.includes(tool.name) ? ' (detected)' : ''),
|
|
26
|
+
value: tool.name,
|
|
27
|
+
checked: detectedTools.includes(tool.name),
|
|
28
|
+
}));
|
|
29
|
+
const selectedTools = await checkbox({
|
|
30
|
+
message: 'Which AI coding assistants do you use?',
|
|
31
|
+
choices: toolChoices,
|
|
32
|
+
});
|
|
33
|
+
// 3. Prompt for test framework
|
|
34
|
+
const testFramework = await select({
|
|
35
|
+
message: 'Select testing framework:',
|
|
36
|
+
choices: [
|
|
37
|
+
{ name: 'Vitest (recommended)', value: 'vitest' },
|
|
38
|
+
{ name: 'Jest', value: 'jest' },
|
|
39
|
+
{ name: 'Playwright (E2E)', value: 'playwright' },
|
|
40
|
+
],
|
|
41
|
+
default: 'vitest',
|
|
42
|
+
});
|
|
43
|
+
// 4. Prompt for git hooks
|
|
44
|
+
const useGitHooks = await confirm({
|
|
45
|
+
message: 'Enable git hooks for spec validation?',
|
|
46
|
+
default: true,
|
|
47
|
+
});
|
|
48
|
+
spinner.start('Creating project structure...');
|
|
12
49
|
// Create directory structure
|
|
13
50
|
await mkdir('specs/active', { recursive: true });
|
|
14
51
|
await mkdir('specs/completed', { recursive: true });
|
|
@@ -54,16 +91,41 @@ export const initCommand = new Command('init')
|
|
|
54
91
|
## 9. Notes & References
|
|
55
92
|
`;
|
|
56
93
|
await writeFile('specs/template.md', template);
|
|
57
|
-
// Create config file
|
|
94
|
+
// Create config file with selected tools
|
|
95
|
+
const toolsConfig = {};
|
|
96
|
+
for (const tool of selectedTools) {
|
|
97
|
+
toolsConfig[tool] = { enabled: true };
|
|
98
|
+
}
|
|
58
99
|
const config = {
|
|
59
|
-
|
|
100
|
+
project: name,
|
|
60
101
|
version: '1.0.0',
|
|
61
102
|
stages: ['spec', 'test', 'code', 'qa', 'complete'],
|
|
62
|
-
testFramework
|
|
63
|
-
language: 'typescript'
|
|
103
|
+
testFramework,
|
|
104
|
+
language: 'typescript',
|
|
105
|
+
tools: toolsConfig,
|
|
106
|
+
gitHooks: {
|
|
107
|
+
enabled: useGitHooks,
|
|
108
|
+
},
|
|
64
109
|
};
|
|
65
110
|
await writeFile('specsafe.config.json', JSON.stringify(config, null, 2));
|
|
111
|
+
spinner.text = 'Generating tool configurations...';
|
|
112
|
+
// Generate tool configs for selected tools
|
|
113
|
+
for (const tool of selectedTools) {
|
|
114
|
+
await generateToolConfig(tool, '.');
|
|
115
|
+
}
|
|
116
|
+
// Generate git hooks if enabled
|
|
117
|
+
if (useGitHooks) {
|
|
118
|
+
await generateGitHooks('.');
|
|
119
|
+
}
|
|
66
120
|
spinner.succeed(chalk.green(`Initialized SpecSafe project: ${name}`));
|
|
121
|
+
console.log('\n' + chalk.blue('Generated configurations:'));
|
|
122
|
+
for (const tool of selectedTools) {
|
|
123
|
+
console.log(chalk.green(` ✓ ${tool} configuration`));
|
|
124
|
+
}
|
|
125
|
+
if (useGitHooks) {
|
|
126
|
+
console.log(chalk.green(' ✓ Git hooks enabled'));
|
|
127
|
+
}
|
|
128
|
+
console.log(chalk.green(` ✓ ${testFramework} configuration`));
|
|
67
129
|
console.log('\n' + chalk.blue('Next steps:'));
|
|
68
130
|
console.log(' 1. specsafe new <spec-name> - Create a new spec');
|
|
69
131
|
console.log(' 2. specsafe status - View project status');
|
|
@@ -72,7 +134,11 @@ export const initCommand = new Command('init')
|
|
|
72
134
|
catch (error) {
|
|
73
135
|
spinner.fail(chalk.red(`Failed to initialize: ${error.message}`));
|
|
74
136
|
if (error.message.includes('EEXIST')) {
|
|
75
|
-
console.log(chalk.gray(
|
|
137
|
+
console.log(chalk.gray("💡 Tip: Directory already exists. Use 'specsafe init <name>' to create elsewhere."));
|
|
138
|
+
}
|
|
139
|
+
if (error.message.includes('User force closed')) {
|
|
140
|
+
console.log(chalk.gray('💡 Tip: Init was cancelled by user.'));
|
|
141
|
+
process.exit(0);
|
|
76
142
|
}
|
|
77
143
|
process.exit(1);
|
|
78
144
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,GAAG,MAAM,KAAK,CAAC;AACtB,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/commands/rules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC;;GAEG;AACH,eAAO,MAAM,YAAY,SAmKtB,CAAC"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { getAllTools, detectTool, loadInstalledTools, saveToolConfig, removeToolConfig, isValidTool, getTool, } from '../rules/index.js';
|
|
5
|
+
import { downloadRules, updateRules, removeRules, getRulesVersion } from '../rules/downloader.js';
|
|
6
|
+
/**
|
|
7
|
+
* Rules command - manage AI coding assistant integrations
|
|
8
|
+
*/
|
|
9
|
+
export const rulesCommand = new Command('rules')
|
|
10
|
+
.description('Manage AI coding assistant rules and integrations')
|
|
11
|
+
.addCommand(new Command('list')
|
|
12
|
+
.description('List available and installed rules')
|
|
13
|
+
.action(async () => {
|
|
14
|
+
const spinner = ora('Loading rules...').start();
|
|
15
|
+
try {
|
|
16
|
+
const allTools = getAllTools();
|
|
17
|
+
const installedTools = await loadInstalledTools();
|
|
18
|
+
const installedMap = new Map(installedTools.map((t) => [t.name, t]));
|
|
19
|
+
spinner.stop();
|
|
20
|
+
console.log(chalk.bold('\n📋 Available Rules:\n'));
|
|
21
|
+
for (const tool of allTools) {
|
|
22
|
+
const isInstalled = installedMap.has(tool.name);
|
|
23
|
+
const isDetected = await detectTool(tool.name);
|
|
24
|
+
const status = isInstalled
|
|
25
|
+
? chalk.green('[installed]')
|
|
26
|
+
: isDetected
|
|
27
|
+
? chalk.yellow('[detected]')
|
|
28
|
+
: chalk.gray('[available]');
|
|
29
|
+
console.log(` ${status} ${chalk.cyan(tool.name.padEnd(12))} ${tool.description}`);
|
|
30
|
+
if (isInstalled) {
|
|
31
|
+
const installed = installedMap.get(tool.name);
|
|
32
|
+
console.log(` ${chalk.gray(`v${installed.version}`)}`);
|
|
33
|
+
}
|
|
34
|
+
if (isDetected && !isInstalled) {
|
|
35
|
+
console.log(` ${chalk.gray(`Config files found: ${tool.files.join(', ')}`)}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
console.log(chalk.gray(`\n${allTools.length} tools available`));
|
|
39
|
+
console.log(chalk.gray(`\nUse 'specsafe rules add <tool>' to install rules`));
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
spinner.fail(chalk.red(`Failed to list rules: ${error.message}`));
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
}))
|
|
46
|
+
.addCommand(new Command('add')
|
|
47
|
+
.description('Download and install rules for a tool')
|
|
48
|
+
.argument('<tool>', 'Tool name (cursor, continue, aider, zed, git-hooks)')
|
|
49
|
+
.action(async (toolName) => {
|
|
50
|
+
if (!isValidTool(toolName)) {
|
|
51
|
+
console.error(chalk.red(`Error: Unknown tool "${toolName}"`));
|
|
52
|
+
console.log(chalk.gray(`\nAvailable tools: ${getAllTools().map((t) => t.name).join(', ')}`));
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
const tool = getTool(toolName);
|
|
56
|
+
const spinner = ora(`Installing ${toolName} rules...`).start();
|
|
57
|
+
try {
|
|
58
|
+
const result = await downloadRules(toolName);
|
|
59
|
+
if (result.success) {
|
|
60
|
+
// Save to config
|
|
61
|
+
const version = await getRulesVersion(toolName);
|
|
62
|
+
await saveToolConfig(toolName, { enabled: true, version });
|
|
63
|
+
spinner.succeed(chalk.green(result.message));
|
|
64
|
+
console.log(chalk.gray(`\nCreated files:`));
|
|
65
|
+
for (const file of tool.files) {
|
|
66
|
+
console.log(chalk.gray(` • ${file}`));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
spinner.fail(chalk.red(result.message));
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
spinner.fail(chalk.red(`Failed to install ${toolName} rules: ${error.message}`));
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
}))
|
|
79
|
+
.addCommand(new Command('remove')
|
|
80
|
+
.description('Remove rules for a tool')
|
|
81
|
+
.argument('<tool>', 'Tool name to remove')
|
|
82
|
+
.action(async (toolName) => {
|
|
83
|
+
if (!isValidTool(toolName)) {
|
|
84
|
+
console.error(chalk.red(`Error: Unknown tool "${toolName}"`));
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
const spinner = ora(`Removing ${toolName} rules...`).start();
|
|
88
|
+
try {
|
|
89
|
+
const result = await removeRules(toolName);
|
|
90
|
+
if (result.success) {
|
|
91
|
+
// Remove from config
|
|
92
|
+
await removeToolConfig(toolName);
|
|
93
|
+
spinner.succeed(chalk.green(result.message));
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
spinner.fail(chalk.red(result.message));
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
spinner.fail(chalk.red(`Failed to remove ${toolName} rules: ${error.message}`));
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
}))
|
|
105
|
+
.addCommand(new Command('update')
|
|
106
|
+
.description('Update all installed rules to the latest version')
|
|
107
|
+
.action(async () => {
|
|
108
|
+
const spinner = ora('Checking installed rules...').start();
|
|
109
|
+
try {
|
|
110
|
+
const installedTools = await loadInstalledTools();
|
|
111
|
+
if (installedTools.length === 0) {
|
|
112
|
+
spinner.stop();
|
|
113
|
+
console.log(chalk.yellow('No rules installed. Run "specsafe rules add <tool>" first.'));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
spinner.text = `Updating ${installedTools.length} rule set(s)...`;
|
|
117
|
+
const results = await Promise.all(installedTools.map(async (tool) => {
|
|
118
|
+
const result = await updateRules(tool.name);
|
|
119
|
+
return { ...result, toolName: tool.name };
|
|
120
|
+
}));
|
|
121
|
+
spinner.stop();
|
|
122
|
+
let successCount = 0;
|
|
123
|
+
let failCount = 0;
|
|
124
|
+
for (const result of results) {
|
|
125
|
+
if (result.success) {
|
|
126
|
+
console.log(chalk.green(`✓ ${result.message}`));
|
|
127
|
+
const version = await getRulesVersion(result.toolName);
|
|
128
|
+
await saveToolConfig(result.toolName, { enabled: true, version });
|
|
129
|
+
successCount++;
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
console.log(chalk.red(`✗ ${result.message}`));
|
|
133
|
+
failCount++;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
console.log(chalk.gray(`\nUpdated ${successCount} tool(s)`));
|
|
137
|
+
if (failCount > 0) {
|
|
138
|
+
console.log(chalk.red(`${failCount} update(s) failed`));
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
spinner.fail(chalk.red(`Failed to update rules: ${error.message}`));
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
}));
|
|
147
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/commands/rules.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -8,6 +8,12 @@ export interface SpecSafeConfig {
|
|
|
8
8
|
stages: string[];
|
|
9
9
|
testFramework: 'vitest' | 'jest';
|
|
10
10
|
language: 'typescript';
|
|
11
|
+
tools?: {
|
|
12
|
+
[toolName: string]: {
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
version: string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
11
17
|
}
|
|
12
18
|
/**
|
|
13
19
|
* Load SpecSafe configuration from specsafe.config.json
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,EAAE,QAAQ,GAAG,MAAM,CAAC;IACjC,QAAQ,EAAE,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,EAAE,QAAQ,GAAG,MAAM,CAAC;IACjC,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,CAAC,EAAE;QACN,CAAC,QAAQ,EAAE,MAAM,GAAG;YAClB,OAAO,EAAE,OAAO,CAAC;YACjB,OAAO,EAAE,MAAM,CAAC;SACjB,CAAC;KACH,CAAC;CACH;AAUD;;;GAGG;AACH,wBAAsB,UAAU,CAAC,GAAG,GAAE,MAAsB,GAAG,OAAO,CAAC,cAAc,CAAC,CA4BrF"}
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAgB5B,MAAM,cAAc,GAAmB;IACrC,WAAW,EAAE,kBAAkB;IAC/B,OAAO,EAAE,OAAO;IAChB,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,CAAC;IAC9D,aAAa,EAAE,QAAQ;IACvB,QAAQ,EAAE,YAAY;CACvB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAC1D,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,sBAAsB,CAAC,CAAC;IAErD,IAAI,CAAC;QACH,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;QACzB,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAA4B,CAAC;QAE9D,sBAAsB;QACtB,OAAO;YACL,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,cAAc,CAAC,WAAW;YAC7D,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,cAAc,CAAC,OAAO;YACjD,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,cAAc,CAAC,MAAM;YAC9C,aAAa,EAAE,MAAM,CAAC,aAAa,IAAI,cAAc,CAAC,aAAa;YACnE,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,cAAc,CAAC,QAAQ;SACrD,CAAC;IACJ,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC5B,6CAA6C;YAC7C,OAAO,EAAE,GAAG,cAAc,EAAE,CAAC;QAC/B,CAAC;QAED,IAAI,KAAK,YAAY,WAAW,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CAAC,yCAAyC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC5E,CAAC;QAED,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ import { completeCommand } from './commands/complete.js';
|
|
|
16
16
|
import { listCommand } from './commands/list.js';
|
|
17
17
|
import { archiveCommand } from './commands/archive.js';
|
|
18
18
|
import { doctorCommand } from './commands/doctor.js';
|
|
19
|
+
import { rulesCommand } from './commands/rules.js';
|
|
19
20
|
const program = new Command();
|
|
20
21
|
program
|
|
21
22
|
.name('specsafe')
|
|
@@ -33,6 +34,7 @@ program.addCommand(qaCommand);
|
|
|
33
34
|
program.addCommand(completeCommand);
|
|
34
35
|
program.addCommand(archiveCommand);
|
|
35
36
|
program.addCommand(doctorCommand);
|
|
37
|
+
program.addCommand(rulesCommand);
|
|
36
38
|
// Global error handling
|
|
37
39
|
program.exitOverride();
|
|
38
40
|
(async () => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAEnD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,UAAU,CAAC;KAChB,WAAW,CAAC,mFAAmF,CAAC;KAChG,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,eAAe;AACf,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;AAC9B,OAAO,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;AACpC,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AAEjC,wBAAwB;AACxB,OAAO,CAAC,YAAY,EAAE,CAAC;AAEvB,CAAC,KAAK,IAAI,EAAE;IACV,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,IAAI,KAAK,CAAC,IAAI,KAAK,gBAAgB,IAAI,KAAK,CAAC,IAAI,KAAK,mBAAmB,EAAE,CAAC;YAC1E,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;YAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;AACH,CAAC,CAAC,EAAE,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rules Downloader
|
|
3
|
+
* Downloads rules from the GitHub repository or local source
|
|
4
|
+
*/
|
|
5
|
+
import type { DownloadOptions, RuleOperationResult } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Path to the rules directory in the repository
|
|
8
|
+
*/
|
|
9
|
+
export declare function getRulesSourcePath(): string;
|
|
10
|
+
/**
|
|
11
|
+
* Get the path to a specific tool's rules
|
|
12
|
+
*/
|
|
13
|
+
export declare function getToolRulesPath(toolName: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Check if rules exist for a tool in the source
|
|
16
|
+
*/
|
|
17
|
+
export declare function rulesExist(toolName: string): Promise<boolean>;
|
|
18
|
+
/**
|
|
19
|
+
* Get the content of a rule file from the source
|
|
20
|
+
*/
|
|
21
|
+
export declare function getRuleContent(toolName: string, fileName: string): Promise<string | null>;
|
|
22
|
+
/**
|
|
23
|
+
* Get the version of rules for a tool
|
|
24
|
+
* Currently returns '1.0.0' as placeholder
|
|
25
|
+
* In the future, this could read from a manifest.json
|
|
26
|
+
*/
|
|
27
|
+
export declare function getRulesVersion(_toolName: string): Promise<string>;
|
|
28
|
+
/**
|
|
29
|
+
* Download and install rules for a tool
|
|
30
|
+
*/
|
|
31
|
+
export declare function downloadRules(toolName: string, cwd?: string, options?: DownloadOptions): Promise<RuleOperationResult>;
|
|
32
|
+
/**
|
|
33
|
+
* Remove rules for a tool
|
|
34
|
+
*/
|
|
35
|
+
export declare function removeRules(toolName: string, cwd?: string): Promise<RuleOperationResult>;
|
|
36
|
+
/**
|
|
37
|
+
* Update rules for a tool to the latest version
|
|
38
|
+
*/
|
|
39
|
+
export declare function updateRules(toolName: string, cwd?: string): Promise<RuleOperationResult>;
|
|
40
|
+
//# sourceMappingURL=downloader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"downloader.d.ts","sourceRoot":"","sources":["../../src/rules/downloader.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAOvE;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAa3C;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUnE;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAOxB;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGxE;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,GAAG,GAAE,MAAsB,EAC3B,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,mBAAmB,CAAC,CAiF9B;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CAqD9B;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,MAAM,EAChB,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CAW9B"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rules Downloader
|
|
3
|
+
* Downloads rules from the GitHub repository or local source
|
|
4
|
+
*/
|
|
5
|
+
import { readFile, access, mkdir, writeFile, rm, chmod } from 'fs/promises';
|
|
6
|
+
import { join, dirname } from 'path';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
import { existsSync } from 'fs';
|
|
9
|
+
import { getTool, isValidTool } from './registry.js';
|
|
10
|
+
// Get the directory of the current module
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
/**
|
|
14
|
+
* Path to the rules directory in the repository
|
|
15
|
+
*/
|
|
16
|
+
export function getRulesSourcePath() {
|
|
17
|
+
// In development: rules are in the repo root
|
|
18
|
+
// In production: rules are bundled with the package
|
|
19
|
+
const possiblePaths = [
|
|
20
|
+
join(__dirname, '../../../../../rules'), // From dist/cli/src/rules/ to repo root
|
|
21
|
+
join(__dirname, '../../../rules'), // Alternative path
|
|
22
|
+
join(process.cwd(), 'rules'), // Local development
|
|
23
|
+
];
|
|
24
|
+
for (const p of possiblePaths) {
|
|
25
|
+
if (existsSync(p))
|
|
26
|
+
return p;
|
|
27
|
+
}
|
|
28
|
+
return possiblePaths[0];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get the path to a specific tool's rules
|
|
32
|
+
*/
|
|
33
|
+
export function getToolRulesPath(toolName) {
|
|
34
|
+
return join(getRulesSourcePath(), toolName);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Check if rules exist for a tool in the source
|
|
38
|
+
*/
|
|
39
|
+
export async function rulesExist(toolName) {
|
|
40
|
+
if (!isValidTool(toolName))
|
|
41
|
+
return false;
|
|
42
|
+
const toolRulesPath = getToolRulesPath(toolName);
|
|
43
|
+
try {
|
|
44
|
+
await access(toolRulesPath);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Get the content of a rule file from the source
|
|
53
|
+
*/
|
|
54
|
+
export async function getRuleContent(toolName, fileName) {
|
|
55
|
+
const filePath = join(getToolRulesPath(toolName), fileName);
|
|
56
|
+
try {
|
|
57
|
+
return await readFile(filePath, 'utf-8');
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Get the version of rules for a tool
|
|
65
|
+
* Currently returns '1.0.0' as placeholder
|
|
66
|
+
* In the future, this could read from a manifest.json
|
|
67
|
+
*/
|
|
68
|
+
export async function getRulesVersion(_toolName) {
|
|
69
|
+
// Placeholder: in the future, read from manifest.json
|
|
70
|
+
return '1.0.0';
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Download and install rules for a tool
|
|
74
|
+
*/
|
|
75
|
+
export async function downloadRules(toolName, cwd = process.cwd(), options = {}) {
|
|
76
|
+
if (!isValidTool(toolName)) {
|
|
77
|
+
return {
|
|
78
|
+
success: false,
|
|
79
|
+
message: `Unknown tool: ${toolName}`,
|
|
80
|
+
tool: toolName,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const tool = getTool(toolName);
|
|
84
|
+
if (!tool) {
|
|
85
|
+
return {
|
|
86
|
+
success: false,
|
|
87
|
+
message: `Tool definition not found: ${toolName}`,
|
|
88
|
+
tool: toolName,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
// Check if rules exist in source
|
|
93
|
+
const rulesPath = getToolRulesPath(toolName);
|
|
94
|
+
try {
|
|
95
|
+
await access(rulesPath);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return {
|
|
99
|
+
success: false,
|
|
100
|
+
message: `Rules not found for ${toolName} at ${rulesPath}`,
|
|
101
|
+
tool: toolName,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
// Install each file defined for the tool
|
|
105
|
+
for (const file of tool.files) {
|
|
106
|
+
// tool.files contains destination paths (e.g. ".continue/config.yaml")
|
|
107
|
+
// Source files are stored flat in rules/<tool>/ (e.g. "rules/continue/config.yaml")
|
|
108
|
+
// Extract just the filename for source path lookup
|
|
109
|
+
const sourceFile = file.split('/').pop();
|
|
110
|
+
const sourcePath = join(rulesPath, sourceFile);
|
|
111
|
+
const targetPath = join(cwd, file);
|
|
112
|
+
// Create parent directories if needed
|
|
113
|
+
await mkdir(dirname(targetPath), { recursive: true });
|
|
114
|
+
try {
|
|
115
|
+
const content = await readFile(sourcePath, 'utf-8');
|
|
116
|
+
await writeFile(targetPath, content);
|
|
117
|
+
// Set execute permission for hook files
|
|
118
|
+
if (file.includes('hook') || file === 'pre-commit' || file === 'post-commit') {
|
|
119
|
+
await chmod(targetPath, 0o755);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
// If file doesn't exist in source, create an empty placeholder
|
|
124
|
+
if (error.code === 'ENOENT') {
|
|
125
|
+
await mkdir(dirname(targetPath), { recursive: true });
|
|
126
|
+
await writeFile(targetPath, getPlaceholderContent(toolName, file));
|
|
127
|
+
// Set execute permission for hook files
|
|
128
|
+
if (file.includes('hook') || file === 'pre-commit' || file === 'post-commit') {
|
|
129
|
+
await chmod(targetPath, 0o755);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const version = await getRulesVersion(toolName);
|
|
138
|
+
return {
|
|
139
|
+
success: true,
|
|
140
|
+
message: `Installed ${toolName} rules (v${version})`,
|
|
141
|
+
tool: toolName,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
return {
|
|
146
|
+
success: false,
|
|
147
|
+
message: `Failed to install ${toolName} rules: ${error.message}`,
|
|
148
|
+
tool: toolName,
|
|
149
|
+
error,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Remove rules for a tool
|
|
155
|
+
*/
|
|
156
|
+
export async function removeRules(toolName, cwd = process.cwd()) {
|
|
157
|
+
if (!isValidTool(toolName)) {
|
|
158
|
+
return {
|
|
159
|
+
success: false,
|
|
160
|
+
message: `Unknown tool: ${toolName}`,
|
|
161
|
+
tool: toolName,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
const tool = getTool(toolName);
|
|
165
|
+
if (!tool) {
|
|
166
|
+
return {
|
|
167
|
+
success: false,
|
|
168
|
+
message: `Tool definition not found: ${toolName}`,
|
|
169
|
+
tool: toolName,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
const removedFiles = [];
|
|
174
|
+
for (const file of tool.files) {
|
|
175
|
+
const filePath = join(cwd, file);
|
|
176
|
+
try {
|
|
177
|
+
await access(filePath);
|
|
178
|
+
await rm(filePath);
|
|
179
|
+
removedFiles.push(file);
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// File doesn't exist, skip
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (removedFiles.length === 0) {
|
|
186
|
+
return {
|
|
187
|
+
success: true,
|
|
188
|
+
message: `No ${toolName} rules found to remove`,
|
|
189
|
+
tool: toolName,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
success: true,
|
|
194
|
+
message: `Removed ${toolName} rules`,
|
|
195
|
+
tool: toolName,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
return {
|
|
200
|
+
success: false,
|
|
201
|
+
message: `Failed to remove ${toolName} rules: ${error.message}`,
|
|
202
|
+
tool: toolName,
|
|
203
|
+
error,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Update rules for a tool to the latest version
|
|
209
|
+
*/
|
|
210
|
+
export async function updateRules(toolName, cwd = process.cwd()) {
|
|
211
|
+
// For now, update is the same as download (overwrite)
|
|
212
|
+
// In the future, this could check versions and only update if needed
|
|
213
|
+
const result = await downloadRules(toolName, cwd, { force: true });
|
|
214
|
+
if (result.success) {
|
|
215
|
+
const version = await getRulesVersion(toolName);
|
|
216
|
+
result.message = `Updated ${toolName} rules to v${version}`;
|
|
217
|
+
}
|
|
218
|
+
return result;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Get placeholder content for a rule file
|
|
222
|
+
*/
|
|
223
|
+
function getPlaceholderContent(toolName, fileName) {
|
|
224
|
+
const extension = fileName.split('.').pop();
|
|
225
|
+
switch (extension) {
|
|
226
|
+
case 'json':
|
|
227
|
+
return JSON.stringify({
|
|
228
|
+
name: toolName,
|
|
229
|
+
version: '1.0.0',
|
|
230
|
+
description: `SpecSafe rules for ${toolName}`,
|
|
231
|
+
note: 'This is a placeholder. Content will be added in a future update.',
|
|
232
|
+
}, null, 2);
|
|
233
|
+
case 'yml':
|
|
234
|
+
case 'yaml':
|
|
235
|
+
return `# SpecSafe Rules for ${toolName}
|
|
236
|
+
# Version: 1.0.0
|
|
237
|
+
# This is a placeholder. Content will be added in a future update.
|
|
238
|
+
`;
|
|
239
|
+
case 'md':
|
|
240
|
+
return `# SpecSafe Rules for ${toolName}
|
|
241
|
+
|
|
242
|
+
## Version: 1.0.0
|
|
243
|
+
|
|
244
|
+
> This is a placeholder. Content will be added in a future update.
|
|
245
|
+
`;
|
|
246
|
+
default:
|
|
247
|
+
return `# SpecSafe Rules for ${toolName}
|
|
248
|
+
# Version: 1.0.0
|
|
249
|
+
# This is a placeholder. Content will be added in a future update.
|
|
250
|
+
`;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=downloader.js.map
|