@codyswann/lisa 3.4.0 → 3.5.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/README.md +22 -0
- package/all/copy-contents/gitignore +7 -0
- package/all/copy-overwrite/scripts/lisa-command-envelope.mjs +44 -4
- package/all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh +31 -0
- package/all/copy-overwrite/scripts/lisa-schema-validate.mjs +125 -47
- package/all/copy-overwrite/scripts/lisa-work-item.mjs +24 -2
- package/cdk/merge/.oxlintrc.json +1 -1
- package/dist/cli/apply.d.ts.map +1 -1
- package/dist/cli/apply.js +48 -13
- package/dist/cli/apply.js.map +1 -1
- package/dist/cli/doctor-apply-freshness.d.ts +29 -0
- package/dist/cli/doctor-apply-freshness.d.ts.map +1 -0
- package/dist/cli/doctor-apply-freshness.js +194 -0
- package/dist/cli/doctor-apply-freshness.js.map +1 -0
- package/dist/cli/doctor-learnings-merge-driver.d.ts +23 -0
- package/dist/cli/doctor-learnings-merge-driver.d.ts.map +1 -0
- package/dist/cli/doctor-learnings-merge-driver.js +73 -0
- package/dist/cli/doctor-learnings-merge-driver.js.map +1 -0
- package/dist/cli/doctor-readiness-workflows.js +2 -2
- package/dist/cli/doctor-readiness-workflows.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +13 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/ui-ci-quality-jobs-parse.js +2 -2
- package/dist/cli/ui-ci-quality-jobs-parse.js.map +1 -1
- package/dist/cli/ui-deploy-pipeline-model.d.ts +4 -0
- package/dist/cli/ui-deploy-pipeline-model.d.ts.map +1 -1
- package/dist/cli/ui-deploy-pipeline-model.js +2 -2
- package/dist/cli/ui-deploy-pipeline-model.js.map +1 -1
- package/dist/codex/agent-transformer.js +2 -2
- package/dist/codex/agent-transformer.js.map +1 -1
- package/dist/codex/command-skill-transformer.js +3 -3
- package/dist/codex/command-skill-transformer.js.map +1 -1
- package/dist/codex/scripts/block-no-verify.sh +53 -4
- package/dist/core/apply-receipt.d.ts +68 -0
- package/dist/core/apply-receipt.d.ts.map +1 -0
- package/dist/core/apply-receipt.js +107 -0
- package/dist/core/apply-receipt.js.map +1 -0
- package/dist/core/learnings-merge-driver-install.d.ts +34 -0
- package/dist/core/learnings-merge-driver-install.d.ts.map +1 -1
- package/dist/core/learnings-merge-driver-install.js +36 -0
- package/dist/core/learnings-merge-driver-install.js.map +1 -1
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +62 -39
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/migrations/ensure-lisa-postinstall.d.ts +33 -0
- package/dist/migrations/ensure-lisa-postinstall.d.ts.map +1 -1
- package/dist/migrations/ensure-lisa-postinstall.js +47 -5
- package/dist/migrations/ensure-lisa-postinstall.js.map +1 -1
- package/dist/migrations/ensure-oxlint-base-configs.d.ts +42 -0
- package/dist/migrations/ensure-oxlint-base-configs.d.ts.map +1 -0
- package/dist/migrations/ensure-oxlint-base-configs.js +212 -0
- package/dist/migrations/ensure-oxlint-base-configs.js.map +1 -0
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +3 -0
- package/dist/migrations/index.js.map +1 -1
- package/dist/opencode/command-transformer.js +3 -3
- package/dist/opencode/command-transformer.js.map +1 -1
- package/dist/utils/yaml.d.ts +54 -0
- package/dist/utils/yaml.d.ts.map +1 -0
- package/dist/utils/yaml.js +124 -0
- package/dist/utils/yaml.js.map +1 -0
- package/expo/copy-overwrite/scripts/bdd/baseline.mjs +104 -19
- package/expo/copy-overwrite/scripts/bdd/contract.mjs +20 -1
- package/expo/copy-overwrite/scripts/bdd/discover.mjs +53 -10
- package/expo/copy-overwrite/scripts/bdd/parse.mjs +25 -3
- package/expo/copy-overwrite/scripts/bdd/render.mjs +2 -1
- package/expo/copy-overwrite/scripts/bdd/report.mjs +26 -12
- package/expo/copy-overwrite/scripts/bdd/validate.mjs +2 -2
- package/expo/copy-overwrite/scripts/bdd/waivers.mjs +19 -4
- package/expo/copy-overwrite/scripts/bdd-matrix.mjs +2 -2
- package/expo/copy-overwrite/scripts/check-bdd-coverage.mjs +58 -16
- package/expo/create-only/.github/required-checks.json +10 -4
- package/expo/merge/.oxlintrc.json +1 -1
- package/harper-fabric/merge/.oxlintrc.json +1 -1
- package/nestjs/create-only/.github/required-checks.json +57 -0
- package/nestjs/merge/.oxlintrc.json +1 -1
- package/package.json +1 -1
- package/phaser/merge/.oxlintrc.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-atlassian-access/SKILL.md +7 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-jam-access/SKILL.md +37 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-notion-access/SKILL.md +7 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-posthog-access/SKILL.md +40 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-sentry-access/SKILL.md +44 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-sonarcloud-access/SKILL.md +56 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-track/SKILL.md +1 -1
- package/plugins/lisa/hooks/block-no-verify.agy.sh +53 -4
- package/plugins/lisa/hooks/block-no-verify.sh +31 -0
- package/plugins/lisa/rules/eager/credential-substrate-precedence.md +3 -1
- package/plugins/lisa/rules/eager/tracked-work.md +1 -1
- package/plugins/lisa/rules/reference/credential-substrate-precedence.md +20 -0
- package/plugins/lisa/rules/reference/tracked-work.md +4 -2
- package/plugins/lisa/skills/lisa-atlassian-access/SKILL.md +7 -0
- package/plugins/lisa/skills/lisa-implement/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-jam-access/SKILL.md +37 -2
- package/plugins/lisa/skills/lisa-notion-access/SKILL.md +7 -0
- package/plugins/lisa/skills/lisa-posthog-access/SKILL.md +40 -3
- package/plugins/lisa/skills/lisa-sentry-access/SKILL.md +44 -3
- package/plugins/lisa/skills/lisa-sonarcloud-access/SKILL.md +56 -0
- package/plugins/lisa/skills/lisa-track/SKILL.md +1 -1
- package/plugins/lisa-agy/hooks/block-no-verify.agy.sh +53 -4
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-atlassian-access/SKILL.md +7 -0
- package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-jam-access/SKILL.md +37 -2
- package/plugins/lisa-agy/skills/lisa-notion-access/SKILL.md +7 -0
- package/plugins/lisa-agy/skills/lisa-posthog-access/SKILL.md +40 -3
- package/plugins/lisa-agy/skills/lisa-sentry-access/SKILL.md +44 -3
- package/plugins/lisa-agy/skills/lisa-sonarcloud-access/SKILL.md +56 -0
- package/plugins/lisa-agy/skills/lisa-track/SKILL.md +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/hooks/block-no-verify.sh +31 -0
- package/plugins/lisa-copilot/rules/eager/credential-substrate-precedence.md +3 -1
- package/plugins/lisa-copilot/rules/eager/tracked-work.md +1 -1
- package/plugins/lisa-copilot/rules/reference/credential-substrate-precedence.md +20 -0
- package/plugins/lisa-copilot/rules/reference/tracked-work.md +4 -2
- package/plugins/lisa-copilot/skills/lisa-atlassian-access/SKILL.md +7 -0
- package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-jam-access/SKILL.md +37 -2
- package/plugins/lisa-copilot/skills/lisa-notion-access/SKILL.md +7 -0
- package/plugins/lisa-copilot/skills/lisa-posthog-access/SKILL.md +40 -3
- package/plugins/lisa-copilot/skills/lisa-sentry-access/SKILL.md +44 -3
- package/plugins/lisa-copilot/skills/lisa-sonarcloud-access/SKILL.md +56 -0
- package/plugins/lisa-copilot/skills/lisa-track/SKILL.md +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/block-no-verify.sh +31 -0
- package/plugins/lisa-cursor/rules/credential-substrate-precedence-reference.mdc +20 -0
- package/plugins/lisa-cursor/rules/credential-substrate-precedence.mdc +3 -1
- package/plugins/lisa-cursor/rules/tracked-work-reference.mdc +4 -2
- package/plugins/lisa-cursor/rules/tracked-work.mdc +1 -1
- package/plugins/lisa-cursor/skills/lisa-atlassian-access/SKILL.md +7 -0
- package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-jam-access/SKILL.md +37 -2
- package/plugins/lisa-cursor/skills/lisa-notion-access/SKILL.md +7 -0
- package/plugins/lisa-cursor/skills/lisa-posthog-access/SKILL.md +40 -3
- package/plugins/lisa-cursor/skills/lisa-sentry-access/SKILL.md +44 -3
- package/plugins/lisa-cursor/skills/lisa-sonarcloud-access/SKILL.md +56 -0
- package/plugins/lisa-cursor/skills/lisa-track/SKILL.md +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-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +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-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-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-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-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/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/hooks/block-no-verify.agy.sh +53 -4
- package/plugins/src/base/hooks/block-no-verify.sh +31 -0
- package/plugins/src/base/rules/eager/credential-substrate-precedence.md +3 -1
- package/plugins/src/base/rules/eager/tracked-work.md +1 -1
- package/plugins/src/base/rules/reference/credential-substrate-precedence.md +20 -0
- package/plugins/src/base/rules/reference/tracked-work.md +4 -2
- package/plugins/src/base/skills/lisa-atlassian-access/SKILL.md +7 -0
- package/plugins/src/base/skills/lisa-implement/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-jam-access/SKILL.md +37 -2
- package/plugins/src/base/skills/lisa-notion-access/SKILL.md +7 -0
- package/plugins/src/base/skills/lisa-posthog-access/SKILL.md +40 -3
- package/plugins/src/base/skills/lisa-sentry-access/SKILL.md +44 -3
- package/plugins/src/base/skills/lisa-sonarcloud-access/SKILL.md +56 -0
- package/plugins/src/base/skills/lisa-track/SKILL.md +1 -1
- package/scripts/install-claude-plugins.sh +5 -1
- package/typescript/copy-overwrite/scripts/check-skipped-required-checks.mjs +298 -26
- package/typescript/create-only/.github/required-checks.json +11 -6
- package/typescript/create-only/.github/workflows/required-checks-drift.yml +84 -0
- package/typescript/merge/.oxlintrc.json +1 -1
|
@@ -39,28 +39,69 @@ Sentry documents API auth tokens for REST API calls, and the same token works
|
|
|
39
39
|
interactively and headlessly — which is why it leads. The REST tier uses:
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
+
# Resolve the token through the chokepoint before giving up on the environment.
|
|
43
|
+
# `$SENTRY_AUTH_TOKEN` is the documented fallback, not the only rung: without
|
|
44
|
+
# this, a project that keeps its credentials in Bitwarden, Doppler, or AWS has
|
|
45
|
+
# no tier 1 path at all and silently resolves through the interactive MCP —
|
|
46
|
+
# the exact divergence `credential-substrate-precedence` exists to remove.
|
|
47
|
+
# Mirrors `linear-access`, `atlassian-access`, and `notion-access`.
|
|
48
|
+
read_sentry_token() {
|
|
49
|
+
[ -n "${SENTRY_AUTH_TOKEN:-}" ] && { echo "$SENTRY_AUTH_TOKEN"; return; }
|
|
50
|
+
local resolver
|
|
51
|
+
for resolver in .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs \
|
|
52
|
+
.agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs; do
|
|
53
|
+
if [ -f "$resolver" ]; then
|
|
54
|
+
local via_lisa
|
|
55
|
+
via_lisa=$(node "$resolver" get SENTRY_AUTH_TOKEN 2>/dev/null) \
|
|
56
|
+
&& [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
|
|
57
|
+
break
|
|
58
|
+
fi
|
|
59
|
+
done
|
|
60
|
+
return 1
|
|
61
|
+
}
|
|
62
|
+
|
|
42
63
|
sentry_api() {
|
|
43
64
|
local path="$1"
|
|
44
|
-
|
|
45
|
-
|
|
65
|
+
local token
|
|
66
|
+
token=$(read_sentry_token) || {
|
|
67
|
+
echo "Error: no Sentry auth token. Set SENTRY_AUTH_TOKEN, or store it as" >&2
|
|
68
|
+
echo "SENTRY_AUTH_TOKEN in this project's secrets provider." >&2
|
|
46
69
|
return 1
|
|
47
70
|
}
|
|
48
71
|
curl -sS "https://sentry.io/api/0${path}" \
|
|
49
|
-
-H "Authorization: Bearer $
|
|
72
|
+
-H "Authorization: Bearer $token"
|
|
50
73
|
}
|
|
51
74
|
```
|
|
52
75
|
|
|
76
|
+
Tier 1a authenticates `sentry-cli` from the **same resolved token**
|
|
77
|
+
(`SENTRY_AUTH_TOKEN=$(read_sentry_token) sentry-cli …`) — never from a second
|
|
78
|
+
credential store, and never from an interactive `sentry-cli login` keychain.
|
|
79
|
+
|
|
53
80
|
If neither tier works, fail with:
|
|
54
81
|
|
|
55
82
|
```text
|
|
56
83
|
Error: no Sentry access substrate available. Authenticate Sentry MCP/CLI or set SENTRY_AUTH_TOKEN.
|
|
57
84
|
```
|
|
58
85
|
|
|
86
|
+
## Mutation boundary
|
|
87
|
+
|
|
88
|
+
Every operation in the Invocation Contract is **read-only** — issue, event, and
|
|
89
|
+
release retrieval. So the `credential-substrate-precedence` guarded fallback for
|
|
90
|
+
mutating operations (write, read back, assert the tenant from the response, roll
|
|
91
|
+
back on mismatch) is not engaged here, and a failed tier is simply skipped. Any
|
|
92
|
+
future operation that mutates Sentry state — resolving an issue, editing a
|
|
93
|
+
release — is a write and MUST reconcile by read-back under that protocol before
|
|
94
|
+
any retry; do not add one to the contract without it.
|
|
95
|
+
|
|
59
96
|
## Invariants
|
|
60
97
|
|
|
61
98
|
- Tier order is `credential-substrate-precedence`: `SENTRY_AUTH_TOKEN` first, the
|
|
62
99
|
Sentry MCP as a preserved first-class fallback. Identity-match against the
|
|
63
100
|
configured org/project is mandatory on every tier.
|
|
101
|
+
- The token is resolved through `lisa-secrets-access`, with the bare
|
|
102
|
+
`SENTRY_AUTH_TOKEN` environment variable as the documented fallback. Never read
|
|
103
|
+
a second credential store (OS keychain, `~/.sentryclirc` token) directly — the
|
|
104
|
+
one-store rule lives at the chokepoint.
|
|
64
105
|
- Org/project come from `.sentryclirc`, `.lisa.config.json`, or explicit
|
|
65
106
|
operation args; never infer by searching all accessible orgs.
|
|
66
107
|
- Consumer skills do not embed Sentry REST paths.
|
|
@@ -34,6 +34,49 @@ SonarQube Cloud org. Do **not** substitute the raw MCP-image names
|
|
|
34
34
|
running the Docker image directly, and the `sonar` CLI ignores them (auth exits
|
|
35
35
|
non-zero). The CI scan gate's `SONAR_TOKEN` is a third, separate name.
|
|
36
36
|
|
|
37
|
+
### Where those variables come from
|
|
38
|
+
|
|
39
|
+
The `sonar` CLI reads them **from the environment only** — it has no provider
|
|
40
|
+
integration of its own — so on a surface that deliberately materializes nothing
|
|
41
|
+
to disk the credential can be provisioned and unreachable at the same time.
|
|
42
|
+
Resolve it through `lisa-secrets-access` and export it into the launching
|
|
43
|
+
process, exactly as the shared `sonar-secrets.sh` hook already does. The bare
|
|
44
|
+
`$SONARQUBE_CLI_TOKEN` in the environment is the documented **fallback** rung,
|
|
45
|
+
not the only one:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Preferred: the one sanctioned reader. Without this rung a project keeping its
|
|
49
|
+
# credentials in Bitwarden, Doppler, or AWS cannot authenticate the MCP at all,
|
|
50
|
+
# and Sonar access degrades to "run `sonar auth login` in a browser" — dead in
|
|
51
|
+
# cron, CI, and cloud sessions. See `credential-substrate-precedence`.
|
|
52
|
+
read_sonar_secret() {
|
|
53
|
+
local name="$1"
|
|
54
|
+
[ -n "${!name:-}" ] && { echo "${!name}"; return; }
|
|
55
|
+
local resolver
|
|
56
|
+
for resolver in .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs \
|
|
57
|
+
.agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs; do
|
|
58
|
+
if [ -f "$resolver" ]; then
|
|
59
|
+
local via_lisa
|
|
60
|
+
via_lisa=$(node "$resolver" get "$name" 2>/dev/null) \
|
|
61
|
+
&& [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
|
|
62
|
+
break
|
|
63
|
+
fi
|
|
64
|
+
done
|
|
65
|
+
return 1
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# Exported into this process only. Writing it anywhere durable would create a
|
|
69
|
+
# second live copy of a credential whose single store is the provider.
|
|
70
|
+
SONARQUBE_CLI_TOKEN=$(read_sonar_secret SONARQUBE_CLI_TOKEN) && export SONARQUBE_CLI_TOKEN
|
|
71
|
+
SONARQUBE_CLI_ORG=$(read_sonar_secret SONARQUBE_CLI_ORG) && export SONARQUBE_CLI_ORG
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`sonar-secrets.sh` runs the same resolution with a longer search path (it also
|
|
75
|
+
probes `.codex/skills/…` and `node_modules/@codyswann/lisa/plugins/lisa/…`,
|
|
76
|
+
because a hook fires in checkouts that never installed an agent plugin) and with
|
|
77
|
+
a timeout, because it sits in front of every prompt. Use its search order
|
|
78
|
+
verbatim when this skill runs inside a hook.
|
|
79
|
+
|
|
37
80
|
Wiring is performed once by `/lisa:setup:sonar` (which drives `sonar integrate
|
|
38
81
|
<agent>`); this access layer assumes the MCP is already wired. This is distinct
|
|
39
82
|
from the CI `SONAR_TOKEN` secret that authenticates the SonarCloud scan job in
|
|
@@ -79,9 +122,22 @@ Pass the project key through the tool's `projectKey` argument (or rely on a
|
|
|
79
122
|
server-configured `SONARQUBE_PROJECT_KEY`); pass `branch` / `pullRequest` where the
|
|
80
123
|
tool accepts them.
|
|
81
124
|
|
|
125
|
+
## Mutation boundary
|
|
126
|
+
|
|
127
|
+
Every operation in the map above is **read-only** — quality, coverage, and
|
|
128
|
+
security data. So the `credential-substrate-precedence` guarded fallback for
|
|
129
|
+
mutating operations (write, read back, assert the tenant from the response, roll
|
|
130
|
+
back on mismatch) is not engaged here; with one substrate there is nothing to
|
|
131
|
+
fall back to in any case. A future operation that mutates Sonar state — marking
|
|
132
|
+
a hotspot safe, changing an issue's status — is a write and MUST reconcile by
|
|
133
|
+
read-back before any retry.
|
|
134
|
+
|
|
82
135
|
## Invariants
|
|
83
136
|
|
|
84
137
|
- The official SonarQube MCP is the only substrate; there is no REST fallback.
|
|
138
|
+
- `SONARQUBE_CLI_*` values are resolved through `lisa-secrets-access` and
|
|
139
|
+
exported in-process, with the bare environment variables as the documented
|
|
140
|
+
fallback. Never write them to a dotfile or a `.env` on a local surface.
|
|
85
141
|
- Auth is env-var only (`SONARQUBE_CLI_TOKEN` [+ `SONARQUBE_CLI_ORG` | `SONARQUBE_CLI_SERVER`]);
|
|
86
142
|
never the interactive `sonar auth login` keychain flow inside a factory.
|
|
87
143
|
- Sonar host access requires the host (`sonarcloud.io`, `sonarqube.us`, or the
|
|
@@ -47,7 +47,7 @@ This is intentionally conservative: ambiguity creates one explicit work item ins
|
|
|
47
47
|
2. Persist only the canonical reference in worktree-local machine state:
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
|
-
node scripts/lisa-work-item.mjs
|
|
50
|
+
node scripts/lisa-work-item.mjs link <canonical-ref>
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
3. Read the binding back through `node scripts/lisa-work-item.mjs current` and require it to equal the canonical reference. If binding fails, stop before durable project work.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -102,16 +102,65 @@ def disables_verification(token):
|
|
|
102
102
|
and NO_VERIFY.startswith(token)
|
|
103
103
|
)
|
|
104
104
|
|
|
105
|
+
# The only relocations that keep a repo's own hooks in play. Anything else —
|
|
106
|
+
# including "" and /dev/null, which are simply the two most obvious members of
|
|
107
|
+
# the blocked set rather than special cases — is refused.
|
|
108
|
+
PERMITTED_HOOKS_PATHS = {".husky", ".githooks"}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def is_permitted_hooks_path(value):
|
|
112
|
+
"""Whether a core.hooksPath value relocates hooks rather than disabling them.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
value: The raw core.hooksPath value as it appeared on the command line.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
True if the path is an established in-repo hooks directory.
|
|
119
|
+
"""
|
|
120
|
+
cleaned = value.strip().strip("'\"")
|
|
121
|
+
if cleaned.startswith("./"):
|
|
122
|
+
cleaned = cleaned[2:]
|
|
123
|
+
return cleaned.rstrip("/") in PERMITTED_HOOKS_PATHS
|
|
124
|
+
|
|
125
|
+
|
|
105
126
|
for i, token in enumerate(normalized_tokens):
|
|
106
127
|
if disables_verification(token):
|
|
107
128
|
sys.exit(1)
|
|
108
129
|
if token == "HUSKY=0" or token.startswith("HUSKY_SKIP_HOOKS="):
|
|
109
130
|
sys.exit(1)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
131
|
+
# Allowlist the destinations, do not denylist the disabling ones: hooks are
|
|
132
|
+
# disabled just as completely by any directory that happens to contain none
|
|
133
|
+
# (`-c core.hooksPath=/tmp/empty`), so the set that DISABLES hooks is
|
|
134
|
+
# unbounded while the set that legitimately relocates them is tiny. Matched
|
|
135
|
+
# case-insensitively because git config names are.
|
|
136
|
+
lowered = token.lower()
|
|
137
|
+
if lowered.startswith("core.hookspath="):
|
|
138
|
+
if not is_permitted_hooks_path(token.split("=", 1)[1]):
|
|
139
|
+
sys.exit(1)
|
|
140
|
+
if lowered == "core.hookspath" and i + 1 < len(normalized_tokens):
|
|
141
|
+
if not is_permitted_hooks_path(normalized_tokens[i + 1]):
|
|
142
|
+
sys.exit(1)
|
|
143
|
+
# `--config-env=core.hooksPath=SOMEVAR` reads the path out of the named env
|
|
144
|
+
# var, so it is not in the command at all and cannot be allowlisted.
|
|
145
|
+
if lowered.startswith("--config-env="):
|
|
146
|
+
spec = token.split("=", 1)[1]
|
|
147
|
+
if spec.split("=", 1)[0].strip().lower() == "core.hookspath":
|
|
148
|
+
sys.exit(1)
|
|
149
|
+
# git also accepts `--config-env <name>=<envvar>` as two tokens. Guarding
|
|
150
|
+
# only the `=` spelling let the trailing `core.hooksPath=.husky` fall
|
|
151
|
+
# through to the allowlist above, which reads `.husky` as a path — but here
|
|
152
|
+
# it names an ENVIRONMENT VARIABLE, which can hold /dev/null. Checked at the
|
|
153
|
+
# `--config-env` token, which the loop reaches first.
|
|
154
|
+
if lowered == "--config-env" and i + 1 < len(normalized_tokens):
|
|
155
|
+
spec = normalized_tokens[i + 1]
|
|
156
|
+
if spec.split("=", 1)[0].strip().strip("'\"").lower() == "core.hookspath":
|
|
113
157
|
sys.exit(1)
|
|
114
|
-
|
|
158
|
+
# `GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=core.hooksPath
|
|
159
|
+
# GIT_CONFIG_VALUE_0=/dev/null git ...` sets the same command-scope config
|
|
160
|
+
# via env-var-style assignments. The index is arbitrary below
|
|
161
|
+
# GIT_CONFIG_COUNT, so it is matched as `\d+` rather than pinned to 0.
|
|
162
|
+
key_match = re.match(r"git_config_key_\d+=(.*)$", lowered, re.DOTALL)
|
|
163
|
+
if key_match and key_match.group(1).strip().strip("'\"") == "core.hookspath":
|
|
115
164
|
sys.exit(1)
|
|
116
165
|
|
|
117
166
|
sys.exit(0)
|
|
@@ -186,6 +186,37 @@ for i, token in enumerate(normalized_tokens):
|
|
|
186
186
|
spec = token.split("=", 1)[1]
|
|
187
187
|
if spec.split("=", 1)[0].strip().lower() == "core.hookspath":
|
|
188
188
|
sys.exit(1)
|
|
189
|
+
# git accepts `--config-env <name>=<envvar>` as TWO tokens as well as one,
|
|
190
|
+
# and guarding only the `=` spelling was worse than missing the separate
|
|
191
|
+
# form outright: the trailing `core.hooksPath=.husky` then fell through to
|
|
192
|
+
# the allowlist above, which reads `.husky` as a PATH and permits it. But
|
|
193
|
+
# here it is an ENVIRONMENT VARIABLE NAME, and `env '.husky=/dev/null' git
|
|
194
|
+
# --config-env core.hooksPath=.husky` really does resolve hooksPath to
|
|
195
|
+
# /dev/null. The allowlist was being used as the bypass.
|
|
196
|
+
#
|
|
197
|
+
# Checked at the `--config-env` token, which the loop reaches first, so the
|
|
198
|
+
# refusal happens before the value token can be mistaken for a path.
|
|
199
|
+
if lowered == "--config-env" and i + 1 < len(normalized_tokens):
|
|
200
|
+
spec = normalized_tokens[i + 1]
|
|
201
|
+
if spec.split("=", 1)[0].strip().strip("'\"").lower() == "core.hookspath":
|
|
202
|
+
sys.exit(1)
|
|
203
|
+
# `GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=core.hooksPath
|
|
204
|
+
# GIT_CONFIG_VALUE_0=/dev/null git commit` sets command-scope config the
|
|
205
|
+
# same way `-c core.hooksPath=...` does — env-var-style assignments ahead of
|
|
206
|
+
# the invocation instead of a flag — so it disables every hook just as
|
|
207
|
+
# completely while matching none of the token shapes above. Upstream missed
|
|
208
|
+
# this until a downstream fork hardened its own copy against it, which is
|
|
209
|
+
# the one direction a guard must never be caught in.
|
|
210
|
+
#
|
|
211
|
+
# The index is matched as `\d+` rather than pinned to 0: git accepts any
|
|
212
|
+
# index below GIT_CONFIG_COUNT, so a single-index check is evaded by typing
|
|
213
|
+
# a 1. Refused outright, like --config-env=, because the path lives in a
|
|
214
|
+
# separate GIT_CONFIG_VALUE_<n> token that can be exported earlier,
|
|
215
|
+
# reordered, or left out entirely — there is nothing here to allowlist
|
|
216
|
+
# against.
|
|
217
|
+
key_match = re.match(r"git_config_key_\d+=(.*)$", lowered, re.DOTALL)
|
|
218
|
+
if key_match and key_match.group(1).strip().strip("'\"") == "core.hookspath":
|
|
219
|
+
sys.exit(1)
|
|
189
220
|
|
|
190
221
|
sys.exit(0)
|
|
191
222
|
PY
|