pum-agent 0.2.16-beta.2 → 0.2.18-beta.1

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.
Files changed (88) hide show
  1. package/README.md +12 -3
  2. package/assets/tree-sitter/README.md +26 -0
  3. package/assets/tree-sitter/bash/highlights.scm +56 -0
  4. package/assets/tree-sitter/bash/tree-sitter-bash.wasm +0 -0
  5. package/assets/tree-sitter/go/highlights.scm +123 -0
  6. package/assets/tree-sitter/go/tree-sitter-go.wasm +0 -0
  7. package/assets/tree-sitter/json/highlights.scm +16 -0
  8. package/assets/tree-sitter/json/tree-sitter-json.wasm +0 -0
  9. package/assets/tree-sitter/python/highlights.scm +137 -0
  10. package/assets/tree-sitter/python/tree-sitter-python.wasm +0 -0
  11. package/assets/tree-sitter/rust/highlights.scm +161 -0
  12. package/assets/tree-sitter/rust/tree-sitter-rust.wasm +0 -0
  13. package/package.json +2 -1
  14. package/src/afk-command.ts +36 -0
  15. package/src/afk-delegate.ts +338 -0
  16. package/src/afk.ts +188 -0
  17. package/src/animation.tsx +502 -68
  18. package/src/app.tsx +1874 -387
  19. package/src/apply-patch.ts +17 -1
  20. package/src/attachment-markers.ts +96 -0
  21. package/src/bash-output.ts +53 -2
  22. package/src/check-mode.ts +52 -12
  23. package/src/check-policy.ts +271 -29
  24. package/src/cli.ts +83 -17
  25. package/src/commands.ts +27 -2
  26. package/src/credential-path.ts +35 -1
  27. package/src/explanation-strength.ts +4 -3
  28. package/src/filesystem-sandbox.ts +101 -6
  29. package/src/git-branch.ts +12 -0
  30. package/src/goal-command.ts +58 -0
  31. package/src/goal-judge.ts +152 -0
  32. package/src/goal-line.ts +94 -0
  33. package/src/goal-review.ts +69 -0
  34. package/src/goal.ts +475 -0
  35. package/src/headless-stats.ts +29 -2
  36. package/src/headless.ts +46 -7
  37. package/src/help-popup.tsx +72 -37
  38. package/src/index.tsx +14 -0
  39. package/src/login-controller.ts +16 -0
  40. package/src/main.tsx +99 -8
  41. package/src/mode-line.ts +149 -0
  42. package/src/news-popup.tsx +2 -2
  43. package/src/news.ts +8 -2
  44. package/src/outer-sandbox.ts +30 -6
  45. package/src/output-minimal.ts +26 -6
  46. package/src/pasted-text.ts +12 -1
  47. package/src/path-autocomplete.ts +52 -14
  48. package/src/platform.ts +65 -3
  49. package/src/prompt-cache-identity.ts +106 -0
  50. package/src/prompt-cache.ts +156 -11
  51. package/src/questionnaire.ts +60 -0
  52. package/src/queue-recall.ts +32 -0
  53. package/src/relocation.ts +187 -0
  54. package/src/replay.ts +34 -7
  55. package/src/sandbox/linux.ts +16 -3
  56. package/src/sandbox/windows.ts +264 -264
  57. package/src/sandbox-policy.ts +10 -8
  58. package/src/session-history-metadata.ts +51 -1
  59. package/src/session-settings.ts +119 -0
  60. package/src/session-stats.ts +127 -16
  61. package/src/settings-popup.tsx +11 -5
  62. package/src/settings.ts +62 -7
  63. package/src/shells/manager.ts +9 -3
  64. package/src/shells/process.ts +8 -1
  65. package/src/shells/tools.ts +2 -1
  66. package/src/shells/types.ts +11 -1
  67. package/src/status-bar.tsx +6 -21
  68. package/src/status-metadata.ts +19 -0
  69. package/src/subagents/manager.ts +494 -67
  70. package/src/subagents/readonly.ts +5 -0
  71. package/src/subagents/types.ts +37 -0
  72. package/src/syntax-grammars.ts +49 -0
  73. package/src/syntax.ts +6 -0
  74. package/src/theme.ts +76 -19
  75. package/src/todo-popup.tsx +350 -0
  76. package/src/todo-tools.ts +341 -0
  77. package/src/todo.ts +320 -0
  78. package/src/tool-groups.ts +24 -1
  79. package/src/tool-line.ts +50 -34
  80. package/src/tool-preview.ts +121 -10
  81. package/src/transcript-output.ts +18 -4
  82. package/src/transcript.tsx +373 -45
  83. package/src/triggers/manager.ts +86 -24
  84. package/src/triggers/process.ts +42 -11
  85. package/src/triggers/types.ts +2 -0
  86. package/src/worktree-command.ts +51 -1
  87. package/src/worktree-start.ts +177 -0
  88. package/src/worktree.ts +71 -9
