@iinm/plain-agent 1.7.18 → 1.7.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,10 +1,6 @@
1
- <p align="center">
2
- <img src="https://pub-0bb49aa929f242d49c89ed8c297932b5.r2.dev/plain-agent/plain-agent-logo.png" alt="plain-agent logo" width="320">
3
- </p>
4
-
5
1
  # Plain Agent
6
2
 
7
- A lightweight CLI-based coding agent with zero framework dependencies.
3
+ A lightweight CLI-based coding agent.
8
4
 
9
5
  ## Why Plain Agent?
10
6
 
@@ -23,6 +19,7 @@ A lightweight CLI-based coding agent with zero framework dependencies.
23
19
 
24
20
  ## Limitations
25
21
 
22
+ - **CLI only** — Plain Agent does not provide a terminal UI.
26
23
  - **Sequential subagent execution** — Subagents run one at a time rather than
27
24
  in parallel. The trade-off is full visibility: every step is streamed to
28
25
  your terminal so you can follow exactly what each subagent is doing.
@@ -31,7 +28,7 @@ A lightweight CLI-based coding agent with zero framework dependencies.
31
28
 
32
29
  - Node.js 22 or later
33
30
  - LLM provider credentials
34
- - bash / docker for sandboxed execution
31
+ - Bash / Docker for sandboxed execution
35
32
  - [ripgrep](https://github.com/burntsushi/ripgrep)
36
33
  - [fd](https://github.com/sharkdp/fd)
37
34
 
@@ -52,8 +49,8 @@ Create the configuration.
52
49
  ```js
53
50
  // ~/.config/plain-agent/config.local.json
54
51
  {
55
- "model": "gpt-5.4+thinking-high",
56
- // "model": "claude-sonnet-4-6+thinking-high",
52
+ "model": "claude-sonnet-4-6+thinking-high",
53
+ // "model": "gpt-5.5+thinking-high",
57
54
 
58
55
  // Configure the providers you want to use
59
56
  "platforms": [
@@ -83,15 +80,11 @@ Create the configuration.
83
80
  "provider": "gemini",
84
81
  "apiKey": "<GEMINI_API_KEY>",
85
82
  "model": "gemini-3-flash-preview"
86
- // Optional
87
- // "baseURL": "<proxy_url>"
88
83
 
89
84
  // Or use Vertex AI (Requires gcloud CLI to get authentication token)
90
85
  // "provider": "gemini-vertex-ai",
91
86
  // "baseURL": "https://aiplatform.googleapis.com/v1beta1/projects/<project_id>/locations/<location>",
92
87
  // "model": "gemini-3-flash-preview"
93
- // Optional:
94
- // "account": "<service_account_email>"
95
88
  },
96
89
 
97
90
  // askURL: Answers questions based on provided URL content.
@@ -100,15 +93,8 @@ Create the configuration.
100
93
  "provider": "gemini",
101
94
  "apiKey": "<GEMINI_API_KEY>"
102
95
  "model": "gemini-3-flash-preview"
103
- // Optional
104
- // "baseURL": "<proxy_url>"
105
96
 
106
97
  // Or use Vertex AI (Requires gcloud CLI to get authentication token)
107
- // "provider": "gemini-vertex-ai",
108
- // "baseURL": "https://aiplatform.googleapis.com/v1beta1/projects/<project_id>/locations/<location>",
109
- // "model": "gemini-3-flash-preview"
110
- // Optional:
111
- // "account": "<service_account_email>"
112
98
  }
113
99
  },
114
100
 
@@ -166,6 +152,7 @@ Create the configuration.
166
152
  ```
167
153
 
168
154
  ```js
155
+ // OpenAI-compatible provider (Ollama) example with a custom model
169
156
  {
170
157
  "platforms": [
171
158
  {
@@ -279,6 +266,9 @@ plain
279
266
  plain -m <model+variant>
280
267
  ```
281
268
 
269
+ Interrupt the agent while it's running:
270
+ Press **Ctrl-C** to pause auto-approve. The agent will finish the current tool call, then return to the prompt.
271
+
282
272
  (Optional) Set up a sandbox for your project with the `sandbox-configurator` agent.
283
273
 
284
274
  ```
@@ -292,7 +282,7 @@ After the agent finishes, run the generated setup script once to build the sandb
292
282
  ```
293
283
 
294
284
  Run in batch mode (non-interactive).
295
- In batch mode, config files are not loaded automatically. Only the files specified with `--config` are loaded.
285
+ In batch mode, config files are not loaded automatically. Only the files specified with `-c` are loaded.
296
286
 
297
287
  ```sh
298
288
  plain batch \
@@ -307,9 +297,14 @@ Display the help message.
307
297
  /help
308
298
  ```
309
299
 
310
- Interrupt the agent while it's running:
300
+ Show daily token costs across sessions. `plain cost` reads
301
+ `~/.local/share/plain-agent/usage.jsonl`; use `--from` / `--to` to set the
302
+ period. Currencies are shown separately.
311
303
 
312
- Press **Ctrl-C** to pause auto-approve. The agent will finish the current tool call, then return to the prompt.
304
+ ```sh
305
+ plain cost
306
+ plain cost --from 2026-04-01 --to 2026-04-30
307
+ ```
313
308
 
314
309
  ## Available Tools
315
310
 
@@ -327,29 +322,24 @@ The agent can use the following tools to assist with tasks:
327
322
 
328
323
  ## Configuration
329
324
 
325
+ Files are loaded in the following order. Settings in later files override earlier ones.
326
+
330
327
  ```
331
328
  ~/.config/plain-agent/
332
- \__ config.json # User configuration
333
- \__ config.local.json # User local configuration (including secrets)
334
- \__ prompts/ # Global/User-defined prompts
335
- \__ agents/ # Global/User-defined agent roles
329
+ ├── (1) config.json # User configuration
330
+ ├── (2) config.local.json # User local configuration (including secrets)
331
+ ├── prompts/ # Global/User-defined prompts
332
+ └── agents/ # Global/User-defined agent roles
336
333
 
337
334
  <project-root>
338
- \__ .plain-agent/
339
- \__ config.json # Project-specific configuration
340
- \__ config.local.json # Project-specific local configuration (including secrets)
341
- \__ memory/ # Task-specific memory files
342
- \__ prompts/ # Project-specific prompts
343
- \__ agents/ # Project-specific agent roles
335
+ └── .plain-agent/
336
+ ├── (3) config.json # Project-specific configuration
337
+ ├── (4) config.local.json # Project-specific local configuration (including secrets)
338
+ ├── memory/ # Task-specific memory files
339
+ ├── prompts/ # Project-specific prompts
340
+ └── agents/ # Project-specific agent roles
344
341
  ```
345
342
 
346
- The agent loads configuration files in the following order. Settings in later files will override those in earlier files.
347
-
348
- - `~/.config/plain-agent/config.json`
349
- - `~/.config/plain-agent/config.local.json`
350
- - `.plain-agent/config.json`
351
- - `.plain-agent/config.local.json`
352
-
353
343
  ### Example
354
344
 
355
345
  <details>
@@ -412,19 +402,19 @@ The agent loads configuration files in the following order. Settings in later fi
412
402
  "patterns": [
413
403
  {
414
404
  "toolName": { "$regex": "^(write_file|patch_file)$" },
415
- "input": { "filePath": { "$regex": "^(\\./)?\\.plain-agent/memory/.+\\.md$" } },
405
+ "input": { "filePath": { "$regex": "^\\.plain-agent/memory/.+\\.md$" } },
416
406
  "action": "allow"
417
407
  },
418
408
  {
419
409
  "toolName": { "$regex": "^(write_file|patch_file)$" },
420
- "input": { "filePath": { "$regex": "^(\\./)?src/" } },
410
+ "input": { "filePath": { "$regex": "^src/" } },
421
411
  "action": "allow"
422
412
  },
423
413
 
424
414
  // ⚠️ Arbitrary code execution can access unauthorized files and networks. Always use a sandbox.
425
415
  {
426
416
  "toolName": "exec_command",
427
- "input": { "command": "npm", "args": ["run", { "$regex": "^(check|test|lint|fix)$" }] },
417
+ "input": { "command": "npm", "args": ["run", { "$regex": "^(lint|test)$" }] },
428
418
  "action": "allow"
429
419
  },
430
420
 
@@ -500,8 +490,8 @@ The agent loads configuration files in the following order. Settings in later fi
500
490
  }
501
491
  },
502
492
 
503
- // Override default notification command
504
- // "notifyCmd": "/path/to/notification-command"
493
+ // Override default notification command (falls back to terminal bell)
494
+ // "notifyCmd": { "command": "plain-notify-desktop", "args": [] }
505
495
 
506
496
  // (Optional) Voice input. See "Voice Input" below.
507
497
  // "voiceInput": {
@@ -516,6 +506,17 @@ The agent loads configuration files in the following order. Settings in later fi
516
506
 
517
507
  You can define reusable prompts in Markdown files.
518
508
 
509
+ ### Locations
510
+
511
+ The agent searches for prompts in the following directories:
512
+
513
+ - `~/.config/plain-agent/prompts/`
514
+ - `.plain-agent/prompts/`
515
+ - `.claude/commands/`
516
+ - `.claude/skills/`
517
+
518
+ The prompt ID is the relative path of the file without the `.md` extension. For example, `.plain-agent/prompts/commit.md` becomes `/prompts:commit`.
519
+
519
520
  ### Prompt File Format
520
521
 
521
522
  ```md
@@ -537,30 +538,8 @@ import: https://raw.githubusercontent.com/anthropics/claude-code/5cff78741f54a0d
537
538
  - Parallel execution of subagents is not supported. Delegate to subagents sequentially.
538
539
  ```
539
540
 
540
- ```md
541
- ---
542
- import: https://raw.githubusercontent.com/anthropics/claude-code/db8834ba1d72e9a26fba30ac85f3bc4316bb0689/plugins/code-review/commands/code-review.md
543
- ---
544
-
545
- - Parallel execution of subagents is not supported. Delegate to subagents sequentially.
546
- - If CLAUDE.md is not found, refer to AGENTS.md instead for project rules and conventions.
547
- - If the PR branch is already checked out, review changes from local files instead of fetching from GitHub.
548
- - After explaining the review results to the user, ask whether to post the comments to GitHub as well.
549
- ```
550
-
551
541
  Remote prompts are fetched and cached locally. The local content will be appended to the imported content.
552
542
 
553
- ### Locations
554
-
555
- The agent searches for prompts in the following directories:
556
-
557
- - `~/.config/plain-agent/prompts/`
558
- - `.plain-agent/prompts/`
559
- - `.claude/commands/`
560
- - `.claude/skills/`
561
-
562
- The prompt ID is the relative path of the file without the `.md` extension. For example, `.plain-agent/prompts/commit.md` becomes `/prompts:commit`.
563
-
564
543
  ### Shortcuts
565
544
 
566
545
  Prompts located in a `shortcuts/` subdirectory (e.g., `.plain-agent/prompts/shortcuts/commit.md`) can be invoked directly as a top-level command (e.g., `/commit`).
@@ -569,6 +548,14 @@ Prompts located in a `shortcuts/` subdirectory (e.g., `.plain-agent/prompts/shor
569
548
 
570
549
  Subagents are specialized agents designed for specific tasks.
571
550
 
551
+ ### Locations
552
+
553
+ The agent searches for subagent definitions in the following directories:
554
+
555
+ - `~/.config/plain-agent/agents/`
556
+ - `.plain-agent/agents/`
557
+ - `.claude/agents/`
558
+
572
559
  ### Subagent File Format
573
560
 
574
561
  ```md
@@ -591,14 +578,6 @@ Use AGENTS.md instead of CLAUDE.md in this project.
591
578
 
592
579
  Remote subagents are fetched and cached locally. The local content will be appended to the imported content.
593
580
 
594
- ### Locations
595
-
596
- The agent searches for subagent definitions in the following directories:
597
-
598
- - `~/.config/plain-agent/agents/`
599
- - `.plain-agent/agents/`
600
- - `.claude/agents/`
601
-
602
581
  ## Claude Code Plugin Support
603
582
 
604
583
  Example:
@@ -642,7 +621,7 @@ and send them like regular text.
642
621
 
643
622
  ### Providers
644
623
 
645
- **OpenAI Realtime** (default, recommended):
624
+ **OpenAI Realtime**
646
625
 
647
626
  ```js
648
627
  {
@@ -655,7 +634,7 @@ and send them like regular text.
655
634
  }
656
635
  ```
657
636
 
658
- **Gemini Live** (preview API; model names and pricing may change):
637
+ **Gemini Live**
659
638
 
660
639
  ```js
661
640
  {
@@ -672,8 +651,7 @@ and send them like regular text.
672
651
 
673
652
  - `toggleKey` — Rebind the toggle. Accepts `"ctrl-<char>"` where `<char>`
674
653
  is a letter (a-z) or one of `[ \ ] ^ _`. Defaults to `"ctrl-o"`.
675
- - `recorder` — Override recorder auto-detection. Must write raw 16-bit
676
- little-endian mono PCM to stdout at 24 kHz (OpenAI) or 16 kHz (Gemini).
654
+ - `recorder` — Override recorder auto-detection, e.g. `{ "command": "sox", "args": ["-q", "-d", "-b", "16", "-c", "1", "-r", "24000", "-e", "signed-integer", "-t", "raw", "-"] }`. Must write raw 16-bit little-endian mono PCM to stdout at 24 kHz (OpenAI) or 16 kHz (Gemini).
677
655
 
678
656
  ## Development
679
657
 
@@ -694,13 +672,9 @@ npx npm-check-updates -t minor -c 3 -u
694
672
  ## Release
695
673
 
696
674
  ```sh
697
- npm run check
698
-
699
- git commit -m "<message>"
700
-
701
675
  npm version <major|minor|patch>
702
- git push --follow-tags
703
676
 
677
+ git push --follow-tags
704
678
  gh release create $(git describe --tags) --generate-notes
705
679
 
706
680
  npm publish --access public
@@ -813,7 +813,7 @@
813
813
  }
814
814
  },
