oh-my-claude-sisyphus 3.8.15 → 3.9.0

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.
Files changed (259) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.mcp.json +1 -1
  4. package/README.md +9 -11
  5. package/agents/analyst.md +41 -0
  6. package/agents/architect.md +45 -0
  7. package/agents/critic.md +42 -0
  8. package/agents/deep-executor.md +193 -0
  9. package/agents/planner.md +82 -0
  10. package/bridge/mcp-server.cjs +1 -1
  11. package/commands/autopilot.md +2 -6
  12. package/commands/hud.md +7 -2
  13. package/commands/ralph.md +3 -3
  14. package/commands/ultrapilot.md +2 -6
  15. package/dist/__tests__/agent-registry.test.js +1 -1
  16. package/dist/__tests__/delegation-enforcement-levels.test.js +0 -1
  17. package/dist/__tests__/delegation-enforcement-levels.test.js.map +1 -1
  18. package/dist/__tests__/hooks/learner/parser.test.d.ts +5 -0
  19. package/dist/__tests__/hooks/learner/parser.test.d.ts.map +1 -0
  20. package/dist/__tests__/hooks/learner/parser.test.js +201 -0
  21. package/dist/__tests__/hooks/learner/parser.test.js.map +1 -0
  22. package/dist/__tests__/hud/cwd.test.d.ts +2 -0
  23. package/dist/__tests__/hud/cwd.test.d.ts.map +1 -0
  24. package/dist/__tests__/hud/cwd.test.js +62 -0
  25. package/dist/__tests__/hud/cwd.test.js.map +1 -0
  26. package/dist/__tests__/hud/defaults.test.d.ts +2 -0
  27. package/dist/__tests__/hud/defaults.test.d.ts.map +1 -0
  28. package/dist/__tests__/hud/defaults.test.js +21 -0
  29. package/dist/__tests__/hud/defaults.test.js.map +1 -0
  30. package/dist/__tests__/hud/render.test.d.ts +2 -0
  31. package/dist/__tests__/hud/render.test.d.ts.map +1 -0
  32. package/dist/__tests__/hud/render.test.js +141 -0
  33. package/dist/__tests__/hud/render.test.js.map +1 -0
  34. package/dist/__tests__/hud/thinking.test.d.ts +2 -0
  35. package/dist/__tests__/hud/thinking.test.d.ts.map +1 -0
  36. package/dist/__tests__/hud/thinking.test.js +32 -0
  37. package/dist/__tests__/hud/thinking.test.js.map +1 -0
  38. package/dist/__tests__/installer.test.js +8 -8
  39. package/dist/__tests__/installer.test.js.map +1 -1
  40. package/dist/__tests__/mnemosyne/parser.test.js +1 -1
  41. package/dist/__tests__/mnemosyne/parser.test.js.map +1 -1
  42. package/dist/__tests__/omc-tools-server.test.js +2 -2
  43. package/dist/__tests__/omc-tools-server.test.js.map +1 -1
  44. package/dist/__tests__/skills.test.js +5 -4
  45. package/dist/__tests__/skills.test.js.map +1 -1
  46. package/dist/agents/deep-executor.d.ts +15 -0
  47. package/dist/agents/deep-executor.d.ts.map +1 -0
  48. package/dist/agents/deep-executor.js +47 -0
  49. package/dist/agents/deep-executor.js.map +1 -0
  50. package/dist/agents/definitions.d.ts +15 -0
  51. package/dist/agents/definitions.d.ts.map +1 -1
  52. package/dist/agents/definitions.js +25 -0
  53. package/dist/agents/definitions.js.map +1 -1
  54. package/dist/agents/index.d.ts +1 -0
  55. package/dist/agents/index.d.ts.map +1 -1
  56. package/dist/agents/index.js +1 -0
  57. package/dist/agents/index.js.map +1 -1
  58. package/dist/cli/commands/doctor-conflicts.d.ts +55 -0
  59. package/dist/cli/commands/doctor-conflicts.d.ts.map +1 -0
  60. package/dist/cli/commands/doctor-conflicts.js +261 -0
  61. package/dist/cli/commands/doctor-conflicts.js.map +1 -0
  62. package/dist/cli/index.js +16 -1
  63. package/dist/cli/index.js.map +1 -1
  64. package/dist/features/auto-update.d.ts +12 -0
  65. package/dist/features/auto-update.d.ts.map +1 -1
  66. package/dist/features/auto-update.js +4 -1
  67. package/dist/features/auto-update.js.map +1 -1
  68. package/dist/features/context-injector/types.d.ts +1 -1
  69. package/dist/features/context-injector/types.d.ts.map +1 -1
  70. package/dist/features/continuation-enforcement.js +1 -1
  71. package/dist/features/state-manager/index.d.ts.map +1 -1
  72. package/dist/features/state-manager/index.js +7 -4
  73. package/dist/features/state-manager/index.js.map +1 -1
  74. package/dist/features/verification/example.d.ts.map +1 -1
  75. package/dist/features/verification/example.js +4 -2
  76. package/dist/features/verification/example.js.map +1 -1
  77. package/dist/hooks/__tests__/bridge.test.d.ts +2 -0
  78. package/dist/hooks/__tests__/bridge.test.d.ts.map +1 -0
  79. package/dist/hooks/__tests__/bridge.test.js +199 -0
  80. package/dist/hooks/__tests__/bridge.test.js.map +1 -0
  81. package/dist/hooks/beads-context/__tests__/index.test.d.ts +2 -0
  82. package/dist/hooks/beads-context/__tests__/index.test.d.ts.map +1 -0
  83. package/dist/hooks/beads-context/__tests__/index.test.js +150 -0
  84. package/dist/hooks/beads-context/__tests__/index.test.js.map +1 -0
  85. package/dist/hooks/beads-context/constants.d.ts +3 -0
  86. package/dist/hooks/beads-context/constants.d.ts.map +1 -0
  87. package/dist/hooks/beads-context/constants.js +35 -0
  88. package/dist/hooks/beads-context/constants.js.map +1 -0
  89. package/dist/hooks/beads-context/index.d.ts +21 -0
  90. package/dist/hooks/beads-context/index.d.ts.map +1 -0
  91. package/dist/hooks/beads-context/index.js +62 -0
  92. package/dist/hooks/beads-context/index.js.map +1 -0
  93. package/dist/hooks/beads-context/types.d.ts +7 -0
  94. package/dist/hooks/beads-context/types.d.ts.map +1 -0
  95. package/dist/hooks/beads-context/types.js +2 -0
  96. package/dist/hooks/beads-context/types.js.map +1 -0
  97. package/dist/hooks/bridge.d.ts +4 -0
  98. package/dist/hooks/bridge.d.ts.map +1 -1
  99. package/dist/hooks/bridge.js +80 -47
  100. package/dist/hooks/bridge.js.map +1 -1
  101. package/dist/hooks/index.d.ts +2 -1
  102. package/dist/hooks/index.d.ts.map +1 -1
  103. package/dist/hooks/index.js +4 -1
  104. package/dist/hooks/index.js.map +1 -1
  105. package/dist/hooks/learner/parser.d.ts.map +1 -1
  106. package/dist/hooks/learner/parser.js +12 -5
  107. package/dist/hooks/learner/parser.js.map +1 -1
  108. package/dist/hooks/mode-registry/index.d.ts +2 -0
  109. package/dist/hooks/mode-registry/index.d.ts.map +1 -1
  110. package/dist/hooks/mode-registry/index.js +8 -19
  111. package/dist/hooks/mode-registry/index.js.map +1 -1
  112. package/dist/hooks/permission-handler/index.d.ts.map +1 -1
  113. package/dist/hooks/permission-handler/index.js +3 -1
  114. package/dist/hooks/permission-handler/index.js.map +1 -1
  115. package/dist/hooks/persistent-mode/index.d.ts +1 -1
  116. package/dist/hooks/persistent-mode/index.d.ts.map +1 -1
  117. package/dist/hooks/persistent-mode/index.js +5 -33
  118. package/dist/hooks/persistent-mode/index.js.map +1 -1
  119. package/dist/hooks/ralph/index.d.ts +1 -1
  120. package/dist/hooks/ralph/index.d.ts.map +1 -1
  121. package/dist/hooks/ralph/index.js +1 -1
  122. package/dist/hooks/ralph/index.js.map +1 -1
  123. package/dist/hooks/ralph/loop.d.ts +1 -9
  124. package/dist/hooks/ralph/loop.d.ts.map +1 -1
  125. package/dist/hooks/ralph/loop.js +1 -37
  126. package/dist/hooks/ralph/loop.js.map +1 -1
  127. package/dist/hooks/ralph/prd.js +1 -1
  128. package/dist/hooks/ralph/verifier.d.ts +4 -5
  129. package/dist/hooks/ralph/verifier.d.ts.map +1 -1
  130. package/dist/hooks/ralph/verifier.js +7 -10
  131. package/dist/hooks/ralph/verifier.js.map +1 -1
  132. package/dist/hooks/session-end/index.d.ts +13 -0
  133. package/dist/hooks/session-end/index.d.ts.map +1 -1
  134. package/dist/hooks/session-end/index.js +69 -0
  135. package/dist/hooks/session-end/index.js.map +1 -1
  136. package/dist/hooks/setup/index.d.ts.map +1 -1
  137. package/dist/hooks/setup/index.js +12 -5
  138. package/dist/hooks/setup/index.js.map +1 -1
  139. package/dist/hooks/subagent-tracker/index.d.ts.map +1 -1
  140. package/dist/hooks/subagent-tracker/index.js +25 -9
  141. package/dist/hooks/subagent-tracker/index.js.map +1 -1
  142. package/dist/hooks/ultrawork/index.d.ts +2 -2
  143. package/dist/hooks/ultrawork/index.d.ts.map +1 -1
  144. package/dist/hooks/ultrawork/index.js +2 -46
  145. package/dist/hooks/ultrawork/index.js.map +1 -1
  146. package/dist/hud/elements/cwd.d.ts +15 -0
  147. package/dist/hud/elements/cwd.d.ts.map +1 -0
  148. package/dist/hud/elements/cwd.js +39 -0
  149. package/dist/hud/elements/cwd.js.map +1 -0
  150. package/dist/hud/elements/index.d.ts +1 -0
  151. package/dist/hud/elements/index.d.ts.map +1 -1
  152. package/dist/hud/elements/index.js +1 -0
  153. package/dist/hud/elements/index.js.map +1 -1
  154. package/dist/hud/elements/thinking.d.ts +7 -5
  155. package/dist/hud/elements/thinking.d.ts.map +1 -1
  156. package/dist/hud/elements/thinking.js +18 -6
  157. package/dist/hud/elements/thinking.js.map +1 -1
  158. package/dist/hud/index.js +5 -3
  159. package/dist/hud/index.js.map +1 -1
  160. package/dist/hud/omc-state.d.ts +1 -1
  161. package/dist/hud/omc-state.d.ts.map +1 -1
  162. package/dist/hud/omc-state.js +14 -31
  163. package/dist/hud/omc-state.js.map +1 -1
  164. package/dist/hud/render.d.ts +9 -0
  165. package/dist/hud/render.d.ts.map +1 -1
  166. package/dist/hud/render.js +27 -7
  167. package/dist/hud/render.js.map +1 -1
  168. package/dist/hud/state.d.ts +2 -2
  169. package/dist/hud/state.d.ts.map +1 -1
  170. package/dist/hud/state.js +4 -33
  171. package/dist/hud/state.js.map +1 -1
  172. package/dist/hud/transcript.d.ts +4 -1
  173. package/dist/hud/transcript.d.ts.map +1 -1
  174. package/dist/hud/transcript.js +4 -9
  175. package/dist/hud/transcript.js.map +1 -1
  176. package/dist/hud/types.d.ts +20 -1
  177. package/dist/hud/types.d.ts.map +1 -1
  178. package/dist/hud/types.js +38 -9
  179. package/dist/hud/types.js.map +1 -1
  180. package/dist/index.js +1 -1
  181. package/dist/index.js.map +1 -1
  182. package/dist/installer/__tests__/claude-md-merge.test.d.ts +6 -0
  183. package/dist/installer/__tests__/claude-md-merge.test.d.ts.map +1 -0
  184. package/dist/installer/__tests__/claude-md-merge.test.js +220 -0
  185. package/dist/installer/__tests__/claude-md-merge.test.js.map +1 -0
  186. package/dist/installer/__tests__/safe-installer.test.d.ts +6 -0
  187. package/dist/installer/__tests__/safe-installer.test.d.ts.map +1 -0
  188. package/dist/installer/__tests__/safe-installer.test.js +172 -0
  189. package/dist/installer/__tests__/safe-installer.test.js.map +1 -0
  190. package/dist/installer/hooks.d.ts +1 -1
  191. package/dist/installer/hooks.d.ts.map +1 -1
  192. package/dist/installer/hooks.js +4 -2
  193. package/dist/installer/hooks.js.map +1 -1
  194. package/dist/installer/index.d.ts +27 -1
  195. package/dist/installer/index.d.ts.map +1 -1
  196. package/dist/installer/index.js +209 -85
  197. package/dist/installer/index.js.map +1 -1
  198. package/dist/mcp/omc-tools-server.d.ts +1 -1
  199. package/dist/mcp/omc-tools-server.d.ts.map +1 -1
  200. package/dist/mcp/omc-tools-server.js +3 -3
  201. package/dist/mcp/omc-tools-server.js.map +1 -1
  202. package/dist/mcp/standalone-server.js +1 -1
  203. package/dist/mcp/standalone-server.js.map +1 -1
  204. package/dist/verification/tier-selector.d.ts +40 -0
  205. package/dist/verification/tier-selector.d.ts.map +1 -0
  206. package/dist/verification/tier-selector.js +95 -0
  207. package/dist/verification/tier-selector.js.map +1 -0
  208. package/dist/verification/tier-selector.test.d.ts +2 -0
  209. package/dist/verification/tier-selector.test.d.ts.map +1 -0
  210. package/dist/verification/tier-selector.test.js +282 -0
  211. package/dist/verification/tier-selector.test.js.map +1 -0
  212. package/docs/AGENTS.md +100 -0
  213. package/docs/ARCHITECTURE.md +11 -7
  214. package/docs/CLAUDE.md +89 -379
  215. package/docs/DELEGATION-ENFORCER.md +1 -2
  216. package/docs/MIGRATION.md +1 -1
  217. package/docs/REFERENCE.md +29 -9
  218. package/docs/SYNC-SYSTEM.md +0 -2
  219. package/docs/partials/agent-tiers.md +165 -0
  220. package/docs/partials/features.md +131 -0
  221. package/docs/partials/mode-hierarchy.md +120 -0
  222. package/docs/partials/mode-selection-guide.md +82 -0
  223. package/docs/partials/verification-tiers.md +107 -0
  224. package/docs/shared/agent-tiers.md +165 -0
  225. package/docs/shared/features.md +131 -0
  226. package/docs/shared/mode-hierarchy.md +120 -0
  227. package/docs/shared/mode-selection-guide.md +82 -0
  228. package/docs/shared/verification-tiers.md +107 -0
  229. package/package.json +4 -3
  230. package/scripts/compose-docs.mjs +44 -0
  231. package/scripts/keyword-detector.mjs +13 -3
  232. package/scripts/persistent-mode.mjs +78 -47
  233. package/scripts/test-mutual-exclusion.ts +3 -3
  234. package/skills/AGENTS.md +59 -44
  235. package/skills/autopilot/SKILL.md +0 -2
  236. package/skills/cancel/SKILL.md +13 -32
  237. package/skills/deep-executor/SKILL.md +50 -0
  238. package/skills/ecomode/SKILL.md +58 -104
  239. package/skills/hud/SKILL.md +3 -2
  240. package/skills/omc-setup/SKILL.md +197 -20
  241. package/skills/plan/SKILL.md +62 -0
  242. package/skills/project-session-manager/SKILL.md +87 -4
  243. package/skills/project-session-manager/lib/config.sh +54 -5
  244. package/skills/project-session-manager/lib/parse.sh +65 -11
  245. package/skills/project-session-manager/lib/providers/github.sh +52 -0
  246. package/skills/project-session-manager/lib/providers/interface.sh +76 -0
  247. package/skills/project-session-manager/lib/providers/jira.sh +79 -0
  248. package/skills/project-session-manager/lib/session.sh +49 -12
  249. package/skills/project-session-manager/lib/worktree.sh +37 -4
  250. package/skills/project-session-manager/psm.sh +116 -51
  251. package/skills/ralph/SKILL.md +48 -44
  252. package/skills/ultrawork/SKILL.md +56 -67
  253. package/templates/hooks/keyword-detector.mjs +21 -13
  254. package/templates/hooks/lib/stdin.mjs +62 -0
  255. package/templates/hooks/persistent-mode.mjs +75 -34
  256. package/templates/hooks/post-tool-use.mjs +8 -10
  257. package/templates/hooks/pre-tool-use.mjs +9 -6
  258. package/templates/hooks/session-start.mjs +7 -8
  259. package/agents/AGENTS.md +0 -144
