productkit 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +36 -1
- package/package.json +1 -1
- package/src/cli.js +23 -1
- package/src/commands/diff.js +68 -0
- package/src/commands/doctor.js +115 -0
- package/src/commands/export.js +50 -0
- package/src/commands/init.js +16 -7
- package/src/commands/reset.js +5 -2
- package/src/commands/status.js +3 -1
- package/src/utils/fileUtils.js +12 -0
- package/templates/CLAUDE.md +4 -1
- package/templates/README.md +4 -1
- package/templates/commands/productkit.analyze.md +3 -1
- package/templates/commands/productkit.assumptions.md +3 -1
- package/templates/commands/productkit.bootstrap.md +81 -0
- package/templates/commands/productkit.clarify.md +3 -1
- package/templates/commands/productkit.constitution.md +3 -1
- package/templates/commands/productkit.prioritize.md +3 -1
- package/templates/commands/productkit.problem.md +3 -1
- package/templates/commands/productkit.solution.md +3 -1
- package/templates/commands/productkit.spec.md +4 -2
- package/templates/commands/productkit.users.md +3 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Douno
|
|
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
CHANGED
|
@@ -4,6 +4,8 @@ Slash-command-driven product thinking toolkit for [Claude Code](https://claude.c
|
|
|
4
4
|
|
|
5
5
|
Product Kit gives PMs a structured workflow for validating product ideas — user personas, problem statements, assumptions mapping — all through guided AI conversations.
|
|
6
6
|
|
|
7
|
+
**[Read the full guide →](https://iamquechua.github.io/product-kit/)**
|
|
8
|
+
|
|
7
9
|
## Prerequisites
|
|
8
10
|
|
|
9
11
|
- **Node.js** 18 or later
|
|
@@ -43,6 +45,19 @@ cd my-project
|
|
|
43
45
|
|
|
44
46
|
This scaffolds a project with slash commands, a `CLAUDE.md` context file, and a `.productkit/` config directory.
|
|
45
47
|
|
|
48
|
+
For existing projects:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
cd my-existing-project
|
|
52
|
+
productkit init --existing
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
To keep artifacts out of the project root (recommended for busy codebases):
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
productkit init --existing --artifact-dir docs/product
|
|
59
|
+
```
|
|
60
|
+
|
|
46
61
|
### 2. Open Claude Code
|
|
47
62
|
|
|
48
63
|
```bash
|
|
@@ -64,12 +79,13 @@ Each command starts a guided conversation. Claude asks questions, pushes back on
|
|
|
64
79
|
| 7 | `/productkit.spec` | Generate a complete product spec | `spec.md` |
|
|
65
80
|
| — | `/productkit.clarify` | Resolve ambiguities and contradictions across artifacts | Updates existing files |
|
|
66
81
|
| — | `/productkit.analyze` | Run a consistency and completeness check | Analysis in chat |
|
|
82
|
+
| — | `/productkit.bootstrap` | Auto-draft all artifacts from existing codebase | All missing artifacts |
|
|
67
83
|
|
|
68
84
|
Commands build on each other — `/productkit.problem` reads your `users.md`, `/productkit.solution` reads your problem and users, and `/productkit.spec` synthesizes everything into a single document. You can run `/productkit.clarify` and `/productkit.analyze` at any stage to check your work.
|
|
69
85
|
|
|
70
86
|
### 4. Review your artifacts
|
|
71
87
|
|
|
72
|
-
After running the commands, your project
|
|
88
|
+
After running the commands, your project contains:
|
|
73
89
|
|
|
74
90
|
```
|
|
75
91
|
my-project/
|
|
@@ -87,6 +103,8 @@ my-project/
|
|
|
87
103
|
└── .gitignore
|
|
88
104
|
```
|
|
89
105
|
|
|
106
|
+
If you used `--artifact-dir docs/product`, artifacts live in `docs/product/` instead of the project root.
|
|
107
|
+
|
|
90
108
|
These markdown files are your product foundation — share them with your team, commit them to git, or hand `spec.md` to engineering.
|
|
91
109
|
|
|
92
110
|
## CLI Commands
|
|
@@ -95,13 +113,30 @@ These markdown files are your product foundation — share them with your team,
|
|
|
95
113
|
|---------|-------------|
|
|
96
114
|
| `productkit init <name>` | Scaffold a new project |
|
|
97
115
|
| `productkit init --existing` | Add Product Kit to the current directory |
|
|
116
|
+
| `productkit init --minimal` | Skip constitution, start with users/problem |
|
|
117
|
+
| `productkit init --artifact-dir <dir>` | Store artifacts in a custom directory |
|
|
98
118
|
| `productkit status` | Show progress — which artifacts exist and what's next |
|
|
119
|
+
| `productkit export` | Export all artifacts as a single combined markdown file |
|
|
120
|
+
| `productkit export --output <file>` | Export to a custom filename |
|
|
121
|
+
| `productkit diff` | Show what changed in artifacts since last commit |
|
|
122
|
+
| `productkit diff --staged` | Show staged artifact changes |
|
|
123
|
+
| `productkit doctor` | Check project health (missing files, outdated commands) |
|
|
99
124
|
| `productkit update` | Refresh slash commands to the latest version |
|
|
100
125
|
| `productkit reset` | Remove all artifacts and start over |
|
|
101
126
|
| `productkit list` | Show available slash commands with descriptions |
|
|
102
127
|
| `productkit completion` | Output shell completion script (bash/zsh) |
|
|
103
128
|
| `productkit check` | Verify Claude Code is installed |
|
|
104
129
|
|
|
130
|
+
## Cowork Plugin (No CLI Required)
|
|
131
|
+
|
|
132
|
+
If you prefer Claude Cowork over the command line, Product Kit is also available as a Cowork plugin. Same guided workflows, no terminal needed.
|
|
133
|
+
|
|
134
|
+
1. Download [`product-kit-plugin.zip`](https://github.com/iamquechua/product-kit/releases/download/latest-plugin/product-kit-plugin.zip) from [GitHub Releases](https://github.com/iamquechua/product-kit/releases/tag/latest-plugin)
|
|
135
|
+
2. In Cowork, go to **Plugins → + → Upload plugin**
|
|
136
|
+
3. Select the zip file
|
|
137
|
+
|
|
138
|
+
Once installed, type `/product-kit:users`, `/product-kit:problem`, etc. in Cowork's chat. See [plugin/README.md](plugin/README.md) for details.
|
|
139
|
+
|
|
105
140
|
## How It Works
|
|
106
141
|
|
|
107
142
|
Product Kit is a thin scaffolding tool. The real work happens in slash commands — markdown prompt files that live in `.claude/commands/`. When you type `/productkit.users` in Claude Code, it reads the prompt file and starts a guided conversation.
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -9,18 +9,23 @@ const updateCommand = require('./commands/update');
|
|
|
9
9
|
const resetCommand = require('./commands/reset');
|
|
10
10
|
const listCommand = require('./commands/list');
|
|
11
11
|
const completionCommand = require('./commands/completion');
|
|
12
|
+
const exportCommand = require('./commands/export');
|
|
13
|
+
const diffCommand = require('./commands/diff');
|
|
14
|
+
const doctorCommand = require('./commands/doctor');
|
|
12
15
|
|
|
13
16
|
const program = new Command();
|
|
14
17
|
|
|
15
18
|
program
|
|
16
19
|
.name('productkit')
|
|
17
20
|
.description(chalk.cyan.bold('Product thinking toolkit for Claude Code'))
|
|
18
|
-
.version('1.
|
|
21
|
+
.version('1.8.0');
|
|
19
22
|
|
|
20
23
|
program
|
|
21
24
|
.command('init [projectName]')
|
|
22
25
|
.description('Initialize a new product research project')
|
|
23
26
|
.option('--existing', 'Add Product Kit to the current directory')
|
|
27
|
+
.option('--minimal', 'Skip constitution, start with users/problem')
|
|
28
|
+
.option('--artifact-dir <dir>', 'Directory for artifacts (default: project root)')
|
|
24
29
|
.action(initCommand);
|
|
25
30
|
|
|
26
31
|
program
|
|
@@ -55,6 +60,23 @@ program
|
|
|
55
60
|
.option('--shell <shell>', 'Shell type (bash or zsh)')
|
|
56
61
|
.action(completionCommand);
|
|
57
62
|
|
|
63
|
+
program
|
|
64
|
+
.command('export')
|
|
65
|
+
.description('Export all artifacts as a single combined markdown file')
|
|
66
|
+
.option('--output <file>', 'Output filename', 'export.md')
|
|
67
|
+
.action(exportCommand);
|
|
68
|
+
|
|
69
|
+
program
|
|
70
|
+
.command('diff')
|
|
71
|
+
.description('Show what changed since last commit across artifacts')
|
|
72
|
+
.option('--staged', 'Show staged changes instead of unstaged')
|
|
73
|
+
.action(diffCommand);
|
|
74
|
+
|
|
75
|
+
program
|
|
76
|
+
.command('doctor')
|
|
77
|
+
.description('Check project health (missing files, outdated commands, etc.)')
|
|
78
|
+
.action(doctorCommand);
|
|
79
|
+
|
|
58
80
|
program.parse(process.argv);
|
|
59
81
|
|
|
60
82
|
if (process.argv.length === 2) {
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const { execSync } = require('child_process');
|
|
5
|
+
const { getArtifactDir } = require('../utils/fileUtils');
|
|
6
|
+
|
|
7
|
+
const ARTIFACT_FILES = [
|
|
8
|
+
'constitution.md',
|
|
9
|
+
'users.md',
|
|
10
|
+
'problem.md',
|
|
11
|
+
'assumptions.md',
|
|
12
|
+
'solution.md',
|
|
13
|
+
'priorities.md',
|
|
14
|
+
'spec.md',
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
async function diff(options) {
|
|
18
|
+
const root = process.cwd();
|
|
19
|
+
const configPath = path.join(root, '.productkit', 'config.json');
|
|
20
|
+
|
|
21
|
+
if (!fs.existsSync(configPath)) {
|
|
22
|
+
console.error(chalk.red('Not a Product Kit project.'));
|
|
23
|
+
console.log('Run: productkit init <name>');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Check if git is available
|
|
28
|
+
try {
|
|
29
|
+
execSync('git rev-parse --git-dir', { cwd: root, stdio: 'ignore' });
|
|
30
|
+
} catch {
|
|
31
|
+
console.error(chalk.red('Not a git repository. The diff command requires git.'));
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const artifactDir = getArtifactDir(root);
|
|
36
|
+
const relDir = path.relative(root, artifactDir);
|
|
37
|
+
const existing = ARTIFACT_FILES
|
|
38
|
+
.map(f => relDir && relDir !== '.' ? path.join(relDir, f) : f)
|
|
39
|
+
.filter(f => fs.existsSync(path.join(root, f)));
|
|
40
|
+
|
|
41
|
+
if (existing.length === 0) {
|
|
42
|
+
console.error(chalk.red('No artifacts found. Run some slash commands first.'));
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const gitArgs = options.staged ? ['diff', '--cached'] : ['diff'];
|
|
47
|
+
const cmd = ['git', ...gitArgs, '--', ...existing].join(' ');
|
|
48
|
+
|
|
49
|
+
let output;
|
|
50
|
+
try {
|
|
51
|
+
output = execSync(cmd, { cwd: root, encoding: 'utf-8' });
|
|
52
|
+
} catch (err) {
|
|
53
|
+
// git diff returns exit code 1 when there are differences in some configs
|
|
54
|
+
output = err.stdout || '';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (!output) {
|
|
58
|
+
console.log(chalk.yellow('No changes to artifacts since last commit.'));
|
|
59
|
+
if (!options.staged) {
|
|
60
|
+
console.log(chalk.dim('Tip: use --staged to see staged changes.'));
|
|
61
|
+
}
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
console.log(output);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
module.exports = diff;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const { execSync } = require('child_process');
|
|
5
|
+
|
|
6
|
+
async function doctor() {
|
|
7
|
+
const root = process.cwd();
|
|
8
|
+
const configPath = path.join(root, '.productkit', 'config.json');
|
|
9
|
+
|
|
10
|
+
if (!fs.existsSync(configPath)) {
|
|
11
|
+
console.error(chalk.red('Not a Product Kit project.'));
|
|
12
|
+
console.log('Run: productkit init <name>');
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const results = { pass: 0, warn: 0, fail: 0 };
|
|
17
|
+
|
|
18
|
+
function pass(msg) { results.pass++; console.log(chalk.green(` pass ${msg}`)); }
|
|
19
|
+
function warn(msg) { results.warn++; console.log(chalk.yellow(` warn ${msg}`)); }
|
|
20
|
+
function fail(msg) { results.fail++; console.log(chalk.red(` fail ${msg}`)); }
|
|
21
|
+
|
|
22
|
+
console.log();
|
|
23
|
+
console.log(chalk.bold('Project health check'));
|
|
24
|
+
console.log();
|
|
25
|
+
|
|
26
|
+
// 1. Config file
|
|
27
|
+
try {
|
|
28
|
+
const config = fs.readJsonSync(configPath);
|
|
29
|
+
if (config.version) {
|
|
30
|
+
pass('Config file is valid');
|
|
31
|
+
} else {
|
|
32
|
+
warn('Config file missing version field');
|
|
33
|
+
}
|
|
34
|
+
} catch {
|
|
35
|
+
fail('Config file is not valid JSON');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 2. Commands directory
|
|
39
|
+
const commandsDir = path.join(root, '.claude', 'commands');
|
|
40
|
+
if (fs.existsSync(commandsDir)) {
|
|
41
|
+
pass('Commands directory exists');
|
|
42
|
+
} else {
|
|
43
|
+
fail('Commands directory missing (.claude/commands/)');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 3. Check for expected command templates
|
|
47
|
+
const templatesDir = path.join(__dirname, '..', '..', 'templates', 'commands');
|
|
48
|
+
const expectedCommands = fs.readdirSync(templatesDir);
|
|
49
|
+
|
|
50
|
+
// Account for minimal mode
|
|
51
|
+
let config = {};
|
|
52
|
+
try { config = fs.readJsonSync(configPath); } catch {}
|
|
53
|
+
const skippable = config.minimal ? ['productkit.constitution.md'] : [];
|
|
54
|
+
|
|
55
|
+
const missing = [];
|
|
56
|
+
for (const cmd of expectedCommands) {
|
|
57
|
+
if (skippable.includes(cmd)) continue;
|
|
58
|
+
if (!fs.existsSync(path.join(commandsDir, cmd))) {
|
|
59
|
+
missing.push(cmd);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (missing.length === 0) {
|
|
64
|
+
pass('All expected command templates present');
|
|
65
|
+
} else {
|
|
66
|
+
for (const m of missing) {
|
|
67
|
+
fail(`Missing command template: ${m}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 4. Check for outdated commands
|
|
72
|
+
const outdated = [];
|
|
73
|
+
for (const cmd of expectedCommands) {
|
|
74
|
+
if (skippable.includes(cmd)) continue;
|
|
75
|
+
const installedPath = path.join(commandsDir, cmd);
|
|
76
|
+
if (!fs.existsSync(installedPath)) continue;
|
|
77
|
+
|
|
78
|
+
const installed = fs.readFileSync(installedPath, 'utf-8');
|
|
79
|
+
const bundled = fs.readFileSync(path.join(templatesDir, cmd), 'utf-8');
|
|
80
|
+
if (installed !== bundled) {
|
|
81
|
+
outdated.push(cmd);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (outdated.length === 0) {
|
|
86
|
+
pass('All command templates up to date');
|
|
87
|
+
} else {
|
|
88
|
+
for (const o of outdated) {
|
|
89
|
+
warn(`Outdated command: ${o} — run productkit update`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 5. Git initialized
|
|
94
|
+
try {
|
|
95
|
+
execSync('git rev-parse --git-dir', { cwd: root, stdio: 'ignore' });
|
|
96
|
+
pass('Git repository initialized');
|
|
97
|
+
} catch {
|
|
98
|
+
warn('No git repository — consider running git init');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Summary
|
|
102
|
+
console.log();
|
|
103
|
+
const parts = [];
|
|
104
|
+
if (results.pass > 0) parts.push(chalk.green(`${results.pass} passed`));
|
|
105
|
+
if (results.warn > 0) parts.push(chalk.yellow(`${results.warn} warning(s)`));
|
|
106
|
+
if (results.fail > 0) parts.push(chalk.red(`${results.fail} failed`));
|
|
107
|
+
console.log(chalk.bold('Result: ') + parts.join(', '));
|
|
108
|
+
console.log();
|
|
109
|
+
|
|
110
|
+
if (results.fail > 0) {
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = doctor;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const { getArtifactDir } = require('../utils/fileUtils');
|
|
5
|
+
|
|
6
|
+
const ARTIFACTS = [
|
|
7
|
+
{ file: 'constitution.md', label: 'Constitution' },
|
|
8
|
+
{ file: 'users.md', label: 'Users' },
|
|
9
|
+
{ file: 'problem.md', label: 'Problem' },
|
|
10
|
+
{ file: 'assumptions.md', label: 'Assumptions' },
|
|
11
|
+
{ file: 'solution.md', label: 'Solution' },
|
|
12
|
+
{ file: 'priorities.md', label: 'Priorities' },
|
|
13
|
+
{ file: 'spec.md', label: 'Spec' },
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
async function exportCommand(options) {
|
|
17
|
+
const root = process.cwd();
|
|
18
|
+
const configPath = path.join(root, '.productkit', 'config.json');
|
|
19
|
+
|
|
20
|
+
if (!fs.existsSync(configPath)) {
|
|
21
|
+
console.error(chalk.red('Not a Product Kit project.'));
|
|
22
|
+
console.log('Run: productkit init <name>');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const artifactDir = getArtifactDir(root);
|
|
27
|
+
const existing = ARTIFACTS.filter(a => fs.existsSync(path.join(artifactDir, a.file)));
|
|
28
|
+
|
|
29
|
+
if (existing.length === 0) {
|
|
30
|
+
console.error(chalk.red('No artifacts found. Run some slash commands first.'));
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const sections = [];
|
|
35
|
+
for (const artifact of existing) {
|
|
36
|
+
const content = fs.readFileSync(path.join(artifactDir, artifact.file), 'utf-8');
|
|
37
|
+
sections.push(content);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const projectName = path.basename(root);
|
|
41
|
+
const header = `# ${projectName} — Product Kit Export\n\n_Exported: ${new Date().toISOString().split('T')[0]}_\n\n---\n`;
|
|
42
|
+
const combined = header + sections.join('\n\n---\n\n') + '\n';
|
|
43
|
+
|
|
44
|
+
const outputFile = options.output || 'export.md';
|
|
45
|
+
fs.writeFileSync(path.join(root, outputFile), combined);
|
|
46
|
+
|
|
47
|
+
console.log(chalk.green.bold(`Exported ${existing.length} artifact(s) to ${outputFile}`));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = exportCommand;
|
package/src/commands/init.js
CHANGED
|
@@ -2,7 +2,7 @@ const fs = require('fs-extra');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
4
|
|
|
5
|
-
function scaffold(projectRoot, projectName) {
|
|
5
|
+
function scaffold(projectRoot, projectName, minimal, artifactDir) {
|
|
6
6
|
const templatesDir = path.join(__dirname, '..', '..', 'templates');
|
|
7
7
|
|
|
8
8
|
// Create directories
|
|
@@ -10,15 +10,24 @@ function scaffold(projectRoot, projectName) {
|
|
|
10
10
|
fs.ensureDirSync(path.join(projectRoot, '.claude', 'commands'));
|
|
11
11
|
|
|
12
12
|
// Write config
|
|
13
|
-
|
|
13
|
+
const config = {
|
|
14
14
|
version: '1.0.0',
|
|
15
15
|
created: new Date().toISOString(),
|
|
16
|
-
}
|
|
16
|
+
};
|
|
17
|
+
if (minimal) {
|
|
18
|
+
config.minimal = true;
|
|
19
|
+
}
|
|
20
|
+
if (artifactDir) {
|
|
21
|
+
config.artifact_dir = artifactDir;
|
|
22
|
+
fs.ensureDirSync(path.join(projectRoot, artifactDir));
|
|
23
|
+
}
|
|
24
|
+
fs.writeJsonSync(path.join(projectRoot, '.productkit', 'config.json'), config, { spaces: 2 });
|
|
17
25
|
|
|
18
26
|
// Copy slash command templates
|
|
19
27
|
const commandsDir = path.join(templatesDir, 'commands');
|
|
20
28
|
const commandFiles = fs.readdirSync(commandsDir);
|
|
21
29
|
for (const file of commandFiles) {
|
|
30
|
+
if (minimal && file === 'productkit.constitution.md') continue;
|
|
22
31
|
fs.copyFileSync(
|
|
23
32
|
path.join(commandsDir, file),
|
|
24
33
|
path.join(projectRoot, '.claude', 'commands', file)
|
|
@@ -61,13 +70,13 @@ async function init(projectName, options) {
|
|
|
61
70
|
}
|
|
62
71
|
|
|
63
72
|
try {
|
|
64
|
-
scaffold(projectRoot, path.basename(projectRoot));
|
|
73
|
+
scaffold(projectRoot, path.basename(projectRoot), options.minimal, options.artifactDir);
|
|
65
74
|
|
|
66
75
|
console.log(chalk.green.bold('Product Kit added to existing project!'));
|
|
67
76
|
console.log();
|
|
68
77
|
console.log(chalk.cyan('Next steps:'));
|
|
69
78
|
console.log(' 1. claude');
|
|
70
|
-
console.log(
|
|
79
|
+
console.log(` 2. /productkit.${options.minimal ? 'users' : 'constitution'}`);
|
|
71
80
|
console.log();
|
|
72
81
|
} catch (error) {
|
|
73
82
|
console.error(chalk.red('Error initializing:'), error.message);
|
|
@@ -89,7 +98,7 @@ async function init(projectName, options) {
|
|
|
89
98
|
}
|
|
90
99
|
|
|
91
100
|
try {
|
|
92
|
-
scaffold(projectRoot, projectName);
|
|
101
|
+
scaffold(projectRoot, projectName, options.minimal, options.artifactDir);
|
|
93
102
|
|
|
94
103
|
// Init git repo
|
|
95
104
|
const { execSync } = require('child_process');
|
|
@@ -104,7 +113,7 @@ async function init(projectName, options) {
|
|
|
104
113
|
console.log(chalk.cyan('Next steps:'));
|
|
105
114
|
console.log(` 1. cd ${projectName}`);
|
|
106
115
|
console.log(' 2. claude');
|
|
107
|
-
console.log(
|
|
116
|
+
console.log(` 3. /productkit.${options.minimal ? 'users' : 'constitution'}`);
|
|
108
117
|
console.log();
|
|
109
118
|
} catch (error) {
|
|
110
119
|
console.error(chalk.red('Error initializing project:'), error.message);
|
package/src/commands/reset.js
CHANGED
|
@@ -2,6 +2,7 @@ const fs = require('fs-extra');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
4
|
const readline = require('readline');
|
|
5
|
+
const { getArtifactDir } = require('../utils/fileUtils');
|
|
5
6
|
|
|
6
7
|
const ARTIFACTS = [
|
|
7
8
|
'constitution.md',
|
|
@@ -36,9 +37,11 @@ async function reset(options) {
|
|
|
36
37
|
process.exit(1);
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
const artifactDir = getArtifactDir(root);
|
|
41
|
+
|
|
39
42
|
// Find existing artifacts
|
|
40
43
|
const existing = ARTIFACTS.filter(file =>
|
|
41
|
-
fs.existsSync(path.join(
|
|
44
|
+
fs.existsSync(path.join(artifactDir, file))
|
|
42
45
|
);
|
|
43
46
|
|
|
44
47
|
if (existing.length === 0) {
|
|
@@ -67,7 +70,7 @@ async function reset(options) {
|
|
|
67
70
|
}
|
|
68
71
|
|
|
69
72
|
for (const file of existing) {
|
|
70
|
-
fs.removeSync(path.join(
|
|
73
|
+
fs.removeSync(path.join(artifactDir, file));
|
|
71
74
|
console.log(chalk.yellow(` removed ${file}`));
|
|
72
75
|
}
|
|
73
76
|
|
package/src/commands/status.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs-extra');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
|
+
const { getArtifactDir } = require('../utils/fileUtils');
|
|
4
5
|
|
|
5
6
|
const ARTIFACTS = [
|
|
6
7
|
{ file: 'constitution.md', command: '/productkit.constitution', label: 'Constitution' },
|
|
@@ -22,11 +23,12 @@ async function status() {
|
|
|
22
23
|
process.exit(1);
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
const artifactDir = getArtifactDir(root);
|
|
25
27
|
const done = [];
|
|
26
28
|
const remaining = [];
|
|
27
29
|
|
|
28
30
|
for (const artifact of ARTIFACTS) {
|
|
29
|
-
const exists = fs.existsSync(path.join(
|
|
31
|
+
const exists = fs.existsSync(path.join(artifactDir, artifact.file));
|
|
30
32
|
if (exists) {
|
|
31
33
|
done.push(artifact);
|
|
32
34
|
} else {
|
package/src/utils/fileUtils.js
CHANGED
|
@@ -12,7 +12,19 @@ function getProjectRoot() {
|
|
|
12
12
|
return null;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
function getArtifactDir(root) {
|
|
16
|
+
const configPath = path.join(root, '.productkit', 'config.json');
|
|
17
|
+
try {
|
|
18
|
+
const config = fs.readJsonSync(configPath);
|
|
19
|
+
if (config.artifact_dir) {
|
|
20
|
+
return path.join(root, config.artifact_dir);
|
|
21
|
+
}
|
|
22
|
+
} catch {}
|
|
23
|
+
return root;
|
|
24
|
+
}
|
|
25
|
+
|
|
15
26
|
module.exports = {
|
|
16
27
|
isProductKitProject,
|
|
17
28
|
getProjectRoot,
|
|
29
|
+
getArtifactDir,
|
|
18
30
|
};
|
package/templates/CLAUDE.md
CHANGED
|
@@ -15,10 +15,11 @@ Use these commands in order to build your product foundation:
|
|
|
15
15
|
7. `/productkit.spec` — Generate a product spec
|
|
16
16
|
8. `/productkit.clarify` — Resolve ambiguities across artifacts
|
|
17
17
|
9. `/productkit.analyze` — Run a completeness/consistency check
|
|
18
|
+
10. `/productkit.bootstrap` — Auto-draft all artifacts from an existing codebase
|
|
18
19
|
|
|
19
20
|
## Artifacts
|
|
20
21
|
|
|
21
|
-
Product artifacts are written
|
|
22
|
+
Product artifacts are written as markdown files. Check `.productkit/config.json` for an `artifact_dir` field — if set, artifacts live in that directory instead of the project root. Default artifact locations:
|
|
22
23
|
- `constitution.md` — Product principles and values
|
|
23
24
|
- `users.md` — Target user personas
|
|
24
25
|
- `problem.md` — Problem statement
|
|
@@ -30,3 +31,5 @@ Product artifacts are written to the project root as markdown files:
|
|
|
30
31
|
## Workflow
|
|
31
32
|
|
|
32
33
|
Start with `/productkit.constitution` or `/productkit.users`, then work through the commands in order. Each command reads previous artifacts to maintain consistency.
|
|
34
|
+
|
|
35
|
+
For existing projects, use `/productkit.bootstrap` to auto-draft all artifacts from your codebase in one session.
|
package/templates/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# {{PROJECT_NAME}}
|
|
2
2
|
|
|
3
|
-
A product research project powered by [Product Kit](https://github.com/
|
|
3
|
+
A product research project powered by [Product Kit](https://github.com/iamquechua/product-kit). See the [full guide](https://iamquechua.github.io/product-kit/) for a walkthrough.
|
|
4
4
|
|
|
5
5
|
## Getting Started
|
|
6
6
|
|
|
@@ -19,9 +19,12 @@ Then use the slash commands to build your product foundation:
|
|
|
19
19
|
7. `/productkit.spec` — Generate a product spec
|
|
20
20
|
8. `/productkit.clarify` — Resolve ambiguities
|
|
21
21
|
9. `/productkit.analyze` — Check consistency and completeness
|
|
22
|
+
10. `/productkit.bootstrap` — Auto-draft all artifacts from existing codebase
|
|
22
23
|
|
|
23
24
|
## Artifacts
|
|
24
25
|
|
|
26
|
+
Artifacts are written to the project root by default. If `artifact_dir` is set in `.productkit/config.json`, they are written there instead.
|
|
27
|
+
|
|
25
28
|
| File | Description |
|
|
26
29
|
|------|-------------|
|
|
27
30
|
| `constitution.md` | Product principles and values |
|
|
@@ -10,7 +10,9 @@ Evaluate the overall quality, consistency, and completeness of the product think
|
|
|
10
10
|
|
|
11
11
|
## Before You Start
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Check `.productkit/config.json` for an `artifact_dir` field. If set, read artifacts there instead of the project root. If not set, default to the project root.
|
|
14
|
+
|
|
15
|
+
Read all existing artifacts:
|
|
14
16
|
- `constitution.md`
|
|
15
17
|
- `users.md`
|
|
16
18
|
- `problem.md`
|
|
@@ -41,7 +41,9 @@ Also read if they exist:
|
|
|
41
41
|
|
|
42
42
|
## Output
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
Check `.productkit/config.json` for an `artifact_dir` field. If set, write artifacts there instead of the project root. If not set, default to the project root.
|
|
45
|
+
|
|
46
|
+
Write to `assumptions.md`:
|
|
45
47
|
|
|
46
48
|
```markdown
|
|
47
49
|
# Assumptions
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Auto-draft all product artifacts from an existing codebase
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
You are a product analyst bootstrapping Product Kit artifacts for an existing project. Your job is to read the codebase and draft each artifact so the user gets a fast start instead of building from scratch.
|
|
6
|
+
|
|
7
|
+
## Your Role
|
|
8
|
+
|
|
9
|
+
Analyze the existing project — code, docs, README, config files, comments — and draft product artifacts in workflow order. Present each draft for user approval before writing it.
|
|
10
|
+
|
|
11
|
+
## Before You Start
|
|
12
|
+
|
|
13
|
+
1. Read the project's README, CLAUDE.md, package.json (or equivalent), and scan the directory structure to understand what this project does.
|
|
14
|
+
2. Check `.productkit/config.json` for an `artifact_dir` field. If set, read and write artifacts there instead of the project root. If not set, default to the project root.
|
|
15
|
+
3. Check which artifacts already exist (constitution.md, users.md, problem.md, assumptions.md, solution.md, priorities.md, spec.md) in the artifact directory. **Skip any that already exist** — tell the user you're skipping them.
|
|
16
|
+
4. Check `.productkit/config.json` — if `minimal: true`, skip `constitution.md`.
|
|
17
|
+
|
|
18
|
+
## Process
|
|
19
|
+
|
|
20
|
+
Work through each missing artifact in this order:
|
|
21
|
+
|
|
22
|
+
### 1. Constitution (`constitution.md`)
|
|
23
|
+
Draft based on: README vision/mission, CLAUDE.md principles, project conventions.
|
|
24
|
+
- Product vision — infer from what the project does
|
|
25
|
+
- Core principles — infer from code patterns, docs, and design choices
|
|
26
|
+
- Non-negotiables — infer from what the project explicitly avoids
|
|
27
|
+
|
|
28
|
+
### 2. Users (`users.md`)
|
|
29
|
+
Draft based on: README audience, docs, issue tracker themes, CLI help text, UI copy.
|
|
30
|
+
- Identify 2-4 user types from project context
|
|
31
|
+
- Describe each with specifics inferred from the codebase
|
|
32
|
+
|
|
33
|
+
### 3. Problem (`problem.md`)
|
|
34
|
+
Draft based on: README "why", issue patterns, gaps the project fills.
|
|
35
|
+
- Frame the core problem the project solves
|
|
36
|
+
- Ground it in the users you just defined
|
|
37
|
+
|
|
38
|
+
### 4. Assumptions (`assumptions.md`)
|
|
39
|
+
Draft based on: implicit bets in the architecture, undocumented dependencies, target audience guesses.
|
|
40
|
+
- Surface 5-10 assumptions from code and docs
|
|
41
|
+
- Categorize by risk (high/medium/low)
|
|
42
|
+
|
|
43
|
+
### 5. Solution (`solution.md`)
|
|
44
|
+
Draft based on: the actual implementation, architecture choices, alternatives mentioned in docs/comments.
|
|
45
|
+
- Describe the chosen approach and why
|
|
46
|
+
- Note alternatives that were likely considered
|
|
47
|
+
|
|
48
|
+
### 6. Priorities (`priorities.md`)
|
|
49
|
+
Draft based on: feature completeness, TODO comments, open issues, roadmap docs.
|
|
50
|
+
- List features/capabilities by apparent priority
|
|
51
|
+
- Flag gaps between what exists and what's needed
|
|
52
|
+
|
|
53
|
+
### 7. Spec (`spec.md`)
|
|
54
|
+
Draft based on: all previous artifacts plus technical implementation details.
|
|
55
|
+
- Synthesize everything into a product spec
|
|
56
|
+
|
|
57
|
+
## For Each Artifact
|
|
58
|
+
|
|
59
|
+
1. **Show your draft** — present the full markdown content
|
|
60
|
+
2. **Explain your reasoning** — briefly note what codebase signals you used
|
|
61
|
+
3. **Ask for approval** — "Should I write this to `[filename]`? Or would you like to adjust anything?"
|
|
62
|
+
4. **On approval** — write the file to the artifact directory
|
|
63
|
+
5. **On feedback** — revise and re-present
|
|
64
|
+
|
|
65
|
+
## Conversation Style
|
|
66
|
+
|
|
67
|
+
- Be direct — present drafts quickly, don't over-ask before showing something
|
|
68
|
+
- Flag low-confidence sections with "[Needs input]" where the codebase doesn't give enough signal
|
|
69
|
+
- If the codebase has very little documentation, acknowledge gaps honestly and ask the user to fill in
|
|
70
|
+
- After each artifact, move to the next without prompting — keep momentum
|
|
71
|
+
|
|
72
|
+
## Output
|
|
73
|
+
|
|
74
|
+
Each artifact follows the same format as its corresponding slash command would produce. Refer to the individual command templates for the expected structure.
|
|
75
|
+
|
|
76
|
+
## When Done
|
|
77
|
+
|
|
78
|
+
After all artifacts are written (or skipped), summarize:
|
|
79
|
+
- Which artifacts were drafted and written
|
|
80
|
+
- Which were skipped (already existed)
|
|
81
|
+
- Suggest running `/productkit.clarify` to resolve any cross-artifact inconsistencies
|
|
@@ -10,7 +10,9 @@ Cross-reference all existing artifacts, find inconsistencies, and guide the user
|
|
|
10
10
|
|
|
11
11
|
## Before You Start
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Check `.productkit/config.json` for an `artifact_dir` field. If set, read and write artifacts there instead of the project root. If not set, default to the project root.
|
|
14
|
+
|
|
15
|
+
Read all existing artifacts:
|
|
14
16
|
- `constitution.md`
|
|
15
17
|
- `users.md`
|
|
16
18
|
- `problem.md`
|
|
@@ -25,7 +25,9 @@ Act as a seasoned PM mentor. Guide the user through defining their product's cor
|
|
|
25
25
|
|
|
26
26
|
## Output
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
Check `.productkit/config.json` for an `artifact_dir` field. If set, write artifacts there instead of the project root. If not set, default to the project root.
|
|
29
|
+
|
|
30
|
+
Write the final constitution to `constitution.md` with this format:
|
|
29
31
|
|
|
30
32
|
```markdown
|
|
31
33
|
# Product Constitution
|
|
@@ -42,7 +42,9 @@ If `solution.md` does not exist, tell the user to run `/productkit.solution` fir
|
|
|
42
42
|
|
|
43
43
|
## Output
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
Check `.productkit/config.json` for an `artifact_dir` field. If set, write artifacts there instead of the project root. If not set, default to the project root.
|
|
46
|
+
|
|
47
|
+
Write to `priorities.md`:
|
|
46
48
|
|
|
47
49
|
```markdown
|
|
48
50
|
# Feature Priorities
|
|
@@ -34,7 +34,9 @@ If `users.md` does not exist, tell the user to run `/productkit.users` first.
|
|
|
34
34
|
|
|
35
35
|
## Output
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
Check `.productkit/config.json` for an `artifact_dir` field. If set, write artifacts there instead of the project root. If not set, default to the project root.
|
|
38
|
+
|
|
39
|
+
Write the problem statement to `problem.md`:
|
|
38
40
|
|
|
39
41
|
```markdown
|
|
40
42
|
# Problem Statement
|
|
@@ -44,7 +44,9 @@ If `users.md` or `problem.md` do not exist, tell the user to run `/productkit.us
|
|
|
44
44
|
|
|
45
45
|
## Output
|
|
46
46
|
|
|
47
|
-
|
|
47
|
+
Check `.productkit/config.json` for an `artifact_dir` field. If set, write artifacts there instead of the project root. If not set, default to the project root.
|
|
48
|
+
|
|
49
|
+
Write to `solution.md`:
|
|
48
50
|
|
|
49
51
|
```markdown
|
|
50
52
|
# Solution
|
|
@@ -10,7 +10,9 @@ Pull together everything the user has built — constitution, users, problem, as
|
|
|
10
10
|
|
|
11
11
|
## Before You Start
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Check `.productkit/config.json` for an `artifact_dir` field. If set, read and write artifacts there instead of the project root. If not set, default to the project root.
|
|
14
|
+
|
|
15
|
+
Read all existing artifacts:
|
|
14
16
|
- `constitution.md` — product principles
|
|
15
17
|
- `users.md` — target users (required)
|
|
16
18
|
- `problem.md` — problem statement (required)
|
|
@@ -37,7 +39,7 @@ At minimum, `users.md`, `problem.md`, and `solution.md` must exist. If any are m
|
|
|
37
39
|
|
|
38
40
|
## Output
|
|
39
41
|
|
|
40
|
-
Write to `spec.md
|
|
42
|
+
Write to `spec.md`:
|
|
41
43
|
|
|
42
44
|
```markdown
|
|
43
45
|
# Product Spec: [Product Name]
|
|
@@ -33,7 +33,9 @@ Read `constitution.md` if it exists — use the product vision to inform user di
|
|
|
33
33
|
|
|
34
34
|
## Output
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
Check `.productkit/config.json` for an `artifact_dir` field. If set, write artifacts there instead of the project root. If not set, default to the project root.
|
|
37
|
+
|
|
38
|
+
Write the final personas to `users.md` with this format:
|
|
37
39
|
|
|
38
40
|
```markdown
|
|
39
41
|
# Target Users
|