pulse-framework-cli 0.4.3 → 0.4.4

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.
@@ -66,7 +66,7 @@ function registerDoctorCommand(program) {
66
66
  }
67
67
  }
68
68
  // ════════════════════════════════════════════════════════════════════════
69
- // Preset-Verletzungen explizit hinzufügen
69
+ // Add preset limit violations explicitly
70
70
  // ════════════════════════════════════════════════════════════════════════
71
71
  const scope = (0, briefing_js_1.calculateScopeCheck)(config, scan.stats);
72
72
  if (scope.exceeded) {
@@ -38,7 +38,7 @@ function registerResetCommand(program) {
38
38
  }
39
39
  }
40
40
  // ══════════════════════════════════════════════════════════════════════
41
- // Zeige betroffene Commits
41
+ // Show affected commits
42
42
  // ══════════════════════════════════════════════════════════════════════
43
43
  const recentLog = await (0, git_js_1.gitLogOneline)(repoRoot, numCommits + 2);
44
44
  const logLines = recentLog.split("\n").filter((l) => l.trim());
@@ -57,7 +57,7 @@ function registerResetCommand(program) {
57
57
  // eslint-disable-next-line no-console
58
58
  console.log("");
59
59
  // ══════════════════════════════════════════════════════════════════════
60
- // Reset-Modus wählen
60
+ // Choose reset mode
61
61
  // ══════════════════════════════════════════════════════════════════════
62
62
  let mode = "mixed";
63
63
  if (opts.soft) {
@@ -75,7 +75,7 @@ function registerResetCommand(program) {
75
75
  mode = await (0, input_js_1.promptSelect)("Reset mode", choices, "mixed");
76
76
  }
77
77
  // ══════════════════════════════════════════════════════════════════════
78
- // Letzte Bestätigung bei Hard Reset
78
+ // Final confirmation for hard reset
79
79
  // ══════════════════════════════════════════════════════════════════════
80
80
  if (mode === "hard" && !opts.yes) {
81
81
  // eslint-disable-next-line no-console
@@ -88,7 +88,7 @@ function registerResetCommand(program) {
88
88
  }
89
89
  }
90
90
  // ══════════════════════════════════════════════════════════════════════
91
- // Git Reset ausführen
91
+ // Execute git reset
92
92
  // ══════════════════════════════════════════════════════════════════════
93
93
  const resetArg = `HEAD~${numCommits}`;
94
94
  const modeArg = mode === "mixed" ? "" : `--${mode}`;
@@ -102,14 +102,14 @@ function registerResetCommand(program) {
102
102
  process.exit(1);
103
103
  }
104
104
  // ══════════════════════════════════════════════════════════════════════
105
- // Erfolgsmeldung
105
+ // Success message
106
106
  // ══════════════════════════════════════════════════════════════════════
107
107
  const newLog = await (0, git_js_1.gitLogOneline)(repoRoot, 1);
108
108
  // eslint-disable-next-line no-console
109
109
  console.log(`✅ Reset successful!`);
110
110
  // eslint-disable-next-line no-console
111
111
  console.log(`📍 New HEAD: ${newLog}\n`);
112
- // Hinweise
112
+ // Tips
113
113
  if (mode === "soft" || mode === "mixed") {
114
114
  // eslint-disable-next-line no-console
115
115
  console.log(`💡 Tip: Changes are still there.`);
@@ -46,15 +46,15 @@ function registerRunCommand(program) {
46
46
  program
47
47
  .command("run")
48
48
  .description("Combined workflow: Start → Watch → Checkpoints → Review")
49
- .option("-t, --template <id>", "Vorlage: feature, bugfix, refactor, concept, analyze, review")
49
+ .option("-t, --template <id>", "Template: feature, bugfix, refactor, concept, analyze, review")
50
50
  .option("--minutes <n>", "Minutes between checkpoint reminders")
51
51
  .option("--no-watch", "Don't start watcher")
52
- .option("--action <text>", "ACTION direkt angeben")
53
- .option("-C, --clipboard", "Prompt in Zwischenablage kopieren")
52
+ .option("--action <text>", "Provide ACTION directly")
53
+ .option("-C, --clipboard", "Copy prompt to clipboard")
54
54
  .action(async (opts) => {
55
55
  const repoRoot = await (0, paths_js_1.findRepoRoot)(process.cwd());
56
56
  if (!repoRoot)
57
- throw new Error("Nicht in einem Git-Repository.");
57
+ throw new Error("Not in a git repository.");
58
58
  const [state, config] = await Promise.all([(0, artifacts_js_1.loadState)(repoRoot), (0, config_js_1.loadConfig)(repoRoot)]);
59
59
  // Use preset checkpoint interval if not specified
60
60
  const minutes = opts.minutes
@@ -76,17 +76,17 @@ function registerRunCommand(program) {
76
76
  // ════════════════════════════════════════════════════════════════════════
77
77
  // eslint-disable-next-line no-console
78
78
  console.log(`━━━ PHASE 1: Prompt ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
79
- // Template auswählen
79
+ // Select template
80
80
  let template = opts.template ? (0, prompts_js_1.getTemplateById)(opts.template) : undefined;
81
81
  if (!template && !opts.action) {
82
82
  const choices = prompts_js_1.PROMPT_TEMPLATES.map((t) => ({
83
83
  value: t.id,
84
84
  label: `${t.name} - ${t.description}`,
85
85
  }));
86
- const selectedId = await (0, input_js_1.promptSelect)("📋 Vorlage wählen", choices, "feature");
86
+ const selectedId = await (0, input_js_1.promptSelect)("📋 Choose template", choices, "feature");
87
87
  template = (0, prompts_js_1.getTemplateById)(selectedId);
88
88
  }
89
- // 6-Elemente sammeln
89
+ // Collect the 6 elements
90
90
  const el = {
91
91
  role: template?.defaults.role,
92
92
  context: template?.defaults.context,
@@ -95,14 +95,14 @@ function registerRunCommand(program) {
95
95
  };
96
96
  const layer = template?.layer ?? state.profile;
97
97
  if (!el.action?.trim()) {
98
- el.action = await (0, input_js_1.promptText)("⚡ ACTION (Was soll gemacht werden?)", "");
98
+ el.action = await (0, input_js_1.promptText)("⚡ ACTION (what should be built?)", "");
99
99
  }
100
100
  if (!el.context?.trim()) {
101
- el.context = await (0, input_js_1.promptText)("📍 KONTEXT (Projekt, Stack)", "");
101
+ el.context = await (0, input_js_1.promptText)("📍 CONTEXT (project, stack)", "");
102
102
  }
103
- // Prompt generieren
103
+ // Generate prompt
104
104
  const prompt = (0, prompts_js_1.renderSixElementPrompt)(layer, el);
105
- // Artefakt speichern
105
+ // Save artifact
106
106
  const ts = (0, artifacts_js_1.timestampId)();
107
107
  const filename = `${ts}-run.md`;
108
108
  const content = [
@@ -110,7 +110,7 @@ function registerRunCommand(program) {
110
110
  ``,
111
111
  `- Profile: **${presetProfile}**`,
112
112
  `- Layer: **${layer}**`,
113
- template ? `- Vorlage: **${template.name}**` : "",
113
+ template ? `- Template: **${template.name}**` : "",
114
114
  `- Checkpoint interval: **${minutes} min**`,
115
115
  ``,
116
116
  `## Prompt`,
@@ -126,7 +126,7 @@ function registerRunCommand(program) {
126
126
  // eslint-disable-next-line no-console
127
127
  console.log(`✅ Template: ${template?.name ?? "custom"}`);
128
128
  // eslint-disable-next-line no-console
129
- console.log(`✅ Artefakt: ${artifactPath}`);
129
+ console.log(`✅ Artifact: ${artifactPath}`);
130
130
  // Clipboard
131
131
  if (opts.clipboard) {
132
132
  const clipMsg = await (0, clipboard_js_1.copyAndNotify)(prompt);
@@ -148,19 +148,19 @@ function registerRunCommand(program) {
148
148
  // ════════════════════════════════════════════════════════════════════════
149
149
  if (opts.watch === false) {
150
150
  // eslint-disable-next-line no-console
151
- console.log(`━━━ PHASE 2: Übersprungen ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
151
+ console.log(`━━━ PHASE 2: Skipped ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
152
152
  // eslint-disable-next-line no-console
153
153
  console.log("Watcher not started (--no-watch).\n");
154
154
  // eslint-disable-next-line no-console
155
155
  console.log("💡 Next steps:");
156
156
  // eslint-disable-next-line no-console
157
- console.log(" 1. Prompt in Cursor einfügen");
157
+ console.log(" 1. Paste the prompt into Cursor");
158
158
  // eslint-disable-next-line no-console
159
- console.log(" 2. `pulse checkpoint` alle 5-10 Min");
159
+ console.log(" 2. Run `pulse checkpoint` every 5-10 min");
160
160
  // eslint-disable-next-line no-console
161
- console.log(" 3. Bei Problemen: `pulse escalate`");
161
+ console.log(" 3. If stuck: `pulse escalate`");
162
162
  // eslint-disable-next-line no-console
163
- console.log(" 4. Am Ende: `pulse review`\n");
163
+ console.log(" 4. At the end: `pulse review`\n");
164
164
  return;
165
165
  }
166
166
  // eslint-disable-next-line no-console
@@ -170,12 +170,12 @@ function registerRunCommand(program) {
170
170
  // eslint-disable-next-line no-console
171
171
  console.log(`📍 Watcher running... (Ctrl+C to stop)\n`);
172
172
  // eslint-disable-next-line no-console
173
- console.log(` 1. Kopiere den Prompt oben in Cursor`);
173
+ console.log(` 1. Paste the prompt above into Cursor`);
174
174
  // eslint-disable-next-line no-console
175
- console.log(` 2. Arbeite los`);
175
+ console.log(` 2. Start working`);
176
176
  // eslint-disable-next-line no-console
177
- console.log(` 3. Ctrl+C wenn fertig\n`);
178
- await (0, notifications_js_1.notify)(config.notifications, "Pulse Run gestartet", `Checkpoint reminder every ${minutes} min. Ctrl+C to exit.`);
177
+ console.log(` 3. Ctrl+C when done\n`);
178
+ await (0, notifications_js_1.notify)(config.notifications, "Pulse Run started", `Checkpoint reminder every ${minutes} min. Ctrl+C to exit.`);
179
179
  // Update state
180
180
  state.lastCheckpointAt = new Date().toISOString();
181
181
  await (0, artifacts_js_1.saveState)(repoRoot, state);
@@ -200,11 +200,11 @@ function registerRunCommand(program) {
200
200
  // eslint-disable-next-line no-console
201
201
  console.log(`└${"─".repeat(58)}┘`);
202
202
  // eslint-disable-next-line no-console
203
- console.log(` Zeit: ${new Date().toLocaleTimeString()}`);
203
+ console.log(` Time: ${new Date().toLocaleTimeString()}`);
204
204
  // eslint-disable-next-line no-console
205
- console.log(` Status: Uncommitted Changes vorhanden`);
205
+ console.log(` Status: uncommitted changes present`);
206
206
  // eslint-disable-next-line no-console
207
- console.log(` → pulse checkpoint -m 'deine message'\n`);
207
+ console.log(` → pulse checkpoint -m 'your message'\n`);
208
208
  }
209
209
  else {
210
210
  // eslint-disable-next-line no-console
@@ -217,15 +217,15 @@ function registerRunCommand(program) {
217
217
  const cleanup = async (signal) => {
218
218
  clearInterval(interval);
219
219
  // eslint-disable-next-line no-console
220
- console.log(`\n\n━━━ PHASE 3: Abschluss ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
220
+ console.log(`\n\n━━━ PHASE 3: Wrap-up ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
221
221
  // eslint-disable-next-line no-console
222
- console.log(`🛑 ${signal} empfangen - Beende Pulse Run...\n`);
222
+ console.log(`🛑 Received ${signal} - stopping Pulse Run...\n`);
223
223
  // Check final status
224
224
  const status = await (0, git_js_1.gitStatusPorcelain)(repoRoot);
225
225
  const dirty = status.trim().length > 0;
226
226
  if (dirty) {
227
227
  // eslint-disable-next-line no-console
228
- console.log("📝 Uncommitted Changes gefunden:\n");
228
+ console.log("📝 Uncommitted changes found:\n");
229
229
  const stat = await (0, git_js_1.gitDiffStat)(repoRoot);
230
230
  // eslint-disable-next-line no-console
231
231
  console.log(stat);
@@ -243,25 +243,25 @@ function registerRunCommand(program) {
243
243
  }
244
244
  else {
245
245
  // eslint-disable-next-line no-console
246
- console.log("✨ Keine uncommitted Changes.\n");
246
+ console.log("✨ No uncommitted changes.\n");
247
247
  }
248
248
  // Offer review
249
249
  const doReview = await (0, input_js_1.promptConfirm)("Create review?", dirty);
250
250
  if (doReview) {
251
251
  // eslint-disable-next-line no-console
252
- console.log("\n💡 Führe aus: pulse review\n");
252
+ console.log("\n💡 Run: pulse review\n");
253
253
  }
254
254
  // Summary
255
255
  // eslint-disable-next-line no-console
256
256
  console.log(`┌${"─".repeat(58)}┐`);
257
257
  // eslint-disable-next-line no-console
258
- console.log(`│ 👋 Pulse Run beendet${" ".repeat(37)}│`);
258
+ console.log(`│ 👋 Pulse Run finished${" ".repeat(35)}│`);
259
259
  // eslint-disable-next-line no-console
260
260
  console.log(`│ │`);
261
261
  // eslint-disable-next-line no-console
262
262
  console.log(`│ Checkpoints: ${checkpointCount}${" ".repeat(43 - String(checkpointCount).length)}│`);
263
263
  // eslint-disable-next-line no-console
264
- console.log(`│ Artefakt: .pulse/pulses/${filename}${" ".repeat(Math.max(0, 32 - filename.length))}│`);
264
+ console.log(`│ Artifact: .pulse/pulses/${filename}${" ".repeat(Math.max(0, 32 - filename.length))}│`);
265
265
  // eslint-disable-next-line no-console
266
266
  console.log(`└${"─".repeat(58)}┘\n`);
267
267
  process.exit(0);
@@ -75,13 +75,39 @@ fi
75
75
  `, "utf8");
76
76
  await ensureExecutable(postCommit);
77
77
  await promises_1.default.writeFile(prePush, `#!/bin/sh
78
- set -e
79
-
80
78
  # Pulse safeguard: never push without explicit permission.
81
79
  # Allow push explicitly by running:
82
80
  # PULSE_ALLOW_PUSH=1 git push ...
83
81
 
84
- pulse doctor --hook pre-push
82
+ if [ "$PULSE_SKIP_HOOKS" = "1" ]; then
83
+ exit 0
84
+ fi
85
+
86
+ if [ "$PULSE_ALLOW_PUSH" != "1" ]; then
87
+ echo "PULSE safeguard: push blocked. Set PULSE_ALLOW_PUSH=1 for an explicit push."
88
+ exit 1
89
+ fi
90
+
91
+ # Run doctor check if pulse CLI is available
92
+ if command -v pulse >/dev/null 2>&1; then
93
+ pulse doctor --hook pre-push
94
+ EXIT_CODE=$?
95
+ elif command -v pulse-framework >/dev/null 2>&1; then
96
+ pulse-framework doctor --hook pre-push
97
+ EXIT_CODE=$?
98
+ else
99
+ EXIT_CODE=0
100
+ fi
101
+
102
+ # Only block on CRITICAL (exit 2), allow warnings (exit 1) - consistent with pre-commit
103
+ if [ $EXIT_CODE -eq 2 ]; then
104
+ echo ""
105
+ echo "❌ Push blocked by PULSE (critical findings)"
106
+ echo " Fix issues or use: PULSE_SKIP_HOOKS=1 git push ..."
107
+ exit 2
108
+ fi
109
+
110
+ exit 0
85
111
  `, "utf8");
86
112
  await ensureExecutable(prePush);
87
113
  // eslint-disable-next-line no-console
package/dist/lib/input.js CHANGED
@@ -37,7 +37,7 @@ async function promptSelect(label, choices, defaultValue) {
37
37
  });
38
38
  // eslint-disable-next-line no-console
39
39
  console.log("");
40
- const ans = await rl.question("Nummer wählen: ");
40
+ const ans = await rl.question("Enter number: ");
41
41
  const num = parseInt(ans.trim(), 10);
42
42
  if (num >= 1 && num <= choices.length) {
43
43
  const choice = choices[num - 1];
@@ -65,12 +65,13 @@ async function promptSelect(label, choices, defaultValue) {
65
65
  async function promptConfirm(label, defaultYes = false) {
66
66
  const rl = promises_1.default.createInterface({ input: process.stdin, output: process.stdout });
67
67
  try {
68
- const hint = defaultYes ? "(J/n)" : "(j/N)";
68
+ const hint = defaultYes ? "(Y/n)" : "(y/N)";
69
69
  const ans = await rl.question(`${label} ${hint}: `);
70
70
  const v = ans.trim().toLowerCase();
71
- if (v === "j" || v === "y" || v === "ja" || v === "yes")
71
+ // Accept a few common variants (English + legacy German inputs)
72
+ if (v === "y" || v === "yes" || v === "j" || v === "ja")
72
73
  return true;
73
- if (v === "n" || v === "nein" || v === "no")
74
+ if (v === "n" || v === "no" || v === "nein")
74
75
  return false;
75
76
  return defaultYes;
76
77
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pulse-framework-cli",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "Pulse Framework CLI – Guardrails, checkpoints, and escalation for AI-assisted development.",
5
5
  "author": "Manuel Fuß <kontakt@manuel-fuss.de>",
6
6
  "license": "MIT",
@@ -156,4 +156,4 @@ Agent:
156
156
  - Call the tools ACTIVELY, not only when user asks
157
157
  - When uncertain: STOP and ASK
158
158
  - The safeguards ALWAYS apply, even if user argues against them
159
- - **ALWAYS update CHANGELOG.md** after significant changes (features, fixes, changes)
159
+ - **ALWAYS update CHANGELOG.md** after significant changes (features, fixes, changes)