@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.
- package/generated/GENERATED-NOTES.md +16 -0
- package/generated/agents/toolu--architect.md +52 -0
- package/generated/agents/toolu--deep-explore.md +65 -0
- package/generated/agents/toolu--implementer.md +50 -0
- package/generated/agents/toolu--quick-task.md +54 -0
- package/generated/agents/toolu--research-agent.md +101 -0
- package/generated/commands/toolu--commit.md +8 -0
- package/generated/commands/toolu--review-and-commit.md +8 -0
- package/generated/opencode.toolu.json +116 -0
- package/generated/skills/toolu--brainstorm/SKILL.md +57 -0
- package/generated/skills/toolu--brainstorm/references/design-questions.md +36 -0
- package/generated/skills/toolu--commit--skill/SKILL.md +11 -0
- package/generated/skills/toolu--debug/SKILL.md +54 -0
- package/generated/skills/toolu--deep-research/SKILL.md +51 -0
- package/generated/skills/toolu--execution/SKILL.md +77 -0
- package/generated/skills/toolu--orchestrator/SKILL.md +145 -0
- package/generated/skills/toolu--orchestrator/references/delegation-cost.md +104 -0
- package/generated/skills/toolu--orchestrator/references/model-routing.md +112 -0
- package/generated/skills/toolu--plan/SKILL.md +56 -0
- package/generated/skills/toolu--plan/references/ledger.md +18 -0
- package/generated/skills/toolu--plan-review/SKILL.md +48 -0
- package/generated/skills/toolu--review-and-commit--skill/SKILL.md +10 -0
- package/generated/skills/toolu--setup/SKILL.md +24 -0
- package/generated/skills/toolu--setup/scripts/setup.sh +206 -0
- package/generated/skills/toolu--spec/SKILL.md +67 -0
- package/generated/skills/toolu--spec-review/SKILL.md +50 -0
- package/generated/skills/toolu--test/SKILL.md +49 -0
- package/package.json +51 -0
- package/plugins/agent-browser/.claude-plugin/plugin.json +18 -0
- package/plugins/agent-browser/.codex-plugin/plugin.json +7 -0
- package/plugins/agent-browser/README.md +18 -0
- package/plugins/agent-browser/hooks/hooks.json +15 -0
- package/plugins/agent-browser/hooks/session-start.sh +42 -0
- package/plugins/agent-browser/skills/agent-browser/SKILL.md +77 -0
- package/plugins/agent-browser/skills/agent-browser/scripts/agent-browser.sh +80 -0
- package/plugins/ast-grep/.claude-plugin/plugin.json +18 -0
- package/plugins/ast-grep/.codex-plugin/plugin.json +7 -0
- package/plugins/ast-grep/README.md +21 -0
- package/plugins/ast-grep/hooks/hooks.json +15 -0
- package/plugins/ast-grep/hooks/post-tools.d/byte-savings.sh +67 -0
- package/plugins/ast-grep/hooks/pre-tools.d/search-nudge.sh +170 -0
- package/plugins/ast-grep/hooks/register.sh +68 -0
- package/plugins/ast-grep/scripts/byte-savings-report.sh +28 -0
- package/plugins/ast-grep/skills/ast-grep/SKILL.md +127 -0
- package/plugins/ast-grep/skills/ast-grep/references/ast-grep-advanced.md +44 -0
- package/plugins/ast-grep/skills/ast-grep/scripts/ast-grep.sh +103 -0
- package/plugins/context7/.claude-plugin/plugin.json +18 -0
- package/plugins/context7/.codex-plugin/plugin.json +7 -0
- package/plugins/context7/README.md +19 -0
- package/plugins/context7/hooks/hooks.json +15 -0
- package/plugins/context7/hooks/session-start.sh +42 -0
- package/plugins/context7/skills/context7/SKILL.md +64 -0
- package/plugins/context7/skills/context7/scripts/search.sh +151 -0
- package/plugins/exa-search/.claude-plugin/plugin.json +18 -0
- package/plugins/exa-search/.codex-plugin/plugin.json +7 -0
- package/plugins/exa-search/README.md +19 -0
- package/plugins/exa-search/hooks/hooks.json +15 -0
- package/plugins/exa-search/hooks/session-start.sh +34 -0
- package/plugins/exa-search/skills/exa-search/SKILL.md +91 -0
- package/plugins/exa-search/skills/exa-search/scripts/search.sh +221 -0
- package/plugins/jev/.claude-plugin/plugin.json +19 -0
- package/plugins/jev/.codex-plugin/plugin.json +7 -0
- package/plugins/jev/README.md +61 -0
- package/plugins/jev/hooks/hooks.json +25 -0
- package/plugins/jev/hooks/lib/common.sh +49 -0
- package/plugins/jev/hooks/session-start.sh +45 -0
- package/plugins/jev/hooks/user-prompt-submit.sh +41 -0
- package/plugins/jev/skills/jev/SKILL.md +81 -0
- package/plugins/jev/skills/jev/evals/README.md +98 -0
- package/plugins/jev/skills/jev/references/problem-solving.md +198 -0
- package/plugins/jev/skills/jev/scripts/jev.sh +405 -0
- package/plugins/jira/.claude-plugin/plugin.json +18 -0
- package/plugins/jira/.codex-plugin/plugin.json +7 -0
- package/plugins/jira/README.md +42 -0
- package/plugins/jira/hooks/hooks.json +15 -0
- package/plugins/jira/hooks/session-start.sh +34 -0
- package/plugins/jira/skills/jira/SKILL.md +123 -0
- package/plugins/jira/skills/jira/scripts/jira.sh +70 -0
- package/plugins/jira/skills/jira/scripts/lib/adf.sh +17 -0
- package/plugins/jira/skills/jira/scripts/lib/attachment.sh +94 -0
- package/plugins/jira/skills/jira/scripts/lib/board.sh +58 -0
- package/plugins/jira/skills/jira/scripts/lib/http.sh +184 -0
- package/plugins/jira/skills/jira/scripts/lib/issue.sh +159 -0
- package/plugins/jira/skills/jira/scripts/lib/paginate.sh +45 -0
- package/plugins/jira/skills/jira/scripts/lib/plan-parse.sh +70 -0
- package/plugins/jira/skills/jira/scripts/lib/plan-run.sh +115 -0
- package/plugins/jira/skills/jira/scripts/lib/plan-store.sh +168 -0
- package/plugins/jira/skills/jira/scripts/lib/plan.sh +108 -0
- package/plugins/jira/skills/jira/scripts/lib/project.sh +42 -0
- package/plugins/jira/skills/jira/scripts/lib/raw.sh +15 -0
- package/plugins/jira/skills/jira/scripts/lib/search.sh +41 -0
- package/plugins/jira/skills/jira/scripts/lib/sprint.sh +88 -0
- package/plugins/jira/skills/jira/scripts/lib/user.sh +45 -0
- package/plugins/jira/skills/jira/scripts/lib/worklog.sh +56 -0
- package/plugins/pr-babysit/.claude-plugin/plugin.json +25 -0
- package/plugins/pr-babysit/.codex-plugin/plugin.json +7 -0
- package/plugins/pr-babysit/README.md +38 -0
- package/plugins/pr-babysit/commands/babysit.md +13 -0
- package/plugins/pr-babysit/hooks/check-toolu.sh +20 -0
- package/plugins/pr-babysit/hooks/hooks.json +15 -0
- package/plugins/pr-babysit/scripts/babysit-tick.sh +133 -0
- package/plugins/pr-babysit/scripts/collect-pr.sh +224 -0
- package/plugins/pr-babysit/scripts/lib/common.sh +146 -0
- package/plugins/pr-babysit/scripts/lib/gh.sh +150 -0
- package/plugins/pr-babysit/scripts/lib/lock.sh +84 -0
- package/plugins/pr-babysit/scripts/lib/normalize.sh +98 -0
- package/plugins/pr-babysit/scripts/lib/state.sh +37 -0
- package/plugins/pr-babysit/scripts/parse-verdict.sh +171 -0
- package/plugins/pr-babysit/scripts/record.sh +69 -0
- package/plugins/pr-babysit/scripts/reduce-state.sh +245 -0
- package/plugins/pr-babysit/scripts/reply-thread.sh +90 -0
- package/plugins/pr-babysit/scripts/resolve-thread.sh +71 -0
- package/plugins/pr-babysit/skills/babysit/SKILL.md +31 -0
- package/plugins/pr-babysit/skills/babysit/references/helper.md +209 -0
- package/plugins/pr-babysit/workflows/babysit.md +722 -0
- package/plugins/python-quality/.claude-plugin/plugin.json +24 -0
- package/plugins/python-quality/.codex-plugin/plugin.json +6 -0
- package/plugins/python-quality/README.md +23 -0
- package/plugins/python-quality/hooks/check-toolu.sh +20 -0
- package/plugins/python-quality/hooks/concerns/00-preamble.sh +85 -0
- package/plugins/python-quality/hooks/concerns/10-size-file.sh +6 -0
- package/plugins/python-quality/hooks/concerns/20-tests.sh +37 -0
- package/plugins/python-quality/hooks/concerns/30-suppression.sh +33 -0
- package/plugins/python-quality/hooks/concerns/50-size-fn.sh +49 -0
- package/plugins/python-quality/hooks/concerns/70-no-mocks.sh +76 -0
- package/plugins/python-quality/hooks/concerns/90-docs.sh +52 -0
- package/plugins/python-quality/hooks/concerns/99-finalize.sh +36 -0
- package/plugins/python-quality/hooks/hooks.json +19 -0
- package/plugins/python-quality/hooks/register.sh +65 -0
- package/plugins/rust-quality/.claude-plugin/plugin.json +24 -0
- package/plugins/rust-quality/.codex-plugin/plugin.json +6 -0
- package/plugins/rust-quality/README.md +24 -0
- package/plugins/rust-quality/hooks/check-toolu.sh +20 -0
- package/plugins/rust-quality/hooks/concerns/00-preamble.sh +93 -0
- package/plugins/rust-quality/hooks/concerns/10-size-file.sh +10 -0
- package/plugins/rust-quality/hooks/concerns/20-tests.sh +84 -0
- package/plugins/rust-quality/hooks/concerns/30-suppression.sh +26 -0
- package/plugins/rust-quality/hooks/concerns/40-unsafe.sh +39 -0
- package/plugins/rust-quality/hooks/concerns/50-size-fn.sh +38 -0
- package/plugins/rust-quality/hooks/concerns/55-size-impl.sh +28 -0
- package/plugins/rust-quality/hooks/concerns/60-error-handling.sh +149 -0
- package/plugins/rust-quality/hooks/concerns/70-no-mocks.sh +139 -0
- package/plugins/rust-quality/hooks/concerns/90-docs.sh +32 -0
- package/plugins/rust-quality/hooks/concerns/99-finalize.sh +36 -0
- package/plugins/rust-quality/hooks/hooks.json +19 -0
- package/plugins/rust-quality/hooks/register.sh +65 -0
- package/plugins/statusline/.claude-plugin/plugin.json +19 -0
- package/plugins/statusline/.codex-plugin/plugin.json +7 -0
- package/plugins/statusline/README.md +108 -0
- package/plugins/statusline/commands/setup.md +30 -0
- package/plugins/statusline/hooks/hooks.json +15 -0
- package/plugins/statusline/hooks/session-start.sh +45 -0
- package/plugins/statusline/scripts/collect-status.sh +147 -0
- package/plugins/statusline/scripts/setup.sh +93 -0
- package/plugins/statusline/scripts/status.sh +55 -0
- package/plugins/statusline/skills/status/SKILL.md +13 -0
- package/plugins/statusline/statusline.sh +177 -0
- package/plugins/toolu/.claude-plugin/plugin.json +19 -0
- package/plugins/toolu/.codex-plugin/plugin.json +7 -0
- package/plugins/toolu/README.md +35 -0
- package/plugins/toolu/agents/.gitkeep +0 -0
- package/plugins/toolu/agents/architect.md +52 -0
- package/plugins/toolu/agents/deep-explore.md +65 -0
- package/plugins/toolu/agents/implementer.md +50 -0
- package/plugins/toolu/agents/quick-task.md +54 -0
- package/plugins/toolu/agents/research-agent.md +107 -0
- package/plugins/toolu/assets/agents/architect.toml +9 -0
- package/plugins/toolu/assets/agents/deep-explore.toml +9 -0
- package/plugins/toolu/assets/agents/implementer.toml +9 -0
- package/plugins/toolu/assets/agents/quick-task.toml +9 -0
- package/plugins/toolu/assets/agents/research-agent.toml +9 -0
- package/plugins/toolu/commands/.gitkeep +0 -0
- package/plugins/toolu/commands/commit.md +5 -0
- package/plugins/toolu/commands/review-and-commit.md +5 -0
- package/plugins/toolu/hooks/docs/gates.md +157 -0
- package/plugins/toolu/hooks/docs/model-routing.md +14 -0
- package/plugins/toolu/hooks/docs/post-compaction.md +3 -0
- package/plugins/toolu/hooks/docs/push-review.md +123 -0
- package/plugins/toolu/hooks/docs/session-start-python.md +3 -0
- package/plugins/toolu/hooks/docs/session-start-rust.md +4 -0
- package/plugins/toolu/hooks/docs/session-start-ts.md +3 -0
- package/plugins/toolu/hooks/docs/session-start.md +16 -0
- package/plugins/toolu/hooks/hooks.json +76 -0
- package/plugins/toolu/hooks/lib/config.sh +355 -0
- package/plugins/toolu/hooks/lib/detect.sh +929 -0
- package/plugins/toolu/hooks/lib/diff-sha.sh +32 -0
- package/plugins/toolu/hooks/lib/dispatch.sh +365 -0
- package/plugins/toolu/hooks/lib/docs-sync-config.sh +81 -0
- package/plugins/toolu/hooks/lib/edit-records.sh +157 -0
- package/plugins/toolu/hooks/lib/gate-file.sh +201 -0
- package/plugins/toolu/hooks/lib/gate-mode.sh +244 -0
- package/plugins/toolu/hooks/lib/host.sh +205 -0
- package/plugins/toolu/hooks/lib/permissions.sh +125 -0
- package/plugins/toolu/hooks/lib/plan-ledger-parse.sh +219 -0
- package/plugins/toolu/hooks/lib/plan-ledger-preflight.sh +137 -0
- package/plugins/toolu/hooks/lib/plan-ledger.sh +749 -0
- package/plugins/toolu/hooks/lib/push-waiver.sh +116 -0
- package/plugins/toolu/hooks/lib/quality-config.sh +184 -0
- package/plugins/toolu/hooks/lib/registry.sh +72 -0
- package/plugins/toolu/hooks/lib/state-sweeper.sh +194 -0
- package/plugins/toolu/hooks/lib/telemetry.sh +94 -0
- package/plugins/toolu/hooks/lib/verdict.sh +477 -0
- package/plugins/toolu/hooks/post-tools/mod.sh +42 -0
- package/plugins/toolu/hooks/post-tools/modules/gate-status.sh +140 -0
- package/plugins/toolu/hooks/post-tools/modules/push-waiver.sh +97 -0
- package/plugins/toolu/hooks/pre-compact.sh +18 -0
- package/plugins/toolu/hooks/pre-tools/agent-tier.sh +111 -0
- package/plugins/toolu/hooks/pre-tools/mod.sh +37 -0
- package/plugins/toolu/hooks/pre-tools/modules/bash-commands.sh +187 -0
- package/plugins/toolu/hooks/pre-tools/modules/code-edit-rules.sh +78 -0
- package/plugins/toolu/hooks/pre-tools/modules/commit-gate.sh +70 -0
- package/plugins/toolu/hooks/pre-tools/modules/docs-sync.sh +137 -0
- package/plugins/toolu/hooks/pre-tools/modules/mcp-blocker.sh +151 -0
- package/plugins/toolu/hooks/pre-tools/modules/plan-ledger.sh +201 -0
- package/plugins/toolu/hooks/pre-tools/modules/protected-files.sh +177 -0
- package/plugins/toolu/hooks/pre-tools/modules/push-review.sh +273 -0
- package/plugins/toolu/hooks/pre-tools/modules/quality-gate.sh +87 -0
- package/plugins/toolu/hooks/session-start.sh +379 -0
- package/plugins/toolu/hooks/user-prompt-submit.sh +197 -0
- package/plugins/toolu/scripts/context-budget.sh +111 -0
- package/plugins/toolu/scripts/debug-log.sh +98 -0
- package/plugins/toolu/scripts/debug-stack.sh +108 -0
- package/plugins/toolu/scripts/debug-testfail.sh +97 -0
- package/plugins/toolu/settings/.gitkeep +0 -0
- package/plugins/toolu/settings/README.md +127 -0
- package/plugins/toolu/settings/bash-allowlist.txt +12 -0
- package/plugins/toolu/settings/bash-denylist.txt +25 -0
- package/plugins/toolu/settings/code-edit-rules.json +67 -0
- package/plugins/toolu/settings/commit-prefixes.txt +12 -0
- package/plugins/toolu/settings/mcp-blocklist.txt +8 -0
- package/plugins/toolu/settings/permissions.fragment.json +36 -0
- package/plugins/toolu/settings/protected-files.txt +30 -0
- package/plugins/toolu/settings/rust-unsafe-exemptions.txt +5 -0
- package/plugins/toolu/settings/toolu.config.example.json +130 -0
- package/plugins/toolu/skills/.gitkeep +0 -0
- package/plugins/toolu/skills/brainstorm/SKILL.md +57 -0
- package/plugins/toolu/skills/brainstorm/references/design-questions.md +36 -0
- package/plugins/toolu/skills/commit/SKILL.md +11 -0
- package/plugins/toolu/skills/debug/SKILL.md +54 -0
- package/plugins/toolu/skills/debug/evals/README.md +17 -0
- package/plugins/toolu/skills/debug/evals/cases.json +24 -0
- package/plugins/toolu/skills/debug/evals/results.json +34 -0
- package/plugins/toolu/skills/deep-research/SKILL.md +51 -0
- package/plugins/toolu/skills/execution/SKILL.md +77 -0
- package/plugins/toolu/skills/orchestrator/SKILL.md +145 -0
- package/plugins/toolu/skills/orchestrator/references/delegation-cost.md +104 -0
- package/plugins/toolu/skills/orchestrator/references/model-routing.md +112 -0
- package/plugins/toolu/skills/plan/SKILL.md +56 -0
- package/plugins/toolu/skills/plan/references/ledger.md +18 -0
- package/plugins/toolu/skills/plan-review/SKILL.md +48 -0
- package/plugins/toolu/skills/review-and-commit/SKILL.md +10 -0
- package/plugins/toolu/skills/setup/SKILL.md +24 -0
- package/plugins/toolu/skills/setup/scripts/setup.sh +206 -0
- package/plugins/toolu/skills/spec/SKILL.md +67 -0
- package/plugins/toolu/skills/spec-review/SKILL.md +50 -0
- package/plugins/toolu/skills/test/SKILL.md +49 -0
- package/plugins/toolu/workflows/commit.md +21 -0
- package/plugins/toolu/workflows/host-mapping.md +22 -0
- package/plugins/toolu/workflows/review-and-commit.md +54 -0
- package/plugins/toolu/workflows/semantic-judgments.md +35 -0
- package/plugins/toolu-review/.claude-plugin/plugin.json +18 -0
- package/plugins/toolu-review/.codex-plugin/plugin.json +7 -0
- package/plugins/toolu-review/README.md +17 -0
- package/plugins/toolu-review/hooks/hooks.json +15 -0
- package/plugins/toolu-review/hooks/session-start.sh +34 -0
- package/plugins/toolu-review/skills/review/SKILL.md +90 -0
- package/plugins/toolu-review/skills/review/scripts/write-state.sh +189 -0
- package/plugins/ts-quality/.claude-plugin/plugin.json +24 -0
- package/plugins/ts-quality/.codex-plugin/plugin.json +6 -0
- package/plugins/ts-quality/README.md +24 -0
- package/plugins/ts-quality/hooks/check-toolu.sh +20 -0
- package/plugins/ts-quality/hooks/concerns/00-preamble.sh +123 -0
- package/plugins/ts-quality/hooks/concerns/10-imports.sh +33 -0
- package/plugins/ts-quality/hooks/concerns/15-type-as.sh +9 -0
- package/plugins/ts-quality/hooks/concerns/20-tests.sh +26 -0
- package/plugins/ts-quality/hooks/concerns/25-size-file.sh +40 -0
- package/plugins/ts-quality/hooks/concerns/30-size-fn.sh +53 -0
- package/plugins/ts-quality/hooks/concerns/35-react-hooks.sh +7 -0
- package/plugins/ts-quality/hooks/concerns/40-factory.sh +5 -0
- package/plugins/ts-quality/hooks/concerns/45-typeguard.sh +6 -0
- package/plugins/ts-quality/hooks/concerns/50-type-dup.sh +16 -0
- package/plugins/ts-quality/hooks/concerns/55-naming.sh +11 -0
- package/plugins/ts-quality/hooks/concerns/60-console.sh +6 -0
- package/plugins/ts-quality/hooks/concerns/65-suppression.sh +12 -0
- package/plugins/ts-quality/hooks/concerns/70-ui-confirm.sh +8 -0
- package/plugins/ts-quality/hooks/concerns/72-ui-radix.sh +12 -0
- package/plugins/ts-quality/hooks/concerns/74-react-props.sh +6 -0
- package/plugins/ts-quality/hooks/concerns/76-toast.sh +10 -0
- package/plugins/ts-quality/hooks/concerns/78-error-ast.sh +242 -0
- package/plugins/ts-quality/hooks/concerns/80-throw-literal.sh +23 -0
- package/plugins/ts-quality/hooks/concerns/85-no-mocks.sh +109 -0
- package/plugins/ts-quality/hooks/concerns/90-duplication.sh +30 -0
- package/plugins/ts-quality/hooks/concerns/92-docs.sh +36 -0
- package/plugins/ts-quality/hooks/concerns/94-handler.sh +15 -0
- package/plugins/ts-quality/hooks/concerns/99-finalize.sh +46 -0
- package/plugins/ts-quality/hooks/hooks.json +19 -0
- package/plugins/ts-quality/hooks/register.sh +65 -0
- package/src/adapter/__tests__/evaluate.test.ts +82 -0
- package/src/adapter/__tests__/permission-map.test.ts +91 -0
- package/src/adapter/evaluate.ts +54 -0
- package/src/adapter/permission-map.ts +177 -0
- package/src/bootstrap/__tests__/bootstrap.test.ts +148 -0
- package/src/bootstrap/entrypoint.ts +18 -0
- package/src/bootstrap/index.ts +5 -0
- package/src/bootstrap/readiness.ts +59 -0
- package/src/bootstrap/result.ts +21 -0
- package/src/bootstrap/runtime.ts +99 -0
- package/src/bootstrap/test-helpers.ts +25 -0
- package/src/host/__tests__/host.test.ts +37 -0
- package/src/host/detect.ts +46 -0
- package/src/host/index.ts +10 -0
- package/src/host/roots.ts +59 -0
- package/src/host/types.ts +14 -0
- package/src/inventory/__tests__/inventory.test.ts +69 -0
- package/src/inventory/index.ts +9 -0
- package/src/inventory/manifest.ts +46 -0
- package/src/inventory/scan.ts +66 -0
- package/src/inventory/selection.ts +92 -0
- package/src/inventory/types.ts +22 -0
- package/src/lifecycle/__tests__/lifecycle.test.ts +7 -0
- package/src/lifecycle/index.ts +2 -0
- package/src/lifecycle/table.ts +42 -0
- package/src/plugin/__tests__/plugin-stub.test.ts +6 -0
- package/src/plugin/plugin-stub.ts +7 -0
- package/src/plugin/toolu.ts +142 -0
- package/src/preflight/__tests__/preflight.test.ts +20 -0
- package/src/preflight/check.ts +75 -0
- package/src/preflight/index.ts +2 -0
- package/src/select/__tests__/select.test.ts +52 -0
- package/src/select/index.ts +2 -0
- package/src/select/resolve.ts +87 -0
|
@@ -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
|
+
}
|