yandecode 0.1.0 → 0.2.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/dist/bin.js +14 -8
- package/dist/bin.js.map +7 -1
- package/dist/chunks/chunk-23DG7PZZ.js +58 -0
- package/dist/chunks/chunk-23DG7PZZ.js.map +7 -0
- package/dist/chunks/chunk-2S6B3DDQ.js +1769 -0
- package/dist/chunks/chunk-2S6B3DDQ.js.map +7 -0
- package/dist/chunks/chunk-6J5CZVCJ.js +120 -0
- package/dist/chunks/chunk-6J5CZVCJ.js.map +7 -0
- package/dist/chunks/chunk-7L6DD2VD.js +768 -0
- package/dist/chunks/chunk-7L6DD2VD.js.map +7 -0
- package/dist/chunks/chunk-DWD37AI2.js +39 -0
- package/dist/chunks/chunk-DWD37AI2.js.map +7 -0
- package/dist/chunks/chunk-KPDBXQ62.js +62 -0
- package/dist/chunks/chunk-KPDBXQ62.js.map +7 -0
- package/dist/chunks/chunk-KVBKQXTU.js +351 -0
- package/dist/chunks/chunk-KVBKQXTU.js.map +7 -0
- package/dist/chunks/chunk-L2DDIZPI.js +53 -0
- package/dist/chunks/chunk-L2DDIZPI.js.map +7 -0
- package/dist/chunks/chunk-OIAMKDTQ.js +27 -0
- package/dist/chunks/chunk-OIAMKDTQ.js.map +7 -0
- package/dist/chunks/chunk-PUWSEWX3.js +61 -0
- package/dist/chunks/chunk-PUWSEWX3.js.map +7 -0
- package/dist/chunks/chunk-QCQC73WX.js +122 -0
- package/dist/chunks/chunk-QCQC73WX.js.map +7 -0
- package/dist/chunks/chunk-TYLH4NIC.js +39 -0
- package/dist/chunks/chunk-TYLH4NIC.js.map +7 -0
- package/dist/chunks/chunk-VKVJGBNJ.js +72 -0
- package/dist/chunks/chunk-VKVJGBNJ.js.map +7 -0
- package/dist/chunks/chunk-Y2MIXROU.js +20072 -0
- package/dist/chunks/chunk-Y2MIXROU.js.map +7 -0
- package/dist/chunks/cli-V6PJTDLM.js +3464 -0
- package/dist/chunks/cli-V6PJTDLM.js.map +7 -0
- package/dist/chunks/doctor-LLDVEPWL.js +184 -0
- package/dist/chunks/doctor-LLDVEPWL.js.map +7 -0
- package/dist/chunks/entry-YAOEX3EB.js +187 -0
- package/dist/chunks/entry-YAOEX3EB.js.map +7 -0
- package/dist/chunks/init-4ZJFCKIJ.js +46 -0
- package/dist/chunks/init-4ZJFCKIJ.js.map +7 -0
- package/dist/chunks/mcp-serve-KWKMUGWZ.js +22575 -0
- package/dist/chunks/mcp-serve-KWKMUGWZ.js.map +7 -0
- package/dist/chunks/modules-CJB5IKUJ.js +121 -0
- package/dist/chunks/modules-CJB5IKUJ.js.map +7 -0
- package/dist/chunks/runtime-3E4NIFAL.js +208 -0
- package/dist/chunks/runtime-3E4NIFAL.js.map +7 -0
- package/dist/chunks/runtime-7CRR63B5.js +395 -0
- package/dist/chunks/runtime-7CRR63B5.js.map +7 -0
- package/dist/chunks/runtime-CKMVHAU4.js +64 -0
- package/dist/chunks/runtime-CKMVHAU4.js.map +7 -0
- package/dist/chunks/runtime-CVZMWIJO.js +318 -0
- package/dist/chunks/runtime-CVZMWIJO.js.map +7 -0
- package/dist/chunks/runtime-DBNXBOAL.js +295 -0
- package/dist/chunks/runtime-DBNXBOAL.js.map +7 -0
- package/dist/chunks/runtime-EKHO2JXB.js +197 -0
- package/dist/chunks/runtime-EKHO2JXB.js.map +7 -0
- package/dist/chunks/runtime-EY6SVZYP.js +539 -0
- package/dist/chunks/runtime-EY6SVZYP.js.map +7 -0
- package/dist/chunks/runtime-UB2O2HUN.js +212 -0
- package/dist/chunks/runtime-UB2O2HUN.js.map +7 -0
- package/dist/chunks/runtime-WAFQWW4M.js +755 -0
- package/dist/chunks/runtime-WAFQWW4M.js.map +7 -0
- package/dist/chunks/runtime-ZGGF6RVA.js +271 -0
- package/dist/chunks/runtime-ZGGF6RVA.js.map +7 -0
- package/dist/chunks/status-SPIESL4F.js +38 -0
- package/dist/chunks/status-SPIESL4F.js.map +7 -0
- package/dist/chunks/uninstall-J2OWCX4A.js +54 -0
- package/dist/chunks/uninstall-J2OWCX4A.js.map +7 -0
- package/dist/chunks/update-OX5MZYQY.js +34 -0
- package/dist/chunks/update-OX5MZYQY.js.map +7 -0
- package/dist/content/agents/yandecode-reviewer.md +22 -0
- package/dist/content/agents/yandecode-security-reviewer.md +23 -0
- package/dist/content/skills/NOTICE.md +29 -0
- package/dist/content/skills/yandecode-code-review/SKILL.md +37 -0
- package/dist/content/skills/yandecode-debugging/SKILL.md +33 -0
- package/dist/content/skills/yandecode-instructions/SKILL.md +26 -0
- package/dist/content/skills/yandecode-memory/SKILL.md +36 -0
- package/dist/content/skills/yandecode-security-review/SKILL.md +24 -0
- package/dist/content/skills/yandecode-tdd/SKILL.md +38 -0
- package/dist/content/skills/yandecode-verification/SKILL.md +29 -0
- package/dist/content/skills/yandecode-workflow/SKILL.md +50 -0
- package/package.json +25 -19
- package/dist/bin.d.ts +0 -3
- package/dist/bin.d.ts.map +0 -1
- package/dist/cli.d.ts +0 -6
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js +0 -21
- package/dist/cli.js.map +0 -1
- package/dist/commands/doctor.d.ts +0 -2
- package/dist/commands/doctor.d.ts.map +0 -1
- package/dist/commands/doctor.js +0 -19
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/hook.d.ts +0 -2
- package/dist/commands/hook.d.ts.map +0 -1
- package/dist/commands/hook.js +0 -23
- package/dist/commands/hook.js.map +0 -1
- package/dist/commands/index-cmd.d.ts +0 -2
- package/dist/commands/index-cmd.d.ts.map +0 -1
- package/dist/commands/index-cmd.js +0 -47
- package/dist/commands/index-cmd.js.map +0 -1
- package/dist/commands/index.d.ts +0 -13
- package/dist/commands/index.d.ts.map +0 -1
- package/dist/commands/index.js +0 -15
- package/dist/commands/index.js.map +0 -1
- package/dist/commands/init.d.ts +0 -2
- package/dist/commands/init.d.ts.map +0 -1
- package/dist/commands/init.js +0 -22
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/mcp.d.ts +0 -2
- package/dist/commands/mcp.d.ts.map +0 -1
- package/dist/commands/mcp.js +0 -71
- package/dist/commands/mcp.js.map +0 -1
- package/dist/commands/rag.d.ts +0 -2
- package/dist/commands/rag.d.ts.map +0 -1
- package/dist/commands/rag.js +0 -27
- package/dist/commands/rag.js.map +0 -1
- package/dist/commands/start.d.ts +0 -6
- package/dist/commands/start.d.ts.map +0 -1
- package/dist/commands/start.js +0 -76
- package/dist/commands/start.js.map +0 -1
- package/dist/commands/status.d.ts +0 -2
- package/dist/commands/status.d.ts.map +0 -1
- package/dist/commands/status.js +0 -29
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/statusline.d.ts +0 -2
- package/dist/commands/statusline.d.ts.map +0 -1
- package/dist/commands/statusline.js +0 -90
- package/dist/commands/statusline.js.map +0 -1
- package/dist/commands/swarm.d.ts +0 -4
- package/dist/commands/swarm.d.ts.map +0 -1
- package/dist/commands/swarm.js +0 -48
- package/dist/commands/swarm.js.map +0 -1
- package/dist/commands/uninstall.d.ts +0 -2
- package/dist/commands/uninstall.d.ts.map +0 -1
- package/dist/commands/uninstall.js +0 -16
- package/dist/commands/uninstall.js.map +0 -1
- package/dist/context.d.ts +0 -13
- package/dist/context.d.ts.map +0 -1
- package/dist/context.js +0 -31
- package/dist/context.js.map +0 -1
- package/dist/doctor/checks.d.ts +0 -18
- package/dist/doctor/checks.d.ts.map +0 -1
- package/dist/doctor/checks.js +0 -269
- package/dist/doctor/checks.js.map +0 -1
- package/dist/hooks/handlers.d.ts +0 -21
- package/dist/hooks/handlers.d.ts.map +0 -1
- package/dist/hooks/handlers.js +0 -191
- package/dist/hooks/handlers.js.map +0 -1
- package/dist/index.d.ts +0 -9
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -9
- package/dist/index.js.map +0 -1
- package/dist/integration/claude-md.d.ts +0 -6
- package/dist/integration/claude-md.d.ts.map +0 -1
- package/dist/integration/claude-md.js +0 -30
- package/dist/integration/claude-md.js.map +0 -1
- package/dist/integration/gitignore.d.ts +0 -3
- package/dist/integration/gitignore.d.ts.map +0 -1
- package/dist/integration/gitignore.js +0 -13
- package/dist/integration/gitignore.js.map +0 -1
- package/dist/integration/init.d.ts +0 -17
- package/dist/integration/init.d.ts.map +0 -1
- package/dist/integration/init.js +0 -86
- package/dist/integration/init.js.map +0 -1
- package/dist/integration/json-utils.d.ts +0 -13
- package/dist/integration/json-utils.d.ts.map +0 -1
- package/dist/integration/json-utils.js +0 -33
- package/dist/integration/json-utils.js.map +0 -1
- package/dist/integration/manifest.d.ts +0 -17
- package/dist/integration/manifest.d.ts.map +0 -1
- package/dist/integration/manifest.js +0 -26
- package/dist/integration/manifest.js.map +0 -1
- package/dist/integration/materialize.d.ts +0 -9
- package/dist/integration/materialize.d.ts.map +0 -1
- package/dist/integration/materialize.js +0 -21
- package/dist/integration/materialize.js.map +0 -1
- package/dist/integration/mcp-config.d.ts +0 -4
- package/dist/integration/mcp-config.d.ts.map +0 -1
- package/dist/integration/mcp-config.js +0 -11
- package/dist/integration/mcp-config.js.map +0 -1
- package/dist/integration/settings.d.ts +0 -7
- package/dist/integration/settings.d.ts.map +0 -1
- package/dist/integration/settings.js +0 -70
- package/dist/integration/settings.js.map +0 -1
- package/dist/integration/uninstall.d.ts +0 -10
- package/dist/integration/uninstall.d.ts.map +0 -1
- package/dist/integration/uninstall.js +0 -71
- package/dist/integration/uninstall.js.map +0 -1
- package/dist/launcher.d.ts +0 -8
- package/dist/launcher.d.ts.map +0 -1
- package/dist/launcher.js +0 -18
- package/dist/launcher.js.map +0 -1
- package/dist/mcp/server.d.ts +0 -27
- package/dist/mcp/server.d.ts.map +0 -1
- package/dist/mcp/server.js +0 -311
- package/dist/mcp/server.js.map +0 -1
- package/dist/plugin-content.d.ts +0 -21
- package/dist/plugin-content.d.ts.map +0 -1
- package/dist/plugin-content.js +0 -67
- package/dist/plugin-content.js.map +0 -1
- package/dist/rag/format.d.ts +0 -3
- package/dist/rag/format.d.ts.map +0 -1
- package/dist/rag/format.js +0 -12
- package/dist/rag/format.js.map +0 -1
- package/dist/retrieval-runtime.d.ts +0 -21
- package/dist/retrieval-runtime.d.ts.map +0 -1
- package/dist/retrieval-runtime.js +0 -47
- package/dist/retrieval-runtime.js.map +0 -1
- package/dist/status-line/git.d.ts +0 -3
- package/dist/status-line/git.d.ts.map +0 -1
- package/dist/status-line/git.js +0 -34
- package/dist/status-line/git.js.map +0 -1
- package/dist/status-line/render.d.ts +0 -29
- package/dist/status-line/render.d.ts.map +0 -1
- package/dist/status-line/render.js +0 -72
- package/dist/status-line/render.js.map +0 -1
- package/dist/swarm-runtime.d.ts +0 -11
- package/dist/swarm-runtime.d.ts.map +0 -1
- package/dist/swarm-runtime.js +0 -76
- package/dist/swarm-runtime.js.map +0 -1
- package/dist/version.d.ts +0 -2
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js +0 -5
- package/dist/version.js.map +0 -1
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
import { createRequire as __yandecodeCreateRequire } from 'node:module';
|
|
2
|
+
const require = __yandecodeCreateRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
digest,
|
|
5
|
+
execShell
|
|
6
|
+
} from "./chunk-6J5CZVCJ.js";
|
|
7
|
+
import {
|
|
8
|
+
parseSettings,
|
|
9
|
+
requireWorkspace
|
|
10
|
+
} from "./chunk-QCQC73WX.js";
|
|
11
|
+
import {
|
|
12
|
+
contextSettings,
|
|
13
|
+
fetchInput,
|
|
14
|
+
getInput,
|
|
15
|
+
runInput,
|
|
16
|
+
searchInput2 as searchInput
|
|
17
|
+
} from "./chunk-7L6DD2VD.js";
|
|
18
|
+
import {
|
|
19
|
+
fitRows
|
|
20
|
+
} from "./chunk-OIAMKDTQ.js";
|
|
21
|
+
import {
|
|
22
|
+
external_exports,
|
|
23
|
+
openModuleDb,
|
|
24
|
+
resolveInsideRoot,
|
|
25
|
+
toFtsQuery
|
|
26
|
+
} from "./chunk-Y2MIXROU.js";
|
|
27
|
+
|
|
28
|
+
// packages/cli/src/modules/context/runtime.ts
|
|
29
|
+
import { join } from "node:path";
|
|
30
|
+
|
|
31
|
+
// packages/cli/src/modules/context/classify.ts
|
|
32
|
+
var VERBOSE_PATTERNS = [
|
|
33
|
+
/^(npm|pnpm|yarn|bun) (run )?(test|build|lint|typecheck|check)\b/,
|
|
34
|
+
/^npx (vitest|jest|tsc|eslint|mocha|playwright)\b/,
|
|
35
|
+
/^(vitest|jest|mocha|pytest|tox|phpunit|rspec)\b/,
|
|
36
|
+
/^(cargo|go) (test|build|clippy)\b/,
|
|
37
|
+
/^(mvn|gradle|\.\/gradlew|make|cmake|ctest|dotnet (test|build))\b/,
|
|
38
|
+
/^(docker|kubectl|podman) logs\b/,
|
|
39
|
+
/^journalctl\b/,
|
|
40
|
+
/^find \//
|
|
41
|
+
];
|
|
42
|
+
function stripQuoted(command) {
|
|
43
|
+
return command.replace(/'[^']*'|"(?:\\.|[^"\\])*"/g, "''");
|
|
44
|
+
}
|
|
45
|
+
function segments(command) {
|
|
46
|
+
return command.split(/&&|\|\||;|\|/).map((s) => s.trim().replace(/^(sudo\s+|(\w+=\S*\s+)+)/, "")).filter((s) => s.length > 0);
|
|
47
|
+
}
|
|
48
|
+
function isNetworkPrint(segment, redirected) {
|
|
49
|
+
if (/^curl\b/.test(segment)) {
|
|
50
|
+
return !redirected && !/\s(-o|--output|-O|--remote-name)\b/.test(segment);
|
|
51
|
+
}
|
|
52
|
+
return /^wget\b/.test(segment) && /\s-\w*O\s*-(\s|$)/.test(`${segment} `);
|
|
53
|
+
}
|
|
54
|
+
function isUnboundedGitLog(segment) {
|
|
55
|
+
return /^git log\b/.test(segment) && !/\s(-n\s*\d+|-\d+|--max-count)/.test(segment);
|
|
56
|
+
}
|
|
57
|
+
function classifyCommand(command) {
|
|
58
|
+
const bare = stripQuoted(command);
|
|
59
|
+
const redirected = /[^2&]>\s*[^&\s]/.test(` ${bare}`);
|
|
60
|
+
const parts = segments(bare);
|
|
61
|
+
if (parts.some((s) => isNetworkPrint(s, redirected))) return "network";
|
|
62
|
+
if (parts.some((s) => VERBOSE_PATTERNS.some((p) => p.test(s)) || isUnboundedGitLog(s))) {
|
|
63
|
+
return "verbose";
|
|
64
|
+
}
|
|
65
|
+
return "plain";
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// packages/cli/src/modules/context/html.ts
|
|
69
|
+
var ENTITIES = {
|
|
70
|
+
amp: "&",
|
|
71
|
+
lt: "<",
|
|
72
|
+
gt: ">",
|
|
73
|
+
quot: '"',
|
|
74
|
+
apos: "'",
|
|
75
|
+
nbsp: " "
|
|
76
|
+
};
|
|
77
|
+
function decodeEntities(text) {
|
|
78
|
+
return text.replace(/&(#x[0-9a-f]+|#\d+|[a-z]+);/gi, (match, entity) => {
|
|
79
|
+
if (entity.startsWith("#x") || entity.startsWith("#X"))
|
|
80
|
+
return String.fromCodePoint(parseInt(entity.slice(2), 16));
|
|
81
|
+
if (entity.startsWith("#")) return String.fromCodePoint(Number(entity.slice(1)));
|
|
82
|
+
return ENTITIES[entity.toLowerCase()] ?? match;
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
var BLOCK = "p|div|ul|ol|table|section|article|header|footer|main|nav|pre|blockquote|figure|dl";
|
|
86
|
+
function htmlToText(html) {
|
|
87
|
+
const text = html.replace(/<(script|style|noscript|svg|template|head)\b[\s\S]*?<\/\1>/gi, "").replace(/<!--[\s\S]*?-->/g, "").replace(/<h([1-6])\b[^>]*>/gi, (_, level) => `
|
|
88
|
+
|
|
89
|
+
${"#".repeat(Number(level))} `).replace(/<\/h[1-6]>/gi, "\n\n").replace(/<li\b[^>]*>/gi, "\n- ").replace(new RegExp(`</?(${BLOCK})\\b[^>]*>`, "gi"), "\n\n").replace(/<(br|tr)\b[^>]*>/gi, "\n").replace(/<[^>]+>/g, "");
|
|
90
|
+
return decodeEntities(text).split("\n").map((line) => line.replace(/[ \t\f\v]+/g, " ").trim()).join("\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// packages/cli/src/modules/context/store.ts
|
|
94
|
+
import { randomBytes } from "node:crypto";
|
|
95
|
+
import { mkdirSync } from "node:fs";
|
|
96
|
+
import { dirname } from "node:path";
|
|
97
|
+
var CHUNK_LINES = 20;
|
|
98
|
+
var MIGRATIONS = [
|
|
99
|
+
`
|
|
100
|
+
CREATE TABLE outputs (
|
|
101
|
+
handle TEXT PRIMARY KEY,
|
|
102
|
+
source TEXT NOT NULL,
|
|
103
|
+
label TEXT NOT NULL,
|
|
104
|
+
exit_code INTEGER,
|
|
105
|
+
duration_ms INTEGER NOT NULL,
|
|
106
|
+
created_at TEXT NOT NULL,
|
|
107
|
+
line_count INTEGER NOT NULL,
|
|
108
|
+
byte_count INTEGER NOT NULL,
|
|
109
|
+
body TEXT NOT NULL
|
|
110
|
+
);
|
|
111
|
+
CREATE INDEX outputs_created ON outputs (created_at);
|
|
112
|
+
CREATE VIRTUAL TABLE output_fts USING fts5 (
|
|
113
|
+
text, handle UNINDEXED, start_line UNINDEXED, end_line UNINDEXED,
|
|
114
|
+
tokenize = 'porter unicode61'
|
|
115
|
+
);
|
|
116
|
+
CREATE TABLE session_hints (session_id TEXT NOT NULL, kind TEXT NOT NULL, PRIMARY KEY (session_id, kind));
|
|
117
|
+
`
|
|
118
|
+
];
|
|
119
|
+
var OutputStore = class _OutputStore {
|
|
120
|
+
constructor(db) {
|
|
121
|
+
this.db = db;
|
|
122
|
+
}
|
|
123
|
+
db;
|
|
124
|
+
static open(file) {
|
|
125
|
+
mkdirSync(dirname(file), { recursive: true });
|
|
126
|
+
return new _OutputStore(openModuleDb(file, MIGRATIONS));
|
|
127
|
+
}
|
|
128
|
+
close() {
|
|
129
|
+
this.db.close();
|
|
130
|
+
}
|
|
131
|
+
save(input) {
|
|
132
|
+
const handle = `o${randomBytes(3).toString("hex")}`;
|
|
133
|
+
const lines = input.text.split("\n");
|
|
134
|
+
this.db.transaction(() => {
|
|
135
|
+
this.db.prepare(
|
|
136
|
+
"INSERT INTO outputs (handle, source, label, exit_code, duration_ms, created_at, line_count, byte_count, body) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
|
137
|
+
).run(
|
|
138
|
+
handle,
|
|
139
|
+
input.source,
|
|
140
|
+
input.label,
|
|
141
|
+
input.exitCode,
|
|
142
|
+
input.durationMs,
|
|
143
|
+
(/* @__PURE__ */ new Date()).toISOString(),
|
|
144
|
+
lines.length,
|
|
145
|
+
Buffer.byteLength(input.text),
|
|
146
|
+
input.text
|
|
147
|
+
);
|
|
148
|
+
const insert = this.db.prepare(
|
|
149
|
+
"INSERT INTO output_fts (text, handle, start_line, end_line) VALUES (?, ?, ?, ?)"
|
|
150
|
+
);
|
|
151
|
+
for (let start = 0; start < lines.length; start += CHUNK_LINES) {
|
|
152
|
+
const end = Math.min(start + CHUNK_LINES, lines.length);
|
|
153
|
+
insert.run(lines.slice(start, end).join("\n"), handle, start + 1, end);
|
|
154
|
+
}
|
|
155
|
+
})();
|
|
156
|
+
return handle;
|
|
157
|
+
}
|
|
158
|
+
get(handle) {
|
|
159
|
+
const row = this.db.prepare(
|
|
160
|
+
"SELECT handle, source, label, exit_code, duration_ms, created_at, line_count, byte_count FROM outputs WHERE handle = ?"
|
|
161
|
+
).get(handle);
|
|
162
|
+
if (!row) return null;
|
|
163
|
+
return {
|
|
164
|
+
handle: row.handle,
|
|
165
|
+
source: row.source,
|
|
166
|
+
label: row.label,
|
|
167
|
+
exitCode: row.exit_code,
|
|
168
|
+
durationMs: row.duration_ms,
|
|
169
|
+
createdAt: row.created_at,
|
|
170
|
+
lineCount: row.line_count,
|
|
171
|
+
byteCount: row.byte_count
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/** 1-based inclusive line range, verbatim. */
|
|
175
|
+
lines(handle, from, to) {
|
|
176
|
+
const row = this.db.prepare("SELECT body FROM outputs WHERE handle = ?").get(handle);
|
|
177
|
+
if (!row) return [];
|
|
178
|
+
return row.body.split("\n").slice(Math.max(0, from - 1), Math.max(0, to));
|
|
179
|
+
}
|
|
180
|
+
search(query, options = {}) {
|
|
181
|
+
const fts = toFtsQuery(query);
|
|
182
|
+
if (!fts) return [];
|
|
183
|
+
const filter = options.handle ? "AND f.handle = ?" : "";
|
|
184
|
+
const params = [
|
|
185
|
+
fts,
|
|
186
|
+
...options.handle ? [options.handle] : [],
|
|
187
|
+
options.limit ?? 8
|
|
188
|
+
];
|
|
189
|
+
return this.db.prepare(
|
|
190
|
+
`SELECT f.handle, o.label, f.start_line, f.end_line,
|
|
191
|
+
snippet(output_fts, 0, '', '', ' \u2026 ', 14) AS snippet
|
|
192
|
+
FROM output_fts f JOIN outputs o ON o.handle = f.handle
|
|
193
|
+
WHERE output_fts MATCH ? ${filter} ORDER BY rank LIMIT ?`
|
|
194
|
+
).all(...params).map((r) => ({
|
|
195
|
+
handle: r.handle,
|
|
196
|
+
label: r.label,
|
|
197
|
+
startLine: r.start_line,
|
|
198
|
+
endLine: r.end_line,
|
|
199
|
+
snippet: r.snippet.replace(/\s+/g, " ").trim()
|
|
200
|
+
}));
|
|
201
|
+
}
|
|
202
|
+
recent(limit) {
|
|
203
|
+
const handles = this.db.prepare("SELECT handle FROM outputs ORDER BY created_at DESC LIMIT ?").all(limit);
|
|
204
|
+
return handles.flatMap((h) => this.get(h.handle) ?? []);
|
|
205
|
+
}
|
|
206
|
+
purgeOlderThan(cutoff) {
|
|
207
|
+
const old = this.db.prepare("SELECT handle FROM outputs WHERE created_at < ?").all(cutoff.toISOString());
|
|
208
|
+
this.db.transaction(() => {
|
|
209
|
+
for (const { handle } of old) {
|
|
210
|
+
this.db.prepare("DELETE FROM output_fts WHERE handle = ?").run(handle);
|
|
211
|
+
this.db.prepare("DELETE FROM outputs WHERE handle = ?").run(handle);
|
|
212
|
+
}
|
|
213
|
+
})();
|
|
214
|
+
return old.length;
|
|
215
|
+
}
|
|
216
|
+
/** True the first time `kind` is requested for a session: used for one-time hints. */
|
|
217
|
+
firstTimeInSession(sessionId, kind) {
|
|
218
|
+
return this.db.prepare("INSERT OR IGNORE INTO session_hints (session_id, kind) VALUES (?, ?)").run(sessionId, kind).changes === 1;
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// packages/cli/src/modules/context/runtime.ts
|
|
223
|
+
var DAY_MS = 864e5;
|
|
224
|
+
var stores = /* @__PURE__ */ new Map();
|
|
225
|
+
function settingsOf(ctx) {
|
|
226
|
+
return parseSettings("context", contextSettings, ctx.settings);
|
|
227
|
+
}
|
|
228
|
+
function storeFor(ctx) {
|
|
229
|
+
let store = stores.get(ctx.root);
|
|
230
|
+
if (!store) {
|
|
231
|
+
store = OutputStore.open(join(ctx.paths.yandecodeDir, "context.db"));
|
|
232
|
+
store.purgeOlderThan(new Date(Date.now() - settingsOf(ctx).retentionDays * DAY_MS));
|
|
233
|
+
stores.set(ctx.root, store);
|
|
234
|
+
}
|
|
235
|
+
return store;
|
|
236
|
+
}
|
|
237
|
+
function formatBytes(bytes) {
|
|
238
|
+
return bytes < 1024 ? `${bytes} B` : bytes < 1048576 ? `${(bytes / 1024).toFixed(0)} KB` : `${(bytes / 1048576).toFixed(1)} MB`;
|
|
239
|
+
}
|
|
240
|
+
function header(meta, extra = []) {
|
|
241
|
+
const facts = [
|
|
242
|
+
meta.exitCode === null ? null : `exit ${meta.exitCode}`,
|
|
243
|
+
`${meta.lineCount} lines`,
|
|
244
|
+
formatBytes(meta.byteCount),
|
|
245
|
+
meta.source === "run" ? `${(meta.durationMs / 1e3).toFixed(1)} s` : null,
|
|
246
|
+
`handle ${meta.handle}`,
|
|
247
|
+
...extra
|
|
248
|
+
].filter((f) => f !== null);
|
|
249
|
+
return `${meta.source === "run" ? "$" : "GET"} ${meta.label} (${facts.join(" \xB7 ")})`;
|
|
250
|
+
}
|
|
251
|
+
function answer(store, handle, text, intent, maxChars, notes) {
|
|
252
|
+
const meta = store.get(handle);
|
|
253
|
+
const focus = intent ? store.search(intent, { handle, limit: 5 }).flatMap(
|
|
254
|
+
(hit) => Array.from({ length: hit.endLine - hit.startLine + 1 }, (_, i) => hit.startLine - 1 + i)
|
|
255
|
+
) : [];
|
|
256
|
+
const body = digest(text, { maxChars: maxChars - 400, focus });
|
|
257
|
+
const footer = body.omittedLines > 0 ? `\u2014 ${body.omittedLines} line(s) not shown; full output kept: ctx_search(query, handle="${handle}") or ctx_get(handle="${handle}", from, to)` : "";
|
|
258
|
+
return { text: [header(meta, notes), body.text, footer].filter(Boolean).join("\n") };
|
|
259
|
+
}
|
|
260
|
+
async function ctxRun(raw, ctx) {
|
|
261
|
+
const args = external_exports.object(runInput).parse(raw);
|
|
262
|
+
const settings = settingsOf(ctx);
|
|
263
|
+
const cwd = args.cwd ? resolveInsideRoot(ctx.root, args.cwd) : ctx.root;
|
|
264
|
+
const result = await execShell(args.command, {
|
|
265
|
+
cwd,
|
|
266
|
+
timeoutMs: (args.timeout_s ?? 120) * 1e3,
|
|
267
|
+
maxBytes: settings.maxOutputMb * 1048576
|
|
268
|
+
});
|
|
269
|
+
const store = storeFor(ctx);
|
|
270
|
+
const handle = store.save({
|
|
271
|
+
source: "run",
|
|
272
|
+
label: args.command,
|
|
273
|
+
exitCode: result.exitCode,
|
|
274
|
+
durationMs: result.durationMs,
|
|
275
|
+
text: result.output
|
|
276
|
+
});
|
|
277
|
+
const notes = [
|
|
278
|
+
result.timedOut ? `killed after ${args.timeout_s ?? 120} s` : null,
|
|
279
|
+
result.truncatedBytes > 0 ? `${formatBytes(result.truncatedBytes)} beyond the ${settings.maxOutputMb} MB cap discarded` : null
|
|
280
|
+
].filter((n) => n !== null);
|
|
281
|
+
return answer(store, handle, result.output, args.intent, args.max_chars ?? 3e3, notes);
|
|
282
|
+
}
|
|
283
|
+
async function ctxFetch(raw, ctx) {
|
|
284
|
+
const args = external_exports.object(fetchInput).parse(raw);
|
|
285
|
+
const started = Date.now();
|
|
286
|
+
const response = await fetch(args.url, {
|
|
287
|
+
signal: AbortSignal.timeout(3e4),
|
|
288
|
+
headers: {
|
|
289
|
+
"user-agent": "yandecode-ctx-fetch",
|
|
290
|
+
accept: "text/html,application/json,text/plain,*/*"
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
const type = response.headers.get("content-type") ?? "";
|
|
294
|
+
const raw_ = await response.text();
|
|
295
|
+
const text = type.includes("html") ? htmlToText(raw_) : type.includes("json") ? JSON.stringify(JSON.parse(raw_), null, 2) : raw_;
|
|
296
|
+
const store = storeFor(ctx);
|
|
297
|
+
const handle = store.save({
|
|
298
|
+
source: "fetch",
|
|
299
|
+
label: args.url,
|
|
300
|
+
exitCode: response.ok ? null : response.status,
|
|
301
|
+
durationMs: Date.now() - started,
|
|
302
|
+
text
|
|
303
|
+
});
|
|
304
|
+
const notes = response.ok ? [] : [`HTTP ${response.status}`];
|
|
305
|
+
return answer(store, handle, text, args.intent, args.max_chars ?? 3e3, notes);
|
|
306
|
+
}
|
|
307
|
+
function ctxSearch(raw, ctx) {
|
|
308
|
+
const args = external_exports.object(searchInput).parse(raw);
|
|
309
|
+
const store = storeFor(ctx);
|
|
310
|
+
const hits = store.search(args.query, {
|
|
311
|
+
...args.handle ? { handle: args.handle } : {},
|
|
312
|
+
limit: args.limit ?? 8
|
|
313
|
+
});
|
|
314
|
+
if (hits.length === 0) {
|
|
315
|
+
const recent = store.recent(5).map((o) => ` ${o.handle} ${o.label}`);
|
|
316
|
+
return Promise.resolve({
|
|
317
|
+
text: [
|
|
318
|
+
`No stored output matches "${args.query}".`,
|
|
319
|
+
...recent.length > 0 ? ["Recent outputs:", ...recent] : []
|
|
320
|
+
].join("\n")
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
const rows = hits.map(
|
|
324
|
+
(h) => `${h.handle}:${h.startLine}-${h.endLine} (${h.label.slice(0, 60)}) ${h.snippet}`
|
|
325
|
+
);
|
|
326
|
+
return Promise.resolve({ text: fitRows(`ctx_search "${args.query}"`, rows, 3e3) });
|
|
327
|
+
}
|
|
328
|
+
function ctxGet(raw, ctx) {
|
|
329
|
+
const args = external_exports.object(getInput).parse(raw);
|
|
330
|
+
const store = storeFor(ctx);
|
|
331
|
+
const meta = store.get(args.handle);
|
|
332
|
+
if (!meta)
|
|
333
|
+
return Promise.resolve({
|
|
334
|
+
isError: true,
|
|
335
|
+
text: `Unknown handle "${args.handle}" (outputs expire after a few days; re-run the command).`
|
|
336
|
+
});
|
|
337
|
+
const from = args.from ?? 1;
|
|
338
|
+
const to = Math.min(args.to ?? from + 199, meta.lineCount);
|
|
339
|
+
const rows = store.lines(args.handle, from, to).map((line, i) => `${from + i}: ${line}`);
|
|
340
|
+
return Promise.resolve({
|
|
341
|
+
text: fitRows(header(meta, [`lines ${from}-${to}`]), rows, args.max_chars ?? 8e3)
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
var NETWORK_DENY = "Its response would enter the context unfiltered. Use ctx_fetch(url, intent) for web pages and APIs, or ctx_run(command, intent) to keep the full response searchable. To really print it, write to a file (-o file) and read the part you need.";
|
|
345
|
+
var VERBOSE_HINT = "yandecode: this command can print a lot. Prefer ctx_run(command, intent): it runs to completion, keeps the full output (ctx_search/ctx_get) and returns only the failing or relevant lines.";
|
|
346
|
+
var BACKGROUND_HINT = "yandecode: this test/build is going to the background. If you stop to wait for it, the session can end before you see the result. Run it with ctx_run(command, intent) instead: it waits for completion and returns only what matters.";
|
|
347
|
+
function preToolUse(input, ctx) {
|
|
348
|
+
const command = input.tool_input?.command;
|
|
349
|
+
if (typeof command !== "string") return Promise.resolve({ kind: "none" });
|
|
350
|
+
const settings = settingsOf(ctx);
|
|
351
|
+
const kind = classifyCommand(command);
|
|
352
|
+
if (kind === "network" && settings.denyNetworkFetch) {
|
|
353
|
+
ctx.log("deny", { command });
|
|
354
|
+
return Promise.resolve({ kind: "deny", reason: NETWORK_DENY });
|
|
355
|
+
}
|
|
356
|
+
if (kind !== "verbose" || !settings.hints) return Promise.resolve({ kind: "none" });
|
|
357
|
+
const background = input.tool_input?.run_in_background === true;
|
|
358
|
+
const hint = background ? "background" : "verbose";
|
|
359
|
+
if (storeFor(ctx).firstTimeInSession(input.session_id ?? "unknown", hint)) {
|
|
360
|
+
return Promise.resolve({ kind: "context", text: background ? BACKGROUND_HINT : VERBOSE_HINT });
|
|
361
|
+
}
|
|
362
|
+
return Promise.resolve({ kind: "none" });
|
|
363
|
+
}
|
|
364
|
+
var runtime = {
|
|
365
|
+
tools: { ctx_run: ctxRun, ctx_fetch: ctxFetch, ctx_search: ctxSearch, ctx_get: ctxGet },
|
|
366
|
+
hooks: { PreToolUse: preToolUse },
|
|
367
|
+
doctor: (ctx) => {
|
|
368
|
+
const recent = storeFor(ctx).recent(1)[0];
|
|
369
|
+
return Promise.resolve([
|
|
370
|
+
{
|
|
371
|
+
name: "output store",
|
|
372
|
+
status: "ok",
|
|
373
|
+
detail: recent ? `last output ${recent.createdAt} (${recent.label.slice(0, 40)})` : "empty"
|
|
374
|
+
}
|
|
375
|
+
]);
|
|
376
|
+
},
|
|
377
|
+
dispose: () => {
|
|
378
|
+
for (const store of stores.values()) store.close();
|
|
379
|
+
stores.clear();
|
|
380
|
+
return Promise.resolve();
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
function runPurgeCommand(cwd) {
|
|
384
|
+
const ws = requireWorkspace(cwd);
|
|
385
|
+
const store = OutputStore.open(join(ws.paths.yandecodeDir, "context.db"));
|
|
386
|
+
const removed = store.purgeOlderThan(new Date(Date.now() + DAY_MS));
|
|
387
|
+
store.close();
|
|
388
|
+
process.stdout.write(`removed ${removed} stored output(s)
|
|
389
|
+
`);
|
|
390
|
+
}
|
|
391
|
+
export {
|
|
392
|
+
runPurgeCommand,
|
|
393
|
+
runtime
|
|
394
|
+
};
|
|
395
|
+
//# sourceMappingURL=runtime-7CRR63B5.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/modules/context/runtime.ts", "../../src/modules/context/classify.ts", "../../src/modules/context/html.ts", "../../src/modules/context/store.ts"],
|
|
4
|
+
"sourcesContent": ["import { join } from 'node:path';\nimport { resolveInsideRoot } from '@yandecode/core';\nimport { z } from 'zod';\nimport { fitRows } from '@cli/shared/budget';\nimport type { HookResult, ModuleContext, ModuleRuntime, ToolResult } from '@cli/modules/contract';\nimport { parseSettings, requireWorkspace } from '@cli/modules/host';\nimport { classifyCommand } from './classify';\nimport { contextSettings, fetchInput, getInput, runInput, searchInput } from './definition';\nimport { digest } from '@cli/shared/digest';\nimport { execShell } from '@cli/shared/exec';\nimport { htmlToText } from './html';\nimport { OutputStore, type StoredOutput } from './store';\n\nconst DAY_MS = 86_400_000;\nconst stores = new Map<string, OutputStore>();\n\nfunction settingsOf(ctx: ModuleContext): z.infer<typeof contextSettings> {\n return parseSettings('context', contextSettings, ctx.settings);\n}\n\n/** One store per workspace per process; expired outputs are purged when it opens. */\nfunction storeFor(ctx: ModuleContext): OutputStore {\n let store = stores.get(ctx.root);\n if (!store) {\n store = OutputStore.open(join(ctx.paths.yandecodeDir, 'context.db'));\n store.purgeOlderThan(new Date(Date.now() - settingsOf(ctx).retentionDays * DAY_MS));\n stores.set(ctx.root, store);\n }\n return store;\n}\n\nfunction formatBytes(bytes: number): string {\n return bytes < 1024\n ? `${bytes} B`\n : bytes < 1_048_576\n ? `${(bytes / 1024).toFixed(0)} KB`\n : `${(bytes / 1_048_576).toFixed(1)} MB`;\n}\n\nfunction header(meta: StoredOutput, extra: string[] = []): string {\n const facts = [\n meta.exitCode === null ? null : `exit ${meta.exitCode}`,\n `${meta.lineCount} lines`,\n formatBytes(meta.byteCount),\n meta.source === 'run' ? `${(meta.durationMs / 1000).toFixed(1)} s` : null,\n `handle ${meta.handle}`,\n ...extra,\n ].filter((f): f is string => f !== null);\n return `${meta.source === 'run' ? '$' : 'GET'} ${meta.label} (${facts.join(' \u00B7 ')})`;\n}\n\n/** Stores the full text and answers with a digest focused on `intent` when given. */\nfunction answer(\n store: OutputStore,\n handle: string,\n text: string,\n intent: string | undefined,\n maxChars: number,\n notes: string[],\n): ToolResult {\n const meta = store.get(handle)!;\n const focus = intent\n ? store\n .search(intent, { handle, limit: 5 })\n .flatMap((hit) =>\n Array.from({ length: hit.endLine - hit.startLine + 1 }, (_, i) => hit.startLine - 1 + i),\n )\n : [];\n const body = digest(text, { maxChars: maxChars - 400, focus });\n const footer =\n body.omittedLines > 0\n ? `\u2014 ${body.omittedLines} line(s) not shown; full output kept: ctx_search(query, handle=\"${handle}\") or ctx_get(handle=\"${handle}\", from, to)`\n : '';\n return { text: [header(meta, notes), body.text, footer].filter(Boolean).join('\\n') };\n}\n\nasync function ctxRun(raw: Record<string, unknown>, ctx: ModuleContext): Promise<ToolResult> {\n const args = z.object(runInput).parse(raw);\n const settings = settingsOf(ctx);\n const cwd = args.cwd ? resolveInsideRoot(ctx.root, args.cwd) : ctx.root;\n const result = await execShell(args.command, {\n cwd,\n timeoutMs: (args.timeout_s ?? 120) * 1000,\n maxBytes: settings.maxOutputMb * 1_048_576,\n });\n const store = storeFor(ctx);\n const handle = store.save({\n source: 'run',\n label: args.command,\n exitCode: result.exitCode,\n durationMs: result.durationMs,\n text: result.output,\n });\n const notes = [\n result.timedOut ? `killed after ${args.timeout_s ?? 120} s` : null,\n result.truncatedBytes > 0\n ? `${formatBytes(result.truncatedBytes)} beyond the ${settings.maxOutputMb} MB cap discarded`\n : null,\n ].filter((n): n is string => n !== null);\n return answer(store, handle, result.output, args.intent, args.max_chars ?? 3_000, notes);\n}\n\nasync function ctxFetch(raw: Record<string, unknown>, ctx: ModuleContext): Promise<ToolResult> {\n const args = z.object(fetchInput).parse(raw);\n const started = Date.now();\n const response = await fetch(args.url, {\n signal: AbortSignal.timeout(30_000),\n headers: {\n 'user-agent': 'yandecode-ctx-fetch',\n accept: 'text/html,application/json,text/plain,*/*',\n },\n });\n const type = response.headers.get('content-type') ?? '';\n const raw_ = await response.text();\n const text = type.includes('html')\n ? htmlToText(raw_)\n : type.includes('json')\n ? JSON.stringify(JSON.parse(raw_) as unknown, null, 2)\n : raw_;\n const store = storeFor(ctx);\n const handle = store.save({\n source: 'fetch',\n label: args.url,\n exitCode: response.ok ? null : response.status,\n durationMs: Date.now() - started,\n text,\n });\n const notes = response.ok ? [] : [`HTTP ${response.status}`];\n return answer(store, handle, text, args.intent, args.max_chars ?? 3_000, notes);\n}\n\nfunction ctxSearch(raw: Record<string, unknown>, ctx: ModuleContext): Promise<ToolResult> {\n const args = z.object(searchInput).parse(raw);\n const store = storeFor(ctx);\n const hits = store.search(args.query, {\n ...(args.handle ? { handle: args.handle } : {}),\n limit: args.limit ?? 8,\n });\n if (hits.length === 0) {\n const recent = store.recent(5).map((o) => ` ${o.handle} ${o.label}`);\n return Promise.resolve({\n text: [\n `No stored output matches \"${args.query}\".`,\n ...(recent.length > 0 ? ['Recent outputs:', ...recent] : []),\n ].join('\\n'),\n });\n }\n const rows = hits.map(\n (h) => `${h.handle}:${h.startLine}-${h.endLine} (${h.label.slice(0, 60)}) ${h.snippet}`,\n );\n return Promise.resolve({ text: fitRows(`ctx_search \"${args.query}\"`, rows, 3_000) });\n}\n\nfunction ctxGet(raw: Record<string, unknown>, ctx: ModuleContext): Promise<ToolResult> {\n const args = z.object(getInput).parse(raw);\n const store = storeFor(ctx);\n const meta = store.get(args.handle);\n if (!meta)\n return Promise.resolve({\n isError: true,\n text: `Unknown handle \"${args.handle}\" (outputs expire after a few days; re-run the command).`,\n });\n const from = args.from ?? 1;\n const to = Math.min(args.to ?? from + 199, meta.lineCount);\n const rows = store.lines(args.handle, from, to).map((line, i) => `${from + i}: ${line}`);\n return Promise.resolve({\n text: fitRows(header(meta, [`lines ${from}-${to}`]), rows, args.max_chars ?? 8_000),\n });\n}\n\nconst NETWORK_DENY =\n 'Its response would enter the context unfiltered. Use ctx_fetch(url, intent) for web pages and APIs, or ctx_run(command, intent) to keep the full response searchable. To really print it, write to a file (-o file) and read the part you need.';\nconst VERBOSE_HINT =\n 'yandecode: this command can print a lot. Prefer ctx_run(command, intent): it runs to completion, keeps the full output (ctx_search/ctx_get) and returns only the failing or relevant lines.';\nconst BACKGROUND_HINT =\n 'yandecode: this test/build is going to the background. If you stop to wait for it, the session can end before you see the result. Run it with ctx_run(command, intent) instead: it waits for completion and returns only what matters.';\n\nfunction preToolUse(\n input: { session_id?: string; tool_input?: Record<string, unknown> },\n ctx: ModuleContext,\n): Promise<HookResult> {\n const command = input.tool_input?.command;\n if (typeof command !== 'string') return Promise.resolve({ kind: 'none' });\n const settings = settingsOf(ctx);\n const kind = classifyCommand(command);\n if (kind === 'network' && settings.denyNetworkFetch) {\n ctx.log('deny', { command });\n return Promise.resolve({ kind: 'deny', reason: NETWORK_DENY });\n }\n if (kind !== 'verbose' || !settings.hints) return Promise.resolve({ kind: 'none' });\n // A background test run is the costlier habit (sessions ended before seeing results in the\n // pacolang A/B), so it gets its own hint even after the generic one was shown.\n const background = input.tool_input?.run_in_background === true;\n const hint = background ? 'background' : 'verbose';\n if (storeFor(ctx).firstTimeInSession(input.session_id ?? 'unknown', hint)) {\n return Promise.resolve({ kind: 'context', text: background ? BACKGROUND_HINT : VERBOSE_HINT });\n }\n return Promise.resolve({ kind: 'none' });\n}\n\nexport const runtime: ModuleRuntime = {\n tools: { ctx_run: ctxRun, ctx_fetch: ctxFetch, ctx_search: ctxSearch, ctx_get: ctxGet },\n hooks: { PreToolUse: preToolUse },\n doctor: (ctx) => {\n const recent = storeFor(ctx).recent(1)[0];\n return Promise.resolve([\n {\n name: 'output store',\n status: 'ok',\n detail: recent ? `last output ${recent.createdAt} (${recent.label.slice(0, 40)})` : 'empty',\n },\n ]);\n },\n dispose: () => {\n for (const store of stores.values()) store.close();\n stores.clear();\n return Promise.resolve();\n },\n};\n\nexport function runPurgeCommand(cwd: string): void {\n const ws = requireWorkspace(cwd);\n const store = OutputStore.open(join(ws.paths.yandecodeDir, 'context.db'));\n const removed = store.purgeOlderThan(new Date(Date.now() + DAY_MS));\n store.close();\n process.stdout.write(`removed ${removed} stored output(s)\\n`);\n}\n", "/**\n * Coarse classification of a shell command for the PreToolUse hook (ADR-019):\n * - `network`: prints a remote resource to stdout (curl/wget without an output file);\n * - `verbose`: commands whose output is routinely long (tests, builds, logs, history);\n * - `plain`: everything else.\n * Quoted text is ignored so `echo \"curl \u2026\"` or `--body \"run npm test\"` never match.\n */\nexport type CommandKind = 'network' | 'verbose' | 'plain';\n\nconst VERBOSE_PATTERNS: readonly RegExp[] = [\n /^(npm|pnpm|yarn|bun) (run )?(test|build|lint|typecheck|check)\\b/,\n /^npx (vitest|jest|tsc|eslint|mocha|playwright)\\b/,\n /^(vitest|jest|mocha|pytest|tox|phpunit|rspec)\\b/,\n /^(cargo|go) (test|build|clippy)\\b/,\n /^(mvn|gradle|\\.\\/gradlew|make|cmake|ctest|dotnet (test|build))\\b/,\n /^(docker|kubectl|podman) logs\\b/,\n /^journalctl\\b/,\n /^find \\//,\n];\n\nfunction stripQuoted(command: string): string {\n return command.replace(/'[^']*'|\"(?:\\\\.|[^\"\\\\])*\"/g, \"''\");\n}\n\nfunction segments(command: string): string[] {\n return command\n .split(/&&|\\|\\||;|\\|/)\n .map((s) => s.trim().replace(/^(sudo\\s+|(\\w+=\\S*\\s+)+)/, ''))\n .filter((s) => s.length > 0);\n}\n\nfunction isNetworkPrint(segment: string, redirected: boolean): boolean {\n if (/^curl\\b/.test(segment)) {\n return !redirected && !/\\s(-o|--output|-O|--remote-name)\\b/.test(segment);\n }\n // Plain `wget URL` saves to a file; only `-O -` / `-qO-` prints to stdout.\n return /^wget\\b/.test(segment) && /\\s-\\w*O\\s*-(\\s|$)/.test(`${segment} `);\n}\n\nfunction isUnboundedGitLog(segment: string): boolean {\n return /^git log\\b/.test(segment) && !/\\s(-n\\s*\\d+|-\\d+|--max-count)/.test(segment);\n}\n\nexport function classifyCommand(command: string): CommandKind {\n const bare = stripQuoted(command);\n const redirected = /[^2&]>\\s*[^&\\s]/.test(` ${bare}`);\n const parts = segments(bare);\n if (parts.some((s) => isNetworkPrint(s, redirected))) return 'network';\n if (parts.some((s) => VERBOSE_PATTERNS.some((p) => p.test(s)) || isUnboundedGitLog(s))) {\n return 'verbose';\n }\n return 'plain';\n}\n", "const ENTITIES: Readonly<Record<string, string>> = {\n amp: '&',\n lt: '<',\n gt: '>',\n quot: '\"',\n apos: \"'\",\n nbsp: ' ',\n};\n\nfunction decodeEntities(text: string): string {\n return text.replace(/&(#x[0-9a-f]+|#\\d+|[a-z]+);/gi, (match, entity: string) => {\n if (entity.startsWith('#x') || entity.startsWith('#X'))\n return String.fromCodePoint(parseInt(entity.slice(2), 16));\n if (entity.startsWith('#')) return String.fromCodePoint(Number(entity.slice(1)));\n return ENTITIES[entity.toLowerCase()] ?? match;\n });\n}\n\nconst BLOCK = 'p|div|ul|ol|table|section|article|header|footer|main|nav|pre|blockquote|figure|dl';\n\n/** Readable text from HTML without a DOM: headings become `#`, list items `- `, blocks paragraphs. */\nexport function htmlToText(html: string): string {\n const text = html\n .replace(/<(script|style|noscript|svg|template|head)\\b[\\s\\S]*?<\\/\\1>/gi, '')\n .replace(/<!--[\\s\\S]*?-->/g, '')\n .replace(/<h([1-6])\\b[^>]*>/gi, (_, level: string) => `\\n\\n${'#'.repeat(Number(level))} `)\n .replace(/<\\/h[1-6]>/gi, '\\n\\n')\n .replace(/<li\\b[^>]*>/gi, '\\n- ')\n .replace(new RegExp(`</?(${BLOCK})\\\\b[^>]*>`, 'gi'), '\\n\\n')\n .replace(/<(br|tr)\\b[^>]*>/gi, '\\n')\n .replace(/<[^>]+>/g, '');\n return decodeEntities(text)\n .split('\\n')\n .map((line) => line.replace(/[ \\t\\f\\v]+/g, ' ').trim())\n .join('\\n')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim();\n}\n", "import { randomBytes } from 'node:crypto';\nimport { mkdirSync } from 'node:fs';\nimport { dirname } from 'node:path';\nimport { openModuleDb, toFtsQuery, type Database } from '@yandecode/core';\n\nconst CHUNK_LINES = 20;\n\nconst MIGRATIONS: readonly string[] = [\n `\n CREATE TABLE outputs (\n handle TEXT PRIMARY KEY,\n source TEXT NOT NULL,\n label TEXT NOT NULL,\n exit_code INTEGER,\n duration_ms INTEGER NOT NULL,\n created_at TEXT NOT NULL,\n line_count INTEGER NOT NULL,\n byte_count INTEGER NOT NULL,\n body TEXT NOT NULL\n );\n CREATE INDEX outputs_created ON outputs (created_at);\n CREATE VIRTUAL TABLE output_fts USING fts5 (\n text, handle UNINDEXED, start_line UNINDEXED, end_line UNINDEXED,\n tokenize = 'porter unicode61'\n );\n CREATE TABLE session_hints (session_id TEXT NOT NULL, kind TEXT NOT NULL, PRIMARY KEY (session_id, kind));\n `,\n];\n\nexport interface SaveInput {\n source: 'run' | 'fetch';\n label: string;\n exitCode: number | null;\n durationMs: number;\n text: string;\n}\n\nexport interface StoredOutput {\n handle: string;\n source: string;\n label: string;\n exitCode: number | null;\n durationMs: number;\n createdAt: string;\n lineCount: number;\n byteCount: number;\n}\n\nexport interface OutputHit {\n handle: string;\n label: string;\n startLine: number;\n endLine: number;\n snippet: string;\n}\n\ninterface OutputRow {\n handle: string;\n source: string;\n label: string;\n exit_code: number | null;\n duration_ms: number;\n created_at: string;\n line_count: number;\n byte_count: number;\n}\n\n/**\n * Full command/web output kept out of the context window (ADR-019). Every line stays retrievable\n * verbatim by handle and range; FTS5 over 20-line chunks finds the relevant part later.\n */\nexport class OutputStore {\n private constructor(private readonly db: Database) {}\n\n static open(file: string): OutputStore {\n mkdirSync(dirname(file), { recursive: true });\n return new OutputStore(openModuleDb(file, MIGRATIONS));\n }\n\n close(): void {\n this.db.close();\n }\n\n save(input: SaveInput): string {\n const handle = `o${randomBytes(3).toString('hex')}`;\n const lines = input.text.split('\\n');\n this.db.transaction(() => {\n this.db\n .prepare(\n 'INSERT INTO outputs (handle, source, label, exit_code, duration_ms, created_at, line_count, byte_count, body) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',\n )\n .run(\n handle,\n input.source,\n input.label,\n input.exitCode,\n input.durationMs,\n new Date().toISOString(),\n lines.length,\n Buffer.byteLength(input.text),\n input.text,\n );\n const insert = this.db.prepare(\n 'INSERT INTO output_fts (text, handle, start_line, end_line) VALUES (?, ?, ?, ?)',\n );\n for (let start = 0; start < lines.length; start += CHUNK_LINES) {\n const end = Math.min(start + CHUNK_LINES, lines.length);\n insert.run(lines.slice(start, end).join('\\n'), handle, start + 1, end);\n }\n })();\n return handle;\n }\n\n get(handle: string): StoredOutput | null {\n const row = this.db\n .prepare(\n 'SELECT handle, source, label, exit_code, duration_ms, created_at, line_count, byte_count FROM outputs WHERE handle = ?',\n )\n .get(handle) as OutputRow | undefined;\n if (!row) return null;\n return {\n handle: row.handle,\n source: row.source,\n label: row.label,\n exitCode: row.exit_code,\n durationMs: row.duration_ms,\n createdAt: row.created_at,\n lineCount: row.line_count,\n byteCount: row.byte_count,\n };\n }\n\n /** 1-based inclusive line range, verbatim. */\n lines(handle: string, from: number, to: number): string[] {\n const row = this.db.prepare('SELECT body FROM outputs WHERE handle = ?').get(handle) as\n { body: string } | undefined;\n if (!row) return [];\n return row.body.split('\\n').slice(Math.max(0, from - 1), Math.max(0, to));\n }\n\n search(query: string, options: { handle?: string; limit?: number } = {}): OutputHit[] {\n const fts = toFtsQuery(query);\n if (!fts) return [];\n const filter = options.handle ? 'AND f.handle = ?' : '';\n const params: unknown[] = [\n fts,\n ...(options.handle ? [options.handle] : []),\n options.limit ?? 8,\n ];\n return (\n this.db\n .prepare(\n `SELECT f.handle, o.label, f.start_line, f.end_line,\n snippet(output_fts, 0, '', '', ' \u2026 ', 14) AS snippet\n FROM output_fts f JOIN outputs o ON o.handle = f.handle\n WHERE output_fts MATCH ? ${filter} ORDER BY rank LIMIT ?`,\n )\n .all(...params) as {\n handle: string;\n label: string;\n start_line: number;\n end_line: number;\n snippet: string;\n }[]\n ).map((r) => ({\n handle: r.handle,\n label: r.label,\n startLine: r.start_line,\n endLine: r.end_line,\n snippet: r.snippet.replace(/\\s+/g, ' ').trim(),\n }));\n }\n\n recent(limit: number): StoredOutput[] {\n const handles = this.db\n .prepare('SELECT handle FROM outputs ORDER BY created_at DESC LIMIT ?')\n .all(limit) as { handle: string }[];\n return handles.flatMap((h) => this.get(h.handle) ?? []);\n }\n\n purgeOlderThan(cutoff: Date): number {\n const old = this.db\n .prepare('SELECT handle FROM outputs WHERE created_at < ?')\n .all(cutoff.toISOString()) as { handle: string }[];\n this.db.transaction(() => {\n for (const { handle } of old) {\n this.db.prepare('DELETE FROM output_fts WHERE handle = ?').run(handle);\n this.db.prepare('DELETE FROM outputs WHERE handle = ?').run(handle);\n }\n })();\n return old.length;\n }\n\n /** True the first time `kind` is requested for a session: used for one-time hints. */\n firstTimeInSession(sessionId: string, kind: string): boolean {\n return (\n this.db\n .prepare('INSERT OR IGNORE INTO session_hints (session_id, kind) VALUES (?, ?)')\n .run(sessionId, kind).changes === 1\n );\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createRequire as __yandecodeCreateRequire } from 'node:module';
|
|
2
|
+
const require = __yandecodeCreateRequire(import.meta.url);
|
|
3
|
+
import {
|
|
4
|
+
decideChoice,
|
|
5
|
+
jevKey
|
|
6
|
+
} from "./chunk-TYLH4NIC.js";
|
|
7
|
+
import {
|
|
8
|
+
parseSettings,
|
|
9
|
+
resolveEnabled
|
|
10
|
+
} from "./chunk-QCQC73WX.js";
|
|
11
|
+
import {
|
|
12
|
+
MODULES,
|
|
13
|
+
routerSettings
|
|
14
|
+
} from "./chunk-7L6DD2VD.js";
|
|
15
|
+
import "./chunk-Y2MIXROU.js";
|
|
16
|
+
|
|
17
|
+
// packages/cli/src/modules/router/runtime.ts
|
|
18
|
+
function routableOptions(ctx) {
|
|
19
|
+
const enabled = new Set(resolveEnabled(MODULES, ctx.config.modules));
|
|
20
|
+
return MODULES.filter((m) => m.id !== "router" && enabled.has(m.id)).flatMap(
|
|
21
|
+
(m) => (m.routing ?? []).map((r) => ({ intent: r.intent, use: r.use }))
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
function buildCriteria(options) {
|
|
25
|
+
const criteria = {};
|
|
26
|
+
options.forEach((option, i) => {
|
|
27
|
+
criteria[`o${i}`] = option.intent;
|
|
28
|
+
});
|
|
29
|
+
criteria.none = "None of the tools fit: a plain edit, chit-chat, or an unclear ask.";
|
|
30
|
+
return criteria;
|
|
31
|
+
}
|
|
32
|
+
async function onUserPromptSubmit(input, ctx) {
|
|
33
|
+
const settings = parseSettings("router", routerSettings, ctx.settings);
|
|
34
|
+
const apiKey = jevKey();
|
|
35
|
+
const prompt = typeof input.prompt === "string" ? input.prompt.trim() : "";
|
|
36
|
+
if (!apiKey || prompt.length < settings.minPromptChars) return { kind: "none" };
|
|
37
|
+
const options = routableOptions(ctx);
|
|
38
|
+
if (options.length === 0) return { kind: "none" };
|
|
39
|
+
const answer = await decideChoice(
|
|
40
|
+
settings,
|
|
41
|
+
prompt,
|
|
42
|
+
"Which tool best fits the user's current request?",
|
|
43
|
+
buildCriteria(options),
|
|
44
|
+
apiKey
|
|
45
|
+
);
|
|
46
|
+
if (!answer || answer.choice === "none" || answer.confidence < settings.minConfidence)
|
|
47
|
+
return { kind: "none" };
|
|
48
|
+
const option = options[Number(answer.choice.slice(1))];
|
|
49
|
+
if (!option) return { kind: "none" };
|
|
50
|
+
ctx.log("routed", { use: option.use, confidence: answer.confidence });
|
|
51
|
+
return {
|
|
52
|
+
kind: "context",
|
|
53
|
+
text: `yandecode: this turn looks like \xAB${option.intent}\xBB \u2014 prefer \`${option.use}\`. If its schema is not loaded, ToolSearch(select) it instead of falling back to Bash.`
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
var runtime = {
|
|
57
|
+
hooks: { UserPromptSubmit: onUserPromptSubmit }
|
|
58
|
+
};
|
|
59
|
+
export {
|
|
60
|
+
buildCriteria,
|
|
61
|
+
routableOptions,
|
|
62
|
+
runtime
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=runtime-CKMVHAU4.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/modules/router/runtime.ts"],
|
|
4
|
+
"sourcesContent": ["import type { HookResult, ModuleContext, ModuleRuntime } from '@cli/modules/contract';\nimport { parseSettings } from '@cli/modules/host';\nimport { MODULES } from '@cli/modules/registry';\nimport { resolveEnabled } from '@cli/modules/resolve';\nimport { decideChoice, jevKey } from '@cli/shared/jev';\nimport { routerSettings } from './definition';\n\ninterface Option {\n intent: string;\n use: string;\n}\n\n/** The routing rules of every enabled module (except this one) become Jev's choices. */\nexport function routableOptions(ctx: Pick<ModuleContext, 'config'>): Option[] {\n const enabled = new Set(resolveEnabled(MODULES, ctx.config.modules));\n return MODULES.filter((m) => m.id !== 'router' && enabled.has(m.id)).flatMap((m) =>\n (m.routing ?? []).map((r) => ({ intent: r.intent, use: r.use })),\n );\n}\n\n/** Choice keys `o0..oN` mapped to the intent that describes each tool, plus a `none` escape. */\nexport function buildCriteria(options: Option[]): Record<string, string> {\n const criteria: Record<string, string> = {};\n options.forEach((option, i) => {\n criteria[`o${i}`] = option.intent;\n });\n criteria.none = 'None of the tools fit: a plain edit, chit-chat, or an unclear ask.';\n return criteria;\n}\n\nasync function onUserPromptSubmit(\n input: { prompt?: string },\n ctx: ModuleContext,\n): Promise<HookResult> {\n const settings = parseSettings('router', routerSettings, ctx.settings);\n const apiKey = jevKey();\n const prompt = typeof input.prompt === 'string' ? input.prompt.trim() : '';\n if (!apiKey || prompt.length < settings.minPromptChars) return { kind: 'none' };\n const options = routableOptions(ctx);\n if (options.length === 0) return { kind: 'none' };\n\n const answer = await decideChoice(\n settings,\n prompt,\n \"Which tool best fits the user's current request?\",\n buildCriteria(options),\n apiKey,\n );\n // Fail-open: a missing key, timeout, malformed answer, `none` or low confidence adds no hint.\n if (!answer || answer.choice === 'none' || answer.confidence < settings.minConfidence)\n return { kind: 'none' };\n const option = options[Number(answer.choice.slice(1))];\n if (!option) return { kind: 'none' };\n ctx.log('routed', { use: option.use, confidence: answer.confidence });\n return {\n kind: 'context',\n text: `yandecode: this turn looks like \u00AB${option.intent}\u00BB \u2014 prefer \\`${option.use}\\`. If its schema is not loaded, ToolSearch(select) it instead of falling back to Bash.`,\n };\n}\n\nexport const runtime: ModuleRuntime = {\n hooks: { UserPromptSubmit: onUserPromptSubmit },\n};\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;AAaO,SAAS,gBAAgB,KAA8C;AAC5E,QAAM,UAAU,IAAI,IAAI,eAAe,SAAS,IAAI,OAAO,OAAO,CAAC;AACnE,SAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO,YAAY,QAAQ,IAAI,EAAE,EAAE,CAAC,EAAE;AAAA,IAAQ,CAAC,OAC3E,EAAE,WAAW,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,QAAQ,KAAK,EAAE,IAAI,EAAE;AAAA,EACjE;AACF;AAGO,SAAS,cAAc,SAA2C;AACvE,QAAM,WAAmC,CAAC;AAC1C,UAAQ,QAAQ,CAAC,QAAQ,MAAM;AAC7B,aAAS,IAAI,CAAC,EAAE,IAAI,OAAO;AAAA,EAC7B,CAAC;AACD,WAAS,OAAO;AAChB,SAAO;AACT;AAEA,eAAe,mBACb,OACA,KACqB;AACrB,QAAM,WAAW,cAAc,UAAU,gBAAgB,IAAI,QAAQ;AACrE,QAAM,SAAS,OAAO;AACtB,QAAM,SAAS,OAAO,MAAM,WAAW,WAAW,MAAM,OAAO,KAAK,IAAI;AACxE,MAAI,CAAC,UAAU,OAAO,SAAS,SAAS,eAAgB,QAAO,EAAE,MAAM,OAAO;AAC9E,QAAM,UAAU,gBAAgB,GAAG;AACnC,MAAI,QAAQ,WAAW,EAAG,QAAO,EAAE,MAAM,OAAO;AAEhD,QAAM,SAAS,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc,OAAO;AAAA,IACrB;AAAA,EACF;AAEA,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU,OAAO,aAAa,SAAS;AACtE,WAAO,EAAE,MAAM,OAAO;AACxB,QAAM,SAAS,QAAQ,OAAO,OAAO,OAAO,MAAM,CAAC,CAAC,CAAC;AACrD,MAAI,CAAC,OAAQ,QAAO,EAAE,MAAM,OAAO;AACnC,MAAI,IAAI,UAAU,EAAE,KAAK,OAAO,KAAK,YAAY,OAAO,WAAW,CAAC;AACpE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,MAAM,uCAAoC,OAAO,MAAM,wBAAgB,OAAO,GAAG;AAAA,EACnF;AACF;AAEO,IAAM,UAAyB;AAAA,EACpC,OAAO,EAAE,kBAAkB,mBAAmB;AAChD;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|