815
815
  {
816
- "name": "gpt-5.4",
816
+ "name": "gpt-5.5",
817
817
  "variant": "thinking-medium",
818
818
  "platform": {
819
819
  "name": "openai",
@@ -823,7 +823,7 @@
823
823
  "model": {
824
824
  "format": "openai-responses",
825
825
  "config": {
826
- "model": "gpt-5.4",
826
+ "model": "gpt-5.5",
827
827
  "reasoning": { "effort": "medium", "summary": "auto" },
828
828
  "store": false,
829
829
  "include": ["reasoning.encrypted_content"]
@@ -833,14 +833,14 @@
833
833
  "currency": "USD",
834
834
  "unit": "1M",
835
835
  "costs": {
836
- "input_tokens": 2.5,
837
- "input_tokens_details.cached_tokens": -2.25,
838
- "output_tokens": 15
836
+ "input_tokens": 5,
837
+ "input_tokens_details.cached_tokens": -4.5,
838
+ "output_tokens": 30
839
839
  }
840
840
  }
841
841
  },
842
842
  {
843
- "name": "gpt-5.4",
843
+ "name": "gpt-5.5",
844
844
  "variant": "thinking-high",
845
845
  "platform": {
846
846
  "name": "openai",
@@ -850,7 +850,7 @@
850
850
  "model": {
851
851
  "format": "openai-responses",
852
852
  "config": {
853
- "model": "gpt-5.4",
853
+ "model": "gpt-5.5",
854
854
  "reasoning": { "effort": "high", "summary": "auto" },
855
855
  "store": false,
856
856
  "include": ["reasoning.encrypted_content"]
@@ -860,14 +860,14 @@
860
860
  "currency": "USD",
861
861
  "unit": "1M",
862
862
  "costs": {
863
- "input_tokens": 2.5,
864
- "input_tokens_details.cached_tokens": -2.25,
865
- "output_tokens": 15
863
+ "input_tokens": 5,
864
+ "input_tokens_details.cached_tokens": -4.5,
865
+ "output_tokens": 30
866
866
  }
867
867
  }
868
868
  },
869
869
  {
870
- "name": "gpt-5.4",
870
+ "name": "gpt-5.5",
871
871
  "variant": "thinking-xhigh",
872
872
  "platform": {
873
873
  "name": "openai",
@@ -877,7 +877,7 @@
877
877
  "model": {
878
878
  "format": "openai-responses",
879
879
  "config": {
880
- "model": "gpt-5.4",
880
+ "model": "gpt-5.5",
881
881
  "reasoning": { "effort": "xhigh", "summary": "auto" },
882
882
  "store": false,
883
883
  "include": ["reasoning.encrypted_content"]
@@ -887,9 +887,9 @@
887
887
  "currency": "USD",
888
888
  "unit": "1M",
889
889
  "costs": {
890
- "input_tokens": 2.5,
891
- "input_tokens_details.cached_tokens": -2.25,
892
- "output_tokens": 15
890
+ "input_tokens": 5,
891
+ "input_tokens_details.cached_tokens": -4.5,
892
+ "output_tokens": 30
893
893
  }
894
894
  }
895
895
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iinm/plain-agent",
3
- "version": "1.7.18",
3
+ "version": "1.7.20",
4
4
  "description": "A lightweight CLI-based coding agent",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -10,9 +10,7 @@
10
10
  },
11
11
  "bin": {
12
12
  "plain": "./bin/plain",
13
- "plain-interrupt": "./bin/plain-interrupt",
14
13
  "plain-notify-desktop": "./bin/plain-notify-desktop",
15
- "plain-notify-terminal-bell": "./bin/plain-notify-terminal-bell",
16
14
  "plain-sandbox": "./sandbox/bin/plain-sandbox"
17
15
  },
18
16
  "files": [
package/src/agentLoop.mjs CHANGED
@@ -128,7 +128,9 @@ export function createAgentLoop({
128
128
 
129
129
  const { message: assistantMessage, providerTokenUsage } = modelOutput;
130
130
  stateManager.appendMessages([assistantMessage]);
131
- agentEventEmitter.emit("providerTokenUsage", providerTokenUsage);
131
+ if (providerTokenUsage) {
132
+ agentEventEmitter.emit("providerTokenUsage", providerTokenUsage);
133
+ }
132
134
 
133
135
  // Gemini may stop with "thinking" -> continue
134
136
  const lastContent = assistantMessage.content.at(-1);
package/src/cliArgs.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @typedef {HelpSubcommand | InteractiveSubcommand | BatchSubcommand | ListModelsSubcommand | InstallClaudeCodePluginsSubcommand} Subcommand
2
+ * @typedef {HelpSubcommand | InteractiveSubcommand | BatchSubcommand | ListModelsSubcommand | InstallClaudeCodePluginsSubcommand | CostSubcommand} Subcommand
3
3
  */
4
4
 
5
5
  /**
@@ -22,6 +22,10 @@
22
22
  * @typedef {{ type: 'install-claude-code-plugins' }} InstallClaudeCodePluginsSubcommand
23
23
  */
24
24
 
25
+ /**
26
+ * @typedef {{ type: 'cost', from: string | null, to: string | null }} CostSubcommand
27
+ */
28
+
25
29
  /**
26
30
  * @typedef {Object} CliArgs
27
31
  * @property {Subcommand} subcommand - The subcommand to execute
@@ -106,6 +110,28 @@ export function parseCliArgs(argv) {
106
110
  };
107
111
  }
108
112
 
113
+ if (subcommandName === "cost") {
114
+ const costArgs = args.slice(1);
115
+ let from = null;
116
+ let to = null;
117
+ for (let i = 0; i < costArgs.length; i++) {
118
+ if (costArgs[i] === "--from") {
119
+ if (costArgs[i + 1]) {
120
+ from = costArgs[i + 1];
121
+ i++;
122
+ }
123
+ } else if (costArgs[i] === "--to") {
124
+ if (costArgs[i + 1]) {
125
+ to = costArgs[i + 1];
126
+ i++;
127
+ }
128
+ }
129
+ }
130
+ return {
131
+ subcommand: { type: "cost", from, to },
132
+ };
133
+ }
134
+
109
135
  return {
110
136
  subcommand: { type: "help" },
111
137
  };
@@ -119,6 +145,7 @@ export function printHelp(exitCode = 0) {
119
145
  console.log(`
120
146
  Usage: plain [options]
121
147
  plain batch [options] <task>
148
+ plain cost [--from YYYY-MM-DD] [--to YYYY-MM-DD]
122
149
  plain list-models
123
150
  plain install-claude-code-plugins
124
151
 
@@ -131,6 +158,9 @@ Subcommands:
131
158
  batch <task> Run in batch mode with the given task instruction.
132
159
  Config files are NOT auto-loaded in batch mode;
133
160
  use -c to specify config files explicitly.
161
+ cost Show aggregated token cost per day for a period.
162
+ Defaults to the first day of the current month
163
+ through today.
134
164
  list-models List available models
135
165
  install-claude-code-plugins Install Claude Code plugins
136
166
 
package/src/cliBatch.mjs CHANGED
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  import { formatCostForBatch } from "./cliFormatter.mjs";
6
+ import { appendUsageRecord, buildUsageRecord } from "./usageStore.mjs";
6
7
 
7
8
  /**
8
9
  * @typedef {object} BatchSessionOptions
@@ -46,6 +47,27 @@ export async function startBatchSession({
46
47
  timestamp: new Date().toISOString(),
47
48
  cost: formatCostForBatch(costSummary),
48
49
  });
50
+
51
+ try {
52
+ const record = buildUsageRecord({
53
+ sessionId,
54
+ mode: "batch",
55
+ modelName,
56
+ workingDir: process.cwd(),
57
+ costSummary,
58
+ });
59
+ if (record) {
60
+ await appendUsageRecord(record);
61
+ }
62
+ } catch (err) {
63
+ const message = err instanceof Error ? err.message : String(err);
64
+ outputEvent({
65
+ type: "error",
66
+ error: { message: `failed to record usage: ${message}` },
67
+ timestamp: new Date().toISOString(),
68
+ });
69
+ }
70
+
49
71
  await onStop();
50
72
  resolve();
51
73
  });