Skip to content

feat(pkg): transitive dep resolution for ilo add (ILO-357)#678

Merged
danieljohnmorris merged 2 commits into
feature/ilo-addfrom
feature/ilo-add-transitive
May 22, 2026
Merged

feat(pkg): transitive dep resolution for ilo add (ILO-357)#678
danieljohnmorris merged 2 commits into
feature/ilo-addfrom
feature/ilo-add-transitive

Conversation

@danieljohnmorris
Copy link
Copy Markdown
Collaborator

Summary

  • After ilo add owner/repo clones a package, scan its top-level *.ilo files for use "owner/repo" declarations
  • Recursively fetch each package dependency and record all resolved SHAs in ilo.lock
  • Detect dependency cycles via a DFS ancestor stack; report cycle path and exit with code 1
  • Skip already-resolved packages via a visited set (idempotent, handles diamonds)

Implementation notes

  • cmd_add now delegates to add_recursive(spec, visited, stack) — public API is unchanged
  • collect_pkg_deps(pkg_dir) scans top-level *.ilo files and deduplicates slugs
  • extract_use_pkg_slugs(source) is a lightweight string scanner (no lexer/parser dep) that finds use "..." at word boundaries
  • Bases on feature/ilo-add; if ILO-356 (semver) has not landed, exact-version-only refs are used as-is

Test plan

  • cargo test --lib pkg — all 16 unit tests pass
  • cargo clippy --lib — no new warnings in pkg.rs
  • Manual: ilo add on a package with a use "other/pkg" declaration fetches the transitive dep and writes both entries to ilo.lock
  • Manual: circular use chain produces "dependency cycle detected" error

Closes ILO-357

🤖 Generated with Claude Code

@danieljohnmorris danieljohnmorris added the mini Created by mini PC autonomous workflow label May 22, 2026
@danieljohnmorris
Copy link
Copy Markdown
Collaborator Author

needs manual rebase (conflicts in: src/pkg.rs)

Daniel Morris added 2 commits May 22, 2026 10:15
After cloning a package into the cache, walk its top-level *.ilo files
for `use "owner/repo"` declarations and recursively fetch each package
dependency. All resolved versions are written to ilo.lock. Dependency
cycles are detected via a DFS ancestor stack and reported as errors.
Already-resolved packages are skipped via a visited set.

Closes ILO-357
@danieljohnmorris danieljohnmorris force-pushed the feature/ilo-add-transitive branch from 98fd0c9 to 58c576d Compare May 22, 2026 09:15
@danieljohnmorris danieljohnmorris merged commit cf336ea into feature/ilo-add May 22, 2026
@danieljohnmorris danieljohnmorris deleted the feature/ilo-add-transitive branch May 22, 2026 09:19
danieljohnmorris added a commit that referenced this pull request May 22, 2026
* feat(pkg): transitive dep resolution for ilo add

After cloning a package into the cache, walk its top-level *.ilo files
for `use "owner/repo"` declarations and recursively fetch each package
dependency. All resolved versions are written to ilo.lock. Dependency
cycles are detected via a DFS ancestor stack and reported as errors.
Already-resolved packages are skipped via a visited set.

Closes ILO-357

* chore: cargo fmt --all

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
danieljohnmorris added a commit that referenced this pull request May 22, 2026
* feat: package registry — ilo add GitHub-org-based (ILO-63)

Add `ilo add <owner>/<repo>[@<ref>]` and `ilo update` subcommands.
Packages are shallow-cloned into ~/.ilo/pkgs/<owner>/<repo>/ and
locked in ilo.lock (tab-separated slug/sha/url).

`use "owner/repo"` in any .ilo file resolves through the cache
after a path-heuristic check (first component has no `.`); missing
packages emit ILO-P017 with an `ilo add` hint.

Deferred: version constraints, transitive deps, auth, non-GitHub hosts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: bump SKILL.md cap to 20 KB

* ci: cargo fmt

* feat(pkg): semver version constraints for ilo add

Extend `ilo add owner/repo@<ref>` to accept semver constraints:
- `^MAJOR[.MINOR[.PATCH]]`  — caret (compatible) range
- `~MAJOR.MINOR[.PATCH]`    — tilde (patch-compatible) range
- `MAJOR.MINOR.PATCH`       — exact semver triple

