pulse-framework-cli 0.4.1
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/dist/commands/checkpoint.d.ts +2 -0
- package/dist/commands/checkpoint.js +129 -0
- package/dist/commands/correct.d.ts +2 -0
- package/dist/commands/correct.js +77 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +183 -0
- package/dist/commands/escalate.d.ts +2 -0
- package/dist/commands/escalate.js +226 -0
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +570 -0
- package/dist/commands/learn.d.ts +2 -0
- package/dist/commands/learn.js +137 -0
- package/dist/commands/profile.d.ts +2 -0
- package/dist/commands/profile.js +39 -0
- package/dist/commands/reset.d.ts +2 -0
- package/dist/commands/reset.js +130 -0
- package/dist/commands/review.d.ts +2 -0
- package/dist/commands/review.js +129 -0
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +272 -0
- package/dist/commands/start.d.ts +2 -0
- package/dist/commands/start.js +196 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +239 -0
- package/dist/commands/watch.d.ts +2 -0
- package/dist/commands/watch.js +98 -0
- package/dist/hooks/install.d.ts +1 -0
- package/dist/hooks/install.js +89 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +40 -0
- package/dist/lib/artifacts.d.ts +7 -0
- package/dist/lib/artifacts.js +52 -0
- package/dist/lib/briefing.d.ts +77 -0
- package/dist/lib/briefing.js +231 -0
- package/dist/lib/clipboard.d.ts +9 -0
- package/dist/lib/clipboard.js +116 -0
- package/dist/lib/config.d.ts +14 -0
- package/dist/lib/config.js +167 -0
- package/dist/lib/context-export.d.ts +30 -0
- package/dist/lib/context-export.js +149 -0
- package/dist/lib/exec.d.ts +9 -0
- package/dist/lib/exec.js +23 -0
- package/dist/lib/git.d.ts +24 -0
- package/dist/lib/git.js +74 -0
- package/dist/lib/input.d.ts +15 -0
- package/dist/lib/input.js +80 -0
- package/dist/lib/notifications.d.ts +2 -0
- package/dist/lib/notifications.js +25 -0
- package/dist/lib/paths.d.ts +4 -0
- package/dist/lib/paths.js +39 -0
- package/dist/lib/prompts.d.ts +43 -0
- package/dist/lib/prompts.js +270 -0
- package/dist/lib/scanner.d.ts +37 -0
- package/dist/lib/scanner.js +413 -0
- package/dist/lib/types.d.ts +37 -0
- package/dist/lib/types.js +2 -0
- package/package.json +42 -0
- package/templates/.cursorrules +159 -0
- package/templates/AGENTS.md +198 -0
- package/templates/cursor/mcp.json +9 -0
- package/templates/cursor/pulse.mdc +144 -0
- package/templates/roles/architect.cursorrules +15 -0
- package/templates/roles/backend.cursorrules +12 -0
- package/templates/roles/frontend.cursorrules +12 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Briefing Library - Aggregates data for Decision Briefings
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.calculateScopeCheck = calculateScopeCheck;
|
|
7
|
+
exports.calculateRiskSummary = calculateRiskSummary;
|
|
8
|
+
exports.calculateTimeSummary = calculateTimeSummary;
|
|
9
|
+
exports.generateRecommendation = generateRecommendation;
|
|
10
|
+
exports.renderProgressBar = renderProgressBar;
|
|
11
|
+
exports.renderBriefing = renderBriefing;
|
|
12
|
+
/**
|
|
13
|
+
* Calculate scope check against preset limits
|
|
14
|
+
*/
|
|
15
|
+
function calculateScopeCheck(config, stats) {
|
|
16
|
+
const totalLines = stats.linesAdded + stats.linesDeleted;
|
|
17
|
+
const files = {
|
|
18
|
+
current: stats.filesChanged,
|
|
19
|
+
max: config.thresholds.warnMaxFilesChanged,
|
|
20
|
+
percent: Math.round((stats.filesChanged / config.thresholds.warnMaxFilesChanged) * 100),
|
|
21
|
+
};
|
|
22
|
+
const lines = {
|
|
23
|
+
current: totalLines,
|
|
24
|
+
max: config.thresholds.warnMaxLinesChanged,
|
|
25
|
+
percent: Math.round((totalLines / config.thresholds.warnMaxLinesChanged) * 100),
|
|
26
|
+
};
|
|
27
|
+
const deletes = {
|
|
28
|
+
current: stats.linesDeleted,
|
|
29
|
+
max: config.thresholds.warnMaxDeletions,
|
|
30
|
+
percent: Math.round((stats.linesDeleted / config.thresholds.warnMaxDeletions) * 100),
|
|
31
|
+
};
|
|
32
|
+
const exceededFields = [];
|
|
33
|
+
if (files.percent > 100)
|
|
34
|
+
exceededFields.push("files");
|
|
35
|
+
if (lines.percent > 100)
|
|
36
|
+
exceededFields.push("lines");
|
|
37
|
+
if (deletes.percent > 100)
|
|
38
|
+
exceededFields.push("deletes");
|
|
39
|
+
return {
|
|
40
|
+
files,
|
|
41
|
+
lines,
|
|
42
|
+
deletes,
|
|
43
|
+
exceeded: exceededFields.length > 0,
|
|
44
|
+
exceededFields,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Summarize risks from scan results
|
|
49
|
+
*/
|
|
50
|
+
function calculateRiskSummary(scanResult) {
|
|
51
|
+
const criticalCount = scanResult.findings.filter((f) => f.severity === "critical").length;
|
|
52
|
+
const warningCount = scanResult.findings.filter((f) => f.severity === "warn").length;
|
|
53
|
+
const loopSignals = scanResult.findings
|
|
54
|
+
.filter((f) => f.code === "LOOP_SIGNAL")
|
|
55
|
+
.map((f) => f.message);
|
|
56
|
+
let loopRisk = "LOW";
|
|
57
|
+
if (loopSignals.length >= 3) {
|
|
58
|
+
loopRisk = "HIGH";
|
|
59
|
+
}
|
|
60
|
+
else if (loopSignals.length >= 1) {
|
|
61
|
+
loopRisk = "MEDIUM";
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
criticalCount,
|
|
65
|
+
warningCount,
|
|
66
|
+
findings: scanResult.findings,
|
|
67
|
+
loopRisk,
|
|
68
|
+
loopSignals,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Calculate time-based metrics
|
|
73
|
+
*/
|
|
74
|
+
function calculateTimeSummary(lastCheckpointAt, checkpointReminderMinutes) {
|
|
75
|
+
const now = Date.now();
|
|
76
|
+
let minutesSinceCheckpoint = null;
|
|
77
|
+
let checkpointOverdue = false;
|
|
78
|
+
if (lastCheckpointAt) {
|
|
79
|
+
const lastCp = Date.parse(lastCheckpointAt);
|
|
80
|
+
if (Number.isFinite(lastCp)) {
|
|
81
|
+
minutesSinceCheckpoint = Math.floor((now - lastCp) / 60000);
|
|
82
|
+
checkpointOverdue = minutesSinceCheckpoint > checkpointReminderMinutes;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
minutesSinceCheckpoint,
|
|
87
|
+
checkpointOverdue,
|
|
88
|
+
sessionMinutes: minutesSinceCheckpoint, // For now, same as checkpoint
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Generate recommendation based on all factors
|
|
93
|
+
*/
|
|
94
|
+
function generateRecommendation(scope, risk, time) {
|
|
95
|
+
// Critical findings = STOP
|
|
96
|
+
if (risk.criticalCount > 0) {
|
|
97
|
+
return {
|
|
98
|
+
action: "stop",
|
|
99
|
+
reason: `${risk.criticalCount} Critical Finding(s) - Fix vor Merge`,
|
|
100
|
+
command: "pulse doctor",
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
// High loop risk = Escalate
|
|
104
|
+
if (risk.loopRisk === "HIGH") {
|
|
105
|
+
return {
|
|
106
|
+
action: "escalate",
|
|
107
|
+
reason: "High loop risk detected",
|
|
108
|
+
command: "pulse escalate",
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
// Scope exceeded significantly = Checkpoint
|
|
112
|
+
if (scope.exceeded && scope.lines.percent > 150) {
|
|
113
|
+
return {
|
|
114
|
+
action: "checkpoint",
|
|
115
|
+
reason: `Scope exceeded (${scope.lines.current}/${scope.lines.max} Lines)`,
|
|
116
|
+
command: "pulse checkpoint -m 'wip: progress'",
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
// Checkpoint overdue = Checkpoint
|
|
120
|
+
if (time.checkpointOverdue) {
|
|
121
|
+
return {
|
|
122
|
+
action: "checkpoint",
|
|
123
|
+
reason: `${time.minutesSinceCheckpoint} min since last checkpoint`,
|
|
124
|
+
command: "pulse checkpoint -m 'wip: progress'",
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// Medium loop risk = Checkpoint
|
|
128
|
+
if (risk.loopRisk === "MEDIUM") {
|
|
129
|
+
return {
|
|
130
|
+
action: "checkpoint",
|
|
131
|
+
reason: "Loop signals detected - checkpoint recommended",
|
|
132
|
+
command: "pulse checkpoint -m 'wip: checkpoint before continuing'",
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
// Warnings but manageable = Approve with note
|
|
136
|
+
if (risk.warningCount > 0) {
|
|
137
|
+
return {
|
|
138
|
+
action: "approve",
|
|
139
|
+
reason: `${risk.warningCount} Warning(s) - Approve with reservations`,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
// All good
|
|
143
|
+
return {
|
|
144
|
+
action: "approve",
|
|
145
|
+
reason: "No findings, scope OK",
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Render progress bar
|
|
150
|
+
*/
|
|
151
|
+
function renderProgressBar(percent, width = 15) {
|
|
152
|
+
const filled = Math.min(width, Math.round((percent / 100) * width));
|
|
153
|
+
const empty = width - filled;
|
|
154
|
+
const bar = "█".repeat(filled) + "░".repeat(empty);
|
|
155
|
+
return bar;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Render full Decision Briefing to terminal
|
|
159
|
+
*/
|
|
160
|
+
function renderBriefing(briefing) {
|
|
161
|
+
const lines = [];
|
|
162
|
+
const boxWidth = 55;
|
|
163
|
+
const hr = "─".repeat(boxWidth);
|
|
164
|
+
lines.push(`┌${"─".repeat(boxWidth)}┐`);
|
|
165
|
+
lines.push(`│ PULSE Review – Decision Briefing${" ".repeat(boxWidth - 35)}│`);
|
|
166
|
+
lines.push(`├${hr}┤`);
|
|
167
|
+
// Preset + Profile
|
|
168
|
+
const presetProfile = briefing.preset
|
|
169
|
+
? `${briefing.preset}/${briefing.profile}`
|
|
170
|
+
: briefing.profile;
|
|
171
|
+
lines.push(`│ Profile: ${presetProfile}${" ".repeat(boxWidth - 11 - presetProfile.length)}│`);
|
|
172
|
+
lines.push(`├${hr}┤`);
|
|
173
|
+
// Scope Check
|
|
174
|
+
lines.push(`│ SCOPE-CHECK${" ".repeat(boxWidth - 12)}│`);
|
|
175
|
+
const { scope } = briefing;
|
|
176
|
+
const filesBar = renderProgressBar(scope.files.percent);
|
|
177
|
+
const linesBar = renderProgressBar(scope.lines.percent);
|
|
178
|
+
const deletesBar = renderProgressBar(scope.deletes.percent);
|
|
179
|
+
const filesLine = ` Files: ${scope.files.current}/${scope.files.max} (${scope.files.percent}%) ${filesBar}`;
|
|
180
|
+
const linesLine = ` Lines: ${scope.lines.current}/${scope.lines.max} (${scope.lines.percent}%) ${linesBar}`;
|
|
181
|
+
const deletesLine = ` Deletes: ${scope.deletes.current}/${scope.deletes.max} (${scope.deletes.percent}%) ${deletesBar}`;
|
|
182
|
+
lines.push(`│${filesLine}${" ".repeat(Math.max(0, boxWidth - filesLine.length))}│`);
|
|
183
|
+
lines.push(`│${linesLine}${" ".repeat(Math.max(0, boxWidth - linesLine.length))}│`);
|
|
184
|
+
lines.push(`│${deletesLine}${" ".repeat(Math.max(0, boxWidth - deletesLine.length))}│`);
|
|
185
|
+
if (scope.exceeded) {
|
|
186
|
+
const warn = ` ⚠️ Exceeded: ${scope.exceededFields.join(", ")}`;
|
|
187
|
+
lines.push(`│${warn}${" ".repeat(Math.max(0, boxWidth - warn.length))}│`);
|
|
188
|
+
}
|
|
189
|
+
lines.push(`├${hr}┤`);
|
|
190
|
+
// Risk Summary
|
|
191
|
+
lines.push(`│ RISK-SUMMARY${" ".repeat(boxWidth - 13)}│`);
|
|
192
|
+
const { risk, time } = briefing;
|
|
193
|
+
if (risk.criticalCount > 0) {
|
|
194
|
+
const crit = ` 🚨 ${risk.criticalCount} Critical`;
|
|
195
|
+
lines.push(`│${crit}${" ".repeat(Math.max(0, boxWidth - crit.length))}│`);
|
|
196
|
+
}
|
|
197
|
+
if (risk.warningCount > 0) {
|
|
198
|
+
const warn = ` ⚠️ ${risk.warningCount} Warnings`;
|
|
199
|
+
lines.push(`│${warn}${" ".repeat(Math.max(0, boxWidth - warn.length))}│`);
|
|
200
|
+
}
|
|
201
|
+
if (risk.criticalCount === 0 && risk.warningCount === 0) {
|
|
202
|
+
const ok = ` ✅ No findings`;
|
|
203
|
+
lines.push(`│${ok}${" ".repeat(Math.max(0, boxWidth - ok.length))}│`);
|
|
204
|
+
}
|
|
205
|
+
const timeStr = time.minutesSinceCheckpoint !== null
|
|
206
|
+
? ` ⏱️ Checkpoint ${time.minutesSinceCheckpoint} min ago`
|
|
207
|
+
: ` ⏱️ No checkpoint`;
|
|
208
|
+
lines.push(`│${timeStr}${" ".repeat(Math.max(0, boxWidth - timeStr.length))}│`);
|
|
209
|
+
const loopEmoji = risk.loopRisk === "HIGH" ? "🔴" : risk.loopRisk === "MEDIUM" ? "🟡" : "🟢";
|
|
210
|
+
const loopStr = ` ${loopEmoji} Loop Risk: ${risk.loopRisk}`;
|
|
211
|
+
lines.push(`│${loopStr}${" ".repeat(Math.max(0, boxWidth - loopStr.length))}│`);
|
|
212
|
+
lines.push(`├${hr}┤`);
|
|
213
|
+
// Recommendation
|
|
214
|
+
const { recommendation } = briefing;
|
|
215
|
+
const actionEmoji = {
|
|
216
|
+
approve: "✅",
|
|
217
|
+
checkpoint: "⏱️",
|
|
218
|
+
escalate: "🚨",
|
|
219
|
+
stop: "🛑",
|
|
220
|
+
}[recommendation.action];
|
|
221
|
+
const recLine = `${actionEmoji} RECOMMENDATION: ${recommendation.action.toUpperCase()}`;
|
|
222
|
+
lines.push(`│ ${recLine}${" ".repeat(Math.max(0, boxWidth - recLine.length - 1))}│`);
|
|
223
|
+
const reasonLine = ` → ${recommendation.reason}`;
|
|
224
|
+
lines.push(`│${reasonLine}${" ".repeat(Math.max(0, boxWidth - reasonLine.length))}│`);
|
|
225
|
+
if (recommendation.command) {
|
|
226
|
+
const cmdLine = ` → ${recommendation.command}`;
|
|
227
|
+
lines.push(`│${cmdLine}${" ".repeat(Math.max(0, boxWidth - cmdLine.length))}│`);
|
|
228
|
+
}
|
|
229
|
+
lines.push(`└${"─".repeat(boxWidth)}┘`);
|
|
230
|
+
return lines.join("\n");
|
|
231
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copy text to system clipboard (cross-platform)
|
|
3
|
+
* Returns true if successful, false otherwise
|
|
4
|
+
*/
|
|
5
|
+
export declare function copyToClipboard(text: string): Promise<boolean>;
|
|
6
|
+
/**
|
|
7
|
+
* Copy text to clipboard and return status message
|
|
8
|
+
*/
|
|
9
|
+
export declare function copyAndNotify(text: string): Promise<string>;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.copyToClipboard = copyToClipboard;
|
|
40
|
+
exports.copyAndNotify = copyAndNotify;
|
|
41
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
42
|
+
/**
|
|
43
|
+
* Copy text to system clipboard (cross-platform)
|
|
44
|
+
* Returns true if successful, false otherwise
|
|
45
|
+
*/
|
|
46
|
+
async function copyToClipboard(text) {
|
|
47
|
+
const platform = node_os_1.default.platform();
|
|
48
|
+
try {
|
|
49
|
+
if (platform === "darwin") {
|
|
50
|
+
// macOS: pbcopy
|
|
51
|
+
await execWithStdin("pbcopy", [], text);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
if (platform === "win32") {
|
|
55
|
+
// Windows: clip
|
|
56
|
+
await execWithStdin("clip", [], text);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
if (platform === "linux") {
|
|
60
|
+
// Linux: try xclip, then xsel
|
|
61
|
+
try {
|
|
62
|
+
await execWithStdin("xclip", ["-selection", "clipboard"], text);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
try {
|
|
67
|
+
await execWithStdin("xsel", ["--clipboard", "--input"], text);
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Execute a command with stdin input
|
|
83
|
+
*/
|
|
84
|
+
async function execWithStdin(cmd, args, input) {
|
|
85
|
+
const { spawn } = await Promise.resolve().then(() => __importStar(require("node:child_process")));
|
|
86
|
+
return new Promise((resolve, reject) => {
|
|
87
|
+
const proc = spawn(cmd, args, {
|
|
88
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
89
|
+
});
|
|
90
|
+
proc.on("error", reject);
|
|
91
|
+
proc.on("close", (code) => {
|
|
92
|
+
if (code === 0) {
|
|
93
|
+
resolve();
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
reject(new Error(`${cmd} exited with code ${code}`));
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
proc.stdin?.write(input);
|
|
100
|
+
proc.stdin?.end();
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Copy text to clipboard and return status message
|
|
105
|
+
*/
|
|
106
|
+
async function copyAndNotify(text) {
|
|
107
|
+
const success = await copyToClipboard(text);
|
|
108
|
+
if (success) {
|
|
109
|
+
return "📋 In Zwischenablage kopiert!";
|
|
110
|
+
}
|
|
111
|
+
const platform = node_os_1.default.platform();
|
|
112
|
+
if (platform === "linux") {
|
|
113
|
+
return "⚠️ Clipboard not available (xclip/xsel not installed)";
|
|
114
|
+
}
|
|
115
|
+
return "⚠️ Could not copy to clipboard";
|
|
116
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PulseConfig, PresetName, PresetConfig } from "./types.js";
|
|
2
|
+
export declare const PRESETS: Record<PresetName, PresetConfig>;
|
|
3
|
+
export declare const DEFAULT_CONFIG: PulseConfig;
|
|
4
|
+
export declare function loadConfig(repoRoot: string): Promise<PulseConfig>;
|
|
5
|
+
export declare function writeDefaultConfig(repoRoot: string): Promise<void>;
|
|
6
|
+
export declare function normalizeConfig(input: any): PulseConfig;
|
|
7
|
+
/**
|
|
8
|
+
* Get all available preset names
|
|
9
|
+
*/
|
|
10
|
+
export declare function getPresetNames(): PresetName[];
|
|
11
|
+
/**
|
|
12
|
+
* Get preset configuration by name
|
|
13
|
+
*/
|
|
14
|
+
export declare function getPreset(name: PresetName): PresetConfig;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DEFAULT_CONFIG = exports.PRESETS = void 0;
|
|
7
|
+
exports.loadConfig = loadConfig;
|
|
8
|
+
exports.writeDefaultConfig = writeDefaultConfig;
|
|
9
|
+
exports.normalizeConfig = normalizeConfig;
|
|
10
|
+
exports.getPresetNames = getPresetNames;
|
|
11
|
+
exports.getPreset = getPreset;
|
|
12
|
+
const promises_1 = __importDefault(require("node:fs/promises"));
|
|
13
|
+
const paths_js_1 = require("./paths.js");
|
|
14
|
+
// ════════════════════════════════════════════════════════════════════════════
|
|
15
|
+
// TEAM PRESETS
|
|
16
|
+
// ════════════════════════════════════════════════════════════════════════════
|
|
17
|
+
exports.PRESETS = {
|
|
18
|
+
frontend: {
|
|
19
|
+
warnMaxFilesChanged: 10,
|
|
20
|
+
warnMaxLinesChanged: 200,
|
|
21
|
+
warnMaxDeletions: 30,
|
|
22
|
+
checkpointReminderMinutes: 20,
|
|
23
|
+
extraSecretPatterns: [
|
|
24
|
+
"NEXT_PUBLIC_",
|
|
25
|
+
"VITE_",
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
backend: {
|
|
29
|
+
warnMaxFilesChanged: 15,
|
|
30
|
+
warnMaxLinesChanged: 400,
|
|
31
|
+
warnMaxDeletions: 50,
|
|
32
|
+
checkpointReminderMinutes: 30,
|
|
33
|
+
extraSecretPatterns: [
|
|
34
|
+
"DATABASE_URL",
|
|
35
|
+
"REDIS_URL",
|
|
36
|
+
"SMTP_",
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
fullstack: {
|
|
40
|
+
warnMaxFilesChanged: 20,
|
|
41
|
+
warnMaxLinesChanged: 500,
|
|
42
|
+
warnMaxDeletions: 60,
|
|
43
|
+
checkpointReminderMinutes: 25,
|
|
44
|
+
},
|
|
45
|
+
monorepo: {
|
|
46
|
+
warnMaxFilesChanged: 30,
|
|
47
|
+
warnMaxLinesChanged: 800,
|
|
48
|
+
warnMaxDeletions: 100,
|
|
49
|
+
checkpointReminderMinutes: 30,
|
|
50
|
+
},
|
|
51
|
+
custom: {
|
|
52
|
+
warnMaxFilesChanged: 15,
|
|
53
|
+
warnMaxLinesChanged: 300,
|
|
54
|
+
warnMaxDeletions: 50,
|
|
55
|
+
checkpointReminderMinutes: 30,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
exports.DEFAULT_CONFIG = {
|
|
59
|
+
version: 1,
|
|
60
|
+
projectType: "unknown",
|
|
61
|
+
enforcement: "mixed",
|
|
62
|
+
notifications: "both",
|
|
63
|
+
thresholds: {
|
|
64
|
+
warnMaxFilesChanged: 15,
|
|
65
|
+
warnMaxLinesChanged: 300,
|
|
66
|
+
warnMaxDeletions: 50,
|
|
67
|
+
},
|
|
68
|
+
patterns: {
|
|
69
|
+
// Keep these as regex source strings (without surrounding / /).
|
|
70
|
+
// We intentionally include a few high-signal patterns + a generic catch-all.
|
|
71
|
+
secret: [
|
|
72
|
+
"AKIA[0-9A-Z]{16}", // AWS access key id
|
|
73
|
+
"ASIA[0-9A-Z]{16}", // AWS temporary key id
|
|
74
|
+
"ghp_[A-Za-z0-9]{36}", // GitHub classic token
|
|
75
|
+
"github_pat_[A-Za-z0-9_]{80,}", // GitHub fine-grained token
|
|
76
|
+
"sk_(live|test)_[A-Za-z0-9]{16,}", // Stripe
|
|
77
|
+
"AIzaSy[A-Za-z0-9_-]{30,}", // Google API key
|
|
78
|
+
"(?i)(api[_-]?key|secret|token|password)\\s*[:=]\\s*['\\\"][^'\\\"]{8,}['\\\"]", // generic
|
|
79
|
+
"(?i)-----BEGIN (RSA|EC|OPENSSH) PRIVATE KEY-----",
|
|
80
|
+
],
|
|
81
|
+
prodUrl: [
|
|
82
|
+
// any http(s) URL that isn't localhost or example domains
|
|
83
|
+
"https?://(?!localhost\\b)(?!127\\.0\\.0\\.1\\b)(?!0\\.0\\.0\\.0\\b)(?!example\\.com\\b)[^\\s'\\\"]+",
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
commands: {
|
|
87
|
+
test: "",
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
async function loadConfig(repoRoot) {
|
|
91
|
+
const p = (0, paths_js_1.configFile)(repoRoot);
|
|
92
|
+
try {
|
|
93
|
+
const raw = await promises_1.default.readFile(p, "utf8");
|
|
94
|
+
const parsed = JSON.parse(raw);
|
|
95
|
+
return normalizeConfig(parsed);
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return exports.DEFAULT_CONFIG;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function writeDefaultConfig(repoRoot) {
|
|
102
|
+
const p = (0, paths_js_1.configFile)(repoRoot);
|
|
103
|
+
await promises_1.default.writeFile(p, JSON.stringify(exports.DEFAULT_CONFIG, null, 2) + "\n", "utf8");
|
|
104
|
+
}
|
|
105
|
+
function normalizeConfig(input) {
|
|
106
|
+
// Apply preset defaults first if preset is set
|
|
107
|
+
const presetName = input?.preset;
|
|
108
|
+
const presetDefaults = presetName && exports.PRESETS[presetName] ? exports.PRESETS[presetName] : null;
|
|
109
|
+
const merged = {
|
|
110
|
+
...exports.DEFAULT_CONFIG,
|
|
111
|
+
...(typeof input === "object" && input ? input : {}),
|
|
112
|
+
thresholds: {
|
|
113
|
+
...exports.DEFAULT_CONFIG.thresholds,
|
|
114
|
+
// Apply preset thresholds
|
|
115
|
+
...(presetDefaults ? {
|
|
116
|
+
warnMaxFilesChanged: presetDefaults.warnMaxFilesChanged,
|
|
117
|
+
warnMaxLinesChanged: presetDefaults.warnMaxLinesChanged,
|
|
118
|
+
warnMaxDeletions: presetDefaults.warnMaxDeletions,
|
|
119
|
+
} : {}),
|
|
120
|
+
// User overrides take precedence
|
|
121
|
+
...(input?.thresholds ?? {}),
|
|
122
|
+
},
|
|
123
|
+
patterns: {
|
|
124
|
+
...exports.DEFAULT_CONFIG.patterns,
|
|
125
|
+
...(input?.patterns ?? {}),
|
|
126
|
+
// Merge extra secret patterns from preset
|
|
127
|
+
secret: [
|
|
128
|
+
...exports.DEFAULT_CONFIG.patterns.secret,
|
|
129
|
+
...(presetDefaults?.extraSecretPatterns ?? []),
|
|
130
|
+
...(input?.patterns?.secret ?? []),
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
commands: {
|
|
134
|
+
...exports.DEFAULT_CONFIG.commands,
|
|
135
|
+
...(input?.commands ?? {}),
|
|
136
|
+
},
|
|
137
|
+
// Apply preset checkpoint reminder
|
|
138
|
+
checkpointReminderMinutes: input?.checkpointReminderMinutes
|
|
139
|
+
?? presetDefaults?.checkpointReminderMinutes
|
|
140
|
+
?? 30,
|
|
141
|
+
};
|
|
142
|
+
// Defensive: ensure required shapes
|
|
143
|
+
merged.version = 1;
|
|
144
|
+
merged.preset = presetName;
|
|
145
|
+
if (!["advisory", "mixed", "strict"].includes(merged.enforcement)) {
|
|
146
|
+
merged.enforcement = exports.DEFAULT_CONFIG.enforcement;
|
|
147
|
+
}
|
|
148
|
+
if (!["terminal", "macos", "both"].includes(merged.notifications)) {
|
|
149
|
+
merged.notifications = exports.DEFAULT_CONFIG.notifications;
|
|
150
|
+
}
|
|
151
|
+
if (!["node", "python", "unknown"].includes(merged.projectType)) {
|
|
152
|
+
merged.projectType = exports.DEFAULT_CONFIG.projectType;
|
|
153
|
+
}
|
|
154
|
+
return merged;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Get all available preset names
|
|
158
|
+
*/
|
|
159
|
+
function getPresetNames() {
|
|
160
|
+
return Object.keys(exports.PRESETS);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Get preset configuration by name
|
|
164
|
+
*/
|
|
165
|
+
function getPreset(name) {
|
|
166
|
+
return exports.PRESETS[name] ?? exports.PRESETS.custom;
|
|
167
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Export - Selective file export for escalation
|
|
3
|
+
*/
|
|
4
|
+
export type FileExport = {
|
|
5
|
+
path: string;
|
|
6
|
+
content: string;
|
|
7
|
+
lines: number;
|
|
8
|
+
};
|
|
9
|
+
export type ContextExport = {
|
|
10
|
+
files: FileExport[];
|
|
11
|
+
totalFiles: number;
|
|
12
|
+
totalLines: number;
|
|
13
|
+
truncated: boolean;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Read and export specific files
|
|
17
|
+
*/
|
|
18
|
+
export declare function exportFiles(repoRoot: string, patterns: string[]): Promise<ContextExport>;
|
|
19
|
+
/**
|
|
20
|
+
* Auto-detect relevant files from git diff
|
|
21
|
+
*/
|
|
22
|
+
export declare function autoDetectFiles(repoRoot: string, diffNameStatus: string): Promise<string[]>;
|
|
23
|
+
/**
|
|
24
|
+
* Render context export as markdown
|
|
25
|
+
*/
|
|
26
|
+
export declare function renderContextExport(ctx: ContextExport): string;
|
|
27
|
+
/**
|
|
28
|
+
* Render context export in XML-style for AI models
|
|
29
|
+
*/
|
|
30
|
+
export declare function renderContextExportXml(ctx: ContextExport): string;
|