@toolu/opencode 6.8.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 (331) hide show
  1. package/generated/GENERATED-NOTES.md +16 -0
  2. package/generated/agents/toolu--architect.md +52 -0
  3. package/generated/agents/toolu--deep-explore.md +65 -0
  4. package/generated/agents/toolu--implementer.md +50 -0
  5. package/generated/agents/toolu--quick-task.md +54 -0
  6. package/generated/agents/toolu--research-agent.md +101 -0
  7. package/generated/commands/toolu--commit.md +8 -0
  8. package/generated/commands/toolu--review-and-commit.md +8 -0
  9. package/generated/opencode.toolu.json +116 -0
  10. package/generated/skills/toolu--brainstorm/SKILL.md +57 -0
  11. package/generated/skills/toolu--brainstorm/references/design-questions.md +36 -0
  12. package/generated/skills/toolu--commit--skill/SKILL.md +11 -0
  13. package/generated/skills/toolu--debug/SKILL.md +54 -0
  14. package/generated/skills/toolu--deep-research/SKILL.md +51 -0
  15. package/generated/skills/toolu--execution/SKILL.md +77 -0
  16. package/generated/skills/toolu--orchestrator/SKILL.md +145 -0
  17. package/generated/skills/toolu--orchestrator/references/delegation-cost.md +104 -0
  18. package/generated/skills/toolu--orchestrator/references/model-routing.md +112 -0
  19. package/generated/skills/toolu--plan/SKILL.md +56 -0
  20. package/generated/skills/toolu--plan/references/ledger.md +18 -0
  21. package/generated/skills/toolu--plan-review/SKILL.md +48 -0
  22. package/generated/skills/toolu--review-and-commit--skill/SKILL.md +10 -0
  23. package/generated/skills/toolu--setup/SKILL.md +24 -0
  24. package/generated/skills/toolu--setup/scripts/setup.sh +206 -0
  25. package/generated/skills/toolu--spec/SKILL.md +67 -0
  26. package/generated/skills/toolu--spec-review/SKILL.md +50 -0
  27. package/generated/skills/toolu--test/SKILL.md +49 -0
  28. package/package.json +51 -0
  29. package/plugins/agent-browser/.claude-plugin/plugin.json +18 -0
  30. package/plugins/agent-browser/.codex-plugin/plugin.json +7 -0
  31. package/plugins/agent-browser/README.md +18 -0
  32. package/plugins/agent-browser/hooks/hooks.json +15 -0
  33. package/plugins/agent-browser/hooks/session-start.sh +42 -0
  34. package/plugins/agent-browser/skills/agent-browser/SKILL.md +77 -0
  35. package/plugins/agent-browser/skills/agent-browser/scripts/agent-browser.sh +80 -0
  36. package/plugins/ast-grep/.claude-plugin/plugin.json +18 -0
  37. package/plugins/ast-grep/.codex-plugin/plugin.json +7 -0
  38. package/plugins/ast-grep/README.md +21 -0
  39. package/plugins/ast-grep/hooks/hooks.json +15 -0
  40. package/plugins/ast-grep/hooks/post-tools.d/byte-savings.sh +67 -0
  41. package/plugins/ast-grep/hooks/pre-tools.d/search-nudge.sh +170 -0
  42. package/plugins/ast-grep/hooks/register.sh +68 -0
  43. package/plugins/ast-grep/scripts/byte-savings-report.sh +28 -0
  44. package/plugins/ast-grep/skills/ast-grep/SKILL.md +127 -0
  45. package/plugins/ast-grep/skills/ast-grep/references/ast-grep-advanced.md +44 -0
  46. package/plugins/ast-grep/skills/ast-grep/scripts/ast-grep.sh +103 -0
  47. package/plugins/context7/.claude-plugin/plugin.json +18 -0
  48. package/plugins/context7/.codex-plugin/plugin.json +7 -0
  49. package/plugins/context7/README.md +19 -0
  50. package/plugins/context7/hooks/hooks.json +15 -0
  51. package/plugins/context7/hooks/session-start.sh +42 -0
  52. package/plugins/context7/skills/context7/SKILL.md +64 -0
  53. package/plugins/context7/skills/context7/scripts/search.sh +151 -0
  54. package/plugins/exa-search/.claude-plugin/plugin.json +18 -0
  55. package/plugins/exa-search/.codex-plugin/plugin.json +7 -0
  56. package/plugins/exa-search/README.md +19 -0
  57. package/plugins/exa-search/hooks/hooks.json +15 -0
  58. package/plugins/exa-search/hooks/session-start.sh +34 -0
  59. package/plugins/exa-search/skills/exa-search/SKILL.md +91 -0
  60. package/plugins/exa-search/skills/exa-search/scripts/search.sh +221 -0
  61. package/plugins/jev/.claude-plugin/plugin.json +19 -0
  62. package/plugins/jev/.codex-plugin/plugin.json +7 -0
  63. package/plugins/jev/README.md +61 -0
  64. package/plugins/jev/hooks/hooks.json +25 -0
  65. package/plugins/jev/hooks/lib/common.sh +49 -0
  66. package/plugins/jev/hooks/session-start.sh +45 -0
  67. package/plugins/jev/hooks/user-prompt-submit.sh +41 -0
  68. package/plugins/jev/skills/jev/SKILL.md +81 -0
  69. package/plugins/jev/skills/jev/evals/README.md +98 -0
  70. package/plugins/jev/skills/jev/references/problem-solving.md +198 -0
  71. package/plugins/jev/skills/jev/scripts/jev.sh +405 -0
  72. package/plugins/jira/.claude-plugin/plugin.json +18 -0
  73. package/plugins/jira/.codex-plugin/plugin.json +7 -0
  74. package/plugins/jira/README.md +42 -0
  75. package/plugins/jira/hooks/hooks.json +15 -0
  76. package/plugins/jira/hooks/session-start.sh +34 -0
  77. package/plugins/jira/skills/jira/SKILL.md +123 -0
  78. package/plugins/jira/skills/jira/scripts/jira.sh +70 -0
  79. package/plugins/jira/skills/jira/scripts/lib/adf.sh +17 -0
  80. package/plugins/jira/skills/jira/scripts/lib/attachment.sh +94 -0
  81. package/plugins/jira/skills/jira/scripts/lib/board.sh +58 -0
  82. package/plugins/jira/skills/jira/scripts/lib/http.sh +184 -0
  83. package/plugins/jira/skills/jira/scripts/lib/issue.sh +159 -0
  84. package/plugins/jira/skills/jira/scripts/lib/paginate.sh +45 -0
  85. package/plugins/jira/skills/jira/scripts/lib/plan-parse.sh +70 -0
  86. package/plugins/jira/skills/jira/scripts/lib/plan-run.sh +115 -0
  87. package/plugins/jira/skills/jira/scripts/lib/plan-store.sh +168 -0
  88. package/plugins/jira/skills/jira/scripts/lib/plan.sh +108 -0
  89. package/plugins/jira/skills/jira/scripts/lib/project.sh +42 -0
  90. package/plugins/jira/skills/jira/scripts/lib/raw.sh +15 -0
  91. package/plugins/jira/skills/jira/scripts/lib/search.sh +41 -0
  92. package/plugins/jira/skills/jira/scripts/lib/sprint.sh +88 -0
  93. package/plugins/jira/skills/jira/scripts/lib/user.sh +45 -0
  94. package/plugins/jira/skills/jira/scripts/lib/worklog.sh +56 -0
  95. package/plugins/pr-babysit/.claude-plugin/plugin.json +25 -0
  96. package/plugins/pr-babysit/.codex-plugin/plugin.json +7 -0
  97. package/plugins/pr-babysit/README.md +38 -0
  98. package/plugins/pr-babysit/commands/babysit.md +13 -0
  99. package/plugins/pr-babysit/hooks/check-toolu.sh +20 -0
  100. package/plugins/pr-babysit/hooks/hooks.json +15 -0
  101. package/plugins/pr-babysit/scripts/babysit-tick.sh +133 -0
  102. package/plugins/pr-babysit/scripts/collect-pr.sh +224 -0
  103. package/plugins/pr-babysit/scripts/lib/common.sh +146 -0
  104. package/plugins/pr-babysit/scripts/lib/gh.sh +150 -0
  105. package/plugins/pr-babysit/scripts/lib/lock.sh +84 -0
  106. package/plugins/pr-babysit/scripts/lib/normalize.sh +98 -0
  107. package/plugins/pr-babysit/scripts/lib/state.sh +37 -0
  108. package/plugins/pr-babysit/scripts/parse-verdict.sh +171 -0
  109. package/plugins/pr-babysit/scripts/record.sh +69 -0
  110. package/plugins/pr-babysit/scripts/reduce-state.sh +245 -0
  111. package/plugins/pr-babysit/scripts/reply-thread.sh +90 -0
  112. package/plugins/pr-babysit/scripts/resolve-thread.sh +71 -0
  113. package/plugins/pr-babysit/skills/babysit/SKILL.md +31 -0
  114. package/plugins/pr-babysit/skills/babysit/references/helper.md +209 -0
  115. package/plugins/pr-babysit/workflows/babysit.md +722 -0
  116. package/plugins/python-quality/.claude-plugin/plugin.json +24 -0
  117. package/plugins/python-quality/.codex-plugin/plugin.json +6 -0
  118. package/plugins/python-quality/README.md +23 -0
  119. package/plugins/python-quality/hooks/check-toolu.sh +20 -0
  120. package/plugins/python-quality/hooks/concerns/00-preamble.sh +85 -0
  121. package/plugins/python-quality/hooks/concerns/10-size-file.sh +6 -0
  122. package/plugins/python-quality/hooks/concerns/20-tests.sh +37 -0
  123. package/plugins/python-quality/hooks/concerns/30-suppression.sh +33 -0
  124. package/plugins/python-quality/hooks/concerns/50-size-fn.sh +49 -0
  125. package/plugins/python-quality/hooks/concerns/70-no-mocks.sh +76 -0
  126. package/plugins/python-quality/hooks/concerns/90-docs.sh +52 -0
  127. package/plugins/python-quality/hooks/concerns/99-finalize.sh +36 -0
  128. package/plugins/python-quality/hooks/hooks.json +19 -0
  129. package/plugins/python-quality/hooks/register.sh +65 -0
  130. package/plugins/rust-quality/.claude-plugin/plugin.json +24 -0
  131. package/plugins/rust-quality/.codex-plugin/plugin.json +6 -0
  132. package/plugins/rust-quality/README.md +24 -0
  133. package/plugins/rust-quality/hooks/check-toolu.sh +20 -0
  134. package/plugins/rust-quality/hooks/concerns/00-preamble.sh +93 -0
  135. package/plugins/rust-quality/hooks/concerns/10-size-file.sh +10 -0
  136. package/plugins/rust-quality/hooks/concerns/20-tests.sh +84 -0
  137. package/plugins/rust-quality/hooks/concerns/30-suppression.sh +26 -0
  138. package/plugins/rust-quality/hooks/concerns/40-unsafe.sh +39 -0
  139. package/plugins/rust-quality/hooks/concerns/50-size-fn.sh +38 -0
  140. package/plugins/rust-quality/hooks/concerns/55-size-impl.sh +28 -0
  141. package/plugins/rust-quality/hooks/concerns/60-error-handling.sh +149 -0
  142. package/plugins/rust-quality/hooks/concerns/70-no-mocks.sh +139 -0
  143. package/plugins/rust-quality/hooks/concerns/90-docs.sh +32 -0
  144. package/plugins/rust-quality/hooks/concerns/99-finalize.sh +36 -0
  145. package/plugins/rust-quality/hooks/hooks.json +19 -0
  146. package/plugins/rust-quality/hooks/register.sh +65 -0
  147. package/plugins/statusline/.claude-plugin/plugin.json +19 -0
  148. package/plugins/statusline/.codex-plugin/plugin.json +7 -0
  149. package/plugins/statusline/README.md +108 -0
  150. package/plugins/statusline/commands/setup.md +30 -0
  151. package/plugins/statusline/hooks/hooks.json +15 -0
  152. package/plugins/statusline/hooks/session-start.sh +45 -0
  153. package/plugins/statusline/scripts/collect-status.sh +147 -0
  154. package/plugins/statusline/scripts/setup.sh +93 -0
  155. package/plugins/statusline/scripts/status.sh +55 -0
  156. package/plugins/statusline/skills/status/SKILL.md +13 -0
  157. package/plugins/statusline/statusline.sh +177 -0
  158. package/plugins/toolu/.claude-plugin/plugin.json +19 -0
  159. package/plugins/toolu/.codex-plugin/plugin.json +7 -0
  160. package/plugins/toolu/README.md +35 -0
  161. package/plugins/toolu/agents/.gitkeep +0 -0
  162. package/plugins/toolu/agents/architect.md +52 -0
  163. package/plugins/toolu/agents/deep-explore.md +65 -0
  164. package/plugins/toolu/agents/implementer.md +50 -0
  165. package/plugins/toolu/agents/quick-task.md +54 -0
  166. package/plugins/toolu/agents/research-agent.md +107 -0
  167. package/plugins/toolu/assets/agents/architect.toml +9 -0
  168. package/plugins/toolu/assets/agents/deep-explore.toml +9 -0
  169. package/plugins/toolu/assets/agents/implementer.toml +9 -0
  170. package/plugins/toolu/assets/agents/quick-task.toml +9 -0
  171. package/plugins/toolu/assets/agents/research-agent.toml +9 -0
  172. package/plugins/toolu/commands/.gitkeep +0 -0
  173. package/plugins/toolu/commands/commit.md +5 -0
  174. package/plugins/toolu/commands/review-and-commit.md +5 -0
  175. package/plugins/toolu/hooks/docs/gates.md +157 -0
  176. package/plugins/toolu/hooks/docs/model-routing.md +14 -0
  177. package/plugins/toolu/hooks/docs/post-compaction.md +3 -0
  178. package/plugins/toolu/hooks/docs/push-review.md +123 -0
  179. package/plugins/toolu/hooks/docs/session-start-python.md +3 -0
  180. package/plugins/toolu/hooks/docs/session-start-rust.md +4 -0
  181. package/plugins/toolu/hooks/docs/session-start-ts.md +3 -0
  182. package/plugins/toolu/hooks/docs/session-start.md +16 -0
  183. package/plugins/toolu/hooks/hooks.json +76 -0
  184. package/plugins/toolu/hooks/lib/config.sh +355 -0
  185. package/plugins/toolu/hooks/lib/detect.sh +929 -0
  186. package/plugins/toolu/hooks/lib/diff-sha.sh +32 -0
  187. package/plugins/toolu/hooks/lib/dispatch.sh +365 -0
  188. package/plugins/toolu/hooks/lib/docs-sync-config.sh +81 -0
  189. package/plugins/toolu/hooks/lib/edit-records.sh +157 -0
  190. package/plugins/toolu/hooks/lib/gate-file.sh +201 -0
  191. package/plugins/toolu/hooks/lib/gate-mode.sh +244 -0
  192. package/plugins/toolu/hooks/lib/host.sh +205 -0
  193. package/plugins/toolu/hooks/lib/permissions.sh +125 -0
  194. package/plugins/toolu/hooks/lib/plan-ledger-parse.sh +219 -0
  195. package/plugins/toolu/hooks/lib/plan-ledger-preflight.sh +137 -0
  196. package/plugins/toolu/hooks/lib/plan-ledger.sh +749 -0
  197. package/plugins/toolu/hooks/lib/push-waiver.sh +116 -0
  198. package/plugins/toolu/hooks/lib/quality-config.sh +184 -0
  199. package/plugins/toolu/hooks/lib/registry.sh +72 -0
  200. package/plugins/toolu/hooks/lib/state-sweeper.sh +194 -0
  201. package/plugins/toolu/hooks/lib/telemetry.sh +94 -0
  202. package/plugins/toolu/hooks/lib/verdict.sh +477 -0
  203. package/plugins/toolu/hooks/post-tools/mod.sh +42 -0
  204. package/plugins/toolu/hooks/post-tools/modules/gate-status.sh +140 -0
  205. package/plugins/toolu/hooks/post-tools/modules/push-waiver.sh +97 -0
  206. package/plugins/toolu/hooks/pre-compact.sh +18 -0
  207. package/plugins/toolu/hooks/pre-tools/agent-tier.sh +111 -0
  208. package/plugins/toolu/hooks/pre-tools/mod.sh +37 -0
  209. package/plugins/toolu/hooks/pre-tools/modules/bash-commands.sh +187 -0
  210. package/plugins/toolu/hooks/pre-tools/modules/code-edit-rules.sh +78 -0
  211. package/plugins/toolu/hooks/pre-tools/modules/commit-gate.sh +70 -0
  212. package/plugins/toolu/hooks/pre-tools/modules/docs-sync.sh +137 -0
  213. package/plugins/toolu/hooks/pre-tools/modules/mcp-blocker.sh +151 -0
  214. package/plugins/toolu/hooks/pre-tools/modules/plan-ledger.sh +201 -0
  215. package/plugins/toolu/hooks/pre-tools/modules/protected-files.sh +177 -0
  216. package/plugins/toolu/hooks/pre-tools/modules/push-review.sh +273 -0
  217. package/plugins/toolu/hooks/pre-tools/modules/quality-gate.sh +87 -0
  218. package/plugins/toolu/hooks/session-start.sh +379 -0
  219. package/plugins/toolu/hooks/user-prompt-submit.sh +197 -0
  220. package/plugins/toolu/scripts/context-budget.sh +111 -0
  221. package/plugins/toolu/scripts/debug-log.sh +98 -0
  222. package/plugins/toolu/scripts/debug-stack.sh +108 -0
  223. package/plugins/toolu/scripts/debug-testfail.sh +97 -0
  224. package/plugins/toolu/settings/.gitkeep +0 -0
  225. package/plugins/toolu/settings/README.md +127 -0
  226. package/plugins/toolu/settings/bash-allowlist.txt +12 -0
  227. package/plugins/toolu/settings/bash-denylist.txt +25 -0
  228. package/plugins/toolu/settings/code-edit-rules.json +67 -0
  229. package/plugins/toolu/settings/commit-prefixes.txt +12 -0
  230. package/plugins/toolu/settings/mcp-blocklist.txt +8 -0
  231. package/plugins/toolu/settings/permissions.fragment.json +36 -0
  232. package/plugins/toolu/settings/protected-files.txt +30 -0
  233. package/plugins/toolu/settings/rust-unsafe-exemptions.txt +5 -0
  234. package/plugins/toolu/settings/toolu.config.example.json +130 -0
  235. package/plugins/toolu/skills/.gitkeep +0 -0
  236. package/plugins/toolu/skills/brainstorm/SKILL.md +57 -0
  237. package/plugins/toolu/skills/brainstorm/references/design-questions.md +36 -0
  238. package/plugins/toolu/skills/commit/SKILL.md +11 -0
  239. package/plugins/toolu/skills/debug/SKILL.md +54 -0
  240. package/plugins/toolu/skills/debug/evals/README.md +17 -0
  241. package/plugins/toolu/skills/debug/evals/cases.json +24 -0
  242. package/plugins/toolu/skills/debug/evals/results.json +34 -0
  243. package/plugins/toolu/skills/deep-research/SKILL.md +51 -0
  244. package/plugins/toolu/skills/execution/SKILL.md +77 -0
  245. package/plugins/toolu/skills/orchestrator/SKILL.md +145 -0
  246. package/plugins/toolu/skills/orchestrator/references/delegation-cost.md +104 -0
  247. package/plugins/toolu/skills/orchestrator/references/model-routing.md +112 -0
  248. package/plugins/toolu/skills/plan/SKILL.md +56 -0
  249. package/plugins/toolu/skills/plan/references/ledger.md +18 -0
  250. package/plugins/toolu/skills/plan-review/SKILL.md +48 -0
  251. package/plugins/toolu/skills/review-and-commit/SKILL.md +10 -0
  252. package/plugins/toolu/skills/setup/SKILL.md +24 -0
  253. package/plugins/toolu/skills/setup/scripts/setup.sh +206 -0
  254. package/plugins/toolu/skills/spec/SKILL.md +67 -0
  255. package/plugins/toolu/skills/spec-review/SKILL.md +50 -0
  256. package/plugins/toolu/skills/test/SKILL.md +49 -0
  257. package/plugins/toolu/workflows/commit.md +21 -0
  258. package/plugins/toolu/workflows/host-mapping.md +22 -0
  259. package/plugins/toolu/workflows/review-and-commit.md +54 -0
  260. package/plugins/toolu/workflows/semantic-judgments.md +35 -0
  261. package/plugins/toolu-review/.claude-plugin/plugin.json +18 -0
  262. package/plugins/toolu-review/.codex-plugin/plugin.json +7 -0
  263. package/plugins/toolu-review/README.md +17 -0
  264. package/plugins/toolu-review/hooks/hooks.json +15 -0
  265. package/plugins/toolu-review/hooks/session-start.sh +34 -0
  266. package/plugins/toolu-review/skills/review/SKILL.md +90 -0
  267. package/plugins/toolu-review/skills/review/scripts/write-state.sh +189 -0
  268. package/plugins/ts-quality/.claude-plugin/plugin.json +24 -0
  269. package/plugins/ts-quality/.codex-plugin/plugin.json +6 -0
  270. package/plugins/ts-quality/README.md +24 -0
  271. package/plugins/ts-quality/hooks/check-toolu.sh +20 -0
  272. package/plugins/ts-quality/hooks/concerns/00-preamble.sh +123 -0
  273. package/plugins/ts-quality/hooks/concerns/10-imports.sh +33 -0
  274. package/plugins/ts-quality/hooks/concerns/15-type-as.sh +9 -0
  275. package/plugins/ts-quality/hooks/concerns/20-tests.sh +26 -0
  276. package/plugins/ts-quality/hooks/concerns/25-size-file.sh +40 -0
  277. package/plugins/ts-quality/hooks/concerns/30-size-fn.sh +53 -0
  278. package/plugins/ts-quality/hooks/concerns/35-react-hooks.sh +7 -0
  279. package/plugins/ts-quality/hooks/concerns/40-factory.sh +5 -0
  280. package/plugins/ts-quality/hooks/concerns/45-typeguard.sh +6 -0
  281. package/plugins/ts-quality/hooks/concerns/50-type-dup.sh +16 -0
  282. package/plugins/ts-quality/hooks/concerns/55-naming.sh +11 -0
  283. package/plugins/ts-quality/hooks/concerns/60-console.sh +6 -0
  284. package/plugins/ts-quality/hooks/concerns/65-suppression.sh +12 -0
  285. package/plugins/ts-quality/hooks/concerns/70-ui-confirm.sh +8 -0
  286. package/plugins/ts-quality/hooks/concerns/72-ui-radix.sh +12 -0
  287. package/plugins/ts-quality/hooks/concerns/74-react-props.sh +6 -0
  288. package/plugins/ts-quality/hooks/concerns/76-toast.sh +10 -0
  289. package/plugins/ts-quality/hooks/concerns/78-error-ast.sh +242 -0
  290. package/plugins/ts-quality/hooks/concerns/80-throw-literal.sh +23 -0
  291. package/plugins/ts-quality/hooks/concerns/85-no-mocks.sh +109 -0
  292. package/plugins/ts-quality/hooks/concerns/90-duplication.sh +30 -0
  293. package/plugins/ts-quality/hooks/concerns/92-docs.sh +36 -0
  294. package/plugins/ts-quality/hooks/concerns/94-handler.sh +15 -0
  295. package/plugins/ts-quality/hooks/concerns/99-finalize.sh +46 -0
  296. package/plugins/ts-quality/hooks/hooks.json +19 -0
  297. package/plugins/ts-quality/hooks/register.sh +65 -0
  298. package/src/adapter/__tests__/evaluate.test.ts +82 -0
  299. package/src/adapter/__tests__/permission-map.test.ts +91 -0
  300. package/src/adapter/evaluate.ts +54 -0
  301. package/src/adapter/permission-map.ts +177 -0
  302. package/src/bootstrap/__tests__/bootstrap.test.ts +148 -0
  303. package/src/bootstrap/entrypoint.ts +18 -0
  304. package/src/bootstrap/index.ts +5 -0
  305. package/src/bootstrap/readiness.ts +59 -0
  306. package/src/bootstrap/result.ts +21 -0
  307. package/src/bootstrap/runtime.ts +99 -0
  308. package/src/bootstrap/test-helpers.ts +25 -0
  309. package/src/host/__tests__/host.test.ts +37 -0
  310. package/src/host/detect.ts +46 -0
  311. package/src/host/index.ts +10 -0
  312. package/src/host/roots.ts +59 -0
  313. package/src/host/types.ts +14 -0
  314. package/src/inventory/__tests__/inventory.test.ts +69 -0
  315. package/src/inventory/index.ts +9 -0
  316. package/src/inventory/manifest.ts +46 -0
  317. package/src/inventory/scan.ts +66 -0
  318. package/src/inventory/selection.ts +92 -0
  319. package/src/inventory/types.ts +22 -0
  320. package/src/lifecycle/__tests__/lifecycle.test.ts +7 -0
  321. package/src/lifecycle/index.ts +2 -0
  322. package/src/lifecycle/table.ts +42 -0
  323. package/src/plugin/__tests__/plugin-stub.test.ts +6 -0
  324. package/src/plugin/plugin-stub.ts +7 -0
  325. package/src/plugin/toolu.ts +142 -0
  326. package/src/preflight/__tests__/preflight.test.ts +20 -0
  327. package/src/preflight/check.ts +75 -0
  328. package/src/preflight/index.ts +2 -0
  329. package/src/select/__tests__/select.test.ts +52 -0
  330. package/src/select/index.ts +2 -0
  331. package/src/select/resolve.ts +87 -0