Uses `git ls-remote --tags` to list remote tags without a full clone,
then picks the highest version tag matching the constraint via the
`semver` crate.  The resolved tag name is passed to the clone step so
`ilo.lock` records the concrete SHA.

Closes ILO-356.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(pkg): transitive dep resolution for ilo add (ILO-357) (#678)

* feat(pkg): transitive dep resolution for ilo add

After cloning a package into the cache, walk its top-level *.ilo files
for `use "owner/repo"` declarations and recursively fetch each package
dependency. All resolved versions are written to ilo.lock. Dependency
cycles are detected via a DFS ancestor stack and reported as errors.
Already-resolved packages are skipped via a visited set.

Closes ILO-357

* chore: cargo fmt --all

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
danieljohnmorris added a commit that referenced this pull request May 22, 2026
* feat: bounded generics for sound polymorphism (ILO-61)

Add explicit generic type parameters with bound constraints to ilo
function signatures. The verifier now enforces cross-call-site type
variable consistency and bound satisfaction, closing the soundness hole
where `fn id a>a` silently accepted inconsistent types at different call
sites.

MVP:
- Parser: `name<a:bound ...>` block after fn name; `<a>` unbounded
- Bounds: any (default), comparable (n/t/b), numeric (n), text (t)
- Verifier: unifies type variable bindings per call site (ILO-T044)
- Backward compatible: legacy `fn x:a>a;x` style unchanged
- New error codes: ILO-T044 (inconsistency/bound violation), ILO-P022
  (malformed type-param block)
- `examples/generics-bounded.ilo` passes on tree engine
- 7 new verify tests; all 3330+ existing tests green

Deferred: variance, higher-kinded types, multiple-bound conjunctions,
bound inference from usage, return-type generic substitution, VM/JIT
type-param-aware dispatch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: bring CI patches in line with main fleet (caps + budgets)

* verify: substitute generic return types at call sites (ILO-385) (#628)

When a function has explicit type_params (e.g. `gid<a> x:a>a`), the
verifier now infers the concrete return type at each call site by
substituting the type-variable bindings collected from the arguments.

Previously `gid 5` produced `Ty::Unknown`; it now correctly produces
`Ty::Number`, enabling downstream type-checking to catch mismatches
(ILO-T007) when the return value is passed to typed contexts.

- Add `original_return: ast::Type` to `FuncSig` to carry the AST
  return type alongside the already-stored `original_params`
- Add `collect_type_var_bindings` to extract bindings from compound
  param shapes (`L a`, `M t a`, `R a t`, etc.)
- Add `subst_return_ty` to recursively substitute type-variable letters
  in the return type using the collected bindings
- Hoist `var_bindings` out of the `if has_explicit_bounds` block so
  it is available for return-type substitution in all cases
- Add 4 regression tests covering: number identity, text identity,
  type mismatch detection, and list-element extraction

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* hotfix: fix examples_engines failures blocking all open PR CI (#723)

* fix(examples): rewrite mpairs.ilo iter fold to use brace-lambda syntax

Labelled-arg inline lambdas (`acc:L _; pair:L _;…`) inside `fld` now fail
with ILO-P003 since the parser treats `:` as a type-ascription delimiter.
Replace with brace-lambda `{acc pair > [acc, pair]}` — supported since
ILO-404 — and drop the `cat` call (comma-spread in list literal achieves
the same append without the `cat` arg-2 type error on `L (L _)`).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(builtins): add Sha256Hex and Sha256d to Builtin::ALL

The variants were defined and dispatched (tree-bridge, interpreter) but
missing from ALL, causing a panic (`Builtin::ALL must include every
variant`) whenever sha256-hex or sha256d was called via the VM.  Append
them immediately after CtEq — the existing crypto-primitives cluster —
preserving all prior on-wire tags.

Fixes examples/sha256-hex.ilo and examples/sha256d-bitcoin.ilo which
both panicked at src/builtins.rs:1198.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* hotfix: clippy errors on main blocking all PR lint jobs (#724)

Fixes 5 clippy errors:
- Remove unreachable HeapObj::LazyStdinLines arms in FOREACHPREP/FOREACHNEXT match blocks (vm/mod.rs)
- Add #[allow(clippy::should_implement_trait)] to UsePredicate::from_str (ast/mod.rs)
- Extract StdinLinesInner type alias to reduce complex type (interpreter/mod.rs)
- Add Default impl for StdinLinesHandle (interpreter/mod.rs)
- Convert match to matches! macro in BuildTarget::eval (main.rs)
- Suppress unused import warning in test (main.rs)

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: bump ilo-language cap to 1800 (measured 1746) (#725)

ILO-382 set the per-module caps to measured-baseline-plus-50 to gate
silent doc drift. ilo-language.md grew past 1700 (now 1746) as the
post-#722 brace-lambda + or-pattern sections landed, blocking every
PR's CI lint. Bump to 1800 with the same ~50-token headroom.

* fix(builtins): add Idxof to Builtin::ALL (cross-engine dispatch) (#726)

`Builtin::Idxof` was added to the enum but never appended to
`Builtin::ALL`, so calling `idxof` panicked with
'Builtin::ALL must include every variant' on every engine.
Appended last to preserve every existing on-wire tag.

Restores `tests/regression_idxof.rs::idxof_empty_haystack_not_found`
and `::idxof_both_empty_returns_zero` to passing.

* fix(verify): restrict `with` from adding new fields to anon records (ILO-368) (#661)

* fix(verify): restrict `with` from adding new fields to anon records (ILO-368)

Emit ILO-T044 when a `with` expression on an anonymous record references
a field that does not exist in the source record. Adds registry entry and
two coverage tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(verify): restrict `with` from adding new fields to anon records (ILO-368)

Anonymous record `with` updates must only touch existing fields.
Adding a new field via `with` would silently produce a different
record type. Verifier emits ILO-T044 with the missing field name.

Drive-by main-fixes folded into the same PR because every PR needs
them to lint-pass:
- src/diagnostic/registry.rs: ILO-T044 entry gains the required
  `phase: Phase::Verify` field (registry shape changed after the
  PR was opened)
- src/main.rs: collapse `BuildTarget::eval` match into a single
  `matches!` (clippy::match_like_matches_macro), drop an unused
  `std::io::Write` import
- src/ast/mod.rs: silence clippy::should_implement_trait on
  `UsePredicate::from_str` — implementing FromStr would change the
  return type from Option to Result
- src/interpreter/mod.rs: `#[allow]` on `StdinLinesHandle` for the
  intentional complex iterator type and `new()`-without-Default
  (StdinLinesHandle is a singleton, never Default-constructed)
- src/vm/mod.rs: drop unreachable `HeapObj::LazyStdinLines` arms in
  the two foreach catch-alls (the variant is handled earlier in the
  same match)

* hotfix: clippy errors on main blocking all PR lint jobs (#724)

Fixes 5 clippy errors:
- Remove unreachable HeapObj::LazyStdinLines arms in FOREACHPREP/FOREACHNEXT match blocks (vm/mod.rs)
- Add #[allow(clippy::should_implement_trait)] to UsePredicate::from_str (ast/mod.rs)
- Extract StdinLinesInner type alias to reduce complex type (interpreter/mod.rs)
- Add Default impl for StdinLinesHandle (interpreter/mod.rs)
- Convert match to matches! macro in BuildTarget::eval (main.rs)
- Suppress unused import warning in test (main.rs)

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: bump ilo-language cap to 1800 (measured 1746) (#725)

ILO-382 set the per-module caps to measured-baseline-plus-50 to gate
silent doc drift. ilo-language.md grew past 1700 (now 1746) as the
post-#722 brace-lambda + or-pattern sections landed, blocking every
PR's CI lint. Bump to 1800 with the same ~50-token headroom.

* fix(builtins): add Idxof to Builtin::ALL (cross-engine dispatch) (#726)

`Builtin::Idxof` was added to the enum but never appended to
`Builtin::ALL`, so calling `idxof` panicked with
'Builtin::ALL must include every variant' on every engine.
Appended last to preserve every existing on-wire tag.

Restores `tests/regression_idxof.rs::idxof_empty_haystack_not_found`
and `::idxof_both_empty_returns_zero` to passing.

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: native VM bytecode for defer/errdefer (ILO-366) (#662)

* feat: add defer and errdefer for guaranteed cleanup (ILO-56)

- Parser: defer/errdefer as statement-starts; both keywords reserved
- AST: Stmt::Defer { expr, kind: DeferKind::Always | OnError }
- Interpreter: per-frame defer stack, drained LIFO at function exit
- VM: program-wide bridge to tree-walker when any fn contains defer
- Codegen: fmt, explain, python all handle Stmt::Defer
- Verifier: type-checks deferred expression, yields Nil
- 18 regression tests; 2 examples (defer-basic.ilo, errdefer.ilo)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: bump BYTE_BUDGET_PER_MODULE to 8_000

* ci: cargo fmt

* feat: native VM bytecode for defer/errdefer (ILO-366)

Replaces the program-wide tree-bridge fallback for defer-containing
functions with two new opcodes compiled directly into the VM bytecode:

  OP_DEFER_PUSH (191) — snapshots a 0-arg closure onto the current
    frame's per-frame defer stack at defer-registration time.

  OP_DEFER_DRAIN (192) — drains the stack LIFO before every OP_RET,
    calling Always-kind thunks unconditionally and OnError-kind thunks
    only when the return value carries TAG_ERR.

Each `defer expr` / `errdefer expr` is compiled into a synthetic thunk
chunk that captures all in-scope locals by value (via OP_MAKE_CLOSURE),
then pushed with OP_DEFER_PUSH.  The compiler emits OP_DEFER_DRAIN
before every OP_RET in defer-containing functions via the new emit_ret()
helper, including early returns inside braceless guards and explicit
`ret` statements.

Performance: 47× faster than the tree-bridge (500-iteration timing test
added to regression_defer.rs shows ~156 ms tree vs ~3.3 ms VM in debug
builds).

All 1 167 existing tests pass.  19 regression_defer tests pass including
7 new VM-path tests covering LIFO ordering, errdefer fire/no-fire, and
early-return semantics.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: align with fleet patches

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(rd): remove extension-based auto-parse; add rd-json builtin (ILO-374) (#654)

* fix(rd): remove extension-based auto-parse; add rd-json builtin (ILO-374)

rd! on a .json path silently returned a parsed value instead of raw
text, breaking the documented t → R t t contract. This caused type
errors when callers applied jpar on the result of rd.

Fix: rd (1-arg) always returns R t t (raw text). Extension-based
auto-parse is removed from the interpreter, VM bytecode, and JIT paths.

Add rd-json: t → R ? t that reads and parses JSON explicitly. Migrate
config-shaper.ilo (rd → rd-json) and ecommerce-analytics.ilo
(rd → rd path "csv"). Add verifier hint (ILO-W001 warning) when rd
is called on a literal .json path without an explicit format arg.

Regression tests added in interpreter and VM test suites.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: align with fleet patches

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* Add hex-rev builtin for byte-pair reversal / endian conversion (ILO-372) (#653)

* Add hex-rev builtin for byte-pair reversal / endian conversion (ILO-372)

Implements `hex-rev s > t`: reverses a hex-encoded string byte-pair-wise
for little↔big endian conversions (Bitcoin txid display vs wire encoding).
Odd-length input errors ILO-T013. Case preserved. Tree-bridge eligible.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: align with fleet patches

* hotfix: fix examples_engines failures blocking all open PR CI (#723)

* fix(examples): rewrite mpairs.ilo iter fold to use brace-lambda syntax

Labelled-arg inline lambdas (`acc:L _; pair:L _;…`) inside `fld` now fail
with ILO-P003 since the parser treats `:` as a type-ascription delimiter.
Replace with brace-lambda `{acc pair > [acc, pair]}` — supported since
ILO-404 — and drop the `cat` call (comma-spread in list literal achieves
the same append without the `cat` arg-2 type error on `L (L _)`).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(builtins): add Sha256Hex and Sha256d to Builtin::ALL

The variants were defined and dispatched (tree-bridge, interpreter) but
missing from ALL, causing a panic (`Builtin::ALL must include every
variant`) whenever sha256-hex or sha256d was called via the VM.  Append
them immediately after CtEq — the existing crypto-primitives cluster —
preserving all prior on-wire tags.

Fixes examples/sha256-hex.ilo and examples/sha256d-bitcoin.ilo which
both panicked at src/builtins.rs:1198.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* hotfix: clippy errors on main blocking all PR lint jobs (#724)

Fixes 5 clippy errors:
- Remove unreachable HeapObj::LazyStdinLines arms in FOREACHPREP/FOREACHNEXT match blocks (vm/mod.rs)
- Add #[allow(clippy::should_implement_trait)] to UsePredicate::from_str (ast/mod.rs)
- Extract StdinLinesInner type alias to reduce complex type (interpreter/mod.rs)
- Add Default impl for StdinLinesHandle (interpreter/mod.rs)
- Convert match to matches! macro in BuildTarget::eval (main.rs)
- Suppress unused import warning in test (main.rs)

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: bump ilo-language cap to 1800 (measured 1746) (#725)

ILO-382 set the per-module caps to measured-baseline-plus-50 to gate
silent doc drift. ilo-language.md grew past 1700 (now 1746) as the
post-#722 brace-lambda + or-pattern sections landed, blocking every
PR's CI lint. Bump to 1800 with the same ~50-token headroom.

* fix(builtins): add Idxof to Builtin::ALL (cross-engine dispatch) (#726)

`Builtin::Idxof` was added to the enum but never appended to
`Builtin::ALL`, so calling `idxof` panicked with
'Builtin::ALL must include every variant' on every engine.
Appended last to preserve every existing on-wire tag.

Restores `tests/regression_idxof.rs::idxof_empty_haystack_not_found`
and `::idxof_both_empty_returns_zero` to passing.

* fix(verify): restrict `with` from adding new fields to anon records (ILO-368) (#661)

* fix(verify): restrict `with` from adding new fields to anon records (ILO-368)

Emit ILO-T044 when a `with` expression on an anonymous record references
a field that does not exist in the source record. Adds registry entry and
two coverage tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(verify): restrict `with` from adding new fields to anon records (ILO-368)

Anonymous record `with` updates must only touch existing fields.
Adding a new field via `with` would silently produce a different
record type. Verifier emits ILO-T044 with the missing field name.

Drive-by main-fixes folded into the same PR because every PR needs
them to lint-pass:
- src/diagnostic/registry.rs: ILO-T044 entry gains the required
  `phase: Phase::Verify` field (registry shape changed after the
  PR was opened)
- src/main.rs: collapse `BuildTarget::eval` match into a single
  `matches!` (clippy::match_like_matches_macro), drop an unused
  `std::io::Write` import
- src/ast/mod.rs: silence clippy::should_implement_trait on
  `UsePredicate::from_str` — implementing FromStr would change the
  return type from Option to Result
- src/interpreter/mod.rs: `#[allow]` on `StdinLinesHandle` for the
  intentional complex iterator type and `new()`-without-Default
  (StdinLinesHandle is a singleton, never Default-constructed)
- src/vm/mod.rs: drop unreachable `HeapObj::LazyStdinLines` arms in
  the two foreach catch-alls (the variant is handled earlier in the
  same match)

* hotfix: clippy errors on main blocking all PR lint jobs (#724)

Fixes 5 clippy errors:
- Remove unreachable HeapObj::LazyStdinLines arms in FOREACHPREP/FOREACHNEXT match blocks (vm/mod.rs)
- Add #[allow(clippy::should_implement_trait)] to UsePredicate::from_str (ast/mod.rs)
- Extract StdinLinesInner type alias to reduce complex type (interpreter/mod.rs)
- Add Default impl for StdinLinesHandle (interpreter/mod.rs)
- Convert match to matches! macro in BuildTarget::eval (main.rs)
- Suppress unused import warning in test (main.rs)

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: bump ilo-language cap to 1800 (measured 1746) (#725)

ILO-382 set the per-module caps to measured-baseline-plus-50 to gate
silent doc drift. ilo-language.md grew past 1700 (now 1746) as the
post-#722 brace-lambda + or-pattern sections landed, blocking every
PR's CI lint. Bump to 1800 with the same ~50-token headroom.

* fix(builtins): add Idxof to Builtin::ALL (cross-engine dispatch) (#726)

`Builtin::Idxof` was added to the enum but never appended to
`Builtin::ALL`, so calling `idxof` panicked with
'Builtin::ALL must include every variant' on every engine.
Appended last to preserve every existing on-wire tag.

Restores `tests/regression_idxof.rs::idxof_empty_haystack_not_found`
and `::idxof_both_empty_returns_zero` to passing.

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: fix clippy unnecessary to_string

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: package registry — ilo add GitHub-org-based (ILO-63) (#614)

* feat: package registry — ilo add GitHub-org-based (ILO-63)

Add `ilo add <owner>/<repo>[@<ref>]` and `ilo update` subcommands.
Packages are shallow-cloned into ~/.ilo/pkgs/<owner>/<repo>/ and
locked in ilo.lock (tab-separated slug/sha/url).

`use "owner/repo"` in any .ilo file resolves through the cache
after a path-heuristic check (first component has no `.`); missing
packages emit ILO-P017 with an `ilo add` hint.

Deferred: version constraints, transitive deps, auth, non-GitHub hosts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: bump SKILL.md cap to 20 KB

* ci: cargo fmt

* feat(pkg): semver version constraints for ilo add

Extend `ilo add owner/repo@<ref>` to accept semver constraints:
- `^MAJOR[.MINOR[.PATCH]]`  — caret (compatible) range
- `~MAJOR.MINOR[.PATCH]`    — tilde (patch-compatible) range
- `MAJOR.MINOR.PATCH`       — exact semver triple

Uses `git ls-remote --tags` to list remote tags without a full clone,
then picks the highest version tag matching the constraint via the
`semver` crate.  The resolved tag name is passed to the clone step so
`ilo.lock` records the concrete SHA.

Closes ILO-356.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(pkg): transitive dep resolution for ilo add (ILO-357) (#678)

* feat(pkg): transitive dep resolution for ilo add

After cloning a package into the cache, walk its top-level *.ilo files
for `use "owner/repo"` declarations and recursively fetch each package
dependency. All resolved versions are written to ilo.lock. Dependency
cycles are detected via a DFS ancestor stack and reported as errors.
Already-resolved packages are skipped via a visited set.

Closes ILO-357

* chore: cargo fmt --all

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

* hotfix: bump ilo-builtins-io token cap 2000→2200 (#729)

Co-authored-by: Daniel Morris <daniel@cubitts.com>

* hotfix: pkg.rs build break + resolve_imports arity (ILO-63 followup)

PR #614 (ilo add) merged with two compile errors:

1. src/pkg.rs:253 unwraps git_ref to &str, then line 260 tries to
   match it as Option. Remove the early unwrap; the later match at
   273-276 already handles the Option correctly.

2. src/main.rs:2465 calls resolve_imports with 4 args but it takes 5
   (the build_target arg was added by #643). Add the missing arg.

Restores main to a clean build.

* Add tokcount builtin and port check-skill-tokens to ilo (ILO-47) (#705)

Adds a `tokcount s > n` builtin (bytes/3.4 approximation of cl100k_base
token count) so skill-file budget checks can be written in ilo itself,
removing the last Python file from the build chain.

- New Builtin::Tokcount in builtins.rs, verify.rs, and vm.rs (tree-bridge
  eligible, appended to ALL to preserve on-wire tags)
- tokcount_impl in interpreter/mod.rs: ceil(bytes / 3.4)
- scripts/check-skill-tokens.ilo: ilo port of the deleted Python script,
  matching output format; caps set for the bytes/3.4 approximation
- scripts/check-skill-tokens.py: deleted
- .github/workflows/rust.yml: replace tiktoken/python step with
  `cargo run -- run scripts/check-skill-tokens.ilo`
- examples/tokcount-basic.ilo: cross-engine regression test
- SPEC.md / ai.txt / skills/ilo/ilo-builtins-text.md: doc touch-points

Deferred (ILO-47 follow-up): replace bytes/3.4 stub with tiktoken-rs BPE
once crate WASM and licence questions are resolved.

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Daniel Morris <daniel@cubitts.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mini Created by mini PC autonomous workflow

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant