@hopla/claude-setup 1.4.4 → 1.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +57 -62
- package/files/commands/hopla-prime.md +1 -1
- package/files/commands/hopla-review-plan.md +2 -3
- package/files/commands/hopla-system-review.md +28 -0
- package/files/skills/hopla-code-review/SKILL.md +1 -1
- package/files/skills/hopla-git/SKILL.md +3 -3
- package/files/skills/hopla-prime/SKILL.md +2 -2
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -72,17 +72,38 @@ function removeFile(dest, label) {
|
|
|
72
72
|
async function uninstall() {
|
|
73
73
|
log(`\n${BOLD}@hopla/claude-setup${RESET} — Uninstall\n`);
|
|
74
74
|
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
const srcEntries = fs.readdirSync(path.join(FILES_DIR, "commands"));
|
|
76
|
+
const srcFiles = srcEntries.filter((f) =>
|
|
77
|
+
fs.statSync(path.join(FILES_DIR, "commands", f)).isFile()
|
|
78
|
+
);
|
|
79
|
+
const srcDirs = srcEntries.filter((f) =>
|
|
80
|
+
fs.statSync(path.join(FILES_DIR, "commands", f)).isDirectory()
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
const itemsToRemove = [
|
|
84
|
+
{ dest: path.join(CLAUDE_DIR, "CLAUDE.md"), label: "~/.claude/CLAUDE.md", isDir: false },
|
|
85
|
+
...srcFiles.map((file) => ({
|
|
79
86
|
dest: path.join(COMMANDS_DIR, file),
|
|
80
87
|
label: `~/.claude/commands/${file}`,
|
|
88
|
+
isDir: false,
|
|
89
|
+
})),
|
|
90
|
+
...srcDirs.map((dir) => ({
|
|
91
|
+
dest: path.join(COMMANDS_DIR, dir),
|
|
92
|
+
label: `~/.claude/commands/${dir}/`,
|
|
93
|
+
isDir: true,
|
|
81
94
|
})),
|
|
82
95
|
];
|
|
83
96
|
|
|
84
|
-
|
|
85
|
-
|
|
97
|
+
// Also remove skills and hooks installed by hopla
|
|
98
|
+
if (fs.existsSync(SKILLS_DIR)) {
|
|
99
|
+
itemsToRemove.push({ dest: SKILLS_DIR, label: "~/.claude/skills/", isDir: true });
|
|
100
|
+
}
|
|
101
|
+
if (fs.existsSync(HOOKS_DIR)) {
|
|
102
|
+
itemsToRemove.push({ dest: HOOKS_DIR, label: "~/.claude/hooks/", isDir: true });
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
log(`The following will be removed:`);
|
|
106
|
+
for (const { label } of itemsToRemove) {
|
|
86
107
|
log(` ${RED}✕${RESET} ${label}`);
|
|
87
108
|
}
|
|
88
109
|
|
|
@@ -93,40 +114,34 @@ async function uninstall() {
|
|
|
93
114
|
}
|
|
94
115
|
|
|
95
116
|
log("");
|
|
96
|
-
for (const { dest, label } of
|
|
97
|
-
|
|
117
|
+
for (const { dest, label, isDir } of itemsToRemove) {
|
|
118
|
+
if (fs.existsSync(dest)) {
|
|
119
|
+
fs.rmSync(dest, { recursive: isDir });
|
|
120
|
+
log(` ${RED}✕${RESET} Removed: ${label}`);
|
|
121
|
+
} else {
|
|
122
|
+
log(` ${YELLOW}↷${RESET} Not found: ${label}`);
|
|
123
|
+
}
|
|
98
124
|
}
|
|
99
125
|
|
|
100
126
|
log(`\n${GREEN}${BOLD}Done!${RESET} Files removed.\n`);
|
|
101
127
|
}
|
|
102
128
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
"commit.md",
|
|
107
|
-
"create-prd.md",
|
|
108
|
-
"execute.md",
|
|
109
|
-
"execution-report.md",
|
|
110
|
-
"plan-feature.md",
|
|
111
|
-
"prime.md",
|
|
112
|
-
"system-review.md",
|
|
113
|
-
"hopla-lang.md",
|
|
114
|
-
"hopla-commit.md",
|
|
115
|
-
];
|
|
116
|
-
|
|
117
|
-
function removeLegacyFiles() {
|
|
129
|
+
function removeStaleCommands(currentCommandFiles) {
|
|
130
|
+
if (!fs.existsSync(COMMANDS_DIR)) return;
|
|
131
|
+
const currentSet = new Set(currentCommandFiles);
|
|
118
132
|
const removed = [];
|
|
119
|
-
for (const file of
|
|
120
|
-
const
|
|
121
|
-
if (fs.
|
|
122
|
-
|
|
133
|
+
for (const file of fs.readdirSync(COMMANDS_DIR)) {
|
|
134
|
+
const filePath = path.join(COMMANDS_DIR, file);
|
|
135
|
+
if (!fs.statSync(filePath).isFile()) continue;
|
|
136
|
+
if (file.startsWith("hopla-") && !currentSet.has(file)) {
|
|
137
|
+
fs.rmSync(filePath);
|
|
123
138
|
removed.push(file);
|
|
124
139
|
}
|
|
125
140
|
}
|
|
126
141
|
if (removed.length > 0) {
|
|
127
|
-
log(`${CYAN}
|
|
142
|
+
log(`${CYAN}Removing stale commands from previous versions...${RESET}`);
|
|
128
143
|
for (const file of removed) {
|
|
129
|
-
log(` ${YELLOW}↷${RESET} Removed
|
|
144
|
+
log(` ${YELLOW}↷${RESET} Removed: ~/.claude/commands/${file}`);
|
|
130
145
|
}
|
|
131
146
|
log("");
|
|
132
147
|
}
|
|
@@ -142,25 +157,6 @@ const PLANNING_COMMANDS = [
|
|
|
142
157
|
"hopla-git-pr.md",
|
|
143
158
|
];
|
|
144
159
|
|
|
145
|
-
function removeExecutionCommands() {
|
|
146
|
-
const planningSet = new Set(PLANNING_COMMANDS);
|
|
147
|
-
const removed = [];
|
|
148
|
-
if (!fs.existsSync(COMMANDS_DIR)) return;
|
|
149
|
-
for (const file of fs.readdirSync(COMMANDS_DIR)) {
|
|
150
|
-
if (file.startsWith("hopla-") && !planningSet.has(file)) {
|
|
151
|
-
fs.rmSync(path.join(COMMANDS_DIR, file));
|
|
152
|
-
removed.push(file);
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
if (removed.length > 0) {
|
|
156
|
-
log(`${CYAN}Removing execution commands (planning mode)...${RESET}`);
|
|
157
|
-
for (const file of removed) {
|
|
158
|
-
log(` ${RED}✕${RESET} Removed: ~/.claude/commands/${file}`);
|
|
159
|
-
}
|
|
160
|
-
log("");
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
160
|
async function install() {
|
|
165
161
|
const modeLabel = PLANNING ? "Planning Mode (Robert)" : "Full Install";
|
|
166
162
|
log(`\n${BOLD}@hopla/claude-setup${RESET} — Agentic Coding System ${CYAN}[${modeLabel}]${RESET}\n`);
|
|
@@ -169,20 +165,7 @@ async function install() {
|
|
|
169
165
|
fs.mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
170
166
|
fs.mkdirSync(COMMANDS_DIR, { recursive: true });
|
|
171
167
|
|
|
172
|
-
//
|
|
173
|
-
removeLegacyFiles();
|
|
174
|
-
|
|
175
|
-
// In planning mode, remove any execution commands left from a previous full install
|
|
176
|
-
if (PLANNING) removeExecutionCommands();
|
|
177
|
-
|
|
178
|
-
log(`${CYAN}Installing global rules...${RESET}`);
|
|
179
|
-
await installFile(
|
|
180
|
-
path.join(FILES_DIR, "CLAUDE.md"),
|
|
181
|
-
path.join(CLAUDE_DIR, "CLAUDE.md"),
|
|
182
|
-
"~/.claude/CLAUDE.md"
|
|
183
|
-
);
|
|
184
|
-
|
|
185
|
-
log(`\n${CYAN}Installing commands...${RESET}`);
|
|
168
|
+
// Determine which command files will be installed
|
|
186
169
|
const allCommandEntries = fs.readdirSync(path.join(FILES_DIR, "commands"));
|
|
187
170
|
const allCommandFiles = allCommandEntries.filter((f) => {
|
|
188
171
|
const stat = fs.statSync(path.join(FILES_DIR, "commands", f));
|
|
@@ -195,6 +178,18 @@ async function install() {
|
|
|
195
178
|
const commandFiles = PLANNING
|
|
196
179
|
? allCommandFiles.filter((f) => PLANNING_COMMANDS.includes(f))
|
|
197
180
|
: allCommandFiles;
|
|
181
|
+
|
|
182
|
+
// Remove stale hopla-* commands not in the current version
|
|
183
|
+
removeStaleCommands(commandFiles);
|
|
184
|
+
|
|
185
|
+
log(`${CYAN}Installing global rules...${RESET}`);
|
|
186
|
+
await installFile(
|
|
187
|
+
path.join(FILES_DIR, "CLAUDE.md"),
|
|
188
|
+
path.join(CLAUDE_DIR, "CLAUDE.md"),
|
|
189
|
+
"~/.claude/CLAUDE.md"
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
log(`\n${CYAN}Installing commands...${RESET}`);
|
|
198
193
|
for (const file of commandFiles.sort()) {
|
|
199
194
|
await installFile(
|
|
200
195
|
path.join(FILES_DIR, "commands", file),
|
|
@@ -53,6 +53,6 @@ Pending plans:
|
|
|
53
53
|
- add-user-authentication.md ← ready to execute with /hopla-execute
|
|
54
54
|
```
|
|
55
55
|
|
|
56
|
-
End with a sentence like: "
|
|
56
|
+
End with a sentence like: "All caught up — what are we working on today?" (adapt to the user's language).
|
|
57
57
|
|
|
58
58
|
Do NOT use headers in the prose summary. Write it as natural, friendly prose, then the pending plans list if applicable.
|
|
@@ -41,9 +41,8 @@ Do NOT reproduce the full plan. Instead, present a structured summary in the use
|
|
|
41
41
|
|
|
42
42
|
## Step 3: Ask for Approval
|
|
43
43
|
|
|
44
|
-
After the summary, ask:
|
|
45
|
-
> "
|
|
46
|
-
> (or in English if the conversation is in English)
|
|
44
|
+
After the summary, ask (in the user's language):
|
|
45
|
+
> "All clear? You can approve to execute, request changes, or ask questions about any task."
|
|
47
46
|
|
|
48
47
|
**Review loop:**
|
|
49
48
|
- If the user has questions → answer them based on the plan content
|
|
@@ -10,6 +10,22 @@ Perform a meta-level analysis of how well the implementation followed the plan.
|
|
|
10
10
|
- **$1** — Path to the structured plan file
|
|
11
11
|
- **$2** — Path to the execution report file
|
|
12
12
|
|
|
13
|
+
## Step 0: Check for Existing Review
|
|
14
|
+
|
|
15
|
+
Before doing anything else, check if a system review already exists for this plan:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
ls .agents/system-reviews/
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Look for a file that matches the feature name derived from **$1** (the plan filename). If a matching review exists:
|
|
22
|
+
- Skip Steps 1–6
|
|
23
|
+
- Go directly to **Step 7** to archive the plan
|
|
24
|
+
|
|
25
|
+
If no matching review exists, continue with Step 1.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
13
29
|
## Step 1: Load All Context
|
|
14
30
|
|
|
15
31
|
Read these four artifacts in order:
|
|
@@ -137,3 +153,15 @@ After the analysis, use this to prioritize actions:
|
|
|
137
153
|
| Same manual step done 3+ times | Create a new command |
|
|
138
154
|
| Plan was ambiguous in the same spot twice | Update plan-feature command |
|
|
139
155
|
| First time seeing this issue | Note it, don't over-engineer yet |
|
|
156
|
+
|
|
157
|
+
## Step 7: Archive the Plan
|
|
158
|
+
|
|
159
|
+
Move the completed plan to the archive folder:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
mkdir -p .agents/plans/done
|
|
163
|
+
mv "$1" .agents/plans/done/
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Then notify the user:
|
|
167
|
+
> "✅ System review saved to `.agents/system-reviews/[feature]-review.md`. Plan archived to `.agents/plans/done/[plan-name].md` — the active plans folder is now clean."
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hopla-code-review
|
|
3
|
-
description: Performs a technical code review on recently changed files. Use when the user says "review the code", "
|
|
3
|
+
description: Performs a technical code review on recently changed files. Use when the user says "review the code", "code review", "check my code", "look for issues", or asks for feedback on their implementation.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Perform a technical code review focused on finding real bugs and issues.
|
|
@@ -7,13 +7,13 @@ Detect the user's intent and execute the appropriate git workflow.
|
|
|
7
7
|
|
|
8
8
|
## Intent Detection
|
|
9
9
|
|
|
10
|
-
**If the user wants to commit** (keywords: "commit", "
|
|
10
|
+
**If the user wants to commit** (keywords: "commit", "save changes", "create commit"):
|
|
11
11
|
- Read and follow the instructions in `commit.md` (located in the same directory as this skill)
|
|
12
12
|
|
|
13
|
-
**If the user wants to create a PR or push** (keywords: "PR", "pull request", "
|
|
13
|
+
**If the user wants to create a PR or push** (keywords: "PR", "pull request", "push", "merge request"):
|
|
14
14
|
- Read and follow the instructions in `pr.md` (located in the same directory as this skill)
|
|
15
15
|
|
|
16
|
-
**If unclear**, ask the user one short question: "
|
|
16
|
+
**If unclear**, ask the user one short question: "Commit or Pull Request?"
|
|
17
17
|
|
|
18
18
|
## File References
|
|
19
19
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hopla-prime
|
|
3
|
-
description: Orients Claude in a project at the start of a session. Use when the user says "orient yourself", "get oriented", "prime yourself", "
|
|
3
|
+
description: Orients Claude in a project at the start of a session. Use when the user says "orient yourself", "get oriented", "prime yourself", "what is this project", "load context", or asks Claude to familiarize itself with the codebase before starting work.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
Get oriented in this project before doing any work.
|
|
@@ -54,6 +54,6 @@ Pending plans:
|
|
|
54
54
|
- add-user-authentication.md ← ready to execute with /hopla-execute
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
End with a sentence like: "
|
|
57
|
+
End with a sentence like: "All caught up — what are we working on today?" (adapt to the user's language).
|
|
58
58
|
|
|
59
59
|
Do NOT use headers in the prose summary. Write it as natural, friendly prose, then the pending plans list if applicable.
|