lyman 0.1.0 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42c4c9146ed53714fa0725775e91ffcbc599e485d652e84d483dedca2f47e047
4
- data.tar.gz: e17d5dde1e338e8bae43cb97f5633b0cd4e24e06d66535bafdd2ff74319c9764
3
+ metadata.gz: c688da4f3dbae3ea1eae1ddadbbcb9c8024ca15d6b396d6a2b8aa85dd77bb91c
4
+ data.tar.gz: 31ead51a810ff07e2ef56943f36cc379ef8df8cbb7dcdfc611ad92c2d098c61e
5
5
  SHA512:
6
- metadata.gz: 48c20fb9bb06c5f685b47491cc3d96cf33e7835b7b44bc1723bd87feec82a75d334b923a38c5d5838595bba31697e958e150dad4a5305e113edb454bcfd53e17
7
- data.tar.gz: 26e9984e59ddc9722cb50bac1a3567cf9c424367c8dbed5e48bbf6b47fe3051052c59cd9e24a83fd5c7b49a81f056b2b256de89dda000c92798d536e072a8d5f
6
+ metadata.gz: f5c814ed46a0ff60e7058850901a13a4b5367a87afe935efcdc86c881619580a656e5387e9cedd47c9e7ea47caf958ffb2ddb73b6a4697563f6dac21995a4a05
7
+ data.tar.gz: a6b3450171f5db5c04eb8ce3bb5e3fb742abf692d45f30c1dcb1077afda75570e73b2e0a054fbeadcbca92d1876a5b17ed51cf8f9b07c6c444e1b86d9c6fddfe
data/README.md CHANGED
@@ -152,9 +152,15 @@ Early and moving. What exists today: the vision, circuit-pattern, and
152
152
  deployment design docs, the core `Conversation` item, chat-completion and
153
153
  tool-execution workers, a working tool-using chat harness against live local
154
154
  models, and the generator CLI (`new` / `add` / `update` / `eject` / `diff` /
155
- `doctor` / `list`) with a Minitest suite behind it. Not yet published to
156
- rubygems.org — for now, run it from a checkout. On deck: publishing the gem,
157
- tool-call fan-out, and a one-shot (non-REPL) harness.
155
+ `doctor` / `list`) with a Minitest suite behind it. Published to
156
+ [rubygems.org](https://rubygems.org/gems/lyman)install it with:
157
+
158
+ ```sh
159
+ gem install lyman
160
+ lyman new my-agent
161
+ ```
162
+
163
+ On deck: tool-call fan-out and a one-shot (non-REPL) harness.
158
164
 
159
165
  ## License
160
166
 
@@ -150,7 +150,12 @@ Three channels, ordered by reach:
150
150
  `AGENTS.md`) as a first-class artifact carrying the load-bearing facts: the
151
151
  nil-source footgun, the runaway-turn guard, item-as-control discipline,
152
152
  wire-vs-conversation separation. Highest-value channel: works with any
153
- coding agent, zero install steps, travels with the code.
153
+ coding agent, zero install steps, travels with the code. For projects that
154
+ already have a `CLAUDE.md` lyman shouldn't clobber, the same guidance
155
+ ships as an opt-in Claude Code skill (`lyman add claude_skill` plants
156
+ `.claude/skills/lyman/SKILL.md`); `lyman add claude_md` points there when
157
+ it refuses to overwrite an existing file. Opt-in artifacts (`optional:` in
158
+ the registry) are skipped by `new` and reached with `add`.
154
159
  2. **A Claude plugin marketplace** in the `joelhelbling/lyman` repo, for
155
160
  richer *procedures* — e.g. a skill that knows how to wire a new tool worker
156
161
  correctly, or how to read a stalled pipeline. Skills earn their keep when
@@ -31,8 +31,13 @@ module Lyman
31
31
  end
32
32
  else
33
33
  if File.exist?(dest) && !force
