@dzhechkov/p-replicator 1.2.0 → 1.5.2

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 (46) hide show
  1. package/README.md +55 -0
  2. package/bin/cli.js +0 -0
  3. package/package.json +10 -3
  4. package/src/cli.js +23 -6
  5. package/src/commands/doctor.js +46 -29
  6. package/src/commands/init.js +61 -8
  7. package/src/commands/list.js +5 -26
  8. package/src/commands/update.js +73 -7
  9. package/src/commands/verify.js +111 -0
  10. package/src/utils.js +275 -4
  11. package/templates/.claude/commands/deploy.md +100 -0
  12. package/templates/.claude/commands/docs.md +79 -0
  13. package/templates/.claude/commands/feature.md +134 -0
  14. package/templates/.claude/commands/go.md +115 -0
  15. package/templates/.claude/commands/myinsights.md +72 -0
  16. package/templates/.claude/commands/next.md +110 -0
  17. package/templates/.claude/commands/plan.md +88 -0
  18. package/templates/.claude/commands/replicate.md +103 -17
  19. package/templates/.claude/commands/run.md +151 -0
  20. package/templates/.claude/commands/start.md +88 -0
  21. package/templates/.claude/hooks/autocommit-insights.cjs +39 -0
  22. package/templates/.claude/hooks/autocommit-plans.cjs +39 -0
  23. package/templates/.claude/hooks/autocommit-roadmap.cjs +44 -0
  24. package/templates/.claude/hooks/session-insights.cjs +28 -0
  25. package/templates/.claude/hooks/state-update.cjs +79 -0
  26. package/templates/.claude/hooks/statusline.cjs +399 -0
  27. package/templates/.claude/rules/feature-lifecycle.md +145 -0
  28. package/templates/.claude/rules/git-workflow.md +74 -0
  29. package/templates/.claude/rules/insights-capture.md +77 -0
  30. package/templates/.claude/rules/replicate-pipeline.md +97 -20
  31. package/templates/.claude/settings.json +44 -0
  32. package/templates/.claude/skills/brutal-honesty-review/SKILL.md +43 -0
  33. package/templates/.claude/skills/brutal-honesty-review/scripts/assess-code.sh +0 -0
  34. package/templates/.claude/skills/brutal-honesty-review/scripts/assess-tests.sh +0 -0
  35. package/templates/.claude/skills/cc-toolkit-generator-enhanced/SKILL.md +8 -0
  36. package/templates/.claude/skills/cc-toolkit-generator-enhanced/modules/03-generate-p0.md +46 -0
  37. package/templates/.claude/skills/cc-toolkit-generator-enhanced/modules/04-generate-p1.md +9 -0
  38. package/templates/.claude/skills/cc-toolkit-generator-enhanced/modules/05-generate-p2p3.md +11 -0
  39. package/templates/.claude/skills/cc-toolkit-generator-enhanced/modules/06-package-deliver.md +46 -0
  40. package/templates/.claude/skills/cc-toolkit-generator-enhanced/references/security-patterns-library.md +196 -0
  41. package/templates/.claude/skills/cc-toolkit-generator-enhanced/references/templates/automation-commands.md +15 -0
  42. package/templates/.claude/skills/cc-toolkit-generator-enhanced/references/templates/ddd-agents.md +39 -0
  43. package/templates/.claude/skills/cc-toolkit-generator-enhanced/references/templates/feature-lifecycle.md +8 -0
  44. package/templates/.claude/skills/goap-research-ed25519/scripts/ed25519_verifier.py +0 -0
  45. package/templates/.claude/skills/goap-research-ed25519/scripts/goap_planner.py +0 -0
  46. package/templates/.claude/skills/requirements-validator/SKILL.md +25 -1
