@specsafe/cli 0.1.0 → 0.2.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.
Files changed (37) hide show
  1. package/dist/commands/init.d.ts.map +1 -1
  2. package/dist/commands/init.js +72 -6
  3. package/dist/commands/init.js.map +1 -1
  4. package/dist/commands/rules.d.ts +6 -0
  5. package/dist/commands/rules.d.ts.map +1 -0
  6. package/dist/commands/rules.js +147 -0
  7. package/dist/commands/rules.js.map +1 -0
  8. package/dist/config.d.ts +6 -0
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js.map +1 -1
  11. package/dist/index.js +2 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/rules/downloader.d.ts +40 -0
  14. package/dist/rules/downloader.d.ts.map +1 -0
  15. package/dist/rules/downloader.js +253 -0
  16. package/dist/rules/downloader.js.map +1 -0
  17. package/dist/rules/index.d.ts +8 -0
  18. package/dist/rules/index.d.ts.map +1 -0
  19. package/dist/rules/index.js +8 -0
  20. package/dist/rules/index.js.map +1 -0
  21. package/dist/rules/registry.d.ts +45 -0
  22. package/dist/rules/registry.d.ts.map +1 -0
  23. package/dist/rules/registry.js +148 -0
  24. package/dist/rules/registry.js.map +1 -0
  25. package/dist/rules/types.d.ts +86 -0
  26. package/dist/rules/types.d.ts.map +1 -0
  27. package/dist/rules/types.js +6 -0
  28. package/dist/rules/types.js.map +1 -0
  29. package/dist/utils/detectTools.d.ts +15 -0
  30. package/dist/utils/detectTools.d.ts.map +1 -0
  31. package/dist/utils/detectTools.js +40 -0
  32. package/dist/utils/detectTools.js.map +1 -0
  33. package/dist/utils/generateToolConfig.d.ts +12 -0
  34. package/dist/utils/generateToolConfig.d.ts.map +1 -0
  35. package/dist/utils/generateToolConfig.js +200 -0
  36. package/dist/utils/generateToolConfig.js.map +1 -0
  37. 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;AAMpC,eAAO,MAAM,WAAW,SA8EpB,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"}
@@ -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 (name) => {
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
- projectName: name,
100
+ project: name,
60
101
  version: '1.0.0',
61
102
  stages: ['spec', 'test', 'code', 'qa', 'complete'],
62
- testFramework: 'vitest',
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('💡 Tip: Directory already exists. Use \'specsafe init <name>\' to create elsewhere.'));
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;AAEhD,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;KAC3C,WAAW,CAAC,mCAAmC,CAAC;KAChD,QAAQ,CAAC,QAAQ,EAAE,cAAc,EAAE,YAAY,CAAC;KAChD,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE;IAC7B,MAAM,OAAO,GAAG,GAAG,CAAC,kCAAkC,CAAC,CAAC,KAAK,EAAE,CAAC;IAEhE,IAAI,CAAC;QACH,6BAA6B;QAC7B,MAAM,KAAK,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,MAAM,KAAK,CAAC,iBAAiB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,MAAM,KAAK,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAClD,MAAM,KAAK,CAAC,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACxC,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE1C,0BAA0B;QAC1B,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QAClD,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAE/B,uBAAuB;QACvB,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCtB,CAAC;QACI,MAAM,SAAS,CAAC,mBAAmB,EAAE,QAAQ,CAAC,CAAC;QAE/C,qBAAqB;QACrB,MAAM,MAAM,GAAG;YACb,WAAW,EAAE,IAAI;YACjB,OAAO,EAAE,OAAO;YAChB,MAAM,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,CAAC;YAClD,aAAa,EAAE,QAAQ;YACvB,QAAQ,EAAE,YAAY;SACvB,CAAC;QACF,MAAM,SAAS,CAAC,sBAAsB,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAEzE,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,iCAAiC,IAAI,EAAE,CAAC,CAAC,CAAC;QAEtE,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,oDAAoD,CAAC,CAAC;QAClE,OAAO,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;IAChE,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,yBAAyB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAClE,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,qFAAqF,CAAC,CAAC,CAAC;QACjH,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { Command } from 'commander';
2
+ /**
3
+ * Rules command - manage AI coding assistant integrations
4
+ */
5
+ export declare const rulesCommand: Command;
6
+ //# sourceMappingURL=rules.d.ts.map
@@ -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
@@ -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;CACxB;AAUD;;;GAGG;AACH,wBAAsB,UAAU,CAAC,GAAG,GAAE,MAAsB,GAAG,OAAO,CAAC,cAAc,CAAC,CA4BrF"}
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"}
@@ -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;AAU5B,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"}
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;AAErD,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;AAElC,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"}
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