34
- raise Thor::Error, "#{dest} already exists and isn't tracked by lyman. " \
34
+ message = "#{dest} already exists and isn't tracked by lyman. " \
35
35
  "Move it aside, or run `lyman add #{name} --force` to overwrite it."
36
+ if (alt = spec[:alternative])
37
+ message += " Or plant `lyman add #{alt}` instead, " \
38
+ "which leaves #{spec[:dest]} untouched."
39
+ end
40
+ raise Thor::Error, message
36
41
  end
37
42
  end
38
43
 
@@ -19,7 +19,7 @@ module Lyman
19
19
  FileUtils.mkdir_p(project_root)
20
20
 
21
21
  manifest = Manifest.load(project_root)
22
- Registry::ARTIFACTS.each do |artifact_name, spec|
22
+ Registry.default.each do |artifact_name, spec|
23
23
  plant(manifest, artifact_name, spec, project_root)
24
24
  end
25
25
  manifest.save
@@ -42,8 +42,21 @@ module Lyman
42
42
  source: "templates/CLAUDE.md",
43
43
  dest: "CLAUDE.md",
44
44
  role: :owned,
45
+ alternative: "claude_skill",
45
46
  description: "Guidance for coding agents working in this project"
46
47
  },
48
+ # The same guidance as claude_md, packaged as a Claude Code skill —
49
+ # for projects that already have a CLAUDE.md lyman shouldn't clobber.
50
+ # `optional:` keeps `new` from planting it (a fresh scaffold gets
51
+ # claude_md instead); `alternative:` on claude_md points here when
52
+ # `add` refuses to overwrite an existing CLAUDE.md.
53
+ "claude_skill" => {
54
+ source: "templates/SKILL.md",
55
+ dest: ".claude/skills/lyman/SKILL.md",
56
+ role: :owned,
57
+ optional: true,
58
+ description: "Claude Code skill variant of the CLAUDE.md guidance — for projects with their own CLAUDE.md"
59
+ },
47
60
  "gemfile" => {
48
61
  source: "templates/Gemfile",
49
62
  dest: "Gemfile",
@@ -90,6 +103,13 @@ module Lyman
90
103
  ARTIFACTS.select { |_, spec| spec[:role] == :managed }
91
104
  end
92
105
 
106
+ # What `new` plants: everything except opt-in artifacts (those exist
107
+ # for situations a fresh scaffold can't be in, like a pre-existing
108
+ # CLAUDE.md — reach them with `lyman add`).
109
+ def self.default
110
+ ARTIFACTS.reject { |_, spec| spec[:optional] }
111
+ end
112
+
93
113
  def self.source_path(spec, source_root: GEM_ROOT)
94
114
  File.join(source_root, spec[:source])
95
115
  end
@@ -2,6 +2,6 @@ module Lyman
2
2
  module CLI
3
3
  # The gem's own version — distinct from any artifact's `planted_at`.
4
4
  # This is what the manifest's top-level `lyman:` key records.
5
- VERSION = "0.1.0"
5
+ VERSION = "0.2.0"
6
6
  end
7
7
  end
data/lib/lyman/cli.rb CHANGED
@@ -80,7 +80,8 @@ module Lyman
80
80
  entry ? entry["status"] : "not planted"
81
81
  end
82
82
 
83
- line = "#{name} (#{spec[:role]}) #{spec[:dest]}#{" — #{status}" if status} — #{spec[:description]}"
83
+ role = "#{spec[:role]}#{", opt-in" if spec[:optional]}"
84
+ line = "#{name} (#{role}) #{spec[:dest]}#{" — #{status}" if status} — #{spec[:description]}"
84
85
  say line
85
86
  end
86
87
  end
@@ -0,0 +1,62 @@
1
+ ---
2
+ name: lyman
3
+ description: Guidance for working in a lyman-scaffolded agentic harness — the pipeline-of-workers model, the managed/owned boundary, and lyman's sharp edges. Use when reading or changing the harness (harness/chat.rb), anything under lib/lyman/, or when running lyman CLI commands (add, update, eject, diff, doctor).
4
+ ---
5
+
6
+ # Working in a lyman project
7
+
8
+ This project was scaffolded by [lyman](https://github.com/joelhelbling/lyman):
9
+ an agentic harness built on the [shifty](https://github.com/joelhelbling/shifty)
10
+ gem's pipeline-of-workers model. `harness/chat.rb` wires a model⇄tool loop
11
+ against any OpenAI-compatible chat completions endpoint (Ollama by default).
12
+
13
+ ## Running it
14
+
15
+ - `bundle install` — install dependencies (just `shifty` and `ostruct`; this
16
+ project has no runtime dependency on the `lyman` gem itself).
17
+ - `ruby harness/chat.rb` — run the interactive chat harness. Defaults to
18
+ Ollama at `http://localhost:11434/v1`; override with `LYMAN_MODEL` and
19
+ `LYMAN_BASE_URL` env vars.
20
+
21
+ ## The managed/owned boundary
22
+
23
+ Everything under the `Lyman::` namespace (`lib/lyman.rb`, `lib/lyman/`) is
24
+ **managed** by the lyman CLI: it was planted here, and `lyman update` can
25
+ refresh it from newer lyman releases. Treat it as a library to extend, not a
26
+ file to hand-edit — if you need to change one, run `lyman eject <name>` first,
27
+ which takes ownership explicitly rather than leaving it silently forked.
28
+ `.lyman/manifest.yml` is the record of what's managed, what's owned, and what
29
+ you've ejected; commit it.
30
+
31
+ `harness/chat.rb` and everything outside `Lyman::` is **owned** — yours from
32
+ day one, never touched by `lyman update`. Put your own workers in your own
33
+ namespace or directory, not inside `lib/lyman/`.
34
+
35
+ ## Four load-bearing facts
36
+
37
+ 1. **The nil-source footgun.** In shifty, a source returning `nil` ends the
38
+ stream permanently. If you're driving a pipeline off a queue, enqueue
39
+ before you shift — never pull a source worker while its queue is empty.
40
+
41
+ 2. **The runaway-turn guard.** The model⇄tool circuit is bounded by
42
+ `Conversation#runaway?` / `max_rounds`. Keep that guard intact when
43
+ rewiring the circuit; without it, a model that keeps calling tools never
44
+ lets a turn end.
45
+
46
+ 3. **Item-as-control discipline.** An item may tell a worker *whether* to
47
+ act, never *which of several things* to do. A worker that switches
48
+ between jobs based on item state is the anti-pattern to avoid
49
+ (multi-way dispatch) — split it into stages, or use a splitter, instead.
50
+
51
+ 4. **Wire vs. conversation.** Reasoning/thinking content stays on messages in
52
+ the `Conversation` for observability, but `Workers.wire_messages` strips
53
+ it before anything goes out over the wire. Preserve that separation if
54
+ you touch message handling.
55
+
56
+ ## Other conventions
57
+
58
+ - `lyman doctor` smoke-tests the pipeline end to end against a stub
59
+ transport — run it after `lyman update` or whenever something feels off.
60
+ - `lyman list` shows what's managed, owned, or ejected in this project.
61
+ - Messages use string keys throughout, matching the OpenAI-compatible wire
62
+ format — no symbol-key message hashes.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lyman
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joel Helbling
@@ -83,6 +83,7 @@ files:
83
83
  - lib/lyman/workers/tool_execution.rb
84
84
  - templates/CLAUDE.md
85
85
  - templates/Gemfile
86
+ - templates/SKILL.md
86
87
  - templates/gitignore
87
88
  - templates/lib_lyman.rb
88
89
  homepage: https://github.com/joelhelbling/lyman