Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
141 commits
Select commit Hold shift + click to select a range
fdc4176
chore: update devcontainer config and project tooling
gerchowl Feb 24, 2026
a11b618
chore: update devcontainer config and project tooling (#7)
gerchowl Feb 24, 2026
0da954d
chore(devc-remote): add auto-clone and init-workspace for remote hosts
gerchowl Feb 24, 2026
857dd4b
chore(devc-remote): add auto-clone and init-workspace for remote host…
gerchowl Feb 24, 2026
5295618
chore: resolve merge conflict in devc-remote.sh
gerchowl Feb 24, 2026
be1aee5
chore: Update project configuration and documentation
gerchowl Feb 24, 2026
16bab38
chore: merge dev into update-devcontainer-config
gerchowl Feb 24, 2026
b51fc53
chore: update devcontainer config and devc-remote script (#9)
gerchowl Feb 24, 2026
9de46e4
feat: add runtime dependencies and fd5 CLI entry point
gerchowl Feb 25, 2026
5d37114
feat: add runtime dependencies and fd5 CLI entry point (#25)
gerchowl Feb 25, 2026
128f95e
test(naming): add failing tests for generate_filename
gerchowl Feb 25, 2026
5384a3e
feat(naming): implement generate_filename utility
gerchowl Feb 25, 2026
1a15756
spike: add PoC for inline SHA-256 hashing during h5py chunked writes …
gerchowl Feb 25, 2026
db5784f
test(units): add tests for write_quantity, read_quantity, set_dataset…
gerchowl Feb 25, 2026
86bf35a
feat(units): implement write_quantity, read_quantity, set_dataset_units
gerchowl Feb 25, 2026
7dbddb6
test(h5io): add failing tests for dict_to_h5 and h5_to_dict
gerchowl Feb 25, 2026
a9f02c6
feat(h5io): implement dict_to_h5 and h5_to_dict metadata helpers
gerchowl Feb 25, 2026
7de7137
feat(naming): implement generate_filename utility (#28)
gerchowl Feb 25, 2026
8591c02
spike: validate h5py streaming chunk write + inline hashing (#29)
gerchowl Feb 25, 2026
aef83f8
feat(h5io): implement dict_to_h5 and h5_to_dict metadata helpers (#31)
gerchowl Feb 25, 2026
0a5e8ee
feat(units): implement physical units convention helpers (#33)
gerchowl Feb 25, 2026
7b93402
feat(registry): implement product schema registry with entry-point di…
gerchowl Feb 25, 2026
8930d7d
feat(registry): implement product schema registry (#35)
gerchowl Feb 25, 2026
00cd922
test(schema): add failing tests for embed_schema, validate, dump_sche…
gerchowl Feb 25, 2026
1b3e0a3
test(hash): add failing tests for fd5.hash module
gerchowl Feb 25, 2026
6ee50ed
feat(hash): implement Merkle tree hashing and content_hash computation
gerchowl Feb 25, 2026
1004624
test(provenance): add failing tests for write_sources, write_original…
gerchowl Feb 25, 2026
8535f36
feat(provenance): implement write_sources, write_original_files, writ…
gerchowl Feb 25, 2026
34cf649
test(manifest): add failing tests for build_manifest, write_manifest,…
gerchowl Feb 25, 2026
744b193
feat(manifest): implement build_manifest, write_manifest, read_manifest
gerchowl Feb 25, 2026
07d1d2d
feat(schema): implement embed_schema, validate, dump_schema, generate…
gerchowl Feb 25, 2026
bc46ee9
docs(changelog): add manifest module entry
gerchowl Feb 25, 2026
77a39a2
feat(provenance): implement provenance group writers (#37)
gerchowl Feb 25, 2026
81de194
feat(manifest): implement TOML manifest generation and parsing (#39)
gerchowl Feb 25, 2026
f993ca9
feat(hash): implement Merkle tree hashing and content_hash (#40)
gerchowl Feb 25, 2026
657f6d8
feat(schema): implement JSON Schema embedding and validation (#41)
gerchowl Feb 25, 2026
0871fa0
test(recon): add failing tests for ReconSchema product schema
gerchowl Feb 25, 2026
64f798f
test(create): add failing tests for fd5.create builder/context-manage…
gerchowl Feb 25, 2026
dec5fa6
feat(recon): implement ReconSchema product schema for fd5-imaging
gerchowl Feb 25, 2026
279e32a
feat(create): implement Fd5Builder context-manager API
gerchowl Feb 25, 2026
b857428
test(cli): add failing tests for validate, info, schema-dump, manifes…
gerchowl Feb 25, 2026
3bacfa4
feat(cli): implement validate, info, schema-dump, manifest commands
gerchowl Feb 25, 2026
6a50d52
feat(recon): implement recon product schema (#45)
gerchowl Feb 25, 2026
436dc0b
feat(create): implement fd5.create() builder/context-manager (#46)
gerchowl Feb 25, 2026
de465e5
feat(cli): implement validate, info, schema-dump, manifest commands (…
gerchowl Feb 25, 2026
d007048
chore: sync issues and PRs
commit-action-bot[bot] Feb 25, 2026
0e9bbd3
fix(ci): add missing lint tool dependencies to dev extras (#48)
gerchowl Feb 25, 2026
b0d4c15
test: add end-to-end integration test for fd5 workflow (#49)
gerchowl Feb 25, 2026
7350ac8
fix(ci): add missing lint tool dependencies (#50)
gerchowl Feb 25, 2026
077bea5
test: add end-to-end integration test (#62)
gerchowl Feb 25, 2026
42795aa
fix(ci): skip vig-utils hooks unavailable outside devcontainer
gerchowl Feb 25, 2026
955c5a1
fix(ci): skip vig-utils hooks unavailable outside devcontainer (#64)
gerchowl Feb 25, 2026
5efba19
docs: add project README and backfill CHANGELOG entries (#65)
gerchowl Feb 25, 2026
562b19e
docs: add project README and backfill CHANGELOG (#66)
gerchowl Feb 25, 2026
734077d
docs: add RFC-001, DES-001, and design template
gerchowl Feb 25, 2026
1623dcb
docs: add RFC-001, DES-001 with implementation tracking (#67)
gerchowl Feb 25, 2026
1fd2bcd
feat(imaging): add sinogram product schema for projection data
gerchowl Feb 25, 2026
5fd7839
feat(imaging): add listmode product schema (#51)
gerchowl Feb 25, 2026
65f7bb7
feat(imaging): add spectrum product schema for histogrammed/binned data
gerchowl Feb 25, 2026
76854a6
feat(imaging): add ROI product schema for regions of interest (#57)
gerchowl Feb 25, 2026
2c5cdcd
feat(imaging): add transform product schema for spatial registrations
gerchowl Feb 25, 2026
4771e40
feat(imaging): add device_data product schema (#58)
gerchowl Feb 25, 2026
deba03b
feat(imaging): add sim product schema for Monte Carlo simulation data
gerchowl Feb 25, 2026
87438ab
feat(imaging): add calibration product schema
gerchowl Feb 25, 2026
000e2e5
feat(imaging): add sinogram product schema (#68)
gerchowl Feb 25, 2026
7643e69
feat(imaging): add listmode product schema (#69)
gerchowl Feb 25, 2026
c217756
feat(imaging): add spectrum product schema (#70)
gerchowl Feb 25, 2026
7b9f3f6
feat(imaging): add roi product schema (#71)
gerchowl Feb 25, 2026
312c64b
feat(imaging): add sim product schema (#72)
gerchowl Feb 25, 2026
bebd449
feat(imaging): add device_data product schema (#73)
gerchowl Feb 25, 2026
a5660b1
feat(imaging): add calibration product schema (#74)
gerchowl Feb 25, 2026
5af8096
feat(imaging): add transform product schema (#75)
gerchowl Feb 25, 2026
966cf0a
chore: register Phase 3 imaging schemas as entry points
gerchowl Feb 25, 2026
033f551
chore: register Phase 3 imaging schemas as entry points (#76)
gerchowl Feb 25, 2026
fe463e4
feat(datacite): add fd5.datacite module for DataCite metadata export
gerchowl Feb 25, 2026
d4009e8
feat(datacite): add DataCite metadata export module and CLI command (…
gerchowl Feb 25, 2026
6046712
feat(rocrate): add RO-Crate 1.2 JSON-LD export module and CLI command
gerchowl Feb 25, 2026
b4a6270
feat(rocrate): add RO-Crate 1.2 JSON-LD export module and CLI command…
gerchowl Feb 25, 2026
56cdb45
docs: update RFC-001 tracking for Phases 3-4 completion
gerchowl Feb 25, 2026
335c9e1
chore: add pytest coverage config and close coverage gaps (#80)
gerchowl Feb 25, 2026
1d8726d
docs: update RFC-001 tracking for Phases 3-4 completion (#82)
gerchowl Feb 25, 2026
6cbbe15
chore: add pytest coverage config and close coverage gaps (#83)
gerchowl Feb 25, 2026
e5a078b
docs: update RFC-001 with Phase 5 issues, PR refs, and overall stats
gerchowl Feb 25, 2026
6d21be4
fix: address audit findings from issue #81
gerchowl Feb 25, 2026
7321296
docs: update RFC-001 with Phase 5 issues and overall stats (#93)
gerchowl Feb 25, 2026
0a4efd9
fix: address audit findings — pyyaml dep, py.typed, default attr, uni…
gerchowl Feb 25, 2026
f5886ca
feat: add _types.py shared types module and update source handling
gerchowl Feb 25, 2026
d148037
test(migrate): add failing tests for fd5.migrate module
gerchowl Feb 25, 2026
6413f69
feat(migrate): add fd5.migrate module with migration registry and cop…
gerchowl Feb 25, 2026
0c4b68c
test(cli): add failing tests for fd5 migrate CLI command
gerchowl Feb 25, 2026
cf00abe
feat(cli): add fd5 migrate command for schema version upgrades
gerchowl Feb 25, 2026
8cb0720
feat(migrate): export migrate from fd5 package __init__
gerchowl Feb 25, 2026
309545b
feat(imaging): add optional schema features per white-paper §recon/§l…
gerchowl Feb 25, 2026
def39d2
feat: integrate streaming chunk hashing into fd5.create() write path
gerchowl Feb 25, 2026
4b88ec9
feat: add _types.py shared types module and SourceRecord dataclass (#95)
gerchowl Feb 25, 2026
9dc2c09
feat: integrate streaming chunk hashing into fd5.create() write path …
gerchowl Feb 25, 2026
3e221a5
feat(imaging): add optional schema features per white-paper (#98)
gerchowl Feb 25, 2026
60b922d
feat(migrate): implement fd5.migrate module for schema version upgrad…
gerchowl Feb 25, 2026
92059d4
feat(datalad): add DataLad integration hooks and CLI command
gerchowl Feb 25, 2026
518effd
feat(datalad): add DataLad integration hooks and CLI command (#101)
gerchowl Feb 25, 2026
15eb701
feat(quality): add description quality validation heuristic
gerchowl Feb 25, 2026
2f6022a
feat(quality): add description quality validation heuristic (#103)
gerchowl Feb 25, 2026
e9e9631
feat(benchmarks): add performance benchmarks for fd5 core operations
gerchowl Feb 25, 2026
5a64487
feat(benchmarks): add performance benchmarks for fd5 core operations …
gerchowl Feb 25, 2026
8768fe4
docs: update RFC-001 — Phase 5 complete, 974 tests, 99% coverage
gerchowl Feb 25, 2026
df05a0e
docs: update RFC-001 — Phase 5 complete, 974 tests, 99% coverage (#105)
gerchowl Feb 25, 2026
b1cdedd
feat(tests): add testing and benchmarking commands to justfile
gerchowl Feb 25, 2026
2189dba
feat(tests): add testing and benchmarking commands to justfile (#107)
gerchowl Feb 25, 2026
3afa7f3
feat(registry): implement product schema registry with entry-point di…
gerchowl Feb 25, 2026
3cb65ab
feat(ingest): add Loader protocol and shared helpers (#109) (#120)
gerchowl Feb 25, 2026
41405fc
feat(registry): implement product schema registry with entry-point di…
gerchowl Feb 25, 2026
0d6c973
feat(ingest): add raw/numpy array loader (#112) (#125)
gerchowl Feb 25, 2026
1f4ec60
feat(registry): implement product schema registry with entry-point di…
gerchowl Feb 25, 2026
412ab25
feat(ingest): add CSV/TSV tabular data loader (#116) (#126)
gerchowl Feb 25, 2026
e8f99da
feat(registry): implement product schema registry with entry-point di…
gerchowl Feb 25, 2026
0764ce5
feat(ingest): add RO-Crate and DataCite metadata import (#119) (#127)
gerchowl Feb 25, 2026
c1413ac
feat(ingest): add ingest layer — base, raw, csv, nifti, metadata loaders
gerchowl Feb 25, 2026
d67e41a
feat(ingest): add ingest layer — base, raw, csv, nifti, metadata load…
gerchowl Feb 25, 2026
475e225
feat(ingest): add DICOM series loader and Parquet columnar data loader
gerchowl Feb 25, 2026
44af46f
feat(ingest): add DICOM and Parquet loaders (#110, #117) (#129)
gerchowl Feb 25, 2026
70debd3
feat(cli): add fd5 ingest CLI subcommand group
gerchowl Feb 25, 2026
143d668
feat(cli): add fd5 ingest CLI subcommand group (#113) (#130)
gerchowl Feb 25, 2026
b82e8fb
test(ingest): add idempotency tests for all loaders
gerchowl Feb 25, 2026
e60f5e9
test(ingest): add fd5.schema.validate() smoke tests for all loaders
gerchowl Feb 25, 2026
a08ee40
feat(cli): add fd5 ingest parquet CLI subcommand
gerchowl Feb 25, 2026
b83cf0b
test+feat: TDD gap fixes — idempotency, validate smoke, CLI parquet (…
gerchowl Feb 25, 2026
042fd8f
test: add failing tests for preflight feedback and status reporting
gerchowl Feb 26, 2026
d6348fc
feat(devc-remote): add per-check status lines and summary dashboard t…
gerchowl Feb 26, 2026
ab9b575
docs: add preflight feedback entry to changelog
gerchowl Feb 26, 2026
0a0a956
chore: wire up worktree recipes and fix solve-and-pr prompt path
gerchowl Feb 26, 2026
12908fb
chore: wire up worktree recipes and fix solve-and-pr prompt path (#152)
gerchowl Feb 26, 2026
c61e959
feat(devc-remote): improve preflight feedback and add missing checks …
gerchowl Feb 26, 2026
0521e3f
test: add failing tests for --yes flag, path annotation, container pr…
gerchowl Feb 26, 2026
0096477
feat(devc-remote): add --yes flag, path annotations, container prompt…
gerchowl Feb 26, 2026
7f636ac
docs: update changelog for preflight feedback improvements
gerchowl Feb 26, 2026
f8ef080
chore: sync issues and PRs
commit-action-bot[bot] Feb 26, 2026
818bdc9
feat(devc-remote): add --yes flag, container prompt, and SSH agent im…
gerchowl Feb 26, 2026
89ba415
feat(ingest): add NIfTI loader with Loader protocol and shared helpers
gerchowl Feb 28, 2026
28517ae
feat(fd5): add Rust fd5 crate, JSON schemas, and Cargo workspace
gerchowl Mar 2, 2026
eb5dd1c
test: RED phase -- add failing tests for audit trail, identity, and C…
gerchowl Mar 2, 2026
37112c4
feat: GREEN phase -- implement audit trail, identity, and CLI commands
gerchowl Mar 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .cursor/skills/pr_create/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Prepare and submit a pull request for **feature or bugfix work**.

- Run `git status` and `git fetch origin`. If the current branch has a remote tracking branch, run `git pull --rebase origin <current-branch>` (or `git pull` if the user prefers merge) so the branch is up to date with the remote.
- If there are uncommitted changes, list them and ask the user to commit or stash before submitting the PR. Do not prepare the PR until the working tree is clean (or the user explicitly says to proceed with uncommitted changes).
- **Merge the base branch:** Once the base branch is confirmed (step 2), run `git merge origin/<base_branch>` to integrate the latest base before creating the PR. **Conflict handling:** If merge conflicts occur, list the conflicting files and ask the user to resolve them manually before proceeding.

### 2. Verify target branch

Expand Down
1 change: 1 addition & 0 deletions .cursor/skills/pr_solve/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ Show the user a structured summary before any fixes:

### 5. Execute fixes

- **Merge the base branch** before the first push: run `git fetch origin` and `git merge origin/<base_branch>` (use `baseRefName` from step 1's PR metadata). **Conflict handling:** If merge conflicts occur, list the conflicting files and ask the user to resolve them before proceeding.
- Work through approved tasks one at a time.
- Follow [code_tdd](../code_tdd/SKILL.md) discipline where applicable (write test first, then fix).
- Commit each fix via [git_commit](../git_commit/SKILL.md).
Expand Down
2 changes: 1 addition & 1 deletion .cursor/skills/solve-and-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ This command:
- Sets up the environment (`uv sync`, `pre-commit install`)
- Captures the local gh user as the reviewer (`gh api user --jq '.login'`)
- Launches a tmux session running `cursor-agent` with `--yolo` mode
- Passes `/worktree-solve-and-pr` as the initial prompt
- Passes `/worktree_solve-and-pr` as the initial prompt

### 3. Report back to the user

Expand Down
31 changes: 19 additions & 12 deletions .cursor/skills/worktree_pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,7 @@ Create a pull request **without user interaction**. This is the worktree variant

## Workflow Steps

### 1. Ensure clean state

```bash
git status
git fetch origin
```

- If there are uncommitted changes, commit them first.
- Push the branch: `git push -u origin HEAD`

### 2. Determine base branch
### 1. Determine base branch

Detect whether this issue is a sub-issue and resolve the correct merge target:

Expand All @@ -50,6 +40,23 @@ Detect whether this issue is a sub-issue and resolve the correct merge target:

4. If no parent exists, use `dev` as `<base_branch>`.

### 2. Ensure clean state

```bash
git status
git fetch origin
```

- If there are uncommitted changes, commit them first.
- **Merge the base branch** before pushing:

```bash
git merge origin/<base_branch>
```

**Conflict handling:** If merge conflicts occur, list the conflicting files and invoke [worktree_ask](../worktree_ask/SKILL.md) to post a question on the issue asking for help resolving the conflict. Do not push until conflicts are resolved.
- Push the branch: `git push -u origin HEAD`

### 3. Gather context

```bash
Expand Down Expand Up @@ -116,7 +123,7 @@ The reviewer is the person who launched the worktree (their gh user login), not

The following steps SHOULD be delegated to reduce token consumption:

- **Steps 1-2** (precondition check, ensure clean state, determine base branch): Spawn a Task subagent with `model: "fast"` that validates the branch name, runs `git status`/`git fetch`, pushes the branch, checks for a parent issue via `gh api`, resolves the base branch. Returns: issue number, base branch name, clean state confirmation.
- **Steps 1-2** (precondition check, determine base branch, ensure clean state): Spawn a Task subagent with `model: "fast"` that validates the branch name, checks for a parent issue via `gh api`, resolves the base branch, runs `git status`/`git fetch`, merges `origin/<base_branch>`, and pushes. Returns: issue number, base branch name, clean state confirmation. On merge conflict, the subagent must invoke worktree_ask and return without pushing.
- **Step 3** (gather context): Spawn a Task subagent with `model: "fast"` that executes `git log`, `git diff`, `gh issue view` and returns the raw outputs. Returns: commit log, diff stat, issue title/body.
- **Steps 6-7** (create PR, clean up): Spawn a Task subagent with `model: "fast"` that takes the PR title and body file path, executes `gh pr create`, deletes the draft file, and returns the PR URL.

Expand Down
1 change: 1 addition & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"nefrob.vscode-just-syntax"
],
"settings": {
"terminal.integrated.defaultProfile.linux": "bash",
"python.defaultInterpreterPath": "/root/assets/workspace/.venv/bin/python",
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
Expand Down
14 changes: 7 additions & 7 deletions .devcontainer/justfile.base
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ clean-artifacts:
[group('info')]
check *args:
#!/usr/bin/env bash
SCRIPT_DIR="$(cd "$(dirname "{{justfile_directory()}}")/.devcontainer/scripts" && pwd)" || {
SCRIPT_DIR="$(cd "{{source_directory()}}/scripts" && pwd)" || {
echo "Error: Could not locate .devcontainer/scripts directory"
exit 1
}
Expand Down Expand Up @@ -373,10 +373,10 @@ sidecar name *args:
# -------------------------------------------------------------------------------

# Start a devcontainer on a remote host and open Cursor/VS Code
# Usage: just devc-remote <ssh-host>[:<remote-path>]
# Example: just devc-remote myserver
# just devc-remote user@host:/opt/projects/myrepo
# just devc-remote myserver:/home/user/repo
# Auto-clones the repo and runs init-workspace if needed
# Usage: just devc-remote myserver
# just devc-remote myserver:/home/user/repo
# just devc-remote --repo git@github.com:org/repo.git myserver
[group('devcontainer')]
devc-remote host_path:
bash scripts/devc-remote.sh {{host_path}}
devc-remote *args:
bash scripts/devc-remote.sh {{args}}
2 changes: 1 addition & 1 deletion .devcontainer/justfile.gh
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ _gh_scripts := source_directory() / "scripts"
# List open issues and PRs grouped by milestone
[group('github')]
gh-issues:
python3 {{ _gh_scripts }}/gh_issues.py
uv run python {{ _gh_scripts }}/gh_issues.py
39 changes: 28 additions & 11 deletions .devcontainer/justfile.worktree
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
_wt_repo := `basename "$(git rev-parse --show-toplevel)"`
_wt_base := "../" + _wt_repo + "-worktrees"

# Scripts path: resolves to scripts/ in devcontainer repo, .devcontainer/scripts/ in workspace
_scripts := source_directory() / "scripts"

# -------------------------------------------------------------------------------
# START
# -------------------------------------------------------------------------------
Expand Down Expand Up @@ -114,10 +117,11 @@ worktree-start issue prompt="" reviewer="":
echo " tmux session '$SESSION' is running. Use: just worktree-attach $ISSUE"
else
echo " No tmux session found. Starting one..."
AGENT_MODEL=$(_read_model "autonomous")
if [ -n "$PROMPT" ]; then
tmux new-session -d -s "$SESSION" -c "$WT_DIR" -e "PR_REVIEWER=$REVIEWER" "agent chat --yolo --approve-mcps \"$PROMPT\""
tmux new-session -d -s "$SESSION" -c "$WT_DIR" -e "PR_REVIEWER=$REVIEWER" "agent chat --model $AGENT_MODEL --yolo --approve-mcps \"$PROMPT\""
else
tmux new-session -d -s "$SESSION" -c "$WT_DIR" -e "PR_REVIEWER=$REVIEWER" "agent chat --approve-mcps"
tmux new-session -d -s "$SESSION" -c "$WT_DIR" -e "PR_REVIEWER=$REVIEWER" "agent chat --model $AGENT_MODEL --approve-mcps"
fi
sleep 2 && tmux send-keys -t "$SESSION" "a" 2>/dev/null || true
echo "[OK] tmux session '$SESSION' started. Use: just worktree-attach $ISSUE"
Expand All @@ -126,7 +130,7 @@ worktree-start issue prompt="" reviewer="":
fi

# Resolve the issue's linked branch (may already exist from issue:claim)
BRANCH=$(gh issue develop --list "$ISSUE" 2>/dev/null | "$(pwd)/scripts/resolve-branch.sh")
BRANCH=$(gh issue develop --list "$ISSUE" 2>/dev/null | "{{ _scripts }}/resolve-branch.sh")

if [ -z "$BRANCH" ]; then
echo "[*] No linked branch for issue #${ISSUE}. Creating one..."
Expand All @@ -144,11 +148,16 @@ worktree-start issue prompt="" reviewer="":
fi

# Use agent ONLY for the intelligent part: deriving the short summary
# Try lightweight first; on failure retry with standard model (#183)
NAMING_RULE="$(pwd)/.cursor/rules/branch-naming.mdc"
SUMMARY=$("$(pwd)/scripts/derive-branch-summary.sh" "$TITLE" "$NAMING_RULE" 2>/dev/null) || true
SUMMARY=$("{{ _scripts }}/derive-branch-summary.sh" "$TITLE" "$NAMING_RULE" "lightweight") || true

if [ -z "$SUMMARY" ]; then
echo "[!] Lightweight model failed. Retrying with standard model..."
SUMMARY=$("{{ _scripts }}/derive-branch-summary.sh" "$TITLE" "$NAMING_RULE" "standard") || true
fi

if [ -z "$SUMMARY" ]; then
"$(pwd)/scripts/derive-branch-summary.sh" "$TITLE" "$NAMING_RULE" >/dev/null || true
echo " Create one manually: gh issue develop ${ISSUE} --base dev --name ${TYPE}/${ISSUE}-<summary>"
exit 1
fi
Expand All @@ -157,7 +166,7 @@ worktree-start issue prompt="" reviewer="":
OWNER_REPO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
PARENT=$(gh api "repos/${OWNER_REPO}/issues/${ISSUE}/parent" --jq '.number' 2>/dev/null || true)
if [ -n "$PARENT" ]; then
BASE=$(gh issue develop --list "$PARENT" 2>/dev/null | "$(pwd)/scripts/resolve-branch.sh")
BASE=$(gh issue develop --list "$PARENT" 2>/dev/null | "{{ _scripts }}/resolve-branch.sh")
BASE="${BASE:-dev}"
else
BASE="dev"
Expand Down Expand Up @@ -213,10 +222,11 @@ worktree-start issue prompt="" reviewer="":

# Start tmux session
# --yolo: auto-approve all shell commands (autonomous agent, no human at the terminal)
AGENT_MODEL=$(_read_model "autonomous")
if [ -n "$PROMPT" ]; then
tmux new-session -d -s "$SESSION" -c "$WT_DIR" -e "PR_REVIEWER=$REVIEWER" "agent chat --yolo --approve-mcps \"$PROMPT\""
tmux new-session -d -s "$SESSION" -c "$WT_DIR" -e "PR_REVIEWER=$REVIEWER" "agent chat --model $AGENT_MODEL --yolo --approve-mcps \"$PROMPT\""
else
tmux new-session -d -s "$SESSION" -c "$WT_DIR" -e "PR_REVIEWER=$REVIEWER" "agent chat --approve-mcps"
tmux new-session -d -s "$SESSION" -c "$WT_DIR" -e "PR_REVIEWER=$REVIEWER" "agent chat --model $AGENT_MODEL --approve-mcps"
fi
sleep 2 && tmux send-keys -t "$SESSION" "a" 2>/dev/null || true

Expand Down Expand Up @@ -299,6 +309,12 @@ worktree-attach issue:
fi
}

_read_model() {
local tier="$1"
local cfg="$(git rev-parse --show-toplevel)/.cursor/agent-models.toml"
grep "^${tier}" "$cfg" | sed 's/.*= *"//' | sed 's/".*//'
}

ISSUE="{{ issue }}"
SESSION="wt-${ISSUE}"
WT_DIR="{{ _wt_base }}/${ISSUE}"
Expand All @@ -307,11 +323,12 @@ worktree-attach issue:
if [ -d "$WT_DIR" ]; then
echo "[!] tmux session '$SESSION' stopped. Restarting..."
_wt_ensure_trust "$WT_DIR"
REVIEWER=$(gh api user --jq '.login' 2>/dev/null || echo "")
if [ -n "${WORKTREE_ATTACH_RESTART_CMD:-}" ]; then
tmux new-session -d -s "$SESSION" -c "$WT_DIR" -e "PR_REVIEWER=$REVIEWER" "$WORKTREE_ATTACH_RESTART_CMD"
tmux new-session -d -s "$SESSION" -c "$WT_DIR" "$WORKTREE_ATTACH_RESTART_CMD"
else
tmux new-session -d -s "$SESSION" -c "$WT_DIR" -e "PR_REVIEWER=$REVIEWER" "agent chat --approve-mcps"
REVIEWER=$(gh api user --jq '.login' 2>/dev/null || echo "")
AGENT_MODEL=$(_read_model "autonomous")
tmux new-session -d -s "$SESSION" -c "$WT_DIR" -e "PR_REVIEWER=$REVIEWER" "agent chat --model $AGENT_MODEL --approve-mcps"
fi
sleep 2 && tmux send-keys -t "$SESSION" "a" 2>/dev/null || true
echo "[OK] tmux session '$SESSION' restarted"
Expand Down
35 changes: 35 additions & 0 deletions .devcontainer/scripts/check-skill-names.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/usr/bin/env bash
# Check that all skill directory names under a given path use only
# lowercase letters, digits, hyphens, and underscores.
#
# Usage: check-skill-names.sh [skills_dir]
# skills_dir Path to scan (default: .cursor/skills)
#
# Exit 0 if all names are valid, 1 if any are invalid.

set -euo pipefail

skills_dir="${1:-.cursor/skills}"

if [[ ! -d "$skills_dir" ]]; then
echo "Error: directory not found: $skills_dir" >&2
exit 1
fi

invalid=()

for dir in "$skills_dir"/*/; do
[[ -d "$dir" ]] || continue
name="$(basename "$dir")"
if [[ ! "$name" =~ ^[a-z0-9][a-z0-9_-]*$ ]]; then
invalid+=("$name")
fi
done

if [[ ${#invalid[@]} -gt 0 ]]; then
echo "Invalid skill directory name(s) — must match [a-z0-9][a-z0-9_-]*:" >&2
for name in "${invalid[@]}"; do
echo " $name" >&2
done
exit 1
fi
41 changes: 41 additions & 0 deletions .devcontainer/scripts/derive-branch-summary.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
# Derive a kebab-case branch summary from an issue title.
# Used by worktree-start when no linked branch exists.
#
# Usage: derive-branch-summary.sh <TITLE> [NAMING_RULE] [MODEL_TIER]
# TITLE: issue title
# NAMING_RULE: path to branch-naming.mdc (default: .cursor/rules/branch-naming.mdc)
# MODEL_TIER: agent-models.toml tier (default: lightweight). Use standard for retry.
#
# Env: BRANCH_SUMMARY_CMD — override for tests (e.g. "echo test-summary")
# When set, runs instead of agent. Must output summary to stdout.
# BRANCH_SUMMARY_MODEL — override model tier (e.g. "standard"). Ignored if BRANCH_SUMMARY_CMD set.
# DERIVE_BRANCH_TIMEOUT — timeout in seconds (default: 30). Use 2 for tests.
set -euo pipefail

TITLE="${1:?Usage: derive-branch-summary.sh <TITLE> [NAMING_RULE] [MODEL_TIER]}"
REPO_ROOT="$(git rev-parse --show-toplevel)"
NAMING_RULE="${2:-${REPO_ROOT}/.cursor/rules/branch-naming.mdc}"
MODEL_TIER="${3:-${BRANCH_SUMMARY_MODEL:-lightweight}}"
TIMEOUT="${DERIVE_BRANCH_TIMEOUT:-30}"

if [ -n "${BRANCH_SUMMARY_CMD:-}" ]; then
SUMMARY=$(timeout "$TIMEOUT" sh -c "$BRANCH_SUMMARY_CMD" 2>/dev/null | tail -1 | tr -d '[:space:]') || true
else
MODEL=$(grep "^${MODEL_TIER}" "${REPO_ROOT}/.cursor/agent-models.toml" | sed 's/.*= *"//' | sed 's/".*//')
SUMMARY=$(timeout "$TIMEOUT" agent --print --yolo --trust --model "$MODEL" \
"Read the branch naming rules in ${NAMING_RULE}. " \
"The issue title is: ${TITLE} " \
"Output ONLY a kebab-case short summary suitable for a branch name (a few words). " \
"Omit prefixes like FEATURE, BUG, Add, Implement, Support. " \
"Example: 'Standardize and Enforce Commit Message Format' -> 'standardize-commit-messages'. " \
"No explanation. No quotes. Just the summary." 2>/dev/null | tail -1 | tr -d '[:space:]') || true
fi

if [ -z "$SUMMARY" ]; then
echo "[ERROR] Failed to derive branch summary from title: ${TITLE}" >&2
echo " Create one manually: gh issue develop <ISSUE> --base dev --name <type>/<issue>-<summary>" >&2
exit 1
fi

echo "$SUMMARY"
27 changes: 25 additions & 2 deletions .devcontainer/scripts/gh_issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -374,13 +374,34 @@ def _infer_review(pr: dict) -> tuple[str, str]:
return ("", "—")


def _dedupe_status_checks(rollup: list[dict]) -> list[dict]:
"""Deduplicate statusCheckRollup by check name, keeping latest by completedAt.

GitHub includes re-runs of the same check; we keep only the latest result
per check name so the CI column matches what GitHub shows on the PR page.
Ref: #176
"""
by_name: dict[str, dict] = {}
for check in rollup:
name = check.get("name") or "?"
completed = check.get("completedAt") or ""
existing = by_name.get(name)
if existing is None:
by_name[name] = check
else:
existing_completed = existing.get("completedAt") or ""
if completed >= existing_completed:
by_name[name] = check
return list(by_name.values())


def _format_ci_status(pr: dict, owner_repo: str) -> str:
"""Return Rich markup for CI status cell: pass/fail/pending summary with link.

Uses statusCheckRollup from gh pr list. Links to PR checks tab.
Ref: #143
"""
rollup = pr.get("statusCheckRollup") or []
rollup = _dedupe_status_checks(pr.get("statusCheckRollup") or [])
if not rollup:
return _styled("—", "dim")

Expand Down Expand Up @@ -485,7 +506,9 @@ def _build_pr_table(

linked = pr_to_issues.get(pr["number"], [])
issues_cell = (
" ".join(_styled(f"#{n}", "cyan") for n in sorted(linked)) if linked else ""
" ".join(_gh_link(owner_repo, n, "issues") for n in sorted(linked))
if linked
else ""
)

ci_cell = _format_ci_status(pr, owner_repo)
Expand Down
5 changes: 5 additions & 0 deletions .devcontainer/scripts/resolve-branch.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
# Extract the branch name from `gh issue develop --list` output.
# Input: tab-separated lines on stdin (branch<TAB>URL)
# Output: first branch name (first field of first line)
head -1 | cut -f1
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ jobs:
sync-dependencies: 'true'

- name: Run pre-commit hooks
env:
SKIP: check-action-pins,validate-commit-msg
run: uv run pre-commit run --all-files --show-diff-on-failure

test:
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ repos:
hooks:
- id: check-skill-names
name: check-skill-names (enforce naming convention)
entry: scripts/check-skill-names.sh .cursor/skills
entry: .devcontainer/scripts/check-skill-names.sh .cursor/skills
language: script
files: ^\.cursor/skills/
pass_filenames: false
Expand Down
Loading
Loading