@@ -146,23 +146,81 @@ mkdir -p .claude && echo ".claude directory ready"
146
146
  ### Download Fresh CLAUDE.md
147
147
 
148
148
  ```bash
149
+ # Define target path
150
+ TARGET_PATH=".claude/CLAUDE.md"
151
+
149
152
  # Extract old version before download
150
- OLD_VERSION=$(grep -m1 "^# oh-my-claudecode" .claude/CLAUDE.md 2>/dev/null | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' || echo "none")
153
+ OLD_VERSION=$(grep -m1 "^# oh-my-claudecode" "$TARGET_PATH" 2>/dev/null | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' || echo "none")
151
154
 
152
- # Backup existing CLAUDE.md before overwriting (if it exists)
153
- if [ -f ".claude/CLAUDE.md" ]; then
154
- BACKUP_DATE=$(date +%Y-%m-%d)
155
- BACKUP_PATH=".claude/CLAUDE.md.backup.${BACKUP_DATE}"
156
- cp .claude/CLAUDE.md "$BACKUP_PATH"
155
+ # Backup existing
156
+ if [ -f "$TARGET_PATH" ]; then
157
+ BACKUP_DATE=$(date +%Y-%m-%d_%H%M%S)
158
+ BACKUP_PATH="${TARGET_PATH}.backup.${BACKUP_DATE}"
159
+ cp "$TARGET_PATH" "$BACKUP_PATH"
157
160
  echo "Backed up existing CLAUDE.md to $BACKUP_PATH"
158
161
  fi
159
162
 
160
- # Download fresh CLAUDE.md from GitHub
161
- curl -fsSL "https://raw.githubusercontent.com/Yeachan-Heo/oh-my-claudecode/main/docs/CLAUDE.md" -o .claude/CLAUDE.md && \
162
- echo "Downloaded CLAUDE.md to .claude/CLAUDE.md"
163
+ # Download fresh OMC content to temp file
164
+ TEMP_OMC=$(mktemp /tmp/omc-claude-XXXXXX.md)
165
+ trap 'rm -f "$TEMP_OMC"' EXIT
166
+ curl -fsSL "https://raw.githubusercontent.com/Yeachan-Heo/oh-my-claudecode/main/docs/CLAUDE.md" -o "$TEMP_OMC"
167
+
168
+ if [ ! -s "$TEMP_OMC" ]; then
169
+ echo "ERROR: Failed to download CLAUDE.md. Aborting."
170
+ rm -f "$TEMP_OMC"
171
+ return 1
172
+ fi
173
+
174
+ # Strip existing markers from downloaded content (idempotency)
175
+ if grep -q '<!-- OMC:START -->' "$TEMP_OMC"; then
176
+ # Extract content between markers
177
+ sed -n '/<!-- OMC:START -->/,/<!-- OMC:END -->/{//!p}' "$TEMP_OMC" > "${TEMP_OMC}.clean"
178
+ mv "${TEMP_OMC}.clean" "$TEMP_OMC"
179
+ fi
180
+
181
+ if [ ! -f "$TARGET_PATH" ]; then
182
+ # Fresh install: wrap in markers
183
+ {
184
+ echo '<!-- OMC:START -->'
185
+ cat "$TEMP_OMC"
186
+ echo '<!-- OMC:END -->'
187
+ } > "$TARGET_PATH"
188
+ rm -f "$TEMP_OMC"
189
+ echo "Installed CLAUDE.md (fresh)"
190
+ else
191
+ # Merge: preserve user content outside OMC markers
192
+ if grep -q '<!-- OMC:START -->' "$TARGET_PATH"; then
193
+ # Has markers: replace OMC section, keep user content
194
+ BEFORE_OMC=$(sed -n '1,/<!-- OMC:START -->/{ /<!-- OMC:START -->/!p }' "$TARGET_PATH")
195
+ AFTER_OMC=$(sed -n '/<!-- OMC:END -->/,${ /<!-- OMC:END -->/!p }' "$TARGET_PATH")
196
+ {
197
+ [ -n "$BEFORE_OMC" ] && printf '%s\n' "$BEFORE_OMC"
198
+ echo '<!-- OMC:START -->'
199
+ cat "$TEMP_OMC"
200
+ echo '<!-- OMC:END -->'
201
+ [ -n "$AFTER_OMC" ] && printf '%s\n' "$AFTER_OMC"
202
+ } > "${TARGET_PATH}.tmp"
203
+ mv "${TARGET_PATH}.tmp" "$TARGET_PATH"
204
+ echo "Updated OMC section (user customizations preserved)"
205
+ else
206
+ # No markers: wrap new content in markers, append old content as user section
207
+ OLD_CONTENT=$(cat "$TARGET_PATH")
208
+ {
209
+ echo '<!-- OMC:START -->'
210
+ cat "$TEMP_OMC"
211
+ echo '<!-- OMC:END -->'
212
+ echo ""
213
+ echo "<!-- User customizations (migrated from previous CLAUDE.md) -->"
214
+ printf '%s\n' "$OLD_CONTENT"
215
+ } > "${TARGET_PATH}.tmp"
216
+ mv "${TARGET_PATH}.tmp" "$TARGET_PATH"
217
+ echo "Migrated existing CLAUDE.md (added OMC markers, preserved old content)"
218
+ fi
219
+ rm -f "$TEMP_OMC"
220
+ fi
163
221
 
164
222
  # Extract new version and report
165
- NEW_VERSION=$(grep -m1 "^# oh-my-claudecode" .claude/CLAUDE.md 2>/dev/null | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' || echo "unknown")
223
+ NEW_VERSION=$(grep -m1 "^# oh-my-claudecode" "$TARGET_PATH" 2>/dev/null | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' || echo "unknown")
166
224
  if [ "$OLD_VERSION" = "none" ]; then
167
225
  echo "Installed CLAUDE.md: $NEW_VERSION"
168
226
  elif [ "$OLD_VERSION" = "$NEW_VERSION" ]; then
@@ -227,23 +285,81 @@ Do not continue to HUD setup or other steps.
227
285
  ### Download Fresh CLAUDE.md
228
286
 
229
287
  ```bash