@@ -0,0 +1,15 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "matcher": "startup|resume|clear|compact",
6
+ "hooks": [
7
+ {
8
+ "type": "command",
9
+ "command": "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh"
10
+ }
11
+ ]
12
+ }
13
+ ]
14
+ }
15
+ }
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env bash
2
+ # SessionStart hook — publish the jira wrapper at a STABLE, env-independent
3
+ # path. See context7's session-start.sh for the same fix rationale:
4
+ # ${CLAUDE_PLUGIN_ROOT} is exported to hook subprocesses only — NOT to the
5
+ # agent's Bash tool subshell — so a SKILL.md path of
6
+ # "${CLAUDE_PLUGIN_ROOT}/skills/jira/scripts/jira.sh" expands to
7
+ # "/skills/.../jira.sh: No such file" when an agent pastes it. Mirror the
8
+ # statusline plugin: symlink the wrapper to
9
+ # ${CLAUDE_CONFIG_DIR:-$HOME/.claude}/jira/jira.sh
10
+ # Refreshed every session; silent on success; every step non-fatal.
11
+
12
+ # Consume stdin so Claude Code's hook IPC never stalls.
13
+ cat > /dev/null 2>&1 || true
14
+
15
+ plugin_dir="$(cd "$(dirname "$0")/.." 2>/dev/null && pwd)"
16
+ src="${plugin_dir:+$plugin_dir/skills/jira/scripts/jira.sh}"
17
+ [ -n "$src" ] && [ -f "$src" ] || exit 0
18
+
19
+ if [ -n "${TOOLU_CONFIG_DIR:-}" ]; then
20
+ config_root="$TOOLU_CONFIG_DIR"
21
+ elif [ "${TOOLU_HOST_OVERRIDE:-}" = codex ] || { [ -z "${TOOLU_HOST_OVERRIDE:-}" ] && [ -n "${PLUGIN_ROOT:-}" ]; }; then
22
+ config_root="${CODEX_HOME:-$HOME/.codex}"
23
+ else
24
+ config_root="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
25
+ fi
26
+ reg_root="$config_root/jira"
27
+ mkdir -p "$reg_root" 2>/dev/null || { echo "jira: cannot create $reg_root — wrapper not published" >&2; exit 0; }
28
+
29
+ dst="$reg_root/jira.sh"
30
+ if [ -L "$dst" ] || [ ! -e "$dst" ]; then
31
+ ln -sf "$src" "$dst" 2>/dev/null || true
32
+ fi
33
+
34
+ exit 0
@@ -0,0 +1,123 @@
1
+ ---
2
+ name: jira
3
+ description: Search and act on Jira issues from the session via a REST wrapper — JQL search, read/create/comment/transition/assign issues, plus boards, sprints, worklogs, projects, users, attachments. Works against Jira Cloud and Server/Data Center. Triggers when the user mentions Jira, a JQL query, an issue key like ABC-123, a pasted atlassian.net/browse/ABC-123 link, "my tickets", "create a task at Jira", "create/comment on/transition/assign an issue", sprints or boards. Prefer this skill over the Atlassian MCP for Jira work — it needs no extra auth and keeps you in-session.
4
+ ---
5
+
6
+ # Jira
7
+
8
+ Use this skill to work a Jira ticket without leaving the session: search by JQL, read an issue, comment, move it through its workflow, manage sprints/worklogs, or reach any endpoint via `raw`.
9
+
10
+ **Trigger phrases:** jira, JQL, my open tickets, issue ABC-123, a pasted atlassian.net/browse link, create a task at jira, create an issue, comment on, transition / move to, assign to, log work, sprint, board, plan the ticket, break down ABC-123.
11
+
12
+ **Prefer this over the Atlassian MCP.** When the user mentions Jira, an issue key, or pastes a Jira link, use this skill — not `mcp__*Atlassian*` tools. This wrapper reuses your existing Jira/`jira` CLI auth and stays in-session; reach for the MCP only if this skill cannot reach the endpoint.
13
+
14
+ ## CLI tool
15
+
16
+ Invoke at the **stable published path** (a symlink the plugin's SessionStart hook refreshes every session):
17
+
18
+ ```bash
19
+ # Codex
20
+ TOOLU_HOST_OVERRIDE=codex \
21
+ "${TOOLU_CONFIG_DIR:-${CODEX_HOME:-$HOME/.codex}}/jira/jira.sh" [--api-version N] [--lean] <family> <action> [options]
22
+ # Claude Code
23
+ TOOLU_HOST_OVERRIDE=claude \
24
+ "${TOOLU_CONFIG_DIR:-${CLAUDE_CONFIG_DIR:-$HOME/.claude}}/jira/jira.sh" [--api-version N] [--lean] <family> <action> [options]
25
+ ```
26
+
27
+ Choose the complete command for the active host, including its override; every
28
+ `jira.sh` shorthand below means that chosen prefix. The override propagates to
29
+ nested plan checks and their state paths. Ordinary shell calls do not inherit
30
+ plugin lifecycle variables, so never collapse these into one ambiguous
31
+ fallback. Use the published path; plugin-root variables are lifecycle-only.
32
+
33
+ Repo-checkout fallback (for tests/dev when the plugin is not installed): `plugins/jira/skills/jira/scripts/jira.sh`.
34
+
35
+ ## Setup
36
+
37
+ **Easiest:** if the [`jira` CLI](https://github.com/ankitpokhrel/jira-cli) is configured (`jira init`), this plugin reuses its login automatically — server + login from `~/.config/.jira/.config.yml`, API token from the OS keyring (or `$JIRA_API_TOKEN`). No extra setup.
38
+
39
+ **Or set environment variables** (these always take precedence; never a `.env` file):
40
+
41
+ ```
42
+ JIRA_BASE_URL required, e.g. https://acme.atlassian.net (no trailing slash)
43
+ JIRA_PAT Bearer auth — Personal Access Token (Cloud or Server/DC)
44
+ JIRA_EMAIL + JIRA_API_TOKEN basic auth — Cloud API token (used when JIRA_PAT is unset)
45
+ JIRA_API_VERSION 2 or 3 (default 3). Server/Data Center → 2.
46
+ ```
47
+
48
+ Resolution: explicit env wins; else the jira CLI config + keyring fill the gaps. Auth: `JIRA_PAT` → Bearer; else `JIRA_EMAIL`+`JIRA_API_TOKEN` → basic. When nothing is available the plugin prints a short, friendly setup prompt (not an error) and exits.
49
+
50
+ ## Families
51
+
52
+ ```
53
+ jira.sh search -q '<JQL>' [-n MAX] [--all] [--fields f1,f2] [--lean]
54
+ jira.sh issue get <KEY> [--lean]
55
+ jira.sh issue create -p <PROJECT> -t <TYPE> -s <SUMMARY> [-d DESC] [--assignee ID] [-f field=val]...
56
+ jira.sh issue comment <KEY> <TEXT>
57
+ jira.sh issue transitions <KEY> # list available transitions
58
+ jira.sh issue transition <KEY> <NAME|ID> # name resolved case-insensitively
59
+ jira.sh issue assign <KEY> <ACCOUNT_ID|-> # '-' unassigns
60
+ jira.sh issue update <KEY> [-s SUMMARY] [-d DESC] [-f field=val]...
61
+ jira.sh issue delete <KEY>
62
+ jira.sh board list [-p PROJECT] | get <ID> | issues <ID> [-q JQL]
63
+ jira.sh sprint list <BOARD_ID> | get <ID> | issues <ID> | create <BOARD_ID> -n NAME
64
+ | move <SPRINT_ID> <KEY...> | start <ID> | complete <ID>
65
+ jira.sh worklog add <KEY> -t <TIME> [-c COMMENT] | list <KEY> | delete <KEY> <WORKLOG_ID>
66
+ jira.sh project list | get <KEY> | versions <KEY> | components <KEY>
67
+ jira.sh user whoami | search -q <QUERY> | get <ACCOUNT_ID>
68
+ jira.sh attachment add <KEY> <FILE> | list <KEY> | get <ATTACHMENT_ID>
69
+ | download <ATTACHMENT_ID> [-o FILE] | read <ATTACHMENT_ID> # read: text→context, binary→saved path
70
+ jira.sh raw <GET|POST|PUT|DELETE> <PATH> [JSON_BODY] # escape hatch; PATH relative to JIRA_BASE_URL
71
+ jira.sh plan init <KEY> # scaffold the plan doc, titled from a live issue get
72
+ jira.sh plan run <DOC> [--step <id>] [--activity <s>] # run the checks, write the ledger
73
+ jira.sh plan status <KEY> | path <KEY> # ledger summary / ledger path
74
+ ```
75
+
76
+ ## Plan first — when to decompose
77
+
78
+ **Read-only lookup → call directly.** `issue get`, `search`, `board list`, `sprint list`, `user whoami`, `attachment read` and friends are one-shot. No plan.
79
+
80
+ **Mutating, or two-or-more actions → write a plan first.** Anything that changes a live ticket (`issue create/update/comment/transition/assign`, `sprint create/move/start/complete`, `worklog add/delete`, `attachment add`), or any task needing more than one call, gets decomposed:
81
+
82
+ 1. `jira.sh plan init <KEY>` — scaffolds the active host's project state path:
83
+ `.claude/tmp/jira/plans/<KEY>.md` or `.codex/tmp/jira/plans/<KEY>.md`.
84
+ 2. Author the `## Steps (machine-readable)` array: one `{id, title, check}` per small, independently verifiable action.
85
+ 3. `jira.sh plan run <DOC> --step <id>` after doing each step; a final full `jira.sh plan run <DOC>` at the end.
86
+
87
+ This writes a ledger below the active host's `<repo>/.claude/tmp/plan-ledger/`
88
+ or `<repo>/.codex/tmp/plan-ledger/`. It is **not** the branch ledger and never
89
+ blocks `git push`.
90
+
91
+ ## Authoring a `check`
92
+
93
+ A `check` is a shell command that must exit 0 **only when Jira itself reflects the change**. Assert against a live read — never `true`, never a marker file. `$JIRA` is bound to this CLI when the check runs.
94
+
95
+ | Mutation | Assertion |
96
+ |---|---|
97
+ | `issue transition` | `"$JIRA" issue get K --lean \| jq -e '.status=="Done"'` |
98
+ | `issue assign` | `"$JIRA" issue get K \| jq -e '.fields.assignee.accountId=="<id>"'` |
99
+ | `issue comment` | `"$JIRA" raw GET /rest/api/3/issue/K/comment \| jq -e '[.comments[].body] \| any(tostring \| contains("<text>"))'` |
100
+ | `issue update` (field) | `"$JIRA" issue get K \| jq -e '.fields.summary=="<new>"'` |
101
+ | `sprint move` | `"$JIRA" sprint issues <SID> \| jq -e '[.issues[].key] \| index("K")'` |
102
+ | `worklog add` | `"$JIRA" worklog list K \| jq -e '[.worklogs[].timeSpent] \| index("2h")'` |
103
+
104
+ `plan run` probes Jira once (`user whoami`) before any check. If Jira is unreachable it aborts and writes **no** statuses — an auth or network failure never marks a step red. A step goes red only when a check ran and Jira disagreed; the reason is kept in the step's `evidence_tail`.
105
+
106
+ ## Mutating operations — confirm intent first
107
+
108
+ These change live tickets and have no undo prompt (the verb is the confirmation):
109
+ `issue create`, `issue update`, `issue comment`, `issue transition`, `issue assign`, `issue delete`,
110
+ `sprint create/move/start/complete`, `worklog add/delete`, `attachment add`. Know the target `KEY`/`ID` before running them.
111
+
112
+ Each of these is also a **plan-first** operation — decompose before you mutate (see above).
113
+
114
+ ## Notes & constraints
115
+
116
+ | Topic | Detail |
117
+ |---|---|
118
+ | Search endpoint | v3 (Cloud) uses `POST /rest/api/3/search/jql` (`nextPageToken`, no `total`); v2 uses `/rest/api/2/search` (`startAt`). `--all` follows whichever model applies. |
119
+ | Comment/description bodies | v3 needs ADF — the tool converts your plain text automatically; v2 takes plain text. Rich ADF (tables, mentions) is not supported — use `raw`. |
120
+ | Boards & sprints | Always hit `/rest/agile/1.0` regardless of `--api-version`. |
121
+ | `-f field=val` | Sets string scalars only. Fields needing objects/arrays (e.g. a custom-field object) → use `jira raw`. |
122
+ | `--lean` | Strips null/empty fields and Jira's verbose envelopes — use it when feeding results back to the model to cut tokens. |
123
+ | Anything unwrapped | `jira raw <METHOD> <path> [body]` reaches every endpoint the families don't cover. |
@@ -0,0 +1,70 @@
1
+ #!/usr/bin/env bash
2
+ # jira.sh — Jira CLI dispatcher. Strips global flags (--api-version, --lean) from
3
+ # anywhere in argv, then routes <family> <action> to the matching lib module.
4
+ # Reads creds from JIRA_* env (never .env). See SKILL.md for full usage.
5
+ set -euo pipefail
6
+
7
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8
+ LIB="$SCRIPT_DIR/lib"
9
+ FAMILIES="search issue board sprint worklog project user attachment raw plan"
10
+
11
+ usage() {
12
+ cat >&2 <<'EOF'
13
+ jira — Jira from the session (Cloud + Server/DC)
14
+
15
+ Usage: jira [--api-version N] [--lean] <family> <action> [options]
16
+
17
+ Families:
18
+ search JQL search
19
+ issue get|create|update|delete|comment|transition|transitions|assign
20
+ board list|get|issues
21
+ sprint list|get|create|issues|move|start|complete
22
+ worklog add|list|delete
23
+ project list|get|versions|components
24
+ user whoami|search|get
25
+ attachment add|list|get
26
+ raw <METHOD> <path> [body]
27
+ plan init|run|status|path
28
+
29
+ Environment:
30
+ JIRA_BASE_URL required (e.g. https://acme.atlassian.net)
31
+ JIRA_PAT Bearer auth (Cloud or Server/DC)
32
+ JIRA_EMAIL + JIRA_API_TOKEN basic auth (Cloud API token)
33
+ JIRA_API_VERSION 2 or 3 (default 3)
34
+ EOF
35
+ exit 1
36
+ }
37
+
38
+ # Strip global flags from anywhere; export resolved transport globals.
39
+ args=()
40
+ while [[ $# -gt 0 ]]; do
41
+ case "$1" in
42
+ --api-version) export _JIRA_VER="${2:-}"; shift 2;;
43
+ --api-version=*) export _JIRA_VER="${1#*=}"; shift;;
44
+ --lean) export _JIRA_LEAN=1; shift;;
45
+ *) args+=("$1"); shift;;
46
+ esac
47
+ done
48
+ set -- ${args[@]+"${args[@]}"}
49
+
50
+ [[ $# -lt 1 ]] && usage
51
+ family="$1"; shift
52
+
53
+ case " $FAMILIES " in
54
+ *" $family "*) ;;
55
+ *) echo "jira: unknown family '$family'" >&2; usage;;
56
+ esac
57
+
58
+ # shellcheck source=/dev/null
59
+ source "$LIB/http.sh"
60
+ source "$LIB/adf.sh"
61
+ source "$LIB/paginate.sh"
62
+ # shellcheck source=/dev/null
63
+ source "$LIB/$family.sh"
64
+
65
+ # `plan path` only prints a local ledger path — it never reaches Jira, so it must
66
+ # work without credentials. Every other family/action does reach Jira.
67
+ if ! { [ "$family" = "plan" ] && [ "${1:-}" = "path" ]; }; then
68
+ jira_require_env
69
+ fi
70
+ "jira_$family" "$@"
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env bash
2
+ # adf.sh — render a plain-text comment/description body for the resolved API
3
+ # version: a minimal ADF document for v3 (Cloud), a plain JSON string for v2
4
+ # (Server/DC). Sourced; defines a function only.
5
+
6
+ # jira_text_to_adf TEXT: print the JSON *value* for a body field (not wrapped).
7
+ # v3 -> {"type":"doc","version":1,"content":[{"type":"paragraph",...}]}
8
+ # v2 -> the text as a JSON string. Callers wrap it, e.g. {"body": <value>}.
9
+ jira_text_to_adf() {
10
+ local text="$1"
11
+ if [[ "${_JIRA_VER:-3}" == "2" ]]; then
12
+ jq -n --arg t "$text" '$t'
13
+ else
14
+ jq -n --arg t "$text" \
15
+ '{type:"doc",version:1,content:[{type:"paragraph",content:[{type:"text",text:$t}]}]}'
16
+ fi
17
+ }
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env bash
2
+ # attachment.sh — Jira issue attachments: add (multipart upload), list, get
3
+ # (metadata), download (binary content), read (download + surface text content
4
+ # as context). Sourced; depends on http.sh (jira_curl/jira_lean/jira_download and
5
+ # the resolved transport state). The multipart `add` builds its own curl
6
+ # (jira_curl only speaks JSON), reusing the same auth resolution.
7
+
8
+ jira_attachment() {
9
+ local action="${1:-}"; [[ $# -gt 0 ]] && shift
10
+ case "$action" in
11
+ add) _jira_attachment_add "$@";;
12
+ list) _jira_attachment_list "$@";;
13
+ get) _jira_attachment_get "$@";;
14
+ download) _jira_attachment_download "$@";;
15
+ read) _jira_attachment_read "$@";;
16
+ *) echo "Usage: jira attachment <add|list|get|download|read> ..." >&2; return 1;;
17
+ esac
18
+ }
19
+
20
+ # _jira_attachment_add KEY FILE: multipart-upload FILE to issue KEY's attachments.
21
+ _jira_attachment_add() {
22
+ local key="${1:-}" file="${2:-}"
23
+ [[ -z "$key" || -z "$file" ]] && { echo "Usage: jira attachment add <KEY> <FILE>" >&2; return 1; }
24
+ [[ -f "$file" ]] || { echo "attachment add: file not found: $file" >&2; return 1; }
25
+ local args=(-sS --fail-with-body -X POST -H "X-Atlassian-Token: no-check" \
26
+ -H "Accept: application/json" -F "file=@$file")
27
+ if [[ "${_JIRA_AUTH_MODE:-}" == "bearer" ]]; then
28
+ args+=(-H "Authorization: Bearer ${JIRA_PAT}")
29
+ else
30
+ args+=(-u "${JIRA_EMAIL}:${JIRA_API_TOKEN}")
31
+ fi
32
+ curl "${args[@]}" "${_JIRA_BASE}/rest/api/${_JIRA_VER}/issue/${key}/attachments" | jira_lean '.'
33
+ }
34
+
35
+ # _jira_attachment_list KEY: list KEY's attachments (id, filename, size).
36
+ _jira_attachment_list() {
37
+ local key="${1:-}"
38
+ [[ -z "$key" ]] && { echo "Usage: jira attachment list <KEY>" >&2; return 1; }
39
+ jira_curl GET "/rest/api/${_JIRA_VER}/issue/${key}?fields=attachment" \
40
+ | jira_lean '{attachments:[.fields.attachment[]|{id, filename, size}]}'
41
+ }
42
+
43
+ # _jira_attachment_get ATTACHMENT_ID: fetch one attachment's metadata.
44
+ _jira_attachment_get() {
45
+ local id="${1:-}"
46
+ [[ -z "$id" ]] && { echo "Usage: jira attachment get <ATTACHMENT_ID>" >&2; return 1; }
47
+ jira_curl GET "/rest/api/${_JIRA_VER}/attachment/${id}" | jira_lean '.'
48
+ }
49
+
50
+ # _jira_attachment_download ATTACHMENT_ID [-o OUTFILE]: download the binary
51
+ # content. Without -o, the filename is taken from the attachment metadata.
52
+ _jira_attachment_download() {
53
+ local id="${1:-}"; [[ $# -gt 0 ]] && shift
54
+ local out=""
55
+ while [[ $# -gt 0 ]]; do
56
+ case "$1" in
57
+ -o|--output) out="$2"; shift 2;;
58
+ *) echo "attachment download: unknown option '$1'" >&2; return 1;;
59
+ esac
60
+ done
61
+ [[ -z "$id" ]] && { echo "Usage: jira attachment download <ATTACHMENT_ID> [-o OUTFILE]" >&2; return 1; }
62
+ if [[ -z "$out" ]]; then
63
+ local fn; fn=$(jira_curl GET "/rest/api/${_JIRA_VER}/attachment/${id}" | jq -r '.filename // empty')
64
+ out="${fn:-attachment-${id}}"
65
+ fi
66
+ jira_download "/rest/api/${_JIRA_VER}/attachment/content/${id}" "$out"
67
+ echo "downloaded attachment ${id} -> ${out}"
68
+ }
69
+
70
+ # _jira_attachment_read ATTACHMENT_ID: download the content and, for text-like
71
+ # types, print it (so it becomes usable context); for binary types, report the
72
+ # type/size and the saved path instead of dumping bytes.
73
+ _jira_attachment_read() {
74
+ local id="${1:-}"
75
+ [[ -z "$id" ]] && { echo "Usage: jira attachment read <ATTACHMENT_ID>" >&2; return 1; }
76
+ local meta mime fn
77
+ meta=$(jira_curl GET "/rest/api/${_JIRA_VER}/attachment/${id}") || return 1
78
+ mime=$(jq -r '.mimeType // ""' <<<"$meta")
79
+ fn=$(jq -r '.filename // "attachment"' <<<"$meta")
80
+ local tmp; tmp="$(mktemp -t jira-attachment.XXXXXX)"
81
+ jira_download "/rest/api/${_JIRA_VER}/attachment/content/${id}" "$tmp" || { rm -f "$tmp"; return 1; }
82
+ case "$mime" in
83
+ text/*|application/json|application/xml|application/javascript|application/x-yaml|application/yaml|*+json|*+xml)
84
+ printf '# %s (%s)\n\n' "$fn" "$mime"
85
+ cat "$tmp"
86
+ rm -f "$tmp"
87
+ ;;
88
+ *)
89
+ local size; size=$(wc -c < "$tmp" | tr -d ' ')
90
+ printf '%s — binary attachment (%s, %s bytes) saved to %s\n' "$fn" "${mime:-unknown}" "$size" "$tmp"
91
+ printf 'Run `jira attachment download %s -o <path>` to save it elsewhere.\n' "$id"
92
+ ;;
93
+ esac
94
+ }
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env bash
2
+ # board.sh — Jira Agile board operations: list boards, fetch one, list a board's
3
+ # issues. Sourced; depends on http.sh (jira_curl/jira_lean). Agile endpoints are
4
+ # always under /rest/agile/1.0 regardless of the configured REST API version.
5
+
6
+ # jira_board <list|get|issues> ...: dispatch on the first arg (sub-action).
7
+ jira_board() {
8
+ local action="${1:-}"; [[ $# -gt 0 ]] && shift
9
+ case "$action" in
10
+ list) _jira_board_list "$@";;
11
+ get) _jira_board_get "$@";;
12
+ issues) _jira_board_issues "$@";;
13
+ *) echo "Usage: jira board <list|get|issues> ..." >&2; return 1;;
14
+ esac
15
+ }
16
+
17
+ # _jira_board_list [-p PROJECT]: GET /rest/agile/1.0/board, optionally scoped to
18
+ # a project key/id via ?projectKeyOrId=PROJECT.
19
+ _jira_board_list() {
20
+ local project=""
21
+ while [[ $# -gt 0 ]]; do
22
+ case "$1" in
23
+ -p|--project) project="$2"; shift 2;;
24
+ *) echo "board list: unknown option '$1'" >&2; return 1;;
25
+ esac
26
+ done
27
+ local path="/rest/agile/1.0/board"
28
+ [[ -n "$project" ]] && path="${path}?projectKeyOrId=${project}"
29
+ jira_curl GET "$path" | jira_lean '{values: [.values[] | {id, name, type}]}'
30
+ }
31
+
32
+ # _jira_board_get <ID>: GET /rest/agile/1.0/board/<ID>.
33
+ _jira_board_get() {
34
+ local id="${1:-}"
35
+ [[ -z "$id" ]] && { echo "Usage: jira board get <ID>" >&2; return 1; }
36
+ jira_curl GET "/rest/agile/1.0/board/$id" | jira_lean '.'
37
+ }
38
+
39
+ # _jira_board_issues <ID> [-q JQL] [-n MAX]: GET /rest/agile/1.0/board/<ID>/issue,
40
+ # appending ?jql=...&maxResults=... when those filters are supplied.
41
+ _jira_board_issues() {
42
+ local id="${1:-}"; [[ $# -gt 0 ]] && shift
43
+ [[ -z "$id" ]] && { echo "Usage: jira board issues <ID> [-q JQL] [-n MAX]" >&2; return 1; }
44
+ local jql="" max=""
45
+ while [[ $# -gt 0 ]]; do
46
+ case "$1" in
47
+ -q|--query) jql="$2"; shift 2;;
48
+ -n|--num) max="$2"; shift 2;;
49
+ *) echo "board issues: unknown option '$1'" >&2; return 1;;
50
+ esac
51
+ done
52
+ local path="/rest/agile/1.0/board/$id/issue" query=""
53
+ [[ -n "$jql" ]] && query="${query}&jql=$(jira_urlencode "$jql")"
54
+ [[ -n "$max" ]] && query="${query}&maxResults=${max}"
55
+ [[ -n "$query" ]] && path="${path}?${query#&}"
56
+ jira_curl GET "$path" \
57
+ | jira_lean '{issues: [.issues[] | {key, summary: .fields.summary, status: .fields.status.name}]}'
58
+ }
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env bash
2
+ # http.sh — shared Jira transport: credential resolution, the curl wrapper, lean
3
+ # output. Credentials come from JIRA_* env, or are reused from an installed
4
+ # ankitpokhrel jira-cli (config + OS keyring) when the env vars are unset.
5
+ # Sourced by jira.sh and every per-family lib module; defines functions only
6
+ # (no top-level side effects), so it is safe to source in unit tests.
7
+
8
+ # jira_require_env: validate JIRA_* env and resolve transport state.
9
+ # Sets _JIRA_AUTH_MODE (bearer|basic), _JIRA_VER (2|3), _JIRA_BASE (no trailing
10
+ # slash). Returns 1 (naming the offending var) on missing tools/creds/base/version.
11
+ jira_require_env() {
12
+ command -v curl >/dev/null 2>&1 || { echo "jira: curl required" >&2; return 1; }
13
+ command -v jq >/dev/null 2>&1 || { echo "jira: jq required" >&2; return 1; }
14
+
15
+ # Fill any unset JIRA_* from an installed jira CLI (best-effort; explicit env
16
+ # always wins, and a missing CLI is a no-op — never an error).
17
+ jira_load_cli_creds
18
+
19
+ if [[ -z "${JIRA_BASE_URL:-}" ]]; then jira_creds_help; return 1; fi
20
+ _JIRA_BASE="$JIRA_BASE_URL"
21
+ while [[ "$_JIRA_BASE" == */ ]]; do _JIRA_BASE="${_JIRA_BASE%/}"; done
22
+
23
+ if [[ -n "${JIRA_PAT:-}" ]]; then
24
+ _JIRA_AUTH_MODE="bearer"
25
+ elif [[ -n "${JIRA_EMAIL:-}" && -n "${JIRA_API_TOKEN:-}" ]]; then
26
+ _JIRA_AUTH_MODE="basic"
27
+ else
28
+ jira_creds_help; return 1
29
+ fi
30
+
31
+ _JIRA_VER="${_JIRA_VER:-${JIRA_API_VERSION:-3}}"
32
+ if [[ "$_JIRA_VER" != "2" && "$_JIRA_VER" != "3" ]]; then
33
+ echo "jira: api version must be 2 or 3 (got '$_JIRA_VER')" >&2
34
+ return 1
35
+ fi
36
+ export _JIRA_AUTH_MODE _JIRA_VER _JIRA_BASE
37
+ }
38
+
39
+ # jira_curl METHOD PATH [BODY]: authenticated request to _JIRA_BASE+PATH.
40
+ # Adds the resolved auth, JSON Accept, and --fail-with-body; BODY (when given)
41
+ # is sent as a JSON payload. Stdout is the raw response body.
42
+ jira_curl() {
43
+ local method="$1" path="$2" body="${3:-}"
44
+ local args=(-sS --fail-with-body -X "$method" -H "Accept: application/json")
45
+ if [[ "${_JIRA_AUTH_MODE:-}" == "bearer" ]]; then
46
+ args+=(-H "Authorization: Bearer ${JIRA_PAT}")
47
+ else
48
+ args+=(-u "${JIRA_EMAIL}:${JIRA_API_TOKEN}")
49
+ fi
50
+ if [[ -n "$body" ]]; then
51
+ args+=(-H "Content-Type: application/json" --data "$body")
52
+ fi
53
+ curl "${args[@]}" "${_JIRA_BASE}${path}"
54
+ }
55
+
56
+ # jira_lean [JQ_PROJECTION]: filter stdin through PROJECTION when _JIRA_LEAN=1
57
+ # (token-frugal output for LLM prompts), else pretty-print the full body.
58
+ jira_lean() {
59
+ local projection="${1:-.}"
60
+ if [[ "${_JIRA_LEAN:-}" == "1" ]]; then
61
+ jq "$projection"
62
+ else
63
+ jq '.'
64
+ fi
65
+ }
66
+
67
+ # jira_urlencode TEXT: percent-encode TEXT for safe use as a URL query value
68
+ # (spaces, =, &, etc.). Used by families that pass user input via query params.
69
+ jira_urlencode() {
70
+ jq -nr --arg s "$1" '$s|@uri'
71
+ }
72
+
73
+ # jira_download PATH OUTFILE: authenticated GET that follows redirects and
74
+ # writes the response body to OUTFILE (Jira attachment content 302-redirects to
75
+ # a media host). Auth is inlined rather than shared via a nameref to stay
76
+ # portable to bash 3.2.
77
+ jira_download() {
78
+ local path="$1" out="$2"
79
+ local args=(-sS --fail-with-body -L -o "$out" -H "Accept: */*")
80
+ if [[ "${_JIRA_AUTH_MODE:-}" == "bearer" ]]; then
81
+ args+=(-H "Authorization: Bearer ${JIRA_PAT}")
82
+ else
83
+ args+=(-u "${JIRA_EMAIL}:${JIRA_API_TOKEN}")
84
+ fi
85
+ curl "${args[@]}" "${_JIRA_BASE}${path}"
86
+ }
87
+
88
+ # ── credential discovery (ankitpokhrel jira-cli) ───────────────────────────
89
+ # jira-cli keeps server+login in a flat YAML config and the API token in one of
90
+ # several places. We reuse them to fill any unset JIRA_* var, matching jira-cli's
91
+ # own token order (config api_token → $JIRA_API_TOKEN → ~/.netrc → OS keyring),
92
+ # so a configured CLI needs no extra setup. Explicit JIRA_* env always wins.
93
+
94
+ # _jira_yaml_get FILE KEY: print a top-level scalar (key: value) from flat YAML.
95
+ _jira_yaml_get() {
96
+ [[ -r "$1" ]] || return 0
97
+ awk -v k="$2" 'index($0, k":")==1 { sub(/^[^:]*:[[:space:]]*/,""); sub(/[[:space:]]+$/,""); print; exit }' "$1"
98
+ }
99
+
100
+ # _jira_keyring_token LOGIN: best-effort token from the OS keyring (macOS
101
+ # security, then Linux secret-tool). Empty string on any miss — never errors.
102
+ # macOS may require interactive keychain authorization the first time.
103
+ _jira_keyring_token() {
104
+ local login="$1" svc="${JIRA_KEYRING_SERVICE:-jira-cli}" tok=""
105
+ if command -v security >/dev/null 2>&1; then
106
+ tok=$(security find-generic-password -s "$svc" -a "$login" -w 2>/dev/null || true)
107
+ fi
108
+ if [[ -z "$tok" ]] && command -v secret-tool >/dev/null 2>&1; then
109
+ tok=$(secret-tool lookup service "$svc" username "$login" 2>/dev/null || true)
110
+ fi
111
+ printf '%s' "$tok"
112
+ }
113
+
114
+ # _jira_netrc_token HOST: token (password) for HOST from ~/.netrc (or $NETRC).
115
+ # Flattens tokens so multi-line entries work. Empty on miss — never errors.
116
+ _jira_netrc_token() {
117
+ local host="$1" file="${NETRC:-$HOME/.netrc}"
118
+ [[ -r "$file" ]] || return 0
119
+ awk -v h="$host" '
120
+ { for (i=1;i<=NF;i++) a[++n]=$i }
121
+ END { for (i=1;i<=n;i++) {
122
+ if (a[i]=="machine" && a[i+1]==h) inhost=1
123
+ else if (a[i]=="machine") inhost=0
124
+ if (inhost && a[i]=="password") { print a[i+1]; exit }
125
+ } }' "$file"
126
+ }
127
+
128
+ # jira_load_cli_creds: fill unset JIRA_* from the jira-cli config + keyring.
129
+ # No-op (and no error) when the CLI is not configured.
130
+ jira_load_cli_creds() {
131
+ local cfg="${JIRA_CLI_CONFIG:-${XDG_CONFIG_HOME:-$HOME/.config}/.jira/.config.yml}"
132
+ [[ -r "$cfg" ]] || return 0
133
+ local server login installation
134
+ server=$(_jira_yaml_get "$cfg" server)
135
+ login=$(_jira_yaml_get "$cfg" login)
136
+ installation=$(_jira_yaml_get "$cfg" installation)
137
+ [[ -z "${JIRA_BASE_URL:-}" && -n "$server" ]] && export JIRA_BASE_URL="$server"
138
+ [[ -z "${JIRA_EMAIL:-}" && -n "$login" ]] && export JIRA_EMAIL="$login"
139
+ if [[ -z "${JIRA_API_VERSION:-}" && -n "$installation" ]]; then
140
+ case "$installation" in
141
+ [Cc]loud) export JIRA_API_VERSION=3 ;;
142
+ *) export JIRA_API_VERSION=2 ;;
143
+ esac
144
+ fi
145
+ if [[ -z "${JIRA_PAT:-}" && -z "${JIRA_API_TOKEN:-}" ]]; then
146
+ # Match jira-cli's token sources, in its order: config api_token, then
147
+ # ~/.netrc (by server host), then the OS keyring.
148
+ local tok host
149
+ tok=$(_jira_yaml_get "$cfg" api_token)
150
+ if [[ -z "$tok" && -n "$server" ]]; then
151
+ host="${server#*://}"; host="${host%%/*}"
152
+ tok=$(_jira_netrc_token "$host")
153
+ fi
154
+ [[ -z "$tok" && -n "$login" ]] && tok=$(_jira_keyring_token "$login")
155
+ [[ -n "$tok" ]] && export JIRA_API_TOKEN="$tok"
156
+ fi
157
+ return 0
158
+ }
159
+
160
+ # jira_creds_help: friendly, non-error guidance when no credentials resolve.
161
+ # Reads as a one-time setup step, not a failure.
162
+ jira_creds_help() {
163
+ cat >&2 <<'EOF'
164
+ jira: no Jira credentials found yet — let's get you connected.
165
+
166
+ This plugin reuses your `jira` CLI login automatically when it's configured,
167
+ or reads JIRA_* environment variables. Right now neither is set.
168
+
169
+ Connect with either:
170
+
171
+ • Your jira CLI (easiest if it's installed):
172
+ jira init
173
+ then re-run your command — the plugin picks up the server, login, and
174
+ API token from the CLI automatically.
175
+
176
+ • Or environment variables:
177
+ export JIRA_BASE_URL=https://your-site.atlassian.net
178
+ export JIRA_EMAIL=you@example.com
179
+ export JIRA_API_TOKEN=… # id.atlassian.com → Security → API tokens
180
+ (Jira Server/Data Center: use JIRA_PAT instead of JIRA_EMAIL + JIRA_API_TOKEN.)
181
+
182
+ Nothing is broken — this is just a one-time setup step.
183
+ EOF
184
+ }