@codyswann/lisa 2.21.0 → 2.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/package.json +3 -2
  2. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  3. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  4. package/plugins/lisa/agents/confluence-prd-intake.md +11 -9
  5. package/plugins/lisa/agents/github-agent.md +18 -10
  6. package/plugins/lisa/agents/github-build-intake.md +10 -8
  7. package/plugins/lisa/agents/github-prd-intake.md +11 -9
  8. package/plugins/lisa/agents/jira-agent.md +12 -8
  9. package/plugins/lisa/agents/jira-build-intake.md +9 -7
  10. package/plugins/lisa/agents/linear-agent.md +15 -9
  11. package/plugins/lisa/agents/linear-build-intake.md +13 -11
  12. package/plugins/lisa/agents/linear-prd-intake.md +11 -9
  13. package/plugins/lisa/agents/notion-prd-intake.md +11 -9
  14. package/plugins/lisa/commands/setup/atlassian.md +7 -0
  15. package/plugins/lisa/commands/setup/confluence.md +7 -0
  16. package/plugins/lisa/commands/setup/jira.md +7 -0
  17. package/plugins/lisa/commands/setup/notion.md +7 -0
  18. package/plugins/lisa/rules/base-rules.md +2 -2
  19. package/plugins/lisa/rules/config-resolution.md +242 -24
  20. package/plugins/lisa/rules/repo-scope-split.md +41 -0
  21. package/plugins/lisa/rules/verification.md +13 -0
  22. package/plugins/lisa/skills/atlassian-access/SKILL.md +260 -0
  23. package/plugins/lisa/skills/confluence-prd-intake/SKILL.md +167 -82
  24. package/plugins/lisa/skills/confluence-to-tracker/SKILL.md +39 -26
  25. package/plugins/lisa/skills/git-submit-pr/SKILL.md +1 -1
  26. package/plugins/lisa/skills/github-add-journey/SKILL.md +1 -0
  27. package/plugins/lisa/skills/github-build-intake/SKILL.md +104 -40
  28. package/plugins/lisa/skills/github-evidence/SKILL.md +22 -5
  29. package/plugins/lisa/skills/github-prd-intake/SKILL.md +87 -51
  30. package/plugins/lisa/skills/github-to-tracker/SKILL.md +2 -2
  31. package/plugins/lisa/skills/github-validate-issue/SKILL.md +11 -1
  32. package/plugins/lisa/skills/jira-add-journey/SKILL.md +1 -0
  33. package/plugins/lisa/skills/jira-build-intake/SKILL.md +110 -45
  34. package/plugins/lisa/skills/jira-create/SKILL.md +5 -3
  35. package/plugins/lisa/skills/jira-evidence/SKILL.md +19 -2
  36. package/plugins/lisa/skills/jira-journey/SKILL.md +3 -1
  37. package/plugins/lisa/skills/jira-read-ticket/SKILL.md +10 -8
  38. package/plugins/lisa/skills/jira-sync/SKILL.md +11 -5
  39. package/plugins/lisa/skills/jira-validate-ticket/SKILL.md +22 -10
  40. package/plugins/lisa/skills/jira-verify/SKILL.md +5 -3
  41. package/plugins/lisa/skills/jira-write-ticket/SKILL.md +16 -14
  42. package/plugins/lisa/skills/linear-add-journey/SKILL.md +1 -0
  43. package/plugins/lisa/skills/linear-build-intake/SKILL.md +90 -32
  44. package/plugins/lisa/skills/linear-evidence/SKILL.md +22 -5
  45. package/plugins/lisa/skills/linear-prd-intake/SKILL.md +92 -57
  46. package/plugins/lisa/skills/linear-validate-issue/SKILL.md +10 -0
  47. package/plugins/lisa/skills/notion-access/SKILL.md +193 -0
  48. package/plugins/lisa/skills/notion-prd-intake/SKILL.md +105 -46
  49. package/plugins/lisa/skills/notion-to-tracker/SKILL.md +7 -5
  50. package/plugins/lisa/skills/setup-atlassian/SKILL.md +316 -0
  51. package/plugins/lisa/skills/setup-confluence/SKILL.md +245 -0
  52. package/plugins/lisa/skills/setup-jira/SKILL.md +198 -0
  53. package/plugins/lisa/skills/setup-notion/SKILL.md +283 -0
  54. package/plugins/lisa/skills/task-decomposition/SKILL.md +2 -0
  55. package/plugins/lisa/skills/ticket-triage/SKILL.md +4 -1
  56. package/plugins/lisa/skills/tracker-evidence/SKILL.md +1 -0
  57. package/plugins/lisa/skills/verification-lifecycle/SKILL.md +2 -0
  58. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  60. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  62. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  63. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  64. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  66. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  68. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  69. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  70. package/plugins/src/base/agents/confluence-prd-intake.md +11 -9
  71. package/plugins/src/base/agents/github-agent.md +18 -10
  72. package/plugins/src/base/agents/github-build-intake.md +10 -8
  73. package/plugins/src/base/agents/github-prd-intake.md +11 -9
  74. package/plugins/src/base/agents/jira-agent.md +12 -8
  75. package/plugins/src/base/agents/jira-build-intake.md +9 -7
  76. package/plugins/src/base/agents/linear-agent.md +15 -9
  77. package/plugins/src/base/agents/linear-build-intake.md +13 -11
  78. package/plugins/src/base/agents/linear-prd-intake.md +11 -9
  79. package/plugins/src/base/agents/notion-prd-intake.md +11 -9
  80. package/plugins/src/base/commands/setup/atlassian.md +7 -0
  81. package/plugins/src/base/commands/setup/confluence.md +7 -0
  82. package/plugins/src/base/commands/setup/jira.md +7 -0
  83. package/plugins/src/base/commands/setup/notion.md +7 -0
  84. package/plugins/src/base/rules/base-rules.md +2 -2
  85. package/plugins/src/base/rules/config-resolution.md +242 -24
  86. package/plugins/src/base/rules/repo-scope-split.md +41 -0
  87. package/plugins/src/base/rules/verification.md +13 -0
  88. package/plugins/src/base/skills/atlassian-access/SKILL.md +260 -0
  89. package/plugins/src/base/skills/confluence-prd-intake/SKILL.md +167 -82
  90. package/plugins/src/base/skills/confluence-to-tracker/SKILL.md +39 -26
  91. package/plugins/src/base/skills/git-submit-pr/SKILL.md +1 -1
  92. package/plugins/src/base/skills/github-add-journey/SKILL.md +1 -0
  93. package/plugins/src/base/skills/github-build-intake/SKILL.md +104 -40
  94. package/plugins/src/base/skills/github-evidence/SKILL.md +22 -5
  95. package/plugins/src/base/skills/github-prd-intake/SKILL.md +87 -51
  96. package/plugins/src/base/skills/github-to-tracker/SKILL.md +2 -2
  97. package/plugins/src/base/skills/github-validate-issue/SKILL.md +11 -1
  98. package/plugins/src/base/skills/jira-add-journey/SKILL.md +1 -0
  99. package/plugins/src/base/skills/jira-build-intake/SKILL.md +110 -45
  100. package/plugins/src/base/skills/jira-create/SKILL.md +5 -3
  101. package/plugins/src/base/skills/jira-evidence/SKILL.md +19 -2
  102. package/plugins/src/base/skills/jira-journey/SKILL.md +3 -1
  103. package/plugins/src/base/skills/jira-read-ticket/SKILL.md +10 -8
  104. package/plugins/src/base/skills/jira-sync/SKILL.md +11 -5
  105. package/plugins/src/base/skills/jira-validate-ticket/SKILL.md +22 -10
  106. package/plugins/src/base/skills/jira-verify/SKILL.md +5 -3
  107. package/plugins/src/base/skills/jira-write-ticket/SKILL.md +16 -14
  108. package/plugins/src/base/skills/linear-add-journey/SKILL.md +1 -0
  109. package/plugins/src/base/skills/linear-build-intake/SKILL.md +90 -32
  110. package/plugins/src/base/skills/linear-evidence/SKILL.md +22 -5
  111. package/plugins/src/base/skills/linear-prd-intake/SKILL.md +92 -57
  112. package/plugins/src/base/skills/linear-validate-issue/SKILL.md +10 -0
  113. package/plugins/src/base/skills/notion-access/SKILL.md +193 -0
  114. package/plugins/src/base/skills/notion-prd-intake/SKILL.md +105 -46
  115. package/plugins/src/base/skills/notion-to-tracker/SKILL.md +7 -5
  116. package/plugins/src/base/skills/setup-atlassian/SKILL.md +316 -0
  117. package/plugins/src/base/skills/setup-confluence/SKILL.md +245 -0
  118. package/plugins/src/base/skills/setup-jira/SKILL.md +198 -0
  119. package/plugins/src/base/skills/setup-notion/SKILL.md +283 -0
  120. package/plugins/src/base/skills/task-decomposition/SKILL.md +2 -0
  121. package/plugins/src/base/skills/ticket-triage/SKILL.md +4 -1
  122. package/plugins/src/base/skills/tracker-evidence/SKILL.md +1 -0
  123. package/plugins/src/base/skills/verification-lifecycle/SKILL.md +2 -0
  124. package/scripts/check-plugins-sync.sh +45 -0
