pikuri-workspace 0.0.3 → 0.0.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7cd37f2cda8958c1a0098160a71b6a0a3e375cd6f8169c298909950a8c6fdb87
4
- data.tar.gz: fb99c3d2b8dcb886f5f5e93bb6562bf2e87b8bad0d44cdf71da2ece883ef3270
3
+ metadata.gz: 4be40e803f487eb130b8ee758b86efb7a744c92691a1dd103aae68d91e59a2f9
4
+ data.tar.gz: 981478770e9e7f8acef1fb1ca2b1896cf6a25c7eb73c01c1d57592f81583cf0f
5
5
  SHA512:
6
- metadata.gz: 4a79642151199e5e4ceeefda2130c270de29015350fa5a128d54d1a692fac9e37949363db2f4222d635d7619e09409140d6bad41c7cc841f1b9f1cde58762fc0
7
- data.tar.gz: eb45fe6977711e452f399f58a9e4f0d80450e62008c1af32fc6c0d9fe04d7b3c63b8149c685f296dbed1de3e9f9cd883dea13714e23baa87681abeb28eb02f05
6
+ metadata.gz: a211df218e4270acbe737f624cc8a77cc145d8a051c3500131036692168996c800a0110a27bd714a7cad3fb5cb9dd3e25fdfdeefb917fb73ccead021cd807c94
7
+ data.tar.gz: fe2ff7510012d619caa166252ccef80148750e35fa58a68cd1722cec372758dc17e5605cbe380c4f106538b301134402da32e0fb13c6f23d6acec7c4aceb301e
data/README.md CHANGED
@@ -4,15 +4,17 @@ Filesystem tools + Workspace/Confirmer seams for the
4
4
  [pikuri](https://codeberg.org/mvysny/pikuri) AI-assistant toolkit.
5
5
 
6
6
  Self-contained "operate on a directory tree" toolkit:
7
- - `Pikuri::Tool::Workspace` — abstract base + bundled
8
- `Workspace::Cwd` that scopes filesystem access to a chosen root,
9
- rejecting `..`-escapes and symlinks that resolve outside the root.
10
- - `Pikuri::Tool::Confirmer` abstract base + `AUTO_APPROVE` /
7
+ - `Pikuri::Workspace::Filesystem` — scopes filesystem access to a
8
+ project root + explicit readable / writable prefix lists, with an
9
+ optional ephemeral temp playground. Rejects `..`-escapes and
10
+ symlinks that resolve outside the configured roots.
11
+ - `Pikuri::Workspace::Confirmer` — abstract base + `AUTO_APPROVE` /
11
12
  `TERMINAL` for user-state mutations.
12
- - Five file tools: `Pikuri::Tool::Read`, `Pikuri::Tool::Write`,
13
- `Pikuri::Tool::Edit`, `Pikuri::Tool::Grep`, `Pikuri::Tool::Glob`.
13
+ - Five file tools: `Pikuri::Workspace::Read`,
14
+ `Pikuri::Workspace::Write`, `Pikuri::Workspace::Edit`,
15
+ `Pikuri::Workspace::Grep`, `Pikuri::Workspace::Glob`.
14
16
 
15
- No shell execution — `Pikuri::Tool::Bash` ships in
17
+ No shell execution — `Pikuri::Code::Bash` ships in
16
18
  [`pikuri-code`](../pikuri-code/README.md) on top of these.
17
19
 
18
20
  ## Install
@@ -28,23 +30,36 @@ gem 'pikuri-workspace'
28
30
  require 'pikuri-core'
29
31
  require 'pikuri-workspace'
30
32
 
31
- workspace = Pikuri::Tool::Workspace::Cwd.new(root: Dir.pwd)
32
- confirmer = Pikuri::Tool::Confirmer::TERMINAL
33
+ workspace = Pikuri::Workspace::Filesystem.new(project_root: Dir.pwd)
34
+ confirmer = Pikuri::Workspace::Confirmer::TERMINAL
33
35
 
34
36
  agent = Pikuri::Agent.new(
35
37
  transport: ...,
36
38
  system_prompt: ...,
37
39
  ) do |c|
38
- c.add_tool Pikuri::Tool::Read.new(workspace: workspace)
39
- c.add_tool Pikuri::Tool::Grep.new(workspace: workspace)
40
- c.add_tool Pikuri::Tool::Glob.new(workspace: workspace)
41
- c.add_tool Pikuri::Tool::Edit.new(workspace: workspace)
42
- c.add_tool Pikuri::Tool::Write.new(workspace: workspace, confirmer: confirmer)
40
+ c.add_tool Pikuri::Workspace::Read.new(workspace: workspace)
41
+ c.add_tool Pikuri::Workspace::Grep.new(workspace: workspace)
42
+ c.add_tool Pikuri::Workspace::Glob.new(workspace: workspace)
43
+ c.add_tool Pikuri::Workspace::Edit.new(workspace: workspace)
44
+ c.add_tool Pikuri::Workspace::Write.new(workspace: workspace, confirmer: confirmer)
43
45
  c.add_listener ...
44
46
  end
45
47
  ```
46
48
 
47
- `Workspace::Cwd` is the "look-but-don't-leak" guard around filesystem
49
+ `Workspace` is the "look-but-don't-leak" guard around filesystem
48
50
  access. Read tools route through `#resolve_for_read(path)`; mutating
49
51
  tools route through `#resolve_for_write(path)` + the Confirmer's
50
- `#confirm?(prompt:)`.
52
+ `#confirm?(prompt:)`. Pass `temp: true` to mint an ephemeral
53
+ writable playground via `Dir.mktmpdir` — its path is exposed as
54
+ `workspace.temp` and auto-removed at process exit.
55
+
56
+ ## Further reading
57
+
58
+ - **Narrative walkthrough:** the "Workspace seam" and "Confirmer
59
+ seam" sections of
60
+ [chapter 8 of the pikuri guide](../docs/guide/08-code.md) — what
61
+ each kwarg of `Filesystem.new` controls, when to use the
62
+ `AllowAll` variant, and how the seams fit alongside `Sandbox`.
63
+ - **API reference:** browse the YARD docs at
64
+ <https://rubydoc.info/gems/pikuri-workspace> (once published),
65
+ or run `bundle exec yard` in this directory for a local copy.
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pikuri
4
- class Tool
4
+ module Workspace
5
5
  # Port for asking the user to confirm a potentially destructive tool
6
- # operation — currently {Tool::Bash} (every command) and
7
- # {Tool::Write} (overwrite of an existing file with non-identical
6
+ # operation — currently {Pikuri::Code::Bash} (every command) and
7
+ # {Write} (overwrite of an existing file with non-identical
8
8
  # content). Subclass and implement {#confirm?}.
9
9
  #
10
10
  # == Why a Boolean return
@@ -1,21 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pikuri
4
- class Tool
4
+ module Workspace
5
5
  # The +edit+ tool — exact-string replacement on an existing file.
6
- # Instantiating +Tool::Edit.new(workspace: ws)+ produces a tool whose
7
- # {Tool#to_ruby_llm_tool} wiring is identical to any bundled tool's.
8
- # Same shape as {Tool::Read} (workspace captured by +execute+; no
6
+ # Instantiating +Edit.new(workspace: ws)+ produces a tool whose
7
+ # {Pikuri::Tool#to_ruby_llm_tool} wiring is identical to any bundled
8
+ # tool's. Same shape as {Read} (workspace captured by +execute+; no
9
9
  # confirmer needed).
10
10
  #
11
11
  # == Why no confirmer
12
12
  #
13
13
  # The +old_string+ argument is itself an implicit read-check: the
14
14
  # model can't write a correct +old_string+ without having seen the
15
- # file (via {Tool::Read} or out-of-band), so the blast radius of any
15
+ # file (via {Read} or out-of-band), so the blast radius of any
16
16
  # Edit is bounded by the model's actual knowledge of file state.
17
17
  # That makes Edit safe to execute without prompting — by contrast,
18
- # {Tool::Write} requires a confirmer because a hallucinated 500-line
18
+ # {Write} requires a confirmer because a hallucinated 500-line
19
19
  # +content+ could clobber unread bytes.
20
20
  #
21
21
  # == Matching is strict (no fuzz cascade)
@@ -23,7 +23,7 @@ module Pikuri
23
23
  # +old_string+ must match the file byte-for-byte. v1 ships *no*
24
24
  # fallback replacer (no whitespace-normalized, line-trimmed, block-
25
25
  # anchor, etc.). Predictability beats fuzz: when an Edit fails, the
26
- # model re-reads with {Tool::Read} and retries — clear failure mode,
26
+ # model re-reads with {Read} and retries — clear failure mode,
27
27
  # no compounding-heuristic risk. opencode runs a 9-replacer cascade
28
28
  # under the hood despite its own description saying "must match
29
29
  # exactly"; pi stays strict. We match pi.
@@ -32,7 +32,7 @@ module Pikuri
32
32
  #
33
33
  # The one structural exception to "strict bytes": files with CRLF
34
34
  # line endings get matched in LF space, and the original line ending
35
- # is restored on write. Reason: {Tool::Read} renders content via
35
+ # is restored on write. Reason: {Read} renders content via
36
36
  # +each_line+ + +chomp+, which strips +\r\n+ to +\n+ in what the
37
37
  # model sees. A pure strict byte-match would then never succeed on
38
38
  # CRLF files because the model can only ever supply LF. opencode and
@@ -62,7 +62,7 @@ module Pikuri
62
62
  # multi-match error suggesting more context or +replace_all+.
63
63
  # * File missing / is a directory / is binary → respective error.
64
64
  # * Workspace boundary violation / EACCES → standard rescue path.
65
- class Edit < Tool
65
+ class Edit < Pikuri::Tool
66
66
  # Description shown to the LLM. Follows the opencode-shape (summary
67
67
  # + +Usage:+ bullets) prescribed by the project's tool-description
68
68
  # convention. Per-parameter constraints live in the parameter
@@ -82,7 +82,7 @@ module Pikuri
82
82
  - CRLF files are matched in LF space; the original line endings are preserved on write.
83
83
  DESC
84
84
 
85
- # @param workspace [Tool::Workspace] captured for path resolution;
85
+ # @param workspace [Filesystem] captured for path resolution;
86
86
  # all reads/writes route through +workspace.resolve_for_write+
87
87
  # (Edit modifies, so it uses the write-set even though it doesn't
88
88
  # create files).
@@ -122,7 +122,7 @@ module Pikuri
122
122
  # binary), match +old_string+ in line-ending-normalized form, and
123
123
  # write the result back preserving the file's original line endings.
124
124
  #
125
- # @param workspace [Tool::Workspace]
125
+ # @param workspace [Filesystem]
126
126
  # @param path [String] raw path as supplied by the LLM
127
127
  # @param old_string [String] text to find
128
128
  # @param new_string [String] text to substitute in
@@ -137,9 +137,9 @@ module Pikuri
137
137
  return "Error: file not found: #{path}" unless resolved.exist?
138
138
  return "Error: #{path} is a directory" if resolved.directory?
139
139
 
140
+ return "Error: cannot edit binary file: #{path}" if Pikuri::FileType.binary?(resolved)
141
+
140
142
  raw = resolved.binread
141
- sample = raw.byteslice(0, Tool::Read::BINARY_SAMPLE_BYTES)
142
- return "Error: cannot edit binary file: #{path}" if Tool::Read.binary?(sample)
143
143
 
144
144
  crlf = raw.include?("\r\n")
145
145
  content = crlf ? raw.gsub("\r\n", "\n") : raw
@@ -173,7 +173,7 @@ module Pikuri
173
173
  resolved.write(final)
174
174
 
175
175
  "Edited #{path}: replaced #{replaced} occurrence#{replaced == 1 ? '' : 's'}."
176
- rescue Tool::Workspace::Error => e
176
+ rescue Filesystem::Error => e
177
177
  "Error: #{e.message}"
178
178
  rescue Errno::EACCES => e
179
179
  "Error: cannot edit #{path}: #{e.message}"