Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ Common options have short forms:
> - Most commands accept **names or IDs** for spaces, boards, lists, sprints, users, and tags
> - Use `-s SPACE` to help when board or list names are unclear
> - Use `--json` for scripted output: `suth cards assigned me --json`
> - Use `me` as a shortcut: `suth cards assigned me`
> - Use `me` in any user argument: `suth cards assigned me`, `suth cards assign CARD me`, `--owner me`
> - Priority levels: 1=Urgent, 2=High, 3=Medium, 4=Low

### Mentions
Expand Down
2 changes: 1 addition & 1 deletion lib/superthread/cli/cards.rb
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ def get(card_id)
option :priority, type: :numeric, desc: "Priority level (1=low, 4=urgent)"
option :parent_card, type: :string, desc: "Parent card ID"
option :epic, type: :string, desc: "Epic ID"
option :owner, type: :string, aliases: "-o", desc: "Owner (user ID, name, or email)"
option :owner, type: :string, aliases: "-o", desc: "Owner (user ID, name, email, or 'me')"
# Create a new card on a board or sprint.
#
# @return [void]
Expand Down
10 changes: 10 additions & 0 deletions lib/superthread/cli/concerns/user_resolvable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ module UserResolvable
# @raise [Thor::Error] if name/email is provided but not found
def resolve_user(ref)
return ref if ref.nil?

return resolve_me if ref.downcase == "me"

return ref if looks_like_id?(ref)

user = find_user_by_name(ref)
Expand All @@ -29,6 +32,13 @@ def resolve_user(ref)
raise Thor::Error, "User not found: '#{ref}'. Use 'suth members list' to see available users."
end

# Resolve 'me' to the current user's identifier (cached).
#
# @return [String] the current user's identifier
def resolve_me
@me_cache ||= client.users.me.user_identifier
end

# Get cached workspace members list.
#
# @return [Array<Superthread::Models::User>] all workspace members
Expand Down
4 changes: 2 additions & 2 deletions lib/superthread/cli/projects.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def get(project_id)
option :content, type: :string, desc: "Project description"
option :start_date, type: :numeric, desc: "Start date (Unix timestamp)"
option :due_date, type: :numeric, desc: "Due date (Unix timestamp)"
option :owner, type: :string, aliases: "-o", desc: "Owner (user ID, name, or email)"
option :owner, type: :string, aliases: "-o", desc: "Owner (user ID, name, email, or 'me')"
option :priority, type: :numeric, desc: "Priority level"
# Creates a new project on a board list.
#
Expand All @@ -60,7 +60,7 @@ def create
option :list, type: :string, aliases: "-l", desc: "Destination list (ID or name, requires --board)"
option :board, type: :string, aliases: "-b", desc: "Board (helps resolve list name)"
option :space, type: :string, aliases: "-s", desc: "Space (helps resolve board name)"
option :owner, type: :string, aliases: "-o", desc: "New owner (user ID, name, or email)"
option :owner, type: :string, aliases: "-o", desc: "New owner (user ID, name, email, or 'me')"
option :start_date, type: :numeric, desc: "Start date"
option :due_date, type: :numeric, desc: "Due date"
option :priority, type: :numeric, desc: "Priority"
Expand Down
21 changes: 11 additions & 10 deletions skills/superthread/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ suth search query "term" # Search everything
--json # Structured output (for scripts/agents)
-y / --yes # Skip confirmations
-s SPACE # Resolve ambiguous names (boards, lists, sprints)
me # Use as user reference: suth cards assigned me
me # Current user in any user argument (assigned, assign, --owner, --user, etc.)
```

Most commands accept **names or IDs** for spaces, boards, lists, sprints, users, and tags.
Expand Down Expand Up @@ -128,7 +128,7 @@ suth spaces create --title "Name"
suth spaces update SPACE --title "New Name"
# Options: --description, --icon NAME, --icon-color "#HEX"
suth spaces delete SPACE
suth spaces add_member SPACE USERS [--role ROLE] # Comma-separated users
suth spaces add_member SPACE USERS [--role ROLE] # IDs, names, emails, or 'me'
suth spaces remove_member SPACE USERS
```

Expand Down Expand Up @@ -171,7 +171,7 @@ suth lists delete LIST
suth cards list -b BOARD
suth cards list --sprint SPRINT -s SPACE
# Options: --list, --include-archived, --since DATE, --updated-since DATE, -s SPACE
suth cards assigned USER
suth cards assigned USER # USER = ID, name, email, or 'me'
suth cards assigned me
# Options: --board, --space, --project, --include-archived,
# --since DATE, --updated-since DATE
Expand All @@ -187,7 +187,7 @@ suth cards get CARD
suth cards create --title "Task" -l LIST -b BOARD
# Options: --content HTML, --project ID, --parent-card ID, --epic ID,
# --sprint SPRINT, -s SPACE, --start-date TIMESTAMP,
# --due-date TIMESTAMP, --priority N, --owner/-o USER
# --due-date TIMESTAMP, --priority N, --owner/-o USER (or 'me')
suth cards update CARD
# Options: --title, --content HTML, --list LIST, --board BOARD, --sprint SPRINT,
# -s SPACE, --position N, --priority N, --epic ID, --archived/--no-archived
Expand All @@ -198,8 +198,9 @@ suth cards duplicate CARD --project ID -b BOARD -l LIST
# Required: --project, --board/-b, --list/-l
# Options: --title, --space/-s

