@codyswann/lisa 2.21.1 → 2.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -2
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/agents/confluence-prd-intake.md +11 -9
- package/plugins/lisa/agents/github-agent.md +18 -10
- package/plugins/lisa/agents/github-build-intake.md +10 -8
- package/plugins/lisa/agents/github-prd-intake.md +11 -9
- package/plugins/lisa/agents/jira-agent.md +12 -8
- package/plugins/lisa/agents/jira-build-intake.md +9 -7
- package/plugins/lisa/agents/learnings-synthesizer.md +1 -1
- package/plugins/lisa/agents/linear-agent.md +15 -9
- package/plugins/lisa/agents/linear-build-intake.md +13 -11
- package/plugins/lisa/agents/linear-prd-intake.md +11 -9
- package/plugins/lisa/agents/notion-prd-intake.md +11 -9
- package/plugins/lisa/agents/pr-mining-specialist.md +1 -1
- package/plugins/lisa/agents/tracker-mining-specialist.md +1 -1
- package/plugins/lisa/commands/setup/atlassian.md +7 -0
- package/plugins/lisa/commands/setup/confluence.md +7 -0
- package/plugins/lisa/commands/setup/jira.md +7 -0
- package/plugins/lisa/commands/setup/notion.md +7 -0
- package/plugins/lisa/hooks/enforce-team-first.sh +14 -6
- package/plugins/lisa/rules/base-rules.md +3 -3
- package/plugins/lisa/rules/config-resolution.md +242 -24
- package/plugins/lisa/rules/intent-routing.md +4 -4
- package/plugins/lisa/rules/repo-scope-split.md +41 -0
- package/plugins/lisa/rules/verification.md +13 -0
- package/plugins/lisa/skills/atlassian-access/SKILL.md +260 -0
- package/plugins/lisa/skills/confluence-prd-intake/SKILL.md +167 -82
- package/plugins/lisa/skills/confluence-to-tracker/SKILL.md +39 -26
- package/plugins/lisa/skills/debrief/SKILL.md +4 -5
- package/plugins/lisa/skills/github-add-journey/SKILL.md +1 -0
- package/plugins/lisa/skills/github-build-intake/SKILL.md +104 -40
- package/plugins/lisa/skills/github-evidence/SKILL.md +22 -5
- package/plugins/lisa/skills/github-prd-intake/SKILL.md +87 -51
- package/plugins/lisa/skills/github-to-tracker/SKILL.md +2 -2
- package/plugins/lisa/skills/github-validate-issue/SKILL.md +11 -1
- package/plugins/lisa/skills/implement/SKILL.md +5 -6
- package/plugins/lisa/skills/intake/SKILL.md +5 -6
- package/plugins/lisa/skills/jira-add-journey/SKILL.md +1 -0
- package/plugins/lisa/skills/jira-build-intake/SKILL.md +110 -45
- package/plugins/lisa/skills/jira-create/SKILL.md +5 -3
- package/plugins/lisa/skills/jira-evidence/SKILL.md +19 -2
- package/plugins/lisa/skills/jira-journey/SKILL.md +3 -1
- package/plugins/lisa/skills/jira-read-ticket/SKILL.md +10 -8
- package/plugins/lisa/skills/jira-sync/SKILL.md +11 -5
- package/plugins/lisa/skills/jira-validate-ticket/SKILL.md +22 -10
- package/plugins/lisa/skills/jira-verify/SKILL.md +5 -3
- package/plugins/lisa/skills/jira-write-ticket/SKILL.md +16 -14
- package/plugins/lisa/skills/linear-add-journey/SKILL.md +1 -0
- package/plugins/lisa/skills/linear-build-intake/SKILL.md +90 -32
- package/plugins/lisa/skills/linear-evidence/SKILL.md +22 -5
- package/plugins/lisa/skills/linear-prd-intake/SKILL.md +92 -57
- package/plugins/lisa/skills/linear-validate-issue/SKILL.md +10 -0
- package/plugins/lisa/skills/monitor/SKILL.md +4 -5
- package/plugins/lisa/skills/notion-access/SKILL.md +193 -0
- package/plugins/lisa/skills/notion-prd-intake/SKILL.md +105 -46
- package/plugins/lisa/skills/notion-to-tracker/SKILL.md +7 -5
- package/plugins/lisa/skills/plan/SKILL.md +4 -5
- package/plugins/lisa/skills/research/SKILL.md +4 -5
- package/plugins/lisa/skills/setup-atlassian/SKILL.md +316 -0
- package/plugins/lisa/skills/setup-confluence/SKILL.md +245 -0
- package/plugins/lisa/skills/setup-jira/SKILL.md +198 -0
- package/plugins/lisa/skills/setup-notion/SKILL.md +283 -0
- package/plugins/lisa/skills/task-decomposition/SKILL.md +2 -0
- package/plugins/lisa/skills/ticket-triage/SKILL.md +4 -1
- package/plugins/lisa/skills/tracker-evidence/SKILL.md +1 -0
- package/plugins/lisa/skills/verification-lifecycle/SKILL.md +2 -0
- package/plugins/lisa/skills/verify/SKILL.md +4 -5
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/skills/ops-browser-uat/SKILL.md +1 -1
- package/plugins/lisa-expo/skills/ops-check-logs/SKILL.md +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/skills/nestjs-graphql/references/project-patterns.md +48 -0
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/src/base/agents/confluence-prd-intake.md +11 -9
- package/plugins/src/base/agents/github-agent.md +18 -10
- package/plugins/src/base/agents/github-build-intake.md +10 -8
- package/plugins/src/base/agents/github-prd-intake.md +11 -9
- package/plugins/src/base/agents/jira-agent.md +12 -8
- package/plugins/src/base/agents/jira-build-intake.md +9 -7
- package/plugins/src/base/agents/learnings-synthesizer.md +1 -1
- package/plugins/src/base/agents/linear-agent.md +15 -9
- package/plugins/src/base/agents/linear-build-intake.md +13 -11
- package/plugins/src/base/agents/linear-prd-intake.md +11 -9
- package/plugins/src/base/agents/notion-prd-intake.md +11 -9
- package/plugins/src/base/agents/pr-mining-specialist.md +1 -1
- package/plugins/src/base/agents/tracker-mining-specialist.md +1 -1
- package/plugins/src/base/commands/setup/atlassian.md +7 -0
- package/plugins/src/base/commands/setup/confluence.md +7 -0
- package/plugins/src/base/commands/setup/jira.md +7 -0
- package/plugins/src/base/commands/setup/notion.md +7 -0
- package/plugins/src/base/hooks/enforce-team-first.sh +14 -6
- package/plugins/src/base/rules/base-rules.md +3 -3
- package/plugins/src/base/rules/config-resolution.md +242 -24
- package/plugins/src/base/rules/intent-routing.md +4 -4
- package/plugins/src/base/rules/repo-scope-split.md +41 -0
- package/plugins/src/base/rules/verification.md +13 -0
- package/plugins/src/base/skills/atlassian-access/SKILL.md +260 -0
- package/plugins/src/base/skills/confluence-prd-intake/SKILL.md +167 -82
- package/plugins/src/base/skills/confluence-to-tracker/SKILL.md +39 -26
- package/plugins/src/base/skills/debrief/SKILL.md +4 -5
- package/plugins/src/base/skills/github-add-journey/SKILL.md +1 -0
- package/plugins/src/base/skills/github-build-intake/SKILL.md +104 -40
- package/plugins/src/base/skills/github-evidence/SKILL.md +22 -5
- package/plugins/src/base/skills/github-prd-intake/SKILL.md +87 -51
- package/plugins/src/base/skills/github-to-tracker/SKILL.md +2 -2
- package/plugins/src/base/skills/github-validate-issue/SKILL.md +11 -1
- package/plugins/src/base/skills/implement/SKILL.md +5 -6
- package/plugins/src/base/skills/intake/SKILL.md +5 -6
- package/plugins/src/base/skills/jira-add-journey/SKILL.md +1 -0
- package/plugins/src/base/skills/jira-build-intake/SKILL.md +110 -45
- package/plugins/src/base/skills/jira-create/SKILL.md +5 -3
- package/plugins/src/base/skills/jira-evidence/SKILL.md +19 -2
- package/plugins/src/base/skills/jira-journey/SKILL.md +3 -1
- package/plugins/src/base/skills/jira-read-ticket/SKILL.md +10 -8
- package/plugins/src/base/skills/jira-sync/SKILL.md +11 -5
- package/plugins/src/base/skills/jira-validate-ticket/SKILL.md +22 -10
- package/plugins/src/base/skills/jira-verify/SKILL.md +5 -3
- package/plugins/src/base/skills/jira-write-ticket/SKILL.md +16 -14
- package/plugins/src/base/skills/linear-add-journey/SKILL.md +1 -0
- package/plugins/src/base/skills/linear-build-intake/SKILL.md +90 -32
- package/plugins/src/base/skills/linear-evidence/SKILL.md +22 -5
- package/plugins/src/base/skills/linear-prd-intake/SKILL.md +92 -57
- package/plugins/src/base/skills/linear-validate-issue/SKILL.md +10 -0
- package/plugins/src/base/skills/monitor/SKILL.md +4 -5
- package/plugins/src/base/skills/notion-access/SKILL.md +193 -0
- package/plugins/src/base/skills/notion-prd-intake/SKILL.md +105 -46
- package/plugins/src/base/skills/notion-to-tracker/SKILL.md +7 -5
- package/plugins/src/base/skills/plan/SKILL.md +4 -5
- package/plugins/src/base/skills/research/SKILL.md +4 -5
- package/plugins/src/base/skills/setup-atlassian/SKILL.md +316 -0
- package/plugins/src/base/skills/setup-confluence/SKILL.md +245 -0
- package/plugins/src/base/skills/setup-jira/SKILL.md +198 -0
- package/plugins/src/base/skills/setup-notion/SKILL.md +283 -0
- package/plugins/src/base/skills/task-decomposition/SKILL.md +2 -0
- package/plugins/src/base/skills/ticket-triage/SKILL.md +4 -1
- package/plugins/src/base/skills/tracker-evidence/SKILL.md +1 -0
- package/plugins/src/base/skills/verification-lifecycle/SKILL.md +2 -0
- package/plugins/src/base/skills/verify/SKILL.md +4 -5
- package/plugins/src/expo/skills/ops-browser-uat/SKILL.md +1 -1
- package/plugins/src/expo/skills/ops-check-logs/SKILL.md +1 -1
- package/plugins/src/nestjs/skills/nestjs-graphql/references/project-patterns.md +48 -0
- package/scripts/check-plugins-sync.sh +45 -0
|
@@ -32,11 +32,11 @@ What this rule still enforces:
|
|
|
32
32
|
> **Orchestration: agent team** (or **single agent**)
|
|
33
33
|
> One-sentence justification.
|
|
34
34
|
|
|
35
|
-
2. **Cascade rule (load-bearing)**: Before
|
|
35
|
+
2. **Cascade rule (load-bearing)**: Before creating a team, check whether you are already operating inside an agent team. Signs you are inside a team: a prior successful team-creation tool call exists in this session; you were spawned into a team context; your context references a team lead. If any of these are true, **do NOT create a second team** — many harnesses reject double-creates and the work stalls. Continue within the existing team. Invoke flows via the Skill tool; the team lead inherits responsibility for orchestration.
|
|
36
36
|
|
|
37
37
|
3. **Default mode**: `Research`, `Plan`, `Implement`, `Intake`, and `Debrief` run as agent teams. The `Implement` flow — including every work type (`Build`, `Fix`, `Improve`, `Investigate-Only`) — is **always** a team flow. Bug fixes that "look simple" are not an exception: the Reproduce sub-flow, debug-specialist, bug-fixer, parallel reviewers, and verification-specialist all need to compose. `Debrief` runs as a team because tracker-mining and pr-mining parallelize cleanly and synthesis gates on both completing. `Verify` (standalone) and `Monitor` (standalone) use the One-shot Sub-agents pattern (see `## Orchestration` below) — these flows are linear with no parallelism and the team overhead is not warranted. Single-agent mode is otherwise reserved for: `product-walkthrough` invoked standalone (not as part of Research/Plan), `debrief-apply` (deterministic routing of human-marked dispositions), and one-off diagnostic Bash/Read sessions that don't invoke any lifecycle skill. When in doubt, use a team.
|
|
38
38
|
|
|
39
|
-
The mechanical
|
|
39
|
+
The mechanical team bootstrap directive lives inside each lifecycle skill — see those skills' orchestration preambles for the exact wording. The Claude path uses `TeamCreate`; other runtimes must use their equivalent team-discovery and team-creation tools, or explicitly declare the no-team fallback when no such tool exists.
|
|
40
40
|
|
|
41
41
|
## Readiness Gate Protocol
|
|
42
42
|
|
|
@@ -364,7 +364,7 @@ Lifecycle skills dispatch their agents according to the flow's shape. The follow
|
|
|
364
364
|
|
|
365
365
|
### Agent Teams (default for multi-step flows)
|
|
366
366
|
|
|
367
|
-
Use an **agent team** (
|
|
367
|
+
Use an **agent team** (runtime team creation + durable task state per step) for:
|
|
368
368
|
|
|
369
369
|
- **Implement** (Build, Fix, Improve) — long sequences with parallel review and a real risk of compaction
|
|
370
370
|
- **Plan** — multiple specialists feeding a shared decomposition
|
|
@@ -390,7 +390,7 @@ Use direct `Agent` tool invocations (no team) for:
|
|
|
390
390
|
- **Investigate Only** spikes — single investigation, findings out
|
|
391
391
|
- Any flow chained as a sub-flow inside a larger team — its agents become tasks in the parent team, not a new team
|
|
392
392
|
|
|
393
|
-
Why:
|
|
393
|
+
Why: team creation plus per-step task state is real overhead. For a one-or-two-agent flow with no parallelism, the bookkeeping cost outweighs the recovery and orchestration benefits.
|
|
394
394
|
|
|
395
395
|
### When in doubt
|
|
396
396
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Repo Scope & Work-Time Splitting
|
|
2
|
+
|
|
3
|
+
Leaf work units are single-repo. A **leaf work unit** is an individually implementable ticket with no child tickets — issue types **Bug, Task, Sub-task, Improvement**. Each must name exactly one repository. **Epic, Story, Spike** are coordination containers and may span repos.
|
|
4
|
+
|
|
5
|
+
This invariant is enforced at three points: gate **S10** in the `*-validate-*` skills (write time), `task-decomposition` step 1.5 (PRD-decomposition time), and the work-time split procedure below (when an agent picks up an existing ticket to implement it).
|
|
6
|
+
|
|
7
|
+
## Two splitting strategies, by phase
|
|
8
|
+
|
|
9
|
+
The strategy depends on whether the tickets exist yet. Do not mix them.
|
|
10
|
+
|
|
11
|
+
- **Decomposition-time (greenfield — no tickets exist yet).** Use `task-decomposition` step 1.5: create one work unit per repo and group them under a **parent Story** (the cross-repo coordination container). Children are per-repo; the parent stays cross-repo. This is the right shape when you are creating the tickets from a PRD in the first place.
|
|
12
|
+
- **Work-time (a ticket already exists and an agent is about to implement it).** Use the procedure below: keep the original ticket, **narrow it to one repo**, spin off a **sibling** per additional repo, and link them with a dependency. Do **not** invent a new parent Story — re-homing an in-flight ticket's hierarchy is more disruptive than narrowing it. The siblings inherit the original's existing parent (Epic/Story/Project) if it has one.
|
|
13
|
+
|
|
14
|
+
## Work-time split procedure
|
|
15
|
+
|
|
16
|
+
When an agent reads an existing leaf work unit at the pre-flight gate (before any code is written) and the work touches more than one repo, it must STOP and split before proceeding. This is an agent-performed fix, not a product question — like auto-transitioning status, auto-splitting a cross-repo work unit is explicitly allowed (S10 is `product_relevant: false`: a cross-repo work unit is a decomposition error the agent owns, not something to bounce to the reporter).
|
|
17
|
+
|
|
18
|
+
1. **Detect the repos.** Parse the description, acceptance criteria, and technical approach for repo references, and confirm against the actual code surfaces the change requires. If the work fits in one repo, proceed normally — no split.
|
|
19
|
+
2. **Pick the repo the original keeps.** Default: the original retains the **consumer / user-facing repo** (e.g. frontend), because that is usually the ticket a stakeholder is watching and the one whose acceptance criteria describe the user-visible outcome. Each **producer repo** (e.g. backend) becomes a new sibling.
|
|
20
|
+
3. **Create one sibling per additional repo, cloning the original's metadata.** Carry over: summary (re-prefixed `[repo-name]`), the three audience sections, priority, labels/components, parent (Epic/Story/Project) if the original has one, target backend environment, sign-in requirements, and a Validation Journey scoped to that repo. Scope the acceptance criteria to that repo only.
|
|
21
|
+
4. **Link by dependency.** The producer repo **blocks** the consumer repo (`is blocked by` on the consumer / `blocks` on the producer), so execution order is explicit: the producing sibling ships first. When there is no clear producer/consumer direction, use `relates to`.
|
|
22
|
+
5. **Narrow the original.** Edit its summary prefix, its `Repository` section, and its acceptance criteria down to the retained repo only. Remove every cross-repo reference ("and the backend should also…").
|
|
23
|
+
6. **Comment on the original.** Note the split and link each new sibling so the history is auditable.
|
|
24
|
+
7. **Re-validate.** Run `tracker-verify` (which runs S10) against the original and each new sibling. Every one must now PASS single-repo scope. If any still fails, the split was incomplete — fix it before proceeding.
|
|
25
|
+
8. **Proceed in dependency order.** Implement the producer sibling(s) first, then the consumer (the narrowed original), respecting the `blocks` links.
|
|
26
|
+
|
|
27
|
+
### When to block instead of split
|
|
28
|
+
|
|
29
|
+
Auto-split only when the split is unambiguous. Fall back to the standard BLOCK + reassign-to-reporter path (see the pre-flight gate in `base-rules`) when:
|
|
30
|
+
|
|
31
|
+
- The repos touched cannot be determined confidently from the ticket and the code.
|
|
32
|
+
- Splitting would strand stakeholder context that only the reporter can re-scope (e.g. the acceptance criteria describe a single indivisible cross-repo behavior with no clean per-repo boundary).
|
|
33
|
+
- The metadata required to clone (parent, environment, credentials) is itself missing — block on the missing metadata first; do not propagate gaps into the siblings.
|
|
34
|
+
|
|
35
|
+
## Vendor mechanics
|
|
36
|
+
|
|
37
|
+
The procedure is vendor-neutral; the create + link + edit mechanics differ:
|
|
38
|
+
|
|
39
|
+
- **JIRA** — create via `mcp__atlassian__createJiraIssue` (clone fields, set the same epic parent); link via `mcp__atlassian__createIssueLink` with `Blocks` / `is blocked by` (resolve names via `mcp__atlassian__getIssueLinkTypes`); narrow the original via `mcp__atlassian__editJiraIssue`; comment via `mcp__atlassian__addCommentToJiraIssue`. See `jira-write-ticket` Phase 6.
|
|
40
|
+
- **GitHub** — create the sibling issue with the same labels and parent sub-issue; encode the dependency in the body (`Blocked by #<n>` / `Blocks #<n>`) and via the sub-issue/parent graph where used; edit the original's body to narrow scope. See `github-write-issue` Phase 6.
|
|
41
|
+
- **Linear** — create via `mcp__linear-server__save_issue` (clone fields, set the same `projectId`); add a blocking relation via the `relations` field or a paired relation call; edit the original to narrow scope. See `linear-write-issue` Phase 6.
|
|
@@ -96,6 +96,19 @@ Every change requires one or more verification types. Classify the change first,
|
|
|
96
96
|
|
|
97
97
|
---
|
|
98
98
|
|
|
99
|
+
## Per-Work-Unit Evidence Contract
|
|
100
|
+
|
|
101
|
+
Every **leaf work unit** — an individually implementable ticket with no child tickets (issue types Bug, Task, Sub-task, Improvement) — that changes runtime behavior must declare, at creation time, the exact evidence that proves it is done. Epics, Stories, and Spikes are coordination containers, not work units: their evidence is the rollup of their children, so this contract does not apply to them.
|
|
102
|
+
|
|
103
|
+
The declaration is not a separate field — it is the set of `[EVIDENCE: name]` markers in the work unit's **Validation Journey**. Those markers are the work unit's **evidence manifest**: an enumerated, named list of the artifacts a verifier must capture. The manifest binds both ends of the ticket lifecycle:
|
|
104
|
+
|
|
105
|
+
- **At creation** — the work unit cannot be written without a Validation Journey that names at least one `[EVIDENCE: name]` artifact (enforced by gate S14 in `tracker-validate` and the vendor `*-validate-*` skills). A behavior-changing unit should name both a success artifact and an error/edge artifact.
|
|
106
|
+
- **At completion** — the work unit cannot be marked complete, nor transitioned to its review/Done state, until every `[EVIDENCE: name]` marker in its manifest has a captured, non-empty artifact attached to the ticket (enforced by the Task Completion Rules and Definition of Done in `verification-lifecycle`, and by the evidence-manifest gate in `tracker-evidence`). A manifest with a missing or empty artifact blocks completion exactly like a failed verification.
|
|
107
|
+
|
|
108
|
+
The manifest is the single source of truth for "what evidence is required": authored once in the Validation Journey, enforced at write time, replayed during `tracker-journey`, and checked again before the ticket closes. There is no second list to keep in sync.
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
99
112
|
## Local vs Remote Verification
|
|
100
113
|
|
|
101
114
|
Verification happens at two stages in the workflow:
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: atlassian-access
|
|
3
|
+
description: "Vendor-neutral access layer for Atlassian (JIRA + Confluence). Every jira-* and confluence-* skill MUST delegate through this skill rather than calling Atlassian directly. Resolves a substrate per operation in this order: (1) acli if installed and its active profile matches the configured site, (2) Atlassian MCP if authenticated and the configured cloudId is in its accessible resources, (3) curl + API-token Basic auth. Verifies the active connection matches `.lisa.config.json` before every operation — substrates authenticated as a different Atlassian account are skipped, not used."
|
|
4
|
+
allowed-tools: ["Bash", "Read", "Skill"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Atlassian Access: $ARGUMENTS
|
|
8
|
+
|
|
9
|
+
Single chokepoint for all Atlassian operations. Routes each op to a substrate, enforces connection match, returns structured result. Caller skills (`jira-*`, `confluence-*`) MUST go through this — they MUST NOT invoke `acli` directly, call Atlassian MCP tools directly, or curl the Atlassian REST API themselves.
|
|
10
|
+
|
|
11
|
+
## Invocation contract
|
|
12
|
+
|
|
13
|
+
The caller passes one operation plus its arguments. Operations are listed in the dispatch table below. The skill returns either the structured operation result (JSON when the substrate provides it) or a clear error.
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
operation: read-ticket key: PROJ-123
|
|
17
|
+
operation: write-ticket payload: {...}
|
|
18
|
+
operation: transition key: PROJ-123 to: "In Review"
|
|
19
|
+
operation: comment key: PROJ-123 body: "..."
|
|
20
|
+
operation: search-issues jql: "project = SE AND status = Open"
|
|
21
|
+
operation: read-page id: 12345
|
|
22
|
+
operation: write-page payload: {...}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Workflow
|
|
26
|
+
|
|
27
|
+
### Step 1 — Substrate selection (per operation)
|
|
28
|
+
|
|
29
|
+
Read config:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
SITE=$(jq -r '.atlassian.site // empty' .lisa.config.json)
|
|
33
|
+
CLOUDID=$(jq -r '.atlassian.cloudId // empty' .lisa.config.json)
|
|
34
|
+
EMAIL=$(jq -r '.atlassian.email // empty' .lisa.config.local.json 2>/dev/null)
|
|
35
|
+
[ -z "$CLOUDID" ] && { echo "Error: atlassian.cloudId not set. Run /lisa:setup:atlassian." >&2; exit 1; }
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Probe each tier in order; the first that's ready AND identity-matches is the substrate for this operation. Identity-match is verified before any operation; substrates authenticated as a different Atlassian account are skipped, not used.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
substrate=""
|
|
42
|
+
|
|
43
|
+
# Tier 1: acli
|
|
44
|
+
if command -v acli >/dev/null 2>&1 && acli auth status >/dev/null 2>&1; then
|
|
45
|
+
current_site=$(acli auth status 2>/dev/null | awk '/^ Site:/{print $2}')
|
|
46
|
+
if [ "$current_site" = "$SITE" ]; then
|
|
47
|
+
substrate="acli"
|
|
48
|
+
else
|
|
49
|
+
# acli installed but pointing at a different site. Try switching profiles.
|
|
50
|
+
if acli auth switch --site "$SITE" ${EMAIL:+--email "$EMAIL"} >/dev/null 2>&1; then
|
|
51
|
+
substrate="acli"
|
|
52
|
+
fi
|
|
53
|
+
fi
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# Tier 2: Atlassian MCP (if acli not ready OR the operation isn't acli-covered)
|
|
57
|
+
# $OP_REQUIRES is a conceptual variable set by the dispatch table to "non-acli" for
|
|
58
|
+
# operations that have no acli adapter (e.g. read-page-descendants). It is not a real
|
|
59
|
+
# shell variable initialized here — the condition is illustrative pseudo-code.
|
|
60
|
+
if [ -z "$substrate" ] || [ "$OP_REQUIRES" = "non-acli" ]; then
|
|
61
|
+
# Probe via mcp__plugin_atlassian_atlassian__getAccessibleAtlassianResources.
|
|
62
|
+
# (Pseudo-code; actual call is the MCP tool invocation, not a bash command.)
|
|
63
|
+
# If the MCP returns a list and $CLOUDID is in it, MCP is identity-matched.
|
|
64
|
+
# If the MCP is unauthenticated or $CLOUDID is NOT in the list, MCP is skipped.
|
|
65
|
+
if mcp_atlassian_authenticated_and_matches_cloudid "$CLOUDID"; then
|
|
66
|
+
: ${substrate:=mcp}
|
|
67
|
+
# Mark MCP as available even if acli already won tier 1 — used for ops acli can't do.
|
|
68
|
+
mcp_available=true
|
|
69
|
+
fi
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# Tier 3: curl + API token (headless / multi-account / scoped-token path)
|
|
73
|
+
read_atlassian_token() {
|
|
74
|
+
local email="$1"
|
|
75
|
+
[ -n "$ATLASSIAN_API_TOKEN" ] && { echo "$ATLASSIAN_API_TOKEN"; return; }
|
|
76
|
+
local slug=$(echo "$email" | tr '[:upper:]@.' '[:lower:]__')
|
|
77
|
+
local varname="ATLASSIAN_API_TOKEN_${slug}"
|
|
78
|
+
[ -n "${!varname}" ] && { echo "${!varname}"; return; }
|
|
79
|
+
case "$(uname -s)" in
|
|
80
|
+
Darwin) security find-generic-password -s lisa-atlassian -a "$email" -w 2>/dev/null ;;
|
|
81
|
+
Linux) command -v secret-tool >/dev/null && secret-tool lookup service lisa-atlassian account "$email" 2>/dev/null ;;
|
|
82
|
+
MINGW*|MSYS*|CYGWIN*) cmdkey /list:"lisa-atlassian-${email}" 2>/dev/null | grep Password | awk '{print $NF}' ;;
|
|
83
|
+
esac
|
|
84
|
+
}
|
|
85
|
+
TOKEN=$(read_atlassian_token "$EMAIL")
|
|
86
|
+
[ -n "$TOKEN" ] && curl_available=true && : ${substrate:=curl}
|
|
87
|
+
|
|
88
|
+
# Fail loudly with actionable remediation if nothing works.
|
|
89
|
+
if [ -z "$substrate" ]; then
|
|
90
|
+
# Detect plugin enablement state for the suggestion.
|
|
91
|
+
plugin_enabled_global=$(jq -r '.enabledPlugins["atlassian@claude-plugins-official"] // false' ~/.claude/settings.json 2>/dev/null || echo "false")
|
|
92
|
+
plugin_enabled_project=$(jq -r '.enabledPlugins["atlassian@claude-plugins-official"] // false' .claude/settings.json 2>/dev/null || echo "false")
|
|
93
|
+
plugin_enabled_local=$(jq -r '.enabledPlugins["atlassian@claude-plugins-official"] // false' .claude/settings.local.json 2>/dev/null || echo "false")
|
|
94
|
+
|
|
95
|
+
cat >&2 <<EOF
|
|
96
|
+
Error: no Atlassian access substrate available for site $SITE.
|
|
97
|
+
|
|
98
|
+
Attempted:
|
|
99
|
+
acli — $(command -v acli >/dev/null && echo "installed but identity mismatch or unauthenticated" || echo "not installed")
|
|
100
|
+
MCP — $([ "$plugin_enabled_global" = "true" ] || [ "$plugin_enabled_project" = "true" ] || [ "$plugin_enabled_local" = "true" ] && echo "plugin enabled but not authenticated or cloudId $CLOUDID not in accessible resources" || echo "plugin not enabled in any settings.json scope")
|
|
101
|
+
curl — no ATLASSIAN_API_TOKEN found for $EMAIL (env, slug-suffixed env, or keychain)
|
|
102
|
+
|
|
103
|
+
Remediation paths (pick one):
|
|
104
|
+
|
|
105
|
+
1. Install the Atlassian MCP plugin (local scope — per-developer, gitignored).
|
|
106
|
+
This is the simplest path for single-account developers.
|
|
107
|
+
|
|
108
|
+
Run in your terminal:
|
|
109
|
+
|
|
110
|
+
jq '.enabledPlugins["atlassian@claude-plugins-official"] = true' \\
|
|
111
|
+
.claude/settings.local.json 2>/dev/null > /tmp/s && \\
|
|
112
|
+
mv /tmp/s .claude/settings.local.json || \\
|
|
113
|
+
echo '{"enabledPlugins":{"atlassian@claude-plugins-official":true}}' > .claude/settings.local.json
|
|
114
|
+
|
|
115
|
+
Then restart Claude Code (or run /restart-mcp) to load the plugin, and
|
|
116
|
+
invoke 'mcp__plugin_atlassian_atlassian__authenticate' to complete OAuth.
|
|
117
|
+
|
|
118
|
+
2. Install acli and authenticate (best for multi-account developers).
|
|
119
|
+
|
|
120
|
+
brew tap atlassian/homebrew-acli && brew install acli
|
|
121
|
+
acli auth login # OAuth as the account matching $EMAIL
|
|
122
|
+
|
|
123
|
+
3. Provision an API token (headless / CI / scoped-token environments).
|
|
124
|
+
|
|
125
|
+
Run /lisa:setup:atlassian — guided flow with clipboard-piped keychain store.
|
|
126
|
+
|
|
127
|
+
EOF
|
|
128
|
+
exit 1
|
|
129
|
+
fi
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Operation dispatch then uses `$substrate` for the primary route. If the operation has no `acli` adapter and `$substrate=acli`, fall through to `$mcp_available` then `$curl_available` for the actual call. The fall-through stops at the first available tier that can perform the operation.
|
|
133
|
+
|
|
134
|
+
### Step 2 — Connection-match check
|
|
135
|
+
|
|
136
|
+
The active connection MUST point at the cloudId/site declared in `.lisa.config.json`. Step 1's substrate selection already verifies this implicitly (substrates that don't match are skipped). This step is the explicit assertion before any operation runs — defensive in case the substrate state changed since selection.
|
|
137
|
+
|
|
138
|
+
Read configured site:
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
cloudid=$(jq -r '.atlassian.cloudId // empty' .lisa.config.json)
|
|
142
|
+
site=$(jq -r '.atlassian.site // empty' .lisa.config.json) # optional human-readable site URL
|
|
143
|
+
email=$(jq -r '.atlassian.email // empty' .lisa.config.json) # optional, for multi-account disambiguation
|
|
144
|
+
|
|
145
|
+
if [ -z "$cloudid" ]; then
|
|
146
|
+
echo "Error: atlassian.cloudId not set in .lisa.config.json. Run /lisa:setup:atlassian." >&2
|
|
147
|
+
exit 1
|
|
148
|
+
fi
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**CLI mode check**:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
# Compare active acli profile against config.
|
|
155
|
+
current=$(acli auth status --json 2>/dev/null)
|
|
156
|
+
current_site=$(echo "$current" | jq -r '.site // empty')
|
|
157
|
+
current_email=$(echo "$current" | jq -r '.email // empty')
|
|
158
|
+
|
|
159
|
+
if [ -n "$site" ] && [ "$current_site" != "$site" ]; then
|
|
160
|
+
# Profile mismatch — switch.
|
|
161
|
+
acli auth switch --site "$site" ${email:+--email "$email"}
|
|
162
|
+
fi
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
If `acli auth switch` fails because no matching profile exists, surface the error verbatim and instruct the caller to run `/lisa:setup:atlassian` to add the profile.
|
|
166
|
+
|
|
167
|
+
**curl mode check** (when the chosen op routes to curl):
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
# Validate the active ATLASSIAN_API_TOKEN points at the configured account by
|
|
171
|
+
# hitting /rest/api/3/myself and comparing emailAddress.
|
|
172
|
+
AUTH=$(printf '%s:%s' "$email" "$ATLASSIAN_API_TOKEN" | base64)
|
|
173
|
+
myself=$(curl -s -H "Authorization: Basic $AUTH" \
|
|
174
|
+
"https://${site}/rest/api/3/myself")
|
|
175
|
+
me_email=$(echo "$myself" | jq -r '.emailAddress // empty')
|
|
176
|
+
|
|
177
|
+
if [ -z "$me_email" ]; then
|
|
178
|
+
echo "Error: ATLASSIAN_API_TOKEN failed authentication against $site. Run /lisa:setup:atlassian to re-issue." >&2
|
|
179
|
+
exit 1
|
|
180
|
+
fi
|
|
181
|
+
if [ "$me_email" != "$email" ]; then
|
|
182
|
+
echo "Error: ATLASSIAN_API_TOKEN belongs to '$me_email', but .lisa.config.local.json declares '$email'. Multi-account misconfiguration." >&2
|
|
183
|
+
exit 1
|
|
184
|
+
fi
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
If validation fails, never silently proceed — abort and instruct the user to fix env.
|
|
188
|
+
|
|
189
|
+
### Step 3 — Operation dispatch
|
|
190
|
+
|
|
191
|
+
Substrate column meanings:
|
|
192
|
+
|
|
193
|
+
- **`acli`**: routes through `acli`. Preferred when available and identity-matched.
|
|
194
|
+
- **`MCP`**: routes through the Atlassian MCP. Preferred when acli can't do the op and the MCP is identity-matched (cloudId in `getAccessibleAtlassianResources`).
|
|
195
|
+
- **`curl`**: routes through curl + Basic auth + `ATLASSIAN_API_TOKEN`. Used when neither acli nor MCP is available.
|
|
196
|
+
- Multiple cells filled means tier ordering applies — try acli, then MCP, then curl, taking the first that has an adapter for the op AND is identity-matched.
|
|
197
|
+
- One cell means only that substrate can perform the op.
|
|
198
|
+
|
|
199
|
+
`<SITE>` = `.atlassian.site` (e.g. `propswap.atlassian.net`). `<CLOUDID>` = `.atlassian.cloudId`. `<AUTH>` = `Basic $(printf '%s:%s' "$email" "$ATLASSIAN_API_TOKEN" | base64)`. JIRA paths use `/rest/api/3/...`; Confluence uses `/wiki/rest/api/...` (v1) or `/api/v2/...` (v2).
|
|
200
|
+
|
|
201
|
+
| Operation | acli adapter | MCP adapter | curl adapter |
|
|
202
|
+
|---|---|---|---|
|
|
203
|
+
| **JIRA ops** | | | |
|
|
204
|
+
| `read-ticket key:<K>` | `acli jira workitem view <K> --json` | `mcp__plugin_atlassian_atlassian__getJiraIssue` | `GET https://<SITE>/rest/api/3/issue/<K>` |
|
|
205
|
+
| `write-ticket payload:<P>` (create) | `acli jira workitem create --from-json <P>` | `mcp__plugin_atlassian_atlassian__createJiraIssue` | `POST https://<SITE>/rest/api/3/issue` body=`<P>` |
|
|
206
|
+
| `write-ticket payload:<P>` (edit) | `acli jira workitem edit <K> --from-json <P>` | `mcp__plugin_atlassian_atlassian__editJiraIssue` | `PUT https://<SITE>/rest/api/3/issue/<K>` body=`<P>` |
|
|
207
|
+
| `transition key:<K> to:<S>` | `acli jira workitem transition --key <K> --status "<S>" --yes` | `mcp__plugin_atlassian_atlassian__transitionJiraIssue` | resolve transition id then `POST .../issue/<K>/transitions` |
|
|
208
|
+
| `transitions key:<K>` | (not exposed) | `mcp__plugin_atlassian_atlassian__getTransitionsForJiraIssue` | `GET https://<SITE>/rest/api/3/issue/<K>/transitions` |
|
|
209
|
+
| `comment key:<K> body:<B>` | `acli jira workitem comment add --key <K> --body "<B>"` | `mcp__plugin_atlassian_atlassian__addCommentToJiraIssue` | `POST https://<SITE>/rest/api/3/issue/<K>/comment` |
|
|
210
|
+
| `link from:<K> to:<K2> type:<T>` | `acli jira workitem link create --inward <K> --outward <K2> --type "<T>"` | `mcp__plugin_atlassian_atlassian__createJiraIssueLink` | `POST https://<SITE>/rest/api/3/issueLink` |
|
|
211
|
+
| `remote-links key:<K>` | (not exposed) | `mcp__plugin_atlassian_atlassian__getJiraIssueRemoteIssueLinks` | `GET https://<SITE>/rest/api/3/issue/<K>/remotelink` |
|
|
212
|
+
| `search-issues jql:<J>` | `acli jira workitem search --jql "<J>" --json` | `mcp__plugin_atlassian_atlassian__searchJiraIssuesUsingJql` | `POST https://<SITE>/rest/api/3/search/jql` |
|
|
213
|
+
| `list-projects` | `acli jira project list --paginate --json` | `mcp__plugin_atlassian_atlassian__getVisibleJiraProjects` | `GET https://<SITE>/rest/api/3/project/search` |
|
|
214
|
+
| `issue-type-metadata project:<K>` | `acli jira project view --key <K> --include-all --json` | `mcp__plugin_atlassian_atlassian__getJiraProjectIssueTypesMetadata` | `GET https://<SITE>/rest/api/3/issuetype/project?projectId=<id>` |
|
|
215
|
+
| **Confluence ops** | | | |
|
|
216
|
+
| `read-page id:<I>` | `acli confluence page view --id <I> --json` | `mcp__plugin_atlassian_atlassian__getConfluencePage` | `GET https://api.atlassian.com/ex/confluence/<CLOUDID>/wiki/rest/api/content/<I>` |
|
|
217
|
+
| `read-page-descendants id:<I>` | — | `mcp__plugin_atlassian_atlassian__getConfluencePageDescendants` | `GET .../content/<I>/descendant/page` |
|
|
218
|
+
| `read-page-comments id:<I> kind:<footer\|inline>` | — | `mcp__plugin_atlassian_atlassian__getConfluencePageFooterComments` / `getConfluencePageInlineComments` | `GET .../content/<I>/child/comment?location=<kind>` |
|
|
219
|
+
| `read-comment-children id:<C>` | — | `mcp__plugin_atlassian_atlassian__getConfluenceCommentChildren` | `GET .../content/<C>/child/comment` |
|
|
220
|
+
| `write-page payload:<P>` (create) | — | `mcp__plugin_atlassian_atlassian__createConfluencePage` | `POST .../wiki/rest/api/content` body=`<P>` |
|
|
221
|
+
| `write-page payload:<P>` (edit) | — | `mcp__plugin_atlassian_atlassian__updateConfluencePage` | `PUT .../content/<I>` body must include `version.number` bumped |
|
|
222
|
+
| `label-page id:<I> add:<L1,L2> remove:<L3,L4>` | — | (no v2 label-write endpoint on MCP) | (Atlassian gap; not used by lisa — see "Confluence PRD lifecycle" rule) |
|
|
223
|
+
| `comment-page id:<I> kind:<footer\|inline> body:<B>` | — | `mcp__plugin_atlassian_atlassian__createConfluenceFooterComment` / `createConfluenceInlineComment` | `POST .../content` body has `type=comment` |
|
|
224
|
+
| `search-pages cql:<Q>` | — | `mcp__plugin_atlassian_atlassian__searchConfluenceUsingCql` | `GET .../content/search?cql=<Q>` |
|
|
225
|
+
| `list-spaces` | `acli confluence space list --type global --json` | `mcp__plugin_atlassian_atlassian__getConfluenceSpaces` | `GET .../wiki/rest/api/space` |
|
|
226
|
+
| **Common ops** | | | |
|
|
227
|
+
| `list-sites` | `acli auth status --json` | `mcp__plugin_atlassian_atlassian__getAccessibleAtlassianResources` | `GET https://api.atlassian.com/oauth/token/accessible-resources` |
|
|
228
|
+
|
|
229
|
+
**Confluence v1 vs v2:** every Confluence curl path above uses **v1** (`/wiki/rest/api/...`). v1 is deprecated by Atlassian but as of writing remains functional for API-token Basic auth. The v2 API (`/api/v2/...`) requires *granular* OAuth scopes that aren't issued to Basic-auth API tokens consistently — so v1 is the safer path for now. When Atlassian fully retires v1, this table must move to v2 (the dispatch is the only thing that changes; the substrate-selection logic is unaffected).
|
|
230
|
+
|
|
231
|
+
**acli flag note:** acli's `--output` flag does not exist; the correct flag is `--json`. List commands require `--paginate` or `--limit` (no implicit fetch-all). Several documented adapters are nominal — verify against `acli <subcmd> --help` before relying on them. When acli's adapter is broken or missing for a specific op, fall through to MCP (if identity-matched) then curl per the tier ordering.
|
|
232
|
+
|
|
233
|
+
Operations not in this table are unsupported — add an adapter row before using them. Adapters MUST return a structured response (parse `acli`'s `--json`; jq-process curl's raw JSON).
|
|
234
|
+
|
|
235
|
+
### Payload conventions
|
|
236
|
+
|
|
237
|
+
- `write-ticket` payload: full JSON spec when creating; partial JSON (only changed fields, with `key` to identify) when editing. Adapters detect create vs edit by presence of `key`.
|
|
238
|
+
- `write-page` payload: supports a label-only mutation form — `{ "id": "<I>", "labels": { "add": [...], "remove": [...] } }` — so callers transitioning PRD lifecycle labels do not need to resend the page body. Full create/update payloads also accepted.
|
|
239
|
+
- `comment-page` `kind: inline` requires `anchor` (the highlighted text the comment attaches to). `kind: footer` ignores `anchor`.
|
|
240
|
+
|
|
241
|
+
### Step 4 — Return result
|
|
242
|
+
|
|
243
|
+
Emit either:
|
|
244
|
+
|
|
245
|
+
- The structured operation result (JSON object), wrapped in a `<result>` block for caller parsing, OR
|
|
246
|
+
- An error message prefixed with `Error:` and a remediation hint. Exit non-zero. Include the HTTP status code (curl) or acli exit code so callers can route on it.
|
|
247
|
+
|
|
248
|
+
Do not paraphrase substrate output beyond JSON normalization.
|
|
249
|
+
|
|
250
|
+
## Invariants
|
|
251
|
+
|
|
252
|
+
- Caller skills never invoke `acli` or `curl` against Atlassian directly. They only invoke this skill.
|
|
253
|
+
- Substrate is decided once per skill invocation and never switches mid-operation.
|
|
254
|
+
- Connection match is mandatory. Operations that bypass it (because "the user obviously meant the configured site") are forbidden.
|
|
255
|
+
- Profile mutations (`acli auth switch`) are allowed when acli is the active substrate. The curl substrate never mutates the token — if `ATLASSIAN_API_TOKEN` doesn't match the configured account, fail loud rather than silently substituting.
|
|
256
|
+
- `.lisa.config.local.json` overrides `.lisa.config.json` per-key — the same precedence rule as every other consumer of project config.
|
|
257
|
+
|
|
258
|
+
## Headless behavior
|
|
259
|
+
|
|
260
|
+
In a headless / non-interactive context (no TTY, `CI=true`, or `-p` mode), the MCP tier is unavailable (its OAuth flow needs a browser). The substrate ladder collapses to: acli (if pre-authenticated, e.g., a CI image baked with a service-account token) → curl + `ATLASSIAN_API_TOKEN`. Never block on interactive prompts. If both fail readiness checks, exit non-zero with a deterministic error.
|