@onuraslan/sdd 1.0.5

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 (70) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +184 -0
  3. package/bin/sdd.js +9 -0
  4. package/cli/index.js +74 -0
  5. package/cli/install.js +156 -0
  6. package/cli/prompts.js +98 -0
  7. package/cli/skills.js +56 -0
  8. package/cli/targets.js +69 -0
  9. package/package.json +23 -0
  10. package/sdd/plugin.json +10 -0
  11. package/sdd/skills/chicago-tdd/SKILL.md +26 -0
  12. package/sdd/skills/deep-spec/SKILL.md +69 -0
  13. package/sdd/skills/deep-spec/spec-format.md +32 -0
  14. package/sdd/skills/feature-implementer/SKILL.md +48 -0
  15. package/sdd/skills/feature-implementer/reference/task-implementer.md +5 -0
  16. package/sdd/skills/implementer/SKILL.md +11 -0
  17. package/sdd/skills/prd-test-writer/SKILL.md +86 -0
  18. package/sdd/skills/prd-test-writer/references/user-story-test-writer-prompt.md +12 -0
  19. package/sdd/skills/prd-to-task/SKILL.md +51 -0
  20. package/sdd/skills/prd-to-task/reference/ascii-mock-format.md +67 -0
  21. package/sdd/skills/prd-to-task/reference/feature-template.md +20 -0
  22. package/sdd/skills/prd-to-task/reference/task-template.md +63 -0
  23. package/sdd/skills/spec-to-prd/SKILL.md +76 -0
  24. package/sdd/skills/spec-to-prd/references/mapping-guide.md +95 -0
  25. package/sdd/skills/spec-to-prd/references/prd-format.md +122 -0
  26. package/sdd/skills/workflow-generator/SKILL.md +60 -0
  27. package/sdd/skills/workflow-generator/reference/workflow-bug-fix-backend.md +35 -0
  28. package/sdd/skills/workflow-generator/reference/workflow-bug-fix-frontend.md +34 -0
  29. package/sdd/skills/workflow-generator/reference/workflow-enhancement-backend.md +35 -0
  30. package/sdd/skills/workflow-generator/reference/workflow-enhancement-frontend.md +34 -0
  31. package/sdd/skills/workflow-generator/reference/workflow-feature-development-backend.md +105 -0
  32. package/sdd/skills/workflow-generator/reference/workflow-feature-frontend-development.md +78 -0
  33. package/sdd-backend/plugin.json +10 -0
  34. package/sdd-backend/skills/backend-enhancement/SKILL.md +54 -0
  35. package/sdd-backend/skills/bug-fix-backend/SKILL.md +21 -0
  36. package/sdd-backend/skills/verify-with-curl/SKILL.md +8 -0
  37. package/sdd-frontend/.mcp.json +8 -0
  38. package/sdd-frontend/plugin.json +10 -0
  39. package/sdd-frontend/skills/bug-fix-frontend/SKILL.md +15 -0
  40. package/sdd-frontend/skills/feature-e2e-verifier/SKILL.md +64 -0
  41. package/sdd-frontend/skills/feature-e2e-verifier/reference/e2e-verifier.md +17 -0
  42. package/sdd-frontend/skills/frontend-enhancement/SKILL.md +44 -0
  43. package/sdd-frontend/skills/git-cleanup-playwright-artifacts/SKILL.md +7 -0
  44. package/sdd-frontend/skills/ui-heuristic-click-audit/SKILL.md +168 -0
  45. package/sdd-frontend/skills/ui-heuristic-click-audit/references/checklist.md +72 -0
  46. package/sdd-frontend/skills/using-playwright-mcp/SKILL.md +7 -0
  47. package/sdd-frontend/skills/verify-with-playwright-mcp/SKILL.md +14 -0
  48. package/sdd-utility/plugin.json +10 -0
  49. package/sdd-utility/skills/ask-first/SKILL.md +30 -0
  50. package/sdd-utility/skills/buy-before-build/SKILL.md +11 -0
  51. package/sdd-utility/skills/code-slop-review/SKILL.md +134 -0
  52. package/sdd-utility/skills/code-slop-review/references/agent-prompt.md +110 -0
  53. package/sdd-utility/skills/code-slop-review/references/aggregate-and-report.md +62 -0
  54. package/sdd-utility/skills/code-slop-review/references/architecture-slop.md +126 -0
  55. package/sdd-utility/skills/code-slop-review/references/dead-code-slop.md +107 -0
  56. package/sdd-utility/skills/code-slop-review/references/error-handling-slop.md +101 -0
  57. package/sdd-utility/skills/code-slop-review/references/final-report-template.md +55 -0
  58. package/sdd-utility/skills/code-slop-review/references/fix-mode.md +68 -0
  59. package/sdd-utility/skills/code-slop-review/references/structural-slop.md +150 -0
  60. package/sdd-utility/skills/code-slop-review/references/test-slop.md +88 -0
  61. package/sdd-utility/skills/gap-analysis/SKILL.md +7 -0
  62. package/sdd-utility/skills/glossary-builder/SKILL.md +28 -0
  63. package/sdd-utility/skills/grounded-mode/SKILL.md +13 -0
  64. package/sdd-utility/skills/handoff/SKILL.md +67 -0
  65. package/sdd-utility/skills/handoff-resume/SKILL.md +15 -0
  66. package/sdd-utility/skills/init-feature/SKILL.md +63 -0
  67. package/sdd-utility/skills/init-feature/references/explorer-prompt.md +58 -0
  68. package/sdd-utility/skills/init-feature/references/prd-format.md +122 -0
  69. package/sdd-utility/skills/init-feature/references/prd-writer-prompt.md +23 -0
  70. package/sdd-utility/skills/using-glossary/SKILL.md +11 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Onur ASLAN
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,184 @@
1
+ # SDD Plugin Suite
2
+
3
+ **Spec-driven development from planning to implementation.**
4
+
5
+ This repository is a collection of skills that automate software development workflows using Spec-Driven Development (SDD). Designed for developers and QA engineers who want structured, adaptive workflows without rigid templates.
6
+
7
+ ---
8
+
9
+ ## Installation
10
+
11
+ You can install SDD in two ways:
12
+
13
+ ### Option 1: npm package installation (recommended)
14
+
15
+ Install the package and run the interactive installer from the npm bundle itself:
16
+
17
+ ```bash
18
+ npx @onuraslan/sdd install
19
+ ```
20
+
21
+ This installer reads the skill content from the installed npm package and copies it directly to the target agent directory. It does not clone the GitHub repository at runtime.
22
+
23
+ ### Option 2: Claude Code plugin marketplace
24
+
25
+ ```bash
26
+ /plugin marketplace add https://github.com/onur-aslan/sdd
27
+ ```
28
+
29
+ ### Option 3: skills.sh installer
30
+
31
+ If you want to copy the skills into your project for local editing and customization, you can also install them with `skills.sh`:
32
+
33
+ ```bash
34
+ npx skills@latest add onuraslan/sdd
35
+ ```
36
+
37
+ > The npm package route is the recommended runtime distribution path for the installer. The repository is kept as the source of truth for publishing and release management.
38
+ ---
39
+
40
+ ## Plugins
41
+
42
+ | Plugin | Description |
43
+ |--------|-------------|
44
+ | [**SDD Core**](sdd/) | Automated workflows from spec design to implementation |
45
+ | [**SDD Frontend**](sdd-frontend/) | Frontend-specific skills for UI design, enhancement, and testing |
46
+ | [**SDD Backend**](sdd-backend/) | Backend development skills for API, database, and server-side logic |
47
+ | [**SDD Utility**](sdd-utilty/) | Code quality analysis, gap analysis, session handoff, and glossary |
48
+
49
+ ---
50
+
51
+ ## SDD Core Plugin
52
+
53
+ Automated workflows that guide you from specification to implementation. The **workflow-gateway** skill selects the right workflow based on your task type.
54
+
55
+ ### Getting Started
56
+
57
+ **1. Run workflow-generator to select your workflow:**
58
+
59
+ ```bash
60
+ /workflow-generator
61
+ ```
62
+
63
+ This will:
64
+ - Ask you to choose: Feature Development, Bug Fix, Enhancement, or Help me decide
65
+ - Detect if it's Backend, Frontend, or Both
66
+ - Write the selected workflow to `docs/workflow.md`
67
+
68
+ **2. Follow the steps in `docs/workflow.md`:**
69
+
70
+ Each workflow file contains a sequence of skills to run in order. Run them one by one:
71
+
72
+ ---
73
+
74
+ ### Workflows
75
+
76
+ #### 1. Feature Development (Frontend)
77
+
78
+ ```
79
+ deep-spec -> spec-to-prd -> [small scope: implementer] | [large PRD: prd-to-task -> feature-implementer] -> feature-e2e-verifier -> prd-test-writer -> gap-analysis -> code-slop-review
80
+ ```
81
+
82
+ #### 2. Feature Development (Backend)
83
+
84
+ ```
85
+ deep-spec -> spec-to-prd -> [small scope: implementer -> verify-with-curl] | [large PRD: prd-to-task -> chicago-tdd (per task)] -> feature-e2e-verifier -> prd-test-writer -> gap-analysis -> code-slop-review
86
+ ```
87
+
88
+ #### 3. Bug Fix
89
+
90
+ ```
91
+ bug-fix-frontend|backend -> verify-with-playwright-mcp|verify-with-curl
92
+ ```
93
+
94
+ #### 4. Enhancement
95
+
96
+ ```
97
+ frontend|backend-enhancement -> verify-with-playwright-mcp|verify-with-curl
98
+ ```
99
+
100
+ ### Core Skills
101
+
102
+ | Skill | Command | Description |
103
+ |-------|---------|-------------|
104
+ | `workflow-generator` | `/workflow-generator` | Entry point -- selects workflow, writes `docs/workflow.md` |
105
+ | `deep-spec` | `/deep-spec` | Interview-driven spec creation |
106
+ | `spec-to-prd` | `/spec-to-prd` | Convert spec.md to PRD |
107
+ | `prd-to-task` | `/prd-to-task` | Split PRD into tasks with Gherkin scenarios |
108
+ | `implementer` | `/implementer` | Implement from conversation context (no files needed) |
109
+ | `feature-implementer` | `/feature-implementer <feature.md>` | Implement tasks sequentially |
110
+ | `chicago-tdd` | `/chicago-tdd <task.md>` | TDD for a single task (Red-Green-Refactor) |
111
+ | `prd-test-writer` | `/prd-test-writer <prd.md>` | Write acceptance tests in parallel |
112
+ | `feature-e2e-verifier` | `/feature-e2e-verifier <prd.md>` | E2E verify User Stories |
113
+
114
+ ---
115
+
116
+ ## SDD Frontend Plugin
117
+
118
+ Frontend-specific skills for UI design, enhancement, and verification.
119
+
120
+ ### Core Skills
121
+
122
+ | Skill | Command | Description |
123
+ |-------|---------|-------------|
124
+ | `frontend-enhancement` | `/frontend-enhancement` | Interview-driven UI enhancement with ASCII mockup planning |
125
+ | `bug-fix-frontend` | `/bug-fix-frontend "<description>"` | Senior-level frontend bug fixing with auto-verification |
126
+ | `feature-e2e-verifier` | `/feature-e2e-verifier <prd.md>` | E2E verify User Stories by testing all Acceptance Criteria sequentially |
127
+ | `verify-with-playwright-mcp` | `/verify-with-playwright-mcp` | Visual verification with auto-fix loop |
128
+ | `using-playwright-mcp` | `/using-playwright-mcp` | General-purpose Playwright browser automation |
129
+ | `git-cleanup-playwright-artifacts` | `/git-cleanup-playwright-artifacts` | Clean up Playwright screenshots and test artifacts |
130
+
131
+ ---
132
+
133
+ ## SDD Backend Plugin
134
+
135
+ Backend development skills for API, database, and server-side logic with unit testing.
136
+
137
+ ### Core Skills
138
+
139
+ | Skill | Command | Description |
140
+ |-------|---------|-------------|
141
+ | `bug-fix-backend` | `/bug-fix-backend` | Fix backend bugs |
142
+ | `backend-enhancement` | `/backend-enhancement` | Enhance backend features |
143
+ | `verify-with-curl` | `/verify-with-curl` | Verify APIs with curl requests |
144
+
145
+ ---
146
+
147
+ ## SDD Utility Plugin
148
+
149
+ Code quality and session management utilities.
150
+
151
+ ### Core Skills
152
+
153
+ | Skill | Command | Description |
154
+ |-------|---------|-------------|
155
+ | `code-slop-review` | `/code-slop-review` | 5-layer code quality analysis |
156
+ | `gap-analysis` | `/gap-analysis` | Compare implementation against requirements |
157
+ | `glossary-builder` | `/glossary-builder` | Build domain glossary for project |
158
+ | `using-glossary` | `/using-glossary` | Use glossary for consistent terminology |
159
+ | `ask-first` | `/ask-first` | Plan before implement — pause for approval on non-trivial tasks |
160
+ | `handoff` | `/handoff` | Capture session knowledge for the next skill |
161
+ | `handoff-resume` | `/handoff-resume` | Resume session from handoff file |
162
+ | `init-feature` | `/init-feature` | Initialize a new feature |
163
+
164
+ ### Handoff Workflow
165
+
166
+ Use this 3-step workflow to transfer session knowledge between sessions:
167
+
168
+ ```
169
+ /handoff -> /clear -> /handoff-resume
170
+ ```
171
+
172
+ 1. **`/handoff`** — Captures current session context, decisions, and progress into a handoff file.
173
+ 2. **`/clear`** — Clears the conversation history to start fresh.
174
+ 3. **`/handoff-resume`** — Loads the handoff file and restores session context for the next skill.
175
+
176
+ ---
177
+
178
+ ## License
179
+
180
+ MIT
181
+
182
+ ## Author
183
+
184
+ Onur ASLAN
package/bin/sdd.js ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { run } from '../cli/index.js';
4
+
5
+ const result = await run(process.argv.slice(2));
6
+
7
+ if (typeof result === 'string') {
8
+ process.stdout.write(`${result}\n`);
9
+ }
package/cli/index.js ADDED
@@ -0,0 +1,74 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import readline from 'node:readline/promises';
5
+
6
+ import { installSelectedSkills } from './install.js';
7
+ import { getSkillGroups } from './skills.js';
8
+ import { askInteractiveInstall } from './prompts.js';
9
+
10
+ const packageJsonPath = fileURLToPath(new URL('../package.json', import.meta.url));
11
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
12
+
13
+ function formatHelp() {
14
+ return `SDD - Spec Driven Development\n\nUsage:\n sdd <command>\n\nCommands:\n install Install SDD skills\n list Show installed SDD skills\n update Update SDD installation\n doctor Diagnose SDD installation\n\nOptions:\n --help\n --version`;
15
+ }
16
+
17
+ export async function run(argv = process.argv.slice(2)) {
18
+ if (argv.includes('--help') || argv.includes('-h')) {
19
+ return formatHelp();
20
+ }
21
+
22
+ if (argv.includes('--version') || argv.includes('-v')) {
23
+ return packageJson.version;
24
+ }
25
+
26
+ if (argv.length === 0) {
27
+ return formatHelp();
28
+ }
29
+
30
+ const [command, ...rest] = argv;
31
+
32
+ if (command === 'install') {
33
+ const args = rest;
34
+
35
+ if (process.stdin.isTTY && !args.includes('--non-interactive')) {
36
+ const options = await askInteractiveInstall();
37
+ const result = await installSelectedSkills({
38
+ targetId: options.targetId,
39
+ plugins: options.plugins,
40
+ scope: options.scope,
41
+ version: packageJson.version
42
+ });
43
+
44
+ return `Installed ${result.installedCount} skills to ${result.installRoot}.`;
45
+ }
46
+
47
+ const requestedTargets = args.includes('--all') ? Object.keys(getSkillGroups()) : ['core'];
48
+ const targetId = args.includes('--target') ? args[args.indexOf('--target') + 1] : 'claude';
49
+ const scope = args.includes('--user') ? 'user' : 'project';
50
+
51
+ const result = await installSelectedSkills({
52
+ targetId,
53
+ plugins: requestedTargets,
54
+ scope,
55
+ version: packageJson.version
56
+ });
57
+
58
+ return `Installed ${result.installedCount} skills to ${result.installRoot}.`;
59
+ }
60
+
61
+ if (command === 'list') {
62
+ return `SDD ${packageJson.version}\nInstalled targets:\nClaude Code`;
63
+ }
64
+
65
+ if (command === 'update') {
66
+ return 'Update support is not implemented yet.';
67
+ }
68
+
69
+ if (command === 'doctor') {
70
+ return 'Doctor support is not implemented yet.';
71
+ }
72
+
73
+ return `${formatHelp()}\n\nUnknown command: ${command}`;
74
+ }
package/cli/install.js ADDED
@@ -0,0 +1,156 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { getPackageSkillsRoot, getSkillGroups, getSkillsForGroup } from './skills.js';
4
+ import { resolveTargetPath } from './targets.js';
5
+
6
+ function ensureDirectory(targetPath) {
7
+ fs.mkdirSync(targetPath, { recursive: true });
8
+ }
9
+
10
+ function writeMetadata(metadataPath, targetId, plugins, version) {
11
+ const metadata = {
12
+ version,
13
+ plugins,
14
+ installedAt: new Date().toISOString()
15
+ };
16
+
17
+ fs.writeFileSync(metadataPath, `${JSON.stringify(metadata, null, 2)}\n`, 'utf8');
18
+ }
19
+
20
+ function writeMcpConfig(target, selectedPlugins) {
21
+ if (!selectedPlugins.includes('frontend')) {
22
+ return;
23
+ }
24
+
25
+ const { mcpConfigPath, mcpFormat } = target;
26
+ const playwrightConfig = {
27
+ command: 'npx',
28
+ args: ['@playwright/mcp']
29
+ };
30
+
31
+ if (mcpFormat === 'toml') {
32
+ const dir = path.dirname(mcpConfigPath);
33
+ fs.mkdirSync(dir, { recursive: true });
34
+ const existing = fs.existsSync(mcpConfigPath)
35
+ ? fs.readFileSync(mcpConfigPath, 'utf8')
36
+ : '';
37
+ const lines = existing.trim() ? existing.trim().split('\n') : [];
38
+ const section = '[mcp_servers.playwright]';
39
+ const block = [
40
+ section,
41
+ 'command = "npx"',
42
+ 'args = ["@playwright/mcp"]'
43
+ ];
44
+
45
+ const filtered = lines.filter((line) => !line.startsWith('[mcp_servers.playwright]') && !line.startsWith('command = ') && !line.startsWith('args = '));
46
+ const nextContent = [...filtered, '', ...block, ''].join('\n');
47
+ fs.writeFileSync(mcpConfigPath, `${nextContent}\n`, 'utf8');
48
+ return;
49
+ }
50
+
51
+ if (mcpFormat === 'json-opencode') {
52
+ const existing = fs.existsSync(mcpConfigPath)
53
+ ? JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8'))
54
+ : {};
55
+
56
+ const nextConfig = {
57
+ ...existing,
58
+ mcp: {
59
+ ...(existing.mcp || {}),
60
+ playwright: {
61
+ type: 'stdio',
62
+ command: 'npx',
63
+ args: ['@playwright/mcp']
64
+ }
65
+ }
66
+ };
67
+
68
+ fs.writeFileSync(mcpConfigPath, `${JSON.stringify(nextConfig, null, 2)}\n`, 'utf8');
69
+ return;
70
+ }
71
+
72
+ const existing = fs.existsSync(mcpConfigPath)
73
+ ? JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8'))
74
+ : {};
75
+
76
+ const nextConfig = {
77
+ ...existing,
78
+ mcpServers: {
79
+ ...(existing.mcpServers || {}),
80
+ playwright: playwrightConfig
81
+ },
82
+ playwright: playwrightConfig
83
+ };
84
+
85
+ const dir = path.dirname(mcpConfigPath);
86
+ fs.mkdirSync(dir, { recursive: true });
87
+ fs.writeFileSync(mcpConfigPath, `${JSON.stringify(nextConfig, null, 2)}\n`, 'utf8');
88
+ }
89
+
90
+ export async function installSelectedSkills({
91
+ targetId = 'claude',
92
+ plugins = Object.keys(getSkillGroups()),
93
+ scope = 'project',
94
+ cwd = process.cwd(),
95
+ version = '1.0.2',
96
+ conflictHandler = null
97
+ }) {
98
+ const selected = Array.isArray(plugins) ? plugins : [plugins];
99
+ const consistentPlugins = selected.filter(Boolean);
100
+ const target = resolveTargetPath(targetId, scope, cwd);
101
+
102
+ ensureDirectory(target.installRoot);
103
+ let installedCount = 0;
104
+
105
+ for (const pluginId of consistentPlugins) {
106
+ const pluginConfig = getSkillGroups()[pluginId];
107
+ if (!pluginConfig) {
108
+ throw new Error(`Plugin "${pluginId}" is missing from this package. The installation cannot continue.`);
109
+ }
110
+
111
+ const groupDirectory = path.join(pluginConfig.pluginRoot, 'skills');
112
+ if (!fs.existsSync(groupDirectory)) {
113
+ throw new Error(`Plugin "${pluginId}" is missing from this package. The installation cannot continue.`);
114
+ }
115
+
116
+ const skills = getSkillsForGroup(pluginId);
117
+
118
+ for (const skillName of skills) {
119
+ const source = path.join(groupDirectory, skillName);
120
+ const destination = path.join(target.installRoot, skillName);
121
+
122
+ if (fs.existsSync(destination)) {
123
+ if (conflictHandler) {
124
+ const action = await conflictHandler({ skillName, destination, source, pluginId });
125
+ if (action === 'skip') {
126
+ continue;
127
+ }
128
+ if (action === 'abort') {
129
+ throw new Error(`Installation aborted while processing Skill: ${skillName}`);
130
+ }
131
+ if (action === 'overwrite') {
132
+ fs.rmSync(destination, { recursive: true, force: true });
133
+ }
134
+ } else {
135
+ throw new Error(`Skill already exists: ${skillName}`);
136
+ }
137
+ }
138
+
139
+ fs.cpSync(source, destination, { recursive: true, force: true });
140
+ installedCount += 1;
141
+ }
142
+ }
143
+
144
+ writeMetadata(target.metadataPath, targetId, consistentPlugins, version);
145
+ writeMcpConfig(target, consistentPlugins);
146
+
147
+ return {
148
+ installedCount,
149
+ targetId,
150
+ plugins: consistentPlugins,
151
+ scope,
152
+ installRoot: target.installRoot,
153
+ metadataPath: target.metadataPath,
154
+ mcpConfigPath: target.mcpConfigPath
155
+ };
156
+ }
package/cli/prompts.js ADDED
@@ -0,0 +1,98 @@
1
+ import readline from 'node:readline/promises';
2
+ import { targets } from './targets.js';
3
+ import { getSkillGroups } from './skills.js';
4
+
5
+ export async function promptTarget(rl) {
6
+ const targetEntries = Object.values(targets);
7
+
8
+ console.log('\nSelect target:');
9
+ targetEntries.forEach((target, index) => {
10
+ console.log(`${index + 1}. ${target.name}`);
11
+ });
12
+
13
+ const answer = await rl.question('Choose a target [1-5]: ');
14
+ const index = Number.parseInt(answer, 10) - 1;
15
+
16
+ if (!Number.isInteger(index) || index < 0 || index >= targetEntries.length) {
17
+ throw new Error('Unknown installation target.');
18
+ }
19
+
20
+ return targetEntries[index].id;
21
+ }
22
+
23
+ export async function promptPluginSelection(rl) {
24
+ const groups = Object.values(getSkillGroups());
25
+
26
+ console.log('\nSelect plugins:');
27
+ groups.forEach((group, index) => {
28
+ console.log(`${index + 1}. ${group.name}`);
29
+ });
30
+
31
+ const answer = await rl.question('Choose plugin numbers separated by commas (or "all"): ');
32
+ const normalized = answer.trim().toLowerCase();
33
+
34
+ if (normalized === 'all') {
35
+ return groups.map((group) => group.id);
36
+ }
37
+
38
+ const selected = new Set();
39
+
40
+ for (const value of normalized.split(',')) {
41
+ const trimmed = value.trim();
42
+ if (!trimmed) continue;
43
+ const index = Number.parseInt(trimmed, 10) - 1;
44
+ if (!Number.isInteger(index) || index < 0 || index >= groups.length) {
45
+ throw new Error(`Unknown plugin selection: ${trimmed}`);
46
+ }
47
+ selected.add(groups[index].id);
48
+ }
49
+
50
+ if (selected.size === 0) {
51
+ return [groups[0].id];
52
+ }
53
+
54
+ return [...selected];
55
+ }
56
+
57
+ export async function promptScope(rl) {
58
+ console.log('\nWhere should SDD be installed?');
59
+ console.log('1. Current project');
60
+ console.log('2. User home directory');
61
+ const answer = await rl.question('Choose a location [1-2]: ');
62
+ const value = Number.parseInt(answer, 10);
63
+
64
+ if (value === 2) {
65
+ return 'user';
66
+ }
67
+
68
+ return 'project';
69
+ }
70
+
71
+ export async function promptConfirmation(rl, targetId, plugins, scope) {
72
+ console.log(`\nInstalling SDD for ${targets[targetId].name} (${scope})`);
73
+ console.log(`Plugins: ${plugins.join(', ')}`);
74
+ const answer = await rl.question('Proceed? [Y/n]: ');
75
+ return answer.trim().toLowerCase() !== 'n';
76
+ }
77
+
78
+ export async function askInteractiveInstall() {
79
+ const rl = readline.createInterface({
80
+ input: process.stdin,
81
+ output: process.stdout
82
+ });
83
+
84
+ try {
85
+ const targetId = await promptTarget(rl);
86
+ const plugins = await promptPluginSelection(rl);
87
+ const scope = await promptScope(rl);
88
+ const confirmed = await promptConfirmation(rl, targetId, plugins, scope);
89
+
90
+ if (!confirmed) {
91
+ throw new Error('Installation cancelled.');
92
+ }
93
+
94
+ return { targetId, plugins, scope };
95
+ } finally {
96
+ rl.close();
97
+ }
98
+ }
package/cli/skills.js ADDED
@@ -0,0 +1,56 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+
5
+ const packageRoot = path.resolve(fileURLToPath(new URL('..', import.meta.url)));
6
+
7
+ export const skillGroups = {
8
+ core: {
9
+ id: 'core',
10
+ name: 'Core',
11
+ pluginRoot: path.join(packageRoot, 'sdd')
12
+ },
13
+ frontend: {
14
+ id: 'frontend',
15
+ name: 'Frontend',
16
+ pluginRoot: path.join(packageRoot, 'sdd-frontend')
17
+ },
18
+ backend: {
19
+ id: 'backend',
20
+ name: 'Backend',
21
+ pluginRoot: path.join(packageRoot, 'sdd-backend')
22
+ },
23
+ utility: {
24
+ id: 'utility',
25
+ name: 'Utility',
26
+ pluginRoot: path.join(packageRoot, 'sdd-utility')
27
+ }
28
+ };
29
+
30
+ export function getSkillGroups() {
31
+ return { ...skillGroups };
32
+ }
33
+
34
+ export function getPackageSkillsRoot() {
35
+ return packageRoot;
36
+ }
37
+
38
+ export function getSkillsForGroup(groupId) {
39
+ const group = skillGroups[groupId];
40
+
41
+ if (!group) {
42
+ return [];
43
+ }
44
+
45
+ const groupDirectory = path.join(group.pluginRoot, 'skills');
46
+
47
+ if (!fs.existsSync(groupDirectory)) {
48
+ throw new Error(`Plugin "${groupId}" is missing from this package.`);
49
+ }
50
+
51
+ return fs
52
+ .readdirSync(groupDirectory, { withFileTypes: true })
53
+ .filter((entry) => entry.isDirectory())
54
+ .map((entry) => entry.name)
55
+ .sort();
56
+ }
package/cli/targets.js ADDED
@@ -0,0 +1,69 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+
4
+ export const targets = {
5
+ claude: {
6
+ id: 'claude',
7
+ name: 'Claude Code',
8
+ skillDirectory: '.claude/skills',
9
+ mcpConfigPath: '.mcp.json',
10
+ mcpFormat: 'json'
11
+ },
12
+ cursor: {
13
+ id: 'cursor',
14
+ name: 'Cursor',
15
+ skillDirectory: '.cursor/skills',
16
+ mcpConfigPath: '.cursor/mcp.json',
17
+ mcpFormat: 'json'
18
+ },
19
+ codex: {
20
+ id: 'codex',
21
+ name: 'Codex',
22
+ skillDirectory: '.codex/skills',
23
+ mcpConfigPath: '.codex/config.toml',
24
+ mcpFormat: 'toml'
25
+ },
26
+ windsurf: {
27
+ id: 'windsurf',
28
+ name: 'Windsurf',
29
+ skillDirectory: '.windsurf/skills',
30
+ mcpConfigPath: '.windsurf/mcp.json',
31
+ mcpFormat: 'json'
32
+ },
33
+ opencode: {
34
+ id: 'opencode',
35
+ name: 'OpenCode',
36
+ skillDirectory: '.opencode/skills',
37
+ mcpConfigPath: 'opencode.json',
38
+ mcpFormat: 'json-opencode'
39
+ }
40
+ };
41
+
42
+ export function resolveInstallBase(scope = 'project', cwd = process.cwd()) {
43
+ if (scope === 'user') {
44
+ return os.homedir();
45
+ }
46
+
47
+ return cwd;
48
+ }
49
+
50
+ export function resolveTargetPath(targetId, scope = 'project', cwd = process.cwd()) {
51
+ const target = targets[targetId];
52
+
53
+ if (!target) {
54
+ throw new Error(`Unknown installation target: ${targetId}`);
55
+ }
56
+
57
+ const baseDir = resolveInstallBase(scope, cwd);
58
+ const installRoot = path.resolve(baseDir, target.skillDirectory);
59
+ const metadataPath = path.resolve(baseDir, path.dirname(target.skillDirectory), 'sdd.json');
60
+ const mcpConfigPath = path.resolve(baseDir, target.mcpConfigPath);
61
+
62
+ return {
63
+ ...target,
64
+ baseDir,
65
+ installRoot,
66
+ metadataPath,
67
+ mcpConfigPath
68
+ };
69
+ }
package/package.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@onuraslan/sdd",
3
+ "version": "1.0.5",
4
+ "type": "module",
5
+ "description": "Spec-Driven Development skill package and interactive installer for AI coding agents.",
6
+ "license": "MIT",
7
+ "bin": {
8
+ "sdd": "bin/sdd.js"
9
+ },
10
+ "files": [
11
+ "bin",
12
+ "cli",
13
+ "sdd",
14
+ "sdd-backend",
15
+ "sdd-frontend",
16
+ "sdd-utility",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "test": "node --test"
22
+ }
23
+ }