# Members
suth cards assign CARD USERS # Comma-separated
# Members (USERS = IDs, names, emails, or 'me'; comma-separated)
suth cards assign CARD USERS
suth cards assign CARD me # Assign yourself
suth cards unassign CARD USERS

# Relationships
Expand All @@ -218,9 +219,9 @@ suth projects list
suth projects get PROJECT
suth projects create --title "Q1" -l LIST [-b BOARD]
# Options: --content, --start-date TIMESTAMP, --due-date TIMESTAMP,
# --priority N, --owner/-o USER, -s SPACE
# --priority N, --owner/-o USER (or 'me'), -s SPACE
suth projects update PROJECT --title "New"
# Options: --list/-l, --board/-b, --space/-s, --owner/-o USER,
# Options: --list/-l, --board/-b, --space/-s, --owner/-o USER (or 'me'),
# --start-date, --due-date, --priority, --archived
suth projects delete PROJECT
suth projects add_card PROJECT CARD
Expand Down Expand Up @@ -321,7 +322,7 @@ suth search query TERM
```bash
suth activity # Recent activity (default: today)
# Runs `show` subcommand by default
# Options: --since DATE, --user USER, --board/-b BOARD, --space/-s SPACE
# Options: --since DATE, --user USER (or 'me'), --board/-b BOARD, --space/-s SPACE
```

### Config & Completion
Expand Down Expand Up @@ -358,7 +359,7 @@ suth completion fish
| `--list` | `-l` | List (ID or name) |
| `--card` | `-c` | Card ID |
| `--related` | `-r` | Related card ID |
| `--owner` | `-o` | Owner (user ID, name, or email) |
| `--owner` | `-o` | Owner (user ID, name, email, or 'me') |
| `--yes` | `-y` | Skip confirmations |

Priority levels: 1=Urgent, 2=High, 3=Medium, 4=Low
3 changes: 3 additions & 0 deletions spec/integration/cli/activity_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@

context "with completed cards" do
before do
stub_api_get("users/me", response: ApiFixtures::Users::ME)
stub_api_post("test_workspace/views/preview", response: {
cards: [
{
Expand Down Expand Up @@ -127,6 +128,7 @@

context "with no activity" do
before do
stub_api_get("users/me", response: ApiFixtures::Users::ME)
stub_api_post("test_workspace/views/preview", response: {
cards: [
{
Expand All @@ -151,6 +153,7 @@

describe "activity with date filters" do
before do
stub_api_get("users/me", response: ApiFixtures::Users::ME)
stub_api_post("test_workspace/views/preview", response: {
cards: [
{
Expand Down
83 changes: 83 additions & 0 deletions spec/superthread/cli/concerns/user_resolvable_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# frozen_string_literal: true

require "spec_helper"

RSpec.describe Superthread::Cli::Concerns::UserResolvable do
let(:test_class) do
Class.new do
include Superthread::Cli::Concerns::UserResolvable

attr_accessor :client

def looks_like_id?(value)
value.match?(/\A[a-zA-Z0-9_-]+\z/) && value.length <= 36
end

def workspace_id
"test_workspace"
end

public :resolve_user
end
end

let(:client) { instance_double(Superthread::Client) }
let(:users_resource) { instance_double(Superthread::Resources::Users) }
let(:instance) do
obj = test_class.new
obj.client = client
obj
end

let(:current_user) do
Superthread::Models::User.new(id: "u123abc", display_name: "Test User", email: "test@example.com")
end

let(:members) do
[
Superthread::Models::User.new(user_id: "u123abc", display_name: "Test User", email: "test@example.com"),
Superthread::Models::User.new(user_id: "u456def", display_name: "Another User", email: "another@example.com")
]
end

before do
allow(client).to receive(:users).and_return(users_resource)
allow(users_resource).to receive(:members).with("test_workspace").and_return(members)
end

describe "#resolve_user" do
it "returns nil for nil input" do
expect(instance.resolve_user(nil)).to be_nil
end

it "returns ID directly when ref looks like an ID" do
expect(instance.resolve_user("u123abc")).to eq("u123abc")
end

it "resolves display name to user_identifier" do
expect(instance.resolve_user("Another User")).to eq("u456def")
end

it "raises Thor::Error when name not found" do
expect { instance.resolve_user("Nobody Here") }.to raise_error(Thor::Error, /User not found/)
end

context "when ref is 'me'" do
before do
allow(users_resource).to receive(:me).and_return(current_user)
end

it "resolves 'me' to the current user's identifier" do
expect(instance.resolve_user("me")).to eq("u123abc")
end

it "resolves 'Me' case-insensitively" do
expect(instance.resolve_user("Me")).to eq("u123abc")
end

it "resolves 'ME' case-insensitively" do
expect(instance.resolve_user("ME")).to eq("u123abc")
end
end
end
end