tinker-agent 1.2.1 → 1.4.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/CHANGELOG.md +26 -1
- package/README.md +217 -72
- package/bin/tinker.js +75 -25
- package/package.json +8 -3
- package/src/agent/loop.ts +115 -11
- package/src/agent/runtime-session.ts +34 -15
- package/src/cli/command-line.ts +291 -0
- package/src/cli/config.ts +131 -264
- package/src/cli/index.ts +33 -21
- package/src/cli/main.ts +213 -0
- package/src/cli/model-profiles.ts +143 -72
- package/src/cli/output.ts +113 -0
- package/src/cli/package-metadata.ts +36 -0
- package/src/cli/prompt-source.ts +229 -0
- package/src/cli/public-cli-contract.ts +69 -0
- package/src/cli/public-config-contract.ts +650 -0
- package/src/cli/run-runner.ts +17 -12
- package/src/cli/runner-dependencies.ts +100 -0
- package/src/cli/tui-runner.tsx +52 -49
- package/src/events/observation-text-log.ts +2 -0
- package/src/events/stdout-event-printer.ts +7 -1
- package/src/events/types.ts +27 -3
- package/src/mcp/mcp-manager.ts +2 -19
- package/src/mcp/mcp-tool-executor.ts +3 -4
- package/src/model/model-client.ts +29 -0
- package/src/model/model-context-profile.ts +0 -30
- package/src/model/openai-chat-mapping.ts +54 -15
- package/src/model/openai-chat-model-client.ts +46 -27
- package/src/model/openai-chat-stream.ts +6 -2
- package/src/tools/bash.ts +8 -25
- package/src/tools/grep.ts +9 -1
- package/src/tools/registry.ts +15 -1
- package/src/tools/ripgrep.ts +24 -27
- package/src/tools/web-fetch/index.ts +2 -15
- package/src/tui/app.tsx +3 -0
- package/src/tui/components/prompt-input.tsx +6 -3
- package/src/tui/event-store.ts +24 -7
- package/src/tui/slash-commands.ts +76 -24
- package/src/tui/workspace-file-search.ts +78 -71
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,29 @@ All notable user-facing changes to Tinker are documented here. The project follo
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [1.4.0] - 2026-07-24
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Add stable `tinker --help`, `tinker --version`, and `tinker help run` command
|
|
13
|
+
surfaces with explicit usage errors and exit codes.
|
|
14
|
+
- Let one-shot runs read an exact Prompt from explicit stdin or a UTF-8 text file,
|
|
15
|
+
with bounded input and strict encoding validation.
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
|
|
19
|
+
- Require `tinker run` to receive exactly one shell-quoted Prompt argument,
|
|
20
|
+
`--stdin`, or `--file <path>`. The former unquoted variadic form is no longer
|
|
21
|
+
joined automatically; quote a short Prompt or use stdin/file input instead.
|
|
22
|
+
|
|
23
|
+
## [1.3.0] - 2026-07-22
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- Recover automatically when an OpenAI-compatible provider completes a response
|
|
28
|
+
with reasoning only and no final answer or tool call: Tinker retries that exact
|
|
29
|
+
request once without adding the invalid response to session history.
|
|
30
|
+
|
|
8
31
|
## [1.2.1] - 2026-07-22
|
|
9
32
|
|
|
10
33
|
### Fixed
|
|
@@ -40,7 +63,9 @@ All notable user-facing changes to Tinker are documented here. The project follo
|
|
|
40
63
|
- First formal npm release under the `tinker-agent` package name with the `tinker`
|
|
41
64
|
executable.
|
|
42
65
|
|
|
43
|
-
[Unreleased]: https://github.com/ishowshao/tinker/compare/v1.
|
|
66
|
+
[Unreleased]: https://github.com/ishowshao/tinker/compare/v1.4.0...HEAD
|
|
67
|
+
[1.4.0]: https://github.com/ishowshao/tinker/releases/tag/v1.4.0
|
|
68
|
+
[1.3.0]: https://github.com/ishowshao/tinker/releases/tag/v1.3.0
|
|
44
69
|
[1.2.1]: https://github.com/ishowshao/tinker/releases/tag/v1.2.1
|
|
45
70
|
[1.2.0]: https://github.com/ishowshao/tinker/releases/tag/v1.2.0
|
|
46
71
|
[1.1.0]: https://www.npmjs.com/package/tinker-agent/v/1.1.0
|
package/README.md
CHANGED
|
@@ -29,7 +29,9 @@ Built with [Bun](https://bun.sh) + TypeScript ESM, powered by [Ink](https://gith
|
|
|
29
29
|
context revisions, Recall-addressable cold state, and qualified prefix retirement
|
|
30
30
|
keep long-running sessions recoverable without pretending the model has infinite
|
|
31
31
|
tokens. See the [technical design](docs/infinite-context-technical-design-a.md).
|
|
32
|
-
- **Choice of models**:
|
|
32
|
+
- **Choice of models**: Uses an OpenAI-compatible Chat Completions transport with
|
|
33
|
+
explicit model and context limits. Actual provider support must be established
|
|
34
|
+
by a qualification matrix; transport compatibility alone is not a guarantee.
|
|
33
35
|
|
|
34
36
|
## Quick Start
|
|
35
37
|
|
|
@@ -41,31 +43,65 @@ tinker
|
|
|
41
43
|
|
|
42
44
|
# Run a one-shot prompt
|
|
43
45
|
tinker run "explain the project structure"
|
|
46
|
+
|
|
47
|
+
# Inspect the installed command surface
|
|
48
|
+
tinker --help
|
|
49
|
+
tinker --version
|
|
44
50
|
```
|
|
45
51
|
|
|
46
52
|
The npm package includes its own Bun runtime. To run Tinker from a source checkout,
|
|
47
|
-
install Bun 1.3 or later, then use `bun install` followed by `bun run tinker`.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
53
|
+
install Bun 1.3.14 or later, then use `bun install` followed by `bun run tinker`.
|
|
54
|
+
|
|
55
|
+
`run` accepts exactly one Prompt source: one shell-quoted argument, explicit stdin,
|
|
56
|
+
or a UTF-8 text file. Use stdin for multiline code, private patches, or Prompt text
|
|
57
|
+
containing secrets so it does not become a command-line argument:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
printf '%s\n' 'Review `$HOME`, *.ts, and "quotes" literally.' | tinker run --stdin
|
|
61
|
+
tinker run --file prompts/review.md
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Tinker does not reconstruct shell expansions or join multiple positional arguments.
|
|
65
|
+
Use `tinker run "one quoted prompt"`; the former unquoted variadic form now fails
|
|
66
|
+
with a usage error. File paths are resolved from the current directory and may point
|
|
67
|
+
outside the configured workspace. You are responsible for Prompt-file permissions
|
|
68
|
+
and cleanup.
|
|
69
|
+
|
|
70
|
+
## Commands
|
|
71
|
+
|
|
72
|
+
The installed package exposes this public CLI:
|
|
73
|
+
|
|
74
|
+
<!-- BEGIN GENERATED: PUBLIC CLI COMMANDS -->
|
|
75
|
+
| Command | Description |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| `tinker` | Start the interactive terminal interface. |
|
|
78
|
+
| `tinker --profile <profile-name>` | Start the TUI with a selected model profile. |
|
|
79
|
+
| `tinker run [--profile <profile-name>] <prompt>` | Submit one shell-quoted prompt argument. |
|
|
80
|
+
| `tinker run [--profile <profile-name>] --stdin` | Read the prompt from standard input until EOF. |
|
|
81
|
+
| `tinker run [--profile <profile-name>] --file <path>` | Read the prompt from a UTF-8 text file. |
|
|
82
|
+
| `tinker --help` | Show top-level CLI help. |
|
|
83
|
+
| `tinker help run` | Show one-shot command help. |
|
|
84
|
+
| `tinker --version` | Print the installed package version. |
|
|
85
|
+
<!-- END GENERATED: PUBLIC CLI COMMANDS -->
|
|
86
|
+
|
|
87
|
+
Repository development commands are separate from the installed CLI:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
bun install # Install dependencies
|
|
91
|
+
bun run tinker # Start the interactive TUI
|
|
92
|
+
bun test # Run test suite
|
|
93
|
+
bun run typecheck # TypeScript type checking (tsc --noEmit)
|
|
94
|
+
bun run lint # ESLint (zero warnings required)
|
|
95
|
+
bun run format # Biome code formatting
|
|
96
|
+
bun run check # Full repository quality gate
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`bun run check` runs type checking, formatting verification, linting, the README
|
|
100
|
+
contract check, the full test suite, and the benchmark smoke check. Documentation
|
|
101
|
+
checking is read-only; use `bun run docs:generate` explicitly after changing a
|
|
102
|
+
public declaration.
|
|
103
|
+
|
|
104
|
+
## Agent Skills
|
|
69
105
|
|
|
70
106
|
Tinker scans `<workspace>/.agents/skills/` and `~/.agents/skills/` once when a new
|
|
71
107
|
or resumed runtime starts. Each direct child skill must contain a strictly valid
|
|
@@ -87,61 +123,133 @@ read-only and does not rescan, install, activate, or remove skills.
|
|
|
87
123
|
|
|
88
124
|
## Configuration
|
|
89
125
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
|
96
|
-
|
|
|
97
|
-
| `TINKER_MODELS` | — |
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
126
|
+
Set `TINKER_MODELS` to use a profile file. Without it, the env-mode model fields
|
|
127
|
+
are required. Boolean environment values accept case-insensitive `true/false`,
|
|
128
|
+
`1/0`, `yes/no`, and `on/off`.
|
|
129
|
+
|
|
130
|
+
<!-- BEGIN GENERATED: PUBLIC ENVIRONMENT VARIABLES -->
|
|
131
|
+
| Variable | Area | Applies | Required | Type | Default | Secret | Description |
|
|
132
|
+
| --- | --- | --- | --- | --- | --- | --- | --- |
|
|
133
|
+
| `TINKER_MODELS` | Model | All modes | No | Non-empty string | — | No | Optional model profiles JSON path. Relative paths resolve from the process cwd. |
|
|
134
|
+
| `TINKER_MODEL` | Model | Env mode | Env mode | Non-empty string | — | No | Model name used when model profiles are not configured. |
|
|
135
|
+
| `TINKER_BASE_URL` | Model | Env mode | Env mode | Non-empty string | — | No | OpenAI-compatible Chat Completions API base URL. |
|
|
136
|
+
| `TINKER_API_KEY` | Model | Env mode | Env mode | Non-empty string | — | Yes | API credential for the configured model endpoint. |
|
|
137
|
+
| `TINKER_CONTEXT_WINDOW_TOKENS` | Model | Env mode | Env mode | Positive integer | — | No | Model context-window size in tokens. |
|
|
138
|
+
| `TINKER_MAX_SUPPORTED_OUTPUT_TOKENS` | Model | Env mode | Env mode | Positive integer | — | No | Maximum output-token count supported by the model; must not exceed the context window. |
|
|
139
|
+
| `TINKER_INCLUDE_REASONING_CONTENT` | Model | Env mode | No | Boolean | `false` | No | Include provider reasoning content in the model response mapping. |
|
|
140
|
+
| `TINKER_STREAM` | Model | Env mode | No | Boolean | `true` | No | Use streaming Chat Completions transport. |
|
|
141
|
+
| `TINKER_WEBFETCH_REFINE_MODEL` | Model | Env mode | No | Non-empty string | — | No | Optional WebFetch refiner model; currently must match TINKER_MODEL. |
|
|
142
|
+
| `TINKER_WORKSPACE` | Workspace | All modes | No | Non-empty string | Process cwd | No | Workspace path. Relative paths resolve from the process cwd. |
|
|
143
|
+
| `TINKER_MAX_ITERATIONS` | Workspace | All modes | No | Positive integer | `512` | No | Maximum agent-loop iterations per turn. |
|
|
144
|
+
| `EXA_API_KEY` | Tooling | All modes | No | Non-empty string | — | Yes | Enables WebSearch and the Exa WebFetch backend when set. |
|
|
145
|
+
| `TINKER_MCP_TIMEOUT_MS` | Tooling | All modes | No | Positive integer | `60000` | No | MCP tool-call timeout in milliseconds. |
|
|
146
|
+
| `TINKER_MCP_MAX_OBSERVATION_CHARS` | Tooling | All modes | No | Positive integer | `40000` | No | Maximum model-visible characters in one MCP result. |
|
|
147
|
+
| `TINKER_BASH_DEFAULT_TIMEOUT_MS` | Tooling | All modes | No | Positive integer | `5000` | No | Default Bash foreground timeout in milliseconds. |
|
|
148
|
+
| `TINKER_BASH_MAX_TIMEOUT_MS` | Tooling | All modes | No | Positive integer | `600000` | No | Maximum Bash foreground timeout in milliseconds. |
|
|
149
|
+
| `TINKER_GREP_TIMEOUT_MS` | Tooling | All modes | No | Positive integer | `20000` | No | Bundled ripgrep invocation timeout in milliseconds. |
|
|
150
|
+
| `TINKER_GREP_MAX_BUFFER_BYTES` | Tooling | All modes | No | Positive integer | `20000000` | No | Maximum buffered output from one ripgrep invocation. |
|
|
151
|
+
| `TINKER_WEBFETCH_REFINE_THRESHOLD` | Tooling | All modes | No | Positive integer | `2000` | No | Content-length threshold that enables WebFetch refinement. |
|
|
152
|
+
| `TINKER_RIPGREP_PATH` | Tooling | All modes | No | Non-empty string | Bundled ripgrep | No | Explicit diagnostic override for the bundled ripgrep executable. |
|
|
153
|
+
<!-- END GENERATED: PUBLIC ENVIRONMENT VARIABLES -->
|
|
154
|
+
|
|
155
|
+
Model and estimator API keys are sent to their configured external endpoints.
|
|
156
|
+
`EXA_API_KEY` is sent to Exa when WebSearch or the Exa WebFetch backend is used.
|
|
157
|
+
Keep configuration files private and review each service's data-handling policy.
|
|
108
158
|
|
|
109
159
|
### Multi-Model Profiles
|
|
110
160
|
|
|
111
|
-
Set `TINKER_MODELS`
|
|
161
|
+
Set `TINKER_MODELS` to point to a JSON file with multiple named model
|
|
112
162
|
profiles. When set, the profile's `default` field selects the startup model, and
|
|
113
163
|
the `/model` slash command (available in new sessions before any turns) lets you
|
|
114
164
|
switch to another profile. If `TINKER_MODELS` is not set, Tinker falls back to
|
|
115
165
|
the individual `TINKER_*` environment variables. A configured profiles file must
|
|
116
166
|
exist and be valid; Tinker does not silently fall back when it cannot be loaded.
|
|
117
167
|
|
|
168
|
+
<!-- BEGIN GENERATED: MODEL PROFILE FIELDS -->
|
|
169
|
+
Profile fields:
|
|
170
|
+
|
|
171
|
+
| Field | Required | Type / constraint | Default | Secret | Description |
|
|
172
|
+
| --- | --- | --- | --- | --- | --- |
|
|
173
|
+
| `model` | Yes | Non-empty string | — | No | Provider model name. |
|
|
174
|
+
| `apiBase` | Yes | Non-empty string | — | No | OpenAI-compatible API base URL. |
|
|
175
|
+
| `apiKey` | Yes | Non-empty string | — | Yes | API credential for this profile. |
|
|
176
|
+
| `contextWindowTokens` | Yes | Positive integer | — | No | Model context-window size in tokens. |
|
|
177
|
+
| `maxSupportedOutputTokens` | Yes | Positive integer | — | No | Maximum output-token count supported by the model; must not exceed contextWindowTokens. |
|
|
178
|
+
| `includeReasoningContent` | No | JSON boolean | `false` | No | Include provider reasoning content in response mapping. |
|
|
179
|
+
| `stream` | No | JSON boolean | `true` | No | Use streaming Chat Completions transport. |
|
|
180
|
+
| `inputModalities` | No | Normalized modality array | `["text"]` | No | Accepted model input modalities; normalizes to ["text"] or ["text", "image"]. |
|
|
181
|
+
| `tokenEstimator` | With image | Object | — | Yes | Independent token estimator required for image profiles. |
|
|
182
|
+
|
|
183
|
+
`tokenEstimator` fields:
|
|
184
|
+
|
|
185
|
+
| Field | Type / constraint | Secret | Description |
|
|
186
|
+
| --- | --- | --- | --- |
|
|
187
|
+
| `kind` | Literal `"moonshot-estimate-token-count-v1"` | No | Estimator protocol discriminator. |
|
|
188
|
+
| `model` | Non-empty string | No | Estimator model name. |
|
|
189
|
+
| `apiBase` | Non-empty string | No | Estimator API base URL. |
|
|
190
|
+
| `apiKey` | Non-empty string | Yes | Estimator API credential. |
|
|
191
|
+
| `timeoutMs` | Integer 1000–60000 | No | Estimator request timeout in milliseconds. |
|
|
192
|
+
| `maxRetries` | Literal `0` | No | Estimator retry count; retries are disabled. |
|
|
193
|
+
|
|
194
|
+
Text-only profile example:
|
|
195
|
+
|
|
118
196
|
```json
|
|
119
197
|
{
|
|
120
|
-
"default": "
|
|
198
|
+
"default": "text",
|
|
121
199
|
"profiles": {
|
|
122
|
-
"
|
|
123
|
-
"model": "
|
|
124
|
-
"apiBase": "https://api.
|
|
125
|
-
"apiKey": "
|
|
200
|
+
"text": {
|
|
201
|
+
"model": "example-text-model",
|
|
202
|
+
"apiBase": "https://api.example.com/v1",
|
|
203
|
+
"apiKey": "your-model-api-key",
|
|
126
204
|
"contextWindowTokens": 128000,
|
|
127
|
-
"maxSupportedOutputTokens": 8192
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
"
|
|
131
|
-
|
|
132
|
-
|
|
205
|
+
"maxSupportedOutputTokens": 8192,
|
|
206
|
+
"includeReasoningContent": false,
|
|
207
|
+
"stream": true,
|
|
208
|
+
"inputModalities": [
|
|
209
|
+
"text"
|
|
210
|
+
]
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Image-capable profile example:
|
|
217
|
+
|
|
218
|
+
```json
|
|
219
|
+
{
|
|
220
|
+
"default": "image",
|
|
221
|
+
"profiles": {
|
|
222
|
+
"image": {
|
|
223
|
+
"model": "example-vision-model",
|
|
224
|
+
"apiBase": "https://api.example.com/v1",
|
|
225
|
+
"apiKey": "your-model-api-key",
|
|
133
226
|
"contextWindowTokens": 128000,
|
|
134
|
-
"maxSupportedOutputTokens":
|
|
135
|
-
"includeReasoningContent":
|
|
227
|
+
"maxSupportedOutputTokens": 8192,
|
|
228
|
+
"includeReasoningContent": false,
|
|
229
|
+
"stream": true,
|
|
230
|
+
"inputModalities": [
|
|
231
|
+
"text",
|
|
232
|
+
"image"
|
|
233
|
+
],
|
|
234
|
+
"tokenEstimator": {
|
|
235
|
+
"kind": "moonshot-estimate-token-count-v1",
|
|
236
|
+
"model": "example-token-estimator",
|
|
237
|
+
"apiBase": "https://estimator.example.com/v1",
|
|
238
|
+
"apiKey": "your-estimator-api-key",
|
|
239
|
+
"timeoutMs": 30000,
|
|
240
|
+
"maxRetries": 0
|
|
241
|
+
}
|
|
136
242
|
}
|
|
137
243
|
}
|
|
138
244
|
}
|
|
139
245
|
```
|
|
246
|
+
<!-- END GENERATED: MODEL PROFILE FIELDS -->
|
|
140
247
|
|
|
141
|
-
You can also
|
|
248
|
+
You can also select profiles explicitly for the TUI or one-shot command:
|
|
142
249
|
|
|
143
250
|
```bash
|
|
144
|
-
|
|
251
|
+
tinker --profile text
|
|
252
|
+
tinker run --profile text "explain the project structure"
|
|
145
253
|
```
|
|
146
254
|
|
|
147
255
|
Switching models creates a new session. The previous session is preserved and
|
|
@@ -151,6 +259,46 @@ longer present or its runtime contract has changed. Older sessions without a
|
|
|
151
259
|
stored profile name can resume only when their model name uniquely matches one
|
|
152
260
|
configured profile.
|
|
153
261
|
|
|
262
|
+
### Image Input
|
|
263
|
+
|
|
264
|
+
Image attachment is enabled only for a profile whose `inputModalities` explicitly
|
|
265
|
+
includes `image` and which supplies a valid `tokenEstimator`. In the interactive
|
|
266
|
+
TUI, type `@` and select a file that is inside the workspace and visible to the
|
|
267
|
+
workspace search rules. One-shot commands, clipboard image bytes, remote URLs, and
|
|
268
|
+
files outside or ignored by the workspace search are not supported.
|
|
269
|
+
|
|
270
|
+
Tinker accepts PNG (not APNG), JPEG, and static WebP. It rejects GIF, animated
|
|
271
|
+
WebP, and other formats. A message and provider request may contain at most eight
|
|
272
|
+
images; each image may be at most 20 MiB, 4096 pixels on either edge, and 8,847,360
|
|
273
|
+
pixels in total. See the
|
|
274
|
+
[`multimodal image input design`](docs/multimodal-image-input-design.md) for the
|
|
275
|
+
complete fixed policy and persistence contract.
|
|
276
|
+
|
|
277
|
+
### Built-in Slash Commands
|
|
278
|
+
|
|
279
|
+
<!-- BEGIN GENERATED: BUILT-IN SLASH COMMANDS -->
|
|
280
|
+
| Command | Description |
|
|
281
|
+
| --- | --- |
|
|
282
|
+
| `/status` | Show session and context details |
|
|
283
|
+
| `/skills` | Show available and active Agent Skills |
|
|
284
|
+
| `/mcp` | Show MCP servers and runtime tools |
|
|
285
|
+
| `/compact [retire]` | Swap tool output or retire a cold history prefix |
|
|
286
|
+
| `/clear` | Start a new session and clear conversation |
|
|
287
|
+
| `/fork` | Clone the current session |
|
|
288
|
+
| `/view <path>` | View a local UTF-8 text file |
|
|
289
|
+
| `/copy` | Copy the last response as Markdown |
|
|
290
|
+
| `/model [profile-name]` | Switch model profile (new session) |
|
|
291
|
+
| `/resume [session-id]` | Choose or resume a session |
|
|
292
|
+
| `/session delete <session-id> --confirm` | Manage stored sessions |
|
|
293
|
+
| `/quit` | Exit the TUI |
|
|
294
|
+
<!-- END GENERATED: BUILT-IN SLASH COMMANDS -->
|
|
295
|
+
|
|
296
|
+
`/view` opens a readable UTF-8 text file in a full-window viewer. Relative paths
|
|
297
|
+
must remain inside the workspace; absolute paths may point outside it. `/compact`
|
|
298
|
+
swaps eligible historical tool output, while `/compact retire` retires a complete
|
|
299
|
+
cold prefix whose original history remains available through `Recall`. `/copy`
|
|
300
|
+
copies the last completed assistant response as raw Markdown.
|
|
301
|
+
|
|
154
302
|
### Project Custom Slash Commands
|
|
155
303
|
|
|
156
304
|
The TUI loads optional project-scoped prompt aliases from `.tinker.json` in the
|
|
@@ -178,24 +326,20 @@ one-shot `tinker run` command. See
|
|
|
178
326
|
[`docs/project-custom-slash-commands-design.md`](docs/project-custom-slash-commands-design.md)
|
|
179
327
|
for the full contract.
|
|
180
328
|
|
|
329
|
+
### Workspace-Local Runtime Data
|
|
330
|
+
|
|
331
|
+
Tinker keeps private runtime state under `<workspace>/.tinker/`: each session has
|
|
332
|
+
its own SQLite database, event log, and observation log, while image assets and
|
|
333
|
+
Prompt history are stored at the workspace level. These locations are fixed and
|
|
334
|
+
have no public path-override environment variables. MCP server configuration is
|
|
335
|
+
loaded from `<workspace>/.mcp.json`; project slash commands are loaded from
|
|
336
|
+
`<workspace>/.tinker.json`.
|
|
337
|
+
|
|
181
338
|
`Read` has a fixed 262144-byte (256 KiB) content limit per call. A successful
|
|
182
339
|
call always returns the complete requested line range. Use `offset` and `limit`
|
|
183
340
|
to page through larger files; oversized requests fail instead of returning
|
|
184
341
|
truncated content.
|
|
185
342
|
|
|
186
|
-
## Commands
|
|
187
|
-
|
|
188
|
-
```bash
|
|
189
|
-
bun install # Install dependencies
|
|
190
|
-
bun run tinker # Start the interactive TUI
|
|
191
|
-
bun run tinker run "..." # Run a one-shot CLI prompt
|
|
192
|
-
bun test # Run test suite
|
|
193
|
-
bun run typecheck # TypeScript type checking (tsc --noEmit)
|
|
194
|
-
bun run lint # ESLint (zero warnings required)
|
|
195
|
-
bun run format # Biome code formatting
|
|
196
|
-
bun run check # Full check: typecheck + format + lint + test
|
|
197
|
-
```
|
|
198
|
-
|
|
199
343
|
## Project Structure
|
|
200
344
|
|
|
201
345
|
```
|
|
@@ -228,8 +372,9 @@ tinker/
|
|
|
228
372
|
## Requirements
|
|
229
373
|
|
|
230
374
|
- Node.js 20 or later and npm for the global package installation
|
|
231
|
-
- [Bun](https://bun.sh) 1.3 or later for development from source
|
|
232
|
-
- A compatible
|
|
375
|
+
- [Bun](https://bun.sh) 1.3.14 or later for development from source
|
|
376
|
+
- A configured OpenAI-compatible Chat Completions endpoint; transport compatibility
|
|
377
|
+
alone does not establish provider qualification
|
|
233
378
|
|
|
234
379
|
## Security
|
|
235
380
|
|
package/bin/tinker.js
CHANGED
|
@@ -1,39 +1,89 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { realpathSync } from "node:fs";
|
|
4
5
|
import { createRequire } from "node:module";
|
|
5
6
|
import { dirname, join } from "node:path";
|
|
6
7
|
import { fileURLToPath } from "node:url";
|
|
7
8
|
|
|
8
9
|
const require = createRequire(import.meta.url);
|
|
10
|
+
const START_FAILURE_MESSAGE =
|
|
11
|
+
"Tinker failed to start. Reinstall tinker-agent.\n";
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
export async function launchTinker(
|
|
14
|
+
input = {
|
|
15
|
+
args: process.argv.slice(2),
|
|
16
|
+
stderr: process.stderr,
|
|
17
|
+
},
|
|
18
|
+
injected = {},
|
|
19
|
+
) {
|
|
20
|
+
const resolveBundledBun =
|
|
21
|
+
injected.resolveBundledBun ?? (() => require.resolve("bun/package.json"));
|
|
22
|
+
const startChild = injected.spawn ?? spawn;
|
|
23
|
+
const forwardSignal =
|
|
24
|
+
injected.forwardSignal ?? ((signal) => process.kill(process.pid, signal));
|
|
25
|
+
|
|
26
|
+
let bunPackageJson;
|
|
27
|
+
try {
|
|
28
|
+
bunPackageJson = resolveBundledBun();
|
|
29
|
+
} catch {
|
|
30
|
+
input.stderr.write(START_FAILURE_MESSAGE);
|
|
31
|
+
return 1;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const bunExecutable = join(dirname(bunPackageJson), "bin", "bun.exe");
|
|
35
|
+
const tinkerEntryPoint = fileURLToPath(
|
|
36
|
+
new URL("../src/cli/index.ts", import.meta.url),
|
|
16
37
|
);
|
|
17
|
-
|
|
18
|
-
|
|
38
|
+
let child;
|
|
39
|
+
try {
|
|
40
|
+
child = startChild(
|
|
41
|
+
bunExecutable,
|
|
42
|
+
[tinkerEntryPoint, ...input.args],
|
|
43
|
+
{ stdio: "inherit" },
|
|
44
|
+
);
|
|
45
|
+
} catch {
|
|
46
|
+
input.stderr.write(START_FAILURE_MESSAGE);
|
|
47
|
+
return 1;
|
|
48
|
+
}
|
|
19
49
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
50
|
+
return new Promise((resolve) => {
|
|
51
|
+
let settled = false;
|
|
52
|
+
child.once("error", () => {
|
|
53
|
+
if (settled) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
settled = true;
|
|
57
|
+
input.stderr.write(START_FAILURE_MESSAGE);
|
|
58
|
+
resolve(1);
|
|
59
|
+
});
|
|
60
|
+
child.once("exit", (code, signal) => {
|
|
61
|
+
if (settled) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
settled = true;
|
|
65
|
+
if (signal !== null) {
|
|
66
|
+
forwardSignal(signal);
|
|
67
|
+
resolve(1);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
resolve(code ?? 1);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
33
73
|
}
|
|
34
74
|
|
|
35
|
-
|
|
36
|
-
process.
|
|
75
|
+
function isExecutableEntryPoint() {
|
|
76
|
+
const entryPoint = process.argv[1];
|
|
77
|
+
if (entryPoint === undefined) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
return realpathSync(entryPoint) === realpathSync(fileURLToPath(import.meta.url));
|
|
82
|
+
} catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
37
85
|
}
|
|
38
86
|
|
|
39
|
-
|
|
87
|
+
if (isExecutableEntryPoint()) {
|
|
88
|
+
process.exitCode = await launchTinker();
|
|
89
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tinker-agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "A personal coding agent with an interactive TUI and one-shot CLI.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -58,13 +58,15 @@
|
|
|
58
58
|
"chrome:diagnose": "bun packages/tinker-chrome/src/cli.ts diagnose",
|
|
59
59
|
"chrome:install-host": "bun packages/tinker-chrome/src/cli.ts install-host",
|
|
60
60
|
"chrome:mcp": "bun packages/tinker-chrome/src/cli.ts mcp",
|
|
61
|
+
"docs:generate": "bun scripts/render-public-contract-docs.ts --write",
|
|
62
|
+
"docs:check": "bun scripts/render-public-contract-docs.ts --check",
|
|
61
63
|
"tinker": "bun src/cli/index.ts",
|
|
62
|
-
"check": "bun run typecheck && bun run format:check && bun run lint && bun test && bun run bench:smoke",
|
|
64
|
+
"check": "bun run typecheck && bun run format:check && bun run lint && bun run docs:check && bun run test && bun run bench:smoke",
|
|
63
65
|
"format": "biome format --write .",
|
|
64
66
|
"format:check": "biome format .",
|
|
65
67
|
"lint": "eslint \"bin/**/*.js\" \"src/**/*.{ts,tsx}\" \"scripts/**/*.ts\" \"packages/**/*.ts\" --max-warnings=0",
|
|
66
68
|
"lint:fix": "eslint \"bin/**/*.js\" \"src/**/*.{ts,tsx}\" \"scripts/**/*.ts\" \"packages/**/*.ts\" --fix",
|
|
67
|
-
"test": "bun test",
|
|
69
|
+
"test": "FORCE_COLOR=0 bun test",
|
|
68
70
|
"typecheck": "tsc --noEmit"
|
|
69
71
|
},
|
|
70
72
|
"dependencies": {
|
|
@@ -76,6 +78,7 @@
|
|
|
76
78
|
"@vscode/ripgrep": "1.18.0",
|
|
77
79
|
"bun": "1.3.14",
|
|
78
80
|
"clipboardy": "^5.3.1",
|
|
81
|
+
"commander": "^14.0.3",
|
|
79
82
|
"diff": "^9.0.0",
|
|
80
83
|
"glob": "^13.0.6",
|
|
81
84
|
"ink": "^7.1.0",
|
|
@@ -96,6 +99,8 @@
|
|
|
96
99
|
"@types/chrome": "^0.2.2",
|
|
97
100
|
"@types/react": "^19.2.17",
|
|
98
101
|
"@types/turndown": "^5.0.6",
|
|
102
|
+
"@xterm/addon-unicode11": "0.9.0",
|
|
103
|
+
"@xterm/headless": "6.0.0",
|
|
99
104
|
"eslint": "^10.6.0",
|
|
100
105
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
101
106
|
"globals": "^17.7.0",
|