package/README.md CHANGED
@@ -109,11 +109,14 @@ pum
109
109
 
110
110
  The package is named `pum-agent` because the bare `pum` name is already owned. The installed command is still `pum`.
111
111
 
112
+ PUM runs on [Bun](https://bun.sh), not on Node. Install Bun first. `npm i -g pum-agent` copies the files, but `pum` then fails with `env: 'bun': No such file or directory`, because the command starts with `#!/usr/bin/env bun`.
113
+
112
114
  ## Command-line options
113
115
 
114
116
  ```text
115
117
  pum [options]
116
118
  pum login [options]
119
+ pum -p "<text>" [options]
117
120
  pum s [login] [options] [directory[:ro|:rw] ...]
118
121
  pum sr [login] [options] [directory[:ro|:rw] ...]
119
122
  pum ss
@@ -124,6 +127,10 @@ pum ss
124
127
  | `-h`, `--help` | Print the command-line manual and exit |
125
128
  | `-v`, `--version` | Print the exact `pum-agent` package version and exit |
126
129
  | `-r`, `--resume` | Resume the latest session for the current directory |
130
+ | `-p`, `--prompt <text>` | Run one prompt without the TUI, print the answer, and exit |
131
+ | `--statsFile <path>` | Write a JSON statistics artifact after a headless run |
132
+ | `--override` | Let `--statsFile` replace an existing file |
133
+ | `--` | End the options; later arguments are directories |
127
134
  | `login` | Start PUM with the provider login panel open |
128
135
  | `s` | Start PUM in a writable outer `claudebox` sandbox |
129
136
  | `sr` | Start PUM with the current directory read-only |
@@ -131,7 +138,9 @@ pum ss
131
138
 
132
139
  Plain extra directories use the command default. Add `:ro` or `:rw` to select explicit access. `pum sr` always keeps the launch directory read-only, but it permits an explicit writable extra directory. A custom `PUM_DIR` must remain outside the project for `pum sr`.
133
140
 
134
- Help, version, and sandbox setup checks do not initialize the TUI, credentials, or sessions. Unknown options and commands return an error and a help hint.
141
+ Write `login` before the directories. PUM rejects a later `login` instead of mounting it. Put `--` before a directory whose name starts with a dash or is called `login`.
142
+
143
+ Help, version, and sandbox setup checks do not initialize the TUI, credentials, or sessions. Help and version print even when a later argument is invalid. Other unknown options and commands return an error and a help hint.
135
144
 
136
145
  Set `PUM_DIR` to override PUM's complete configuration and data directory. Set `PUM_CLAUDEBOX` to select a specific `claudebox` executable. Run `pum --help` for a concise directory summary. Enter `?` on an empty in-app prompt to see all controls.
137
146
 
@@ -316,7 +325,7 @@ Verifier prompts stay bounded. For an oversized On-mode review, PUM sends comple
316
325
 
317
326
  Web search is on by default for supported OpenAI Codex providers. Searches appear as transcript tool rows and persist in resumed sessions. Other providers continue without the hosted search tool. Disable web search in `Ctrl+P`.
318
327
 
319
- Detailed explanations show text results for every tool call, including `read`. The **Tool output lines** setting limits each result to five source lines by default. PUM shows this setting only while explanations are detailed.
328
+ Setting **Output** to detailed adds a result preview under the tool row. Bash shows the last five lines of output, `write` shows the first thirty lines of the new file, and `edit` shows the patch. These limits are fixed; there is no setting for them.
320
329
 
321
330
  ### Themes and Markdown
322
331
 
@@ -348,7 +357,7 @@ Set `PUM_DIR` to override the complete PUM data directory.
348
357
  | `auth.json` | Provider credentials and custom-provider keys |
349
358
  | `models.json` | Custom endpoints and model metadata; submitted keys are not stored here |
350
359
  | `settings.json` | Model and thinking level managed by pi |
351
- | `pum.json` | Theme, animation, transcript output, search, writing, explanation, Check mode, sandbox, and subagent settings |
360
+ | `pum.json` | Theme, animation, thinking traces, transcript output, search, writing, explanation, Check mode, extra Check mode roots (`checkPaths`), sandbox, Bash output limits (`bashOutput`), and subagent settings |
352
361
  | `theme.json` | Optional semantic color overrides |
353
362
  | `history.json` | Prompt history by working directory |
354
363
  | `prompt-stash.json` | Cached prompts by working directory (legacy filename) |
@@ -0,0 +1,26 @@
1
+ # Vendored tree-sitter grammars
2
+
3
+ OpenTUI ships parsers for JavaScript, TypeScript, Markdown and Zig only. These
4
+ five cover the next most common languages a diff in the transcript carries.
5
+ They are committed rather than downloaded so that a diff highlights offline,
6
+ inside a sandbox, and on the first run.
7
+
8
+ `src/syntax-grammars.ts` declares them; `src/syntax.ts` registers them once.
9
+
10
+ | Language | `.wasm` from | `highlights.scm` from |
11
+ |---|---|---|
12
+ | python | npm `tree-sitter-wasms@0.1.13`, `out/` | `tree-sitter/tree-sitter-python`, `queries/highlights.scm` |
13
+ | json | npm `tree-sitter-wasms@0.1.13`, `out/` | `tree-sitter/tree-sitter-json`, `queries/highlights.scm` |
14
+ | bash | npm `tree-sitter-wasms@0.1.13`, `out/` | `tree-sitter/tree-sitter-bash`, `queries/highlights.scm` |
15
+ | rust | npm `@repomix/tree-sitter-wasms@0.1.17`, `out/` | `tree-sitter/tree-sitter-rust`, `queries/highlights.scm` |
16
+ | go | npm `tree-sitter-wasms@0.1.13`, `out/` | `tree-sitter/tree-sitter-go`, `queries/highlights.scm` |
17
+
18
+ Rust comes from a different build on purpose. The `tree-sitter-wasms` rust
19
+ binary still exports `tree_sitter_rust_external_scanner_reset`, dropped from
20
+ the external-scanner ABI after version 13, and web-tree-sitter 0.25 refuses to
21
+ load it — `preloadParser` returns false and every rust diff renders flat.
22
+
23
+ `syntax-grammars.test.ts` highlights a sample of each language and asserts the
24
+ captures are ones `buildSyntaxStyle` colours. Run it after replacing any file
25
+ here: a grammar that loads but produces unmapped captures looks identical to
26
+ no grammar at all.
@@ -0,0 +1,56 @@
1
+ [
2
+ (string)
3
+ (raw_string)
4
+ (heredoc_body)
5
+ (heredoc_start)
6
+ ] @string
7
+
8
+ (command_name) @function
9
+
10
+ (variable_name) @property
11
+
12
+ [
13
+ "case"
14
+ "do"
15
+ "done"
16
+ "elif"
17
+ "else"
18
+ "esac"
19
+ "export"
20
+ "fi"
21
+ "for"
22
+ "function"
23
+ "if"
24
+ "in"
25
+ "select"
26
+ "then"
27
+ "unset"
28
+ "until"
29
+ "while"
30
+ ] @keyword
31
+
32
+ (comment) @comment
33
+
34
+ (function_definition name: (word) @function)
35
+
36
+ (file_descriptor) @number
37
+
38
+ [
39
+ (command_substitution)
40
+ (process_substitution)
41
+ (expansion)
42
+ ]@embedded
43
+
44
+ [
45
+ "$"
46
+ "&&"
47
+ ">"
48
+ ">>"
49
+ "<"
50
+ "|"
51
+ ] @operator
52
+
53
+ (
54
+ (command (_) @constant)
55
+ (#match? @constant "^-")
56
+ )
@@ -0,0 +1,123 @@
1
+ ; Function calls
2
+
3
+ (call_expression
4
+ function: (identifier) @function)
5
+
6
+ (call_expression
7
+ function: (identifier) @function.builtin
8
+ (#match? @function.builtin "^(append|cap|close|complex|copy|delete|imag|len|make|new|panic|print|println|real|recover)$"))
9
+
10
+ (call_expression
11
+ function: (selector_expression
12
+ field: (field_identifier) @function.method))
13
+
14
+ ; Function definitions
15
+
16
+ (function_declaration
17
+ name: (identifier) @function)
18
+
19
+ (method_declaration
20
+ name: (field_identifier) @function.method)
21
+
22
+ ; Identifiers
23
+
24
+ (type_identifier) @type
25
+ (field_identifier) @property
26
+ (identifier) @variable
27
+
28
+ ; Operators
29
+
30
+ [
31
+ "--"
32
+ "-"
33
+ "-="
34
+ ":="
35
+ "!"
36
+ "!="
37
+ "..."
38
+ "*"
39
+ "*"
40
+ "*="
41
+ "/"
42
+ "/="
43
+ "&"
44
+ "&&"
45
+ "&="
46
+ "%"
47
+ "%="
48
+ "^"
49
+ "^="
50
+ "+"
51
+ "++"
52
+ "+="
53
+ "<-"
54
+ "<"
55
+ "<<"
56
+ "<<="
57
+ "<="
58
+ "="
59
+ "=="
60
+ ">"
61
+ ">="
62
+ ">>"
63
+ ">>="
64
+ "|"
65
+ "|="
66
+ "||"
67
+ "~"
68
+ ] @operator
69
+
70
+ ; Keywords
71
+
72
+ [
73
+ "break"
74
+ "case"
75
+ "chan"
76
+ "const"
77
+ "continue"
78
+ "default"
79
+ "defer"
80
+ "else"
81
+ "fallthrough"
82
+ "for"
83
+ "func"
84
+ "go"
85
+ "goto"
86
+ "if"
87
+ "import"
88
+ "interface"
89
+ "map"
90
+ "package"
91
+ "range"
92
+ "return"
93
+ "select"
94
+ "struct"
95
+ "switch"
96
+ "type"
97
+ "var"
98
+ ] @keyword
99
+
100
+ ; Literals
101
+
102
+ [
103
+ (interpreted_string_literal)
104
+ (raw_string_literal)
105
+ (rune_literal)
106
+ ] @string
107
+
108
+ (escape_sequence) @escape
109
+
110
+ [
111
+ (int_literal)
112
+ (float_literal)
113
+ (imaginary_literal)
114
+ ] @number
115
+
116
+ [
117
+ (true)
118
+ (false)
119
+ (nil)
120
+ (iota)
121
+ ] @constant.builtin
122
+
123
+ (comment) @comment
@@ -0,0 +1,16 @@
1
+ (pair
2
+ key: (_) @string.special.key)
3
+
4
+ (string) @string
5
+
6
+ (number) @number
7
+
8
+ [
9
+ (null)
10
+ (true)
11
+ (false)
12
+ ] @constant.builtin
13
+
14
+ (escape_sequence) @escape
15
+
16
+ (comment) @comment
@@ -0,0 +1,137 @@
1
+ ; Identifier naming conventions
2
+
3
+ (identifier) @variable
4
+
5
+ ((identifier) @constructor
6
+ (#match? @constructor "^[A-Z]"))
7
+
8
+ ((identifier) @constant
9
+ (#match? @constant "^[A-Z][A-Z_]*$"))
10
+
11
+ ; Function calls
12
+
13
+ (decorator) @function
14
+ (decorator
15
+ (identifier) @function)
16
+
17
+ (call
18
+ function: (attribute attribute: (identifier) @function.method))
19
+ (call
20
+ function: (identifier) @function)
21
+
22
+ ; Builtin functions
23
+
24
+ ((call
25
+ function: (identifier) @function.builtin)
26
+ (#match?
27
+ @function.builtin
28
+ "^(abs|all|any|ascii|bin|bool|breakpoint|bytearray|bytes|callable|chr|classmethod|compile|complex|delattr|dict|dir|divmod|enumerate|eval|exec|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|isinstance|issubclass|iter|len|list|locals|map|max|memoryview|min|next|object|oct|open|ord|pow|print|property|range|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|vars|zip|__import__)$"))
29
+
30
+ ; Function definitions
31
+
32
+ (function_definition
33
+ name: (identifier) @function)
34
+
35
+ (attribute attribute: (identifier) @property)
36
+ (type (identifier) @type)
37
+
38
+ ; Literals
39
+
40
+ [
41
+ (none)
42
+ (true)
43
+ (false)
44
+ ] @constant.builtin
45
+
46
+ [
47
+ (integer)
48
+ (float)
49
+ ] @number
50
+
51
+ (comment) @comment
52
+ (string) @string
53
+ (escape_sequence) @escape
54
+
55
+ (interpolation
56
+ "{" @punctuation.special
57
+ "}" @punctuation.special) @embedded
58
+
59
+ [
60
+ "-"
61
+ "-="
62
+ "!="
63
+ "*"
64
+ "**"
65
+ "**="
66
+ "*="
67
+ "/"
68
+ "//"
69
+ "//="
70
+ "/="
71
+ "&"
72
+ "&="
73
+ "%"
74
+ "%="
75
+ "^"
76
+ "^="
77
+ "+"
78
+ "->"
79
+ "+="
80
+ "<"
81
+ "<<"
82
+ "<<="
83
+ "<="
84
+ "<>"
85
+ "="
86
+ ":="
87
+ "=="
88
+ ">"
89
+ ">="
90
+ ">>"
91
+ ">>="
92
+ "|"
93
+ "|="
94
+ "~"
95
+ "@="
96
+ "and"
97
+ "in"
98
+ "is"
99
+ "not"
100
+ "or"
101
+ "is not"
102
+ "not in"
103
+ ] @operator
104
+
105
+ [
106
+ "as"
107
+ "assert"
108
+ "async"
109
+ "await"
110
+ "break"
111
+ "class"
112
+ "continue"
113
+ "def"
114
+ "del"
115
+ "elif"
116
+ "else"
117
+ "except"
118
+ "exec"
119
+ "finally"
120
+ "for"
121
+ "from"
122
+ "global"
123
+ "if"
124
+ "import"
125
+ "lambda"
126
+ "nonlocal"
127
+ "pass"
128
+ "print"
129
+ "raise"
130
+ "return"
131
+ "try"
132
+ "while"
133
+ "with"
134
+ "yield"
135
+ "match"
136
+ "case"
137
+ ] @keyword
@@ -0,0 +1,161 @@
1
+ ; Identifiers
2
+
3
+ (type_identifier) @type
4
+ (primitive_type) @type.builtin
5
+ (field_identifier) @property
6
+
7
+ ; Identifier conventions
8
+
9
+ ; Assume all-caps names are constants
10
+ ((identifier) @constant
11
+ (#match? @constant "^[A-Z][A-Z\\d_]+$'"))
12
+
13
+ ; Assume uppercase names are enum constructors
14
+ ((identifier) @constructor
15
+ (#match? @constructor "^[A-Z]"))
16
+
17
+ ; Assume that uppercase names in paths are types
18
+ ((scoped_identifier
19
+ path: (identifier) @type)
20
+ (#match? @type "^[A-Z]"))
21
+ ((scoped_identifier
22
+ path: (scoped_identifier
23
+ name: (identifier) @type))
24
+ (#match? @type "^[A-Z]"))
25
+ ((scoped_type_identifier
26
+ path: (identifier) @type)
27
+ (#match? @type "^[A-Z]"))
28
+ ((scoped_type_identifier
29
+ path: (scoped_identifier
30
+ name: (identifier) @type))
31
+ (#match? @type "^[A-Z]"))
32
+
33
+ ; Assume all qualified names in struct patterns are enum constructors. (They're
34
+ ; either that, or struct names; highlighting both as constructors seems to be
35
+ ; the less glaring choice of error, visually.)
36
+ (struct_pattern
37
+ type: (scoped_type_identifier
38
+ name: (type_identifier) @constructor))
39
+
40
+ ; Function calls
41
+
42
+ (call_expression
43
+ function: (identifier) @function)
44
+ (call_expression
45
+ function: (field_expression
46
+ field: (field_identifier) @function.method))
47
+ (call_expression
48
+ function: (scoped_identifier
49
+ "::"
50
+ name: (identifier) @function))
51
+
52
+ (generic_function
53
+ function: (identifier) @function)
54
+ (generic_function
55
+ function: (scoped_identifier
56
+ name: (identifier) @function))
57
+ (generic_function
58
+ function: (field_expression
59
+ field: (field_identifier) @function.method))
60
+
61
+ (macro_invocation
62
+ macro: (identifier) @function.macro
63
+ "!" @function.macro)
64
+
65
+ ; Function definitions
66
+
67
+ (function_item (identifier) @function)
68
+ (function_signature_item (identifier) @function)
69
+
70
+ (line_comment) @comment
71
+ (block_comment) @comment
72
+
73
+ (line_comment (doc_comment)) @comment.documentation
74
+ (block_comment (doc_comment)) @comment.documentation
75
+
76
+ "(" @punctuation.bracket
77
+ ")" @punctuation.bracket
78
+ "[" @punctuation.bracket
79
+ "]" @punctuation.bracket
80
+ "{" @punctuation.bracket
81
+ "}" @punctuation.bracket
82
+
83
+ (type_arguments
84
+ "<" @punctuation.bracket
85
+ ">" @punctuation.bracket)
86
+ (type_parameters
87
+ "<" @punctuation.bracket
88
+ ">" @punctuation.bracket)
89
+
90
+ "::" @punctuation.delimiter
91
+ ":" @punctuation.delimiter
92
+ "." @punctuation.delimiter
93
+ "," @punctuation.delimiter
94
+ ";" @punctuation.delimiter
95
+
96
+ (parameter (identifier) @variable.parameter)
97
+
98
+ (lifetime (identifier) @label)
99
+
100
+ "as" @keyword
101
+ "async" @keyword
102
+ "await" @keyword
103
+ "break" @keyword
104
+ "const" @keyword
105
+ "continue" @keyword
106
+ "default" @keyword
107
+ "dyn" @keyword
108
+ "else" @keyword
109
+ "enum" @keyword
110
+ "extern" @keyword
111
+ "fn" @keyword
112
+ "for" @keyword
113
+ "gen" @keyword
114
+ "if" @keyword
115
+ "impl" @keyword
116
+ "in" @keyword
117
+ "let" @keyword
118
+ "loop" @keyword
119
+ "macro_rules!" @keyword
120
+ "match" @keyword
121
+ "mod" @keyword
122
+ "move" @keyword
123
+ "pub" @keyword
124
+ "raw" @keyword
125
+ "ref" @keyword
126
+ "return" @keyword
127
+ "static" @keyword
128
+ "struct" @keyword
129
+ "trait" @keyword
130
+ "type" @keyword
131
+ "union" @keyword
132
+ "unsafe" @keyword
133
+ "use" @keyword
134
+ "where" @keyword
135
+ "while" @keyword
136
+ "yield" @keyword
137
+ (crate) @keyword
138
+ (mutable_specifier) @keyword
139
+ (use_list (self) @keyword)
140
+ (scoped_use_list (self) @keyword)
141
+ (scoped_identifier (self) @keyword)
142
+ (super) @keyword
143
+
144
+ (self) @variable.builtin
145
+
146
+ (char_literal) @string
147
+ (string_literal) @string
148
+ (raw_string_literal) @string
149
+
150
+ (boolean_literal) @constant.builtin
151
+ (integer_literal) @constant.builtin
152
+ (float_literal) @constant.builtin
153
+
154
+ (escape_sequence) @escape
155
+
156
+ (attribute_item) @attribute
157
+ (inner_attribute_item) @attribute
158
+
159
+ "*" @operator
160
+ "&" @operator
161
+ "'" @operator
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pum-agent",
3
- "version": "0.2.16-beta.2",
3
+ "version": "0.2.18-beta.1",
4
4
  "description": "A compact terminal coding agent powered by pi and OpenTUI.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -33,6 +33,7 @@
33
33
  "src/**/*.tsx",
34
34
  "!src/**/*.test.ts",
35
35
  "!src/**/*.test.tsx",
36
+ "assets/tree-sitter/**",
36
37
  "LICENSE"
37
38
  ],
38
39
  "publishConfig": {
@@ -0,0 +1,36 @@
1
+ import { afkInstructionProblem } from "./afk";
2
+
3
+ /**
4
+ * `/afk` parsing.
5
+ *
6
+ * Unlike `/goal` there are no control words: `/afk` alone toggles the mode and
7
+ * anything after it is guidance. So "/afk stop asking about tests" sets
8
+ * guidance rather than stopping AFK, and nothing the user types can be read as
9
+ * a hidden action.
10
+ */
11
+
12
+ export type AfkCommand =
13
+ | { kind: "toggle" }
14
+ | { kind: "instructions"; text: string }
15
+ | { kind: "error"; message: string };
16
+
17
+ const AFK_USAGE = "/afk toggles away mode. /afk <instructions> starts it, or re-steers a running one.";
18
+
19
+ /** True for any input `/afk` owns, so App can route before the model sees it. */
20
+ export function isAfkCommand(text: string): boolean {
21
+ return /^\/afk(?:\s|$)/.test(text.trim());
22
+ }
23
+
24
+ /** Pure and total: every input yields a command or null, and nothing throws. */
25
+ export function parseAfkCommand(input: string): AfkCommand | null {
26
+ const trimmed = input.trim();
27
+ const match = /^\/afk(?:\s+([\s\S]*))?$/.exec(trimmed);
28
+ if (!match) return null;
29
+
30
+ const argument = (match[1] ?? "").trim();
31
+ if (!argument) return { kind: "toggle" };
32
+
33
+ const problem = afkInstructionProblem(argument);
34
+ if (problem) return { kind: "error", message: `${problem}. ${AFK_USAGE}` };
35
+ return { kind: "instructions", text: argument };
36
+ }