@@ -0,0 +1,399 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * P-Replicator statusline (v1.5.0)
6
+ *
7
+ * Invoked by Claude Code via settings.json `statusLine` config. Output to
8
+ * stdout becomes the status bar above the prompt. Multi-line + ANSI colors.
9
+ *
10
+ * Sources: filesystem heuristics + optional state-file (.claude/.p-replicator-state.json)
11
+ * written by /run, /feature, /replicate via .claude/hooks/state-update.cjs.
12
+ *
13
+ * Defensive: every section is wrapped in try/catch with sensible fallback,
14
+ * so a parse error in any single source never breaks the whole status bar.
15
+ */
16
+
17
+ const fs = require('node:fs');
18
+ const path = require('node:path');
19
+
20
+ const CWD = process.cwd();
21
+ const NOW = Date.now();
22
+
23
+ // ─── ANSI helpers ─────────────────────────────────────────────────────────
24
+ const C = {
25
+ reset: '\x1b[0m',
26
+ bold: '\x1b[1m',
27
+ dim: '\x1b[2m',
28
+ red: '\x1b[31m',
29
+ green: '\x1b[32m',
30
+ yellow: '\x1b[33m',
31
+ blue: '\x1b[34m',
32
+ magenta:'\x1b[35m',
33
+ cyan: '\x1b[36m',
34
+ gray: '\x1b[90m',
35
+ };
36
+ function color(c, s) { return c + s + C.reset; }
37
+ function bold(s) { return C.bold + s + C.reset; }
38
+ function dim(s) { return C.dim + s + C.reset; }
39
+ function green(s) { return C.green + s + C.reset; }
40
+ function yellow(s){ return C.yellow + s + C.reset; }
41
+ function red(s) { return C.red + s + C.reset; }
42
+ function cyan(s) { return C.cyan + s + C.reset; }
43
+ function gray(s) { return C.gray + s + C.reset; }
44
+
45
+ // ─── safe wrappers ────────────────────────────────────────────────────────
46
+ function safeRun(fn, fallback) {
47
+ try { return fn(); } catch { return fallback; }
48
+ }
49
+ function safeReadJson(p) {
50
+ try { return JSON.parse(fs.readFileSync(p, 'utf8')); } catch { return null; }
51
+ }
52
+ function safeReadText(p) {
53
+ try { return fs.readFileSync(p, 'utf8'); } catch { return null; }
54
+ }
55
+ function safeListDir(p) {
56
+ try { return fs.readdirSync(p); } catch { return []; }
57
+ }
58
+ function exists(p) {
59
+ try { fs.accessSync(p); return true; } catch { return false; }
60
+ }
61
+
62
+ // ─── parsers (heuristic) ──────────────────────────────────────────────────
63
+
64
+ function parseManifest() {
65
+ return safeReadJson(path.join(CWD, '.p-replicator.json'));
66
+ }
67
+
68
+ function parseState() {
69
+ const p = path.join(CWD, '.claude', '.p-replicator-state.json');
70
+ const state = safeReadJson(p);
71
+ if (!state) return null;
72
+ // Stale check: state older than 30 min — treat as not-running
73
+ if (state.updatedAt) {
74
+ const age = NOW - new Date(state.updatedAt).getTime();
75
+ if (age > 30 * 60 * 1000) return null;
76
+ }
77
+ return state;
78
+ }
79
+
80
+ function parseRoadmap() {
81
+ const r = safeReadJson(path.join(CWD, '.claude', 'feature-roadmap.json'));
82
+ if (!r || !Array.isArray(r.features)) return null;
83
+ const features = r.features;
84
+ const total = features.length;
85
+ const done = features.filter((f) => f.status === 'done').length;
86
+ const inProgress = features.find((f) => f.status === 'in_progress');
87
+ const blocked = features.filter((f) => f.status === 'blocked').length;
88
+ const mvp = features.filter((f) => f.priority === 'mvp');
89
+ const mvpDone = mvp.filter((f) => f.status === 'done').length;
90
+ return { total, done, inProgress, blocked, mvpTotal: mvp.length, mvpDone };
91
+ }
92
+
93
+ function parseSparcDocs() {
94
+ const docsDir = path.join(CWD, 'docs');
95
+ const expectedSparc = [
96
+ 'PRD.md', 'Solution_Strategy.md', 'Specification.md', 'Pseudocode.md',
97
+ 'Architecture.md', 'Refinement.md', 'Completion.md',
98
+ 'Research_Findings.md', 'Final_Summary.md', 'C4_Diagrams.md', 'ADR.md',
99
+ ];
100
+ const present = expectedSparc.filter((f) => exists(path.join(docsDir, f)));
101
+ return { present: present.length, total: expectedSparc.length };
102
+ }
103
+
104
+ function parseValidationScore() {
105
+ const p = path.join(CWD, 'docs', 'validation-report.md');
106
+ const text = safeReadText(p);
107
+ if (!text) return null;
108
+ // Look for "Average Score: XX/100" or "Score: XX" patterns
109
+ const m = text.match(/(?:average\s+)?score[:\s]+(\d{1,3})(?:\s*\/\s*100)?/i);
110
+ if (!m) return null;
111
+ const score = parseInt(m[1], 10);
112
+ let verdict;
113
+ if (score >= 70) verdict = 'READY';
114
+ else if (score >= 50) verdict = 'CAVEATS';
115
+ else verdict = 'NEEDS_WORK';
116
+ return { score, verdict };
117
+ }
118
+
119
+ function parseAdrs() {
120
+ // Prefer docs/ADR.md (single file with ## ADR-N headings)
121
+ const single = path.join(CWD, 'docs', 'ADR.md');
122
+ if (exists(single)) {
123
+ const text = safeReadText(single) || '';
124
+ const headings = text.match(/^#{2,3}\s+ADR/gm) || [];
125
+ if (headings.length > 0) return headings.length;
126
+ }
127
+ // Or docs/adr/*.md (per-ADR files)
128
+ const dir = path.join(CWD, 'docs', 'adr');
129
+ const files = safeListDir(dir).filter((f) => f.endsWith('.md'));
130
+ if (files.length > 0) return files.length;
131
+ // Or docs/ddd/adr/*.md (DDD pipeline)
132
+ const dddDir = path.join(CWD, 'docs', 'ddd', 'adr');
133
+ const dddFiles = safeListDir(dddDir).filter((f) => f.endsWith('.md'));
134
+ return dddFiles.length;
135
+ }
136
+
137
+ function parsePlans() {
138
+ const dir = path.join(CWD, 'docs', 'plans');
139
+ return safeListDir(dir).filter((f) => f.endsWith('.md')).length;
140
+ }
141
+
142
+ function parseInsights() {
143
+ const p = path.join(CWD, '.claude', 'insights', 'index.md');
144
+ const text = safeReadText(p);
145
+ if (!text) return { count: 0, lastDate: null };
146
+ const headings = text.match(/^##\s+\d{4}-\d{2}-\d{2}/gm) || [];
147
+ // Last date: extract from last heading
148
+ let lastDate = null;
149
+ if (headings.length > 0) {
150
+ const last = headings[headings.length - 1];
151
+ const m = last.match(/\d{4}-\d{2}-\d{2}/);
152
+ if (m) lastDate = m[0];
153
+ }
154
+ return { count: headings.length, lastDate };
155
+ }
156
+
157
+ function parseToolkit() {
158
+ const dir = path.join(CWD, '.claude');
159
+ const skills = safeListDir(path.join(dir, 'skills')).filter((d) => {
160
+ return safeRun(() => fs.statSync(path.join(dir, 'skills', d)).isDirectory(), false);
161
+ }).length;
162
+ const commands = safeListDir(path.join(dir, 'commands'))
163
+ .filter((f) => f.endsWith('.md')).length;
164
+ const agents = safeListDir(path.join(dir, 'agents'))
165
+ .filter((f) => f.endsWith('.md')).length;
166
+ const rules = safeListDir(path.join(dir, 'rules'))
167
+ .filter((f) => f.endsWith('.md')).length;
168
+ const hooks = safeListDir(path.join(dir, 'hooks'))
169
+ .filter((f) => f.endsWith('.cjs')).length;
170
+ return { skills, commands, agents, rules, hooks };
171
+ }
172
+
173
+ function parseExpectedToolkit() {
174
+ // From manifest's components if shippedDefaults available; otherwise fall back to baseline
175
+ return {
176
+ skillsExpected: 10,
177
+ commandsExpected: 11,
178
+ agentsExpected: 4, // pre-shipped only (project agents are extra)
179
+ rulesExpected: 5, // pre-shipped only (project rules are extra)
180
+ hooksExpected: 6, // 4 v1.4.1 hooks + statusline + state-update (v1.5.0)
181
+ };
182
+ }
183
+
184
+ function parseSettingsStatus(manifest) {
185
+ const settingsPath = path.join(CWD, '.claude', 'settings.json');
186
+ if (!exists(settingsPath)) return 'missing';
187
+ const cur = safeReadJson(settingsPath);
188
+ if (!cur) return 'corrupt';
189
+ const shipped = manifest && manifest.shippedDefaults && manifest.shippedDefaults['settings.json'];
190
+ if (!shipped) return 'unknown';
191
+ // If exact match: defaults; else: merged (user customized)
192
+ try {
193
+ const sortKeys = (o) => JSON.stringify(o, Object.keys(o).sort());
194
+ return sortKeys(cur) === sortKeys(shipped) ? 'defaults' : 'merged';
195
+ } catch {
196
+ return 'unknown';
197
+ }
198
+ }
199
+
200
+ function parseMcpServers() {
201
+ const mcpJson = safeReadJson(path.join(CWD, '.mcp.json'));
202
+ if (!mcpJson) return null;
203
+ const servers = mcpJson.mcpServers || mcpJson.servers || {};
204
+ return Object.keys(servers).length;
205
+ }
206
+
207
+ function parseKeysarium() {
208
+ return exists(path.join(CWD, '.keysarium.json'));
209
+ }
210
+
211
+ function parseDomain() {
212
+ const claudeMd = safeReadText(path.join(CWD, 'CLAUDE.md'));
213
+ if (!claudeMd) return null;
214
+ // Heuristic keyword search
215
+ const banking = /банк|bank|финт|fintech|gigachat|yandexgpt|ФЗ-152|ЦБ|ФСТЭК/i;
216
+ const retail = /retail|e-commerce|ecommerce|ритейл|рекоменд|conversion/i;
217
+ const enterprise = /enterprise|b2b|legacy|sla|change\s*management/i;
218
+ const healthcare = /health|medical|клиник|hipaa|ФЗ-323/i;
219
+ if (banking.test(claudeMd)) return 'banking';
220
+ if (retail.test(claudeMd)) return 'retail';
221
+ if (enterprise.test(claudeMd)) return 'enterprise';
222
+ if (healthcare.test(claudeMd)) return 'healthcare';
223
+ return null;
224
+ }
225
+
226
+ function parseLastHarvest() {
227
+ // Heuristic: TOOLKIT_HARVEST.md mtime
228
+ const p = path.join(CWD, 'TOOLKIT_HARVEST.md');
229
+ try {
230
+ const stat = fs.statSync(p);
231
+ return stat.mtime.toISOString().slice(0, 10);
232
+ } catch { return null; }
233
+ }
234
+
235
+ function parseLastTest() {
236
+ // Optional cache file written by users
237
+ const p = path.join(CWD, '.claude', '.last-test.json');
238
+ return safeReadJson(p);
239
+ }
240
+
241
+ // ─── progress bar ─────────────────────────────────────────────────────────
242
+ function bar(progress, width = 8) {
243
+ const fill = Math.round(progress * width);
244
+ const empty = width - fill;
245
+ return '▓'.repeat(fill) + '░'.repeat(empty);
246
+ }
247
+ function dotBar(done, total, width = 8) {
248
+ const w = Math.min(width, Math.max(total, 1));
249
+ const fill = Math.round((done / Math.max(total, 1)) * w);
250
+ return '['+ '●'.repeat(fill) + '○'.repeat(w - fill) + ']';
251
+ }
252
+
253
+ // ─── line builders ───────────────────────────────────────────────────────
254
+ function buildHeader(manifest) {
255
+ const ver = (manifest && manifest.version) || '?';
256
+ const user = process.env.USER || process.env.USERNAME || 'user';
257
+ const model = process.env.CLAUDE_MODEL || process.env.MODEL || 'Claude';
258
+ return `${cyan(bold('P-Replicator'))} ${dim('V' + ver)} ${green('●')} ${user} ${dim('│')} ${model}`;
259
+ }
260
+
261
+ function buildPipeline(state) {
262
+ const parts = ['🚀 ' + bold('Pipeline')];
263
+ if (state && state.currentCommand) {
264
+ const cmd = state.currentCommand;
265
+ const phase = state.currentPhase;
266
+ if (phase) {
267
+ const progress = typeof phase.progress === 'number' ? phase.progress : 0;
268
+ const idx = phase.index ?? '?';
269
+ const total = phase.total ?? '?';
270
+ parts.push(`${cyan(cmd)} ${bar(progress)} ${Math.round(progress*100)}%`);
271
+ parts.push(`${dim('Phase:')} ${phase.name || ''} (${idx}/${total})`);
272
+ } else {
273
+ parts.push(cyan(cmd));
274
+ }
275
+ if (state.lastCommand && state.lastCommand !== cmd) {
276
+ parts.push(`${dim('Last:')} ${state.lastCommand}`);
277
+ }
278
+ } else {
279
+ parts.push(dim('idle'));
280
+ }
281
+ return parts.join(' ' + dim('│') + ' ');
282
+ }
283
+
284
+ function buildRoadmap(roadmap, domain) {
285
+ if (!roadmap) {
286
+ return `🎯 ${bold('Roadmap')} ${dim('— no roadmap yet (run /next or /replicate)')}`;
287
+ }
288
+ const { total, done, inProgress, blocked, mvpTotal, mvpDone } = roadmap;
289
+ const dotbar = dotBar(done, total, 8);
290
+ const parts = [
291
+ `🎯 ${bold('Roadmap')}`,
292
+ `${dotbar} mvp ${green(mvpDone + '/' + mvpTotal)}`,
293
+ `${dim('Done')} ${green(done + '/' + total)}`,
294
+ ];
295
+ if (inProgress) {
296
+ parts.push(`${dim('▶')} ${cyan(inProgress.id)}`);
297
+ }
298
+ if (blocked > 0) {
299
+ parts.push(`${red('Blocked')} ${blocked}`);
300
+ }
301
+ if (domain) {
302
+ parts.push(`${dim('Domain:')} ${cyan(domain)}`);
303
+ }
304
+ return parts.join(' ' + dim('│') + ' ');
305
+ }
306
+
307
+ function buildDocs(sparc, validation, plans, adrs, lastHarvest) {
308
+ const parts = [`📊 ${bold('SPARC')} ${green('●' + sparc.present + '/' + sparc.total)}`];
309
+ if (validation) {
310
+ const verdictIcon = validation.verdict === 'READY' ? '🟢'
311
+ : validation.verdict === 'CAVEATS' ? '🟡' : '🔴';
312
+ parts.push(`${verdictIcon} ${validation.score}/100`);
313
+ }
314
+ parts.push(`${dim('Plans')} ${plans > 0 ? green('●' + plans) : '0'}`);
315
+ parts.push(`${dim('ADRs')} ${adrs > 0 ? green('●' + adrs) : '0'}`);
316
+ if (lastHarvest) {
317
+ parts.push(`${dim('Harvest')} ${lastHarvest}`);
318
+ }
319
+ return parts.join(' ' + dim('│') + ' ');
320
+ }
321
+
322
+ function buildToolkit(toolkit, expected) {
323
+ const dotIf = (v, e) => v >= e ? green('●' + v + '/' + e) : yellow('●' + v + '/' + e);
324
+ const extraAgents = Math.max(0, toolkit.agents - expected.agentsExpected);
325
+ const extraRules = Math.max(0, toolkit.rules - expected.rulesExpected);
326
+ const agentsLbl = toolkit.agents >= expected.agentsExpected
327
+ ? green('●' + expected.agentsExpected + (extraAgents > 0 ? '+' + extraAgents : ''))
328
+ : yellow('●' + toolkit.agents + '/' + expected.agentsExpected);
329
+ const rulesLbl = toolkit.rules >= expected.rulesExpected
330
+ ? green('●' + expected.rulesExpected + (extraRules > 0 ? '+' + extraRules : ''))
331
+ : yellow('●' + toolkit.rules + '/' + expected.rulesExpected);
332
+ return [
333
+ `🛠️ ${bold('Toolkit')}`,
334
+ `Skills ${dotIf(toolkit.skills, expected.skillsExpected)}`,
335
+ `Cmds ${dotIf(toolkit.commands, expected.commandsExpected)}`,
336
+ `Agents ${agentsLbl}`,
337
+ `Rules ${rulesLbl}`,
338
+ `Hooks ${dotIf(toolkit.hooks, expected.hooksExpected)}`,
339
+ ].join(' ' + dim('│') + ' ');
340
+ }
341
+
342
+ function buildStatus(insights, lastTest, mcpServers, settingsStatus, keysarium) {
343
+ const parts = [];
344
+ parts.push(`💡 ${bold('Insights')} ${insights.count > 0 ? green('●' + insights.count) : '0'}` +
345
+ (insights.lastDate ? ` ${dim('(' + insights.lastDate + ')')}` : ''));
346
+
347
+ if (lastTest && typeof lastTest.passed === 'number') {
348
+ const total = lastTest.total ?? lastTest.passed;
349
+ const ok = lastTest.passed === total;
350
+ parts.push(`${ok ? '✅' : '❌'} ${dim('Tests')} ${ok ? green(lastTest.passed + '/' + total) : red(lastTest.passed + '/' + total)}`);
351
+ }
352
+ if (mcpServers !== null && mcpServers !== undefined) {
353
+ parts.push(`🔌 ${dim('MCP')} ${mcpServers > 0 ? green('●' + mcpServers) : '0'}`);
354
+ }
355
+ if (settingsStatus) {
356
+ const map = { defaults: '✓ defaults', merged: '⚠️ merged', corrupt: red('🔴 corrupt'), missing: red('missing'), unknown: '?' };
357
+ parts.push(`⚙️ ${dim('Settings')} ${map[settingsStatus] || settingsStatus}`);
358
+ }
359
+ if (keysarium) {
360
+ parts.push(`🧬 ${green('Keysarium ✓')}`);
361
+ }
362
+ return parts.join(' ' + dim('│') + ' ');
363
+ }
364
+
365
+ // ─── main ─────────────────────────────────────────────────────────────────
366
+ function main() {
367
+ const manifest = safeRun(() => parseManifest(), null);
368
+ const state = safeRun(() => parseState(), null);
369
+ const roadmap = safeRun(() => parseRoadmap(), null);
370
+ const sparc = safeRun(() => parseSparcDocs(), { present: 0, total: 11 });
371
+ const validation = safeRun(() => parseValidationScore(), null);
372
+ const adrs = safeRun(() => parseAdrs(), 0);
373
+ const plans = safeRun(() => parsePlans(), 0);
374
+ const insights = safeRun(() => parseInsights(), { count: 0, lastDate: null });
375
+ const toolkit = safeRun(() => parseToolkit(), { skills: 0, commands: 0, agents: 0, rules: 0, hooks: 0 });
376
+ const expected = parseExpectedToolkit();
377
+ const settingsStatus = safeRun(() => parseSettingsStatus(manifest), null);
378
+ const mcpServers = safeRun(() => parseMcpServers(), null);
379
+ const keysarium = safeRun(() => parseKeysarium(), false);
380
+ const domain = safeRun(() => parseDomain(), null);
381
+ const lastHarvest = safeRun(() => parseLastHarvest(), null);
382
+ const lastTest = safeRun(() => parseLastTest(), null);
383
+
384
+ const lines = [
385
+ buildHeader(manifest),
386
+ buildPipeline(state),
387
+ buildRoadmap(roadmap, domain),
388
+ buildDocs(sparc, validation, plans, adrs, lastHarvest),
389
+ buildToolkit(toolkit, expected),
390
+ buildStatus(insights, lastTest, mcpServers, settingsStatus, keysarium),
391
+ ];
392
+
393
+ // Write to stdout, never throw
394
+ try {
395
+ process.stdout.write(lines.join('\n') + '\n');
396
+ } catch { /* ignore */ }
397
+ }
398
+
399
+ main();
@@ -0,0 +1,145 @@
1
+ # Feature Lifecycle Rules
2
+
3
+ Governance for the `/feature` command's 4-phase pipeline. Applies whenever
4
+ a feature touches ≥ 4 files or introduces new architecture (use `/plan` for
5
+ smaller changes).
6
+
7
+ ## Entry modes
8
+
9
+ `/feature` officially supports two entry modes. The 4-phase pipeline behaves
10
+ **identically** in both — phase sequence, validation thresholds, retry logic,
11
+ and quality gates are the same.
12
+
13
+ ### Mode 1: Post-/replicate (default)
14
+
15
+ Project was bootstrapped via `/replicate`. CLAUDE.md, `docs/` SPARC slots,
16
+ `.claude/` toolkit, and the Docker scaffold are all generated by /replicate.
17
+ `/feature` runs in standard 4-phase mode.
18
+
19
+ ### Mode 2: Existing project (post v1.5.2)
20
+
21
+ Project has its own pre-existing CLAUDE.md, PRD, Specification, and stack.
22
+ The user ran `npx @dzhechkov/p-replicator init` to add the toolkit on top.
23
+ `/feature` runs in standard 4-phase mode.
24
+
25
+ #### Mode 2 prerequisites
26
+
27
+ - `init` completed and `verify` reports pre-shipped contract intact
28
+ - Standard SPARC paths populated: `docs/PRD.md`, `docs/Specification.md`,
29
+ `docs/Architecture.md` (Pseudocode/Refinement/Completion optional)
30
+ - Existing CLAUDE.md preserved at project root (init does NOT overwrite without `--force`)
31
+
32
+ #### Mode 2 detection
33
+
34
+ There is no automatic detection — `/feature` doesn't differentiate between
35
+ modes at runtime. Implicit signals (absence of `.p-replicator.json` "fresh"
36
+ markers, absence of Phase 3 toolkit artifacts) are advisory only. The
37
+ pipeline runs the same code path either way.
38
+
39
+ #### Mode 2 caveats
40
+
41
+ - `/feature` reads docs at standard SPARC paths — non-standard locations
42
+ require a one-time rename or symlink (no `--prd-path` flag yet)
43
+ - `/start` MUST NOT be used in Mode 2 (it's for fresh scaffolds only)
44
+ - `/feature-ent` is unavailable in Mode 2 unless DDD/ADR/C4 docs are added
45
+ manually (Phase 3 of /replicate normally generates `/feature-ent`
46
+ conditionally based on detected DDD docs)
47
+ - Auto-commit hooks (`Stop` hook → `autocommit-roadmap.cjs` /
48
+ `autocommit-insights.cjs` / `autocommit-plans.cjs`) may surprise users
49
+ with custom git workflows; `.claude/settings.json` can be edited after
50
+ `init` to disable them — the v1.4.2+ merge logic with `shippedDefaults`
51
+ baseline preserves user customizations across `update` runs
52
+
53
+ #### Mode 2 test contract
54
+
55
+ Same as Mode 1: pre-shipped contract intact, `verify` exit code 0, manifest
56
+ schema unchanged. No new flags or state-file fields are required.
57
+
58
+ ## Phase Sequence (strict)
59
+
60
+ ```
61
+ PLAN → VALIDATE → IMPLEMENT → REVIEW
62
+ ```
63
+
64
+ NEVER skip phases. NEVER reorder.
65
+
66
+ ## Phase 1: PLAN (sparc-prd-mini)
67
+
68
+ **Skill:** `.claude/skills/sparc-prd-mini/SKILL.md`
69
+
70
+ **Output:** `docs/features/<feature>/01-05_*.md` (5 SPARC docs).
71
+ **Quality gate:** all 5 docs created, no empty placeholders.
72
+
73
+ ## Phase 2: VALIDATE (requirements-validator)
74
+
75
+ **Skill:** `.claude/skills/requirements-validator/SKILL.md`
76
+
77
+ **Output:** `docs/features/<feature>/validation-report.md`
78
+
79
+ | Verdict | Threshold | Next |
80
+ |---------|-----------|------|
81
+ | 🟢 READY | average ≥ 70, no blockers | Phase 3 |
82
+ | 🟡 CAVEATS | 50-69, no blockers | Phase 3 (AUTO retries once on 🟡) |
83
+ | 🔴 NEEDS WORK | < 50 OR any blocker | Phase 1 (max 3 retries) |
84
+
85
+ After 3 retries with 🔴, halt and surface to user.
86
+
87
+ ## Phase 3: IMPLEMENT (parallel agents)
88
+
89
+ **Strategy:** maximum parallelism via `Task` tool.
90
+
91
+ 1. Identify independent work units from Phase 1's Architecture
92
+ 2. Spawn one Task per unit
93
+ 3. Each Task: read SPARC sections + implement + test + commit
94
+ 4. Coordinator merges/integrates after all Tasks complete
95
+ 5. Run full test suite
96
+
97
+ **Quality gate:** tests pass, lint clean, build succeeds.
98
+
99
+ ## Phase 4: REVIEW (brutal-honesty-review)
100
+
101
+ **Skill:** `.claude/skills/brutal-honesty-review/SKILL.md`
102
+
103
+ **Output:** `docs/features/<feature>/review-report.md`
104
+
105
+ | Severity | Action |
106
+ |----------|--------|
107
+ | `blocker` | MUST fix before merge |
108
+ | `high` | Fix in this feature unless explicit deferral |
109
+ | `medium` | Optional fix; create follow-up issue |
110
+ | `low` | Logged, no action required |
111
+
112
+ ## Quality Gates Summary
113
+
114
+ | Gate | Phase | Failure Action |
115
+ |------|-------|----------------|
116
+ | All SPARC docs created | 1 | Halt — investigate sparc-prd-mini |
117
+ | Validator verdict ≠ 🔴 (after retries) | 2 | Halt — surface to user |
118
+ | Tests + lint + build | 3 | Re-run, max 3 attempts |
119
+ | No `blocker` findings | 4 | Loop Phase 4 until clean |
120
+
121
+ ## Commit Discipline
122
+
123
+ - After Phase 1: `docs(<feature>): SPARC plan`
124
+ - After Phase 2: `docs(<feature>): validation report`
125
+ - After Phase 3: `feat(<feature>): implement [phase 3]`
126
+ - After Phase 4: `feat(<feature>): review fixes [phase 4]`
127
+
128
+ ## AUTO Mode (called from /go or /run)
129
+
130
+ Skip per-phase confirmation prompts. Auto-decisions per Phase 1-4 above.
131
+
132
+ ## When `/feature-ent` is the right choice
133
+
134
+ If `.claude/commands/feature-ent.md` exists, prefer it for: cross-bounded-context,
135
+ new ADRs, C4 updates. `/go` makes this decision automatically.
136
+
137
+ ## Anti-Patterns
138
+
139
+ | Anti-Pattern | Why bad |
140
+ |--------------|---------|
141
+ | Skipping Phase 2 (validation) | Phase 3 builds on unvalidated requirements |
142
+ | Implementing in Phase 1 | "Plan" should not contain code |
143
+ | One giant commit at the end | Hard to bisect, hard to roll back phases |
144
+ | Ignoring Phase 4 findings | brutal-honesty-review has measurable defect-catch rate |
145
+ | Adding to scope mid-pipeline | Restart `/feature` if scope grew significantly |
@@ -0,0 +1,74 @@
1
+ # Git Workflow Rules
2
+
3
+ Project-wide commit, branch, and push discipline. Applies to all commands
4
+ that modify files.
5
+
6
+ ## Branch Strategy
7
+
8
+ - `main` — production-ready, protected
9
+ - `feature/<id>-<slug>` — per-feature work
10
+ - `hotfix/<slug>` — emergency fixes
11
+
12
+ ## Commit Discipline
13
+
14
+ ### Cadence
15
+
16
+ Commit after each **logical change**, not at session end. NEVER batch unrelated changes.
17
+
18
+ ### Message Format (Conventional Commits)
19
+
20
+ ```
21
+ <type>(<scope>): <subject>
22
+
23
+ [body — what + why, NOT how]
24
+ ```
25
+
26
+ | Type | Use for |
27
+ |------|---------|
28
+ | `feat` | New user-facing feature |
29
+ | `fix` | Bug fix |
30
+ | `docs` | Documentation only |
31
+ | `refactor` | Behavior unchanged |
32
+ | `test` | Tests only |
33
+ | `chore` | Build, deps, scaffolding |
34
+ | `perf` | Performance |
35
+ | `style` | Formatting |
36
+ | `ci` | CI/CD |
37
+
38
+ ### Examples
39
+
40
+ ```
41
+ feat(auth): JWT-based login with refresh tokens
42
+ fix(payment): handle Stripe webhook retry idempotency
43
+ docs(roadmap): mark auth-jwt as done
44
+ chore: bump @dzhechkov/p-replicator to 1.4.0
45
+ ```
46
+
47
+ ## Push Discipline
48
+
49
+ - Push after each successful phase completion
50
+ - `git push origin HEAD`
51
+ - Force-push (`--force-with-lease`) ONLY on personal feature branches
52
+
53
+ ## Pre-Commit Hooks (auto-installed by `init` via `settings.json`)
54
+
55
+ | Hook | Purpose |
56
+ |------|---------|
57
+ | `Stop` autocommit-roadmap | Commits `.claude/feature-roadmap.json` if changed |
58
+ | `Stop` autocommit-insights | Commits `.claude/insights/` if changed |
59
+ | `Stop` autocommit-plans | Commits new `docs/plans/*.md` if added |
60
+
61
+ ## Forbidden
62
+
63
+ - `git commit --no-verify` (without explicit user approval)
64
+ - `git push --force` to `main`
65
+ - Committing `.env`, `.env.*` (except `.env.example`)
66
+ - Committing secrets, API keys, passwords
67
+ - Committing `node_modules/`, `dist/`, `coverage/`
68
+
69
+ ## Pre-Push Self-Check
70
+
71
+ - [ ] Tests pass
72
+ - [ ] Lint clean
73
+ - [ ] No secrets in diff
74
+ - [ ] Branch up-to-date with origin/main
@@ -0,0 +1,77 @@
1
+ # Insights Capture Rules
2
+
3
+ When and how to record development "грабли" (rakes) into the project knowledge
4
+ base. Used by `/myinsights` and the `SessionStart` hook
5
+ (`.claude/hooks/session-insights.cjs`).
6
+
7
+ ## When to Capture
8
+
9
+ CAPTURE an insight whenever:
10
+ - A bug took > 15 min to root-cause
11
+ - A library / tool behaved differently than docs claimed
12
+ - A workaround was needed for a third-party limitation
13
+ - A non-obvious config or env var fixed a deploy issue
14
+ - An algorithm choice paid off (or didn't) in noticeable ways
15
+
16
+ DO NOT capture:
17
+ - One-line typos or simple syntax errors
18
+ - Information already in official docs that's easy to find
19
+ - Personal preference notes
20
+
21
+ ## How to Capture (via /myinsights)
22
+
23
+ ```
24
+ /myinsights "Prisma migrate dev fails silently if shadow DB unreachable. Workaround: set DATABASE_URL_SHADOW explicitly."
25
+ ```
26
+
27
+ The command parses problem + solution + tags, appends a structured entry to
28
+ `.claude/insights/index.md`, auto-commits via Stop hook.
29
+
30
+ ## Entry Structure
31
+
32
+ ```markdown
33
+ ## <YYYY-MM-DD> — <short title>
34
+
35
+ **Tags:** <comma-separated keywords>
36
+
37
+ **Problem:**
38
+ <1-3 sentences>
39
+
40
+ **Solution:**
41
+ <1-5 sentences with code if relevant>
42
+
43
+ **References:** <file:line | commit hash | external link>
44
+
45
+ ---
46
+ ```
47
+
48
+ ### Tag Conventions
49
+
50
+ Use lowercase, hyphenated, specific tags:
51
+ - ✅ `prisma-migration`, `postgres-timezone`, `docker-compose-network`
52
+ - ❌ `bug`, `fix`, `important` (too generic)
53
+
54
+ Aim for 2-5 tags per entry.
55
+
56
+ ## Auto-Injection on SessionStart
57
+
58
+ The `SessionStart` hook runs `node .claude/hooks/session-insights.cjs` which
59
+ prints recent insights to stdout. Claude Code captures stdout and injects
60
+ it into the initial session context.
61
+
62
+ ## Storage Lifecycle
63
+
64
+ - `<= 50 entries`: single `index.md`
65
+ - `> 50 entries`: split into archive files `<YYYY-MM>.md` with `index.md` as TOC
66
+ - Never delete entries unless factually wrong; mark superseded entries instead
67
+
68
+ ## Anti-Patterns
69
+
70
+ | Anti-Pattern | Why bad |
71
+ |--------------|---------|
72
+ | Capturing every error | Noise drowns signal |
73
+ | Generic tags | Tag-based recall fails |
74
+ | Missing tags | Entry is unfindable |
75
+ | Long prose in problem field | Hard to scan |
76
+ | Solution without code | When code is the fix, include the snippet |
77
+ | Deleting outdated entries | Lose history; supersede instead |