holycodex 0.5.2 → 0.6.1-dev.29547206710.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 +6 -89
- package/THIRD-PARTY-NOTICES.md +6 -2
- package/bin/holycodex.js +1 -1
- package/dist/cli.js +834 -0
- package/package.json +15 -27
- package/marketplace.json +0 -12
- package/plugin/.codex-plugin/plugin.json +0 -23
- package/plugin/.mcp.json +0 -7
- package/plugin/agents/explorer.toml +0 -6
- package/plugin/agents/librarian.toml +0 -6
- package/plugin/agents/worker.toml +0 -6
- package/plugin/hooks/hooks.json +0 -62
- package/plugin/runtime/bootstrap.js +0 -31
- package/plugin/runtime/cli.js +0 -235
- package/plugin/runtime/core-instructions.js +0 -4
- package/plugin/runtime/git-bash.js +0 -355
- package/plugin/runtime/lsp.js +0 -3665
- package/plugin/runtime/mcp-stdio-core.js +0 -209
- package/plugin/runtime/rules.js +0 -171
- package/plugin/skills/ast-grep/LICENSE +0 -21
- package/plugin/skills/ast-grep/SKILL.md +0 -20
- package/plugin/skills/ast-grep/references/cli.md +0 -3
- package/plugin/skills/ast-grep/references/install.md +0 -3
- package/plugin/skills/ast-grep/references/patterns.md +0 -3
- package/plugin/skills/ast-grep/references/pitfalls.md +0 -3
- package/plugin/skills/ast-grep/references/recipes.md +0 -9
- package/plugin/skills/ast-grep/references/sgconfig.md +0 -3
- package/plugin/skills/ast-grep/references/yaml-rules.md +0 -3
- package/plugin/skills/caveman/LICENSE +0 -21
- package/plugin/skills/caveman/SKILL.md +0 -31
- package/plugin/skills/compress/SKILL.md +0 -18
- package/plugin/skills/debugging/SKILL.md +0 -22
- package/plugin/skills/debugging/references/runtimes/README.md +0 -9
- package/plugin/skills/debugging/references/tools/README.md +0 -8
- package/plugin/skills/define-goal/LICENSE.txt +0 -201
- package/plugin/skills/define-goal/SKILL.md +0 -37
- package/plugin/skills/frontend/ATTRIBUTION.md +0 -218
- package/plugin/skills/frontend/LICENSE-Apache-2.0.txt +0 -201
- package/plugin/skills/frontend/SKILL.md +0 -50
- package/plugin/skills/frontend/references/perfection/README.md +0 -5
- package/plugin/skills/frontend/references/perfection/react-perf-tooling.md +0 -3
- package/plugin/skills/frontend/references/ui-ux-db/README.md +0 -5
- package/plugin/skills/frontend/references/ui-ux-db/data/charts.csv +0 -26
- package/plugin/skills/frontend/references/ui-ux-db/data/colors.csv +0 -162
- package/plugin/skills/frontend/references/ui-ux-db/data/icons.csv +0 -106
- package/plugin/skills/frontend/references/ui-ux-db/data/landing.csv +0 -35
- package/plugin/skills/frontend/references/ui-ux-db/data/products.csv +0 -162
- package/plugin/skills/frontend/references/ui-ux-db/data/react-performance.csv +0 -45
- package/plugin/skills/frontend/references/ui-ux-db/data/stacks/astro.csv +0 -54
- package/plugin/skills/frontend/references/ui-ux-db/data/stacks/flutter.csv +0 -53
- package/plugin/skills/frontend/references/ui-ux-db/data/stacks/html-tailwind.csv +0 -56
- package/plugin/skills/frontend/references/ui-ux-db/data/stacks/jetpack-compose.csv +0 -53
- package/plugin/skills/frontend/references/ui-ux-db/data/stacks/nextjs.csv +0 -53
- package/plugin/skills/frontend/references/ui-ux-db/data/stacks/nuxt-ui.csv +0 -51
- package/plugin/skills/frontend/references/ui-ux-db/data/stacks/nuxtjs.csv +0 -59
- package/plugin/skills/frontend/references/ui-ux-db/data/stacks/react-native.csv +0 -52
- package/plugin/skills/frontend/references/ui-ux-db/data/stacks/react.csv +0 -54
- package/plugin/skills/frontend/references/ui-ux-db/data/stacks/shadcn.csv +0 -61
- package/plugin/skills/frontend/references/ui-ux-db/data/stacks/svelte.csv +0 -54
- package/plugin/skills/frontend/references/ui-ux-db/data/stacks/swiftui.csv +0 -51
- package/plugin/skills/frontend/references/ui-ux-db/data/stacks/vue.csv +0 -50
- package/plugin/skills/frontend/references/ui-ux-db/data/styles.csv +0 -85
- package/plugin/skills/frontend/references/ui-ux-db/data/typography.csv +0 -74
- package/plugin/skills/frontend/references/ui-ux-db/data/ui-reasoning.csv +0 -162
- package/plugin/skills/frontend/references/ui-ux-db/data/ux-guidelines.csv +0 -100
- package/plugin/skills/frontend/references/ui-ux-db/data/web-interface.csv +0 -31
- package/plugin/skills/frontend/references/ui-ux-db/scripts/core.py +0 -262
- package/plugin/skills/frontend/references/ui-ux-db/scripts/design_system.py +0 -1148
- package/plugin/skills/frontend/references/ui-ux-db/scripts/search.py +0 -114
- package/plugin/skills/handoff/SKILL.md +0 -10
- package/plugin/skills/lsp/SKILL.md +0 -35
- package/plugin/skills/lsp-setup/SKILL.md +0 -18
- package/plugin/skills/lsp-setup/scripts/detect-lsp.ts +0 -233
- package/plugin/skills/lsp-setup/scripts/lsp-server-table.ts +0 -188
- package/plugin/skills/lsp-setup/scripts/tsconfig.json +0 -17
- package/plugin/skills/lsp-setup/scripts/verify-lsp.ts +0 -166
- package/plugin/skills/plan/SKILL.md +0 -24
- package/plugin/skills/plan-review/SKILL.md +0 -22
- package/plugin/skills/programming/SKILL.md +0 -33
- package/plugin/skills/programming/references/code-smells.md +0 -3
- package/plugin/skills/programming/references/go/README.md +0 -3
- package/plugin/skills/programming/references/logging.md +0 -3
- package/plugin/skills/programming/references/python/README.md +0 -3
- package/plugin/skills/programming/references/rust/README.md +0 -3
- package/plugin/skills/programming/references/rust-ub/README.md +0 -3
- package/plugin/skills/programming/references/typescript/README.md +0 -3
- package/plugin/skills/refactor/SKILL.md +0 -21
- package/plugin/skills/remove-ai-slops/SKILL.md +0 -20
- package/plugin/skills/rules/SKILL.md +0 -16
- package/plugin/skills/security-research/SKILL.md +0 -38
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
//#region packages/mcp-stdio-core/src/record.ts
|
|
2
|
-
function isPlainRecord(value) {
|
|
3
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4
|
-
}
|
|
5
|
-
//#endregion
|
|
6
|
-
//#region packages/mcp-stdio-core/src/responses.ts
|
|
7
|
-
function successResponse(id, result) {
|
|
8
|
-
return {
|
|
9
|
-
jsonrpc: "2.0",
|
|
10
|
-
id,
|
|
11
|
-
result
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
function errorResponse(id, code, message, data) {
|
|
15
|
-
return {
|
|
16
|
-
jsonrpc: "2.0",
|
|
17
|
-
id,
|
|
18
|
-
error: data === void 0 ? {
|
|
19
|
-
code,
|
|
20
|
-
message
|
|
21
|
-
} : {
|
|
22
|
-
code,
|
|
23
|
-
message,
|
|
24
|
-
data
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
function jsonRpcId(value) {
|
|
29
|
-
return typeof value === "string" || typeof value === "number" || value === null ? value : null;
|
|
30
|
-
}
|
|
31
|
-
function messageFromError(error) {
|
|
32
|
-
return error instanceof Error ? error.message : String(error);
|
|
33
|
-
}
|
|
34
|
-
//#endregion
|
|
35
|
-
//#region packages/mcp-stdio-core/src/transport.ts
|
|
36
|
-
var HEADER_SEPARATOR = Buffer.from("\r\n\r\n");
|
|
37
|
-
async function* readStdioJsonRpcMessages(input) {
|
|
38
|
-
let buffer = Buffer.alloc(0);
|
|
39
|
-
for await (const chunk of input) {
|
|
40
|
-
buffer = Buffer.concat([buffer, bufferFromChunk(chunk)]);
|
|
41
|
-
while (true) {
|
|
42
|
-
const result = readNextMessage(buffer);
|
|
43
|
-
if (result.kind === "incomplete") break;
|
|
44
|
-
buffer = result.remaining;
|
|
45
|
-
if (result.message) yield result.message;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
const trailing = buffer.toString("utf8").trim();
|
|
49
|
-
if (trailing.length > 0) yield parseJsonPayload(trailing, "line");
|
|
50
|
-
}
|
|
51
|
-
function writeStdioJsonRpcResponse(output, response, responseMode) {
|
|
52
|
-
const body = JSON.stringify(response);
|
|
53
|
-
if (responseMode === "framed") {
|
|
54
|
-
output.write(`Content-Length: ${Buffer.byteLength(body, "utf8")}\r\n\r\n${body}`);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
output.write(`${body}\n`);
|
|
58
|
-
}
|
|
59
|
-
function readNextMessage(buffer) {
|
|
60
|
-
if (buffer.length === 0) return { kind: "incomplete" };
|
|
61
|
-
return startsWithContentLength(buffer) ? readFramedMessage(buffer) : readLineMessage(buffer);
|
|
62
|
-
}
|
|
63
|
-
function readLineMessage(buffer) {
|
|
64
|
-
const newlineIndex = buffer.indexOf(10);
|
|
65
|
-
if (newlineIndex === -1) return { kind: "incomplete" };
|
|
66
|
-
const line = buffer.subarray(0, newlineIndex).toString("utf8").replace(/\r$/, "");
|
|
67
|
-
if (line.trim().length === 0) return {
|
|
68
|
-
kind: "complete",
|
|
69
|
-
remaining: buffer.subarray(newlineIndex + 1)
|
|
70
|
-
};
|
|
71
|
-
return {
|
|
72
|
-
kind: "complete",
|
|
73
|
-
message: parseJsonPayload(line, "line"),
|
|
74
|
-
remaining: buffer.subarray(newlineIndex + 1)
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
function readFramedMessage(buffer) {
|
|
78
|
-
const separatorIndex = buffer.indexOf(HEADER_SEPARATOR);
|
|
79
|
-
if (separatorIndex === -1) return { kind: "incomplete" };
|
|
80
|
-
const contentLength = parseContentLength(buffer.subarray(0, separatorIndex).toString("ascii"));
|
|
81
|
-
const bodyStart = separatorIndex + HEADER_SEPARATOR.length;
|
|
82
|
-
if (contentLength === void 0) return {
|
|
83
|
-
kind: "complete",
|
|
84
|
-
message: {
|
|
85
|
-
kind: "parse_error",
|
|
86
|
-
message: "Missing or invalid Content-Length header",
|
|
87
|
-
responseMode: "framed"
|
|
88
|
-
},
|
|
89
|
-
remaining: buffer.subarray(bodyStart)
|
|
90
|
-
};
|
|
91
|
-
const bodyEnd = bodyStart + contentLength;
|
|
92
|
-
if (buffer.length < bodyEnd) return { kind: "incomplete" };
|
|
93
|
-
return {
|
|
94
|
-
kind: "complete",
|
|
95
|
-
message: parseJsonPayload(buffer.subarray(bodyStart, bodyEnd).toString("utf8"), "framed"),
|
|
96
|
-
remaining: buffer.subarray(bodyEnd)
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
function startsWithContentLength(buffer) {
|
|
100
|
-
return buffer.subarray(0, 15).toString("ascii").toLowerCase() === "content-length:";
|
|
101
|
-
}
|
|
102
|
-
function parseContentLength(headers) {
|
|
103
|
-
for (const line of headers.split("\r\n")) {
|
|
104
|
-
const match = /^content-length:\s*(\d+)$/i.exec(line);
|
|
105
|
-
if (match === null) continue;
|
|
106
|
-
const value = match[1];
|
|
107
|
-
if (value === void 0) return void 0;
|
|
108
|
-
return Number(value);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
function parseJsonPayload(payload, responseMode) {
|
|
112
|
-
try {
|
|
113
|
-
return {
|
|
114
|
-
kind: "request",
|
|
115
|
-
payload: JSON.parse(payload),
|
|
116
|
-
responseMode
|
|
117
|
-
};
|
|
118
|
-
} catch (error) {
|
|
119
|
-
return {
|
|
120
|
-
kind: "parse_error",
|
|
121
|
-
message: error instanceof Error ? error.message : String(error),
|
|
122
|
-
responseMode
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
function bufferFromChunk(chunk) {
|
|
127
|
-
if (Buffer.isBuffer(chunk)) return chunk;
|
|
128
|
-
if (typeof chunk === "string") return Buffer.from(chunk);
|
|
129
|
-
throw new TypeError(`Unsupported stdio chunk type: ${typeof chunk}`);
|
|
130
|
-
}
|
|
131
|
-
//#endregion
|
|
132
|
-
//#region packages/mcp-stdio-core/src/server.ts
|
|
133
|
-
var DEFAULT_IDLE_TIMEOUT_MS = 10 * 6e4;
|
|
134
|
-
var noopLog = () => {};
|
|
135
|
-
async function runJsonRpcStdioServer(config) {
|
|
136
|
-
const log = config.log ?? noopLog;
|
|
137
|
-
const idleTimeoutMs = config.idleTimeoutMs ?? DEFAULT_IDLE_TIMEOUT_MS;
|
|
138
|
-
const idleTimer = createIdleTimer(idleTimeoutMs, log, config.onIdleTimeout);
|
|
139
|
-
log("stdio_started", {
|
|
140
|
-
cwd: process.cwd(),
|
|
141
|
-
idle_timeout_ms: idleTimeoutMs
|
|
142
|
-
});
|
|
143
|
-
idleTimer.arm();
|
|
144
|
-
try {
|
|
145
|
-
for await (const message of readStdioJsonRpcMessages(config.input)) {
|
|
146
|
-
if (idleTimer.closed()) break;
|
|
147
|
-
idleTimer.arm();
|
|
148
|
-
if (message.kind === "parse_error") {
|
|
149
|
-
handleParseError(message, config, log);
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
await handleRequest(message, config, log);
|
|
153
|
-
}
|
|
154
|
-
} finally {
|
|
155
|
-
idleTimer.clear();
|
|
156
|
-
log("stdio_stopped");
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
function handleParseError(message, config, log) {
|
|
160
|
-
log("parse_error", { message: message.message });
|
|
161
|
-
const response = config.parseErrorResponse?.(message.message) ?? errorResponse(null, -32700, "Parse error", message.message);
|
|
162
|
-
if (response !== void 0) writeStdioJsonRpcResponse(config.output, response, message.responseMode);
|
|
163
|
-
}
|
|
164
|
-
async function handleRequest(message, config, log) {
|
|
165
|
-
const parsed = message.payload;
|
|
166
|
-
const id = isPlainRecord(parsed) ? jsonRpcId(parsed["id"]) : null;
|
|
167
|
-
const method = isPlainRecord(parsed) && typeof parsed["method"] === "string" ? parsed["method"] : null;
|
|
168
|
-
log("request", {
|
|
169
|
-
id: id === null ? null : String(id),
|
|
170
|
-
method
|
|
171
|
-
});
|
|
172
|
-
try {
|
|
173
|
-
const response = await config.handler(parsed, config.handlerOptions);
|
|
174
|
-
if (response === void 0) return;
|
|
175
|
-
writeStdioJsonRpcResponse(config.output, response, message.responseMode);
|
|
176
|
-
log("response", {
|
|
177
|
-
id: String(response.id),
|
|
178
|
-
method,
|
|
179
|
-
is_error: response.error !== void 0
|
|
180
|
-
});
|
|
181
|
-
} catch (error) {
|
|
182
|
-
if (config.onHandlerError === void 0) throw error;
|
|
183
|
-
config.onHandlerError(error);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
|
|
187
|
-
let timer = null;
|
|
188
|
-
let isClosed = false;
|
|
189
|
-
return {
|
|
190
|
-
arm: () => {
|
|
191
|
-
if (timer !== null) clearTimeout(timer);
|
|
192
|
-
if (idleTimeoutMs <= 0) return;
|
|
193
|
-
timer = setTimeout(() => {
|
|
194
|
-
isClosed = true;
|
|
195
|
-
log("idle_timeout", { idle_timeout_ms: idleTimeoutMs });
|
|
196
|
-
onIdleTimeout?.();
|
|
197
|
-
}, idleTimeoutMs);
|
|
198
|
-
timer.unref();
|
|
199
|
-
},
|
|
200
|
-
clear: () => {
|
|
201
|
-
if (timer === null) return;
|
|
202
|
-
clearTimeout(timer);
|
|
203
|
-
timer = null;
|
|
204
|
-
},
|
|
205
|
-
closed: () => isClosed
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
//#endregion
|
|
209
|
-
export { successResponse as a, messageFromError as i, errorResponse as n, isPlainRecord as o, jsonRpcId as r, runJsonRpcStdioServer as t };
|
package/plugin/runtime/rules.js
DELETED
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { t as CORE_INSTRUCTIONS } from "./core-instructions.js";
|
|
3
|
-
import { mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
4
|
-
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
5
|
-
import { stdin, stdout } from "node:process";
|
|
6
|
-
import { tmpdir } from "node:os";
|
|
7
|
-
import { createHash } from "node:crypto";
|
|
8
|
-
//#region src/rules-hook.ts
|
|
9
|
-
var DEFAULT_RULE_LIMIT = 8e3;
|
|
10
|
-
var DEFAULT_RESULT_LIMIT = 24e3;
|
|
11
|
-
var SOURCES = [
|
|
12
|
-
".holycodex/rules",
|
|
13
|
-
".codex/rules",
|
|
14
|
-
".github/instructions"
|
|
15
|
-
];
|
|
16
|
-
async function loadRules(cwd, targetPath) {
|
|
17
|
-
if (process.env.HOLYCODEX_RULES_DISABLED === "1") return [];
|
|
18
|
-
const candidates = [join(cwd, "CONTEXT.md"), join(cwd, ".github", "copilot-instructions.md")];
|
|
19
|
-
for (const source of SOURCES) candidates.push(...await markdownFiles(join(cwd, source)));
|
|
20
|
-
const rules = [];
|
|
21
|
-
const seen = /* @__PURE__ */ new Set();
|
|
22
|
-
let total = 0;
|
|
23
|
-
for (const path of candidates) {
|
|
24
|
-
const text = await readable(path);
|
|
25
|
-
if (text === void 0) continue;
|
|
26
|
-
const parsed = parseRule(text);
|
|
27
|
-
const staticSource = path.endsWith("CONTEXT.md") || path.endsWith("copilot-instructions.md");
|
|
28
|
-
if (!(targetPath === void 0 ? staticSource || parsed.alwaysApply : parsed.globs.some((glob) => globMatches(glob, relative(cwd, targetPath)))) || parsed.body.length === 0) continue;
|
|
29
|
-
const body = parsed.body.slice(0, numberFromEnv("HOLYCODEX_RULES_MAX_RULE_CHARS", DEFAULT_RULE_LIMIT));
|
|
30
|
-
const hash = createHash("sha256").update(body).digest("hex");
|
|
31
|
-
if (seen.has(hash)) continue;
|
|
32
|
-
if (total + body.length > numberFromEnv("HOLYCODEX_RULES_MAX_RESULT_CHARS", DEFAULT_RESULT_LIMIT)) break;
|
|
33
|
-
seen.add(hash);
|
|
34
|
-
total += body.length;
|
|
35
|
-
rules.push({
|
|
36
|
-
path,
|
|
37
|
-
body
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
return rules;
|
|
41
|
-
}
|
|
42
|
-
async function runRulesHook(input) {
|
|
43
|
-
if (typeof input.cwd !== "string" || typeof input.session_id !== "string") return "";
|
|
44
|
-
const event = input.hook_event_name;
|
|
45
|
-
const cache = cachePath(input.session_id);
|
|
46
|
-
if (event === "PostCompact") {
|
|
47
|
-
await rm(cache, { force: true });
|
|
48
|
-
return `${JSON.stringify({ hookSpecificOutput: {
|
|
49
|
-
hookEventName: event,
|
|
50
|
-
additionalContext: CORE_INSTRUCTIONS
|
|
51
|
-
} })}\n`;
|
|
52
|
-
}
|
|
53
|
-
const target = event === "PostToolUse" ? editPath(input.tool_input, input.cwd) : void 0;
|
|
54
|
-
if (event === "PostToolUse" && target === void 0) return "";
|
|
55
|
-
if (event !== "SessionStart" && event !== "UserPromptSubmit" && event !== "PostToolUse") return "";
|
|
56
|
-
const emitted = await filterCached(cache, await loadRules(input.cwd, target), typeof input.transcript_path === "string" ? await readable(input.transcript_path) ?? "" : "");
|
|
57
|
-
if (emitted.length === 0) return "";
|
|
58
|
-
const context = emitted.map((rule) => `Rule ${relative(input.cwd, rule.path)}:\n${rule.body}`).join("\n\n");
|
|
59
|
-
return `${JSON.stringify({ hookSpecificOutput: {
|
|
60
|
-
hookEventName: event,
|
|
61
|
-
additionalContext: context
|
|
62
|
-
} })}\n`;
|
|
63
|
-
}
|
|
64
|
-
function parseRule(text) {
|
|
65
|
-
if (!text.startsWith("---\n")) return {
|
|
66
|
-
alwaysApply: false,
|
|
67
|
-
globs: [],
|
|
68
|
-
body: text.trim()
|
|
69
|
-
};
|
|
70
|
-
const end = text.indexOf("\n---\n", 4);
|
|
71
|
-
if (end < 0) return {
|
|
72
|
-
alwaysApply: false,
|
|
73
|
-
globs: [],
|
|
74
|
-
body: text.trim()
|
|
75
|
-
};
|
|
76
|
-
const header = text.slice(4, end);
|
|
77
|
-
return {
|
|
78
|
-
alwaysApply: /^alwaysApply:\s*true\s*$/m.test(header),
|
|
79
|
-
globs: parseGlobs(header),
|
|
80
|
-
body: text.slice(end + 5).trim()
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
function parseGlobs(header) {
|
|
84
|
-
const lines = header.split("\n");
|
|
85
|
-
const index = lines.findIndex((line) => /^globs\s*:/.test(line));
|
|
86
|
-
if (index < 0) return [];
|
|
87
|
-
const value = lines[index]?.replace(/^globs\s*:\s*/, "").trim() ?? "";
|
|
88
|
-
if (value.length > 0) return (value.startsWith("[") && value.endsWith("]") ? value.slice(1, -1) : value).split(",").map(unquote).filter((glob) => glob.length > 0);
|
|
89
|
-
const globs = [];
|
|
90
|
-
for (const line of lines.slice(index + 1)) {
|
|
91
|
-
const item = /^\s+-\s*(.+?)\s*$/.exec(line)?.[1];
|
|
92
|
-
if (item === void 0) break;
|
|
93
|
-
const glob = unquote(item);
|
|
94
|
-
if (glob.length > 0) globs.push(glob);
|
|
95
|
-
}
|
|
96
|
-
return globs;
|
|
97
|
-
}
|
|
98
|
-
function unquote(value) {
|
|
99
|
-
const trimmed = value.trim();
|
|
100
|
-
return trimmed.length >= 2 && (trimmed.startsWith("\"") && trimmed.endsWith("\"") || trimmed.startsWith("'") && trimmed.endsWith("'")) ? trimmed.slice(1, -1) : trimmed;
|
|
101
|
-
}
|
|
102
|
-
function globMatches(glob, path) {
|
|
103
|
-
const normalized = path.replaceAll("\\", "/");
|
|
104
|
-
const pattern = glob.replaceAll("\\", "/").replace(/[.+^${}()|[\]\\]/g, "\\$&").replaceAll("**", "\0").replaceAll("*", "[^/]*").replaceAll("\0", ".*");
|
|
105
|
-
return new RegExp(`^${pattern}$`).test(normalized);
|
|
106
|
-
}
|
|
107
|
-
async function markdownFiles(root) {
|
|
108
|
-
try {
|
|
109
|
-
const entries = await readdir(root, { withFileTypes: true });
|
|
110
|
-
return (await Promise.all(entries.map(async (entry) => {
|
|
111
|
-
const path = join(root, entry.name);
|
|
112
|
-
return entry.isDirectory() ? markdownFiles(path) : entry.isFile() && entry.name.endsWith(".md") ? [path] : [];
|
|
113
|
-
}))).flat();
|
|
114
|
-
} catch (error) {
|
|
115
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") return [];
|
|
116
|
-
throw error;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
async function readable(path) {
|
|
120
|
-
try {
|
|
121
|
-
return await readFile(path, "utf8");
|
|
122
|
-
} catch (error) {
|
|
123
|
-
if (error instanceof Error && "code" in error && error.code === "ENOENT") return void 0;
|
|
124
|
-
throw error;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
function editPath(value, cwd) {
|
|
128
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
129
|
-
const input = new Map(Object.entries(value));
|
|
130
|
-
for (const key of [
|
|
131
|
-
"filePath",
|
|
132
|
-
"file_path",
|
|
133
|
-
"path",
|
|
134
|
-
"targetPath",
|
|
135
|
-
"target_path"
|
|
136
|
-
]) {
|
|
137
|
-
const candidate = input.get(key);
|
|
138
|
-
if (typeof candidate === "string") return isAbsolute(candidate) ? candidate : resolve(cwd, candidate);
|
|
139
|
-
}
|
|
140
|
-
const patch = typeof input.get("patch") === "string" ? input.get("patch") : typeof input.get("input") === "string" ? input.get("input") : void 0;
|
|
141
|
-
const path = patch === void 0 ? void 0 : /^\*\*\* (?:Add|Update) File: (.+)$/m.exec(patch)?.[1]?.trim();
|
|
142
|
-
return path === void 0 ? void 0 : resolve(cwd, path);
|
|
143
|
-
}
|
|
144
|
-
async function filterCached(path, rules, transcript) {
|
|
145
|
-
const previous = new Set(JSON.parse(await readable(path) ?? "[]"));
|
|
146
|
-
const emitted = rules.filter((rule) => !previous.has(hashRule(rule)) && !transcript.includes(rule.body));
|
|
147
|
-
for (const rule of emitted) previous.add(hashRule(rule));
|
|
148
|
-
await mkdir(dirname(path), { recursive: true });
|
|
149
|
-
await writeFile(path, JSON.stringify([...previous]), "utf8");
|
|
150
|
-
return emitted;
|
|
151
|
-
}
|
|
152
|
-
function hashRule(rule) {
|
|
153
|
-
return createHash("sha256").update(rule.body).digest("hex");
|
|
154
|
-
}
|
|
155
|
-
function cachePath(session) {
|
|
156
|
-
return join(process.env.PLUGIN_DATA ?? join(tmpdir(), "holycodex-plugin-data"), "rules", `${session.replaceAll(/[^A-Za-z0-9._-]/g, "_")}.json`);
|
|
157
|
-
}
|
|
158
|
-
function numberFromEnv(name, fallback) {
|
|
159
|
-
const value = Number(process.env[name]);
|
|
160
|
-
return Number.isInteger(value) && value > 0 ? value : fallback;
|
|
161
|
-
}
|
|
162
|
-
//#endregion
|
|
163
|
-
//#region src/rules-cli.ts
|
|
164
|
-
var raw = "";
|
|
165
|
-
stdin.setEncoding("utf8");
|
|
166
|
-
for await (const chunk of stdin) raw += chunk;
|
|
167
|
-
if (raw.trim()) {
|
|
168
|
-
const input = JSON.parse(raw);
|
|
169
|
-
if (typeof input === "object" && input !== null && !Array.isArray(input)) stdout.write(await runRulesHook(input));
|
|
170
|
-
}
|
|
171
|
-
//#endregion
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Yeongyu Kim
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: ast-grep
|
|
3
|
-
description: Use when a task needs syntax-aware code search or a repeatable structural rewrite across AST-shaped matches; do not use for plain text search, one local edit, or symbol navigation. Produces reviewed deterministic matches or a codemod; unlike LSP it matches syntax, not symbol identity.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# ast-grep
|
|
7
|
-
|
|
8
|
-
Use `sg` for syntax shapes; use `rg` for text.
|
|
9
|
-
|
|
10
|
-
## Flow
|
|
11
|
-
|
|
12
|
-
1. Name language and exact syntax shape.
|
|
13
|
-
2. Start search-only. Use smallest pattern with metavariables.
|
|
14
|
-
3. Inspect match classes; constrain only as needed.
|
|
15
|
-
4. Test rewrite on narrow path. Review diff.
|
|
16
|
-
5. Apply deterministic rewrite. Run formatter, diagnostics, targeted tests.
|
|
17
|
-
|
|
18
|
-
Simple search: `sg run -p '<pattern>' -l <language> <path>`. Use YAML for constraints, relations, or reusable codemods. Review matches before writes. Never regex-replace syntax-bearing code.
|
|
19
|
-
|
|
20
|
-
Load only needed reference: `patterns.md`, `yaml-rules.md`, `recipes.md`, `pitfalls.md`, `sgconfig.md`, `cli.md`, or `install.md`.
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
# Patterns
|
|
2
|
-
|
|
3
|
-
Use code-shaped pattern in target language. `$A` captures one node; `$$$ARGS` captures many; `$_` ignores one. Start concrete, then replace only variable parts. Quote pattern in shell. If syntax fragment cannot parse alone, use `context` plus `selector` in YAML. Verify matches before rewrite.
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
# Pitfalls
|
|
2
|
-
|
|
3
|
-
Text that looks alike may have different AST. Pattern may overmatch nested nodes, comments may not be nodes, and shell may expand `$`. Quote patterns. Specify language. Avoid `$$$` unless arity truly irrelevant. Never write on first query. After rewrite inspect diff and rerun search for leftovers.
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
# Recipes
|
|
2
|
-
|
|
3
|
-
- Call: `pattern: logger.debug($A)`.
|
|
4
|
-
- Any arguments: `pattern: console.log($$$ARGS)`.
|
|
5
|
-
- Empty catch: match `catch ($E) {}` with language-correct syntax.
|
|
6
|
-
- Missing await: match call plus `not`/`inside` constraint excluding await expression.
|
|
7
|
-
- Import migration: match full import/require form; preserve captured module/name in fix.
|
|
8
|
-
|
|
9
|
-
For each recipe: search narrow path, inspect variants, add constraint, dry review, write, format, test.
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
# YAML rules
|
|
2
|
-
|
|
3
|
-
Minimum rule: `id`, `language`, `rule`. Compose with `all`, `any`, `not`; relate with `inside`, `has`, `precedes`, `follows`; constrain captures with `constraints`. Add `fix` only after search result is exact. Keep one semantic purpose per rule. Test positive and negative fixtures with `sg test`.
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Julius Brussee
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: caveman
|
|
3
|
-
description: Use when the user requests caveman mode, terse replies, fewer tokens, or `/caveman`, or when HolyCodex prompt and instruction edits require dense wording; do not remove required detail or silently change ordinary prose tone. Produces constraint-preserving lite, full, ultra, or Wenyan text; unlike compress it controls ongoing voice.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Caveman
|
|
7
|
-
|
|
8
|
-
Write terse. Keep all technical meaning; remove filler.
|
|
9
|
-
|
|
10
|
-
Active every reply until user says `stop caveman` or `normal mode`. Default `full`; switch with `/caveman lite|full|ultra` or Wenyan equivalent.
|
|
11
|
-
|
|
12
|
-
## Rules
|
|
13
|
-
|
|
14
|
-
- Match user's language.
|
|
15
|
-
- Preserve exact technical terms, code, APIs, commands, paths, error text, and commit keywords unless translation requested.
|
|
16
|
-
- Remove articles when clear, filler, pleasantries, hedging, repetition, and decorative formatting. Fragments allowed.
|
|
17
|
-
- Use short familiar words. Keep standard acronyms; invent none. No causal arrows.
|
|
18
|
-
- No self-reference, style announcement, tool narration, or duplicate normal-language recap.
|
|
19
|
-
- Quote only decisive error lines unless more requested.
|
|
20
|
-
- Code, commits, and PR text stay grammatical.
|
|
21
|
-
|
|
22
|
-
Pattern: `[thing] [action] [reason]. [next step].`
|
|
23
|
-
|
|
24
|
-
## Levels
|
|
25
|
-
|
|
26
|
-
- `lite`: grammatical sentences; no filler or hedging.
|
|
27
|
-
- `full`: drop clear articles; fragments and short words allowed.
|
|
28
|
-
- `ultra`: state each fact once; remove safe conjunctions; never shorten technical text.
|
|
29
|
-
- `wenyan-lite|full|ultra`: same levels in semi-classical to fully classical Chinese.
|
|
30
|
-
|
|
31
|
-
Use full grammar for security warnings, irreversible confirmations, ordered steps, ambiguity, or clarification. Resume terse style afterward.
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: compress
|
|
3
|
-
description: Use when a task needs text, prompts, or instructions shortened without losing meaning or constraints; do not use for lossy summaries, code refactors, or ongoing voice changes. Produces a denser constraint-equivalent version; unlike caveman it preserves source tone unless asked otherwise.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Compress
|
|
7
|
-
|
|
8
|
-
1. Identify purpose, audience, format, facts, exact strings, constraints, gates, warnings, links, and tone.
|
|
9
|
-
2. Remove repetition, filler, redundant headings, obvious explanation, decorative wording, and weak transitions.
|
|
10
|
-
3. Merge related rules. Use direct verbs, familiar words, compact lists, and one statement per fact.
|
|
11
|
-
4. Preserve names, code, commands, paths, APIs, errors, numbers, citations, legal terms, user terminology, and order where it matters.
|
|
12
|
-
5. Compare result with source: no lost rule, changed meaning, weaker prohibition, invented claim, broken reference, or scope change.
|
|
13
|
-
|
|
14
|
-
Keep grammar when terseness risks ambiguity, especially for safety, irreversible actions, legal or medical text, and ordered work. Do not adopt caveman voice unless requested.
|
|
15
|
-
|
|
16
|
-
For skills and prompts, preserve frontmatter, triggers, tool and resource routing, permissions, stop conditions, and validation. For code or config, change prose only unless refactoring was requested. Keep evidence and nuance in prose; keep tables only when they improve scanning.
|
|
17
|
-
|
|
18
|
-
Honor requested size or style; otherwise maximize safe reduction. Report size only when useful or requested.
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: debugging
|
|
3
|
-
description: Use when a task involves a crash, wrong result, hang, race, leak, slowdown, or other reproducible defect that needs root-cause proof; do not use for feature work, behavior-preserving refactors, or speculative cleanup. Produces reproduction, evidence, regression proof, and an authorized minimal fix; unlike programming it owns symptom-to-cause diagnosis.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Debugging
|
|
7
|
-
|
|
8
|
-
No guess-fix loop.
|
|
9
|
-
|
|
10
|
-
1. Reproduce exact symptom with smallest command.
|
|
11
|
-
2. Minimize input, environment, and path.
|
|
12
|
-
3. List three plausible cross-layer causes; rank by evidence and cheapest falsifier.
|
|
13
|
-
4. Instrument narrow boundary. Capture values, ordering, ownership, timing, process state.
|
|
14
|
-
5. Disprove causes. Change angle after two failed rounds; add no reviewer or oracle agent.
|
|
15
|
-
6. Confirm root cause with evidence that predicts symptom.
|
|
16
|
-
7. Add failing public-behavior regression test.
|
|
17
|
-
8. Make minimum root fix.
|
|
18
|
-
9. Run targeted test, then proportional suite. Remove temporary instrumentation.
|
|
19
|
-
|
|
20
|
-
No sleeps for async proof; use deterministic events, clocks, traces, debuggers, profilers, sanitizers, or fixtures. Do not implement before proving cause unless user requests mitigation.
|
|
21
|
-
|
|
22
|
-
Load only relevant runtime/tool reference under `references/`. Report reproduction, hypotheses rejected, root cause, proof, fix, checks, residual uncertainty.
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
# Runtime probes
|
|
2
|
-
|
|
3
|
-
- Node/Bun: reproduce with exact runtime/version; enable source maps and warnings; inspect event ordering, handles, promises, memory snapshots, CPU profile. Distinguish module resolution, transpilation, runtime, and external process.
|
|
4
|
-
- Python: use traceback with causes, `faulthandler`, debugger, allocation/profile tools. Check interpreter/env, import path, sync blocking in async loop, resource ownership.
|
|
5
|
-
- Rust/native: debug symbols; backtrace, debugger, sanitizer, Miri, profiler as symptom requires. Check UB, ownership, ABI, signal/thread ordering.
|
|
6
|
-
- Go: race detector, pprof, trace, goroutine dump. Check cancellation, channel ownership, leaked goroutines, copied synchronization.
|
|
7
|
-
- Bundled JS binary: reproduce source and bundle separately; verify target/runtime/externalization and source map. Inspect generated boundary, not minified noise first.
|
|
8
|
-
|
|
9
|
-
Capture smallest evidence that separates hypotheses. Remove probe after proof.
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
# Tool choice
|
|
2
|
-
|
|
3
|
-
- Playwright: deterministic browser flow, console/network/DOM/screenshot evidence.
|
|
4
|
-
- GDB/pwndbg: native crash, registers, stack, memory, heap; local authorized target only.
|
|
5
|
-
- Ghidra: static binary control/data flow when source absent; pair claims with runtime proof when possible.
|
|
6
|
-
- pwntools: local protocol/process PoC with bounded input and timeout; never destructive or third-party.
|
|
7
|
-
|
|
8
|
-
Use least invasive tool that falsifies a hypothesis. Record exact command/version and preserve a safe reproducer.
|