dw-kit 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/hooks/post-write.sh +64 -58
- package/.claude/hooks/pre-commit-gate.sh +96 -90
- package/.claude/hooks/privacy-block.sh +99 -94
- package/.claude/hooks/progress-ping.sh +53 -47
- package/.claude/hooks/safety-guard.sh +60 -54
- package/.claude/hooks/scout-block.sh +88 -82
- package/.claude/hooks/session-init.sh +91 -74
- package/.claude/hooks/stop-check.sh +88 -36
- package/.claude/hooks/telemetry-log.sh +34 -0
- package/.claude/rules/code-style.md +37 -37
- package/.claude/rules/commit-standards.md +37 -37
- package/.claude/rules/dw.md +136 -0
- package/.claude/settings.json +120 -99
- package/.claude/skills/dw-arch-review/SKILL.md +119 -119
- package/.claude/skills/dw-archive/SKILL.md +81 -81
- package/.claude/skills/dw-commit/SKILL.md +81 -81
- package/.claude/skills/dw-config-init/SKILL.md +91 -91
- package/.claude/skills/dw-config-validate/SKILL.md +75 -75
- package/.claude/skills/dw-dashboard/SKILL.md +209 -209
- package/.claude/skills/dw-debug/SKILL.md +97 -97
- package/.claude/skills/dw-decision/SKILL.md +116 -0
- package/.claude/skills/dw-docs-update/SKILL.md +125 -125
- package/.claude/skills/dw-estimate/SKILL.md +90 -90
- package/.claude/skills/dw-execute/SKILL.md +98 -98
- package/.claude/skills/dw-flow/SKILL.md +274 -274
- package/.claude/skills/dw-handoff/SKILL.md +81 -81
- package/.claude/skills/dw-kit-report/SKILL.md +152 -152
- package/.claude/skills/dw-log-work/SKILL.md +69 -69
- package/.claude/skills/dw-onboard/SKILL.md +201 -201
- package/.claude/skills/dw-plan/SKILL.md +125 -125
- package/.claude/skills/dw-prompt/SKILL.md +62 -62
- package/.claude/skills/dw-requirements/SKILL.md +98 -98
- package/.claude/skills/dw-research/SKILL.md +114 -114
- package/.claude/skills/dw-retroactive/SKILL.md +311 -311
- package/.claude/skills/dw-review/SKILL.md +66 -66
- package/.claude/skills/dw-rollback/SKILL.md +90 -90
- package/.claude/skills/dw-sprint-review/SKILL.md +99 -99
- package/.claude/skills/dw-task-init/SKILL.md +59 -59
- package/.claude/skills/dw-test-plan/SKILL.md +113 -113
- package/.claude/skills/dw-thinking/SKILL.md +70 -70
- package/.claude/skills/dw-upgrade/SKILL.md +72 -72
- package/.dw/config/dw.config.yml +82 -82
- package/.dw/core/PILLARS.md +122 -0
- package/.dw/core/templates/v2/spec.md +68 -0
- package/.dw/core/templates/v2/tracking.md +62 -0
- package/.dw/core/v14-evaluation-protocol.md +118 -0
- package/CLAUDE.md +42 -39
- package/MIGRATION-v1.3.md +201 -0
- package/README.md +43 -6
- package/package.json +86 -84
- package/src/cli.mjs +45 -9
- package/src/commands/dashboard.mjs +116 -0
- package/src/commands/doctor.mjs +165 -149
- package/src/commands/init.mjs +339 -332
- package/src/commands/metrics.mjs +165 -0
- package/src/commands/upgrade.mjs +297 -262
- package/src/lib/active-index.mjs +87 -0
- package/src/lib/copy.mjs +118 -110
- package/src/lib/cut-analysis.mjs +161 -0
- package/src/lib/telemetry.mjs +80 -0
- package/.claude/rules/dw-core.md +0 -100
- package/.claude/rules/dw-skills.md +0 -53
- package/.claude/rules/workflow-rules.md +0 -77
package/src/commands/init.mjs
CHANGED
|
@@ -1,332 +1,339 @@
|
|
|
1
|
-
import { existsSync, readFileSync, appendFileSync, writeFileSync } from 'node:fs';
|
|
2
|
-
import { join, resolve } from 'node:path';
|
|
3
|
-
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { banner, ok, warn, info, log, ask, choose } from '../lib/ui.mjs';
|
|
5
|
-
import { buildConfig, writeConfig } from '../lib/config.mjs';
|
|
6
|
-
import { copyDir, copyFile, ensureDir } from '../lib/copy.mjs';
|
|
7
|
-
import { detectPlatform, platformLabel } from '../lib/platform.mjs';
|
|
8
|
-
|
|
9
|
-
const TOOLKIT_ROOT = resolve(fileURLToPath(import.meta.url), '..', '..', '..');
|
|
10
|
-
|
|
11
|
-
const PRESETS = {
|
|
12
|
-
'solo
|
|
13
|
-
'
|
|
14
|
-
'
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
roles =
|
|
68
|
-
|
|
69
|
-
log(`
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
{ value: '
|
|
99
|
-
{ value: '
|
|
100
|
-
], '
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
return
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function
|
|
116
|
-
if (
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
copyDir(join(TOOLKIT_ROOT, '.dw', '
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
##
|
|
216
|
-
|
|
217
|
-
<!--
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
-
|
|
245
|
-
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
-
|
|
252
|
-
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
console.log(
|
|
303
|
-
console.log();
|
|
304
|
-
console.log(
|
|
305
|
-
console.log(`
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
if (adapter === '
|
|
317
|
-
console.log(`
|
|
318
|
-
console.log(`
|
|
319
|
-
|
|
320
|
-
console.log(`
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
console.log(`
|
|
325
|
-
console.log(`
|
|
326
|
-
|
|
327
|
-
console.log(`
|
|
328
|
-
|
|
329
|
-
console.log(`
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
1
|
+
import { existsSync, readFileSync, appendFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { banner, ok, warn, info, log, ask, choose } from '../lib/ui.mjs';
|
|
5
|
+
import { buildConfig, writeConfig } from '../lib/config.mjs';
|
|
6
|
+
import { copyDir, copyFile, ensureDir } from '../lib/copy.mjs';
|
|
7
|
+
import { detectPlatform, platformLabel } from '../lib/platform.mjs';
|
|
8
|
+
|
|
9
|
+
const TOOLKIT_ROOT = resolve(fileURLToPath(import.meta.url), '..', '..', '..');
|
|
10
|
+
|
|
11
|
+
const PRESETS = {
|
|
12
|
+
'solo': { depth: 'quick', roles: ['dev'], tracking: false, hooksProfile: 'safety-only' },
|
|
13
|
+
'solo-quick': { depth: 'quick', roles: ['dev'], tracking: false },
|
|
14
|
+
'small-team': { depth: 'standard', roles: ['dev', 'techlead'], tracking: true },
|
|
15
|
+
'team': { depth: 'standard', roles: ['dev', 'techlead'], tracking: true, hooksProfile: 'full' },
|
|
16
|
+
'enterprise': { depth: 'thorough', roles: ['dev', 'techlead', 'ba', 'qc', 'pm'], tracking: true, hooksProfile: 'full' },
|
|
17
|
+
};
|
|
18
|
+
const VALID_DEPTHS = ['quick', 'standard', 'thorough'];
|
|
19
|
+
const VALID_LANGUAGES = ['vi', 'en'];
|
|
20
|
+
|
|
21
|
+
export async function initCommand(opts) {
|
|
22
|
+
const projectDir = process.cwd();
|
|
23
|
+
|
|
24
|
+
if (existsSync(join(projectDir, '.dw', 'config', 'dw.config.yml'))) {
|
|
25
|
+
warn('dw-kit is already initialized in this project (.dw/config/dw.config.yml exists).');
|
|
26
|
+
const answer = await ask('Reinitialize? This will overwrite config. (y/N)', 'N');
|
|
27
|
+
if (answer.toLowerCase() !== 'y') {
|
|
28
|
+
log('Aborted.');
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let projectName, depth, roles, language;
|
|
34
|
+
|
|
35
|
+
// --solo shortcut maps to --preset solo
|
|
36
|
+
if (opts.solo && !opts.preset) {
|
|
37
|
+
opts.preset = 'solo';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (opts.preset) {
|
|
41
|
+
const preset = PRESETS[opts.preset];
|
|
42
|
+
if (!preset) {
|
|
43
|
+
warn(`Unknown preset: ${opts.preset}. Available: ${Object.keys(PRESETS).join(', ')}`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
banner(`Setup Wizard v1.0 — preset: ${opts.preset}`);
|
|
47
|
+
projectName = process.env.DW_NAME || guessProjectName(projectDir);
|
|
48
|
+
depth = preset.depth;
|
|
49
|
+
roles = preset.roles;
|
|
50
|
+
language = process.env.DW_LANG || 'vi';
|
|
51
|
+
log(`Using preset: ${opts.preset}`);
|
|
52
|
+
log(` Project: ${projectName}, Depth: ${depth}, Roles: ${roles.join(', ')}, Lang: ${language}`);
|
|
53
|
+
} else if (opts.silent) {
|
|
54
|
+
banner('Setup Wizard v1.0 — silent mode');
|
|
55
|
+
projectName = process.env.DW_NAME || guessProjectName(projectDir);
|
|
56
|
+
depth = process.env.DW_DEPTH || 'standard';
|
|
57
|
+
const parsedRoles = parseRolesEnv(process.env.DW_ROLES || '');
|
|
58
|
+
language = process.env.DW_LANG || 'vi';
|
|
59
|
+
if (!VALID_DEPTHS.includes(depth)) {
|
|
60
|
+
warn(`Invalid DW_DEPTH="${depth}". Allowed: ${VALID_DEPTHS.join(', ')}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
if (!VALID_LANGUAGES.includes(language)) {
|
|
64
|
+
warn(`Invalid DW_LANG="${language}". Allowed: ${VALID_LANGUAGES.join(', ')}`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
roles = normalizeRolesForDepth(parsedRoles, depth);
|
|
68
|
+
log(`Silent mode — reading from environment variables`);
|
|
69
|
+
log(` Project: ${projectName}, Depth: ${depth}, Roles: ${roles.join(', ')}, Lang: ${language}`);
|
|
70
|
+
} else {
|
|
71
|
+
banner('Setup Wizard v1.0');
|
|
72
|
+
projectName = await askProjectName(projectDir);
|
|
73
|
+
depth = await askDepth();
|
|
74
|
+
roles = defaultRolesForDepth(depth);
|
|
75
|
+
language = await askLanguage();
|
|
76
|
+
log(` Roles auto-selected by depth: ${roles.join(', ')}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
console.log();
|
|
80
|
+
info('Detecting platform...');
|
|
81
|
+
const adapter = opts.adapter || detectPlatform(projectDir);
|
|
82
|
+
ok(`Platform: ${platformLabel(adapter)}`);
|
|
83
|
+
|
|
84
|
+
info('Setting up project...');
|
|
85
|
+
await setupProject(projectDir, { projectName, depth, roles, language, adapter });
|
|
86
|
+
|
|
87
|
+
printSummary({ projectName, depth, roles, language, adapter });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function askProjectName(projectDir) {
|
|
91
|
+
const guess = guessProjectName(projectDir);
|
|
92
|
+
return ask('[Project] Project name?', guess);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function askDepth() {
|
|
96
|
+
return choose('[Depth] Default workflow depth:', [
|
|
97
|
+
{ value: 'quick', label: 'Solo dev, hotfix, familiar code — minimal ceremony' },
|
|
98
|
+
{ value: 'standard', label: 'Team, feature — full 6-phase workflow' },
|
|
99
|
+
{ value: 'thorough', label: 'Enterprise — full workflow + arch-review + test-plan' },
|
|
100
|
+
], 'standard');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function askLanguage() {
|
|
104
|
+
return choose('[Lang] Documentation language:', [
|
|
105
|
+
{ value: 'vi', label: 'Tiếng Việt' },
|
|
106
|
+
{ value: 'en', label: 'English' },
|
|
107
|
+
], 'vi');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function guessProjectName(dir) {
|
|
111
|
+
const base = dir.split(/[/\\]/).pop();
|
|
112
|
+
return base || 'my-project';
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function parseRolesEnv(rolesStr) {
|
|
116
|
+
if (!rolesStr) return [];
|
|
117
|
+
const valid = ['dev', 'techlead', 'ba', 'qc', 'pm'];
|
|
118
|
+
const parsed = rolesStr.split(',').map(r => r.trim().toLowerCase()).filter(r => valid.includes(r));
|
|
119
|
+
return [...new Set(parsed)];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function defaultRolesForDepth(depth) {
|
|
123
|
+
if (depth === 'quick') return ['dev'];
|
|
124
|
+
if (depth === 'thorough') return ['dev', 'techlead', 'ba', 'qc', 'pm'];
|
|
125
|
+
return ['dev', 'techlead'];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function normalizeRolesForDepth(parsedRoles, depth) {
|
|
129
|
+
const required = defaultRolesForDepth(depth);
|
|
130
|
+
const merged = [...new Set([...parsedRoles, ...required])];
|
|
131
|
+
const missing = required.filter((r) => !parsedRoles.includes(r));
|
|
132
|
+
if (parsedRoles.length > 0 && missing.length > 0) {
|
|
133
|
+
warn(`DW_ROLES missing required roles for depth "${depth}". Auto-added: ${missing.join(', ')}`);
|
|
134
|
+
}
|
|
135
|
+
return merged;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function setupProject(projectDir, { projectName, depth, roles, language, adapter }) {
|
|
139
|
+
copyCoreDocs(projectDir);
|
|
140
|
+
copyConfig(projectDir, { projectName, depth, roles, language });
|
|
141
|
+
copyAdapterStructure(projectDir);
|
|
142
|
+
|
|
143
|
+
if (adapter === 'claude-cli') {
|
|
144
|
+
copyClaudeFiles(projectDir);
|
|
145
|
+
createMinimalCLAUDEmd(projectDir, projectName);
|
|
146
|
+
} else if (adapter === 'cursor') {
|
|
147
|
+
copyCursorFiles(projectDir);
|
|
148
|
+
copyGenericAdapter(projectDir);
|
|
149
|
+
} else if (adapter === 'generic') {
|
|
150
|
+
copyGenericAdapter(projectDir);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
createRuntimeDirs(projectDir);
|
|
154
|
+
updateGitignore(projectDir);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function copyCoreDocs(projectDir) {
|
|
158
|
+
const src = join(TOOLKIT_ROOT, '.dw', 'core');
|
|
159
|
+
const dst = join(projectDir, '.dw', 'core');
|
|
160
|
+
copyDir(src, dst, { overwrite: true });
|
|
161
|
+
ok('.dw/core/ (WORKFLOW.md, THINKING.md, QUALITY.md, ROLES.md, templates/)');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function copyConfig(projectDir, { projectName, depth, roles, language }) {
|
|
165
|
+
const configDir = join(projectDir, '.dw', 'config');
|
|
166
|
+
ensureDir(configDir);
|
|
167
|
+
|
|
168
|
+
const config = buildConfig({ projectName, language, depth, roles });
|
|
169
|
+
writeConfig(join(configDir, 'dw.config.yml'), config);
|
|
170
|
+
|
|
171
|
+
copyFile(
|
|
172
|
+
join(TOOLKIT_ROOT, '.dw', 'config', 'config.schema.json'),
|
|
173
|
+
join(configDir, 'config.schema.json'),
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
const presetsDir = join(configDir, 'presets');
|
|
177
|
+
ensureDir(presetsDir);
|
|
178
|
+
copyDir(join(TOOLKIT_ROOT, '.dw', 'config', 'presets'), presetsDir, { overwrite: true });
|
|
179
|
+
|
|
180
|
+
ok('.dw/config/dw.config.yml + schema + presets');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function copyAdapterStructure(projectDir) {
|
|
184
|
+
const adaptersDir = join(projectDir, '.dw', 'adapters');
|
|
185
|
+
copyDir(join(TOOLKIT_ROOT, '.dw', 'adapters'), adaptersDir);
|
|
186
|
+
ok('.dw/adapters/ (claude-cli, generic)');
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function copyClaudeFiles(projectDir) {
|
|
190
|
+
const src = join(TOOLKIT_ROOT, '.claude');
|
|
191
|
+
const dst = join(projectDir, '.claude');
|
|
192
|
+
const results = copyDir(src, dst, { overwrite: true });
|
|
193
|
+
|
|
194
|
+
const skipCount = results.filter(r => r.action === 'skip').length;
|
|
195
|
+
const copyCount = results.filter(r => r.action === 'copy').length;
|
|
196
|
+
|
|
197
|
+
ok(`.claude/ (${copyCount} files — skills, agents, hooks, rules, templates)`);
|
|
198
|
+
if (skipCount > 0) log(` ${skipCount} existing files preserved`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function createMinimalCLAUDEmd(projectDir, projectName) {
|
|
202
|
+
const dst = join(projectDir, 'CLAUDE.md');
|
|
203
|
+
|
|
204
|
+
if (existsSync(dst)) {
|
|
205
|
+
// User already has their own CLAUDE.md — leave it completely alone
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const content = `# ${projectName}
|
|
210
|
+
|
|
211
|
+
> Add your project description here.
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Tech Stack
|
|
216
|
+
|
|
217
|
+
<!-- Update with your project's actual stack -->
|
|
218
|
+
- Framework:
|
|
219
|
+
- Database:
|
|
220
|
+
- Testing:
|
|
221
|
+
|
|
222
|
+
## Project-Specific Rules
|
|
223
|
+
|
|
224
|
+
<!-- Add project-specific rules here -->
|
|
225
|
+
<!-- dw workflow rules are auto-loaded from .claude/rules/ -->
|
|
226
|
+
`;
|
|
227
|
+
writeFileSync(dst, content, 'utf-8');
|
|
228
|
+
ok('CLAUDE.md (minimal project template — dw rules are in .claude/rules/)');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function copyCursorFiles(projectDir) {
|
|
232
|
+
const rulesDir = join(projectDir, '.cursor', 'rules');
|
|
233
|
+
ensureDir(rulesDir);
|
|
234
|
+
|
|
235
|
+
const ruleContent = `# dw-kit Workflow Rules
|
|
236
|
+
# Auto-generated by dw init --adapter cursor
|
|
237
|
+
# Reference: .dw/core/WORKFLOW.md for full methodology
|
|
238
|
+
|
|
239
|
+
## Workflow
|
|
240
|
+
Follow the 6-phase workflow: Initialize → Understand → Plan → Execute → Verify → Close.
|
|
241
|
+
Read @.dw/core/WORKFLOW.md for detailed phase instructions.
|
|
242
|
+
|
|
243
|
+
## Quality
|
|
244
|
+
- Apply TDD: write test → implement → refactor
|
|
245
|
+
- Read @.dw/core/QUALITY.md for the 4-layer quality strategy
|
|
246
|
+
- Run test/lint commands from .dw/config/dw.config.yml before committing
|
|
247
|
+
|
|
248
|
+
## Thinking
|
|
249
|
+
When planning or debugging, apply the thinking framework from @.dw/core/THINKING.md:
|
|
250
|
+
- Critical Thinking: question assumptions
|
|
251
|
+
- Systems Thinking: trace dependencies and side effects
|
|
252
|
+
- First Principles: break down to fundamentals
|
|
253
|
+
|
|
254
|
+
## Roles
|
|
255
|
+
Check @.dw/core/ROLES.md for role definitions and decision authority.
|
|
256
|
+
|
|
257
|
+
## Task Tracking
|
|
258
|
+
- Task docs go in .dw/tasks/[task-name]/
|
|
259
|
+
- Use templates from .dw/core/templates/ for context, plan, and progress docs
|
|
260
|
+
`;
|
|
261
|
+
|
|
262
|
+
writeFileSync(join(rulesDir, 'dw-workflow.mdc'), ruleContent, 'utf-8');
|
|
263
|
+
ok('.cursor/rules/dw-workflow.mdc (Cursor rules from core methodology)');
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function copyGenericAdapter(projectDir) {
|
|
267
|
+
const src = join(TOOLKIT_ROOT, '.dw', 'adapters', 'generic', 'AGENT.md');
|
|
268
|
+
const dst = join(projectDir, 'AGENT.md');
|
|
269
|
+
if (!existsSync(dst)) {
|
|
270
|
+
copyFile(src, dst);
|
|
271
|
+
ok('AGENT.md (generic adapter for Cursor/Windsurf/Copilot)');
|
|
272
|
+
} else {
|
|
273
|
+
warn('AGENT.md already exists — skipping');
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function createRuntimeDirs(projectDir) {
|
|
278
|
+
for (const dir of ['.dw/tasks', '.dw/docs', '.dw/metrics', '.dw/reports']) {
|
|
279
|
+
ensureDir(join(projectDir, dir));
|
|
280
|
+
}
|
|
281
|
+
ok('.dw/ (tasks, docs, metrics, reports)');
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function updateGitignore(projectDir) {
|
|
285
|
+
const gitignorePath = join(projectDir, '.gitignore');
|
|
286
|
+
const entriesToAdd = ['CLAUDE.local.md', '.claude/settings.local.json', '.dw/config/dw.config.local.yml'];
|
|
287
|
+
|
|
288
|
+
if (existsSync(gitignorePath)) {
|
|
289
|
+
const content = readFileSync(gitignorePath, 'utf-8');
|
|
290
|
+
const missing = entriesToAdd.filter(e => !content.includes(e));
|
|
291
|
+
if (missing.length > 0) {
|
|
292
|
+
appendFileSync(gitignorePath, `\n# dw-kit\n${missing.join('\n')}\n`, 'utf-8');
|
|
293
|
+
ok('.gitignore updated');
|
|
294
|
+
}
|
|
295
|
+
} else {
|
|
296
|
+
writeFileSync(gitignorePath, `# dw-kit\n${entriesToAdd.join('\n')}\n`, 'utf-8');
|
|
297
|
+
ok('.gitignore created');
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function printSummary({ projectName, depth, roles, language, adapter }) {
|
|
302
|
+
console.log();
|
|
303
|
+
console.log(` ✅ Setup complete!`);
|
|
304
|
+
console.log();
|
|
305
|
+
console.log(` Project : ${projectName}`);
|
|
306
|
+
console.log(` Depth : ${depth}`);
|
|
307
|
+
console.log(` Roles : ${roles.join(', ')}`);
|
|
308
|
+
console.log(` Language : ${language}`);
|
|
309
|
+
console.log(` Platform : ${platformLabel(adapter)}`);
|
|
310
|
+
console.log();
|
|
311
|
+
console.log(` Files created:`);
|
|
312
|
+
console.log(` .dw/ — core/, config/, adapters/, tasks, docs`);
|
|
313
|
+
if (adapter === 'claude-cli') {
|
|
314
|
+
console.log(` .claude/ — skills, agents, hooks, rules/`);
|
|
315
|
+
console.log(` CLAUDE.md — minimal project template (dw rules in .claude/rules/)`);
|
|
316
|
+
} else if (adapter === 'cursor') {
|
|
317
|
+
console.log(` .cursor/rules/ — workflow rules for Cursor`);
|
|
318
|
+
console.log(` AGENT.md — methodology reference`);
|
|
319
|
+
} else {
|
|
320
|
+
console.log(` AGENT.md — methodology reference`);
|
|
321
|
+
}
|
|
322
|
+
console.log();
|
|
323
|
+
if (adapter === 'claude-cli') {
|
|
324
|
+
console.log(` Next steps:`);
|
|
325
|
+
console.log(` Run: claude (to open Claude Code in this directory in terminal)`);
|
|
326
|
+
console.log(` Run: /dw:flow [task-name]`);
|
|
327
|
+
console.log(` Suggested: Update Tech Stack + rules in CLAUDE.md`);
|
|
328
|
+
} else if (adapter === 'cursor') {
|
|
329
|
+
console.log(` Next steps:`);
|
|
330
|
+
console.log(` 1. Open Cursor in this directory`);
|
|
331
|
+
console.log(` 2. Rules auto-loaded from .cursor/rules/`);
|
|
332
|
+
console.log(` 3. Reference AGENT.md + .dw/core/WORKFLOW.md for guidance`);
|
|
333
|
+
} else {
|
|
334
|
+
console.log(` Next steps:`);
|
|
335
|
+
console.log(` 1. Open your AI coding tool in this directory`);
|
|
336
|
+
console.log(` 2. Reference AGENT.md for workflow guidance`);
|
|
337
|
+
}
|
|
338
|
+
console.log();
|
|
339
|
+
}
|