@dzhechkov/p-replicator 1.3.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.
- package/README.md +55 -0
- package/bin/cli.js +0 -0
- package/package.json +7 -2
- package/src/cli.js +23 -6
- package/src/commands/doctor.js +46 -29
- package/src/commands/init.js +61 -8
- package/src/commands/list.js +5 -26
- package/src/commands/update.js +73 -7
- package/src/commands/verify.js +111 -0
- package/src/utils.js +275 -4
- package/templates/.claude/commands/deploy.md +100 -0
- package/templates/.claude/commands/docs.md +79 -0
- package/templates/.claude/commands/feature.md +134 -0
- package/templates/.claude/commands/go.md +115 -0
- package/templates/.claude/commands/myinsights.md +72 -0
- package/templates/.claude/commands/next.md +110 -0
- package/templates/.claude/commands/plan.md +88 -0
- package/templates/.claude/commands/replicate.md +103 -17
- package/templates/.claude/commands/run.md +151 -0
- package/templates/.claude/commands/start.md +88 -0
- package/templates/.claude/hooks/autocommit-insights.cjs +39 -0
- package/templates/.claude/hooks/autocommit-plans.cjs +39 -0
- package/templates/.claude/hooks/autocommit-roadmap.cjs +44 -0
- package/templates/.claude/hooks/session-insights.cjs +28 -0
- package/templates/.claude/hooks/state-update.cjs +79 -0
- package/templates/.claude/hooks/statusline.cjs +399 -0
- package/templates/.claude/rules/feature-lifecycle.md +145 -0
- package/templates/.claude/rules/git-workflow.md +74 -0
- package/templates/.claude/rules/insights-capture.md +77 -0
- package/templates/.claude/rules/replicate-pipeline.md +92 -19
- package/templates/.claude/settings.json +44 -0
- package/templates/.claude/skills/brutal-honesty-review/scripts/assess-code.sh +0 -0
- package/templates/.claude/skills/brutal-honesty-review/scripts/assess-tests.sh +0 -0
- package/templates/.claude/skills/goap-research-ed25519/scripts/ed25519_verifier.py +0 -0
- package/templates/.claude/skills/goap-research-ed25519/scripts/goap_planner.py +0 -0
package/README.md
CHANGED
|
@@ -4,6 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
Transform a product idea into a fully documented, validated, toolkit-equipped project ready for development.
|
|
6
6
|
|
|
7
|
+
## Documentation
|
|
8
|
+
|
|
9
|
+
- π·πΊ [ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ Π½Π° ΡΡΡΡΠΊΠΎΠΌ](./README/ru/README.md)
|
|
10
|
+
- π¬π§ [English documentation](./README/eng/README.md)
|
|
11
|
+
|
|
12
|
+
Each language pack includes 7 sections: Quick start, User guide, Admin guide,
|
|
13
|
+
API reference, Architecture, Troubleshooting, Changelog.
|
|
14
|
+
|
|
7
15
|
## Quick Start
|
|
8
16
|
|
|
9
17
|
```bash
|
|
@@ -19,6 +27,53 @@ claude
|
|
|
19
27
|
|
|
20
28
|
The system walks you through 4-5 phases with interactive checkpoints. At each checkpoint you review the output and confirm before proceeding. Estimated time: 45-90 minutes for the full pipeline.
|
|
21
29
|
|
|
30
|
+
### Already have technical documentation?
|
|
31
|
+
|
|
32
|
+
If you already have tech specs, architecture notes, or API docs for the project,
|
|
33
|
+
you can skip Phase 0 (Product Discovery) and feed your existing docs into Phase 1
|
|
34
|
+
as input:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
mkdir -p docs/existing
|
|
38
|
+
cp your-tech-doc-*.md docs/existing/ # place your existing docs here
|
|
39
|
+
|
|
40
|
+
claude
|
|
41
|
+
/replicate "Use my existing docs in docs/existing/, skip Phase 0"
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Three sub-paths are supported:
|
|
45
|
+
- **A. Full pipeline** β `/replicate` reads your docs, generates the 11 SPARC docs + validation + toolkit + scaffold
|
|
46
|
+
- **B. SPARC docs only** β invoke the `sparc-prd-mini` skill directly, no toolkit/scaffold
|
|
47
|
+
- **C. Validation-only** β rename existing docs to SPARC slot names + invoke the `requirements-validator` skill
|
|
48
|
+
|
|
49
|
+
Full recipe with caveats: see [User Guide](./README/eng/02_user_guide.md#starting-from-existing-tech-docs)
|
|
50
|
+
([RU](./README/ru/02_user_guide.md#Π°Π»ΡΡΠ΅ΡΠ½Π°ΡΠΈΠ²Π½ΡΠΉ-Π²Ρ
ΠΎΠ΄-Ρ-Π²Π°Ρ-ΡΠΆΠ΅-Π΅ΡΡΡ-ΡΠ΅Ρ
Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ)).
|
|
51
|
+
|
|
52
|
+
### Adding features to an existing project?
|
|
53
|
+
|
|
54
|
+
If you already have a working project (stack, PRD, CLAUDE.md, Specification
|
|
55
|
+
defined) and want to **add new features** with the same SPARC-mini validation
|
|
56
|
+
cycle that `/replicate` provides β use `/feature` (Mode 2):
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
cd existing-project
|
|
60
|
+
npx @dzhechkov/p-replicator init # idempotent β preserves CLAUDE.md
|
|
61
|
+
claude
|
|
62
|
+
/feature add-stripe-payments # 4-phase: PLAN β VALIDATE β IMPLEMENT β REVIEW
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`/feature` runs the same validation pipeline as `/replicate` Phase 2, scoped
|
|
66
|
+
to a single feature. Same verdicts: π’ READY (β₯70) / π‘ CAVEATS (50-69) /
|
|
67
|
+
π΄ NEEDS WORK (<50). Same retry logic. Same `brutal-honesty-review` post-implementation.
|
|
68
|
+
|
|
69
|
+
Three sub-paths:
|
|
70
|
+
- **A. /feature directly** β single feature, full 4-phase lifecycle
|
|
71
|
+
- **B. /go auto-router** β picks /plan vs /feature based on complexity
|
|
72
|
+
- **C. Direct skill invocation** β only the validation cycle, no full lifecycle
|
|
73
|
+
|
|
74
|
+
Full recipe: see [User Guide](./README/eng/02_user_guide.md#feature-workflow-in-an-existing-project-mode-2)
|
|
75
|
+
([RU](./README/ru/02_user_guide.md#feature-workflow-Π²-ΡΡΡΠ΅ΡΡΠ²ΡΡΡΠ΅ΠΌ-ΠΏΡΠΎΠ΅ΠΊΡΠ΅-mode-2)).
|
|
76
|
+
|
|
22
77
|
## What Gets Installed
|
|
23
78
|
|
|
24
79
|
| Component | Count | Description |
|
package/bin/cli.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dzhechkov/p-replicator",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.2",
|
|
4
4
|
"description": "P-Replicator β Claude Code toolkit for AI-assisted product development (Vibe Coding). Full /replicate pipeline, 10 modular skills (194K+ chars), /harvest knowledge extraction, swarm agents, quality gates, security patterns, cross-project learning.",
|
|
5
5
|
"main": "src/cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -12,7 +12,12 @@
|
|
|
12
12
|
"templates/"
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
|
-
"test": "node
|
|
15
|
+
"test": "node --test tests/unit/utils.test.js tests/e2e/lifecycle.test.js tests/snapshot/templates.test.js",
|
|
16
|
+
"test:unit": "node --test tests/unit/utils.test.js",
|
|
17
|
+
"test:e2e": "node --test tests/e2e/lifecycle.test.js",
|
|
18
|
+
"test:snapshot": "node --test tests/snapshot/templates.test.js",
|
|
19
|
+
"test:doctor": "node bin/cli.js doctor",
|
|
20
|
+
"snapshot:baseline": "node tests/snapshot/update-baseline.js",
|
|
16
21
|
"prepublishOnly": "node scripts/sync-templates.js"
|
|
17
22
|
},
|
|
18
23
|
"keywords": [
|
package/src/cli.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { bold, cyan, dim, green, yellow, error } = require('./utils');
|
|
3
|
+
const { bold, cyan, dim, green, yellow, error, COMPONENTS } = require('./utils');
|
|
4
4
|
|
|
5
5
|
// ---------------------------------------------------------------------------
|
|
6
6
|
// Banner
|
|
@@ -33,11 +33,13 @@ function showHelp() {
|
|
|
33
33
|
console.log(' ' + green('update') + ' Update to latest version');
|
|
34
34
|
console.log(' ' + green('remove') + ' Remove P-Replicator from project');
|
|
35
35
|
console.log(' ' + green('list') + ' List installed components');
|
|
36
|
-
console.log(' ' + green('doctor') + ' Check installation health');
|
|
36
|
+
console.log(' ' + green('doctor') + ' Check installation health (pre-shipped artifacts)');
|
|
37
|
+
console.log(' ' + green('verify') + ' Verify pre-shipped + post-/replicate artifacts');
|
|
37
38
|
console.log('');
|
|
38
39
|
console.log(bold('Options:'));
|
|
39
40
|
console.log(' ' + yellow('--force') + ' Overwrite existing files without prompting');
|
|
40
41
|
console.log(' ' + yellow('--dry-run') + ' Show what would be done without making changes');
|
|
42
|
+
console.log(' ' + yellow('--reset-settings') + ' Force-overwrite settings.json (default: merge with user customizations)');
|
|
41
43
|
console.log(' ' + yellow('--help, -h') + ' Show this help message');
|
|
42
44
|
console.log(' ' + yellow('--version, -v') + ' Show version number');
|
|
43
45
|
console.log('');
|
|
@@ -47,14 +49,21 @@ function showHelp() {
|
|
|
47
49
|
console.log(dim(' $ ') + 'npx @dzhechkov/p-replicator update --dry-run');
|
|
48
50
|
console.log(dim(' $ ') + 'npx @dzhechkov/p-replicator doctor');
|
|
49
51
|
console.log('');
|
|
52
|
+
// Counts derived from utils.COMPONENTS.items (SSOT) β fixes prior "1 rule" drift.
|
|
53
|
+
const c = {
|
|
54
|
+
skills: Object.keys(COMPONENTS.skills.items).length,
|
|
55
|
+
commands: Object.keys(COMPONENTS.commands.items).length,
|
|
56
|
+
agents: Object.keys(COMPONENTS.agents.items).length,
|
|
57
|
+
rules: Object.keys(COMPONENTS.rules.items).length,
|
|
58
|
+
};
|
|
50
59
|
console.log(bold('What gets installed:'));
|
|
51
|
-
console.log(
|
|
60
|
+
console.log(` ${c.skills} skills SPARC docs, research, problem-solving, validation, review,`);
|
|
52
61
|
console.log(' reverse engineering, toolkit generation, pipeline forge,');
|
|
53
62
|
console.log(' knowledge extraction, task exploration');
|
|
54
|
-
console.log(
|
|
55
|
-
console.log(
|
|
63
|
+
console.log(` ${c.commands} commands /replicate (full pipeline), /harvest (knowledge extraction)`);
|
|
64
|
+
console.log(` ${c.agents} agents replicate-coordinator, product-discoverer, doc-validator,`);
|
|
56
65
|
console.log(' harvest-coordinator');
|
|
57
|
-
console.log(
|
|
66
|
+
console.log(` ${c.rules} rule${c.rules === 1 ? '' : 's'} replicate-pipeline, skill-interface-protocol`);
|
|
58
67
|
console.log('');
|
|
59
68
|
console.log(bold('After installation:'));
|
|
60
69
|
console.log(' 1. Open ' + cyan('Claude Code') + ' in this directory');
|
|
@@ -75,6 +84,7 @@ function parseArgs(argv) {
|
|
|
75
84
|
const flags = {
|
|
76
85
|
force: false,
|
|
77
86
|
dryRun: false,
|
|
87
|
+
resetSettings: false,
|
|
78
88
|
targetDir: process.cwd(),
|
|
79
89
|
};
|
|
80
90
|
|
|
@@ -88,6 +98,9 @@ function parseArgs(argv) {
|
|
|
88
98
|
case '--dry-run':
|
|
89
99
|
flags.dryRun = true;
|
|
90
100
|
break;
|
|
101
|
+
case '--reset-settings':
|
|
102
|
+
flags.resetSettings = true;
|
|
103
|
+
break;
|
|
91
104
|
case '--help':
|
|
92
105
|
case '-h':
|
|
93
106
|
command = 'help';
|
|
@@ -148,6 +161,10 @@ function main() {
|
|
|
148
161
|
require('./commands/doctor')(flags);
|
|
149
162
|
break;
|
|
150
163
|
|
|
164
|
+
case 'verify':
|
|
165
|
+
require('./commands/verify')(flags);
|
|
166
|
+
break;
|
|
167
|
+
|
|
151
168
|
default:
|
|
152
169
|
error(`Unknown command: "${resolvedCommand}"`);
|
|
153
170
|
console.log('');
|
package/src/commands/doctor.js
CHANGED
|
@@ -2,40 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const { execFileSync } = require('child_process');
|
|
5
6
|
const {
|
|
6
7
|
green, red, yellow, cyan, bold, dim,
|
|
7
8
|
info, success, warn, error: logError,
|
|
8
9
|
readManifest, fileExists,
|
|
9
|
-
MANIFEST_FILE,
|
|
10
|
+
MANIFEST_FILE, COMPONENTS,
|
|
10
11
|
} = require('../utils');
|
|
11
12
|
|
|
12
13
|
// ---------------------------------------------------------------------------
|
|
13
|
-
// Expected components
|
|
14
|
+
// Expected components β derived from utils.COMPONENTS.items (SSOT)
|
|
14
15
|
// ---------------------------------------------------------------------------
|
|
15
16
|
|
|
16
|
-
const EXPECTED_SKILLS
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
'problem-solver-enhanced',
|
|
21
|
-
'requirements-validator',
|
|
22
|
-
'brutal-honesty-review',
|
|
23
|
-
'cc-toolkit-generator-enhanced',
|
|
24
|
-
'reverse-engineering-unicorn',
|
|
25
|
-
'pipeline-forge',
|
|
26
|
-
'knowledge-extractor',
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
const EXPECTED_COMMANDS = ['replicate', 'harvest'];
|
|
30
|
-
|
|
31
|
-
const EXPECTED_AGENTS = [
|
|
32
|
-
'replicate-coordinator',
|
|
33
|
-
'product-discoverer',
|
|
34
|
-
'doc-validator',
|
|
35
|
-
'harvest-coordinator',
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
const EXPECTED_RULES = ['replicate-pipeline', 'skill-interface-protocol'];
|
|
17
|
+
const EXPECTED_SKILLS = Object.keys(COMPONENTS.skills.items);
|
|
18
|
+
const EXPECTED_COMMANDS = Object.keys(COMPONENTS.commands.items);
|
|
19
|
+
const EXPECTED_AGENTS = Object.keys(COMPONENTS.agents.items);
|
|
20
|
+
const EXPECTED_RULES = Object.keys(COMPONENTS.rules.items);
|
|
39
21
|
|
|
40
22
|
function run(options) {
|
|
41
23
|
const { targetDir } = options;
|
|
@@ -74,7 +56,7 @@ function run(options) {
|
|
|
74
56
|
console.log('');
|
|
75
57
|
|
|
76
58
|
// ββ 3) Skills βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
77
|
-
console.log(bold(
|
|
59
|
+
console.log(bold(`Skills (expected ${EXPECTED_SKILLS.length}):`));
|
|
78
60
|
for (const skill of EXPECTED_SKILLS) {
|
|
79
61
|
const skillPath = path.join(claudeDir, 'skills', skill, 'SKILL.md');
|
|
80
62
|
if (fileExists(skillPath)) {
|
|
@@ -86,7 +68,7 @@ function run(options) {
|
|
|
86
68
|
console.log('');
|
|
87
69
|
|
|
88
70
|
// ββ 4) Commands βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
89
|
-
console.log(bold(
|
|
71
|
+
console.log(bold(`Commands (expected ${EXPECTED_COMMANDS.length}):`));
|
|
90
72
|
for (const cmd of EXPECTED_COMMANDS) {
|
|
91
73
|
const cmdPath = path.join(claudeDir, 'commands', `${cmd}.md`);
|
|
92
74
|
if (fileExists(cmdPath)) {
|
|
@@ -98,7 +80,7 @@ function run(options) {
|
|
|
98
80
|
console.log('');
|
|
99
81
|
|
|
100
82
|
// ββ 5) Agents βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
101
|
-
console.log(bold(
|
|
83
|
+
console.log(bold(`Agents (expected ${EXPECTED_AGENTS.length}):`));
|
|
102
84
|
for (const agent of EXPECTED_AGENTS) {
|
|
103
85
|
const agentPath = path.join(claudeDir, 'agents', `${agent}.md`);
|
|
104
86
|
if (fileExists(agentPath)) {
|
|
@@ -110,7 +92,7 @@ function run(options) {
|
|
|
110
92
|
console.log('');
|
|
111
93
|
|
|
112
94
|
// ββ 6) Rules ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
113
|
-
console.log(bold(
|
|
95
|
+
console.log(bold(`Rules (expected ${EXPECTED_RULES.length}):`));
|
|
114
96
|
for (const rule of EXPECTED_RULES) {
|
|
115
97
|
const rulePath = path.join(claudeDir, 'rules', `${rule}.md`);
|
|
116
98
|
if (fileExists(rulePath)) {
|
|
@@ -121,6 +103,41 @@ function run(options) {
|
|
|
121
103
|
}
|
|
122
104
|
console.log('');
|
|
123
105
|
|
|
106
|
+
// ββ 6b) Settings (hooks config) βββββββββββββββββββββββββββββββββββββββββ
|
|
107
|
+
console.log(bold('Hooks (settings.json):'));
|
|
108
|
+
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
109
|
+
if (fileExists(settingsPath)) {
|
|
110
|
+
pass('settings.json');
|
|
111
|
+
} else {
|
|
112
|
+
fail('settings.json β missing (SessionStart + Stop hooks not configured)');
|
|
113
|
+
}
|
|
114
|
+
console.log('');
|
|
115
|
+
|
|
116
|
+
// ββ 6c) Hook scripts (cross-platform Node, v1.4.1+) βββββββββββββββββββββ
|
|
117
|
+
if (COMPONENTS.hooks) {
|
|
118
|
+
const hookKeys = Object.keys(COMPONENTS.hooks.items);
|
|
119
|
+
console.log(bold(`Hook scripts (expected ${hookKeys.length}):`));
|
|
120
|
+
for (const hook of hookKeys) {
|
|
121
|
+
const hookPath = path.join(claudeDir, 'hooks', `${hook}.cjs`);
|
|
122
|
+
if (fileExists(hookPath)) {
|
|
123
|
+
pass(`${hook}.cjs`);
|
|
124
|
+
} else {
|
|
125
|
+
fail(`${hook}.cjs β missing (referenced by settings.json)`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
console.log('');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// ββ 6d) Prerequisites (system tools the package relies on) ββββββββββββββ
|
|
132
|
+
console.log(bold('Prerequisites:'));
|
|
133
|
+
try {
|
|
134
|
+
execFileSync('git', ['--version'], { stdio: 'pipe' });
|
|
135
|
+
pass('git on PATH');
|
|
136
|
+
} catch {
|
|
137
|
+
fail('git NOT on PATH β autocommit hooks (roadmap, insights, plans) will silently no-op');
|
|
138
|
+
}
|
|
139
|
+
console.log('');
|
|
140
|
+
|
|
124
141
|
// ββ 7) Keysarium integration ββββββββββββββββββββββββββββββββββββββββββββ
|
|
125
142
|
console.log(bold('Integration:'));
|
|
126
143
|
const keysariumManifest = path.join(targetDir, '.keysarium.json');
|
package/src/commands/init.js
CHANGED
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const {
|
|
6
|
-
green, cyan, bold, dim,
|
|
6
|
+
green, cyan, bold, dim, yellow,
|
|
7
7
|
info, success, warn, error: logError, step,
|
|
8
|
-
copyDirRecursive, fileExists, readJSON,
|
|
9
|
-
createManifest, writeManifest, getTemplatesDir, getRelativePaths,
|
|
8
|
+
copyDirRecursive, fileExists, readJSON, writeJSON, ensureDir,
|
|
9
|
+
createManifest, writeManifest, readManifest, getTemplatesDir, getRelativePaths,
|
|
10
10
|
COMPONENTS, MANIFEST_FILE,
|
|
11
|
+
mergeSettingsJson, removeOrphanHooks,
|
|
11
12
|
} = require('../utils');
|
|
12
13
|
|
|
13
14
|
// ---------------------------------------------------------------------------
|
|
@@ -37,7 +38,7 @@ function showKeysariumIntegration(keysariumManifest) {
|
|
|
37
38
|
// Helpers
|
|
38
39
|
// ---------------------------------------------------------------------------
|
|
39
40
|
|
|
40
|
-
function installComponent(key, comp, templatesDir, targetDir) {
|
|
41
|
+
function installComponent(key, comp, templatesDir, targetDir, options) {
|
|
41
42
|
const src = path.join(templatesDir, comp.src);
|
|
42
43
|
const dest = path.join(targetDir, comp.src);
|
|
43
44
|
|
|
@@ -46,7 +47,44 @@ function installComponent(key, comp, templatesDir, targetDir) {
|
|
|
46
47
|
return [];
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
// settings.json merge (v1.4.2 + v1.4.3 orphan detection): on --force, run
|
|
51
|
+
// orphan cleanup (v1.4.3) using shippedDefaults from previous manifest, then
|
|
52
|
+
// merge new template into the cleaned user settings. Use --reset-settings to
|
|
53
|
+
// force full overwrite.
|
|
54
|
+
if (
|
|
55
|
+
comp.isFile &&
|
|
56
|
+
comp.src.endsWith('.json') &&
|
|
57
|
+
fileExists(dest) &&
|
|
58
|
+
!(options && options.resetSettings)
|
|
59
|
+
) {
|
|
60
|
+
try {
|
|
61
|
+
const tplJson = readJSON(src);
|
|
62
|
+
const existingJson = readJSON(dest);
|
|
63
|
+
const fileKey = comp.src.split(/[/\\]/).pop();
|
|
64
|
+
const oldTplJson =
|
|
65
|
+
(options && options.previousManifest && options.previousManifest.shippedDefaults &&
|
|
66
|
+
options.previousManifest.shippedDefaults[fileKey]) || null;
|
|
67
|
+
|
|
68
|
+
// v1.4.3: remove hooks that were defaults in the old shipped template
|
|
69
|
+
// but no longer exist in the new template (true orphans).
|
|
70
|
+
const cleaned = removeOrphanHooks(existingJson, oldTplJson, tplJson);
|
|
71
|
+
const mergedJson = mergeSettingsJson(cleaned, tplJson);
|
|
72
|
+
writeJSON(dest, mergedJson);
|
|
73
|
+
info(`Merged user customizations into ${comp.src} ${dim('(use --reset-settings to overwrite)')}`);
|
|
74
|
+
return [comp.src];
|
|
75
|
+
} catch (err) {
|
|
76
|
+
warn(`settings merge failed (${err.message}) \u2014 falling back to overwrite`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
49
80
|
copyDirRecursive(src, dest);
|
|
81
|
+
|
|
82
|
+
// Single-file components (e.g., settings.json): manifest tracks the file directly,
|
|
83
|
+
// since getRelativePaths(file) returns [] (it expects a directory).
|
|
84
|
+
if (fs.statSync(src).isFile()) {
|
|
85
|
+
return [comp.src];
|
|
86
|
+
}
|
|
87
|
+
|
|
50
88
|
return getRelativePaths(dest).map((rel) => path.join(comp.src, rel));
|
|
51
89
|
}
|
|
52
90
|
|
|
@@ -55,10 +93,13 @@ function installComponent(key, comp, templatesDir, targetDir) {
|
|
|
55
93
|
// ---------------------------------------------------------------------------
|
|
56
94
|
|
|
57
95
|
function run(options) {
|
|
58
|
-
const { force, dryRun, targetDir } = options;
|
|
96
|
+
const { force, dryRun, targetDir, resetSettings } = options;
|
|
59
97
|
const manifestPath = path.join(targetDir, MANIFEST_FILE);
|
|
60
98
|
|
|
61
99
|
// ββ a) Check for existing installation ββββββββββββββββββββββββββββββββββ
|
|
100
|
+
// Read previous manifest BEFORE we overwrite it. Used by installComponent
|
|
101
|
+
// for orphan-hook detection in settings.json (v1.4.3).
|
|
102
|
+
const previousManifest = readManifest(targetDir);
|
|
62
103
|
if (fileExists(manifestPath)) {
|
|
63
104
|
if (!force) {
|
|
64
105
|
warn('P-Replicator is already installed in this directory.');
|
|
@@ -75,7 +116,11 @@ function run(options) {
|
|
|
75
116
|
}
|
|
76
117
|
|
|
77
118
|
// ββ c) Show plan ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
78
|
-
|
|
119
|
+
// Install only pre-shipped groups; project-generated groups have no `src` β
|
|
120
|
+
// they describe artifacts created by /replicate at a later phase.
|
|
121
|
+
const componentKeys = Object.keys(COMPONENTS).filter(
|
|
122
|
+
(k) => COMPONENTS[k].kind === 'pre-shipped'
|
|
123
|
+
);
|
|
79
124
|
const templatesDir = getTemplatesDir();
|
|
80
125
|
|
|
81
126
|
console.log('');
|
|
@@ -103,7 +148,7 @@ function run(options) {
|
|
|
103
148
|
const comp = COMPONENTS[key];
|
|
104
149
|
step(i + 1, totalComponents, `Installing ${comp.label}...`);
|
|
105
150
|
|
|
106
|
-
const files = installComponent(key, comp, templatesDir, targetDir);
|
|
151
|
+
const files = installComponent(key, comp, templatesDir, targetDir, { resetSettings, previousManifest });
|
|
107
152
|
installedFiles.push(...files);
|
|
108
153
|
}
|
|
109
154
|
|
|
@@ -112,7 +157,15 @@ function run(options) {
|
|
|
112
157
|
const pkg = readJSON(pkgPath);
|
|
113
158
|
const version = pkg ? pkg.version : '0.0.0';
|
|
114
159
|
|
|
115
|
-
|
|
160
|
+
// v1.4.3: snapshot the settings.json template we just shipped, so the NEXT
|
|
161
|
+
// upgrade can detect orphan hooks (hooks we used to ship but no longer do).
|
|
162
|
+
const shippedDefaults = {};
|
|
163
|
+
const settingsTemplatePath = path.join(templatesDir, '.claude', 'settings.json');
|
|
164
|
+
if (fileExists(settingsTemplatePath)) {
|
|
165
|
+
const tplContent = readJSON(settingsTemplatePath);
|
|
166
|
+
if (tplContent) shippedDefaults['settings.json'] = tplContent;
|
|
167
|
+
}
|
|
168
|
+
const manifest = createManifest(version, componentKeys, installedFiles.sort(), shippedDefaults);
|
|
116
169
|
writeManifest(targetDir, manifest);
|
|
117
170
|
info(`Created ${MANIFEST_FILE} manifest`);
|
|
118
171
|
|
package/src/commands/list.js
CHANGED
|
@@ -6,37 +6,16 @@ const {
|
|
|
6
6
|
green, cyan, bold, dim, yellow,
|
|
7
7
|
info, warn,
|
|
8
8
|
readManifest, fileExists,
|
|
9
|
-
MANIFEST_FILE,
|
|
9
|
+
MANIFEST_FILE, COMPONENTS,
|
|
10
10
|
} = require('../utils');
|
|
11
11
|
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
|
-
//
|
|
13
|
+
// Display details \u2014 pulled from utils.COMPONENTS.items (SSOT)
|
|
14
14
|
// ---------------------------------------------------------------------------
|
|
15
15
|
|
|
16
|
-
const SKILL_DETAILS
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
'goap-research-ed25519': 'Verified research with Ed25519 anti-hallucination',
|
|
20
|
-
'problem-solver-enhanced': 'First principles + TRIZ (9 modules)',
|
|
21
|
-
'requirements-validator': 'INVEST/SMART validation + BDD scenarios',
|
|
22
|
-
'brutal-honesty-review': 'Unvarnished technical criticism',
|
|
23
|
-
'cc-toolkit-generator-enhanced': 'Modular toolkit generator (9 modules, ~165K chars)',
|
|
24
|
-
'reverse-engineering-unicorn': 'Company reverse engineering + playbook',
|
|
25
|
-
'pipeline-forge': 'Meta-skill: build AI pipelines from patterns',
|
|
26
|
-
'knowledge-extractor': 'Extract reusable knowledge from projects',
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const COMMAND_DETAILS = {
|
|
30
|
-
'replicate': 'Full pipeline: idea \u2192 validated docs \u2192 toolkit',
|
|
31
|
-
'harvest': 'Knowledge extraction from projects',
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const AGENT_DETAILS = {
|
|
35
|
-
'replicate-coordinator': 'Pipeline orchestration (Phases 0-4)',
|
|
36
|
-
'product-discoverer': 'Market research (Phase 0)',
|
|
37
|
-
'doc-validator': 'Documentation validation swarm (Phase 2)',
|
|
38
|
-
'harvest-coordinator': 'Knowledge extraction swarm',
|
|
39
|
-
};
|
|
16
|
+
const SKILL_DETAILS = COMPONENTS.skills.items;
|
|
17
|
+
const COMMAND_DETAILS = COMPONENTS.commands.items;
|
|
18
|
+
const AGENT_DETAILS = COMPONENTS.agents.items;
|
|
40
19
|
|
|
41
20
|
function run(options) {
|
|
42
21
|
const { targetDir } = options;
|
package/src/commands/update.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const fs = require('fs');
|
|
3
4
|
const path = require('path');
|
|
4
5
|
const {
|
|
5
6
|
green, yellow, cyan, bold, dim,
|
|
6
7
|
info, success, warn, error: logError, step,
|
|
7
|
-
copyDirRecursive, fileExists, readJSON,
|
|
8
|
+
copyDirRecursive, fileExists, readJSON, writeJSON,
|
|
8
9
|
diffFiles, getRelativePaths,
|
|
9
10
|
readManifest, writeManifest, createManifest, getTemplatesDir,
|
|
10
11
|
COMPONENTS, MANIFEST_FILE,
|
|
12
|
+
mergeSettingsJson, removeOrphanHooks,
|
|
11
13
|
} = require('../utils');
|
|
12
14
|
|
|
13
15
|
function run(options) {
|
|
14
|
-
const { force, dryRun, targetDir } = options;
|
|
16
|
+
const { force, dryRun, targetDir, resetSettings } = options;
|
|
15
17
|
|
|
16
18
|
// ββ a) Check for existing installation ββββββββββββββββββββββββββββββββββ
|
|
17
19
|
const manifest = readManifest(targetDir);
|
|
@@ -75,24 +77,88 @@ function run(options) {
|
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
// ββ d) Apply updates βββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
80
|
+
// For each modified/added file: regular copy, EXCEPT settings.json β which
|
|
81
|
+
// gets the same merge + orphan-cleanup treatment as init --force (v1.4.3).
|
|
78
82
|
const filesToUpdate = [...added, ...modified];
|
|
79
83
|
let updated = 0;
|
|
84
|
+
const oldSettingsTpl = manifest.shippedDefaults && manifest.shippedDefaults['settings.json'];
|
|
80
85
|
|
|
81
86
|
for (const rel of filesToUpdate) {
|
|
82
87
|
const src = path.join(templateClaude, rel);
|
|
83
88
|
const dest = path.join(projectClaude, rel);
|
|
84
|
-
|
|
89
|
+
const normalized = rel.replace(/\\/g, '/');
|
|
90
|
+
const isSettings = normalized === 'settings.json' || normalized.endsWith('/settings.json');
|
|
91
|
+
|
|
92
|
+
if (isSettings && fileExists(dest) && !resetSettings) {
|
|
93
|
+
try {
|
|
94
|
+
const tplJson = readJSON(src);
|
|
95
|
+
const existingJson = readJSON(dest);
|
|
96
|
+
const cleaned = removeOrphanHooks(existingJson, oldSettingsTpl || null, tplJson);
|
|
97
|
+
const mergedJson = mergeSettingsJson(cleaned, tplJson);
|
|
98
|
+
writeJSON(dest, mergedJson);
|
|
99
|
+
info(`Merged settings.json with user customizations ${dim('(use --reset-settings to overwrite)')}`);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
warn(`settings merge failed (${err.message}) β falling back to overwrite`);
|
|
102
|
+
copyDirRecursive(src, dest);
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
copyDirRecursive(src, dest);
|
|
106
|
+
}
|
|
85
107
|
updated++;
|
|
86
108
|
}
|
|
87
109
|
|
|
88
110
|
// ββ e) Update manifest βββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
111
|
+
// Track ONLY files shipped by the package's templates β never walk the user's
|
|
112
|
+
// .claude/ tree. Walking projectClaude would capture project-generated files
|
|
113
|
+
// (e.g. /replicate output: start.md, feature.md, plan.md) into manifest.files,
|
|
114
|
+
// and a subsequent `remove` would then delete them, contradicting remove.js's
|
|
115
|
+
// own "project-specific files were NOT removed" guarantee.
|
|
116
|
+
const newTrackedFiles = getRelativePaths(templateClaude).map((f) => '.claude/' + f);
|
|
117
|
+
const newTrackedSet = new Set(newTrackedFiles);
|
|
118
|
+
|
|
119
|
+
// Orphan cleanup: files previously tracked by the package but no longer
|
|
120
|
+
// shipped (skill removed in a new package version, etc.). Safe to delete
|
|
121
|
+
// because they were package-owned. Project-generated files are never in the
|
|
122
|
+
// old manifest, so they are never considered orphans.
|
|
123
|
+
const oldTrackedFiles = manifest.files || [];
|
|
124
|
+
const orphans = oldTrackedFiles.filter(
|
|
125
|
+
(f) => f.startsWith('.claude/') && !newTrackedSet.has(f)
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
let removedOrphans = 0;
|
|
129
|
+
for (const orphan of orphans) {
|
|
130
|
+
const fullPath = path.join(targetDir, orphan);
|
|
131
|
+
if (fileExists(fullPath)) {
|
|
132
|
+
try {
|
|
133
|
+
fs.unlinkSync(fullPath);
|
|
134
|
+
removedOrphans++;
|
|
135
|
+
} catch (err) {
|
|
136
|
+
warn(`Could not remove orphan: ${orphan} (${err.message})`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Manifest tracks only pre-shipped groups (project-generated groups describe
|
|
142
|
+
// /replicate Phase 3 outputs, not files this package owns).
|
|
143
|
+
const componentKeys = Object.keys(COMPONENTS).filter(
|
|
144
|
+
(k) => COMPONENTS[k].kind === 'pre-shipped'
|
|
145
|
+
);
|
|
146
|
+
// v1.4.3: refresh shippedDefaults snapshot for the next upgrade to use.
|
|
147
|
+
const shippedDefaults = {};
|
|
148
|
+
const settingsTemplatePath = path.join(templateClaude, 'settings.json');
|
|
149
|
+
if (fileExists(settingsTemplatePath)) {
|
|
150
|
+
const tplContent = readJSON(settingsTemplatePath);
|
|
151
|
+
if (tplContent) shippedDefaults['settings.json'] = tplContent;
|
|
152
|
+
}
|
|
153
|
+
const newManifest = createManifest(newVersion, componentKeys, newTrackedFiles.sort(), shippedDefaults);
|
|
92
154
|
writeManifest(targetDir, newManifest);
|
|
93
155
|
|
|
94
156
|
console.log('');
|
|
95
|
-
|
|
157
|
+
let summary = `Updated ${updated} files to version ${bold(newVersion)}`;
|
|
158
|
+
if (removedOrphans > 0) {
|
|
159
|
+
summary += dim(` (+ ${removedOrphans} orphan${removedOrphans === 1 ? '' : 's'} removed)`);
|
|
160
|
+
}
|
|
161
|
+
success(summary);
|
|
96
162
|
console.log('');
|
|
97
163
|
|
|
98
164
|
process.exit(0);
|