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
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import type { PolicyProfile } from './runtime/policy.js';
|
|
2
|
+
export declare const DEFAULT_API_URL = "https://api.freellmapi.com/v1";
|
|
3
|
+
/** Stream-resume policy. */
|
|
4
|
+
export type StreamResumePolicy = 'auto' | 'never';
|
|
5
|
+
/**
|
|
6
|
+
* Context-budget policy.
|
|
7
|
+
* - `auto` — proactive enforcement is enabled and, if the enforcer
|
|
8
|
+
* asks for compaction, the agent summarises the oldest
|
|
9
|
+
* turns via an LLM call. (default)
|
|
10
|
+
* - `truncate` — proactive enforcement runs but the agent will NOT
|
|
11
|
+
* trigger LLM-based compaction. If even the enforcer
|
|
12
|
+
* cannot fit the budget, the request is sent anyway
|
|
13
|
+
* (and may 413).
|
|
14
|
+
* - `never` — kill-switch. No enforcement, no compaction. Useful
|
|
15
|
+
* when a user wants exact 1:1 historical behaviour.
|
|
16
|
+
*/
|
|
17
|
+
export type ContextBudgetPolicy = 'auto' | 'truncate' | 'never';
|
|
18
|
+
/**
|
|
19
|
+
* Loop-trap configuration. Lives under `preferences.safety.loopTrap`
|
|
20
|
+
* (not `preferences.resilience`) so the safety and resilience
|
|
21
|
+
* concerns remain orthogonal.
|
|
22
|
+
*
|
|
23
|
+
* The defaults match the design decision: warn at 3 consecutive
|
|
24
|
+
* equivalent turns (acts as an intentional psychological disruptor
|
|
25
|
+
* to the LLM via system-prompt injection), hard-abort at 6.
|
|
26
|
+
*/
|
|
27
|
+
export interface LoopTrapPolicy {
|
|
28
|
+
/** Number of consecutive equivalent turns that triggers a directive. */
|
|
29
|
+
triggerCount: number;
|
|
30
|
+
/** Number of consecutive equivalent turns that triggers a hard abort. */
|
|
31
|
+
hardAbortCount: number;
|
|
32
|
+
/** Maximum number of tool-result bytes to include in the fingerprint. */
|
|
33
|
+
toolResultTailBytes: number;
|
|
34
|
+
/** Hard cap on in-memory history to bound memory growth. */
|
|
35
|
+
maxHistory: number;
|
|
36
|
+
/** Master kill-switch; when false, the detector is never invoked. */
|
|
37
|
+
enabled: boolean;
|
|
38
|
+
}
|
|
39
|
+
/** Semantic loop-trap configuration. Tracks file-target frequency
|
|
40
|
+
* inside a sliding window so that an LLM which varies its search
|
|
41
|
+
* arguments but keeps hammering the same file still trips. */
|
|
42
|
+
export interface SemanticLoopTrapPolicy {
|
|
43
|
+
enabled: boolean;
|
|
44
|
+
windowSize: number;
|
|
45
|
+
triggerCount: number;
|
|
46
|
+
hardAbortCount: number;
|
|
47
|
+
}
|
|
48
|
+
/** Pre-save gate severity. */
|
|
49
|
+
export type LspPreSaveMode = 'off' | 'warn' | 'block' | 'sandbox-mock';
|
|
50
|
+
/** Safety preferences — Pillar 1, 2, 3 surface. Pillar 4 lives in the
|
|
51
|
+
* credential vault module, not in the user-facing config. */
|
|
52
|
+
export interface SafetyConfig {
|
|
53
|
+
/** Run file writes through the atomic shadow-staging pipeline. */
|
|
54
|
+
atomicStaging: boolean;
|
|
55
|
+
/** Staged writes older than this (ms) are eligible for auto-GC. */
|
|
56
|
+
stagingTtlMs: number;
|
|
57
|
+
/** How to react to LSP diagnostics on a staged write. */
|
|
58
|
+
lspPreSave: LspPreSaveMode;
|
|
59
|
+
/** Loop-trap thresholds. */
|
|
60
|
+
loopTrap: LoopTrapPolicy;
|
|
61
|
+
/** Semantic loop-trap thresholds (Pillar 2 of the refit). */
|
|
62
|
+
semanticLoopTrap: SemanticLoopTrapPolicy;
|
|
63
|
+
/** Maximum bytes a file may be before read_file is gated by the
|
|
64
|
+
* structural pre-scan rule. Defaults to 15 KiB. */
|
|
65
|
+
largeFileGateBytes: number;
|
|
66
|
+
/** Maximum line count before read_file is gated. Defaults to 350. */
|
|
67
|
+
largeFileGateLines: number;
|
|
68
|
+
/**
|
|
69
|
+
* Predictive context-budget gate (Phase 4). When set to a value in
|
|
70
|
+
* (0, 1], `read_file` projects the token cost of the read against
|
|
71
|
+
* the model's input window and defers if projected total exceeds
|
|
72
|
+
* this fraction. Default 0.85. Set to 1.0 to disable.
|
|
73
|
+
*/
|
|
74
|
+
predictiveBudgetPct?: number;
|
|
75
|
+
}
|
|
76
|
+
/** Resilience preferences for the new withRetry + chatStreamWithResume paths. */
|
|
77
|
+
export interface ResilienceConfig {
|
|
78
|
+
/** When 'auto', mid-stream cuts are resumed transparently (default). */
|
|
79
|
+
streamResume: StreamResumePolicy;
|
|
80
|
+
/** Max additional resume attempts after a mid-stream cut. */
|
|
81
|
+
maxResumeAttempts: number;
|
|
82
|
+
/** When true, the new withRetry engine is used for non-streaming calls. */
|
|
83
|
+
useWithRetry: boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Context-budget policy. When 'auto' or 'truncate', the
|
|
86
|
+
* {@link ContextBudgetEnforcer} runs before every LLM call and
|
|
87
|
+
* trims the conversation to fit the model's input window. When
|
|
88
|
+
* 'auto', the agent may also call `compact()` to summarise the
|
|
89
|
+
* oldest turns.
|
|
90
|
+
*/
|
|
91
|
+
contextBudget: ContextBudgetPolicy;
|
|
92
|
+
/**
|
|
93
|
+
* Fraction of the model context window to use as the hard cap when
|
|
94
|
+
* enforcing the budget. 0.8 leaves 20% headroom for the response.
|
|
95
|
+
*/
|
|
96
|
+
contextBudgetRatio: number;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Global configuration for the FixO CLI.
|
|
100
|
+
* Persisted at `~/.fixocli/config.json`.
|
|
101
|
+
*/
|
|
102
|
+
export interface FreeLLMConfig {
|
|
103
|
+
freellmapi_api_key?: string;
|
|
104
|
+
apiUrl?: string;
|
|
105
|
+
defaultModel: string;
|
|
106
|
+
preferences: {
|
|
107
|
+
autoCommit: boolean;
|
|
108
|
+
streaming: boolean;
|
|
109
|
+
theme: 'dark' | 'light';
|
|
110
|
+
maxRetries: number;
|
|
111
|
+
policy: PolicyProfile;
|
|
112
|
+
telemetry: boolean;
|
|
113
|
+
/**
|
|
114
|
+
* Local NDJSON sink. Defaults to true. When false, no events are
|
|
115
|
+
* written to `~/.fixocli/telemetry.jsonl` — useful for users who
|
|
116
|
+
* want to keep their disk private but still want to send events
|
|
117
|
+
* to the remote sink.
|
|
118
|
+
*/
|
|
119
|
+
telemetryLocal: boolean;
|
|
120
|
+
/**
|
|
121
|
+
* Remote HTTP sink (legacy). Defaults to false. When true, the
|
|
122
|
+
* legacy `logTelemetry` HTTP poster is re-enabled alongside the
|
|
123
|
+
* local sink. The free FixO API server collects anonymous usage
|
|
124
|
+
* stats so we can prioritise provider fixes.
|
|
125
|
+
*/
|
|
126
|
+
telemetryRemote: boolean;
|
|
127
|
+
resilience: ResilienceConfig;
|
|
128
|
+
/**
|
|
129
|
+
* Safety preferences — orthogonal to `resilience`.
|
|
130
|
+
*
|
|
131
|
+
* - `resilience` keeps the system alive through network noise.
|
|
132
|
+
* - `safety` keeps the system from corrupting the user's
|
|
133
|
+
* workspace or leaking secrets.
|
|
134
|
+
*
|
|
135
|
+
* Pillar 1 (loop-trap), Pillar 2 (atomic staging), and Pillar 3
|
|
136
|
+
* (LSP pre-save) are wired up here. Pillar 4 (credential vault)
|
|
137
|
+
* is a programmatic-only surface and is not user-configurable.
|
|
138
|
+
*/
|
|
139
|
+
safety: SafetyConfig;
|
|
140
|
+
};
|
|
141
|
+
_firstRunComplete: boolean;
|
|
142
|
+
}
|
|
143
|
+
/** Returns the FixO CLI config directory (`~/.fixocli/`). */
|
|
144
|
+
export declare function getConfigDir(): string;
|
|
145
|
+
/** Returns the full path to the config file (`~/.fixocli/config.json`). */
|
|
146
|
+
export declare function getConfigPath(): string;
|
|
147
|
+
/** Returns the full path to the prompt history log (`~/.fixocli/history.jsonl`). */
|
|
148
|
+
export declare function getHistoryPath(): string;
|
|
149
|
+
/** Returns a complete default configuration object. */
|
|
150
|
+
export declare function getDefaultConfig(): FreeLLMConfig;
|
|
151
|
+
/**
|
|
152
|
+
* Reads `~/.fixocli/config.json` and returns the parsed config.
|
|
153
|
+
* If the file doesn't exist or is unreadable, a default config is returned
|
|
154
|
+
* instead — the caller can then decide whether to run the setup wizard.
|
|
155
|
+
*/
|
|
156
|
+
export declare function loadConfig(): FreeLLMConfig;
|
|
157
|
+
/**
|
|
158
|
+
* Persists the given config to `~/.fixocli/config.json`.
|
|
159
|
+
* Creates the config directory if it doesn't already exist.
|
|
160
|
+
*/
|
|
161
|
+
export declare function saveConfig(config: FreeLLMConfig): void;
|
|
162
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
export const DEFAULT_API_URL = 'https://api.freellmapi.com/v1';
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Path helpers
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
/** Returns the FixO CLI config directory (`~/.fixocli/`). */
|
|
9
|
+
export function getConfigDir() {
|
|
10
|
+
return path.join(os.homedir(), '.fixocli');
|
|
11
|
+
}
|
|
12
|
+
/** Returns the full path to the config file (`~/.fixocli/config.json`). */
|
|
13
|
+
export function getConfigPath() {
|
|
14
|
+
return path.join(getConfigDir(), 'config.json');
|
|
15
|
+
}
|
|
16
|
+
/** Returns the full path to the prompt history log (`~/.fixocli/history.jsonl`). */
|
|
17
|
+
export function getHistoryPath() {
|
|
18
|
+
return path.join(getConfigDir(), 'history.jsonl');
|
|
19
|
+
}
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Config I/O
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
/** Returns a complete default configuration object. */
|
|
24
|
+
export function getDefaultConfig() {
|
|
25
|
+
return {
|
|
26
|
+
defaultModel: 'auto',
|
|
27
|
+
preferences: {
|
|
28
|
+
autoCommit: false,
|
|
29
|
+
streaming: true,
|
|
30
|
+
theme: 'dark',
|
|
31
|
+
maxRetries: 3,
|
|
32
|
+
policy: 'shell-confirm',
|
|
33
|
+
telemetry: true,
|
|
34
|
+
telemetryLocal: true,
|
|
35
|
+
telemetryRemote: false,
|
|
36
|
+
resilience: {
|
|
37
|
+
streamResume: 'auto',
|
|
38
|
+
maxResumeAttempts: 3,
|
|
39
|
+
useWithRetry: true,
|
|
40
|
+
contextBudget: 'auto',
|
|
41
|
+
contextBudgetRatio: 0.8,
|
|
42
|
+
},
|
|
43
|
+
safety: {
|
|
44
|
+
atomicStaging: true,
|
|
45
|
+
stagingTtlMs: 24 * 60 * 60 * 1000,
|
|
46
|
+
lspPreSave: 'warn',
|
|
47
|
+
loopTrap: {
|
|
48
|
+
triggerCount: 3,
|
|
49
|
+
hardAbortCount: 6,
|
|
50
|
+
toolResultTailBytes: 1024,
|
|
51
|
+
maxHistory: 64,
|
|
52
|
+
enabled: true,
|
|
53
|
+
},
|
|
54
|
+
semanticLoopTrap: {
|
|
55
|
+
enabled: true,
|
|
56
|
+
windowSize: 5,
|
|
57
|
+
triggerCount: 3,
|
|
58
|
+
hardAbortCount: 6,
|
|
59
|
+
},
|
|
60
|
+
largeFileGateBytes: 15 * 1024,
|
|
61
|
+
largeFileGateLines: 350,
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
_firstRunComplete: false,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Reads `~/.fixocli/config.json` and returns the parsed config.
|
|
69
|
+
* If the file doesn't exist or is unreadable, a default config is returned
|
|
70
|
+
* instead — the caller can then decide whether to run the setup wizard.
|
|
71
|
+
*/
|
|
72
|
+
export function loadConfig() {
|
|
73
|
+
const configPath = getConfigPath();
|
|
74
|
+
try {
|
|
75
|
+
const raw = fs.readFileSync(configPath, 'utf-8');
|
|
76
|
+
const parsed = JSON.parse(raw);
|
|
77
|
+
const defaults = getDefaultConfig();
|
|
78
|
+
// Merge top-level keys while keeping nested `preferences` safe.
|
|
79
|
+
// `resilience` and `safety` are deep-merged so old configs that
|
|
80
|
+
// predate a new field still pick up the new default.
|
|
81
|
+
const parsedPreferences = parsed.preferences ?? {};
|
|
82
|
+
const parsedResilience = parsedPreferences.resilience ?? {};
|
|
83
|
+
const parsedSafety = parsedPreferences.safety ?? {};
|
|
84
|
+
const parsedLoopTrap = parsedSafety.loopTrap ?? {};
|
|
85
|
+
const parsedSemanticLoopTrap = parsedSafety
|
|
86
|
+
.semanticLoopTrap ?? {};
|
|
87
|
+
return {
|
|
88
|
+
...defaults,
|
|
89
|
+
...parsed,
|
|
90
|
+
preferences: {
|
|
91
|
+
...defaults.preferences,
|
|
92
|
+
...parsedPreferences,
|
|
93
|
+
resilience: {
|
|
94
|
+
...defaults.preferences.resilience,
|
|
95
|
+
...parsedResilience,
|
|
96
|
+
},
|
|
97
|
+
safety: {
|
|
98
|
+
...defaults.preferences.safety,
|
|
99
|
+
...parsedSafety,
|
|
100
|
+
loopTrap: {
|
|
101
|
+
...defaults.preferences.safety.loopTrap,
|
|
102
|
+
...parsedLoopTrap,
|
|
103
|
+
},
|
|
104
|
+
semanticLoopTrap: {
|
|
105
|
+
...defaults.preferences.safety.semanticLoopTrap,
|
|
106
|
+
...parsedSemanticLoopTrap,
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
// File missing, corrupt, or otherwise unreadable — use defaults.
|
|
114
|
+
return getDefaultConfig();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Persists the given config to `~/.fixocli/config.json`.
|
|
119
|
+
* Creates the config directory if it doesn't already exist.
|
|
120
|
+
*/
|
|
121
|
+
export function saveConfig(config) {
|
|
122
|
+
const dir = getConfigDir();
|
|
123
|
+
if (!fs.existsSync(dir)) {
|
|
124
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
125
|
+
}
|
|
126
|
+
const configPath = getConfigPath();
|
|
127
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', {
|
|
128
|
+
encoding: 'utf-8',
|
|
129
|
+
mode: 0o600,
|
|
130
|
+
});
|
|
131
|
+
try {
|
|
132
|
+
fs.chmodSync(configPath, 0o600);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// Ignore OS limitations
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type FixoMdLoadResult } from './fixo-md.js';
|
|
2
|
+
export type FixoMdWatchResult = {
|
|
3
|
+
kind: 'unchanged';
|
|
4
|
+
} | {
|
|
5
|
+
kind: 'created';
|
|
6
|
+
block: string;
|
|
7
|
+
result: FixoMdLoadResult;
|
|
8
|
+
} | {
|
|
9
|
+
kind: 'updated';
|
|
10
|
+
block: string;
|
|
11
|
+
result: FixoMdLoadResult;
|
|
12
|
+
} | {
|
|
13
|
+
kind: 'deleted';
|
|
14
|
+
previousPath: string;
|
|
15
|
+
};
|
|
16
|
+
export declare class FixoMdWatcher {
|
|
17
|
+
private readonly cwd;
|
|
18
|
+
/** The fingerprint we last surfaced (or captured at construction). */
|
|
19
|
+
private lastSeen;
|
|
20
|
+
constructor(cwd: string);
|
|
21
|
+
/**
|
|
22
|
+
* Compare the current on-disk state to the last surfaced
|
|
23
|
+
* fingerprint and return the delta. Internal state advances on
|
|
24
|
+
* non-`unchanged` results so a single change is announced once.
|
|
25
|
+
*/
|
|
26
|
+
check(): FixoMdWatchResult;
|
|
27
|
+
/**
|
|
28
|
+
* Render a watch result into an injectable directive, or null
|
|
29
|
+
* when nothing should be surfaced. Matches the directive shape
|
|
30
|
+
* used by the safety alert and background-jobs awareness so the
|
|
31
|
+
* agent model already knows how to parse it.
|
|
32
|
+
*/
|
|
33
|
+
formatDirective(watch: FixoMdWatchResult): string | null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Convenience wrapper for tests + callers that don't need the
|
|
37
|
+
* persistent watcher state.
|
|
38
|
+
*
|
|
39
|
+
* @internal
|
|
40
|
+
*/
|
|
41
|
+
export declare function readFixoMd(cwd: string): FixoMdLoadResult;
|
|
42
|
+
//# sourceMappingURL=fixo-md-watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixo-md-watcher.d.ts","sourceRoot":"","sources":["../../src/context/fixo-md-watcher.ts"],"names":[],"mappings":"AAoBA,OAAO,EAIL,KAAK,gBAAgB,EAEtB,MAAM,cAAc,CAAC;AActB,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAgC9C,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,sEAAsE;IACtE,OAAO,CAAC,QAAQ,CAAyB;gBAE7B,GAAG,EAAE,MAAM;IASvB;;;;OAIG;IACH,KAAK,IAAI,iBAAiB;IAkC1B;;;;;OAKG;IACH,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI;CAqBzD;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAExD"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fixo-md-watcher.ts — Phase 4: per-turn re-injection of FIXO.md
|
|
3
|
+
* changes.
|
|
4
|
+
*
|
|
5
|
+
* The system prompt bakes the FIXO.md content in at run start, so
|
|
6
|
+
* a long-running agent loop normally never sees later edits. This
|
|
7
|
+
* watcher closes that gap by stat-checking the active FIXO.md
|
|
8
|
+
* before each `chat()` call and emitting a delta when the file is
|
|
9
|
+
* created, updated, or removed mid-run.
|
|
10
|
+
*
|
|
11
|
+
* Why a delta model instead of always re-injecting? The full
|
|
12
|
+
* contents already live in the system prompt; re-sending them on
|
|
13
|
+
* every turn would double the token cost without any new signal.
|
|
14
|
+
* The watcher injects only when the on-disk file no longer matches
|
|
15
|
+
* what the prompt currently reflects.
|
|
16
|
+
*
|
|
17
|
+
* Pure read: stats, optional read, and an in-memory cursor. No
|
|
18
|
+
* `fs.watch`, no subprocesses, no globals.
|
|
19
|
+
*/
|
|
20
|
+
import fs from 'node:fs';
|
|
21
|
+
import { buildProjectInstructionsBlock, findFixoMdPath, loadProjectInstructions, } from './fixo-md.js';
|
|
22
|
+
/* ──────────────────────── helpers ──────────────────────── */
|
|
23
|
+
function fingerprint(cwd) {
|
|
24
|
+
const found = findFixoMdPath(cwd);
|
|
25
|
+
if (found.path === null)
|
|
26
|
+
return null;
|
|
27
|
+
let stat;
|
|
28
|
+
try {
|
|
29
|
+
stat = fs.statSync(found.path);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
path: found.path,
|
|
36
|
+
source: found.source,
|
|
37
|
+
mtimeMs: stat.mtimeMs,
|
|
38
|
+
bytes: stat.size,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function fingerprintsEqual(a, b) {
|
|
42
|
+
return (a.path === b.path &&
|
|
43
|
+
a.source === b.source &&
|
|
44
|
+
a.mtimeMs === b.mtimeMs &&
|
|
45
|
+
a.bytes === b.bytes);
|
|
46
|
+
}
|
|
47
|
+
/* ──────────────────────── watcher ──────────────────────── */
|
|
48
|
+
export class FixoMdWatcher {
|
|
49
|
+
cwd;
|
|
50
|
+
/** The fingerprint we last surfaced (or captured at construction). */
|
|
51
|
+
lastSeen;
|
|
52
|
+
constructor(cwd) {
|
|
53
|
+
this.cwd = cwd;
|
|
54
|
+
// Snapshot the baseline so the very first `check()` after the
|
|
55
|
+
// agent loop starts returns 'unchanged' for files that were
|
|
56
|
+
// already present. Those files are already baked into the
|
|
57
|
+
// system prompt by `buildSystemPrompt`.
|
|
58
|
+
this.lastSeen = fingerprint(cwd);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Compare the current on-disk state to the last surfaced
|
|
62
|
+
* fingerprint and return the delta. Internal state advances on
|
|
63
|
+
* non-`unchanged` results so a single change is announced once.
|
|
64
|
+
*/
|
|
65
|
+
check() {
|
|
66
|
+
const current = fingerprint(this.cwd);
|
|
67
|
+
const previous = this.lastSeen;
|
|
68
|
+
// Both null → nothing on disk now or before. Quiet path.
|
|
69
|
+
if (current === null && previous === null) {
|
|
70
|
+
return { kind: 'unchanged' };
|
|
71
|
+
}
|
|
72
|
+
// File appeared (or moved into the chain) mid-run.
|
|
73
|
+
if (current !== null && previous === null) {
|
|
74
|
+
const { block, result } = buildProjectInstructionsBlock(this.cwd);
|
|
75
|
+
this.lastSeen = current;
|
|
76
|
+
return { kind: 'created', block, result };
|
|
77
|
+
}
|
|
78
|
+
// File disappeared mid-run.
|
|
79
|
+
if (current === null && previous !== null) {
|
|
80
|
+
const previousPath = previous.path;
|
|
81
|
+
this.lastSeen = null;
|
|
82
|
+
return { kind: 'deleted', previousPath };
|
|
83
|
+
}
|
|
84
|
+
// Both non-null. If fingerprint matches we're done.
|
|
85
|
+
if (current !== null && previous !== null && fingerprintsEqual(current, previous)) {
|
|
86
|
+
return { kind: 'unchanged' };
|
|
87
|
+
}
|
|
88
|
+
// Otherwise: content (or path / source) changed.
|
|
89
|
+
const { block, result } = buildProjectInstructionsBlock(this.cwd);
|
|
90
|
+
this.lastSeen = current;
|
|
91
|
+
return { kind: 'updated', block, result };
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Render a watch result into an injectable directive, or null
|
|
95
|
+
* when nothing should be surfaced. Matches the directive shape
|
|
96
|
+
* used by the safety alert and background-jobs awareness so the
|
|
97
|
+
* agent model already knows how to parse it.
|
|
98
|
+
*/
|
|
99
|
+
formatDirective(watch) {
|
|
100
|
+
if (watch.kind === 'unchanged')
|
|
101
|
+
return null;
|
|
102
|
+
if (watch.kind === 'deleted') {
|
|
103
|
+
return (`[Project Instructions]\n` +
|
|
104
|
+
`The FIXO.md previously loaded from \`${watch.previousPath}\` was ` +
|
|
105
|
+
`removed mid-run. Stop relying on its rules until a new one appears.`);
|
|
106
|
+
}
|
|
107
|
+
// Created or updated — surface the block verbatim under a
|
|
108
|
+
// labelled header so the LLM sees this is a refresh, not a
|
|
109
|
+
// duplicate of what's in the system prompt.
|
|
110
|
+
const verb = watch.kind === 'created' ? 'now available' : 'updated mid-run';
|
|
111
|
+
const sourceLine = watch.result.path ?? '(unknown path)';
|
|
112
|
+
return (`[Project Instructions]\n` +
|
|
113
|
+
`FIXO.md is ${verb}. Source: ${sourceLine}.\n` +
|
|
114
|
+
`Latest content:\n${watch.block.trim()}`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Convenience wrapper for tests + callers that don't need the
|
|
119
|
+
* persistent watcher state.
|
|
120
|
+
*
|
|
121
|
+
* @internal
|
|
122
|
+
*/
|
|
123
|
+
export function readFixoMd(cwd) {
|
|
124
|
+
return loadProjectInstructions(cwd);
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=fixo-md-watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixo-md-watcher.js","sourceRoot":"","sources":["../../src/context/fixo-md-watcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export type FixoMdSource = 'project-fixo' | 'project-cwd' | 'global' | 'none';
|
|
2
|
+
export interface FixoMdLoadResult {
|
|
3
|
+
/** The path that won, or null when no FIXO.md was found. */
|
|
4
|
+
readonly source: FixoMdSource;
|
|
5
|
+
/** Absolute path of the file that was loaded, when source !== 'none'. */
|
|
6
|
+
readonly path: string | null;
|
|
7
|
+
/** Raw file content. Empty string when source === 'none'. */
|
|
8
|
+
readonly content: string;
|
|
9
|
+
/** Byte size of the loaded file. 0 when source === 'none'. */
|
|
10
|
+
readonly bytes: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Search the lookup chain and return the first match. Does not
|
|
14
|
+
* read the file — the caller decides whether to read it
|
|
15
|
+
* synchronously or stream it.
|
|
16
|
+
*/
|
|
17
|
+
export declare function findFixoMdPath(cwd: string): {
|
|
18
|
+
source: FixoMdSource;
|
|
19
|
+
path: string | null;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Read the FIXO.md content from the first match in the lookup
|
|
23
|
+
* chain. Returns `{ source: 'none', ... }` when no file exists.
|
|
24
|
+
*
|
|
25
|
+
* Best-effort sandboxing: each candidate is read through the
|
|
26
|
+
* platform's `fs.readFileSync` and capped at 1 MiB so a runaway
|
|
27
|
+
* file cannot OOM the agent.
|
|
28
|
+
*/
|
|
29
|
+
export declare function loadProjectInstructions(cwd: string): FixoMdLoadResult;
|
|
30
|
+
/**
|
|
31
|
+
* Build the `<project-instructions>` block that is appended to
|
|
32
|
+
* the agent's system prompt. The block is wrapped in clearly
|
|
33
|
+
* labelled fences so the LLM cannot confuse it with the
|
|
34
|
+
* platform-managed system prompt.
|
|
35
|
+
*
|
|
36
|
+
* The block is empty when no FIXO.md was found; callers can
|
|
37
|
+
* detect this by inspecting `source === 'none'`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function buildProjectInstructionsBlock(cwd: string): {
|
|
40
|
+
block: string;
|
|
41
|
+
result: FixoMdLoadResult;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Best-effort telemetry emission. Imported lazily so a missing
|
|
45
|
+
* `telemetry.js` (e.g. during early bootstrap) never blocks
|
|
46
|
+
* the loader. Errors are swallowed — telemetry must never
|
|
47
|
+
* break a tool call.
|
|
48
|
+
*/
|
|
49
|
+
export declare function recordFixoMdLoad(result: FixoMdLoadResult): Promise<void>;
|
|
50
|
+
//# sourceMappingURL=fixo-md.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixo-md.d.ts","sourceRoot":"","sources":["../../src/context/fixo-md.ts"],"names":[],"mappings":"AAsBA,MAAM,MAAM,YAAY,GAAG,cAAc,GAAG,aAAa,GAAG,QAAQ,GAAG,MAAM,CAAC;AAE9E,MAAM,WAAW,gBAAgB;IAC/B,4DAA4D;IAC5D,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,yEAAyE;IACzE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,6DAA6D;IAC7D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAID;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAiBzF;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,CAkBrE;AAID;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,MAAM,GAAG;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,gBAAgB,CAAC;CAC1B,CAeA;AAID;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAY9E"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FIXO.md loader — Phase 2 deliverable.
|
|
3
|
+
*
|
|
4
|
+
* Convention: a single, optional `FIXO.md` (or `.fixo/FIXO.md`)
|
|
5
|
+
* provides project-local instructions for the agent. The
|
|
6
|
+
* loader reads it from a deterministic lookup chain and
|
|
7
|
+
* surfaces the loaded content to the system-prompt builder.
|
|
8
|
+
*
|
|
9
|
+
* Lookup order (first hit wins):
|
|
10
|
+
* 1. `<cwd>/.fixo/FIXO.md`
|
|
11
|
+
* 2. `<cwd>/FIXO.md`
|
|
12
|
+
* 3. `~/.fixocli/FIXO.md`
|
|
13
|
+
*
|
|
14
|
+
* The loader is *additive* — `AgentContext.systemPromptOverride`
|
|
15
|
+
* still wins if the caller already supplied one. The FIXO.md
|
|
16
|
+
* content is appended under a clearly-labelled
|
|
17
|
+
* `<project-instructions>` block.
|
|
18
|
+
*/
|
|
19
|
+
import fs from 'node:fs';
|
|
20
|
+
import path from 'node:path';
|
|
21
|
+
import { getConfigDir } from '../config.js';
|
|
22
|
+
/* ──────────────────────── lookup chain ──────────────────────── */
|
|
23
|
+
/**
|
|
24
|
+
* Search the lookup chain and return the first match. Does not
|
|
25
|
+
* read the file — the caller decides whether to read it
|
|
26
|
+
* synchronously or stream it.
|
|
27
|
+
*/
|
|
28
|
+
export function findFixoMdPath(cwd) {
|
|
29
|
+
const candidates = [
|
|
30
|
+
{ source: 'project-fixo', p: path.join(cwd, '.fixo', 'FIXO.md') },
|
|
31
|
+
{ source: 'project-cwd', p: path.join(cwd, 'FIXO.md') },
|
|
32
|
+
{ source: 'global', p: path.join(getConfigDir(), 'FIXO.md') },
|
|
33
|
+
];
|
|
34
|
+
for (const c of candidates) {
|
|
35
|
+
try {
|
|
36
|
+
const stat = fs.statSync(c.p);
|
|
37
|
+
if (stat.isFile() && stat.size > 0) {
|
|
38
|
+
return { source: c.source, path: c.p };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// ENOENT or permission denied — try the next candidate.
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return { source: 'none', path: null };
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Read the FIXO.md content from the first match in the lookup
|
|
49
|
+
* chain. Returns `{ source: 'none', ... }` when no file exists.
|
|
50
|
+
*
|
|
51
|
+
* Best-effort sandboxing: each candidate is read through the
|
|
52
|
+
* platform's `fs.readFileSync` and capped at 1 MiB so a runaway
|
|
53
|
+
* file cannot OOM the agent.
|
|
54
|
+
*/
|
|
55
|
+
export function loadProjectInstructions(cwd) {
|
|
56
|
+
const found = findFixoMdPath(cwd);
|
|
57
|
+
if (found.path === null) {
|
|
58
|
+
return { source: 'none', path: null, content: '', bytes: 0 };
|
|
59
|
+
}
|
|
60
|
+
let content;
|
|
61
|
+
try {
|
|
62
|
+
content = fs.readFileSync(found.path, 'utf-8');
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return { source: 'none', path: found.path, content: '', bytes: 0 };
|
|
66
|
+
}
|
|
67
|
+
const bytes = Buffer.byteLength(content, 'utf-8');
|
|
68
|
+
return {
|
|
69
|
+
source: found.source,
|
|
70
|
+
path: found.path,
|
|
71
|
+
content,
|
|
72
|
+
bytes,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/* ──────────────────────── prompt block ──────────────────────── */
|
|
76
|
+
/**
|
|
77
|
+
* Build the `<project-instructions>` block that is appended to
|
|
78
|
+
* the agent's system prompt. The block is wrapped in clearly
|
|
79
|
+
* labelled fences so the LLM cannot confuse it with the
|
|
80
|
+
* platform-managed system prompt.
|
|
81
|
+
*
|
|
82
|
+
* The block is empty when no FIXO.md was found; callers can
|
|
83
|
+
* detect this by inspecting `source === 'none'`.
|
|
84
|
+
*/
|
|
85
|
+
export function buildProjectInstructionsBlock(cwd) {
|
|
86
|
+
const result = loadProjectInstructions(cwd);
|
|
87
|
+
if (result.source === 'none' || result.content.length === 0) {
|
|
88
|
+
return { block: '', result };
|
|
89
|
+
}
|
|
90
|
+
const header = result.source === 'global'
|
|
91
|
+
? 'Global instructions (from ~/.fixocli/FIXO.md)'
|
|
92
|
+
: `Project instructions (from ${result.path ?? 'FIXO.md'})`;
|
|
93
|
+
const block = `\n\n<project-instructions source="${result.source}">\n` +
|
|
94
|
+
`## ${header}\n\n` +
|
|
95
|
+
`${result.content.trim()}\n` +
|
|
96
|
+
`</project-instructions>\n`;
|
|
97
|
+
return { block, result };
|
|
98
|
+
}
|
|
99
|
+
/* ──────────────────────── telemetry ──────────────────────── */
|
|
100
|
+
/**
|
|
101
|
+
* Best-effort telemetry emission. Imported lazily so a missing
|
|
102
|
+
* `telemetry.js` (e.g. during early bootstrap) never blocks
|
|
103
|
+
* the loader. Errors are swallowed — telemetry must never
|
|
104
|
+
* break a tool call.
|
|
105
|
+
*/
|
|
106
|
+
export async function recordFixoMdLoad(result) {
|
|
107
|
+
try {
|
|
108
|
+
const { recordTelemetry, telemetry } = await import('../agent/telemetry.js');
|
|
109
|
+
recordTelemetry(telemetry.fixoMdLoaded({
|
|
110
|
+
source: result.source,
|
|
111
|
+
bytes: result.bytes,
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// ignore
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=fixo-md.js.map
|