kritya 0.8.26-beta → 0.8.27-beta
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 +3 -2
- package/dist/agent/skills.js +2 -1
- package/dist/config/config.js +2 -0
- package/dist/config/debug.js +3 -12
- package/dist/config/models.js +18 -0
- package/dist/index.js +25 -2
- package/dist/mcp/client.js +5 -4
- package/dist/net/urlSafety.js +3 -3
- package/dist/plugins/discover.js +2 -1
- package/dist/plugins/mcp.js +2 -1
- package/dist/repomap/repoMap.js +3 -3
- package/dist/stderr.js +47 -0
- package/dist/telemetry/metrics.js +11 -0
- package/dist/telemetry/tracer.js +2 -6
- package/dist/tools/glob.js +4 -4
- package/dist/tools/grep.js +4 -4
- package/dist/tools/ignore.js +2 -2
- package/dist/ui/ApiKeySetupPrompt.js +2 -2
- package/dist/ui/App.js +106 -86
- package/dist/ui/ElicitationPrompt.js +2 -2
- package/dist/ui/Markdown.js +5 -5
- package/dist/ui/McpTrustPrompt.js +1 -1
- package/dist/ui/PermissionPrompt.js +1 -1
- package/dist/ui/SelectList.js +1 -1
- package/dist/ui/Spinner.js +12 -8
- package/dist/ui/StatusLine.js +1 -1
- package/dist/ui/StreamViewport.js +29 -0
- package/dist/ui/TextInput.js +139 -0
- package/dist/ui/TranscriptItem.js +5 -4
- package/dist/ui/openInEditor.js +34 -0
- package/dist/ui/viewport.js +17 -50
- package/dist/undo/undo.js +11 -0
- package/package.json +13 -14
package/README.md
CHANGED
|
@@ -55,7 +55,7 @@ review → fix` for building something new end-to-end
|
|
|
55
55
|
|
|
56
56
|
## Setup
|
|
57
57
|
|
|
58
|
-
Requires Node.js >=22.
|
|
58
|
+
Requires Node.js >=22.19.0.
|
|
59
59
|
|
|
60
60
|
1. Get an API key from your chosen provider (click any
|
|
61
61
|
model → "Get API Key").
|
|
@@ -170,7 +170,8 @@ In-session commands (type `/` to see them with autocomplete; letters filter the
|
|
|
170
170
|
Custom `/commands` you define (see [docs/CONFIGURATION.md](docs/CONFIGURATION.md#custom-slash-commands)) also appear here.
|
|
171
171
|
|
|
172
172
|
`Esc` cancels a running request. `↑/↓` recalls input history. `Ctrl+O` toggles
|
|
173
|
-
full tool output. `Ctrl+
|
|
173
|
+
full tool output. `Ctrl+E` opens the file the agent touched most recently in
|
|
174
|
+
`$EDITOR`. `Ctrl+K` is the kill switch (see below). `Ctrl+C` exits.
|
|
174
175
|
|
|
175
176
|
### More features
|
|
176
177
|
|
package/dist/agent/skills.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { writeStderr } from "../stderr.js";
|
|
4
5
|
import { pluginsDir, pluginSkillsRoots, scanPlugins, userPluginsDir } from "../plugins/discover.js";
|
|
5
6
|
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/;
|
|
6
7
|
const KEY_VALUE_RE = /^([A-Za-z0-9_-]+):\s*(.*)$/;
|
|
@@ -73,7 +74,7 @@ function unquote(value) {
|
|
|
73
74
|
return value;
|
|
74
75
|
}
|
|
75
76
|
let warnSink = (message) => {
|
|
76
|
-
|
|
77
|
+
writeStderr(`kritya: ${message}\n`);
|
|
77
78
|
};
|
|
78
79
|
function warn(message) {
|
|
79
80
|
warnSink(message);
|
package/dist/config/config.js
CHANGED
|
@@ -161,6 +161,8 @@ export function resolveProvider(config, override) {
|
|
|
161
161
|
apiKey = merged.apiKey;
|
|
162
162
|
if (!apiKey && name === "nvidia")
|
|
163
163
|
apiKey = process.env.NVIDIA_API_KEY || config.apiKey;
|
|
164
|
+
if (apiKey)
|
|
165
|
+
apiKey = apiKey.trim();
|
|
164
166
|
return {
|
|
165
167
|
name,
|
|
166
168
|
baseUrl,
|
package/dist/config/debug.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { writeStderr } from "../stderr.js";
|
|
1
2
|
/**
|
|
2
3
|
* Most best-effort persistence (session/audit/telemetry writes, config and
|
|
3
4
|
* trust-file reads, hook loading, …) deliberately swallows its own errors —
|
|
@@ -10,12 +11,7 @@ export function debugLog(context, err) {
|
|
|
10
11
|
if ((process.env.KRITYA_DEBUG ?? "").toLowerCase() !== "1" && process.env.KRITYA_DEBUG !== "true")
|
|
11
12
|
return;
|
|
12
13
|
const message = err instanceof Error ? (err.stack ?? err.message) : String(err);
|
|
13
|
-
|
|
14
|
-
process.stderr.write(`[kritya debug] ${context}: ${message}\n`);
|
|
15
|
-
}
|
|
16
|
-
catch {
|
|
17
|
-
// stderr itself failing isn't something debug logging can do anything about
|
|
18
|
-
}
|
|
14
|
+
writeStderr(`[kritya debug] ${context}: ${message}\n`);
|
|
19
15
|
}
|
|
20
16
|
/**
|
|
21
17
|
* For best-effort persistence that a user actually needs to know failed
|
|
@@ -39,12 +35,7 @@ export function warnUser(context, err) {
|
|
|
39
35
|
}
|
|
40
36
|
warnedContexts.add(context);
|
|
41
37
|
const message = err instanceof Error ? err.message : String(err);
|
|
42
|
-
|
|
43
|
-
process.stderr.write(`[kritya] warning: ${context} failed: ${message}\n`);
|
|
44
|
-
}
|
|
45
|
-
catch {
|
|
46
|
-
// stderr itself failing isn't something this can do anything about
|
|
47
|
-
}
|
|
38
|
+
writeStderr(`[kritya] warning: ${context} failed: ${message}\n`);
|
|
48
39
|
debugLog(context, err);
|
|
49
40
|
}
|
|
50
41
|
/** Contexts currently warning via warnPersistenceFailure, in the order first seen. */
|
package/dist/config/models.js
CHANGED
|
@@ -45,6 +45,24 @@ export const CURATED_MODELS = [
|
|
|
45
45
|
note: "strong agentic tool use",
|
|
46
46
|
contextWindow: 256_000,
|
|
47
47
|
},
|
|
48
|
+
{
|
|
49
|
+
id: "z-ai/glm-5.3",
|
|
50
|
+
label: "GLM 5.3",
|
|
51
|
+
note: "strong agentic tool use",
|
|
52
|
+
contextWindow: 1_048_576,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: "z-ai/glm-5.3-flash",
|
|
56
|
+
label: "GLM 5.3 Flash",
|
|
57
|
+
note: "fast + cheap",
|
|
58
|
+
contextWindow: 1_048_576,
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: "deepseek-ai/deepseek-v4.1-flash",
|
|
62
|
+
label: "DeepSeek V4.1 Flash",
|
|
63
|
+
note: "fast + cheap",
|
|
64
|
+
contextWindow: 1_048_576,
|
|
65
|
+
},
|
|
48
66
|
];
|
|
49
67
|
export const DEFAULT_MODEL = CURATED_MODELS[0].id;
|
|
50
68
|
/**
|
package/dist/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import { AuditLog } from "./audit/audit.js";
|
|
|
16
16
|
import { runAuditCli } from "./audit/cli.js";
|
|
17
17
|
import { runSkillsCli } from "./agent/skillsCli.js";
|
|
18
18
|
import { createTracer, cleanupOldTelemetry } from "./telemetry/tracer.js";
|
|
19
|
-
import { createMeter } from "./telemetry/metrics.js";
|
|
19
|
+
import { createMeter, RENDER_BOUNDS_MS } from "./telemetry/metrics.js";
|
|
20
20
|
import { retentionDaysFor } from "./config/retention.js";
|
|
21
21
|
import { backgroundManager } from "./shell/background.js";
|
|
22
22
|
import { defaultSandboxMode, sandboxAvailable, sandboxUnavailableReason } from "./shell/sandbox.js";
|
|
@@ -415,6 +415,17 @@ async function main() {
|
|
|
415
415
|
sessionMeter.flushAndWait(),
|
|
416
416
|
new Promise((resolve) => setTimeout(resolve, 2000).unref()),
|
|
417
417
|
]);
|
|
418
|
+
// Hand the terminal back before leaving. Ink hides the cursor for the
|
|
419
|
+
// whole time it is rendering and only shows it again from its own
|
|
420
|
+
// teardown — which the process.exit() below skips, leaving the shell's
|
|
421
|
+
// prompt invisible until the user runs `reset`. unmount() is that
|
|
422
|
+
// teardown; it also finishes drawing the frame we are standing on.
|
|
423
|
+
try {
|
|
424
|
+
ui.instance?.unmount();
|
|
425
|
+
}
|
|
426
|
+
catch {
|
|
427
|
+
// Whatever brought us here may have been the UI itself. Exit anyway.
|
|
428
|
+
}
|
|
418
429
|
cleanup();
|
|
419
430
|
process.exit(code);
|
|
420
431
|
})();
|
|
@@ -709,12 +720,24 @@ async function main() {
|
|
|
709
720
|
// compaction — subagents (read-only or write) never do, even though they
|
|
710
721
|
// run the same Agent class and can also trigger auto-compaction.
|
|
711
722
|
agent.autoMemory = true;
|
|
723
|
+
// How long Ink takes to turn the React tree into a frame, per frame. This is
|
|
724
|
+
// the part of drawing a screen that grows with the size of the transcript, so
|
|
725
|
+
// it is the number that says whether the UI is getting sluggish — the
|
|
726
|
+
// terminal write it excludes is bounded by the terminal, not by us.
|
|
727
|
+
// `renderTime` excludes Ink's own throttling, so it is the raw cost of the
|
|
728
|
+
// frame rather than the interval between frames.
|
|
729
|
+
const renderHistogram = sessionMeter.histogram("kritya.render.duration_ms", RENDER_BOUNDS_MS);
|
|
712
730
|
ui.instance = render(_jsx(App, { agent: agent, workspace: workspace, modelRef: modelRef, providerRef: providerRef, config: config, resumedCount: initialHistory.length, initialTasks: initialTasks, undoStack: undoStack, uiBridge: uiBridge, resumeSessions: resumeSessions.length ? resumeSessions : undefined, customCommands: loadCustomCommands(workspace, trustWorkspace, plugins), mcpToolCount: mcpTools.length, onSwitchClient: (newClient) => {
|
|
713
731
|
client = newClient;
|
|
714
732
|
}, onRequestPermissionReady: (fn) => {
|
|
715
733
|
permissionRef.current = fn;
|
|
716
734
|
}, onRequestElicitationReady: (fn) => {
|
|
717
735
|
elicitationRef.current = fn;
|
|
718
|
-
}, privacyMode: privacyMode, firstLaunch: firstLaunch })
|
|
736
|
+
}, privacyMode: privacyMode, firstLaunch: firstLaunch }), {
|
|
737
|
+
onRender: ({ renderTime }) => renderHistogram.record(renderTime),
|
|
738
|
+
// Repaint only the lines that changed instead of the whole frame:
|
|
739
|
+
// less flicker and less CPU while streaming. Ink 8 native.
|
|
740
|
+
incrementalRendering: true,
|
|
741
|
+
});
|
|
719
742
|
markBannerSeen(workspace);
|
|
720
743
|
}
|
package/dist/mcp/client.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { writeStderr } from "../stderr.js";
|
|
4
5
|
import { VERSION } from "../version.js";
|
|
5
6
|
import { NOOP_TRACER } from "../telemetry/tracer.js";
|
|
6
7
|
import { McpAuthRequiredError } from "./oauth.js";
|
|
@@ -644,14 +645,14 @@ function exposedToolName(server, toolName) {
|
|
|
644
645
|
* is registered on the transport so `tools/call` can mirror argument values
|
|
645
646
|
* into `Mcp-Param-{Name}` headers later — see `ModernHttpTransport.
|
|
646
647
|
* setToolHeaderMap`/`send`. Logged the same way other "skip with a warning"
|
|
647
|
-
* MCP failures are (`
|
|
648
|
+
* MCP failures are (`writeStderr("kritya: ...")`).
|
|
648
649
|
*/
|
|
649
650
|
function filterHeaderAnnotatedTools(server, specs, transport) {
|
|
650
651
|
const kept = [];
|
|
651
652
|
for (const spec of specs) {
|
|
652
653
|
const result = validateToolHeaders(spec.inputSchema);
|
|
653
654
|
if (!result.ok) {
|
|
654
|
-
|
|
655
|
+
writeStderr(`kritya: MCP server "${server}" tool "${spec.name}" excluded — invalid x-mcp-header ` +
|
|
655
656
|
`annotation: ${result.reason}\n`);
|
|
656
657
|
continue;
|
|
657
658
|
}
|
|
@@ -675,7 +676,7 @@ function filterSchemaSafeTools(server, specs) {
|
|
|
675
676
|
for (const spec of specs) {
|
|
676
677
|
const result = checkSchemaSafety(spec.inputSchema);
|
|
677
678
|
if (!result.ok) {
|
|
678
|
-
|
|
679
|
+
writeStderr(`kritya: MCP server "${server}" tool "${spec.name}" excluded — unsafe inputSchema: ` +
|
|
679
680
|
`${result.reason}\n`);
|
|
680
681
|
continue;
|
|
681
682
|
}
|
|
@@ -852,7 +853,7 @@ export async function connectServer(name, cfg, trace) {
|
|
|
852
853
|
}
|
|
853
854
|
else {
|
|
854
855
|
status.error = err instanceof Error ? err.message : String(err);
|
|
855
|
-
|
|
856
|
+
writeStderr(`kritya: MCP server "${name}" failed to start: ${status.error}\n`);
|
|
856
857
|
span.setStatus("ERROR", status.error);
|
|
857
858
|
// status.error comes from the (untrusted) server process's own error
|
|
858
859
|
// output, which could echo back an env var or header value it was
|
package/dist/net/urlSafety.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { lookup as dnsLookup } from "node:dns/promises";
|
|
2
|
-
import { Agent } from "undici";
|
|
2
|
+
import { Agent, Dispatcher1Wrapper } from "undici";
|
|
3
3
|
/**
|
|
4
4
|
* Shared "is this host on a private/internal network" check, used by both
|
|
5
5
|
* fetch_url (src/tools/fetchUrl.ts) and the MCP HTTP transport guard
|
|
@@ -210,7 +210,7 @@ export function assertSafeUrl(label, url) {
|
|
|
210
210
|
* it's safe to share across every call site that uses the same policy.
|
|
211
211
|
*/
|
|
212
212
|
function createPinnedDispatcher(isBlockedAddress) {
|
|
213
|
-
return new Agent({
|
|
213
|
+
return new Dispatcher1Wrapper(new Agent({
|
|
214
214
|
connect: {
|
|
215
215
|
lookup(hostname, options, callback) {
|
|
216
216
|
dnsLookup(hostname, { all: true })
|
|
@@ -235,7 +235,7 @@ function createPinnedDispatcher(isBlockedAddress) {
|
|
|
235
235
|
.catch((err) => callback(err, []));
|
|
236
236
|
},
|
|
237
237
|
},
|
|
238
|
-
});
|
|
238
|
+
}));
|
|
239
239
|
}
|
|
240
240
|
/** For callers that never allow loopback either (fetch_url). */
|
|
241
241
|
export const pinnedDispatcher = createPinnedDispatcher(isPrivateOrLoopbackHost);
|
package/dist/plugins/discover.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { writeStderr } from "../stderr.js";
|
|
4
5
|
/**
|
|
5
6
|
* Scans each root for `<name>/plugin.json` folders, in name order, and reports
|
|
6
7
|
* both what loaded and why anything didn't. Roots that don't exist are
|
|
@@ -73,7 +74,7 @@ export function scanPluginsDetailed(roots) {
|
|
|
73
74
|
return { loaded: [...seen.values()], skipped };
|
|
74
75
|
}
|
|
75
76
|
let warnSink = (message) => {
|
|
76
|
-
|
|
77
|
+
writeStderr(`kritya: ${message}\n`);
|
|
77
78
|
};
|
|
78
79
|
function warn(message) {
|
|
79
80
|
warnSink(message);
|
package/dist/plugins/mcp.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { writeStderr } from "../stderr.js";
|
|
3
4
|
function isServerConfig(v) {
|
|
4
5
|
if (!v || typeof v !== "object")
|
|
5
6
|
return false;
|
|
@@ -65,7 +66,7 @@ export function scanPluginMcpServers(plugins) {
|
|
|
65
66
|
* map for labeling `/mcp` output. Skips are reported via `warn` (defaults to
|
|
66
67
|
* stderr, like scanPlugins/scanSkills).
|
|
67
68
|
*/
|
|
68
|
-
export function loadPluginMcpServers(plugins, warn = (m) =>
|
|
69
|
+
export function loadPluginMcpServers(plugins, warn = (m) => writeStderr(`kritya: ${m}\n`)) {
|
|
69
70
|
const { loaded, skipped } = scanPluginMcpServers(plugins);
|
|
70
71
|
for (const s of skipped) {
|
|
71
72
|
warn(`skipping mcp server "${s.name}" from plugin "${s.pluginName}": ${s.reason}`);
|
package/dist/repomap/repoMap.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import fs from "node:fs/promises";
|
|
11
11
|
import path from "node:path";
|
|
12
|
-
import
|
|
12
|
+
import { glob } from "tinyglobby";
|
|
13
13
|
import { isPathSafe, resolveSafe } from "../tools/common.js";
|
|
14
14
|
import { loadIgnorePatterns } from "../tools/ignore.js";
|
|
15
15
|
import { CODE_EXTENSIONS, extractSymbols } from "./symbols.js";
|
|
@@ -88,13 +88,13 @@ export async function buildRepoMap(workspace, subdir = ".", options = {}) {
|
|
|
88
88
|
const opts = { ...DEFAULTS, ...options };
|
|
89
89
|
const root = resolveSafe(workspace, subdir || ".");
|
|
90
90
|
const scopeLabel = path.relative(workspace, root) || "(workspace root)";
|
|
91
|
-
const found = await
|
|
91
|
+
const found = await glob("**/*", {
|
|
92
92
|
cwd: root,
|
|
93
93
|
dot: false,
|
|
94
94
|
onlyFiles: true,
|
|
95
95
|
followSymbolicLinks: false,
|
|
96
|
+
expandDirectories: false,
|
|
96
97
|
ignore: ["**/node_modules/**", "**/.git/**", ...loadIgnorePatterns(workspace)],
|
|
97
|
-
suppressErrors: true,
|
|
98
98
|
});
|
|
99
99
|
// Keep only source files, then rank by path so the maxFiles cap (which bounds
|
|
100
100
|
// how many files we actually read) retains the most important ones.
|
package/dist/stderr.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a warning goes while someone else owns the terminal.
|
|
3
|
+
*
|
|
4
|
+
* Ink owns the terminal for the whole time the UI is up. It keeps its frame in
|
|
5
|
+
* a known place, and anything that reaches the terminal *through* Ink is
|
|
6
|
+
* written between clearing that frame and repainting it — so the text lands
|
|
7
|
+
* above the UI and stays there. A raw `process.stderr.write` bypasses all of
|
|
8
|
+
* that: the bytes land wherever the cursor happened to be, in the middle of
|
|
9
|
+
* the frame, and the next repaint erases and redraws against a line count that
|
|
10
|
+
* no longer matches the screen. The warning comes out garbled and takes the
|
|
11
|
+
* frame with it.
|
|
12
|
+
*
|
|
13
|
+
* Ink patches `console` for exactly this reason, but `console.error` is not
|
|
14
|
+
* available to the plain module functions that raise these warnings — an agent
|
|
15
|
+
* loop, an MCP client, a plugin loader — and a hook (`useStderr`) cannot be
|
|
16
|
+
* called from one. So the UI registers a sink on mount and clears it on
|
|
17
|
+
* unmount, and everything else goes through `writeStderr`, which uses the sink
|
|
18
|
+
* when there is one and raw stderr when there is not: CLI subcommands, headless
|
|
19
|
+
* runs, and anything before the UI mounts or after it is torn down.
|
|
20
|
+
*
|
|
21
|
+
* Deliberately not used by `crash.ts`: by the time a crash handler runs, the
|
|
22
|
+
* UI is being torn down and its frame state is not trustworthy, so the crash
|
|
23
|
+
* report goes straight to stderr.
|
|
24
|
+
*/
|
|
25
|
+
let sink;
|
|
26
|
+
/** Installed by the mounted UI. Pass `undefined` on unmount. */
|
|
27
|
+
export function setStderrSink(fn) {
|
|
28
|
+
sink = fn;
|
|
29
|
+
}
|
|
30
|
+
/** Write a warning to stderr, through the UI when one is mounted. */
|
|
31
|
+
export function writeStderr(text) {
|
|
32
|
+
if (sink) {
|
|
33
|
+
try {
|
|
34
|
+
sink(text);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// A sink that throws must not cost us the warning itself.
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
process.stderr.write(text);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// stderr is gone; there is nowhere left to put it.
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -11,6 +11,17 @@ export const NOOP_METER = {
|
|
|
11
11
|
stop() { },
|
|
12
12
|
};
|
|
13
13
|
export const DEFAULT_LATENCY_BOUNDS_MS = [10, 50, 100, 250, 500, 1000, 2500, 5000, 10000, 30000];
|
|
14
|
+
/**
|
|
15
|
+
* Buckets for Ink's per-frame `onRender` timing.
|
|
16
|
+
*
|
|
17
|
+
* The default latency bounds are useless here: drawing a frame is a
|
|
18
|
+
* sub-millisecond to low-single-digit-millisecond affair, so every sample would
|
|
19
|
+
* land in the first bucket and the histogram would say nothing. These cluster
|
|
20
|
+
* below the frame budget instead, and 33ms is the edge that matters — Ink caps
|
|
21
|
+
* rendering at 30fps (1000 / 30 ≈ 33.3ms), so a sample past it is a frame the
|
|
22
|
+
* UI could not draw on time.
|
|
23
|
+
*/
|
|
24
|
+
export const RENDER_BOUNDS_MS = [0.25, 0.5, 1, 2, 4, 8, 16, 24, 33, 66, 133];
|
|
14
25
|
function nowUnixNano() {
|
|
15
26
|
return (BigInt(Date.now()) * 1000000n).toString();
|
|
16
27
|
}
|
package/dist/telemetry/tracer.js
CHANGED
|
@@ -4,6 +4,7 @@ import path from "node:path";
|
|
|
4
4
|
import { CONFIG_DIR } from "../config/config.js";
|
|
5
5
|
import { hardenWindowsDir } from "../config/winAcl.js";
|
|
6
6
|
import { debugLog, warnPersistenceFailure } from "../config/debug.js";
|
|
7
|
+
import { writeStderr } from "../stderr.js";
|
|
7
8
|
import { VERSION } from "../version.js";
|
|
8
9
|
import { encodeSpan, postOtlp } from "./otlp.js";
|
|
9
10
|
function nowUnixNano() {
|
|
@@ -164,12 +165,7 @@ function fileSink(file) {
|
|
|
164
165
|
}
|
|
165
166
|
function consoleSink() {
|
|
166
167
|
return (span) => {
|
|
167
|
-
|
|
168
|
-
process.stderr.write(`[otel] ${JSON.stringify(span)}\n`);
|
|
169
|
-
}
|
|
170
|
-
catch {
|
|
171
|
-
// best-effort
|
|
172
|
-
}
|
|
168
|
+
writeStderr(`[otel] ${JSON.stringify(span)}\n`);
|
|
173
169
|
};
|
|
174
170
|
}
|
|
175
171
|
function resourceFor(sessionId) {
|
package/dist/tools/glob.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import
|
|
2
|
+
import { glob } from "tinyglobby";
|
|
3
3
|
import { isPathSafe, truncateResult, countLines } from "./common.js";
|
|
4
4
|
import { loadIgnorePatterns } from "./ignore.js";
|
|
5
5
|
export const globTool = {
|
|
@@ -17,14 +17,14 @@ export const globTool = {
|
|
|
17
17
|
summarize: (args) => `Glob ${args.pattern}`,
|
|
18
18
|
resultSummary: (output) => countLines(output, "match", "matches"),
|
|
19
19
|
async execute(args, ctx) {
|
|
20
|
-
// Models on Windows sometimes emit backslash paths;
|
|
21
|
-
const files = await
|
|
20
|
+
// Models on Windows sometimes emit backslash paths; glob patterns need forward slashes.
|
|
21
|
+
const files = await glob(String(args.pattern).replaceAll("\\", "/"), {
|
|
22
22
|
cwd: ctx.workspace,
|
|
23
23
|
dot: false,
|
|
24
24
|
onlyFiles: true,
|
|
25
25
|
followSymbolicLinks: false,
|
|
26
|
+
expandDirectories: false,
|
|
26
27
|
ignore: ["**/node_modules/**", "**/.git/**", ...loadIgnorePatterns(ctx.workspace)],
|
|
27
|
-
suppressErrors: true,
|
|
28
28
|
});
|
|
29
29
|
const safeFiles = files.filter((f) => isPathSafe(ctx.workspace, path.join(ctx.workspace, f)));
|
|
30
30
|
const capped = safeFiles.sort().slice(0, 200);
|
package/dist/tools/grep.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
-
import
|
|
3
|
+
import { glob } from "tinyglobby";
|
|
4
4
|
import { isPathSafe, resolveSafe, safeCompileRegex, truncateResult, meaningfulLines, } from "./common.js";
|
|
5
5
|
import { loadIgnorePatterns } from "./ignore.js";
|
|
6
6
|
const MAX_MATCHES = 200;
|
|
@@ -34,14 +34,14 @@ export const grepTool = {
|
|
|
34
34
|
async execute(args, ctx) {
|
|
35
35
|
const regex = safeCompileRegex(String(args.pattern));
|
|
36
36
|
const searchRoot = resolveSafe(ctx.workspace, String(args.path ?? "."));
|
|
37
|
-
// Models on Windows sometimes emit backslash paths;
|
|
38
|
-
const files = await
|
|
37
|
+
// Models on Windows sometimes emit backslash paths; glob patterns need forward slashes.
|
|
38
|
+
const files = await glob(String(args.include ?? "**/*").replaceAll("\\", "/"), {
|
|
39
39
|
cwd: searchRoot,
|
|
40
40
|
dot: false,
|
|
41
41
|
onlyFiles: true,
|
|
42
42
|
followSymbolicLinks: false,
|
|
43
|
+
expandDirectories: false,
|
|
43
44
|
ignore: ["**/node_modules/**", "**/.git/**", ...loadIgnorePatterns(ctx.workspace)],
|
|
44
|
-
suppressErrors: true,
|
|
45
45
|
});
|
|
46
46
|
const matches = [];
|
|
47
47
|
for (const file of files) {
|
package/dist/tools/ignore.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
const IGNORE_FILE = ".krityaignore";
|
|
4
|
-
/** Converts one gitignore-style line into
|
|
4
|
+
/** Converts one gitignore-style line into glob ignore pattern(s). */
|
|
5
5
|
function toGlobPatterns(line) {
|
|
6
6
|
let pattern = line.trim();
|
|
7
7
|
if (!pattern || pattern.startsWith("#") || pattern.startsWith("!"))
|
|
@@ -15,7 +15,7 @@ function toGlobPatterns(line) {
|
|
|
15
15
|
const base = anchored ? pattern : `**/${pattern}`;
|
|
16
16
|
return isDir ? [`${base}/**`] : [base, `${base}/**`];
|
|
17
17
|
}
|
|
18
|
-
/** Reads `<workspace>/.krityaignore` (gitignore-style, one pattern per line) as
|
|
18
|
+
/** Reads `<workspace>/.krityaignore` (gitignore-style, one pattern per line) as glob ignore patterns. */
|
|
19
19
|
export function loadIgnorePatterns(workspace) {
|
|
20
20
|
let raw;
|
|
21
21
|
try {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState } from "react";
|
|
3
3
|
import { Box, Text, useInput } from "ink";
|
|
4
|
-
import TextInput from "
|
|
4
|
+
import { TextInput } from "./TextInput.js";
|
|
5
5
|
/**
|
|
6
6
|
* Shown once, only when the active provider has no API key resolved, on the
|
|
7
7
|
* interactive TTY path (never from headless — see runHeadless in
|