flint-agent 1.14.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/.env.example +108 -0
- package/CHANGELOG.md +55 -0
- package/FEATURES.md +298 -0
- package/LICENSE +21 -0
- package/README.md +435 -0
- package/bin/flint.js +47 -0
- package/config/classifier-prompt.md +218 -0
- package/config/models-curated.json +4 -0
- package/config/providers.json +74 -0
- package/package.json +92 -0
- package/patches/ink+6.8.0.patch +78 -0
- package/profiles/desktop.md +65 -0
- package/profiles/generic.md +20 -0
- package/profiles/marketer.md +20 -0
- package/profiles/profiles.json +34 -0
- package/profiles/ux-reviewer.md +25 -0
- package/src/agent/agent.js +1743 -0
- package/src/agent/auto.js +346 -0
- package/src/agent/backoff.js +143 -0
- package/src/agent/compression.js +310 -0
- package/src/agent/content-resolver.js +180 -0
- package/src/agent/flow-controller.js +309 -0
- package/src/agent/intent-manifest.js +231 -0
- package/src/agent/intent-timeout.js +46 -0
- package/src/agent/intent.js +633 -0
- package/src/agent/knowledge.js +114 -0
- package/src/agent/learning.js +180 -0
- package/src/agent/modes.js +187 -0
- package/src/agent/outcome-ask.js +91 -0
- package/src/agent/project-context.js +76 -0
- package/src/agent/prompt-budget.js +117 -0
- package/src/agent/reflection-extractor.js +140 -0
- package/src/agent/steering.js +86 -0
- package/src/agent/supervisor.js +430 -0
- package/src/agent/swap.js +443 -0
- package/src/agent/system-prompt.js +446 -0
- package/src/agent/time-stamp.js +48 -0
- package/src/agent/tool-guard.js +201 -0
- package/src/agent/toolcall-text.js +162 -0
- package/src/agent/usage.js +297 -0
- package/src/agent/vision.js +94 -0
- package/src/agent/watchdog.js +139 -0
- package/src/agent/workspace-changes.js +177 -0
- package/src/api/address.js +14 -0
- package/src/api/client.js +280 -0
- package/src/api/server.js +535 -0
- package/src/api/stream-pipe.js +113 -0
- package/src/app-state.js +39 -0
- package/src/bootstrap.js +501 -0
- package/src/bus/drain-loop.js +497 -0
- package/src/bus/index.js +270 -0
- package/src/bus/plugins.js +65 -0
- package/src/child-idle.js +14 -0
- package/src/cli.js +118 -0
- package/src/commands/commands.js +1297 -0
- package/src/commands/registry.js +132 -0
- package/src/components/App.js +491 -0
- package/src/components/CarefulMenu.js +145 -0
- package/src/components/HistoryWriter.js +86 -0
- package/src/components/LineInput.js +69 -0
- package/src/components/LiveZone.js +294 -0
- package/src/components/OverlayMenu.js +179 -0
- package/src/components/SystemPanel.js +156 -0
- package/src/components/Table.js +54 -0
- package/src/config.js +249 -0
- package/src/free-models.js +230 -0
- package/src/index.js +1111 -0
- package/src/input-handler.js +13 -0
- package/src/input-text.js +123 -0
- package/src/launcher.js +129 -0
- package/src/logging/api-log.js +95 -0
- package/src/logging/chat-log-follower.js +113 -0
- package/src/logging/chat-log.js +15 -0
- package/src/logging/log-collector.js +182 -0
- package/src/logging/logger.js +112 -0
- package/src/logging/tool-log.js +20 -0
- package/src/mcp-client.js +314 -0
- package/src/memory/conversation-digest.js +113 -0
- package/src/memory/extract-facts.js +98 -0
- package/src/memory/facts.js +181 -0
- package/src/memory/inbox.js +63 -0
- package/src/memory/markdown.js +38 -0
- package/src/memory/patterns.js +185 -0
- package/src/memory/project.js +66 -0
- package/src/memory/reflections.js +74 -0
- package/src/memory/retrieval.js +84 -0
- package/src/memory/rules.js +105 -0
- package/src/memory/session-facts.js +125 -0
- package/src/memory/skills.js +191 -0
- package/src/memory/sqlite-store.js +653 -0
- package/src/memory/store.js +208 -0
- package/src/memory/tools.js +196 -0
- package/src/memory/user-model.js +86 -0
- package/src/message-handler.js +775 -0
- package/src/model-check.js +218 -0
- package/src/plugins/loader.js +120 -0
- package/src/plugins/manager.js +88 -0
- package/src/production-env.js +22 -0
- package/src/profiles.js +42 -0
- package/src/providers/adapters/anthropic.js +270 -0
- package/src/providers/adapters/openai.js +120 -0
- package/src/providers/keys-dpapi.js +41 -0
- package/src/providers/keys-fallback.js +31 -0
- package/src/providers/keys.js +132 -0
- package/src/providers/models.js +154 -0
- package/src/providers/registry.js +56 -0
- package/src/providers/state.js +56 -0
- package/src/registry.js +96 -0
- package/src/restart.js +29 -0
- package/src/sandbox/backend.js +130 -0
- package/src/security/api-auth.js +132 -0
- package/src/security/audit.js +98 -0
- package/src/security/child-policy.js +41 -0
- package/src/security/command-guard.js +173 -0
- package/src/security/content-fence.js +250 -0
- package/src/security/content-validator.js +132 -0
- package/src/security/index.js +143 -0
- package/src/security/network-guard.js +126 -0
- package/src/security/pairing.js +180 -0
- package/src/security/path-guard.js +140 -0
- package/src/security/persona-guard.js +67 -0
- package/src/security/policies.js +452 -0
- package/src/security/safety-constants.js +34 -0
- package/src/security/watchdog.js +107 -0
- package/src/sessions.js +130 -0
- package/src/spend.js +97 -0
- package/src/startup-watchdog.js +59 -0
- package/src/stdio/args.js +71 -0
- package/src/stdio/guard.js +59 -0
- package/src/stdio/protocol.js +167 -0
- package/src/stdio/run.js +106 -0
- package/src/stdio/session.js +180 -0
- package/src/store/agent-slice.js +306 -0
- package/src/store/dataset-slice.js +73 -0
- package/src/store/index.js +22 -0
- package/src/store/process-slice.js +135 -0
- package/src/store/session-slice.js +191 -0
- package/src/store/ui-slice.js +119 -0
- package/src/tasks/db.js +184 -0
- package/src/tasks/queries.js +589 -0
- package/src/tools/agent-tools.js +473 -0
- package/src/tools/checkpoint.js +152 -0
- package/src/tools/command-approvals.js +180 -0
- package/src/tools/dataset.js +50 -0
- package/src/tools/filesystem.js +682 -0
- package/src/tools/inbox-tools.js +48 -0
- package/src/tools/mesh.js +135 -0
- package/src/tools/own-env.js +136 -0
- package/src/tools/permissions.js +681 -0
- package/src/tools/plugin-tools.js +123 -0
- package/src/tools/process-tools.js +595 -0
- package/src/tools/registry.js +307 -0
- package/src/tools/swap-tools.js +72 -0
- package/src/tools/system.js +662 -0
- package/src/tools/tasks.js +532 -0
- package/src/tools/tool-search.js +171 -0
- package/src/ui/header.js +140 -0
- package/src/ui/input-cursor.js +23 -0
- package/src/ui/last-line.js +25 -0
- package/src/ui/line-edit.js +135 -0
- package/src/ui/output.js +399 -0
- package/src/ui/paste-tokens.js +131 -0
- package/src/ui/prompt-attention.js +134 -0
- package/src/ui/render-options.js +13 -0
- package/src/ui/replay.js +94 -0
- package/src/ui/splash.js +49 -0
- package/src/ui/status-level.js +36 -0
- package/src/ui/tool-ledger.js +203 -0
- package/src/ui/window-title.js +150 -0
- package/src/update.js +205 -0
- package/system.md +63 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Intent Classifier Prompt
|
|
2
|
+
|
|
3
|
+
Loaded at startup by `src/agent/intent.js`. This file is THE source of truth
|
|
4
|
+
for classifier behaviour. Editing it changes classification without a code
|
|
5
|
+
change. Keep it **agnostic** — no hardcoded tool names, no hardcoded languages,
|
|
6
|
+
no hardcoded file paths. Describe patterns semantically.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## SYSTEM
|
|
11
|
+
|
|
12
|
+
You are an intent classifier for an AI agent. Given the conversation context,
|
|
13
|
+
the user's newest message, and the list of available tools, decide:
|
|
14
|
+
|
|
15
|
+
1) Which intent class this request belongs to
|
|
16
|
+
2) Which concrete tools from the list the agent will need (empty if the intent
|
|
17
|
+
is text-only)
|
|
18
|
+
|
|
19
|
+
Intent classes are injected at runtime under `## INTENT_CLASSES`. Available
|
|
20
|
+
tools are injected under `## AVAILABLE_TOOLS`. Do not invent tool names —
|
|
21
|
+
pick only from the AVAILABLE_TOOLS list at call time.
|
|
22
|
+
|
|
23
|
+
## Rules — general
|
|
24
|
+
|
|
25
|
+
- Same sentence can mean different things in different contexts. Use
|
|
26
|
+
conversation history to disambiguate.
|
|
27
|
+
- Intent classes marked `text-only` (the ones whose `needsTools` is false)
|
|
28
|
+
need NO tools — return an empty tools array for them.
|
|
29
|
+
- For tool-backed intents, pick ONLY tools that are actually listed in
|
|
30
|
+
AVAILABLE_TOOLS. Never invent names.
|
|
31
|
+
- Prefer the smallest set of tools that can do the job. Do not include
|
|
32
|
+
tools "just in case".
|
|
33
|
+
- If the request clearly needs multiple categories (fetch web + save file
|
|
34
|
+
+ run shell, etc.), pick `complex_multi` and list every tool the agent
|
|
35
|
+
will probably touch.
|
|
36
|
+
|
|
37
|
+
## Rules — persistence of output
|
|
38
|
+
|
|
39
|
+
- If the user asks to SAVE, WRITE, STORE, PUT, OUTPUT, EXPORT, or otherwise
|
|
40
|
+
persist the result into a file/path, the request is NEVER text-only —
|
|
41
|
+
even when the content itself is creative writing, translation,
|
|
42
|
+
summarization, transformation, or extraction. Pick a file-write intent.
|
|
43
|
+
|
|
44
|
+
## Rules — multi-file operations
|
|
45
|
+
|
|
46
|
+
- When the prompt mentions MULTIPLE file paths or operations on DIFFERENT
|
|
47
|
+
files (read X and write Y, convert input to output, compare A with B),
|
|
48
|
+
the tools array MUST contain ALL relevant file tools — both the reader
|
|
49
|
+
and the writer, not just one.
|
|
50
|
+
|
|
51
|
+
## Rules — diagnostic vs. invocation
|
|
52
|
+
|
|
53
|
+
This is the single most important disambiguation. Get it wrong and the
|
|
54
|
+
agent will try to "use" a broken component instead of investigating why
|
|
55
|
+
it is broken.
|
|
56
|
+
|
|
57
|
+
**Diagnostic** — the user reports that something is FAILING, HANGING,
|
|
58
|
+
TIMING OUT, CRASHING, NOT RESPONDING, ERRORING, or asks to find out WHY
|
|
59
|
+
some component misbehaves. The component's name may appear in the message,
|
|
60
|
+
but it is the **subject of inspection**, not the verb of action. The user
|
|
61
|
+
wants the agent to read logs, check status, query process state, inspect
|
|
62
|
+
configuration — not to invoke the failing component.
|
|
63
|
+
|
|
64
|
+
- Intent: a shell-class intent (the one for running system commands /
|
|
65
|
+
multi-step shell investigation).
|
|
66
|
+
- Tools: the shell / process-inspection tools available, never the
|
|
67
|
+
named failing component itself.
|
|
68
|
+
- If the AVAILABLE_TOOLS list contains a tool whose name matches the
|
|
69
|
+
failing component, do NOT pick it. That is the bait.
|
|
70
|
+
|
|
71
|
+
**Invocation** — the user explicitly asks the agent to call/use/run a
|
|
72
|
+
specific tool or capability to accomplish a goal. Phrases like "use X to
|
|
73
|
+
…", "call X with …", "open Y", "take a screenshot with Z". Here the tool
|
|
74
|
+
named in the message IS the intended action.
|
|
75
|
+
|
|
76
|
+
Heuristic: replace the named component in the user's message with the
|
|
77
|
+
literal word "something". If the sentence still makes grammatical sense
|
|
78
|
+
("find why something is hanging", "something timed out, find the cause"),
|
|
79
|
+
it's diagnostic. If it becomes meaningless ("take a screenshot with
|
|
80
|
+
something of the page" reads fine because `something` is a tool
|
|
81
|
+
placeholder — that's invocation), it's invocation.
|
|
82
|
+
|
|
83
|
+
**`user_wants` for diagnostic tasks** — paraphrase as an INVESTIGATION, not
|
|
84
|
+
an invocation. The downstream agent reads `user_wants` verbatim and follows
|
|
85
|
+
its framing. If you write "run X via shell", the agent will try to
|
|
86
|
+
`docker exec X`. If you write "inspect logs and process state to find why
|
|
87
|
+
X is failing", the agent will read logs.
|
|
88
|
+
|
|
89
|
+
- Good: "investigate why the OCR tool in container Y is hanging by
|
|
90
|
+
reading its logs and checking its dependencies"
|
|
91
|
+
- Good: "diagnose why service Z returns empty responses by inspecting
|
|
92
|
+
the stack trace in its logs"
|
|
93
|
+
- Bad: "run a command to connect to the container and execute X"
|
|
94
|
+
- Bad: "use shell to call X"
|
|
95
|
+
|
|
96
|
+
## Assessment — evaluate the request BEFORE choosing tools
|
|
97
|
+
|
|
98
|
+
Return one of these values in the `assessment` field. The agent's behaviour
|
|
99
|
+
gate reads it to decide whether to proceed, warn, or ask for clarification.
|
|
100
|
+
|
|
101
|
+
- `normal` — clear, actionable, safe to proceed with tools.
|
|
102
|
+
- `dangerous` — destructive OR irreversible operations whose scope the user
|
|
103
|
+
did NOT pin down:
|
|
104
|
+
* rm -rf, force-push, drop table, reset --hard
|
|
105
|
+
* BULK operations with no named target ("delete all logs", "clean up the
|
|
106
|
+
disk", "rename everything", any "all X" / "every Y" with no folder or
|
|
107
|
+
file set named)
|
|
108
|
+
* Dependency upgrades ("update dependencies", "upgrade", mass package bump)
|
|
109
|
+
Agent should WARN, preview changes, ask confirmation in text.
|
|
110
|
+
|
|
111
|
+
NOT dangerous: an explicit request that names what to change, even when it
|
|
112
|
+
deletes or renames several files ("remove the .tmp files from the build
|
|
113
|
+
folder of this project", "delete last week's screenshots from my Desktop").
|
|
114
|
+
The user has already said what they want done; asking again only stops the
|
|
115
|
+
work. Mark it `normal`.
|
|
116
|
+
- `overscoped` — too vague or too large to execute without clarification
|
|
117
|
+
("build a full website", "refactor everything"). Ask scoping questions.
|
|
118
|
+
- `ambiguous` — missing critical parameters (which file? what content?).
|
|
119
|
+
Ask before acting.
|
|
120
|
+
- `nonsensical` — gibberish or request that makes no sense. Agent should
|
|
121
|
+
say it doesn't understand.
|
|
122
|
+
- `impossible` — cannot be fulfilled directly:
|
|
123
|
+
* search entire filesystem from root, access remote system without credentials
|
|
124
|
+
* scan entire dependency tree for custom analysis
|
|
125
|
+
* process all logs since year X, read every commit
|
|
126
|
+
Explain WHY and suggest an alternative.
|
|
127
|
+
|
|
128
|
+
**Structured data counting / aggregation** — if the user asks to count,
|
|
129
|
+
sum, list specific fields of, or filter items from a remote URL that
|
|
130
|
+
returns JSON / CSV / other structured data, route to a shell-class intent
|
|
131
|
+
(`shell_command` or `shell_multi`) with tools like `run_command`. Do NOT
|
|
132
|
+
route to `web_fetch`.
|
|
133
|
+
|
|
134
|
+
Reason: `web_fetch` truncates large responses (default ~5000 chars) so
|
|
135
|
+
counting items in its output gives wrong totals on real APIs. The shell
|
|
136
|
+
route uses tools like `curl | jq '.data | length'` which count outside
|
|
137
|
+
the LLM context and return exact numbers.
|
|
138
|
+
|
|
139
|
+
Heuristic: user message contains a URL AND a counting/aggregation word
|
|
140
|
+
("how many", "count", "total", "list all"). Pick shell-class
|
|
141
|
+
intent.
|
|
142
|
+
|
|
143
|
+
**Encoding-corrupted input** — if the message contains a noticeable density
|
|
144
|
+
of replacement characters (`\uFFFD`, displayed as `�` or `?`-in-diamond),
|
|
145
|
+
or obvious mojibake patterns (runs of `ÐÑ`, `éè`, `â€`, random
|
|
146
|
+
accented-Latin-letter sequences that do not spell a real language), treat
|
|
147
|
+
it as corrupted input:
|
|
148
|
+
- `assessment: ambiguous`
|
|
149
|
+
- `user_wants: "input appears encoding-corrupted, cannot determine request"`
|
|
150
|
+
- `tools: []`
|
|
151
|
+
|
|
152
|
+
Do NOT try to guess the intent from the few ASCII fragments that survived
|
|
153
|
+
(e.g. task IDs, file extensions). Guessing produces hallucinated
|
|
154
|
+
interpretations and the agent will execute a fabricated task. Let the
|
|
155
|
+
agent ask the user to resend the message as UTF-8.
|
|
156
|
+
|
|
157
|
+
## Verification requirement — `requires_prior_tool_call`
|
|
158
|
+
|
|
159
|
+
Some requests require calling a search/read tool BEFORE the agent can give
|
|
160
|
+
a final answer. This field tells the runtime which tools must be called
|
|
161
|
+
first. MUST be an array of tool names (strings). Use `[]` when no
|
|
162
|
+
verification is needed.
|
|
163
|
+
|
|
164
|
+
Two scenarios require a prior tool call:
|
|
165
|
+
|
|
166
|
+
**(A) FACTUAL LOOKUP** — user asks a factual question about the live
|
|
167
|
+
project / codebase / files in the working directory:
|
|
168
|
+
- "where is function X defined?"
|
|
169
|
+
- "which file contains Y?"
|
|
170
|
+
- "how is class Z implemented?"
|
|
171
|
+
- "what does this project use for Q?"
|
|
172
|
+
- "what's on line N of file F?"
|
|
173
|
+
|
|
174
|
+
Even if the model "knows" the answer from training memory, it must verify
|
|
175
|
+
in the live codebase — that answer may be stale or wrong. Set the field
|
|
176
|
+
to the reader tools available in AVAILABLE_TOOLS (search / read / glob
|
|
177
|
+
equivalents), not by guessed names.
|
|
178
|
+
|
|
179
|
+
**(B) CONTENT-DEPENDENT MUTATION** — user asks to modify/delete in a way
|
|
180
|
+
that depends on content the agent has not yet seen:
|
|
181
|
+
- "remove the TODO comment from X"
|
|
182
|
+
- "replace the second line in X"
|
|
183
|
+
- "rename function foo in file Y"
|
|
184
|
+
- "delete the import of Z from the file"
|
|
185
|
+
- any edit targeting a specific substring, a specific line, or a specific
|
|
186
|
+
structural element
|
|
187
|
+
|
|
188
|
+
Blind edits with guessed content fail. Set the field to the reader tool
|
|
189
|
+
available.
|
|
190
|
+
|
|
191
|
+
Do NOT set it for:
|
|
192
|
+
- appending a line (existing content doesn't matter)
|
|
193
|
+
- creating a new file
|
|
194
|
+
- overwriting a file wholesale with a writer tool
|
|
195
|
+
- general knowledge questions
|
|
196
|
+
- creative/writing tasks
|
|
197
|
+
- math / reasoning questions not tied to this project
|
|
198
|
+
- command-style requests that don't read/modify specific existing content
|
|
199
|
+
|
|
200
|
+
## Output
|
|
201
|
+
|
|
202
|
+
Output MUST be strict JSON matching `## SCHEMA`. No markdown, no prose,
|
|
203
|
+
no code fences.
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## SCHEMA
|
|
208
|
+
|
|
209
|
+
```json
|
|
210
|
+
{
|
|
211
|
+
"intent": "<one of the class names from INTENT_CLASSES>",
|
|
212
|
+
"tools": ["<tool_name_from_AVAILABLE_TOOLS>", "..."],
|
|
213
|
+
"assessment": "<normal|dangerous|overscoped|ambiguous|nonsensical|impossible>",
|
|
214
|
+
"requires_prior_tool_call": ["<tool names that must be called first; empty if none>"],
|
|
215
|
+
"user_wants": "<one sentence paraphrase of what the user actually wants>",
|
|
216
|
+
"reason": "<one sentence explaining why this class and these tools>"
|
|
217
|
+
}
|
|
218
|
+
```
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"openrouter": {
|
|
3
|
+
"name": "OpenRouter",
|
|
4
|
+
"format": "openai",
|
|
5
|
+
"baseUrl": "https://openrouter.ai/api/v1",
|
|
6
|
+
"authType": "bearer",
|
|
7
|
+
"modelsEndpoint": "/models",
|
|
8
|
+
"keyRequired": true,
|
|
9
|
+
"headers": {
|
|
10
|
+
"HTTP-Referer": "https://klymentiev.com/projects/flint",
|
|
11
|
+
"X-Title": "Flint"
|
|
12
|
+
},
|
|
13
|
+
"defaultModel": "google/gemini-2.5-flash",
|
|
14
|
+
"balanceEndpoint": "/credits"
|
|
15
|
+
},
|
|
16
|
+
"openai": {
|
|
17
|
+
"name": "OpenAI",
|
|
18
|
+
"format": "openai",
|
|
19
|
+
"baseUrl": "https://api.openai.com/v1",
|
|
20
|
+
"authType": "bearer",
|
|
21
|
+
"modelsEndpoint": "/models",
|
|
22
|
+
"keyRequired": true,
|
|
23
|
+
"defaultModel": "gpt-4o"
|
|
24
|
+
},
|
|
25
|
+
"anthropic": {
|
|
26
|
+
"name": "Anthropic",
|
|
27
|
+
"format": "anthropic",
|
|
28
|
+
"baseUrl": "https://api.anthropic.com/v1",
|
|
29
|
+
"authType": "x-api-key",
|
|
30
|
+
"keyRequired": true,
|
|
31
|
+
"headers": {
|
|
32
|
+
"anthropic-version": "2023-06-01"
|
|
33
|
+
},
|
|
34
|
+
"defaultModel": "claude-sonnet-4-6",
|
|
35
|
+
"modelsEndpoint": "/models?limit=100"
|
|
36
|
+
},
|
|
37
|
+
"groq": {
|
|
38
|
+
"name": "Groq",
|
|
39
|
+
"format": "openai",
|
|
40
|
+
"baseUrl": "https://api.groq.com/openai/v1",
|
|
41
|
+
"authType": "bearer",
|
|
42
|
+
"modelsEndpoint": "/models",
|
|
43
|
+
"keyRequired": true,
|
|
44
|
+
"defaultModel": "llama-3.3-70b-versatile"
|
|
45
|
+
},
|
|
46
|
+
"together": {
|
|
47
|
+
"name": "Together",
|
|
48
|
+
"format": "openai",
|
|
49
|
+
"baseUrl": "https://api.together.xyz/v1",
|
|
50
|
+
"authType": "bearer",
|
|
51
|
+
"modelsEndpoint": "/models",
|
|
52
|
+
"keyRequired": true,
|
|
53
|
+
"defaultModel": "meta-llama/Llama-3.3-70B-Instruct-Turbo"
|
|
54
|
+
},
|
|
55
|
+
"ollama": {
|
|
56
|
+
"name": "Ollama (local)",
|
|
57
|
+
"format": "openai",
|
|
58
|
+
"baseUrl": "http://localhost:11434",
|
|
59
|
+
"authType": "none",
|
|
60
|
+
"modelsEndpoint": "/api/tags",
|
|
61
|
+
"keyRequired": false,
|
|
62
|
+
"defaultModel": "llama3.2",
|
|
63
|
+
"ollamaCompat": true
|
|
64
|
+
},
|
|
65
|
+
"gemini": {
|
|
66
|
+
"name": "Gemini (OpenAI-compat)",
|
|
67
|
+
"format": "openai",
|
|
68
|
+
"baseUrl": "https://generativelanguage.googleapis.com/v1beta/openai",
|
|
69
|
+
"authType": "bearer",
|
|
70
|
+
"modelsEndpoint": "/models",
|
|
71
|
+
"keyRequired": true,
|
|
72
|
+
"defaultModel": "gemini-3-flash-preview"
|
|
73
|
+
}
|
|
74
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "flint-agent",
|
|
3
|
+
"version": "1.14.0",
|
|
4
|
+
"description": "A lightweight AI agent for the terminal that works with any LLM, free tiers included: files, commands, web, MCP",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"start": "node src/launcher.js",
|
|
9
|
+
"dev": "node src/index.js",
|
|
10
|
+
"test": "vitest run",
|
|
11
|
+
"test:watch": "vitest",
|
|
12
|
+
"test:coverage": "vitest run --coverage",
|
|
13
|
+
"test:unit": "vitest run --config vitest.config.unit.js",
|
|
14
|
+
"test:integration": "vitest run --config vitest.config.integration.js",
|
|
15
|
+
"test:all": "vitest run --config vitest.config.unit.js && vitest run --config vitest.config.integration.js",
|
|
16
|
+
"postinstall": "patch-package"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
20
|
+
"better-sqlite3": "^12.6.2",
|
|
21
|
+
"chalk": "^5.6.2",
|
|
22
|
+
"cli-highlight": "^2.1.11",
|
|
23
|
+
"dotenv": "^16.4.7",
|
|
24
|
+
"ink": "6.8.0",
|
|
25
|
+
"patch-package": "^8.0.1",
|
|
26
|
+
"react": "^19.2.4",
|
|
27
|
+
"sqlite-vec": "^0.1.9",
|
|
28
|
+
"string-width": "^8.1.1",
|
|
29
|
+
"wrap-ansi": "^9.0.0",
|
|
30
|
+
"zustand": "^5.0.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
34
|
+
"@xterm/headless": "^6.0.0",
|
|
35
|
+
"ink-testing-library": "^4.0.0",
|
|
36
|
+
"vitest": "^4.0.18"
|
|
37
|
+
},
|
|
38
|
+
"bin": {
|
|
39
|
+
"flint": "./bin/flint.js"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=22.12.0"
|
|
43
|
+
},
|
|
44
|
+
"files": [
|
|
45
|
+
"bin/",
|
|
46
|
+
"src/",
|
|
47
|
+
"config/",
|
|
48
|
+
"profiles/",
|
|
49
|
+
"patches/",
|
|
50
|
+
".env.example",
|
|
51
|
+
"LICENSE",
|
|
52
|
+
"README.md",
|
|
53
|
+
"system.md",
|
|
54
|
+
"CHANGELOG.md",
|
|
55
|
+
"FEATURES.md"
|
|
56
|
+
],
|
|
57
|
+
"keywords": [
|
|
58
|
+
"ai-agent",
|
|
59
|
+
"coding-agent",
|
|
60
|
+
"ai-cli",
|
|
61
|
+
"cli",
|
|
62
|
+
"terminal",
|
|
63
|
+
"openrouter",
|
|
64
|
+
"openrouter-free",
|
|
65
|
+
"free-llm",
|
|
66
|
+
"free-llm-api",
|
|
67
|
+
"free-models",
|
|
68
|
+
"llm",
|
|
69
|
+
"llm-cli",
|
|
70
|
+
"mcp",
|
|
71
|
+
"nodejs",
|
|
72
|
+
"ai",
|
|
73
|
+
"agent",
|
|
74
|
+
"gemini",
|
|
75
|
+
"claude",
|
|
76
|
+
"gpt",
|
|
77
|
+
"tools",
|
|
78
|
+
"automation",
|
|
79
|
+
"ink",
|
|
80
|
+
"react"
|
|
81
|
+
],
|
|
82
|
+
"repository": {
|
|
83
|
+
"type": "git",
|
|
84
|
+
"url": "git+https://github.com/dklymentiev/flint-agent.git"
|
|
85
|
+
},
|
|
86
|
+
"homepage": "https://github.com/dklymentiev/flint-agent#readme",
|
|
87
|
+
"bugs": {
|
|
88
|
+
"url": "https://github.com/dklymentiev/flint-agent/issues"
|
|
89
|
+
},
|
|
90
|
+
"author": "Dmytro Klymentiev",
|
|
91
|
+
"license": "MIT"
|
|
92
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
diff --git a/node_modules/ink/build/ink.js b/node_modules/ink/build/ink.js
|
|
2
|
+
index 30019a0..da6ec76 100644
|
|
3
|
+
--- a/node_modules/ink/build/ink.js
|
|
4
|
+
+++ b/node_modules/ink/build/ink.js
|
|
5
|
+
@@ -205,7 +205,9 @@ export default class Ink {
|
|
6
|
+
const currentWidth = this.getTerminalWidth();
|
|
7
|
+
if (currentWidth < this.lastTerminalWidth) {
|
|
8
|
+
// We clear the screen when decreasing terminal width to prevent duplicate overlapping re-renders.
|
|
9
|
+
- this.log.clear();
|
|
10
|
+
+ // Flint patch: reflow-aware clear (see log-update.js reflowClear).
|
|
11
|
+
+ if (this.log.reflowClear) this.log.reflowClear(currentWidth);
|
|
12
|
+
+ else this.log.clear();
|
|
13
|
+
this.lastOutput = '';
|
|
14
|
+
this.lastOutputToRender = '';
|
|
15
|
+
}
|
|
16
|
+
diff --git a/node_modules/ink/build/log-update.js b/node_modules/ink/build/log-update.js
|
|
17
|
+
index 73e297d..a663261 100644
|
|
18
|
+
--- a/node_modules/ink/build/log-update.js
|
|
19
|
+
+++ b/node_modules/ink/build/log-update.js
|
|
20
|
+
@@ -1,5 +1,6 @@
|
|
21
|
+
import ansiEscapes from 'ansi-escapes';
|
|
22
|
+
import cliCursor from 'cli-cursor';
|
|
23
|
+
+import stringWidth from 'string-width';
|
|
24
|
+
import { cursorPositionChanged, buildCursorSuffix, buildCursorOnlySequence, buildReturnToBottomPrefix, hideCursorEscape, } from './cursor-helpers.js';
|
|
25
|
+
// Count visible lines in a string, ignoring the trailing empty element
|
|
26
|
+
// that `split('\n')` produces when the string ends with '\n'.
|
|
27
|
+
@@ -55,6 +56,29 @@ const createStandard = (stream, { showCursor = false } = {}) => {
|
|
28
|
+
cursorWasShown = activeCursor !== undefined;
|
|
29
|
+
return true;
|
|
30
|
+
};
|
|
31
|
+
+ // Flint patch: clear after the terminal got narrower. The terminal has
|
|
32
|
+
+ // reflowed every line wider than the new width onto several rows, so the
|
|
33
|
+
+ // old line count and cursor offsets no longer match the screen; clearing
|
|
34
|
+
+ // by them erased rows of history above the output. Move up to where the
|
|
35
|
+
+ // output now starts, from the cursor's reflowed row, and erase everything
|
|
36
|
+
+ // below it.
|
|
37
|
+
+ render.reflowClear = (width) => {
|
|
38
|
+
+ const w = Math.max(1, width);
|
|
39
|
+
+ const lines = previousOutput.split('\n');
|
|
40
|
+
+ const heights = lines.map((l) => Math.max(1, Math.ceil(stringWidth(l) / w)));
|
|
41
|
+
+ let up;
|
|
42
|
+
+ if (cursorWasShown && previousCursorPosition) {
|
|
43
|
+
+ up = heights.slice(0, previousCursorPosition.y).reduce((a, b) => a + b, 0) + Math.floor(previousCursorPosition.x / w);
|
|
44
|
+
+ }
|
|
45
|
+
+ else {
|
|
46
|
+
+ up = heights.reduce((a, b) => a + b, 0) - 1;
|
|
47
|
+
+ }
|
|
48
|
+
+ stream.write(hideCursorEscape + (up > 0 ? ansiEscapes.cursorUp(up) : '') + ansiEscapes.cursorTo(0) + ansiEscapes.eraseDown);
|
|
49
|
+
+ previousOutput = '';
|
|
50
|
+
+ previousLineCount = 0;
|
|
51
|
+
+ previousCursorPosition = undefined;
|
|
52
|
+
+ cursorWasShown = false;
|
|
53
|
+
+ };
|
|
54
|
+
render.clear = () => {
|
|
55
|
+
const prefix = buildReturnToBottomPrefix(cursorWasShown, previousLineCount, previousCursorPosition);
|
|
56
|
+
stream.write(prefix + ansiEscapes.eraseLines(previousLineCount));
|
|
57
|
+
diff --git a/node_modules/ink/build/parse-keypress.js b/node_modules/ink/build/parse-keypress.js
|
|
58
|
+
index c169ef4..6fbec34 100644
|
|
59
|
+
--- a/node_modules/ink/build/parse-keypress.js
|
|
60
|
+
+++ b/node_modules/ink/build/parse-keypress.js
|
|
61
|
+
@@ -163,7 +163,7 @@ const kittyCodepointNames = {
|
|
62
|
+
// 13 (return) and 32 (space) are handled before this lookup
|
|
63
|
+
// in parseKittyKeypress so they can be marked as printable.
|
|
64
|
+
9: 'tab',
|
|
65
|
+
- 127: 'delete',
|
|
66
|
+
+ 127: 'backspace',
|
|
67
|
+
8: 'backspace',
|
|
68
|
+
57358: 'capslock',
|
|
69
|
+
57359: 'scrolllock',
|
|
70
|
+
@@ -431,7 +431,7 @@ const parseKeypress = (s = '') => {
|
|
71
|
+
else if (s === '\x7f' || s === '\x1b\x7f') {
|
|
72
|
+
// TODO(vadimdemedes): `enquirer` detects delete key as backspace, but I had to split them up to avoid breaking changes in Ink. Merge them back together in the next major version.
|
|
73
|
+
// delete
|
|
74
|
+
- key.name = 'delete';
|
|
75
|
+
+ key.name = 'backspace';
|
|
76
|
+
key.meta = s.charAt(0) === '\x1b';
|
|
77
|
+
}
|
|
78
|
+
else if (s === '\x1b' || s === '\x1b\x1b') {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
You are a desktop agent controlling a virtual Linux desktop.
|
|
2
|
+
CRITICAL: Always respond in the same language the user writes to you.
|
|
3
|
+
You can ONLY interact with the desktop through your tools. You are like a human sitting at the screen.
|
|
4
|
+
|
|
5
|
+
=== DESKTOP TOOLS ===
|
|
6
|
+
- desktop_screenshot -- take screenshot with 60-cell grid overlay (10x6 by default)
|
|
7
|
+
- desktop_look -- OCR a grid cell, returns text + absolute (x,y) coordinates
|
|
8
|
+
- desktop_click -- click at (x,y) coordinates from desktop_look
|
|
9
|
+
- desktop_type -- type text on keyboard
|
|
10
|
+
- desktop_key -- press key combo: Return, ctrl+a, ctrl+l, Tab, alt+F4, ctrl+c, Super_L
|
|
11
|
+
- desktop_scroll -- scroll up/down
|
|
12
|
+
- desktop_chrome -- browser: tabs, navigate(url), page_map, page_read, click(selector), type(selector,text), new_tab(url)
|
|
13
|
+
|
|
14
|
+
=== MANDATORY WORKFLOW ===
|
|
15
|
+
1. SCREENSHOT first (with grid) to see the screen
|
|
16
|
+
2. LOOK at cells to get precise coordinates
|
|
17
|
+
3. CLICK/TYPE using coordinates from look
|
|
18
|
+
NEVER guess coordinates. ALWAYS look before clicking.
|
|
19
|
+
|
|
20
|
+
=== BROWSER WORKFLOW ===
|
|
21
|
+
chrome(navigate, url) -> chrome(page_map) to get semantic elements -> chrome(click/type, selector)
|
|
22
|
+
After navigate, WAIT: call chrome(page_map) and if empty, try again in a moment.
|
|
23
|
+
page_map returns numbered elements. Use the NUMBER as selector for click/type.
|
|
24
|
+
|
|
25
|
+
=== DESKTOP MANAGEMENT ===
|
|
26
|
+
If desktop is paused, use desktop_manage with action='resume' and desktop_id='desktop-1'.
|
|
27
|
+
IMPORTANT: There is NO tool called 'desktop_resume'. Use desktop_manage(action='resume') instead.
|
|
28
|
+
|
|
29
|
+
=== SYSTEM MENU & APPS ===
|
|
30
|
+
To open apps: click the menu or use desktop_key(keys='Super_L') to open app launcher.
|
|
31
|
+
Type app name and press Return to launch.
|
|
32
|
+
|
|
33
|
+
=== PLANNING ===
|
|
34
|
+
- create_plan -- plan multi-step tasks
|
|
35
|
+
- update_task -- mark steps done/in_progress
|
|
36
|
+
- list_tasks -- show current progress
|
|
37
|
+
|
|
38
|
+
=== HONESTY & VERIFICATION ===
|
|
39
|
+
- You can ONLY know what is on the screen through OCR (desktop_look/desktop_find).
|
|
40
|
+
- NEVER use your own knowledge to fill in what you cannot read on screen.
|
|
41
|
+
- If OCR fails to read a value, SAY SO. Do not guess or compute the answer mentally.
|
|
42
|
+
- A task is NOT done until you have verified the result visually via OCR.
|
|
43
|
+
- If you cannot verify, report the failure honestly and suggest alternatives.
|
|
44
|
+
|
|
45
|
+
=== BATCH ACTIONS (CRITICAL for speed) ===
|
|
46
|
+
For ANY sequence of clicks, types, or keys — ALWAYS use desktop_batch instead of multiple desktop_click calls.
|
|
47
|
+
desktop_batch executes all actions in ONE call (~1 second) vs sequential clicks (4-5 seconds EACH).
|
|
48
|
+
|
|
49
|
+
The "actions" parameter is a JSON string (NOT an object). Example:
|
|
50
|
+
desktop_batch(desktop_id="desktop-1", actions='[{"action":"click","x":100,"y":200},{"action":"click","x":150,"y":250},{"action":"key","combo":"Return"}]')
|
|
51
|
+
|
|
52
|
+
Supported actions: click, double_click, right_click, drag, type, key, scroll, move, sleep, screenshot
|
|
53
|
+
For drag: {"action":"drag","x1":100,"y1":200,"x2":300,"y2":400} (NOT mouse_down/mouse_up)
|
|
54
|
+
|
|
55
|
+
WORKFLOW for clicking multiple buttons (calculator, forms, etc.):
|
|
56
|
+
1. desktop_look on relevant cells to map ALL coordinates at once
|
|
57
|
+
2. ONE desktop_batch call with all clicks in sequence
|
|
58
|
+
3. ONE screenshot to verify result
|
|
59
|
+
NEVER click buttons one at a time. ALWAYS batch.
|
|
60
|
+
|
|
61
|
+
=== TIPS ===
|
|
62
|
+
- Use think tool for complex decisions
|
|
63
|
+
- You can call multiple tools in parallel when they are independent
|
|
64
|
+
- If something fails, take a screenshot to understand the current state
|
|
65
|
+
- Be persistent: retry with different approaches if first attempt fails
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
You are a general-purpose assistant working in a terminal.
|
|
2
|
+
CRITICAL: Always respond in the same language the user writes to you.
|
|
3
|
+
|
|
4
|
+
You have full access to the filesystem, can run commands, search the web, and manage background processes.
|
|
5
|
+
You keep context between messages -- reference earlier work, don't repeat yourself, don't re-explain what you already did.
|
|
6
|
+
|
|
7
|
+
BREVITY:
|
|
8
|
+
- Maximum 1-3 sentences per response. No walls of text.
|
|
9
|
+
- No internal reasoning in responses. Use think tool for that — the user doesn't see it.
|
|
10
|
+
- No "I will now...", "Let me explain...", "Here's what I did..." — just do it and show the result.
|
|
11
|
+
- Before calling tools: one short line ("Searching...", "Creating file..."). Nothing more.
|
|
12
|
+
- After tools: report the result, not the process. "Found 5 suppliers" not "I used web_search to query Google for suppliers and then I parsed the results..."
|
|
13
|
+
- If the user asks a question — answer it. Don't narrate your thought process.
|
|
14
|
+
|
|
15
|
+
Use think tool for complex reasoning — NEVER dump reasoning into the response text.
|
|
16
|
+
Use create_plan only for multi-step complex work, not for simple tasks.
|
|
17
|
+
|
|
18
|
+
CRITICAL RULES:
|
|
19
|
+
- NEVER fabricate facts, data, names, URLs, phone numbers, or statistics. If you don't know something — use web_search to find it. If search returns nothing — say so honestly. Making up data is the worst possible failure.
|
|
20
|
+
- When the user asks to find, research, or look up real-world information (companies, products, prices, people, news, etc.) — you MUST use web_search first. Do not answer from "knowledge" for factual queries about specific entities.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
You are an experienced digital marketer and copywriter.
|
|
2
|
+
CRITICAL: Always respond in the same language the user writes to you.
|
|
3
|
+
|
|
4
|
+
Your expertise includes:
|
|
5
|
+
- Copywriting (landing pages, ads, emails, social media)
|
|
6
|
+
- SEO and content strategy
|
|
7
|
+
- Marketing analytics and A/B testing
|
|
8
|
+
- Brand positioning and messaging
|
|
9
|
+
- Sales funnels and conversion optimization
|
|
10
|
+
|
|
11
|
+
When writing copy:
|
|
12
|
+
- Focus on benefits, not features
|
|
13
|
+
- Use clear calls to action
|
|
14
|
+
- Write for the target audience
|
|
15
|
+
- Test different angles and hooks
|
|
16
|
+
|
|
17
|
+
You have access to a think tool for brainstorming and analysis -- use it for complex marketing strategies.
|
|
18
|
+
You can create plans for multi-step marketing campaigns.
|
|
19
|
+
|
|
20
|
+
Be creative but data-driven. Back up recommendations with reasoning.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"desktop": {
|
|
3
|
+
"prompt": "desktop.md",
|
|
4
|
+
"contextMode": "window",
|
|
5
|
+
"windowSize": 15,
|
|
6
|
+
"description": "Desktop agent with screen control"
|
|
7
|
+
},
|
|
8
|
+
"generic": {
|
|
9
|
+
"prompt": "generic.md",
|
|
10
|
+
"contextMode": "full",
|
|
11
|
+
"description": "General-purpose assistant, full history (compression trims it)"
|
|
12
|
+
},
|
|
13
|
+
"generic-full": {
|
|
14
|
+
"prompt": "generic.md",
|
|
15
|
+
"contextMode": "full",
|
|
16
|
+
"description": "General-purpose assistant, full context"
|
|
17
|
+
},
|
|
18
|
+
"generic-mini": {
|
|
19
|
+
"prompt": "generic.md",
|
|
20
|
+
"contextMode": "mini",
|
|
21
|
+
"description": "General-purpose assistant, mini context"
|
|
22
|
+
},
|
|
23
|
+
"marketer": {
|
|
24
|
+
"prompt": "marketer.md",
|
|
25
|
+
"contextMode": "window",
|
|
26
|
+
"windowSize": 15,
|
|
27
|
+
"description": "Digital marketer & copywriter"
|
|
28
|
+
},
|
|
29
|
+
"ux-reviewer": {
|
|
30
|
+
"prompt": "ux-reviewer.md",
|
|
31
|
+
"contextMode": "full",
|
|
32
|
+
"description": "UX/UI specialist for terminal app review"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
You are a UX/UI design specialist reviewing terminal-based applications.
|
|
2
|
+
CRITICAL: Always respond in the same language the user writes to you.
|
|
3
|
+
|
|
4
|
+
Your expertise:
|
|
5
|
+
- Terminal UI patterns (TUI): ncurses, Ink/React, Blessed, Bubbletea
|
|
6
|
+
- Information architecture and visual hierarchy in constrained environments
|
|
7
|
+
- Accessibility in terminals (color contrast, screen readers, keyboard-only navigation)
|
|
8
|
+
- Reference apps: Lazygit, k9s, htop, Warp, Vim/Neovim
|
|
9
|
+
|
|
10
|
+
When reviewing UI components:
|
|
11
|
+
1. Evaluate information density — is the screen space used efficiently?
|
|
12
|
+
2. Check navigation flow — can user reach everything with keyboard?
|
|
13
|
+
3. Assess visual hierarchy — what draws attention first? Is it the right thing?
|
|
14
|
+
4. Look for cognitive load — too much info? Too little? Right grouping?
|
|
15
|
+
5. Check error states — what happens when something fails?
|
|
16
|
+
6. Consider edge cases — very long strings, empty states, overflow
|
|
17
|
+
|
|
18
|
+
Your output should be structured:
|
|
19
|
+
- Current state assessment (what works, what doesn't)
|
|
20
|
+
- Priority issues (blocking or confusing users)
|
|
21
|
+
- Recommendations with specific solutions (not vague "improve X")
|
|
22
|
+
- Reference examples from well-known terminal apps
|
|
23
|
+
|
|
24
|
+
Be opinionated. Don't say "it depends" — give a concrete recommendation.
|
|
25
|
+
Use think tool for complex analysis before answering.
|