fixo-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +530 -0
- package/dist/agent/agent-client.d.ts +108 -0
- package/dist/agent/agent-client.d.ts.map +1 -0
- package/dist/agent/agent-client.js +1247 -0
- package/dist/agent/agent-client.js.map +1 -0
- package/dist/agent/agent-pool.d.ts +20 -0
- package/dist/agent/agent-pool.d.ts.map +1 -0
- package/dist/agent/agent-pool.js +217 -0
- package/dist/agent/agent-pool.js.map +1 -0
- package/dist/agent/background-awareness.d.ts +55 -0
- package/dist/agent/background-awareness.d.ts.map +1 -0
- package/dist/agent/background-awareness.js +104 -0
- package/dist/agent/background-awareness.js.map +1 -0
- package/dist/agent/command-parser.d.ts +33 -0
- package/dist/agent/command-parser.d.ts.map +1 -0
- package/dist/agent/command-parser.js +120 -0
- package/dist/agent/command-parser.js.map +1 -0
- package/dist/agent/context-budget.d.ts +91 -0
- package/dist/agent/context-budget.d.ts.map +1 -0
- package/dist/agent/context-budget.js +219 -0
- package/dist/agent/context-budget.js.map +1 -0
- package/dist/agent/conversation.d.ts +190 -0
- package/dist/agent/conversation.d.ts.map +1 -0
- package/dist/agent/conversation.js +547 -0
- package/dist/agent/conversation.js.map +1 -0
- package/dist/agent/hooks.d.ts +72 -0
- package/dist/agent/hooks.d.ts.map +1 -0
- package/dist/agent/hooks.js +214 -0
- package/dist/agent/hooks.js.map +1 -0
- package/dist/agent/mcp-bridge.d.ts +13 -0
- package/dist/agent/mcp-bridge.d.ts.map +1 -0
- package/dist/agent/mcp-bridge.js +86 -0
- package/dist/agent/mcp-bridge.js.map +1 -0
- package/dist/agent/mcp-client.d.ts +24 -0
- package/dist/agent/mcp-client.d.ts.map +1 -0
- package/dist/agent/mcp-client.js +146 -0
- package/dist/agent/mcp-client.js.map +1 -0
- package/dist/agent/mcp-manager.d.ts +13 -0
- package/dist/agent/mcp-manager.d.ts.map +1 -0
- package/dist/agent/mcp-manager.js +84 -0
- package/dist/agent/mcp-manager.js.map +1 -0
- package/dist/agent/mcp-registry.d.ts +45 -0
- package/dist/agent/mcp-registry.d.ts.map +1 -0
- package/dist/agent/mcp-registry.js +98 -0
- package/dist/agent/mcp-registry.js.map +1 -0
- package/dist/agent/orchestrator.d.ts +14 -0
- package/dist/agent/orchestrator.d.ts.map +1 -0
- package/dist/agent/orchestrator.js +118 -0
- package/dist/agent/orchestrator.js.map +1 -0
- package/dist/agent/parser-adapter.d.ts +120 -0
- package/dist/agent/parser-adapter.d.ts.map +1 -0
- package/dist/agent/parser-adapter.js +265 -0
- package/dist/agent/parser-adapter.js.map +1 -0
- package/dist/agent/parsers/imports.d.ts +11 -0
- package/dist/agent/parsers/imports.d.ts.map +1 -0
- package/dist/agent/parsers/imports.js +94 -0
- package/dist/agent/parsers/imports.js.map +1 -0
- package/dist/agent/parsers/shell.d.ts +23 -0
- package/dist/agent/parsers/shell.d.ts.map +1 -0
- package/dist/agent/parsers/shell.js +200 -0
- package/dist/agent/parsers/shell.js.map +1 -0
- package/dist/agent/parsers/symbols.d.ts +17 -0
- package/dist/agent/parsers/symbols.d.ts.map +1 -0
- package/dist/agent/parsers/symbols.js +103 -0
- package/dist/agent/parsers/symbols.js.map +1 -0
- package/dist/agent/permissions.d.ts +65 -0
- package/dist/agent/permissions.d.ts.map +1 -0
- package/dist/agent/permissions.js +219 -0
- package/dist/agent/permissions.js.map +1 -0
- package/dist/agent/predictive-gate.d.ts +69 -0
- package/dist/agent/predictive-gate.d.ts.map +1 -0
- package/dist/agent/predictive-gate.js +128 -0
- package/dist/agent/predictive-gate.js.map +1 -0
- package/dist/agent/provider-cooldown.d.ts +144 -0
- package/dist/agent/provider-cooldown.d.ts.map +1 -0
- package/dist/agent/provider-cooldown.js +300 -0
- package/dist/agent/provider-cooldown.js.map +1 -0
- package/dist/agent/providers-manager.d.ts +109 -0
- package/dist/agent/providers-manager.d.ts.map +1 -0
- package/dist/agent/providers-manager.js +464 -0
- package/dist/agent/providers-manager.js.map +1 -0
- package/dist/agent/repo-map.d.ts +6 -0
- package/dist/agent/repo-map.d.ts.map +1 -0
- package/dist/agent/repo-map.js +221 -0
- package/dist/agent/repo-map.js.map +1 -0
- package/dist/agent/retry.d.ts +103 -0
- package/dist/agent/retry.d.ts.map +1 -0
- package/dist/agent/retry.js +276 -0
- package/dist/agent/retry.js.map +1 -0
- package/dist/agent/search/index.d.ts +61 -0
- package/dist/agent/search/index.d.ts.map +1 -0
- package/dist/agent/search/index.js +314 -0
- package/dist/agent/search/index.js.map +1 -0
- package/dist/agent/single-agent.d.ts +76 -0
- package/dist/agent/single-agent.d.ts.map +1 -0
- package/dist/agent/single-agent.js +697 -0
- package/dist/agent/single-agent.js.map +1 -0
- package/dist/agent/skills.d.ts +22 -0
- package/dist/agent/skills.d.ts.map +1 -0
- package/dist/agent/skills.js +139 -0
- package/dist/agent/skills.js.map +1 -0
- package/dist/agent/stream-glue.d.ts +85 -0
- package/dist/agent/stream-glue.d.ts.map +1 -0
- package/dist/agent/stream-glue.js +120 -0
- package/dist/agent/stream-glue.js.map +1 -0
- package/dist/agent/subagent.d.ts +72 -0
- package/dist/agent/subagent.d.ts.map +1 -0
- package/dist/agent/subagent.js +193 -0
- package/dist/agent/subagent.js.map +1 -0
- package/dist/agent/telemetry.d.ts +192 -0
- package/dist/agent/telemetry.d.ts.map +1 -0
- package/dist/agent/telemetry.js +400 -0
- package/dist/agent/telemetry.js.map +1 -0
- package/dist/agent/tokenizer.d.ts +42 -0
- package/dist/agent/tokenizer.d.ts.map +1 -0
- package/dist/agent/tokenizer.js +107 -0
- package/dist/agent/tokenizer.js.map +1 -0
- package/dist/agent/tool-executor.d.ts +289 -0
- package/dist/agent/tool-executor.d.ts.map +1 -0
- package/dist/agent/tool-executor.js +2519 -0
- package/dist/agent/tool-executor.js.map +1 -0
- package/dist/agent/web-impl.d.ts +2 -0
- package/dist/agent/web-impl.d.ts.map +1 -0
- package/dist/agent/web-impl.js +34 -0
- package/dist/agent/web-impl.js.map +1 -0
- package/dist/agent/web.d.ts +8 -0
- package/dist/agent/web.d.ts.map +1 -0
- package/dist/agent/web.js +8 -0
- package/dist/agent/web.js.map +1 -0
- package/dist/agent/worker-agent.d.ts +27 -0
- package/dist/agent/worker-agent.d.ts.map +1 -0
- package/dist/agent/worker-agent.js +503 -0
- package/dist/agent/worker-agent.js.map +1 -0
- package/dist/config.d.ts +162 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +138 -0
- package/dist/config.js.map +1 -0
- package/dist/context/fixo-md-watcher.d.ts +42 -0
- package/dist/context/fixo-md-watcher.d.ts.map +1 -0
- package/dist/context/fixo-md-watcher.js +126 -0
- package/dist/context/fixo-md-watcher.js.map +1 -0
- package/dist/context/fixo-md.d.ts +50 -0
- package/dist/context/fixo-md.d.ts.map +1 -0
- package/dist/context/fixo-md.js +118 -0
- package/dist/context/fixo-md.js.map +1 -0
- package/dist/context/todo.d.ts +65 -0
- package/dist/context/todo.d.ts.map +1 -0
- package/dist/context/todo.js +194 -0
- package/dist/context/todo.js.map +1 -0
- package/dist/git/git-manager.d.ts +33 -0
- package/dist/git/git-manager.d.ts.map +1 -0
- package/dist/git/git-manager.js +293 -0
- package/dist/git/git-manager.js.map +1 -0
- package/dist/git/git-ops.d.ts +10 -0
- package/dist/git/git-ops.d.ts.map +1 -0
- package/dist/git/git-ops.js +131 -0
- package/dist/git/git-ops.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +352 -0
- package/dist/index.js.map +1 -0
- package/dist/indexer.d.ts +30 -0
- package/dist/indexer.d.ts.map +1 -0
- package/dist/indexer.js +273 -0
- package/dist/indexer.js.map +1 -0
- package/dist/lsp/lsp-client.d.ts +24 -0
- package/dist/lsp/lsp-client.d.ts.map +1 -0
- package/dist/lsp/lsp-client.js +205 -0
- package/dist/lsp/lsp-client.js.map +1 -0
- package/dist/lsp/lsp-manager.d.ts +17 -0
- package/dist/lsp/lsp-manager.d.ts.map +1 -0
- package/dist/lsp/lsp-manager.js +154 -0
- package/dist/lsp/lsp-manager.js.map +1 -0
- package/dist/lsp/lsp-pre-save.d.ts +137 -0
- package/dist/lsp/lsp-pre-save.d.ts.map +1 -0
- package/dist/lsp/lsp-pre-save.js +245 -0
- package/dist/lsp/lsp-pre-save.js.map +1 -0
- package/dist/lsp/syntax-fallback.d.ts +83 -0
- package/dist/lsp/syntax-fallback.d.ts.map +1 -0
- package/dist/lsp/syntax-fallback.js +275 -0
- package/dist/lsp/syntax-fallback.js.map +1 -0
- package/dist/model-outcomes.d.ts +12 -0
- package/dist/model-outcomes.d.ts.map +1 -0
- package/dist/model-outcomes.js +46 -0
- package/dist/model-outcomes.js.map +1 -0
- package/dist/planner.d.ts +32 -0
- package/dist/planner.d.ts.map +1 -0
- package/dist/planner.js +163 -0
- package/dist/planner.js.map +1 -0
- package/dist/project-memory.d.ts +29 -0
- package/dist/project-memory.d.ts.map +1 -0
- package/dist/project-memory.js +349 -0
- package/dist/project-memory.js.map +1 -0
- package/dist/review.d.ts +2 -0
- package/dist/review.d.ts.map +1 -0
- package/dist/review.js +61 -0
- package/dist/review.js.map +1 -0
- package/dist/runtime/background-jobs.d.ts +97 -0
- package/dist/runtime/background-jobs.d.ts.map +1 -0
- package/dist/runtime/background-jobs.js +331 -0
- package/dist/runtime/background-jobs.js.map +1 -0
- package/dist/runtime/credential-vault.d.ts +124 -0
- package/dist/runtime/credential-vault.d.ts.map +1 -0
- package/dist/runtime/credential-vault.js +184 -0
- package/dist/runtime/credential-vault.js.map +1 -0
- package/dist/runtime/loop-trap.d.ts +197 -0
- package/dist/runtime/loop-trap.d.ts.map +1 -0
- package/dist/runtime/loop-trap.js +420 -0
- package/dist/runtime/loop-trap.js.map +1 -0
- package/dist/runtime/policy.d.ts +15 -0
- package/dist/runtime/policy.d.ts.map +1 -0
- package/dist/runtime/policy.js +60 -0
- package/dist/runtime/policy.js.map +1 -0
- package/dist/runtime/redaction.d.ts +66 -0
- package/dist/runtime/redaction.d.ts.map +1 -0
- package/dist/runtime/redaction.js +155 -0
- package/dist/runtime/redaction.js.map +1 -0
- package/dist/runtime/session-snapshots.d.ts +76 -0
- package/dist/runtime/session-snapshots.d.ts.map +1 -0
- package/dist/runtime/session-snapshots.js +166 -0
- package/dist/runtime/session-snapshots.js.map +1 -0
- package/dist/runtime/staging.d.ts +205 -0
- package/dist/runtime/staging.d.ts.map +1 -0
- package/dist/runtime/staging.js +526 -0
- package/dist/runtime/staging.js.map +1 -0
- package/dist/runtime/task-session.d.ts +95 -0
- package/dist/runtime/task-session.d.ts.map +1 -0
- package/dist/runtime/task-session.js +263 -0
- package/dist/runtime/task-session.js.map +1 -0
- package/dist/runtime/worktree.d.ts +55 -0
- package/dist/runtime/worktree.d.ts.map +1 -0
- package/dist/runtime/worktree.js +175 -0
- package/dist/runtime/worktree.js.map +1 -0
- package/dist/setup-wizard.d.ts +8 -0
- package/dist/setup-wizard.d.ts.map +1 -0
- package/dist/setup-wizard.js +73 -0
- package/dist/setup-wizard.js.map +1 -0
- package/dist/shared/content.d.ts +43 -0
- package/dist/shared/content.d.ts.map +1 -0
- package/dist/shared/content.js +61 -0
- package/dist/shared/content.js.map +1 -0
- package/dist/shared/types.d.ts +217 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/types.js +3 -0
- package/dist/shared/types.js.map +1 -0
- package/dist/test-runner.d.ts +5 -0
- package/dist/test-runner.d.ts.map +1 -0
- package/dist/test-runner.js +42 -0
- package/dist/test-runner.js.map +1 -0
- package/dist/types.d.ts +85 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/ascii.d.ts +23 -0
- package/dist/ui/ascii.d.ts.map +1 -0
- package/dist/ui/ascii.js +45 -0
- package/dist/ui/ascii.js.map +1 -0
- package/dist/ui/colors.d.ts +111 -0
- package/dist/ui/colors.d.ts.map +1 -0
- package/dist/ui/colors.js +166 -0
- package/dist/ui/colors.js.map +1 -0
- package/dist/ui/image-attach.d.ts +27 -0
- package/dist/ui/image-attach.d.ts.map +1 -0
- package/dist/ui/image-attach.js +100 -0
- package/dist/ui/image-attach.js.map +1 -0
- package/dist/ui/index.d.ts +18 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +18 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/markdown-stream.d.ts +91 -0
- package/dist/ui/markdown-stream.d.ts.map +1 -0
- package/dist/ui/markdown-stream.js +524 -0
- package/dist/ui/markdown-stream.js.map +1 -0
- package/dist/ui/plan-renderer.d.ts +36 -0
- package/dist/ui/plan-renderer.d.ts.map +1 -0
- package/dist/ui/plan-renderer.js +79 -0
- package/dist/ui/plan-renderer.js.map +1 -0
- package/dist/ui/prompt.d.ts +11 -0
- package/dist/ui/prompt.d.ts.map +1 -0
- package/dist/ui/prompt.js +1960 -0
- package/dist/ui/prompt.js.map +1 -0
- package/dist/ui/render-primitives.d.ts +117 -0
- package/dist/ui/render-primitives.d.ts.map +1 -0
- package/dist/ui/render-primitives.js +322 -0
- package/dist/ui/render-primitives.js.map +1 -0
- package/dist/ui/render.d.ts +133 -0
- package/dist/ui/render.d.ts.map +1 -0
- package/dist/ui/render.js +547 -0
- package/dist/ui/render.js.map +1 -0
- package/dist/ui/session-header.d.ts +30 -0
- package/dist/ui/session-header.d.ts.map +1 -0
- package/dist/ui/session-header.js +74 -0
- package/dist/ui/session-header.js.map +1 -0
- package/dist/workspace-guard.d.ts +68 -0
- package/dist/workspace-guard.d.ts.map +1 -0
- package/dist/workspace-guard.js +168 -0
- package/dist/workspace-guard.js.map +1 -0
- package/dist/workspace-lock.d.ts +27 -0
- package/dist/workspace-lock.d.ts.map +1 -0
- package/dist/workspace-lock.js +95 -0
- package/dist/workspace-lock.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* session-header.ts — The boxed session summary rendered once
|
|
3
|
+
* when a session starts or is resumed.
|
|
4
|
+
*
|
|
5
|
+
* The header uses the lava top/bottom border and a SNOW4 side
|
|
6
|
+
* border so it sits inside the same visual family as the AI
|
|
7
|
+
* response frame and the plan renderer.
|
|
8
|
+
*/
|
|
9
|
+
import { C, providerColor } from './colors.js';
|
|
10
|
+
import { visLen } from './colors.js';
|
|
11
|
+
function frameWidth() {
|
|
12
|
+
return Math.max(40, Math.min(100, (process.stdout.columns ?? 100) - 4));
|
|
13
|
+
}
|
|
14
|
+
function modeColor(mode) {
|
|
15
|
+
switch (mode) {
|
|
16
|
+
case 'BUILD': return C.LAVA;
|
|
17
|
+
case 'PLAN': return C.PURPLE;
|
|
18
|
+
case 'REVIEW': return C.YELLOW;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function padInside(s, width) {
|
|
22
|
+
const v = visLen(s);
|
|
23
|
+
if (v >= width)
|
|
24
|
+
return s;
|
|
25
|
+
return s + ' '.repeat(width - v);
|
|
26
|
+
}
|
|
27
|
+
function statusLabel(s) {
|
|
28
|
+
switch (s) {
|
|
29
|
+
case 'new': return 'New session';
|
|
30
|
+
case 'resumed': return 'Resumed session';
|
|
31
|
+
case 'imported': return 'Imported session';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Print the session header box. The box width is `columns - 4`,
|
|
36
|
+
* capped at 100. Safe to call on a non-TTY.
|
|
37
|
+
*/
|
|
38
|
+
export function renderSessionHeader(opts) {
|
|
39
|
+
const w = frameWidth();
|
|
40
|
+
const inner = w - 4; // account for the `│ ` and ` │` side padding
|
|
41
|
+
const top = ` ${C.LAVA}┌── SESSION ${'─'.repeat(Math.max(0, inner - 14))}┐${C.RESET}`;
|
|
42
|
+
const bottom = ` ${C.LAVA}└${'─'.repeat(w - 2)}┘${C.RESET}`;
|
|
43
|
+
const line1 = `${C.SNOW3}${statusLabel(opts.status)}${C.RESET} ${C.SNOW4}·${C.RESET} ${C.SNOW2}${formatDate(opts.startedAt)}${C.RESET}`;
|
|
44
|
+
const line2 = `${C.SNOW4}Provider:${C.RESET} ${C.SNOW}${providerColor(opts.provider)}${opts.provider}${C.RESET} ${C.SNOW4}·${C.RESET} ${C.SNOW4}Model:${C.RESET} ${C.BLUE}${opts.model}${C.RESET}`;
|
|
45
|
+
const line3 = `${C.SNOW4}Mode:${C.RESET} ${modeColor(opts.mode)}${opts.mode}${C.RESET} ${C.SNOW4}·${C.RESET} ${C.SNOW4}Routing:${C.RESET} ${C.SNOW2}${opts.routing}${C.RESET} ${C.SNOW4}·${C.RESET} ${C.SNOW4}Context:${C.RESET} ${C.SNOW2}${opts.contextWindow}${C.RESET}`;
|
|
46
|
+
try {
|
|
47
|
+
process.stdout.write(top + '\n');
|
|
48
|
+
process.stdout.write(` ${C.SNOW4}│${C.RESET} ${padInside(line1, inner)}${C.SNOW4}│${C.RESET}\n`);
|
|
49
|
+
process.stdout.write(` ${C.SNOW4}│${C.RESET} ${padInside(line2, inner)}${C.SNOW4}│${C.RESET}\n`);
|
|
50
|
+
process.stdout.write(` ${C.SNOW4}│${C.RESET} ${padInside(line3, inner)}${C.SNOW4}│${C.RESET}\n`);
|
|
51
|
+
process.stdout.write(bottom + '\n');
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// stdout may be closed during teardown
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function formatDate(iso) {
|
|
58
|
+
try {
|
|
59
|
+
const d = new Date(iso);
|
|
60
|
+
if (Number.isNaN(d.getTime()))
|
|
61
|
+
return iso;
|
|
62
|
+
return d.toLocaleString('en-US', {
|
|
63
|
+
month: 'short',
|
|
64
|
+
day: 'numeric',
|
|
65
|
+
year: 'numeric',
|
|
66
|
+
hour: 'numeric',
|
|
67
|
+
minute: '2-digit',
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return iso;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=session-header.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-header.js","sourceRoot":"","sources":["../../src/ui/session-header.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace boundary guard.
|
|
3
|
+
*
|
|
4
|
+
* Pillar 5 (Self-Protection) — the WorkspaceGuard also enforces
|
|
5
|
+
* a System Path Lock: paths that resolve inside the platform's
|
|
6
|
+
* own runtime (`src/**`, `package.json`, `tsconfig.json`, and
|
|
7
|
+
* the lockfile) are **immutable** from any active agent loop.
|
|
8
|
+
* This stops an autonomous agent from corrupting its own host
|
|
9
|
+
* (the failure mode that produced the
|
|
10
|
+
* `src/agent/tool-executor.ts:983: Unexpected "catch"` incident
|
|
11
|
+
* during the Phase 2 release).
|
|
12
|
+
*/
|
|
13
|
+
export declare class WorkspaceGuard {
|
|
14
|
+
readonly root: string;
|
|
15
|
+
private allowList;
|
|
16
|
+
constructor(root: string, allowList?: string[]);
|
|
17
|
+
private safeRealPath;
|
|
18
|
+
resolve(input: string | undefined, label?: string): string;
|
|
19
|
+
isInside(target: string): boolean;
|
|
20
|
+
relative(target: string): string;
|
|
21
|
+
ensureFile(target: string): string;
|
|
22
|
+
isBinaryFile(target: string, sampleBytes?: number): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* File-system entries that make up the Fixo CLI platform
|
|
25
|
+
* itself. The agent runtime is **never** allowed to mutate
|
|
26
|
+
* these from an active task cycle, regardless of which
|
|
27
|
+
* workspace the user is operating in.
|
|
28
|
+
*
|
|
29
|
+
* The list is intentionally narrow (the platform's own
|
|
30
|
+
* source tree and root-level build manifests) so the lock
|
|
31
|
+
* does not interfere with the user's own code in
|
|
32
|
+
* sub-directories.
|
|
33
|
+
*/
|
|
34
|
+
private static readonly PLATFORM_PATH_PATTERNS;
|
|
35
|
+
/**
|
|
36
|
+
* Return true if the relative path matches a platform-locked
|
|
37
|
+
* pattern. The check is case-insensitive on the extension
|
|
38
|
+
* because Windows and macOS Finder both silently rename
|
|
39
|
+
* `package.json` to `Package.json` if the user double-clicks.
|
|
40
|
+
*/
|
|
41
|
+
isPlatformPath(relativePath: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Static form of {@link isPlatformPath}. Useful in tests and
|
|
44
|
+
* in places where a WorkspaceGuard instance is not yet
|
|
45
|
+
* available.
|
|
46
|
+
*/
|
|
47
|
+
static isPlatformPath(relativePath: string): boolean;
|
|
48
|
+
private static isPlatformPathStatic;
|
|
49
|
+
/**
|
|
50
|
+
* Throws {@link PlatformPathLockedError} if `target` resolves
|
|
51
|
+
* to a path inside the platform's own runtime. This is the
|
|
52
|
+
* one method every mutation tool in the executor must call
|
|
53
|
+
* before staging a write.
|
|
54
|
+
*/
|
|
55
|
+
assertNotPlatformPath(target: string): void;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Thrown when an agent loop attempts to mutate a path inside
|
|
59
|
+
* the platform's own runtime. Caught by the executor's switch
|
|
60
|
+
* statement and surfaced as a plain string error to the LLM,
|
|
61
|
+
* so the model sees the rejection in its next-turn tool result.
|
|
62
|
+
*/
|
|
63
|
+
export declare class PlatformPathLockedError extends Error {
|
|
64
|
+
readonly resolved: string;
|
|
65
|
+
readonly relative: string;
|
|
66
|
+
constructor(resolved: string, relative: string);
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=workspace-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-guard.d.ts","sourceRoot":"","sources":["../src/workspace-guard.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AACH,qBAAa,cAAc;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,SAAS,CAAW;gBAEhB,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,MAAM,EAAO;IASlD,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,SAAS,GAAG,MAAM;IAS1D,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAMjC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAIhC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAiBlC,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,SAAO,GAAG,OAAO;IAgBzD;;;;;;;;;;OAUG;IACH,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,sBAAsB,CAO5C;IAEF;;;;;OAKG;IACI,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO;IAIpD;;;;OAIG;WACW,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO;IAI3D,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAQnC;;;;;OAKG;IACI,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CASnD;AAED;;;;;GAKG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,SAAgB,QAAQ,EAAE,MAAM,CAAC;IACjC,SAAgB,QAAQ,EAAE,MAAM,CAAC;gBACrB,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAS/C"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* Workspace boundary guard.
|
|
5
|
+
*
|
|
6
|
+
* Pillar 5 (Self-Protection) — the WorkspaceGuard also enforces
|
|
7
|
+
* a System Path Lock: paths that resolve inside the platform's
|
|
8
|
+
* own runtime (`src/**`, `package.json`, `tsconfig.json`, and
|
|
9
|
+
* the lockfile) are **immutable** from any active agent loop.
|
|
10
|
+
* This stops an autonomous agent from corrupting its own host
|
|
11
|
+
* (the failure mode that produced the
|
|
12
|
+
* `src/agent/tool-executor.ts:983: Unexpected "catch"` incident
|
|
13
|
+
* during the Phase 2 release).
|
|
14
|
+
*/
|
|
15
|
+
export class WorkspaceGuard {
|
|
16
|
+
root;
|
|
17
|
+
allowList;
|
|
18
|
+
constructor(root, allowList = []) {
|
|
19
|
+
try {
|
|
20
|
+
this.root = fs.realpathSync(path.resolve(root));
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
this.root = path.resolve(root);
|
|
24
|
+
}
|
|
25
|
+
this.allowList = allowList;
|
|
26
|
+
}
|
|
27
|
+
safeRealPath(p) {
|
|
28
|
+
try {
|
|
29
|
+
return fs.realpathSync(p);
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
const code = err.code;
|
|
33
|
+
if (code === 'ENOENT') {
|
|
34
|
+
const parent = path.dirname(p);
|
|
35
|
+
if (parent === p)
|
|
36
|
+
return p;
|
|
37
|
+
return path.join(this.safeRealPath(parent), path.basename(p));
|
|
38
|
+
}
|
|
39
|
+
throw err;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
resolve(input, label = 'path') {
|
|
43
|
+
const raw = input?.trim() || '.';
|
|
44
|
+
const resolved = path.resolve(this.root, raw);
|
|
45
|
+
if (!this.isInside(resolved)) {
|
|
46
|
+
throw new Error(`${label} escapes workspace: ${input ?? '.'}`);
|
|
47
|
+
}
|
|
48
|
+
return resolved;
|
|
49
|
+
}
|
|
50
|
+
isInside(target) {
|
|
51
|
+
const resolvedTarget = this.safeRealPath(path.resolve(target));
|
|
52
|
+
const relative = path.relative(this.root, resolvedTarget);
|
|
53
|
+
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));
|
|
54
|
+
}
|
|
55
|
+
relative(target) {
|
|
56
|
+
return path.relative(this.root, path.resolve(target)) || '.';
|
|
57
|
+
}
|
|
58
|
+
ensureFile(target) {
|
|
59
|
+
const resolved = this.resolve(target, 'file');
|
|
60
|
+
try {
|
|
61
|
+
const stat = fs.statSync(resolved);
|
|
62
|
+
if (!stat.isFile()) {
|
|
63
|
+
throw new Error(`Not a file: ${target}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
const code = err.code;
|
|
68
|
+
if (code === 'ENOENT') {
|
|
69
|
+
throw new Error(`File does not exist: ${target}`);
|
|
70
|
+
}
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
return resolved;
|
|
74
|
+
}
|
|
75
|
+
isBinaryFile(target, sampleBytes = 4096) {
|
|
76
|
+
const fd = fs.openSync(target, 'r');
|
|
77
|
+
try {
|
|
78
|
+
const buffer = Buffer.alloc(sampleBytes);
|
|
79
|
+
const bytesRead = fs.readSync(fd, buffer, 0, sampleBytes, 0);
|
|
80
|
+
for (let i = 0; i < bytesRead; i++) {
|
|
81
|
+
if (buffer[i] === 0)
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
fs.closeSync(fd);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/* ──────────────────── System Path Lock (Pillar 5) ──────────────────── */
|
|
91
|
+
/**
|
|
92
|
+
* File-system entries that make up the Fixo CLI platform
|
|
93
|
+
* itself. The agent runtime is **never** allowed to mutate
|
|
94
|
+
* these from an active task cycle, regardless of which
|
|
95
|
+
* workspace the user is operating in.
|
|
96
|
+
*
|
|
97
|
+
* The list is intentionally narrow (the platform's own
|
|
98
|
+
* source tree and root-level build manifests) so the lock
|
|
99
|
+
* does not interfere with the user's own code in
|
|
100
|
+
* sub-directories.
|
|
101
|
+
*/
|
|
102
|
+
static PLATFORM_PATH_PATTERNS = [
|
|
103
|
+
/(^|\/)src\/.+/,
|
|
104
|
+
/(^|\/)package\.json$/,
|
|
105
|
+
/(^|\/)package-lock\.json$/,
|
|
106
|
+
/(^|\/)tsconfig(\..+)?\.json$/,
|
|
107
|
+
/(^|\/)dist\/.+/,
|
|
108
|
+
/(^|\/)node_modules\/.+/,
|
|
109
|
+
];
|
|
110
|
+
/**
|
|
111
|
+
* Return true if the relative path matches a platform-locked
|
|
112
|
+
* pattern. The check is case-insensitive on the extension
|
|
113
|
+
* because Windows and macOS Finder both silently rename
|
|
114
|
+
* `package.json` to `Package.json` if the user double-clicks.
|
|
115
|
+
*/
|
|
116
|
+
isPlatformPath(relativePath) {
|
|
117
|
+
return WorkspaceGuard.isPlatformPathStatic(relativePath);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Static form of {@link isPlatformPath}. Useful in tests and
|
|
121
|
+
* in places where a WorkspaceGuard instance is not yet
|
|
122
|
+
* available.
|
|
123
|
+
*/
|
|
124
|
+
static isPlatformPath(relativePath) {
|
|
125
|
+
return WorkspaceGuard.isPlatformPathStatic(relativePath);
|
|
126
|
+
}
|
|
127
|
+
static isPlatformPathStatic(relativePath) {
|
|
128
|
+
const normalised = relativePath.replace(/\\/g, '/').toLowerCase();
|
|
129
|
+
for (const pattern of WorkspaceGuard.PLATFORM_PATH_PATTERNS) {
|
|
130
|
+
if (pattern.test(normalised))
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Throws {@link PlatformPathLockedError} if `target` resolves
|
|
137
|
+
* to a path inside the platform's own runtime. This is the
|
|
138
|
+
* one method every mutation tool in the executor must call
|
|
139
|
+
* before staging a write.
|
|
140
|
+
*/
|
|
141
|
+
assertNotPlatformPath(target) {
|
|
142
|
+
// Resolve to an absolute path so symlinks and `..` tricks
|
|
143
|
+
// cannot escape the check.
|
|
144
|
+
const resolved = this.resolve(target, 'platform path');
|
|
145
|
+
const relative = this.relative(resolved);
|
|
146
|
+
if (this.isPlatformPath(relative)) {
|
|
147
|
+
throw new PlatformPathLockedError(resolved, relative);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Thrown when an agent loop attempts to mutate a path inside
|
|
153
|
+
* the platform's own runtime. Caught by the executor's switch
|
|
154
|
+
* statement and surfaced as a plain string error to the LLM,
|
|
155
|
+
* so the model sees the rejection in its next-turn tool result.
|
|
156
|
+
*/
|
|
157
|
+
export class PlatformPathLockedError extends Error {
|
|
158
|
+
resolved;
|
|
159
|
+
relative;
|
|
160
|
+
constructor(resolved, relative) {
|
|
161
|
+
super(`Error: Modification of Fixo CLI core architecture files is strictly ` +
|
|
162
|
+
`prohibited during workspace task cycles (target: ${relative}).`);
|
|
163
|
+
this.name = 'PlatformPathLockedError';
|
|
164
|
+
this.resolved = resolved;
|
|
165
|
+
this.relative = relative;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=workspace-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-guard.js","sourceRoot":"","sources":["../src/workspace-guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface LockState {
|
|
2
|
+
agentId: string;
|
|
3
|
+
type: 'read' | 'write';
|
|
4
|
+
}
|
|
5
|
+
export declare class WorkspaceLockManager {
|
|
6
|
+
private locks;
|
|
7
|
+
/**
|
|
8
|
+
* Tries to acquire a lock on a file.
|
|
9
|
+
* Returns true if successful, false if the lock is held by another agent.
|
|
10
|
+
*/
|
|
11
|
+
acquireLock(filePath: string, agentId: string, lockType: 'read' | 'write'): boolean;
|
|
12
|
+
/**
|
|
13
|
+
* Releases a lock held by an agent on a file.
|
|
14
|
+
* Returns true if a lock was released, false if no lock existed for this agent.
|
|
15
|
+
*/
|
|
16
|
+
releaseLock(filePath: string, agentId: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Releases all locks held by a specific agent.
|
|
19
|
+
*/
|
|
20
|
+
releaseAllLocks(agentId: string): void;
|
|
21
|
+
/**
|
|
22
|
+
* Checks if a file is currently locked in a way that conflicts with the requested lockType.
|
|
23
|
+
*/
|
|
24
|
+
isLocked(filePath: string, lockType: 'read' | 'write'): boolean;
|
|
25
|
+
}
|
|
26
|
+
export declare const workspaceLockManager: WorkspaceLockManager;
|
|
27
|
+
//# sourceMappingURL=workspace-lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-lock.d.ts","sourceRoot":"","sources":["../src/workspace-lock.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,KAAK,CAAkC;IAE/C;;;OAGG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO;IA0CnF;;;OAGG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;IAkBvD;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAWtC;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO;CAehE;AAED,eAAO,MAAM,oBAAoB,sBAA6B,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
export class WorkspaceLockManager {
|
|
3
|
+
locks = new Map();
|
|
4
|
+
/**
|
|
5
|
+
* Tries to acquire a lock on a file.
|
|
6
|
+
* Returns true if successful, false if the lock is held by another agent.
|
|
7
|
+
*/
|
|
8
|
+
acquireLock(filePath, agentId, lockType) {
|
|
9
|
+
const normalizedPath = path.resolve(filePath);
|
|
10
|
+
const activeLocks = this.locks.get(normalizedPath) || [];
|
|
11
|
+
if (activeLocks.length === 0) {
|
|
12
|
+
this.locks.set(normalizedPath, [{ agentId, type: lockType }]);
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
// Check if the current agent already holds a lock of the same or higher type
|
|
16
|
+
const agentLock = activeLocks.find(l => l.agentId === agentId);
|
|
17
|
+
if (agentLock) {
|
|
18
|
+
if (agentLock.type === lockType || agentLock.type === 'write') {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
// Upgrade from read to write
|
|
22
|
+
if (lockType === 'write') {
|
|
23
|
+
// Can only upgrade if this agent is the ONLY reader
|
|
24
|
+
if (activeLocks.length === 1) {
|
|
25
|
+
agentLock.type = 'write';
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
return false; // Other agents hold read locks, cannot upgrade
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// If any write lock is active, block all new locks
|
|
32
|
+
if (activeLocks.some(l => l.type === 'write')) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
// If new lock is write and there are existing read locks, block it
|
|
36
|
+
if (lockType === 'write') {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
// Otherwise, add a shared read lock
|
|
40
|
+
activeLocks.push({ agentId, type: lockType });
|
|
41
|
+
this.locks.set(normalizedPath, activeLocks);
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Releases a lock held by an agent on a file.
|
|
46
|
+
* Returns true if a lock was released, false if no lock existed for this agent.
|
|
47
|
+
*/
|
|
48
|
+
releaseLock(filePath, agentId) {
|
|
49
|
+
const normalizedPath = path.resolve(filePath);
|
|
50
|
+
const activeLocks = this.locks.get(normalizedPath) || [];
|
|
51
|
+
const index = activeLocks.findIndex(l => l.agentId === agentId);
|
|
52
|
+
if (index === -1) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
activeLocks.splice(index, 1);
|
|
56
|
+
if (activeLocks.length === 0) {
|
|
57
|
+
this.locks.delete(normalizedPath);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
this.locks.set(normalizedPath, activeLocks);
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Releases all locks held by a specific agent.
|
|
66
|
+
*/
|
|
67
|
+
releaseAllLocks(agentId) {
|
|
68
|
+
for (const [filePath, activeLocks] of this.locks.entries()) {
|
|
69
|
+
const filtered = activeLocks.filter(l => l.agentId !== agentId);
|
|
70
|
+
if (filtered.length === 0) {
|
|
71
|
+
this.locks.delete(filePath);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
this.locks.set(filePath, filtered);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Checks if a file is currently locked in a way that conflicts with the requested lockType.
|
|
80
|
+
*/
|
|
81
|
+
isLocked(filePath, lockType) {
|
|
82
|
+
const normalizedPath = path.resolve(filePath);
|
|
83
|
+
const activeLocks = this.locks.get(normalizedPath) || [];
|
|
84
|
+
if (activeLocks.length === 0) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
if (lockType === 'write') {
|
|
88
|
+
return true; // Any lock (read or write) blocks a new write lock
|
|
89
|
+
}
|
|
90
|
+
// For read locks, only an active write lock blocks it
|
|
91
|
+
return activeLocks.some(l => l.type === 'write');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export const workspaceLockManager = new WorkspaceLockManager();
|
|
95
|
+
//# sourceMappingURL=workspace-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-lock.js","sourceRoot":"","sources":["../src/workspace-lock.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fixo-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "FixO CLI — Autonomous Free Multi-Provider LLM Coding Tool",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"fixo": "./dist/index.js",
|
|
8
|
+
"fixo-cli": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"main": "./dist/index.js",
|
|
11
|
+
"files": [
|
|
12
|
+
"dist/",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc && chmod +x dist/index.js",
|
|
17
|
+
"dev": "tsx src/index.ts",
|
|
18
|
+
"test": "node --import tsx --test src/__tests__/*.test.ts",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"ai",
|
|
23
|
+
"coding",
|
|
24
|
+
"agent",
|
|
25
|
+
"cli",
|
|
26
|
+
"llm",
|
|
27
|
+
"free",
|
|
28
|
+
"openai",
|
|
29
|
+
"groq",
|
|
30
|
+
"gemini",
|
|
31
|
+
"claude-code-alternative"
|
|
32
|
+
],
|
|
33
|
+
"license": "Apache-2.0",
|
|
34
|
+
"author": "Abrar Akhunji",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/Abrar-Akhunji/FIXO_CLI.git"
|
|
38
|
+
},
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/Abrar-Akhunji/FIXO_CLI/issues"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://github.com/Abrar-Akhunji/FIXO_CLI#readme",
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=22.13.0"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@clack/prompts": "^0.9.0",
|
|
48
|
+
"cheerio": "^1.2.0",
|
|
49
|
+
"gpt-tokenizer": "^3.4.0",
|
|
50
|
+
"js-yaml": "^4.1.0",
|
|
51
|
+
"tree-sitter-bash": "^0.25.0",
|
|
52
|
+
"turndown": "^7.2.4",
|
|
53
|
+
"vscode-jsonrpc": "^8.2.1",
|
|
54
|
+
"web-tree-sitter": "^0.25.10"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/js-yaml": "^4.0.9",
|
|
58
|
+
"@types/node": "^22.15.3",
|
|
59
|
+
"@types/turndown": "^5.0.6",
|
|
60
|
+
"tsx": "^4.19.4",
|
|
61
|
+
"typescript": "^5.8.3"
|
|
62
|
+
}
|
|
63
|
+
}
|