@pipemd-core/pipemd 1.0.0
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/AI_SETUP_PIPEMD.md +184 -0
- package/CHANGELOG.md +47 -0
- package/LICENSE +15 -0
- package/README.md +535 -0
- package/dist/index.js +6647 -0
- package/dist/plugins/opencode-server.js +235 -0
- package/dist/plugins/opencode-tui.js +914 -0
- package/dist/templates/agent-decision-tree.md +113 -0
- package/dist/templates/static-rules.md +7 -0
- package/package.json +68 -0
- package/scripts/C-CPP/architecture/arch.sh +229 -0
- package/scripts/C-CPP/lib/limit.sh +146 -0
- package/scripts/C-CPP/project/class-diagram.sh +96 -0
- package/scripts/C-CPP/project/cmake-targets.sh +68 -0
- package/scripts/C-CPP/project/deps.sh +44 -0
- package/scripts/C-CPP/project/find-todos.sh +6 -0
- package/scripts/C-CPP/project/include-graph.sh +110 -0
- package/scripts/C-CPP/project/interfaces.sh +108 -0
- package/scripts/C-CPP/project/tree.sh +5 -0
- package/scripts/C-CPP/quality/lint.sh +14 -0
- package/scripts/C-CPP/quality/test-summary.sh +22 -0
- package/scripts/C-CPP/quality/type-check.sh +26 -0
- package/scripts/DevOps/architecture/arch.sh +186 -0
- package/scripts/DevOps/devops/aws-context.sh +34 -0
- package/scripts/DevOps/devops/docker-stats.sh +42 -0
- package/scripts/DevOps/devops/k8s-unhealthy.sh +41 -0
- package/scripts/DevOps/devops/tf-state.sh +65 -0
- package/scripts/DevOps/lib/limit.sh +143 -0
- package/scripts/Generic/architecture/arch.sh +570 -0
- package/scripts/Generic/lib/limit.sh +140 -0
- package/scripts/Go/architecture/arch.sh +79 -0
- package/scripts/Go/lib/limit.sh +142 -0
- package/scripts/Go/project/deps.sh +35 -0
- package/scripts/Go/project/find-todos.sh +6 -0
- package/scripts/Go/project/go-interfaces.sh +18 -0
- package/scripts/Go/project/go-packages.sh +28 -0
- package/scripts/Go/project/tree.sh +5 -0
- package/scripts/Go/quality/lint.sh +16 -0
- package/scripts/Go/quality/test-summary.sh +16 -0
- package/scripts/Go/quality/type-check.sh +16 -0
- package/scripts/Node-TypeScript/api/express-routes.sh +14 -0
- package/scripts/Node-TypeScript/api/nest-controllers.sh +18 -0
- package/scripts/Node-TypeScript/architecture/arch.sh +174 -0
- package/scripts/Node-TypeScript/frontend/angular-routes.sh +15 -0
- package/scripts/Node-TypeScript/frontend/nextjs-app-router.sh +13 -0
- package/scripts/Node-TypeScript/frontend/react-components.sh +20 -0
- package/scripts/Node-TypeScript/lib/limit.sh +146 -0
- package/scripts/Node-TypeScript/project/deps.sh +15 -0
- package/scripts/Node-TypeScript/project/find-todos.sh +6 -0
- package/scripts/Node-TypeScript/quality/lint.sh +10 -0
- package/scripts/Node-TypeScript/quality/test-summary.sh +39 -0
- package/scripts/Node-TypeScript/quality/type-check.sh +10 -0
- package/scripts/Python/api/fastapi-routes.sh +12 -0
- package/scripts/Python/architecture/arch.sh +220 -0
- package/scripts/Python/db/django-models.sh +12 -0
- package/scripts/Python/db/sqlalchemy.sh +17 -0
- package/scripts/Python/lib/limit.sh +144 -0
- package/scripts/Python/project/deps.sh +28 -0
- package/scripts/Python/project/find-todos.sh +6 -0
- package/scripts/Python/quality/lint.sh +13 -0
- package/scripts/Python/quality/test-summary.sh +11 -0
- package/scripts/Python/quality/type-check.sh +10 -0
- package/scripts/Rust/architecture/arch.sh +176 -0
- package/scripts/Rust/lib/limit.sh +142 -0
- package/scripts/Rust/project/cargo-deps.sh +42 -0
- package/scripts/Rust/project/cargo-features.sh +26 -0
- package/scripts/Rust/project/find-todos.sh +6 -0
- package/scripts/Rust/project/tree.sh +5 -0
- package/scripts/Rust/quality/lint.sh +16 -0
- package/scripts/Rust/quality/test-summary.sh +16 -0
- package/scripts/Rust/quality/type-check.sh +16 -0
- package/scripts/Shared/api/express-routes.sh +11 -0
- package/scripts/Shared/api/fastapi-routes.sh +10 -0
- package/scripts/Shared/api/nest-controllers.sh +22 -0
- package/scripts/Shared/architecture/normalize.sh +178 -0
- package/scripts/Shared/crew/crew.sh +15 -0
- package/scripts/Shared/db/django-models.sh +11 -0
- package/scripts/Shared/db/prisma.sh +33 -0
- package/scripts/Shared/db/sqlalchemy.sh +12 -0
- package/scripts/Shared/frontend/angular-routes.sh +11 -0
- package/scripts/Shared/frontend/nextjs-app-router.sh +13 -0
- package/scripts/Shared/frontend/react-components.sh +11 -0
- package/scripts/Shared/git/diff-stat.sh +6 -0
- package/scripts/Shared/git/git-branch.sh +16 -0
- package/scripts/Shared/git/git-log.sh +6 -0
- package/scripts/Shared/git/git-status.sh +6 -0
- package/scripts/Shared/lib/limit.sh +144 -0
- package/scripts/Shared/project/compose-md.sh +182 -0
- package/scripts/Shared/project/deps.sh +69 -0
- package/scripts/Shared/project/find-todos.sh +6 -0
- package/scripts/Shared/project/tree.sh +5 -0
- package/scripts/Shared/quality/lint.sh +81 -0
- package/scripts/Shared/quality/test-summary.sh +103 -0
- package/scripts/Shared/quality/type-check.sh +114 -0
- package/scripts/copy-plugins.mjs +4 -0
- package/scripts/copy-templates.mjs +5 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
// pmd-crew — PipeMD Crew coordination plugin.
|
|
2
|
+
// Installed by `pmd crew install-hooks`. Safe to delete manually.
|
|
3
|
+
// @pmd-plugin-version ${PLUGIN_VERSION}
|
|
4
|
+
//
|
|
5
|
+
// Events:
|
|
6
|
+
// tool.execute.before → heartbeat + injection (active mode)
|
|
7
|
+
// tool.execute.after → claim edits + async validation
|
|
8
|
+
// event(session.idle / session.status) → heartbeat + worker cleanup
|
|
9
|
+
// experimental.chat.system.transform → sub-agent detection + LLM context injection
|
|
10
|
+
import { execFile, execFileSync } from "node:child_process";
|
|
11
|
+
import { existsSync, writeFileSync, readFileSync, mkdirSync, renameSync, appendFileSync } from "node:fs";
|
|
12
|
+
import { resolve as resolvePath, join as joinPath } from "node:path";
|
|
13
|
+
|
|
14
|
+
function resolvePmd() {
|
|
15
|
+
const local = resolvePath(process.cwd(), "node_modules/.bin/pmd");
|
|
16
|
+
if (existsSync(local)) return local;
|
|
17
|
+
try {
|
|
18
|
+
const which = execFileSync("which", ["pmd"], { encoding: "utf-8", timeout: 3000 }).trim();
|
|
19
|
+
if (which) return which;
|
|
20
|
+
} catch {}
|
|
21
|
+
return "pmd";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let _cachedBin = "";
|
|
25
|
+
function getPmdBin() {
|
|
26
|
+
if (process.env.PMD_BIN) return process.env.PMD_BIN;
|
|
27
|
+
if (!_cachedBin) { _cachedBin = resolvePmd(); return _cachedBin; }
|
|
28
|
+
if (!existsSync(_cachedBin)) { _cachedBin = resolvePmd(); }
|
|
29
|
+
return _cachedBin;
|
|
30
|
+
}
|
|
31
|
+
const STATS_PATH = joinPath(".pipemd", ".tui-stats.json");
|
|
32
|
+
const ERROR_LOG_PATH = joinPath(".pipemd", ".plugin-errors.log");
|
|
33
|
+
const MAX_ERROR_LINES = 20;
|
|
34
|
+
|
|
35
|
+
const stats = {
|
|
36
|
+
hooksFired: 0,
|
|
37
|
+
claimsMade: 0,
|
|
38
|
+
injectionsDelivered: 0,
|
|
39
|
+
dedupHits: 0,
|
|
40
|
+
deliveryMode: "${DELIVERY_MODE}",
|
|
41
|
+
events: [],
|
|
42
|
+
passiveAgents: [],
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
let coordinatorCrewId = "";
|
|
46
|
+
let coordinatorOcSessionId = "";
|
|
47
|
+
let activeOcSessionId = "";
|
|
48
|
+
const workerSessions = new Map();
|
|
49
|
+
|
|
50
|
+
let lastAgentRefresh = 0;
|
|
51
|
+
|
|
52
|
+
${INJECTION_HELPERS}
|
|
53
|
+
|
|
54
|
+
function extractFilePath(args) {
|
|
55
|
+
return args.path || args.filePath || args.file_path || "";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isEditTool(tool) {
|
|
59
|
+
return /^(write|edit|patch)$/i.test(tool || "");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function logPluginError(handler, err) {
|
|
63
|
+
try {
|
|
64
|
+
mkdirSync(".pipemd", { recursive: true });
|
|
65
|
+
const line = JSON.stringify({ ts: Date.now(), handler, error: String(err?.message || err) }) + "\n";
|
|
66
|
+
appendFileSync(ERROR_LOG_PATH, line, "utf-8");
|
|
67
|
+
const raw = readFileSync(ERROR_LOG_PATH, "utf-8").split("\n").filter(Boolean);
|
|
68
|
+
if (raw.length > MAX_ERROR_LINES) {
|
|
69
|
+
writeFileSync(ERROR_LOG_PATH, raw.slice(-MAX_ERROR_LINES).join("\n") + "\n", "utf-8");
|
|
70
|
+
}
|
|
71
|
+
} catch {}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function refreshAgents() {
|
|
75
|
+
const now = Date.now();
|
|
76
|
+
if (now - lastAgentRefresh < 30000) return;
|
|
77
|
+
lastAgentRefresh = now;
|
|
78
|
+
try {
|
|
79
|
+
const out = execFileSync(getPmdBin(), ["crew", "status", "--json"], { encoding: "utf-8", timeout: 5000 });
|
|
80
|
+
const data = JSON.parse(out);
|
|
81
|
+
stats.passiveAgents = Array.isArray(data.passiveAgents) ? data.passiveAgents : [];
|
|
82
|
+
} catch {}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function writeStats() {
|
|
86
|
+
try {
|
|
87
|
+
mkdirSync(".pipemd", { recursive: true });
|
|
88
|
+
const tmp = STATS_PATH + `.tmp-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
89
|
+
writeFileSync(tmp, JSON.stringify(stats) + "\n", "utf-8");
|
|
90
|
+
renameSync(tmp, STATS_PATH);
|
|
91
|
+
} catch {}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function pushEvent(trigger, tool, file, result, tokens) {
|
|
95
|
+
stats.hooksFired++;
|
|
96
|
+
const sid = getActiveCrewSession();
|
|
97
|
+
const entry = { trigger, tool: tool || "", file: file || "", result, tokens: tokens || 0, ts: new Date().toISOString(), session: sid || undefined };
|
|
98
|
+
if (result === "injected" && stats.lastPayloadFile) {
|
|
99
|
+
entry.payload = stats.lastPayloadFile;
|
|
100
|
+
stats.lastPayloadFile = "";
|
|
101
|
+
}
|
|
102
|
+
stats.events.push(entry);
|
|
103
|
+
if (stats.events.length > 10) stats.events = stats.events.slice(-10);
|
|
104
|
+
writeStats();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function pmd(args) {
|
|
108
|
+
execFile(getPmdBin(), args, (err) => {
|
|
109
|
+
if (process.env.PMD_CREW_DEBUG) console.error("[pmd-crew]", err && err.message);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function pmdSync(args) {
|
|
114
|
+
try {
|
|
115
|
+
return execFileSync(getPmdBin(), args, { encoding: "utf-8", timeout: 10000 }).trim();
|
|
116
|
+
} catch (e) {
|
|
117
|
+
logPluginError("pmdSync", e);
|
|
118
|
+
return "";
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function parseCrewId(out) {
|
|
123
|
+
const m = (out || "").match(/cr_[0-9a-f]+/);
|
|
124
|
+
return m ? m[0] : "";
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function coordinatorAlive() {
|
|
128
|
+
if (!coordinatorCrewId) return false;
|
|
129
|
+
try {
|
|
130
|
+
return existsSync(joinPath(".pipemd", "crew", coordinatorCrewId + ".json"));
|
|
131
|
+
} catch { return false; }
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function join() {
|
|
135
|
+
if (coordinatorAlive()) return;
|
|
136
|
+
coordinatorCrewId = "";
|
|
137
|
+
const out = pmdSync(["crew", "join"]);
|
|
138
|
+
coordinatorCrewId = parseCrewId(out);
|
|
139
|
+
if (!coordinatorOcSessionId) coordinatorOcSessionId = activeOcSessionId;
|
|
140
|
+
refreshAgents();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function claim(filePath) {
|
|
144
|
+
if (typeof filePath !== "string" || !filePath) return;
|
|
145
|
+
stats.claimsMade++;
|
|
146
|
+
const sid = getActiveCrewSession();
|
|
147
|
+
if (sid) pmd(["crew", "claim", filePath, "--session", sid]);
|
|
148
|
+
else pmd(["crew", "claim", filePath]);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function heartbeat() {
|
|
152
|
+
if (!coordinatorAlive()) {
|
|
153
|
+
coordinatorCrewId = "";
|
|
154
|
+
cleanupOrphanedWorkers();
|
|
155
|
+
join();
|
|
156
|
+
}
|
|
157
|
+
const sid = getActiveCrewSession();
|
|
158
|
+
if (sid) pmd(["crew", "heartbeat", "--session", sid]);
|
|
159
|
+
else pmd(["crew", "heartbeat"]);
|
|
160
|
+
refreshAgents();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function getActiveCrewSession() {
|
|
164
|
+
if (activeOcSessionId && workerSessions.has(activeOcSessionId)) {
|
|
165
|
+
return workerSessions.get(activeOcSessionId);
|
|
166
|
+
}
|
|
167
|
+
return coordinatorCrewId || "";
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function handleSessionSwitch(ocSid) {
|
|
171
|
+
if (!ocSid) return;
|
|
172
|
+
if (!activeOcSessionId) {
|
|
173
|
+
activeOcSessionId = ocSid;
|
|
174
|
+
coordinatorOcSessionId = ocSid;
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (ocSid === activeOcSessionId) return;
|
|
178
|
+
if (ocSid === coordinatorOcSessionId) {
|
|
179
|
+
activeOcSessionId = ocSid;
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (!coordinatorAlive()) {
|
|
183
|
+
join();
|
|
184
|
+
if (!coordinatorCrewId) return;
|
|
185
|
+
}
|
|
186
|
+
if (!workerSessions.has(ocSid)) {
|
|
187
|
+
const parentCrewId = workerSessions.has(activeOcSessionId)
|
|
188
|
+
? workerSessions.get(activeOcSessionId)
|
|
189
|
+
: coordinatorCrewId;
|
|
190
|
+
if (!parentCrewId) return;
|
|
191
|
+
const out = pmdSync(["crew", "join", "--role", "worker", "--coordinator", parentCrewId]);
|
|
192
|
+
const wid = parseCrewId(out);
|
|
193
|
+
if (wid) {
|
|
194
|
+
workerSessions.set(ocSid, wid);
|
|
195
|
+
pushEvent("subagent-join", "", "", "worker-joined", 0);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
activeOcSessionId = ocSid;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function leaveWorker(ocSid) {
|
|
202
|
+
const wid = workerSessions.get(ocSid);
|
|
203
|
+
if (wid) {
|
|
204
|
+
workerSessions.delete(ocSid);
|
|
205
|
+
pmd(["crew", "leave", "--session", wid]);
|
|
206
|
+
pushEvent("subagent-leave", "", "", "worker-left", 0);
|
|
207
|
+
}
|
|
208
|
+
if (activeOcSessionId === ocSid) {
|
|
209
|
+
activeOcSessionId = coordinatorOcSessionId;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function cleanupOrphanedWorkers() {
|
|
214
|
+
for (const [ocSid, wid] of workerSessions) {
|
|
215
|
+
try {
|
|
216
|
+
if (!existsSync(joinPath(".pipemd", "crew", wid + ".json"))) {
|
|
217
|
+
workerSessions.delete(ocSid);
|
|
218
|
+
}
|
|
219
|
+
} catch { /* ignore */ }
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
join();
|
|
224
|
+
writeStats();
|
|
225
|
+
setInterval(() => { try { heartbeat(); } catch {} }, 30_000);
|
|
226
|
+
|
|
227
|
+
export default {
|
|
228
|
+
id: "pmd-crew",
|
|
229
|
+
server: async () => ({
|
|
230
|
+
${BEFORE_HANDLER}
|
|
231
|
+
${AFTER_HANDLER}
|
|
232
|
+
${SYSTEM_TRANSFORM}
|
|
233
|
+
${EVENT_HANDLER}
|
|
234
|
+
}),
|
|
235
|
+
};
|