288
+ # Define target path
289
+ TARGET_PATH="$HOME/.claude/CLAUDE.md"
290
+
230
291
  # Extract old version before download
231
- OLD_VERSION=$(grep -m1 "^# oh-my-claudecode" ~/.claude/CLAUDE.md 2>/dev/null | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' || echo "none")
292
+ OLD_VERSION=$(grep -m1 "^# oh-my-claudecode" "$TARGET_PATH" 2>/dev/null | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' || echo "none")
232
293
 
233
- # Backup existing CLAUDE.md before overwriting (if it exists)
234
- if [ -f "$HOME/.claude/CLAUDE.md" ]; then
235
- BACKUP_DATE=$(date +%Y-%m-%d)
236
- BACKUP_PATH="$HOME/.claude/CLAUDE.md.backup.${BACKUP_DATE}"
237
- cp "$HOME/.claude/CLAUDE.md" "$BACKUP_PATH"
294
+ # Backup existing
295
+ if [ -f "$TARGET_PATH" ]; then
296
+ BACKUP_DATE=$(date +%Y-%m-%d_%H%M%S)
297
+ BACKUP_PATH="${TARGET_PATH}.backup.${BACKUP_DATE}"
298
+ cp "$TARGET_PATH" "$BACKUP_PATH"
238
299
  echo "Backed up existing CLAUDE.md to $BACKUP_PATH"
239
300
  fi
240
301
 
241
- # Download fresh CLAUDE.md to global config
242
- curl -fsSL "https://raw.githubusercontent.com/Yeachan-Heo/oh-my-claudecode/main/docs/CLAUDE.md" -o ~/.claude/CLAUDE.md && \
243
- echo "Downloaded CLAUDE.md to ~/.claude/CLAUDE.md"
302
+ # Download fresh OMC content to temp file
303
+ TEMP_OMC=$(mktemp /tmp/omc-claude-XXXXXX.md)
304
+ trap 'rm -f "$TEMP_OMC"' EXIT
305
+ curl -fsSL "https://raw.githubusercontent.com/Yeachan-Heo/oh-my-claudecode/main/docs/CLAUDE.md" -o "$TEMP_OMC"
306
+
307
+ if [ ! -s "$TEMP_OMC" ]; then
308
+ echo "ERROR: Failed to download CLAUDE.md. Aborting."
309
+ rm -f "$TEMP_OMC"
310
+ return 1
311
+ fi
312
+
313
+ # Strip existing markers from downloaded content (idempotency)
314
+ if grep -q '<!-- OMC:START -->' "$TEMP_OMC"; then
315
+ # Extract content between markers
316
+ sed -n '/<!-- OMC:START -->/,/<!-- OMC:END -->/{//!p}' "$TEMP_OMC" > "${TEMP_OMC}.clean"
317
+ mv "${TEMP_OMC}.clean" "$TEMP_OMC"
318
+ fi
319
+
320
+ if [ ! -f "$TARGET_PATH" ]; then
321
+ # Fresh install: wrap in markers
322
+ {
323
+ echo '<!-- OMC:START -->'
324
+ cat "$TEMP_OMC"
325
+ echo '<!-- OMC:END -->'
326
+ } > "$TARGET_PATH"
327
+ rm -f "$TEMP_OMC"
328
+ echo "Installed CLAUDE.md (fresh)"
329
+ else
330
+ # Merge: preserve user content outside OMC markers
331
+ if grep -q '<!-- OMC:START -->' "$TARGET_PATH"; then
332
+ # Has markers: replace OMC section, keep user content
333
+ BEFORE_OMC=$(sed -n '1,/<!-- OMC:START -->/{ /<!-- OMC:START -->/!p }' "$TARGET_PATH")
334
+ AFTER_OMC=$(sed -n '/<!-- OMC:END -->/,${ /<!-- OMC:END -->/!p }' "$TARGET_PATH")
335
+ {
336
+ [ -n "$BEFORE_OMC" ] && printf '%s\n' "$BEFORE_OMC"
337
+ echo '<!-- OMC:START -->'
338
+ cat "$TEMP_OMC"
339
+ echo '<!-- OMC:END -->'
340
+ [ -n "$AFTER_OMC" ] && printf '%s\n' "$AFTER_OMC"
341
+ } > "${TARGET_PATH}.tmp"
342
+ mv "${TARGET_PATH}.tmp" "$TARGET_PATH"
343
+ echo "Updated OMC section (user customizations preserved)"
344
+ else
345
+ # No markers: wrap new content in markers, append old content as user section
346
+ OLD_CONTENT=$(cat "$TARGET_PATH")
347
+ {
348
+ echo '<!-- OMC:START -->'
349
+ cat "$TEMP_OMC"
350
+ echo '<!-- OMC:END -->'
351
+ echo ""
352
+ echo "<!-- User customizations (migrated from previous CLAUDE.md) -->"
353
+ printf '%s\n' "$OLD_CONTENT"
354
+ } > "${TARGET_PATH}.tmp"
355
+ mv "${TARGET_PATH}.tmp" "$TARGET_PATH"
356
+ echo "Migrated existing CLAUDE.md (added OMC markers, preserved old content)"
357
+ fi
358
+ rm -f "$TEMP_OMC"
359
+ fi
244
360
 
245
361
  # Extract new version and report
246
- NEW_VERSION=$(grep -m1 "^# oh-my-claudecode" ~/.claude/CLAUDE.md 2>/dev/null | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' || echo "unknown")
362
+ NEW_VERSION=$(grep -m1 "^# oh-my-claudecode" "$TARGET_PATH" 2>/dev/null | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' || echo "unknown")
247
363
  if [ "$OLD_VERSION" = "none" ]; then
248
364
  echo "Installed CLAUDE.md: $NEW_VERSION"
249
365
  elif [ "$OLD_VERSION" = "$NEW_VERSION" ]; then
@@ -456,6 +572,67 @@ All functionality is available through the plugin system:
456
572
 
457
573
  Skip this step - the plugin provides all features.
458
574
 
575
+ ## Step 3.8.5: Select Task Management Tool
576
+
577
+ First, detect available task tools:
578
+
579
+ ```bash
580
+ # Detect beads (bd)
581
+ BD_VERSION=""
582
+ if command -v bd &>/dev/null; then
583
+ BD_VERSION=$(bd --version 2>/dev/null | head -1 || echo "installed")
584
+ fi
585
+
586
+ # Detect beads-rust (br)
587
+ BR_VERSION=""
588
+ if command -v br &>/dev/null; then
589
+ BR_VERSION=$(br --version 2>/dev/null | head -1 || echo "installed")
590
+ fi
591
+
592
+ # Report findings
593
+ if [ -n "$BD_VERSION" ]; then
594
+ echo "Found beads (bd): $BD_VERSION"
595
+ fi
596
+ if [ -n "$BR_VERSION" ]; then
597
+ echo "Found beads-rust (br): $BR_VERSION"
598
+ fi
599
+ if [ -z "$BD_VERSION" ] && [ -z "$BR_VERSION" ]; then
600
+ echo "No external task tools found. Using built-in Tasks."
601
+ fi
602
+ ```
603
+
604
+ If **neither** beads nor beads-rust is detected, skip this step (default to built-in).
605
+
606
+ If beads or beads-rust is detected, use AskUserQuestion:
607
+
608
+ **Question:** "Which task management tool should I use for tracking work?"
609
+
610
+ **Options:**
611
+ 1. **Built-in Tasks (default)** - Use Claude Code's native TaskCreate/TodoWrite. Tasks are session-only.
612
+ 2. **Beads (bd)** - Git-backed persistent tasks. Survives across sessions. [Only if detected]
613
+ 3. **Beads-Rust (br)** - Lightweight Rust port of beads. [Only if detected]
614
+
615
+ (Only show options 2/3 if the corresponding tool is detected)
616
+
617
+ Store the preference:
618
+
619
+ ```bash
620
+ CONFIG_FILE="$HOME/.claude/.omc-config.json"
621
+ mkdir -p "$(dirname "$CONFIG_FILE")"
622
+
623
+ if [ -f "$CONFIG_FILE" ]; then
624
+ EXISTING=$(cat "$CONFIG_FILE")
625
+ else
626
+ EXISTING='{}'
627
+ fi
628
+
629
+ # USER_CHOICE is "builtin", "beads", or "beads-rust" based on user selection
630
+ echo "$EXISTING" | jq --arg tool "USER_CHOICE" '. + {taskTool: $tool, taskToolConfig: {injectInstructions: true, useMcp: false}}' > "$CONFIG_FILE"
631
+ echo "Task tool set to: USER_CHOICE"
632
+ ```
633
+
634
+ **Note:** The beads context instructions will be injected automatically on the next session start. No restart is needed for config to take effect.
635
+
459
636
  ## Step 4: Verify Plugin Installation
460
637
 
461
638
  ```bash
@@ -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
- - `git` with worktree support (v2.5+)
375
- - `gh` CLI (authenticated)
376
- - `tmux`
377
- - `jq` for JSON parsing
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[\"$alias\"].repo // empty" "$PSM_PROJECTS")
52
- local local_path=$(jq -r ".aliases[\"$alias\"].local // empty" "$PSM_PROJECTS")
53
- local default_base=$(jq -r ".aliases[\"$alias\"].default_base // \"main\"" "$PSM_PROJECTS")
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
- if [[ -z "$repo" ]]; then
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 ".aliases[\"$alias\"] = {\"repo\": \"$repo\", \"local\": \"$local_path\", \"default_base\": \"$default_base\"}" \
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
- # alias#number format (e.g., omc#123)
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
- # Determine type from context (default to issue, caller specifies)
59
- echo "ref|$alias|$repo|$number|$local_path|$base"
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 == \"$target_repo\") | .key" "$PSM_PROJECTS" | head -1
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
+ }