@minhpnq1807/contextos 0.1.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/.agents/plugins/marketplace.json +20 -0
- package/CHANGELOG.md +16 -0
- package/DEMO.md +57 -0
- package/LICENSE +21 -0
- package/README.md +388 -0
- package/bin/ctx.js +261 -0
- package/package.json +63 -0
- package/plugins/ctx/.codex-plugin/plugin.json +35 -0
- package/plugins/ctx/.mcp.json +10 -0
- package/plugins/ctx/bin/on-prompt.js +25 -0
- package/plugins/ctx/bin/on-session-start.js +22 -0
- package/plugins/ctx/bin/on-stop.js +17 -0
- package/plugins/ctx/hooks.json +35 -0
- package/plugins/ctx/lib/analyzer.js +321 -0
- package/plugins/ctx/lib/ctx-mcp-client.js +52 -0
- package/plugins/ctx/lib/embedding-scorer.js +248 -0
- package/plugins/ctx/lib/file-embedding-retriever.js +116 -0
- package/plugins/ctx/lib/fs-utils.js +28 -0
- package/plugins/ctx/lib/global-hooks.js +110 -0
- package/plugins/ctx/lib/graph-retriever.js +226 -0
- package/plugins/ctx/lib/hook-io.js +65 -0
- package/plugins/ctx/lib/import-graph.js +124 -0
- package/plugins/ctx/lib/measure.js +263 -0
- package/plugins/ctx/lib/prompt-hook.js +72 -0
- package/plugins/ctx/lib/reader.js +57 -0
- package/plugins/ctx/lib/reporter.js +105 -0
- package/plugins/ctx/lib/scheduler.js +45 -0
- package/plugins/ctx/lib/score-context.js +55 -0
- package/plugins/ctx/lib/stats.js +127 -0
- package/plugins/ctx/lib/stop-hook.js +32 -0
- package/plugins/ctx/mcp/contextos-server.js +50 -0
- package/plugins/ctx/mcp/server.js +83 -0
package/bin/ctx.js
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { execFileSync } from "node:child_process";
|
|
6
|
+
|
|
7
|
+
import { readAgentsChain } from "../plugins/ctx/lib/reader.js";
|
|
8
|
+
import { parseRules, scoreRules } from "../plugins/ctx/lib/analyzer.js";
|
|
9
|
+
import { scheduleContext } from "../plugins/ctx/lib/scheduler.js";
|
|
10
|
+
import { formatEvidence, formatReport } from "../plugins/ctx/lib/reporter.js";
|
|
11
|
+
import { installGlobalHooks } from "../plugins/ctx/lib/global-hooks.js";
|
|
12
|
+
import { formatStats, loadStats } from "../plugins/ctx/lib/stats.js";
|
|
13
|
+
import { modelCacheDir, warmRuleEmbeddings } from "../plugins/ctx/lib/embedding-scorer.js";
|
|
14
|
+
import { warmFileEmbeddings } from "../plugins/ctx/lib/file-embedding-retriever.js";
|
|
15
|
+
import { scoreContext } from "../plugins/ctx/lib/score-context.js";
|
|
16
|
+
|
|
17
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
const rootDir = path.resolve(__dirname, "..");
|
|
19
|
+
const pluginSourceDir = path.join(rootDir, "plugins", "ctx");
|
|
20
|
+
|
|
21
|
+
function usage() {
|
|
22
|
+
return `ContextOS (ctx)
|
|
23
|
+
|
|
24
|
+
Usage:
|
|
25
|
+
ctx install
|
|
26
|
+
ctx install --quiet
|
|
27
|
+
ctx install --inject
|
|
28
|
+
ctx install --copy
|
|
29
|
+
ctx debug -- "task"
|
|
30
|
+
ctx report
|
|
31
|
+
ctx evidence
|
|
32
|
+
ctx stats
|
|
33
|
+
ctx embeddings warm -- "task"
|
|
34
|
+
ctx --version
|
|
35
|
+
`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function codexHome() {
|
|
39
|
+
return process.env.CODEX_HOME || path.join(process.env.HOME || process.cwd(), ".codex");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function copyDir(src, dest) {
|
|
43
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
44
|
+
for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
|
|
45
|
+
const srcPath = path.join(src, entry.name);
|
|
46
|
+
const destPath = path.join(dest, entry.name);
|
|
47
|
+
if (entry.isDirectory()) {
|
|
48
|
+
copyDir(srcPath, destPath);
|
|
49
|
+
} else if (entry.isFile()) {
|
|
50
|
+
fs.copyFileSync(srcPath, destPath);
|
|
51
|
+
fs.chmodSync(destPath, fs.statSync(srcPath).mode);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function copyPath(src, dest) {
|
|
57
|
+
const stat = fs.statSync(src);
|
|
58
|
+
if (stat.isDirectory()) {
|
|
59
|
+
copyDir(src, dest);
|
|
60
|
+
} else {
|
|
61
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
62
|
+
fs.copyFileSync(src, dest);
|
|
63
|
+
fs.chmodSync(dest, stat.mode);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function copyInstall() {
|
|
68
|
+
const target = path.join(codexHome(), "plugins", "ctx");
|
|
69
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
70
|
+
copyDir(pluginSourceDir, target);
|
|
71
|
+
console.log(`Installed ctx plugin to ${target}`);
|
|
72
|
+
console.log("Restart Codex if it was already running, then submit a task to trigger ContextOS.");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function install({ copy = false, inject = true } = {}) {
|
|
76
|
+
if (copy) {
|
|
77
|
+
copyInstall();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const marketplaceRoot = path.join(codexHome(), "marketplaces", "contextos");
|
|
82
|
+
fs.rmSync(marketplaceRoot, { recursive: true, force: true });
|
|
83
|
+
for (const entry of [".agents", "bin", "plugins", "package.json", "package-lock.json", "README.md", "LICENSE", "node_modules"]) {
|
|
84
|
+
const src = path.join(rootDir, entry);
|
|
85
|
+
if (fs.existsSync(src)) copyPath(src, path.join(marketplaceRoot, entry));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
tryRunCodex(["plugin", "remove", "ctx@contextos"]);
|
|
89
|
+
tryRunCodex(["plugin", "marketplace", "remove", "contextos"]);
|
|
90
|
+
tryRunCodex(["mcp", "remove", "ctx-mcp"]);
|
|
91
|
+
runCodex(["plugin", "marketplace", "add", marketplaceRoot]);
|
|
92
|
+
runCodex(["plugin", "add", "ctx@contextos"]);
|
|
93
|
+
runCodex(["mcp", "add", "ctx-mcp", "--", "node", path.join(marketplaceRoot, "plugins", "ctx", "mcp", "server.js")]);
|
|
94
|
+
const hooksPath = installGlobalHooks({ codexHome: codexHome(), marketplaceRoot, injectPromptContext: inject });
|
|
95
|
+
|
|
96
|
+
console.log("Preparing required local embedding model...");
|
|
97
|
+
const warmResult = await warmInstallEmbeddings();
|
|
98
|
+
console.log("Installed ctx through Codex plugin marketplace.");
|
|
99
|
+
console.log(`Stable marketplace root: ${marketplaceRoot}`);
|
|
100
|
+
console.log(`Installed ContextOS global hooks to ${hooksPath}`);
|
|
101
|
+
console.log("Installed ctx-mcp MCP server.");
|
|
102
|
+
console.log(`Embedding model cache: ${modelCacheDir(contextOSDataDir())}`);
|
|
103
|
+
console.log(`Embedding vectors cache: ${warmResult.cachePath}`);
|
|
104
|
+
console.log(`File path embeddings warmed: ${warmResult.fileCount || 0}`);
|
|
105
|
+
console.log(`Prompt context injection: ${inject ? "enabled" : "quiet logging only"}`);
|
|
106
|
+
console.log("Restart Codex if it was already running, then submit a task to trigger ContextOS.");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function warmInstallEmbeddings() {
|
|
110
|
+
const dataDir = contextOSDataDir();
|
|
111
|
+
const result = await warmRuleEmbeddings({
|
|
112
|
+
rules: [
|
|
113
|
+
{ content: "Always use project rules that are semantically relevant to the user prompt." },
|
|
114
|
+
{ content: "Find code files by meaning, imports, graph relationships, and task intent." },
|
|
115
|
+
{ content: "Use local embeddings to bridge natural language and code vocabulary mismatch." }
|
|
116
|
+
],
|
|
117
|
+
task: "kiểm duyệt upload moderation semantic code search",
|
|
118
|
+
dataDir,
|
|
119
|
+
sources: [],
|
|
120
|
+
allowRemote: true
|
|
121
|
+
});
|
|
122
|
+
const fileResult = await warmFileEmbeddings({
|
|
123
|
+
cwd: process.cwd(),
|
|
124
|
+
dataDir,
|
|
125
|
+
allowRemote: true
|
|
126
|
+
});
|
|
127
|
+
return { ...result, fileCount: fileResult.count };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function tryRunCodex(args) {
|
|
131
|
+
try {
|
|
132
|
+
execFileSync("codex", args, { stdio: "ignore" });
|
|
133
|
+
} catch {
|
|
134
|
+
// Best effort cleanup for repeat installs.
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function runCodex(args) {
|
|
139
|
+
try {
|
|
140
|
+
execFileSync("codex", args, { stdio: "inherit" });
|
|
141
|
+
} catch (error) {
|
|
142
|
+
const status = typeof error.status === "number" ? error.status : 1;
|
|
143
|
+
throw new Error(`codex ${args.join(" ")} failed with exit code ${status}. Make sure Codex CLI is installed and authenticated.`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function loadLastReport() {
|
|
148
|
+
const candidates = [
|
|
149
|
+
process.env.PLUGIN_DATA && path.join(process.env.PLUGIN_DATA, "last-report.json"),
|
|
150
|
+
path.join(codexHome(), "contextos", "last-report.json"),
|
|
151
|
+
path.join(codexHome(), "marketplaces", "contextos", "plugins", "ctx", ".data", "last-report.json"),
|
|
152
|
+
path.join(codexHome(), "plugins", "ctx", ".data", "last-report.json"),
|
|
153
|
+
path.join(process.cwd(), ".contextos", "last-report.json")
|
|
154
|
+
].filter(Boolean);
|
|
155
|
+
|
|
156
|
+
for (const candidate of candidates) {
|
|
157
|
+
if (fs.existsSync(candidate)) {
|
|
158
|
+
return JSON.parse(fs.readFileSync(candidate, "utf8"));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
throw new Error("No ContextOS report found. Run a Codex task with the ctx plugin enabled first.");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function contextOSDataDir() {
|
|
165
|
+
return process.env.PLUGIN_DATA || path.join(codexHome(), "contextos");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
async function debug(task) {
|
|
169
|
+
const cwd = process.cwd();
|
|
170
|
+
const scored = await scoreContext({
|
|
171
|
+
cwd,
|
|
172
|
+
prompt: task,
|
|
173
|
+
dataDir: contextOSDataDir(),
|
|
174
|
+
maxFiles: 3,
|
|
175
|
+
embeddingTimeoutMs: Number(process.env.CONTEXTOS_EMBEDDING_DEBUG_TIMEOUT_MS || 5000)
|
|
176
|
+
});
|
|
177
|
+
const rules = scored.scoredRules;
|
|
178
|
+
const relevantFiles = scored.suggestedFiles.slice(0, 3);
|
|
179
|
+
const scheduled = scheduleContext({ rules, relevantFiles });
|
|
180
|
+
|
|
181
|
+
console.log("ContextOS debug");
|
|
182
|
+
console.log(`cwd: ${cwd}`);
|
|
183
|
+
console.log(`rules: ${rules.length}`);
|
|
184
|
+
console.log(`mcp scorer: ${scored.telemetry.modelStatus}${scored.telemetry.model ? ` (${scored.telemetry.model})` : ""}`);
|
|
185
|
+
console.log(`elapsed: ${scored.telemetry.elapsedMs}ms`);
|
|
186
|
+
console.log("");
|
|
187
|
+
for (const rule of rules.slice(0, 20)) {
|
|
188
|
+
console.log(`${rule.score.toFixed(2)} ${rule.content}`);
|
|
189
|
+
if (rule.reasons.length) console.log(` reasons: ${rule.reasons.join(", ")}`);
|
|
190
|
+
}
|
|
191
|
+
if (rules.length > 20) console.log(`... ${rules.length - 20} more rules`);
|
|
192
|
+
console.log("");
|
|
193
|
+
console.log("Suggested files:");
|
|
194
|
+
for (const file of relevantFiles) {
|
|
195
|
+
const source = file.source ? ` source:${file.source}` : "";
|
|
196
|
+
const reasons = file.reasons?.length ? ` reasons:${file.reasons.join(", ")}` : "";
|
|
197
|
+
console.log(`${Number(file.score || 0).toFixed(2)} ${file.path}${source}${reasons}`);
|
|
198
|
+
}
|
|
199
|
+
if (!relevantFiles.length) console.log("(none)");
|
|
200
|
+
console.log("");
|
|
201
|
+
console.log("Final additionalContext:");
|
|
202
|
+
console.log(scheduled.additionalContext || "(empty)");
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async function warmEmbeddings(task) {
|
|
206
|
+
const cwd = process.cwd();
|
|
207
|
+
const merged = readAgentsChain({ cwd });
|
|
208
|
+
const rules = scoreRules(parseRules(merged.content), task, []);
|
|
209
|
+
const result = await warmRuleEmbeddings({
|
|
210
|
+
rules,
|
|
211
|
+
task,
|
|
212
|
+
dataDir: contextOSDataDir(),
|
|
213
|
+
sources: merged.sources,
|
|
214
|
+
allowRemote: true
|
|
215
|
+
});
|
|
216
|
+
const fileResult = await warmFileEmbeddings({
|
|
217
|
+
cwd,
|
|
218
|
+
dataDir: contextOSDataDir(),
|
|
219
|
+
allowRemote: true
|
|
220
|
+
});
|
|
221
|
+
console.log(`Warmed ${result.count} embeddings`);
|
|
222
|
+
console.log(`Warmed ${fileResult.count} file path embeddings`);
|
|
223
|
+
console.log(`Cache: ${result.cachePath}`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const args = process.argv.slice(2);
|
|
227
|
+
const command = args[0];
|
|
228
|
+
|
|
229
|
+
try {
|
|
230
|
+
if (!command || command === "--help" || command === "-h") {
|
|
231
|
+
console.log(usage());
|
|
232
|
+
} else if (command === "--version" || command === "-v") {
|
|
233
|
+
console.log("0.1.0");
|
|
234
|
+
} else if (command === "install") {
|
|
235
|
+
await install({ copy: args.includes("--copy"), inject: !args.includes("--quiet") });
|
|
236
|
+
} else if (command === "debug") {
|
|
237
|
+
const marker = args.indexOf("--");
|
|
238
|
+
const task = marker >= 0 ? args.slice(marker + 1).join(" ") : args.slice(1).join(" ");
|
|
239
|
+
if (!task.trim()) throw new Error('Usage: ctx debug -- "task"');
|
|
240
|
+
await debug(task);
|
|
241
|
+
} else if (command === "embeddings") {
|
|
242
|
+
if (args[1] === "warm") {
|
|
243
|
+
const marker = args.indexOf("--");
|
|
244
|
+
const task = marker >= 0 ? args.slice(marker + 1).join(" ") : args.slice(2).join(" ");
|
|
245
|
+
await warmEmbeddings(task);
|
|
246
|
+
} else {
|
|
247
|
+
throw new Error(`Unknown embeddings command: ${args[1] || ""}\n\n${usage()}`);
|
|
248
|
+
}
|
|
249
|
+
} else if (command === "report") {
|
|
250
|
+
console.log(formatReport(loadLastReport()));
|
|
251
|
+
} else if (command === "evidence") {
|
|
252
|
+
console.log(formatEvidence(loadLastReport()));
|
|
253
|
+
} else if (command === "stats") {
|
|
254
|
+
console.log(formatStats(loadStats(contextOSDataDir())));
|
|
255
|
+
} else {
|
|
256
|
+
throw new Error(`Unknown command: ${command}\n\n${usage()}`);
|
|
257
|
+
}
|
|
258
|
+
} catch (error) {
|
|
259
|
+
console.error(error.message);
|
|
260
|
+
process.exitCode = 1;
|
|
261
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@minhpnq1807/contextos",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Task-aware AGENTS.md context injection and compliance reporting for Codex.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ctx": "bin/ctx.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/",
|
|
11
|
+
"plugins/",
|
|
12
|
+
".agents/",
|
|
13
|
+
"README.md",
|
|
14
|
+
"DEMO.md",
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"CHANGELOG.md"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"build": "node bin/ctx.js --version",
|
|
21
|
+
"validate:plugin": "node test/validate-plugin.js",
|
|
22
|
+
"test:mcp": "node test/mcp-protocol-smoke.js"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=20"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"vitest": "^3.2.4"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"codex",
|
|
32
|
+
"plugin",
|
|
33
|
+
"agents",
|
|
34
|
+
"context",
|
|
35
|
+
"hooks",
|
|
36
|
+
"codex-cli",
|
|
37
|
+
"mcp",
|
|
38
|
+
"semantic-search",
|
|
39
|
+
"agents-md",
|
|
40
|
+
"developer-tools"
|
|
41
|
+
],
|
|
42
|
+
"author": {
|
|
43
|
+
"name": "ContextOS"
|
|
44
|
+
},
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
48
|
+
"@xenova/transformers": "^2.17.2",
|
|
49
|
+
"sql.js": "^1.14.1",
|
|
50
|
+
"zod": "^4.4.3"
|
|
51
|
+
},
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "git+https://github.com/khovan123/contextOS.git"
|
|
55
|
+
},
|
|
56
|
+
"bugs": {
|
|
57
|
+
"url": "https://github.com/khovan123/contextOS/issues"
|
|
58
|
+
},
|
|
59
|
+
"homepage": "https://github.com/khovan123/contextOS#readme",
|
|
60
|
+
"publishConfig": {
|
|
61
|
+
"access": "public"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ctx",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Inject task-relevant AGENTS.md rules into Codex through plugin hooks.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "ContextOS"
|
|
7
|
+
},
|
|
8
|
+
"homepage": "https://github.com/contextos/ctx",
|
|
9
|
+
"repository": "https://github.com/contextos/ctx",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"keywords": [
|
|
12
|
+
"codex",
|
|
13
|
+
"context",
|
|
14
|
+
"agents",
|
|
15
|
+
"hooks"
|
|
16
|
+
],
|
|
17
|
+
"mcpServers": ".mcp.json",
|
|
18
|
+
"interface": {
|
|
19
|
+
"displayName": "ContextOS",
|
|
20
|
+
"shortDescription": "Task-aware AGENTS.md context injection for Codex",
|
|
21
|
+
"longDescription": "ContextOS reads AGENTS.md guidance, schedules the most relevant rules for the current task, injects them into Codex through hooks, and reports which rules appear to be followed.",
|
|
22
|
+
"developerName": "ContextOS",
|
|
23
|
+
"category": "Coding",
|
|
24
|
+
"capabilities": [
|
|
25
|
+
"Interactive",
|
|
26
|
+
"Read"
|
|
27
|
+
],
|
|
28
|
+
"defaultPrompt": [
|
|
29
|
+
"Inject relevant AGENTS.md rules into my Codex task",
|
|
30
|
+
"Show which rules Codex followed after a task"
|
|
31
|
+
],
|
|
32
|
+
"brandColor": "#534AB7",
|
|
33
|
+
"screenshots": []
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readStdinJson, writeJson, failOpen, logDebug, pluginDataDir } from "../lib/hook-io.js";
|
|
3
|
+
import { handlePromptPayload } from "../lib/prompt-hook.js";
|
|
4
|
+
|
|
5
|
+
const started = Date.now();
|
|
6
|
+
|
|
7
|
+
try {
|
|
8
|
+
const payload = await readStdinJson();
|
|
9
|
+
|
|
10
|
+
logDebug("UserPromptSubmit", payload);
|
|
11
|
+
writeJson(await handlePromptPayload(payload, {
|
|
12
|
+
dataPath: pluginDataDir("last-prompt-context.json"),
|
|
13
|
+
historyPath: pluginDataDir("prompt-history.jsonl"),
|
|
14
|
+
started
|
|
15
|
+
}));
|
|
16
|
+
} catch (error) {
|
|
17
|
+
failOpen("UserPromptSubmit", error, {
|
|
18
|
+
continue: true,
|
|
19
|
+
suppressOutput: true,
|
|
20
|
+
hookSpecificOutput: {
|
|
21
|
+
hookEventName: "UserPromptSubmit",
|
|
22
|
+
additionalContext: ""
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readStdinJson, writeJson, failOpen, logDebug } from "../lib/hook-io.js";
|
|
3
|
+
|
|
4
|
+
try {
|
|
5
|
+
const payload = await readStdinJson();
|
|
6
|
+
logDebug("SessionStart", payload);
|
|
7
|
+
writeJson({
|
|
8
|
+
continue: true,
|
|
9
|
+
suppressOutput: true,
|
|
10
|
+
hookSpecificOutput: {
|
|
11
|
+
hookEventName: "SessionStart"
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
} catch (error) {
|
|
15
|
+
failOpen("SessionStart", error, {
|
|
16
|
+
continue: true,
|
|
17
|
+
suppressOutput: true,
|
|
18
|
+
hookSpecificOutput: {
|
|
19
|
+
hookEventName: "SessionStart"
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readStdinJson, writeJson, failOpen, logDebug, pluginDataDir } from "../lib/hook-io.js";
|
|
3
|
+
import { handleStopPayload } from "../lib/stop-hook.js";
|
|
4
|
+
|
|
5
|
+
try {
|
|
6
|
+
const payload = await readStdinJson();
|
|
7
|
+
logDebug("Stop", payload);
|
|
8
|
+
writeJson(handleStopPayload(payload, {
|
|
9
|
+
contextPath: pluginDataDir("last-prompt-context.json"),
|
|
10
|
+
reportPath: pluginDataDir("last-report.json"),
|
|
11
|
+
historyPath: pluginDataDir("report-history.jsonl")
|
|
12
|
+
}));
|
|
13
|
+
} catch (error) {
|
|
14
|
+
failOpen("Stop", error, {
|
|
15
|
+
continue: true
|
|
16
|
+
});
|
|
17
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "startup|resume",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "node ./bin/on-session-start.js"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"UserPromptSubmit": [
|
|
15
|
+
{
|
|
16
|
+
"hooks": [
|
|
17
|
+
{
|
|
18
|
+
"type": "command",
|
|
19
|
+
"command": "node ./bin/on-prompt.js"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"Stop": [
|
|
25
|
+
{
|
|
26
|
+
"hooks": [
|
|
27
|
+
{
|
|
28
|
+
"type": "command",
|
|
29
|
+
"command": "node ./bin/on-stop.js"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
}
|