commits
Add automatic detection and merging of stacked pull requests with
comprehensive conflict checking and user confirmation. When merging
a PR that's part of a stack, the CLI now:
- Auto-detects stack membership via stack_id field
- Displays preview of all PRs to be merged (current + below)
- Checks cumulative patches for conflicts before merging
- Prompts user for confirmation before executing merge
- Provides clear error messages for conflicts
API changes:
- Add stack fields to Pull: stack_id, change_id, parent_change_id
- Add MergeCheckRequest/Response types for conflict detection
- Add merge_check() method to TangledClient
CLI changes:
- Refactor merge() to detect and handle stacked PRs
- Add helper functions for stack ordering and conflict checking
- Maintain backward compatibility for non-stacked PRs
Support reading secret values from stdin or files to handle
multiline content like SSH keys, certificates, and config files.
New value patterns:
- `--value -` reads from stdin
- `--value @<path>` reads from file
- `--value <text>` uses literal value (backward compatible)
File path handling:
- Supports tilde expansion for home directory (~/)
- Provides clear error messages if file cannot be read
Examples:
# From stdin
cat ~/.ssh/id_ed25519 | tangled spindle secret add \
--repo myrepo --key SSH_KEY --value -
# From file
tangled spindle secret add --repo myrepo \
--key SSH_KEY --value @~/.ssh/id_ed25519
# Literal value (existing behavior)
tangled spindle secret add --repo myrepo \
--key API_KEY --value "my-secret-key"
Fixes issue where multiline values were split into multiple
arguments by the shell, causing clap parsing errors.
Extend automatic OAuth token refresh to all command modules:
Updated commands:
- repo: list, create, clone, info, delete, star, unstar (7 functions)
- issue: list, create, show, edit, comment (5 functions)
- pr: list, create, show, review, merge (5 functions)
- knot: migrate (1 function)
Changes per module:
- Remove SessionManager import (no longer needed)
- Replace SessionManager::load() with util::load_session_with_refresh()
- Handles both pattern variations:
- match mgr.load()? { Some(s) => s, None => ... }
- mgr.load()?.ok_or_else(...)
All authenticated commands now automatically refresh expired tokens
without requiring manual re-authentication, providing a seamless
user experience across the entire CLI.
Add automatic token refresh to gracefully handle expired access tokens:
Token refresh implementation:
- Add refresh_session() method to TangledClient
- Calls com.atproto.server.refreshSession with refresh token
- Returns new Session with updated access and refresh tokens
- Create util module with session management helpers
- load_session(): Basic session loading
- refresh_session(): Refresh using refresh token and save
- load_session_with_refresh(): Smart loading with auto-refresh
Auto-refresh strategy:
- Check session age on every command invocation
- If session is older than 30 minutes, proactively refresh
- Falls back to old session if refresh fails (might still work)
- Preserves PDS URL and updates created_at timestamp
Update all spindle commands to use auto-refresh:
- Replace SessionManager::load() with util::load_session_with_refresh()
- Applies to: config, list, logs, and all secret operations
- Remove now-unused SessionManager imports
Dependencies:
- Add chrono to tangled-cli for timestamp comparison
Fixes ExpiredToken errors that occurred when access tokens expired
after initial login, requiring manual re-authentication.
ServiceAuth token fixes:
- Reduce expiration from 600 to 60 seconds for method-less tokens
per AT Protocol spec (fixes BadExpiration error)
Spindle URL handling:
- Support URLs with or without protocol prefix (e.g., "spindle.vitorpy.com")
- Add protocol (https://) automatically in xrpc_url() when missing
- Extract host correctly for ServiceAuth audience DID in both cases
- Read spindle URL from repo's spindle field for secret operations
- Fall back to TANGLED_SPINDLE_BASE env var or default
Secret operations fixes:
- Add new post() method for endpoints that return empty responses
- Update add_repo_secret() and remove_repo_secret() to use post()
instead of post_json() (fixes JSON parsing error on empty response)
- All secret operations now connect to correct spindle instance
Other improvements:
- Add spindle field to RepoRecord struct
- Display spindle URL in repo info output
- Ensure all three secret operations (list, add, remove) use the
repo's configured spindle instance
- README.md: Complete rewrite with all implemented features
- Comprehensive command overview (auth, repo, issue, pr, knot, spindle)
- Installation instructions (source + AUR)
- Quick start guide with examples
- Configuration and environment variables
- Examples for issues, PRs, and CI/CD
- Development workflow section
- AGENTS.md: Updated from initial handoff to current status doc
- Implementation checklist (all features marked as complete except spindle run)
- Architecture patterns (ServiceAuth, repo creation, listing, PR merging)
- Working with tangled-core reference
- Next steps for contributors (focus on spindle run)
- Troubleshooting guide
- docs/getting-started.md: Expanded from stub to comprehensive tutorial
- Step-by-step installation guide
- First steps (auth, list, create, clone)
- Detailed examples for issues, PRs, and CI/CD
- Advanced topics (migration, output formats, quiet/verbose)
- Configuration and environment variables
- Troubleshooting section
Removed outdated "commands are stubs" messaging and added
accurate feature descriptions for all implemented functionality.
- Add spindle field to Repository struct
- Implement update_repo_spindle() to enable/disable CI for repos
- Implement list_pipelines() to fetch pipeline records from PDS
- Add Pipeline, TriggerMetadata, TriggerRepo, and Workflow structs
- Implement spindle config command:
- Enable/disable spindle for a repository
- Support custom spindle URL via --url flag
- Update repo record's spindle field
- Implement spindle list command:
- List all pipeline runs for a repository
- Display trigger kind, repo, and workflows
- Implement spindle logs command:
- Stream logs from a workflow execution via WebSocket
- Support both full job_id format (knot:rkey:name) and short format (name)
- Add --lines and --follow flags for log control
- Add WebSocket dependencies: tokio-tungstenite and futures-util
- Keep spindle run as stub (to be implemented later)
- Add merge_pull() to API client using sh.tangled.repo.merge
- Implement 'pr merge' CLI command with ServiceAuth flow
- Remove stub knot commands (list, add, verify, set-default, remove)
- Keep only 'knot migrate' which is fully implemented
Add automatic detection and merging of stacked pull requests with
comprehensive conflict checking and user confirmation. When merging
a PR that's part of a stack, the CLI now:
- Auto-detects stack membership via stack_id field
- Displays preview of all PRs to be merged (current + below)
- Checks cumulative patches for conflicts before merging
- Prompts user for confirmation before executing merge
- Provides clear error messages for conflicts
API changes:
- Add stack fields to Pull: stack_id, change_id, parent_change_id
- Add MergeCheckRequest/Response types for conflict detection
- Add merge_check() method to TangledClient
CLI changes:
- Refactor merge() to detect and handle stacked PRs
- Add helper functions for stack ordering and conflict checking
- Maintain backward compatibility for non-stacked PRs
Support reading secret values from stdin or files to handle
multiline content like SSH keys, certificates, and config files.
New value patterns:
- `--value -` reads from stdin
- `--value @<path>` reads from file
- `--value <text>` uses literal value (backward compatible)
File path handling:
- Supports tilde expansion for home directory (~/)
- Provides clear error messages if file cannot be read
Examples:
# From stdin
cat ~/.ssh/id_ed25519 | tangled spindle secret add \
--repo myrepo --key SSH_KEY --value -
# From file
tangled spindle secret add --repo myrepo \
--key SSH_KEY --value @~/.ssh/id_ed25519
# Literal value (existing behavior)
tangled spindle secret add --repo myrepo \
--key API_KEY --value "my-secret-key"
Fixes issue where multiline values were split into multiple
arguments by the shell, causing clap parsing errors.
Extend automatic OAuth token refresh to all command modules:
Updated commands:
- repo: list, create, clone, info, delete, star, unstar (7 functions)
- issue: list, create, show, edit, comment (5 functions)
- pr: list, create, show, review, merge (5 functions)
- knot: migrate (1 function)
Changes per module:
- Remove SessionManager import (no longer needed)
- Replace SessionManager::load() with util::load_session_with_refresh()
- Handles both pattern variations:
- match mgr.load()? { Some(s) => s, None => ... }
- mgr.load()?.ok_or_else(...)
All authenticated commands now automatically refresh expired tokens
without requiring manual re-authentication, providing a seamless
user experience across the entire CLI.
Add automatic token refresh to gracefully handle expired access tokens:
Token refresh implementation:
- Add refresh_session() method to TangledClient
- Calls com.atproto.server.refreshSession with refresh token
- Returns new Session with updated access and refresh tokens
- Create util module with session management helpers
- load_session(): Basic session loading
- refresh_session(): Refresh using refresh token and save
- load_session_with_refresh(): Smart loading with auto-refresh
Auto-refresh strategy:
- Check session age on every command invocation
- If session is older than 30 minutes, proactively refresh
- Falls back to old session if refresh fails (might still work)
- Preserves PDS URL and updates created_at timestamp
Update all spindle commands to use auto-refresh:
- Replace SessionManager::load() with util::load_session_with_refresh()
- Applies to: config, list, logs, and all secret operations
- Remove now-unused SessionManager imports
Dependencies:
- Add chrono to tangled-cli for timestamp comparison
Fixes ExpiredToken errors that occurred when access tokens expired
after initial login, requiring manual re-authentication.
ServiceAuth token fixes:
- Reduce expiration from 600 to 60 seconds for method-less tokens
per AT Protocol spec (fixes BadExpiration error)
Spindle URL handling:
- Support URLs with or without protocol prefix (e.g., "spindle.vitorpy.com")
- Add protocol (https://) automatically in xrpc_url() when missing
- Extract host correctly for ServiceAuth audience DID in both cases
- Read spindle URL from repo's spindle field for secret operations
- Fall back to TANGLED_SPINDLE_BASE env var or default
Secret operations fixes:
- Add new post() method for endpoints that return empty responses
- Update add_repo_secret() and remove_repo_secret() to use post()
instead of post_json() (fixes JSON parsing error on empty response)
- All secret operations now connect to correct spindle instance
Other improvements:
- Add spindle field to RepoRecord struct
- Display spindle URL in repo info output
- Ensure all three secret operations (list, add, remove) use the
repo's configured spindle instance
- README.md: Complete rewrite with all implemented features
- Comprehensive command overview (auth, repo, issue, pr, knot, spindle)
- Installation instructions (source + AUR)
- Quick start guide with examples
- Configuration and environment variables
- Examples for issues, PRs, and CI/CD
- Development workflow section
- AGENTS.md: Updated from initial handoff to current status doc
- Implementation checklist (all features marked as complete except spindle run)
- Architecture patterns (ServiceAuth, repo creation, listing, PR merging)
- Working with tangled-core reference
- Next steps for contributors (focus on spindle run)
- Troubleshooting guide
- docs/getting-started.md: Expanded from stub to comprehensive tutorial
- Step-by-step installation guide
- First steps (auth, list, create, clone)
- Detailed examples for issues, PRs, and CI/CD
- Advanced topics (migration, output formats, quiet/verbose)
- Configuration and environment variables
- Troubleshooting section
Removed outdated "commands are stubs" messaging and added
accurate feature descriptions for all implemented functionality.
- Add spindle field to Repository struct
- Implement update_repo_spindle() to enable/disable CI for repos
- Implement list_pipelines() to fetch pipeline records from PDS
- Add Pipeline, TriggerMetadata, TriggerRepo, and Workflow structs
- Implement spindle config command:
- Enable/disable spindle for a repository
- Support custom spindle URL via --url flag
- Update repo record's spindle field
- Implement spindle list command:
- List all pipeline runs for a repository
- Display trigger kind, repo, and workflows
- Implement spindle logs command:
- Stream logs from a workflow execution via WebSocket
- Support both full job_id format (knot:rkey:name) and short format (name)
- Add --lines and --follow flags for log control
- Add WebSocket dependencies: tokio-tungstenite and futures-util
- Keep spindle run as stub (to be implemented later)