@@ -0,0 +1,198 @@
1
+ ---
2
+ name: setup-jira
3
+ description: "Configure JIRA as the destination tracker for this project. Writes `jira.project` into `.lisa.config.json` and offers to set top-level `tracker: \"jira\"`. Depends on /lisa:setup:atlassian — atlassian.cloudId must already be present, otherwise this skill stops and instructs the user to run setup-atlassian first. Idempotent."
4
+ allowed-tools: ["Bash", "Read", "Write", "Edit", "Skill", "AskUserQuestion"]
5
+ ---
6
+
7
+ # Setup JIRA: $ARGUMENTS
8
+
9
+ Set the JIRA project key and (optionally) make JIRA this project's destination tracker.
10
+
11
+ ## Workflow
12
+
13
+ ### Step 1 — Verify atlassian prerequisite
14
+
15
+ ```bash
16
+ cloudid=$(jq -r '.atlassian.cloudId // empty' .lisa.config.json 2>/dev/null)
17
+ if [ -z "$cloudid" ]; then
18
+ echo "Error: atlassian.cloudId not set. Run /lisa:setup:atlassian first." >&2
19
+ exit 1
20
+ fi
21
+ ```
22
+
23
+ If `atlassian` is missing, invoke `/lisa:setup-atlassian` via the Skill tool first, then resume.
24
+
25
+ ### Step 2 — Resolve the JIRA project key
26
+
27
+ Honor any `--project=KEY` argument. Otherwise, list projects via the active access substrate:
28
+
29
+ - CLI: `acli jira project list --output json`
30
+ - MCP: `mcp__plugin_atlassian_atlassian__getVisibleJiraProjects` with `cloudId=$cloudid`
31
+
32
+ If only one project is returned, pick it. If multiple, present them via `AskUserQuestion` (label = project key, description = project name) so the user picks the one this project should target.
33
+
34
+ ### Step 3 — Probe workflow & resolve role mapping
35
+
36
+ Lisa's build lifecycle uses three role-keyed statuses (`ready` → `claimed` → `done[env]`). Defaults are `Ready`, `In Progress`, and `{dev: "On Dev", staging: "On Stg", production: "Done"}`. Probe the project's workflow to confirm these exist; prompt for overrides if not.
37
+
38
+ ```bash
39
+ # Fetch a sample ticket's available transitions (or use a project-level workflow scheme query)
40
+ # via lisa:atlassian-access operation: transitions key: <SAMPLE-KEY>
41
+ # Collect the union of status names visible in the project.
42
+ ```
43
+
44
+ For each role:
45
+
46
+ 1. If a status with the **default name** exists in the workflow → use the default; no config entry needed.
47
+ 2. If the default name is missing but a status with a similar role exists (e.g. `Resolved` instead of `Done`) → present the project's status list via `AskUserQuestion` and let the user pick which maps to the role.
48
+ 3. If nothing plausible exists → stop and tell the user to either add the missing status to their JIRA workflow (admin task) or pick a fallback that's close enough.
49
+
50
+ Collect overrides as a partial workflow map. ONLY write keys that differ from defaults — don't bloat the config with redundant values.
51
+
52
+ ### Step 4 — Write `jira.project` + overrides
53
+
54
+ ```bash
55
+ # Always write project key.
56
+ jq --arg key "$PROJECT_KEY" \
57
+ '.jira = ((.jira // {}) | .project = $key)' \
58
+ .lisa.config.json > .lisa.config.json.tmp \
59
+ && mv .lisa.config.json.tmp .lisa.config.json
60
+
61
+ # Conditionally write workflow overrides (only if any role differs from default).
62
+ if [ -n "$WORKFLOW_OVERRIDES_JSON" ]; then
63
+ jq --argjson wf "$WORKFLOW_OVERRIDES_JSON" \
64
+ '.jira.workflow = $wf' \
65
+ .lisa.config.json > .lisa.config.json.tmp \
66
+ && mv .lisa.config.json.tmp .lisa.config.json
67
+ fi
68
+ ```
69
+
70
+ ### Step 5 — Verify transition reachability (A → B → C cascade)
71
+
72
+ Confirm every resolved role's status name is actually reachable in the JIRA workflow. **acli has no non-destructive workflow inspector** (the `workitem view --json` `transitions` key is always `null` because acli doesn't pass `expand=transitions`), so verification cascades through three modes, each more invasive than the last.
73
+
74
+ #### Cache check (skip if unchanged)
75
+
76
+ Hash the workflow-mapping object plus the project key:
77
+
78
+ ```bash
79
+ WORKFLOW_HASH=$(jq -c '{project: .jira.project, workflow: .jira.workflow}' .lisa.config.json | shasum -a 256 | awk '{print $1}')
80
+ CACHED=$(jq -r '.jira.verified_workflow_hash // empty' .lisa.config.local.json 2>/dev/null)
81
+ if [ "$WORKFLOW_HASH" = "$CACHED" ]; then
82
+ echo "Workflow already verified; skipping probe."
83
+ # proceed to Step 6
84
+ fi
85
+ ```
86
+
87
+ The verification cache key lives in `.lisa.config.local.json` (per-developer, gitignored) because the workflow on disk might be stale relative to the user's local view of it.
88
+
89
+ #### Mode A — Changelog inspection (non-destructive, fast)
90
+
91
+ Aggregate every status name ever observed in the project's ticket histories:
92
+
93
+ ```bash
94
+ acli jira workitem search --jql "project = $PROJECT" --paginate --json \
95
+ | jq -r '[.[].changelog.histories[]?.items[]? | select(.field == "status") | .fromString, .toString] | unique[]' \
96
+ | sort -u > /tmp/lisa-observed-statuses.txt
97
+ ```
98
+
99
+ For each role (`ready`, `claimed`, `blocked`, each `done.<env>`), check whether the resolved status name appears in the observed set. Roles that match are **verified**. Roles that don't are **unverified** — but that doesn't yet mean they're missing; freshly-added statuses simply haven't accumulated history.
100
+
101
+ If every role is verified, cache the hash and proceed to Step 6.
102
+
103
+ #### Mode B — Create-probe-delete (synthetic ticket)
104
+
105
+ For any role left unverified by Mode A, fall back to creating a throwaway ticket and walking it through.
106
+
107
+ Prompt via `AskUserQuestion`:
108
+
109
+ > Mode A could not verify these roles: `<list>`. Create a synthetic probe ticket to verify (recommended), or designate an existing ticket to use as the probe?
110
+
111
+ If user picks synthetic:
112
+
113
+ ```bash
114
+ # Create a probe ticket. Title makes its purpose obvious so a teammate doesn't act on it.
115
+ PROBE_KEY=$(acli jira workitem create \
116
+ --project "$PROJECT" \
117
+ --type Task \
118
+ --summary "lisa-setup-probe (DELETE ME)" \
119
+ --description "Created by /lisa:setup:jira to verify workflow status reachability. Safe to delete." \
120
+ --json \
121
+ | jq -r '.key')
122
+
123
+ trap 'acli jira workitem delete --key "$PROBE_KEY" --yes 2>/dev/null || acli jira workitem archive --key "$PROBE_KEY" --yes 2>/dev/null' EXIT
124
+
125
+ # Walk through every unverified role's status in order.
126
+ for status in $UNVERIFIED_STATUSES; do
127
+ if ! acli jira workitem transition --key "$PROBE_KEY" --status "$status" --yes 2>/dev/null; then
128
+ echo "Error: status '$status' is not reachable in the workflow from the probe ticket's current state." >&2
129
+ # Mark this role as missing; continue with remaining roles where possible (skip if transition is from a state we couldn't reach).
130
+ break
131
+ fi
132
+ done
133
+
134
+ # trap handles cleanup
135
+ ```
136
+
137
+ If `acli jira workitem create` fails because the project has required fields, surface the field list and instruct the user to either (a) fill them in via `--field`/`--from-json`, (b) temporarily relax the required-fields configuration, or (c) fall through to Mode C.
138
+
139
+ #### Mode C — Probe an existing ticket (last resort)
140
+
141
+ If Mode B can't create a probe (mandatory fields, permission, etc.), prompt the user to designate a low-stakes existing ticket:
142
+
143
+ > Provide a ticket key in `To Do` (or wherever you want to start the probe). I'll walk it through the unverified statuses and revert.
144
+
145
+ Capture the ticket's starting status; walk through the unverified chain; revert to the starting status at the end. Use `trap` to ensure revert runs even on error.
146
+
147
+ #### Outcome
148
+
149
+ Once all roles are verified by some combination of A/B/C, cache the result:
150
+
151
+ ```bash
152
+ jq --arg hash "$WORKFLOW_HASH" \
153
+ '.jira = ((.jira // {}) | .verified_workflow_hash = $hash)' \
154
+ .lisa.config.local.json > .lisa.config.local.json.tmp \
155
+ && mv .lisa.config.local.json.tmp .lisa.config.local.json
156
+ ```
157
+
158
+ If any role is **unreachable** after all three modes, stop and surface a concrete admin-task message (which transition needs to be added, between which two statuses, in which workflow). Do not write a config that points at unreachable transitions.
159
+
160
+ ### Step 6 — Verify mid-build transition chain
161
+
162
+ Independent of role reachability, confirm the lifecycle's required *transition graph* is intact: `ready → claimed`, `claimed → done.<env>` for each env. Mode A's changelog includes paired `(fromString, toString)` entries — count any pair that matches a required transition as verified. Modes B and C do this implicitly by walking the chain in order. If any required transition is missing (e.g., `claimed → done.staging` requires going through `review` first per the workflow), surface and stop with an admin remediation.
163
+
164
+ ### Step 7 — Offer to set top-level `tracker`
165
+
166
+ If `.tracker` is unset or differs from `"jira"`, ask via `AskUserQuestion`:
167
+
168
+ > JIRA project `<KEY>` written. Set top-level `tracker: "jira"` so all vendor-neutral skills target JIRA?
169
+
170
+ Recommend "Yes" unless the project is using a different destination (e.g., GitHub Issues for build-queue but JIRA for read-only mirror — rare).
171
+
172
+ If yes:
173
+
174
+ ```bash
175
+ jq '.tracker = "jira"' .lisa.config.json > .lisa.config.json.tmp \
176
+ && mv .lisa.config.json.tmp .lisa.config.json
177
+ ```
178
+
179
+ ### Step 8 — Verify
180
+
181
+ ```bash
182
+ jq -e '.jira.project' .lisa.config.json >/dev/null
183
+ jq -e '.tracker' .lisa.config.json >/dev/null # if user said yes in step 7
184
+ ```
185
+
186
+ Report success with the resolved project key, the workflow mapping (defaults vs. overrides), and whether `tracker` was set.
187
+
188
+ ## Idempotency
189
+
190
+ - Re-running replaces `jira.project` cleanly (jq merge, not append).
191
+ - Re-running does not re-prompt for `tracker` if it's already `"jira"`.
192
+ - Re-running with an unchanged workflow mapping skips the verification probe (Mode A/B/C) entirely via the `verified_workflow_hash` cache in `.lisa.config.local.json`. Editing any role name invalidates the cache and re-runs the probe.
193
+
194
+ ## Rules
195
+
196
+ - Never invent a project key. If discovery fails (no projects visible), surface the error and ask the user to verify their JIRA permissions.
197
+ - Never set `tracker` without explicit user confirmation — `tracker` is project-wide and switching it changes every downstream skill's behavior.
198
+ - Never write env-level config (api tokens, server URLs) into `.lisa.config.json`.
@@ -0,0 +1,283 @@
1
+ ---
2
+ name: setup-notion
3
+ description: "Configure Notion as the PRD source for this project. Walks the user through creating an internal integration in the target workspace, sharing the PRD database with it, stores the resulting `ntn_*` token in OS keychain (multi-workspace-safe — keyed by workspaceId), validates against the Notion API, and writes `notion.workspaceId`, `notion.prdDatabaseId`, and `notion.values` into `.lisa.config.json`. Idempotent — re-runs update the existing section rather than duplicating it. Offers to set top-level `source: \"notion\"`."
4
+ allowed-tools: ["Bash", "Read", "Write", "Edit", "Skill", "AskUserQuestion"]
5
+ ---
6
+
7
+ # Setup Notion: $ARGUMENTS
8
+
9
+ Provision Notion access for this project. After this skill runs, `.lisa.config.json` contains `notion.workspaceId` and `notion.prdDatabaseId`, and the OS keychain has a `lisa-notion` entry keyed by the workspaceId.
10
+
11
+ ## Workflow
12
+
13
+ ### Step 0 — Pick a setup path
14
+
15
+ Ask via `AskUserQuestion`:
16
+
17
+ > How do you want lisa to talk to Notion for this project?
18
+ >
19
+ > 1. **MCP-only (simplest)** — authenticate the Notion MCP once via browser OAuth; lisa uses it for every operation. Best for: single-workspace developers on a personal laptop. New developers onboard with one OAuth flow, no token sharing, no rotation pain when someone leaves. Skip the rest of this setup.
20
+ > 2. **MCP + internal-integration token (recommended for teams)** — MCP for interactive dev, internal-integration token in keychain for headless / CI / multi-workspace. Continue through token-create steps.
21
+ > 3. **Token-only (headless / CI)** — store a workspace-scoped internal-integration token in the OS keychain; lisa uses curl for everything. Best for: CI pipelines, headless containers. Continue through token-create steps.
22
+
23
+ If the user picks (1) and the MCP is already authenticated to the right workspace (verify by attempting to fetch the configured `prdDatabaseId` via the MCP — success means identity match), write only `notion.workspaceId`, `notion.prdDatabaseId`, and `notion.statusProperty` into `.lisa.config.json` and skip to Step 8 (top-level source offer). If the MCP isn't authed yet, instruct the user to run `mcp__claude_ai_Notion__authenticate` (or the plugin equivalent) and complete the OAuth flow, then re-verify. The PRD database still needs to be shared with the OAuth-granted access — Notion's per-page sharing model applies to OAuth identities the same way it does to internal-integration tokens.
24
+
25
+ If the user picks (2) or (3), continue through the rest of the steps; the token gets stored in addition to (or instead of) the MCP session.
26
+
27
+ ### Step 1 — Open the Notion integration page
28
+
29
+ ```bash
30
+ case "$(uname -s)" in
31
+ Darwin) open "https://www.notion.so/profile/integrations" ;;
32
+ Linux) xdg-open "https://www.notion.so/profile/integrations" 2>/dev/null ;;
33
+ MINGW*|MSYS*|CYGWIN*) start "https://www.notion.so/profile/integrations" ;;
34
+ esac
35
+ ```
36
+
37
+ Print instructions for the user:
38
+
39
+ ```
40
+ 1. Click "New integration".
41
+ 2. Name it: lisa-<project-name> (e.g., lisa-gemini, lisa-acme — pick something descriptive).
42
+ 3. Associated workspace: pick the workspace your PRDs live in.
43
+ 4. Type: "Internal integration".
44
+ 5. Capabilities: leave defaults (Read content, Update content, Insert content). No comment / user-info capabilities needed.
45
+ 6. Click Save.
46
+ 7. On the integration's detail page, click "Show" next to "Internal Integration Token".
47
+ 8. Copy the token — starts with `ntn_`. Atlassian-style scoped tokens have a `=<CRC>` suffix; Notion's do NOT, but tokens are still 50+ chars. Watch for clipboard-truncation in some terminals.
48
+ ```
49
+
50
+ ### Step 2 — Identify the workspace
51
+
52
+ The user picks a stable identifier for this workspace. Two options:
53
+
54
+ - **Workspace name** (human-readable, e.g., `Gemini Sports`). Easy to recognize, can be ambiguous if a workspace is renamed in Notion. Recommended.
55
+ - **Workspace UUID** (returned by Notion's API). Stable but opaque.
56
+
57
+ Default to the workspace name. After the user stores the token (Step 4), Step 5's `/users/me` call surfaces the actual `bot.workspace_name`; if it differs from what the user typed (capitalization, trailing whitespace), prompt to confirm.
58
+
59
+ ```bash
60
+ WORKSPACE=$(jq -r '.notion.workspaceId // empty' .lisa.config.json 2>/dev/null)
61
+ if [ -z "$WORKSPACE" ]; then
62
+ # Prompt the user — accept any non-empty string. They pick the slug; we just store it.
63
+ read -p "Workspace identifier (any stable slug, e.g. 'gemini-sports'): " WORKSPACE
64
+ fi
65
+ ```
66
+
67
+ ### Step 3 — Share the PRD database with the integration
68
+
69
+ This is **non-optional**. Notion's permission model is share-based — the integration cannot see any pages or databases until the user explicitly grants access.
70
+
71
+ Print instructions:
72
+
73
+ ```
74
+ 1. In Notion, navigate to your PRD database (or the parent page containing it).
75
+ 2. Click the "..." menu in the top right of the database.
76
+ 3. Click "Connections".
77
+ 4. Find "lisa-<project>" in the list and click "Connect".
78
+ 5. Confirm any prompts about granting access.
79
+
80
+ The connection cascades to all child pages of the database by default. New PRDs added under the database automatically inherit access.
81
+ ```
82
+
83
+ If `--database=<uuid>` was passed in `$ARGUMENTS`, use it; otherwise prompt:
84
+
85
+ ```bash
86
+ DATABASE_ID=$(jq -r '.notion.prdDatabaseId // empty' .lisa.config.json 2>/dev/null)
87
+ if [ -z "$DATABASE_ID" ]; then
88
+ cat <<EOF
89
+ Paste the PRD database URL or ID:
90
+ - URL form: https://notion.so/<workspace>/<database-id>?v=...
91
+ - ID form: 32-char UUID with or without dashes
92
+ EOF
93
+ read -p "Database: " DB_INPUT
94
+ # Extract UUID from URL if needed.
95
+ DATABASE_ID=$(echo "$DB_INPUT" | grep -oE '[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}' | head -1)
96
+ if [ -z "$DATABASE_ID" ]; then
97
+ echo "Error: could not extract a UUID from '$DB_INPUT'." >&2
98
+ exit 1
99
+ fi
100
+ fi
101
+ ```
102
+
103
+ ### Step 4 — Store the token via OS keychain (token never enters chat)
104
+
105
+ Same security posture as `setup-atlassian`. Print a platform-specific clipboard-pipe command for the user to run **in their own terminal**:
106
+
107
+ ```bash
108
+ case "$(uname -s)" in
109
+ Darwin)
110
+ cat <<EOF
111
+ 1. Copy the integration token from the Notion page.
112
+ 2. Run this single line in your terminal (leading space keeps it out of zsh history):
113
+
114
+ security delete-generic-password -s lisa-notion -a "$WORKSPACE" 2>/dev/null; TOK="\$(pbpaste)"; security add-generic-password -U -s lisa-notion -a "$WORKSPACE" -w "\$TOK"; unset TOK
115
+
116
+ The token is piped from clipboard straight to keychain — never enters the prompt or chat.
117
+ EOF
118
+ ;;
119
+ Linux)
120
+ if command -v secret-tool >/dev/null 2>&1; then
121
+ if command -v wl-paste >/dev/null 2>&1; then CLIP=wl-paste
122
+ elif command -v xclip >/dev/null 2>&1; then CLIP="xclip -selection clipboard -o"
123
+ elif command -v xsel >/dev/null 2>&1; then CLIP="xsel --clipboard --output"
124
+ else CLIP="cat"
125
+ fi
126
+ cat <<EOF
127
+ 1. Copy the integration token.
128
+ 2. Run this in your terminal:
129
+
130
+ secret-tool clear service lisa-notion account "$WORKSPACE" 2>/dev/null; printf '%s' "\$($CLIP)" | secret-tool store --label="Lisa Notion ($WORKSPACE)" service lisa-notion account "$WORKSPACE"
131
+
132
+ (If no clipboard tool is installed: the command reads from stdin — paste, Ctrl-D.)
133
+ EOF
134
+ else
135
+ cat <<EOF
136
+ libsecret / secret-tool not installed. Options:
137
+ 1. Install: sudo apt install libsecret-tools (then re-run /lisa:setup:notion).
138
+ 2. Env-var fallback (headless / CI / Docker):
139
+ export NOTION_API_TOKEN_$(echo "$WORKSPACE" | tr '[:upper:]-' '[:lower:]_')="<paste-token>"
140
+ Plaintext on disk — only acceptable on ephemeral / CI environments.
141
+ EOF
142
+ fi
143
+ ;;
144
+ MINGW*|MSYS*|CYGWIN*)
145
+ cat <<EOF
146
+ PowerShell:
147
+
148
+ \$tok = Get-Clipboard; cmdkey /generic:"lisa-notion-$WORKSPACE" /user:"$WORKSPACE" /pass:"\$tok"; Remove-Variable tok
149
+ EOF
150
+ ;;
151
+ esac
152
+ ```
153
+
154
+ **Never accept the token via chat or stdin into this skill.** Wait for the user to confirm storage.
155
+
156
+ ### Step 5 — Verify the token + workspace match
157
+
158
+ Use the same lookup ladder `notion-access` uses:
159
+
160
+ ```bash
161
+ read_notion_token() {
162
+ local workspace="$1"
163
+ [ -n "$NOTION_API_TOKEN" ] && { echo "$NOTION_API_TOKEN"; return; }
164
+ local slug=$(echo "$workspace" | tr '[:upper:]-' '[:lower:]_')
165
+ local varname="NOTION_API_TOKEN_${slug}"
166
+ [ -n "${!varname}" ] && { echo "${!varname}"; return; }
167
+ case "$(uname -s)" in
168
+ Darwin) security find-generic-password -s lisa-notion -a "$workspace" -w 2>/dev/null ;;
169
+ Linux) command -v secret-tool >/dev/null && secret-tool lookup service lisa-notion account "$workspace" 2>/dev/null ;;
170
+ MINGW*|MSYS*|CYGWIN*) cmdkey /list:"lisa-notion-${workspace}" 2>/dev/null | grep Password | awk '{print $NF}' ;;
171
+ esac
172
+ }
173
+
174
+ TOKEN=$(read_notion_token "$WORKSPACE")
175
+ if [ -z "$TOKEN" ]; then
176
+ echo "Error: token not retrievable after store. Re-run Step 4." >&2
177
+ exit 1
178
+ fi
179
+
180
+ # Notion tokens — sanity length check. Internal-integration tokens are ~50+ chars; if drastically shorter, a paste truncation happened.
181
+ if [ ${#TOKEN} -lt 40 ]; then
182
+ echo "Warning: token is ${#TOKEN} chars — Notion tokens are typically 50+. Possible truncation." >&2
183
+ fi
184
+
185
+ ME=$(curl -s -H "Authorization: Bearer $TOKEN" \
186
+ -H "Notion-Version: 2022-06-28" \
187
+ "https://api.notion.com/v1/users/me")
188
+ ME_WORKSPACE=$(echo "$ME" | jq -r '.bot.workspace_name // empty')
189
+
190
+ if [ -z "$ME_WORKSPACE" ]; then
191
+ echo "Error: token failed Notion /users/me probe. Response: $ME" >&2
192
+ exit 1
193
+ fi
194
+
195
+ # If the user typed a workspace name that differs from what Notion returns, prompt to align.
196
+ if [ "$ME_WORKSPACE" != "$WORKSPACE" ]; then
197
+ cat <<EOF
198
+ The token belongs to workspace '$ME_WORKSPACE', but you provided '$WORKSPACE' as the identifier.
199
+ Use the Notion-returned name for consistency? (recommended — connection-match check uses this string)
200
+ EOF
201
+ # AskUserQuestion: replace WORKSPACE with $ME_WORKSPACE? recommended yes
202
+ fi
203
+
204
+ # Verify database visibility too (Notion's share model means the token sees only what's been shared).
205
+ DB_PROBE=$(curl -s -o /tmp/setup-notion-db -w "%{http_code}" \
206
+ -H "Authorization: Bearer $TOKEN" -H "Notion-Version: 2022-06-28" \
207
+ "https://api.notion.com/v1/databases/$DATABASE_ID")
208
+ if [ "$DB_PROBE" != "200" ]; then
209
+ cat >&2 <<EOF
210
+ Error: integration cannot see database $DATABASE_ID (HTTP $DB_PROBE).
211
+ The most likely cause is that you skipped Step 3 — sharing the database with the integration.
212
+ Open the database in Notion → "..." → Connections → add 'lisa-<project>' → retry.
213
+ EOF
214
+ exit 1
215
+ fi
216
+
217
+ echo "Token validated. Workspace: $ME_WORKSPACE. Database visible."
218
+ ```
219
+
220
+ ### Step 6 — Detect lifecycle value names
221
+
222
+ Read the database schema and find the `Status` property's value list. Compare to lisa defaults and prompt for overrides if names differ.
223
+
224
+ ```bash
225
+ DB_SCHEMA=$(curl -s -H "Authorization: Bearer $TOKEN" -H "Notion-Version: 2022-06-28" \
226
+ "https://api.notion.com/v1/databases/$DATABASE_ID")
227
+ STATUS_PROP=$(jq -r '.properties | to_entries[] | select(.value.type == "status" or .value.type == "select") | .key' <<<"$DB_SCHEMA" | head -1)
228
+ STATUS_VALUES=$(jq -r --arg p "$STATUS_PROP" '.properties[$p] | (.status.options // .select.options) | .[].name' <<<"$DB_SCHEMA")
229
+ ```
230
+
231
+ For each lisa role (`draft`, `ready`, `in_review`, `blocked`, `ticketed`, `shipped`), check if its default name (`Draft`, `Ready`, etc.) appears in `$STATUS_VALUES`. If a role's default is missing but a similar-looking value exists, prompt the user to map it via `AskUserQuestion`. If a role has no plausible match, prompt to either create the value in Notion or accept that the lifecycle stage is unrepresented.
232
+
233
+ Collect overrides as a partial values map. Only write keys that differ from defaults.
234
+
235
+ ### Step 7 — Write `.lisa.config.json`
236
+
237
+ ```bash
238
+ jq --arg ws "$WORKSPACE" --arg db "$DATABASE_ID" --arg sp "$STATUS_PROP" --argjson values "$VALUES_JSON" '
239
+ .notion = ((.notion // {})
240
+ | .workspaceId = $ws
241
+ | .prdDatabaseId = $db
242
+ | (if $sp != "" then .statusProperty = $sp else . end)
243
+ | (if $values != {} then .values = $values else . end))
244
+ ' .lisa.config.json > .lisa.config.json.tmp \
245
+ && mv .lisa.config.json.tmp .lisa.config.json
246
+ ```
247
+
248
+ `VALUES_JSON` is `{}` if all roles use the default names; otherwise contains only the overrides.
249
+
250
+ ### Step 8 — Offer to set top-level `source`
251
+
252
+ If `.source` is unset or differs from `"notion"`, ask via `AskUserQuestion`:
253
+
254
+ > Notion is configured. Set top-level `source: "notion"` so `/lisa:intake` (with no args) scans this database for PRDs?
255
+
256
+ If yes:
257
+
258
+ ```bash
259
+ jq '.source = "notion"' .lisa.config.json > .lisa.config.json.tmp \
260
+ && mv .lisa.config.json.tmp .lisa.config.json
261
+ ```
262
+
263
+ ### Step 9 — Verify
264
+
265
+ ```bash
266
+ jq -e '.notion.workspaceId and .notion.prdDatabaseId' .lisa.config.json >/dev/null
267
+ echo "Token validated (${#TOKEN} chars). Workspace: $ME_WORKSPACE. Database: $DATABASE_ID."
268
+ ```
269
+
270
+ Report success with the resolved workspace, database, status property name, and value overrides (if any). Direct the user to `/lisa:intake` to test.
271
+
272
+ ## Idempotency
273
+
274
+ - Re-running this skill replaces fields in the `notion` section without disturbing others. The keychain entry update in Step 4 is the user's manual action — they re-run the same `security` / `secret-tool` / `cmdkey` command.
275
+ - If `notion.workspaceId` and `notion.prdDatabaseId` already exist in config, skip the prompts in Steps 2–3 and go straight to verification.
276
+
277
+ ## Rules
278
+
279
+ - Never write the token to `.lisa.config.json`. Tokens stay in keychain or env.
280
+ - Never accept a token via this skill's stdin. Always go through the platform's clipboard-pipe pattern so the value never enters the LLM context.
281
+ - Never auto-create the Notion integration via API — Notion offers no programmatic creation flow, and adding one would require building lisa as a public OAuth app (out of scope here).
282
+ - Never proceed past Step 5 with an unverified token + workspace. Silent cross-workspace operations are exactly the multi-account hazard this design exists to prevent.
283
+ - If the user has multiple Notion accounts, each project's `.lisa.config.local.json` `notion.workspaceId` is the sole disambiguator. There is no "active workspace" concept on the Notion side — the token IS the workspace binding.
@@ -36,6 +36,8 @@ If a candidate work unit naturally touches multiple repos (e.g., "add field to b
36
36
 
37
37
  Reject any work unit whose acceptance criteria reference behavior in a different repo from the one it's scoped to. If you find yourself writing "and the frontend should also...", that's a signal to split.
38
38
 
39
+ This is the **decomposition-time** strategy (greenfield — you are creating the tickets now, so a parent Story + per-repo children is the natural shape). It is distinct from the **work-time** strategy in the `repo-scope-split` rule, which applies when an agent picks up an *already-existing* ticket to implement and discovers it spans repos: there it narrows the original in place and spins off sibling work units rather than introducing a new parent. Use the phase-appropriate one; do not mix them.
40
+
39
41
  ### 2. Define Acceptance Criteria
40
42
 
41
43
  For each task, define what "done" looks like:
@@ -19,11 +19,14 @@ Before any analytical work, confirm the ticket carries the content an implemente
19
19
  - Validation Journey missing on a runtime-behavior ticket
20
20
  - Target backend environment missing on a runtime-behavior ticket
21
21
  - Sign-in credentials missing on a ticket that touches authenticated surfaces
22
- - Single-repo scope violated (Bug / Task / Sub-task spanning repos)
23
22
  - Relationship discovery missing (no links AND no documented git + tracker-search outcome)
24
23
 
24
+ Single-repo scope is handled separately — see below.
25
+
25
26
  The caller (jira-agent or github-agent) is responsible for transitioning the ticket to `Blocked` (JIRA status) or relabeling to `status:blocked` (GitHub), reassigning to the **Reporter / original author**, and posting a comment listing the missing requirements. This skill only emits the verdict and the missing-requirements list.
26
27
 
28
+ **Single-repo scope is split, not blocked.** A cross-repo leaf work unit (Bug / Task / Sub-task / Improvement spanning repos) is a decomposition error the agent owns, not a terminal BLOCKED. Do not emit BLOCKED for it. The caller runs the **work-time split procedure** in the `repo-scope-split` rule (narrow the original to one repo, spin off a sibling per additional repo, link the producer→consumer dependency), then re-runs `tracker-verify` and re-enters triage on the now single-repo ticket. Only fall back to BLOCKED if the split is ambiguous (see "When to block instead of split" in that rule).
29
+
27
30
  If `lisa:tracker-verify` returned `PASS` for all the above, proceed to Phase 1.
28
31
 
29
32
  ## Phase 1 -- Relevance Check
@@ -24,6 +24,7 @@ See the `config-resolution` rule for configuration and dispatch table.
24
24
 
25
25
  - The GitHub `pr-assets` release lives on the implementation repo (the one with the PR), regardless of which tracker hosts the ticket/issue. All vendor skills upload there.
26
26
  - Never post evidence to a different ticket than the one named — `$ARGUMENTS` is the source of truth.
27
+ - **Evidence-manifest gate (leaf work units).** Before dispatching to a vendor skill that transitions the ticket, confirm `EVIDENCE_DIR` contains a non-empty artifact for every `[EVIDENCE: name]` marker declared in the ticket's Validation Journey. If any declared marker has no captured artifact (or only an empty one), stop and report the missing markers by name instead of posting — a leaf work unit (Bug / Task / Sub-task / Improvement) may not advance to its review/Done state with an unsatisfied manifest (see the "Per-Work-Unit Evidence Contract" in the `verification` rule). Epics / Stories / Spikes, and leaf units without a Validation Journey, are exempt.
27
28
 
28
29
  ## UI Evidence Checklist (when work is UI-visible)
29
30
 
@@ -228,6 +228,7 @@ Agents must follow this sequence unless explicitly instructed otherwise:
228
228
  3. **If verification fails**: Fix and re-run, don't mark complete
229
229
  4. **If verification blocked** (missing tools, services, etc.): Mark as blocked, not complete
230
230
  5. **Must not be dependent on CI/CD** if necessary, you may use local deploy methods found in the project manifest, but the verification methods must be listed in the pull request and therefore cannot be dependent on CI/CD completing
231
+ 6. **Evidence manifest satisfied (leaf work units)**: For a leaf work unit (Bug / Task / Sub-task / Improvement) whose ticket carries a Validation Journey, do not mark the ticket complete or transition it out of in-progress until every `[EVIDENCE: name]` marker declared on the ticket has a corresponding captured, non-empty artifact attached to the ticket. A missing or empty artifact for any declared marker blocks completion exactly like a failed verification — fix and re-capture, or escalate; never close with an unsatisfied manifest. Epics / Stories / Spikes are exempt (coordination containers, not work units).
231
232
 
232
233
  ---
233
234
 
@@ -330,6 +331,7 @@ A task is done only when:
330
331
  - Required verification surfaces and tooling surfaces are used or explicitly unavailable
331
332
  - Proof artifacts are captured
332
333
  - Every passing empirical verification is codified as a regression test (or has an explicit, documented skip reason from the allowed set)
334
+ - For a leaf work unit, every `[EVIDENCE: name]` marker declared in its Validation Journey has a captured, non-empty artifact attached to the ticket (the evidence manifest is fully satisfied)
333
335
  - Spec conformance verdict is `CONFORMS` (not `PARTIAL`, not `DIVERGES`)
334
336
  - Verification level is declared
335
337
  - Risks and gaps are documented
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env bash
2
+ # Fails if the generated plugin directories (plugins/lisa, plugins/lisa-*) are
3
+ # out of sync with their source (plugins/src). This guards against editing the
4
+ # build artifact directly: build-plugins.sh runs `rm -rf plugins/lisa && cp -r
5
+ # plugins/src/base`, so any artifact-only edit is silently discarded on the next
6
+ # build/release. Two real PRs (#471, #478) were wiped this way before this check
7
+ # existed.
8
+ #
9
+ # The check is a reproducibility test: regenerate the artifacts from source and
10
+ # assert the working tree is unchanged. A diff means either (a) someone edited
11
+ # plugins/lisa** directly instead of plugins/src**, or (b) someone edited
12
+ # plugins/src** but forgot to run `bun run build:plugins` and commit the result.
13
+ set -euo pipefail
14
+
15
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
16
+ ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
17
+ cd "$ROOT_DIR"
18
+
19
+ if [ -n "$(git status --porcelain -- plugins/ 2>/dev/null)" ]; then
20
+ echo "✗ plugins/ has uncommitted changes before the sync check could run." >&2
21
+ echo " Commit or stash them, then re-run: bun run check:plugins" >&2
22
+ exit 1
23
+ fi
24
+
25
+ bun run build:plugins >/dev/null
26
+
27
+ if ! git diff --quiet -- plugins/; then
28
+ echo "✗ Generated plugin artifacts are out of sync with plugins/src." >&2
29
+ echo "" >&2
30
+ echo " Files that changed after rebuilding from source:" >&2
31
+ git --no-pager diff --name-only -- plugins/ | sed 's/^/ /' >&2
32
+ echo "" >&2
33
+ echo " plugins/lisa and plugins/lisa-* are GENERATED from plugins/src by" >&2
34
+ echo " 'bun run build:plugins'. Never edit them directly — the next build" >&2
35
+ echo " overwrites the change." >&2
36
+ echo "" >&2
37
+ echo " Fix:" >&2
38
+ echo " 1. Make your edit under plugins/src/<base|stack>/..." >&2
39
+ echo " 2. Run: bun run build:plugins" >&2
40
+ echo " 3. Commit both plugins/src and the regenerated plugins/lisa*." >&2
41
+ # Leave the tree dirty so the diff is inspectable locally; CI fails on exit.
42
+ exit 1
43
+ fi
44
+
45
+ echo "✓ Plugin artifacts are in sync with plugins/src."