oh-my-claude-sisyphus 3.8.16 → 3.9.1
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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.mcp.json +1 -1
- package/agents/analyst.md +41 -0
- package/agents/architect.md +47 -2
- package/agents/critic.md +42 -0
- package/agents/deep-executor.md +193 -0
- package/agents/planner.md +82 -0
- package/bridge/mcp-server.cjs +181 -181
- package/commands/build-fix.md +3 -3
- package/commands/ralph.md +3 -3
- package/commands/ultraqa.md +4 -4
- package/commands/ultrawork.md +3 -3
- package/dist/__tests__/agent-registry.test.js +1 -1
- package/dist/__tests__/installer.test.js +8 -8
- package/dist/__tests__/installer.test.js.map +1 -1
- package/dist/__tests__/omc-tools-server.test.js +2 -2
- package/dist/__tests__/omc-tools-server.test.js.map +1 -1
- package/dist/__tests__/skills.test.js +5 -4
- package/dist/__tests__/skills.test.js.map +1 -1
- package/dist/agents/deep-executor.d.ts +15 -0
- package/dist/agents/deep-executor.d.ts.map +1 -0
- package/dist/agents/deep-executor.js +47 -0
- package/dist/agents/deep-executor.js.map +1 -0
- package/dist/agents/definitions.d.ts +16 -1
- package/dist/agents/definitions.d.ts.map +1 -1
- package/dist/agents/definitions.js +26 -1
- package/dist/agents/definitions.js.map +1 -1
- package/dist/agents/index.d.ts +1 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +1 -0
- package/dist/agents/index.js.map +1 -1
- package/dist/cli/commands/doctor-conflicts.d.ts +55 -0
- package/dist/cli/commands/doctor-conflicts.d.ts.map +1 -0
- package/dist/cli/commands/doctor-conflicts.js +261 -0
- package/dist/cli/commands/doctor-conflicts.js.map +1 -0
- package/dist/cli/index.js +16 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/features/auto-update.d.ts +12 -0
- package/dist/features/auto-update.d.ts.map +1 -1
- package/dist/features/auto-update.js +4 -1
- package/dist/features/auto-update.js.map +1 -1
- package/dist/features/background-tasks.js +4 -4
- package/dist/features/context-injector/types.d.ts +1 -1
- package/dist/features/context-injector/types.d.ts.map +1 -1
- package/dist/features/task-decomposer/index.js +3 -3
- package/dist/features/task-decomposer/index.js.map +1 -1
- package/dist/features/verification/index.d.ts +3 -3
- package/dist/features/verification/index.js +3 -3
- package/dist/features/verification/index.js.map +1 -1
- package/dist/hooks/__tests__/bridge.test.d.ts +2 -0
- package/dist/hooks/__tests__/bridge.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/bridge.test.js +199 -0
- package/dist/hooks/__tests__/bridge.test.js.map +1 -0
- package/dist/hooks/autopilot/state.js +3 -3
- package/dist/hooks/beads-context/__tests__/index.test.d.ts +2 -0
- package/dist/hooks/beads-context/__tests__/index.test.d.ts.map +1 -0
- package/dist/hooks/beads-context/__tests__/index.test.js +150 -0
- package/dist/hooks/beads-context/__tests__/index.test.js.map +1 -0
- package/dist/hooks/beads-context/constants.d.ts +3 -0
- package/dist/hooks/beads-context/constants.d.ts.map +1 -0
- package/dist/hooks/beads-context/constants.js +35 -0
- package/dist/hooks/beads-context/constants.js.map +1 -0
- package/dist/hooks/beads-context/index.d.ts +21 -0
- package/dist/hooks/beads-context/index.d.ts.map +1 -0
- package/dist/hooks/beads-context/index.js +62 -0
- package/dist/hooks/beads-context/index.js.map +1 -0
- package/dist/hooks/beads-context/types.d.ts +7 -0
- package/dist/hooks/beads-context/types.d.ts.map +1 -0
- package/dist/hooks/beads-context/types.js +2 -0
- package/dist/hooks/beads-context/types.js.map +1 -0
- package/dist/hooks/bridge.d.ts +4 -0
- package/dist/hooks/bridge.d.ts.map +1 -1
- package/dist/hooks/bridge.js +76 -23
- package/dist/hooks/bridge.js.map +1 -1
- package/dist/hooks/clear-suggestions/constants.d.ts +54 -0
- package/dist/hooks/clear-suggestions/constants.d.ts.map +1 -0
- package/dist/hooks/clear-suggestions/constants.js +102 -0
- package/dist/hooks/clear-suggestions/constants.js.map +1 -0
- package/dist/hooks/clear-suggestions/index.d.ts +61 -0
- package/dist/hooks/clear-suggestions/index.d.ts.map +1 -0
- package/dist/hooks/clear-suggestions/index.js +282 -0
- package/dist/hooks/clear-suggestions/index.js.map +1 -0
- package/dist/hooks/clear-suggestions/triggers.d.ts +65 -0
- package/dist/hooks/clear-suggestions/triggers.d.ts.map +1 -0
- package/dist/hooks/clear-suggestions/triggers.js +222 -0
- package/dist/hooks/clear-suggestions/triggers.js.map +1 -0
- package/dist/hooks/clear-suggestions/types.d.ts +92 -0
- package/dist/hooks/clear-suggestions/types.d.ts.map +1 -0
- package/dist/hooks/clear-suggestions/types.js +9 -0
- package/dist/hooks/clear-suggestions/types.js.map +1 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +3 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/permission-handler/index.d.ts.map +1 -1
- package/dist/hooks/permission-handler/index.js +3 -1
- package/dist/hooks/permission-handler/index.js.map +1 -1
- package/dist/hooks/setup/index.d.ts.map +1 -1
- package/dist/hooks/setup/index.js +12 -5
- package/dist/hooks/setup/index.js.map +1 -1
- package/dist/hooks/subagent-tracker/index.d.ts.map +1 -1
- package/dist/hooks/subagent-tracker/index.js +25 -9
- package/dist/hooks/subagent-tracker/index.js.map +1 -1
- package/dist/hooks/ultraqa/index.js +4 -4
- package/dist/hooks/ultraqa/index.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/installer/__tests__/claude-md-merge.test.d.ts +6 -0
- package/dist/installer/__tests__/claude-md-merge.test.d.ts.map +1 -0
- package/dist/installer/__tests__/claude-md-merge.test.js +220 -0
- package/dist/installer/__tests__/claude-md-merge.test.js.map +1 -0
- package/dist/installer/__tests__/safe-installer.test.d.ts +6 -0
- package/dist/installer/__tests__/safe-installer.test.d.ts.map +1 -0
- package/dist/installer/__tests__/safe-installer.test.js +172 -0
- package/dist/installer/__tests__/safe-installer.test.js.map +1 -0
- package/dist/installer/hooks.d.ts.map +1 -1
- package/dist/installer/hooks.js +3 -1
- package/dist/installer/hooks.js.map +1 -1
- package/dist/installer/index.d.ts +27 -1
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +209 -85
- package/dist/installer/index.js.map +1 -1
- package/dist/mcp/omc-tools-server.d.ts +1 -1
- package/dist/mcp/omc-tools-server.d.ts.map +1 -1
- package/dist/mcp/omc-tools-server.js +3 -3
- package/dist/mcp/omc-tools-server.js.map +1 -1
- package/dist/mcp/standalone-server.js +1 -1
- package/dist/mcp/standalone-server.js.map +1 -1
- package/dist/verification/tier-selector.d.ts +40 -0
- package/dist/verification/tier-selector.d.ts.map +1 -0
- package/dist/verification/tier-selector.js +95 -0
- package/dist/verification/tier-selector.js.map +1 -0
- package/dist/verification/tier-selector.test.d.ts +2 -0
- package/dist/verification/tier-selector.test.d.ts.map +1 -0
- package/dist/verification/tier-selector.test.js +282 -0
- package/dist/verification/tier-selector.test.js.map +1 -0
- package/docs/AGENTS.md +1 -1
- package/docs/CLAUDE.md +90 -378
- package/docs/partials/agent-tiers.md +165 -0
- package/docs/partials/features.md +131 -0
- package/docs/partials/mode-hierarchy.md +120 -0
- package/docs/partials/mode-selection-guide.md +82 -0
- package/docs/partials/verification-tiers.md +107 -0
- package/docs/shared/agent-tiers.md +165 -0
- package/docs/shared/features.md +131 -0
- package/docs/shared/mode-hierarchy.md +120 -0
- package/docs/shared/mode-selection-guide.md +82 -0
- package/docs/shared/verification-tiers.md +107 -0
- package/package.json +4 -3
- package/scripts/compose-docs.mjs +44 -0
- package/scripts/keyword-detector.mjs +13 -3
- package/skills/build-fix/SKILL.md +8 -8
- package/skills/deep-executor/SKILL.md +50 -0
- package/skills/deepinit/SKILL.md +2 -2
- package/skills/ecomode/SKILL.md +58 -103
- package/skills/omc-setup/SKILL.md +197 -20
- package/skills/plan/SKILL.md +62 -0
- package/skills/project-session-manager/SKILL.md +87 -4
- package/skills/project-session-manager/lib/config.sh +54 -5
- package/skills/project-session-manager/lib/parse.sh +65 -11
- package/skills/project-session-manager/lib/providers/github.sh +52 -0
- package/skills/project-session-manager/lib/providers/interface.sh +76 -0
- package/skills/project-session-manager/lib/providers/jira.sh +79 -0
- package/skills/project-session-manager/lib/session.sh +49 -12
- package/skills/project-session-manager/lib/worktree.sh +37 -4
- package/skills/project-session-manager/psm.sh +116 -51
- package/skills/ralph/SKILL.md +44 -30
- package/skills/tdd/SKILL.md +2 -2
- package/skills/ultrapilot/SKILL.md +3 -3
- package/skills/ultraqa/SKILL.md +4 -4
- package/skills/ultrawork/SKILL.md +59 -69
- package/templates/hooks/keyword-detector.mjs +21 -13
- package/templates/hooks/lib/stdin.mjs +62 -0
- package/templates/hooks/persistent-mode.mjs +7 -8
- package/templates/hooks/post-tool-use.mjs +8 -10
- package/templates/hooks/pre-tool-use.mjs +9 -6
- package/templates/hooks/session-start.mjs +7 -8
package/skills/plan/SKILL.md
CHANGED
|
@@ -17,6 +17,20 @@ You guide users through planning by:
|
|
|
17
17
|
|
|
18
18
|
## Planning Modes
|
|
19
19
|
|
|
20
|
+
| Mode | Trigger | Behavior |
|
|
21
|
+
|------|---------|----------|
|
|
22
|
+
| interview | Default | Interactive requirements gathering with adaptive exploration |
|
|
23
|
+
| direct | --direct, detailed request | Skip interview, generate plan directly |
|
|
24
|
+
| consensus | --consensus, "ralplan" | Planner → Architect → Critic loop until consensus |
|
|
25
|
+
| review | --review | Critic review of existing plan |
|
|
26
|
+
|
|
27
|
+
### Review Mode
|
|
28
|
+
|
|
29
|
+
When `--review` is specified or user says "review this plan":
|
|
30
|
+
1. Read the plan file from `.omc/plans/`
|
|
31
|
+
2. Spawn Critic agent to review
|
|
32
|
+
3. Return verdict (OKAY or REJECT with improvements)
|
|
33
|
+
|
|
20
34
|
### Auto-Detection: Interview vs Direct Planning
|
|
21
35
|
|
|
22
36
|
**Interview Mode** (when request is BROAD):
|
|
@@ -53,6 +67,54 @@ Ask clarifying questions about: Goals, Constraints, Context, Risks, Preferences
|
|
|
53
67
|
|
|
54
68
|
**When plain text is OK:** Questions needing specific values (port numbers, names) or follow-up clarifications.
|
|
55
69
|
|
|
70
|
+
## Adaptive Context Gathering (CRITICAL)
|
|
71
|
+
|
|
72
|
+
Before asking ANY question, classify it:
|
|
73
|
+
|
|
74
|
+
### Question Classification
|
|
75
|
+
|
|
76
|
+
| Type | Examples | Action |
|
|
77
|
+
|------|----------|--------|
|
|
78
|
+
| **Codebase Fact** | "What patterns exist?", "Where is X implemented?" | Explore first, DON'T ask user |
|
|
79
|
+
| **User Preference** | "Priority?", "Timeline?", "Risk tolerance?" | Ask user via AskUserQuestion |
|
|
80
|
+
| **Scope Decision** | "Include feature Y?" | Ask user |
|
|
81
|
+
| **Requirement** | "Performance constraints?" | Ask user |
|
|
82
|
+
|
|
83
|
+
### Adaptive Flow
|
|
84
|
+
|
|
85
|
+
1. Generate interview question
|
|
86
|
+
2. Classify: Is this a codebase fact or user preference?
|
|
87
|
+
3. If **CODEBASE FACT**:
|
|
88
|
+
a. Spawn `explore` agent (haiku, 30s timeout)
|
|
89
|
+
b. Query: focused on the specific fact needed
|
|
90
|
+
c. Use findings to inform next question or skip question entirely
|
|
91
|
+
4. If **USER PREFERENCE**:
|
|
92
|
+
a. Use AskUserQuestion tool with options
|
|
93
|
+
b. Wait for response
|
|
94
|
+
5. Repeat for next question
|
|
95
|
+
|
|
96
|
+
### Exploration Integration
|
|
97
|
+
|
|
98
|
+
When context is gathered via explore agent:
|
|
99
|
+
- **DO NOT** ask "What patterns does the codebase use?"
|
|
100
|
+
- **DO** say "I see the codebase uses [pattern X]. Would you like to follow this pattern or try something different?"
|
|
101
|
+
|
|
102
|
+
### Example Adaptive Interview
|
|
103
|
+
|
|
104
|
+
**Without Adaptive (BAD):**
|
|
105
|
+
```
|
|
106
|
+
Planner: "Where is authentication implemented in your codebase?"
|
|
107
|
+
User: "Uh, somewhere in src/auth I think?"
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**With Adaptive (GOOD):**
|
|
111
|
+
```
|
|
112
|
+
Planner: [spawns explore agent: "find authentication implementation"]
|
|
113
|
+
Planner: [receives: "Auth is in src/auth/ using JWT with passport.js"]
|
|
114
|
+
Planner: "I see you're using JWT authentication with passport.js in src/auth/.
|
|
115
|
+
For this new feature, should we extend the existing auth or add a separate auth flow?"
|
|
116
|
+
```
|
|
117
|
+
|
|
56
118
|
**MANDATORY: Single Question at a Time**
|
|
57
119
|
|
|
58
120
|
**Core Rule:** Never ask multiple questions in one message during interview mode.
|
|
@@ -49,6 +49,85 @@ Supported formats:
|
|
|
49
49
|
}
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
## Providers
|
|
53
|
+
|
|
54
|
+
PSM supports multiple issue tracking providers:
|
|
55
|
+
|
|
56
|
+
| Provider | CLI Required | Reference Formats | Commands |
|
|
57
|
+
|----------|--------------|-------------------|----------|
|
|
58
|
+
| GitHub (default) | `gh` | `owner/repo#123`, `alias#123`, GitHub URLs | review, fix, feature |
|
|
59
|
+
| Jira | `jira` | `PROJ-123` (if PROJ configured), `alias#123` | fix, feature |
|
|
60
|
+
|
|
61
|
+
### Jira Configuration
|
|
62
|
+
|
|
63
|
+
To use Jira, add an alias with `jira_project` and `provider: "jira"`:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"aliases": {
|
|
68
|
+
"mywork": {
|
|
69
|
+
"jira_project": "MYPROJ",
|
|
70
|
+
"repo": "mycompany/my-project",
|
|
71
|
+
"local": "~/Workspace/my-project",
|
|
72
|
+
"default_base": "develop",
|
|
73
|
+
"provider": "jira"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Important:** The `repo` field is still required for cloning the git repository. Jira tracks issues, but you work in a git repo.
|
|
80
|
+
|
|
81
|
+
For non-GitHub repos, use `clone_url` instead:
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"aliases": {
|
|
85
|
+
"private": {
|
|
86
|
+
"jira_project": "PRIV",
|
|
87
|
+
"clone_url": "git@gitlab.internal:team/repo.git",
|
|
88
|
+
"local": "~/Workspace/repo",
|
|
89
|
+
"provider": "jira"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Jira Reference Detection
|
|
96
|
+
|
|
97
|
+
PSM only recognizes `PROJ-123` format as Jira when `PROJ` is explicitly configured as a `jira_project` in your aliases. This prevents false positives from branch names like `FIX-123`.
|
|
98
|
+
|
|
99
|
+
### Jira Examples
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# Fix a Jira issue (MYPROJ must be configured)
|
|
103
|
+
psm fix MYPROJ-123
|
|
104
|
+
|
|
105
|
+
# Fix using alias (recommended)
|
|
106
|
+
psm fix mywork#123
|
|
107
|
+
|
|
108
|
+
# Feature development (works same as GitHub)
|
|
109
|
+
psm feature mywork add-webhooks
|
|
110
|
+
|
|
111
|
+
# Note: 'psm review' is not supported for Jira (no PR concept)
|
|
112
|
+
# Use 'psm fix' for Jira issues
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Jira CLI Setup
|
|
116
|
+
|
|
117
|
+
Install the Jira CLI:
|
|
118
|
+
```bash
|
|
119
|
+
# macOS
|
|
120
|
+
brew install ankitpokhrel/jira-cli/jira-cli
|
|
121
|
+
|
|
122
|
+
# Linux
|
|
123
|
+
# See: https://github.com/ankitpokhrel/jira-cli#installation
|
|
124
|
+
|
|
125
|
+
# Configure (interactive)
|
|
126
|
+
jira init
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The Jira CLI handles authentication separately from PSM.
|
|
130
|
+
|
|
52
131
|
## Directory Structure
|
|
53
132
|
|
|
54
133
|
```
|
|
@@ -371,10 +450,14 @@ Parse `{{ARGUMENTS}}` to determine:
|
|
|
371
450
|
|
|
372
451
|
## Requirements
|
|
373
452
|
|
|
374
|
-
|
|
375
|
-
- `
|
|
376
|
-
- `
|
|
377
|
-
- `
|
|
453
|
+
Required:
|
|
454
|
+
- `git` - Version control (with worktree support v2.5+)
|
|
455
|
+
- `jq` - JSON parsing
|
|
456
|
+
- `tmux` - Session management (optional, but recommended)
|
|
457
|
+
|
|
458
|
+
Optional (per provider):
|
|
459
|
+
- `gh` - GitHub CLI (for GitHub workflows)
|
|
460
|
+
- `jira` - Jira CLI (for Jira workflows)
|
|
378
461
|
|
|
379
462
|
## Initialization
|
|
380
463
|
|
|
@@ -48,11 +48,12 @@ psm_get_project() {
|
|
|
48
48
|
return 1
|
|
49
49
|
fi
|
|
50
50
|
|
|
51
|
-
local repo=$(jq -r ".aliases[
|
|
52
|
-
local local_path=$(jq -r ".aliases[
|
|
53
|
-
local default_base=$(jq -r ".aliases[
|
|
51
|
+
local repo=$(jq -r --arg a "$alias" '.aliases[$a].repo // empty' "$PSM_PROJECTS")
|
|
52
|
+
local local_path=$(jq -r --arg a "$alias" '.aliases[$a].local // empty' "$PSM_PROJECTS")
|
|
53
|
+
local default_base=$(jq -r --arg a "$alias" '.aliases[$a].default_base // "main"' "$PSM_PROJECTS")
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
local clone_url=$(jq -r --arg a "$alias" '.aliases[$a].clone_url // empty' "$PSM_PROJECTS")
|
|
56
|
+
if [[ -z "$repo" && -z "$clone_url" ]]; then
|
|
56
57
|
return 1
|
|
57
58
|
fi
|
|
58
59
|
|
|
@@ -62,6 +63,53 @@ psm_get_project() {
|
|
|
62
63
|
echo "${repo}|${local_path}|${default_base}"
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
# Get provider for a project alias
|
|
67
|
+
# Usage: psm_get_project_provider "mywork"
|
|
68
|
+
# Returns: "github" | "jira" | empty (defaults to github)
|
|
69
|
+
psm_get_project_provider() {
|
|
70
|
+
local alias="$1"
|
|
71
|
+
if [[ ! -f "$PSM_PROJECTS" ]]; then
|
|
72
|
+
echo "github"
|
|
73
|
+
return
|
|
74
|
+
fi
|
|
75
|
+
local provider
|
|
76
|
+
provider=$(jq -r --arg a "$alias" '.aliases[$a].provider // "github"' "$PSM_PROJECTS")
|
|
77
|
+
echo "$provider"
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
# Get Jira project key for alias
|
|
81
|
+
# Usage: psm_get_project_jira_project "mywork"
|
|
82
|
+
# Returns: "MYPROJ" or empty
|
|
83
|
+
psm_get_project_jira_project() {
|
|
84
|
+
local alias="$1"
|
|
85
|
+
if [[ ! -f "$PSM_PROJECTS" ]]; then
|
|
86
|
+
return
|
|
87
|
+
fi
|
|
88
|
+
jq -r --arg a "$alias" '.aliases[$a].jira_project // empty' "$PSM_PROJECTS"
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# Get explicit clone_url for alias (for non-GitHub repos)
|
|
92
|
+
# Usage: psm_get_project_clone_url "mywork"
|
|
93
|
+
# Returns: URL or empty
|
|
94
|
+
psm_get_project_clone_url() {
|
|
95
|
+
local alias="$1"
|
|
96
|
+
if [[ ! -f "$PSM_PROJECTS" ]]; then
|
|
97
|
+
return
|
|
98
|
+
fi
|
|
99
|
+
jq -r --arg a "$alias" '.aliases[$a].clone_url // empty' "$PSM_PROJECTS"
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# Get repo field for alias
|
|
103
|
+
# Usage: psm_get_project_repo "mywork"
|
|
104
|
+
# Returns: "owner/repo" or empty
|
|
105
|
+
psm_get_project_repo() {
|
|
106
|
+
local alias="$1"
|
|
107
|
+
if [[ ! -f "$PSM_PROJECTS" ]]; then
|
|
108
|
+
return
|
|
109
|
+
fi
|
|
110
|
+
jq -r --arg a "$alias" '.aliases[$a].repo // empty' "$PSM_PROJECTS"
|
|
111
|
+
}
|
|
112
|
+
|
|
65
113
|
# Add or update project alias
|
|
66
114
|
psm_set_project() {
|
|
67
115
|
local alias="$1"
|
|
@@ -70,7 +118,8 @@ psm_set_project() {
|
|
|
70
118
|
local default_base="${4:-main}"
|
|
71
119
|
|
|
72
120
|
local tmp=$(mktemp)
|
|
73
|
-
jq "
|
|
121
|
+
jq --arg a "$alias" --arg r "$repo" --arg l "$local_path" --arg b "$default_base" \
|
|
122
|
+
'.aliases[$a] = {"repo": $r, "local": $l, "default_base": $b}' \
|
|
74
123
|
"$PSM_PROJECTS" > "$tmp" && mv "$tmp" "$PSM_PROJECTS"
|
|
75
124
|
}
|
|
76
125
|
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
# #123 -> number=123 (use current repo)
|
|
10
10
|
#
|
|
11
11
|
# Usage: psm_parse_ref "omc#123"
|
|
12
|
-
# Returns: type|alias|repo|number|local_path|base
|
|
12
|
+
# Returns: type|alias|repo|number|local_path|base|provider|provider_ref
|
|
13
13
|
psm_parse_ref() {
|
|
14
14
|
local ref="$1"
|
|
15
15
|
local type=""
|
|
@@ -29,7 +29,7 @@ psm_parse_ref() {
|
|
|
29
29
|
if [[ -n "$alias" ]]; then
|
|
30
30
|
IFS='|' read -r _ local_path base <<< "$(psm_get_project "$alias")"
|
|
31
31
|
fi
|
|
32
|
-
echo "pr|${alias:-}|$repo|$number|${local_path:-}|$base"
|
|
32
|
+
echo "pr|${alias:-}|$repo|$number|${local_path:-}|$base|github|${repo}#${number}"
|
|
33
33
|
return 0
|
|
34
34
|
fi
|
|
35
35
|
|
|
@@ -42,11 +42,24 @@ psm_parse_ref() {
|
|
|
42
42
|
if [[ -n "$alias" ]]; then
|
|
43
43
|
IFS='|' read -r _ local_path base <<< "$(psm_get_project "$alias")"
|
|
44
44
|
fi
|
|
45
|
-
echo "issue|${alias:-}|$repo|$number|${local_path:-}|$base"
|
|
45
|
+
echo "issue|${alias:-}|$repo|$number|${local_path:-}|$base|github|${repo}#${number}"
|
|
46
46
|
return 0
|
|
47
47
|
fi
|
|
48
48
|
|
|
49
|
-
#
|
|
49
|
+
# Jira direct reference (PROJ-123) - config-validated
|
|
50
|
+
local jira_info
|
|
51
|
+
if jira_info=$(psm_detect_jira_key "$ref"); then
|
|
52
|
+
IFS='|' read -r alias project_key issue_number <<< "$jira_info"
|
|
53
|
+
local project_info
|
|
54
|
+
project_info=$(psm_get_project "$alias")
|
|
55
|
+
if [[ $? -eq 0 ]]; then
|
|
56
|
+
IFS='|' read -r repo local_path base <<< "$project_info"
|
|
57
|
+
echo "issue|${alias}|${repo}|${issue_number}|${local_path}|${base}|jira|${project_key}-${issue_number}"
|
|
58
|
+
return 0
|
|
59
|
+
fi
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# alias#number format (e.g., omc#123 or mywork#123)
|
|
50
63
|
if [[ "$ref" =~ ^([a-zA-Z][a-zA-Z0-9_-]*)#([0-9]+)$ ]]; then
|
|
51
64
|
alias="${BASH_REMATCH[1]}"
|
|
52
65
|
number="${BASH_REMATCH[2]}"
|
|
@@ -55,11 +68,22 @@ psm_parse_ref() {
|
|
|
55
68
|
project_info=$(psm_get_project "$alias")
|
|
56
69
|
if [[ $? -eq 0 ]]; then
|
|
57
70
|
IFS='|' read -r repo local_path base <<< "$project_info"
|
|
58
|
-
|
|
59
|
-
|
|
71
|
+
local provider
|
|
72
|
+
provider=$(psm_get_project_provider "$alias")
|
|
73
|
+
local provider_ref=""
|
|
74
|
+
|
|
75
|
+
if [[ "$provider" == "jira" ]]; then
|
|
76
|
+
local jira_proj
|
|
77
|
+
jira_proj=$(psm_get_project_jira_project "$alias")
|
|
78
|
+
provider_ref="${jira_proj}-${number}"
|
|
79
|
+
else
|
|
80
|
+
provider_ref="${repo}#${number}"
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
echo "ref|$alias|$repo|$number|$local_path|$base|$provider|$provider_ref"
|
|
60
84
|
return 0
|
|
61
85
|
else
|
|
62
|
-
echo "error|Unknown project alias: $alias
|
|
86
|
+
echo "error|Unknown project alias: $alias|||||||"
|
|
63
87
|
return 1
|
|
64
88
|
fi
|
|
65
89
|
fi
|
|
@@ -72,7 +96,7 @@ psm_parse_ref() {
|
|
|
72
96
|
if [[ -n "$alias" ]]; then
|
|
73
97
|
IFS='|' read -r _ local_path base <<< "$(psm_get_project "$alias")"
|
|
74
98
|
fi
|
|
75
|
-
echo "ref|${alias:-}|$repo|$number|${local_path:-}|$base"
|
|
99
|
+
echo "ref|${alias:-}|$repo|$number|${local_path:-}|$base|github|${repo}#${number}"
|
|
76
100
|
return 0
|
|
77
101
|
fi
|
|
78
102
|
|
|
@@ -88,11 +112,11 @@ psm_parse_ref() {
|
|
|
88
112
|
alias=$(psm_find_alias_for_repo "$repo")
|
|
89
113
|
fi
|
|
90
114
|
fi
|
|
91
|
-
echo "ref|${alias:-}|${repo:-}|$number|${local_path:-}|$base"
|
|
115
|
+
echo "ref|${alias:-}|${repo:-}|$number|${local_path:-}|$base|github|${repo:+${repo}#${number}}"
|
|
92
116
|
return 0
|
|
93
117
|
fi
|
|
94
118
|
|
|
95
|
-
echo "error|Cannot parse reference: $ref
|
|
119
|
+
echo "error|Cannot parse reference: $ref||||||"
|
|
96
120
|
return 1
|
|
97
121
|
}
|
|
98
122
|
|
|
@@ -103,7 +127,7 @@ psm_find_alias_for_repo() {
|
|
|
103
127
|
return 1
|
|
104
128
|
fi
|
|
105
129
|
|
|
106
|
-
jq -r ".aliases | to_entries[] | select(.value.repo ==
|
|
130
|
+
jq -r --arg r "$target_repo" '.aliases | to_entries[] | select(.value.repo == $r) | .key' "$PSM_PROJECTS" | head -1
|
|
107
131
|
}
|
|
108
132
|
|
|
109
133
|
# Sanitize a string for use in filenames/session names
|
|
@@ -119,3 +143,33 @@ psm_slugify() {
|
|
|
119
143
|
local max_len="${2:-30}"
|
|
120
144
|
echo "$title" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//' | sed 's/-$//' | head -c "$max_len"
|
|
121
145
|
}
|
|
146
|
+
|
|
147
|
+
# Check if input matches a configured Jira project
|
|
148
|
+
# Usage: psm_detect_jira_key "PROJ-123"
|
|
149
|
+
# Returns: alias|project_key|issue_number OR exits 1
|
|
150
|
+
psm_detect_jira_key() {
|
|
151
|
+
local input="$1"
|
|
152
|
+
|
|
153
|
+
# Must match PROJ-123 pattern (uppercase project, dash, digits)
|
|
154
|
+
if [[ ! "$input" =~ ^([A-Z][A-Z0-9]*)-([0-9]+)$ ]]; then
|
|
155
|
+
return 1
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
local project_prefix="${BASH_REMATCH[1]}"
|
|
159
|
+
local issue_number="${BASH_REMATCH[2]}"
|
|
160
|
+
|
|
161
|
+
# Verify this project prefix exists in config
|
|
162
|
+
if [[ ! -f "$PSM_PROJECTS" ]]; then
|
|
163
|
+
return 1
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
local matching_alias
|
|
167
|
+
matching_alias=$(jq -r --arg p "$project_prefix" '.aliases | to_entries[] | select(.value.jira_project == $p) | .key' "$PSM_PROJECTS" | head -1)
|
|
168
|
+
|
|
169
|
+
if [[ -n "$matching_alias" ]]; then
|
|
170
|
+
echo "${matching_alias}|${project_prefix}|${issue_number}"
|
|
171
|
+
return 0
|
|
172
|
+
fi
|
|
173
|
+
|
|
174
|
+
return 1
|
|
175
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PSM GitHub Provider
|
|
3
|
+
|
|
4
|
+
provider_github_available() {
|
|
5
|
+
command -v gh &> /dev/null
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
provider_github_detect_ref() {
|
|
9
|
+
local ref="$1"
|
|
10
|
+
# Matches github URLs or owner/repo#num patterns
|
|
11
|
+
[[ "$ref" =~ ^https://github\.com/ ]] || [[ "$ref" =~ ^[a-zA-Z0-9_-]+/[a-zA-Z0-9_.-]+#[0-9]+$ ]]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
provider_github_fetch_pr() {
|
|
15
|
+
local pr_number="$1"
|
|
16
|
+
local repo="$2"
|
|
17
|
+
gh pr view "$pr_number" --repo "$repo" --json number,title,author,headRefName,baseRefName,body,url 2>/dev/null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
provider_github_fetch_issue() {
|
|
21
|
+
local issue_number="$1"
|
|
22
|
+
local repo="$2"
|
|
23
|
+
gh issue view "$issue_number" --repo "$repo" --json number,title,body,labels,url 2>/dev/null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
provider_github_pr_merged() {
|
|
27
|
+
local pr_number="$1"
|
|
28
|
+
local repo="$2"
|
|
29
|
+
local merged
|
|
30
|
+
merged=$(gh pr view "$pr_number" --repo "$repo" --json merged 2>/dev/null | jq -r '.merged')
|
|
31
|
+
[[ "$merged" == "true" ]]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
provider_github_issue_closed() {
|
|
35
|
+
local issue_number="$1"
|
|
36
|
+
local repo="$2"
|
|
37
|
+
local closed
|
|
38
|
+
closed=$(gh issue view "$issue_number" --repo "$repo" --json closed 2>/dev/null | jq -r '.closed')
|
|
39
|
+
[[ "$closed" == "true" ]]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
provider_github_clone_url() {
|
|
43
|
+
local repo="$1"
|
|
44
|
+
|
|
45
|
+
# Validate owner/repo format
|
|
46
|
+
if [[ ! "$repo" =~ ^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$ ]]; then
|
|
47
|
+
echo "error|Invalid repository format: $repo" >&2
|
|
48
|
+
return 1
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
echo "https://github.com/${repo}.git"
|
|
52
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PSM Provider Interface
|
|
3
|
+
# Each provider implements: _available, _detect_ref, _fetch_issue, _issue_closed,
|
|
4
|
+
# _fetch_pr (optional), _pr_merged (optional), _clone_url
|
|
5
|
+
|
|
6
|
+
# List available providers
|
|
7
|
+
provider_list() {
|
|
8
|
+
echo "github jira"
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
# Allowlist of valid providers
|
|
12
|
+
readonly VALID_PROVIDERS="github jira"
|
|
13
|
+
|
|
14
|
+
# Check if a provider is available (CLI installed)
|
|
15
|
+
# Usage: provider_available "github"
|
|
16
|
+
provider_available() {
|
|
17
|
+
local provider="$1"
|
|
18
|
+
|
|
19
|
+
# Validate provider against allowlist
|
|
20
|
+
if ! echo "$VALID_PROVIDERS" | grep -qw "$provider"; then
|
|
21
|
+
echo "error|Invalid provider: $provider" >&2
|
|
22
|
+
return 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
"provider_${provider}_available"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# Dispatch to provider function
|
|
29
|
+
# Usage: provider_call "github" "fetch_issue" "123" "owner/repo"
|
|
30
|
+
provider_call() {
|
|
31
|
+
local provider="$1"
|
|
32
|
+
local func="$2"
|
|
33
|
+
shift 2
|
|
34
|
+
|
|
35
|
+
# Validate provider against allowlist
|
|
36
|
+
if ! echo "$VALID_PROVIDERS" | grep -qw "$provider"; then
|
|
37
|
+
echo "error|Invalid provider: $provider" >&2
|
|
38
|
+
return 1
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Validate function name (alphanumeric and underscore only)
|
|
42
|
+
if [[ ! "$func" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]]; then
|
|
43
|
+
echo "error|Invalid function name: $func" >&2
|
|
44
|
+
return 1
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
"provider_${provider}_${func}" "$@"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Detect provider from reference (with config validation)
|
|
51
|
+
# Usage: provider_detect_from_ref "PROJ-123"
|
|
52
|
+
# Returns: provider name or empty
|
|
53
|
+
provider_detect_from_ref() {
|
|
54
|
+
local ref="$1"
|
|
55
|
+
|
|
56
|
+
# Check Jira pattern first (config-validated)
|
|
57
|
+
if psm_detect_jira_key "$ref" >/dev/null 2>&1; then
|
|
58
|
+
echo "jira"
|
|
59
|
+
return 0
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# GitHub URL patterns
|
|
63
|
+
if [[ "$ref" =~ ^https://github\.com/ ]]; then
|
|
64
|
+
echo "github"
|
|
65
|
+
return 0
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# owner/repo#num pattern -> GitHub
|
|
69
|
+
if [[ "$ref" =~ ^[a-zA-Z0-9_-]+/[a-zA-Z0-9_.-]+#[0-9]+$ ]]; then
|
|
70
|
+
echo "github"
|
|
71
|
+
return 0
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
# Default
|
|
75
|
+
echo "github"
|
|
76
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# PSM Jira Provider
|
|
3
|
+
# Uses `jira` CLI (https://github.com/ankitpokhrel/jira-cli)
|
|
4
|
+
|
|
5
|
+
provider_jira_available() {
|
|
6
|
+
command -v jira &> /dev/null
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
provider_jira_detect_ref() {
|
|
10
|
+
local ref="$1"
|
|
11
|
+
# Config-validated detection only
|
|
12
|
+
psm_detect_jira_key "$ref" >/dev/null 2>&1
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
provider_jira_fetch_issue() {
|
|
16
|
+
local issue_key="$1" # e.g., "PROJ-123"
|
|
17
|
+
# Note: second arg (repo) is ignored for Jira
|
|
18
|
+
jira issue view "$issue_key" --output json 2>/dev/null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
provider_jira_issue_closed() {
|
|
22
|
+
local issue_key="$1"
|
|
23
|
+
local status_category
|
|
24
|
+
status_category=$(jira issue view "$issue_key" --output json 2>/dev/null | jq -r '.fields.status.statusCategory.key')
|
|
25
|
+
# Jira status categories: "new", "indeterminate", "done"
|
|
26
|
+
[[ "$status_category" == "done" ]]
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# Jira has no PRs - return error
|
|
30
|
+
provider_jira_fetch_pr() {
|
|
31
|
+
echo '{"error": "Jira does not support pull requests"}' >&2
|
|
32
|
+
return 1
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
provider_jira_pr_merged() {
|
|
36
|
+
return 1 # Always false - Jira has no PRs
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
provider_jira_clone_url() {
|
|
40
|
+
local alias="$1"
|
|
41
|
+
# For Jira, we need to get clone_url from config
|
|
42
|
+
# First try explicit clone_url, then fall back to repo as GitHub
|
|
43
|
+
local clone_url
|
|
44
|
+
clone_url=$(psm_get_project_clone_url "$alias")
|
|
45
|
+
if [[ -n "$clone_url" ]]; then
|
|
46
|
+
echo "$clone_url"
|
|
47
|
+
return 0
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
local repo
|
|
51
|
+
repo=$(psm_get_project_repo "$alias")
|
|
52
|
+
if [[ -n "$repo" ]]; then
|
|
53
|
+
echo "https://github.com/${repo}.git"
|
|
54
|
+
return 0
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
echo "error: No clone_url or repo configured for alias '$alias'" >&2
|
|
58
|
+
return 1
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# Parse Jira reference into components
|
|
62
|
+
# Input: "PROJ-123" or "mywork#123"
|
|
63
|
+
# Output: Extended format for session creation
|
|
64
|
+
provider_jira_parse_ref() {
|
|
65
|
+
local ref="$1"
|
|
66
|
+
local jira_info
|
|
67
|
+
|
|
68
|
+
# Try direct PROJ-123 pattern
|
|
69
|
+
if jira_info=$(psm_detect_jira_key "$ref"); then
|
|
70
|
+
IFS='|' read -r alias project_key issue_number <<< "$jira_info"
|
|
71
|
+
local project_info
|
|
72
|
+
project_info=$(psm_get_project "$alias")
|
|
73
|
+
IFS='|' read -r repo local_path base <<< "$project_info"
|
|
74
|
+
echo "issue|${alias}|${repo}|${issue_number}|${local_path}|${base}|jira|${project_key}-${issue_number}"
|
|
75
|
+
return 0
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
return 1
|
|
79
|
+
}
|