@codyswann/lisa 2.350.0 → 2.352.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/all/copy-overwrite/scripts/lisa-hooks/block-instruction-file-edits.sh +1 -1
- package/all/copy-overwrite/scripts/lisa-hooks/block-shell-json-parsing.sh +1 -1
- package/all/create-only/.agents/rules/README.md +45 -0
- package/dist/cli/doctor-lisa-owned-artifacts.d.ts +18 -0
- package/dist/cli/doctor-lisa-owned-artifacts.d.ts.map +1 -0
- package/dist/cli/doctor-lisa-owned-artifacts.js +129 -0
- package/dist/cli/doctor-lisa-owned-artifacts.js.map +1 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +2 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/codex/scripts/_extract-edit-paths.sh +1 -1
- package/dist/codex/scripts/format-on-edit.sh +1 -1
- package/dist/codex/scripts/lint-on-edit.sh +1 -1
- package/dist/codex/scripts/rubocop-on-edit.sh +1 -1
- package/dist/codex/scripts/sg-scan-on-edit.sh +1 -1
- package/dist/configs/eslint/cdk.js +2 -2
- package/dist/configs/eslint/cdk.js.map +1 -1
- package/dist/configs/eslint/nestjs.js +2 -2
- package/dist/configs/eslint/nestjs.js.map +1 -1
- package/dist/configs/eslint/typescript.js +2 -2
- package/dist/configs/eslint/typescript.js.map +1 -1
- package/dist/core/host-rules-pointer.d.ts +33 -0
- package/dist/core/host-rules-pointer.d.ts.map +1 -0
- package/dist/core/host-rules-pointer.js +115 -0
- package/dist/core/host-rules-pointer.js.map +1 -0
- package/dist/core/instruction-files-migration.d.ts +3 -2
- package/dist/core/instruction-files-migration.d.ts.map +1 -1
- package/dist/core/instruction-files-migration.js +62 -64
- package/dist/core/instruction-files-migration.js.map +1 -1
- package/dist/core/learnings.d.ts +1 -1
- package/dist/core/learnings.d.ts.map +1 -1
- package/dist/core/learnings.js +1 -1
- package/dist/core/learnings.js.map +1 -1
- package/dist/core/lisa-owned-templates.d.ts +34 -0
- package/dist/core/lisa-owned-templates.d.ts.map +1 -0
- package/dist/core/lisa-owned-templates.js +41 -0
- package/dist/core/lisa-owned-templates.js.map +1 -0
- package/dist/core/lisa.d.ts.map +1 -1
- package/dist/core/lisa.js +5 -1
- package/dist/core/lisa.js.map +1 -1
- package/dist/core/project-config.d.ts +39 -9
- package/dist/core/project-config.d.ts.map +1 -1
- package/dist/core/project-config.js +37 -10
- package/dist/core/project-config.js.map +1 -1
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +73 -37
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/opencode/plugin-templates/lisa-block-instruction-file-edits.ts +1 -1
- package/dist/strategies/copy-overwrite.d.ts +13 -6
- package/dist/strategies/copy-overwrite.d.ts.map +1 -1
- package/dist/strategies/copy-overwrite.js +16 -7
- package/dist/strategies/copy-overwrite.js.map +1 -1
- package/dist/sync/registry.d.ts.map +1 -1
- package/dist/sync/registry.js +0 -6
- package/dist/sync/registry.js.map +1 -1
- package/package.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-debrief/SKILL.md +1 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-debrief-apply/SKILL.md +3 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-doctor/SKILL.md +4 -1
- package/plugins/lisa/.codex-plugin/skills/lisa-github-read-issue/SKILL.md +1 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-github-validate-issue/SKILL.md +22 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-github-write-issue/SKILL.md +15 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +6 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-improve-harness/SKILL.md +2 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-jira-write-ticket/SKILL.md +15 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-learnings-audit/SKILL.md +6 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-access/SKILL.md +25 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-validate-issue/SKILL.md +22 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-linear-write-issue/SKILL.md +15 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/SKILL.md +20 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/doctor-secrets.mjs +13 -9
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/note-format.mjs +179 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/resolve-secret.mjs +32 -6
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/tools-from-notes.mjs +6 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-atlassian/SKILL.md +17 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-linear/SKILL.md +17 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-notion/SKILL.md +17 -0
- package/plugins/lisa/agents/learnings-synthesizer.md +1 -1
- package/plugins/lisa/agents/skill-evaluator.md +1 -1
- package/plugins/lisa/hooks/block-instruction-file-edits.sh +1 -1
- package/plugins/lisa/hooks/block-shell-json-parsing.sh +1 -1
- package/plugins/lisa/rules/eager/config-resolution.md +6 -3
- package/plugins/lisa/rules/eager/derived-branch-plan.md +45 -0
- package/plugins/lisa/rules/eager/project-learnings.md +3 -2
- package/plugins/lisa/rules/reference/config-resolution.md +26 -2
- package/plugins/lisa/rules/reference/derived-branch-plan.md +112 -0
- package/plugins/lisa/rules/reference/intent-routing.md +1 -1
- package/plugins/lisa/rules/reference/project-learnings.md +6 -4
- package/plugins/lisa/rules/reference/promotion-contract.md +2 -1
- package/plugins/lisa/skills/lisa-debrief/SKILL.md +1 -1
- package/plugins/lisa/skills/lisa-debrief-apply/SKILL.md +3 -3
- package/plugins/lisa/skills/lisa-doctor/SKILL.md +4 -1
- package/plugins/lisa/skills/lisa-github-read-issue/SKILL.md +1 -0
- package/plugins/lisa/skills/lisa-github-validate-issue/SKILL.md +22 -0
- package/plugins/lisa/skills/lisa-github-write-issue/SKILL.md +15 -0
- package/plugins/lisa/skills/lisa-implement/SKILL.md +6 -0
- package/plugins/lisa/skills/lisa-improve-harness/SKILL.md +2 -2
- package/plugins/lisa/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
- package/plugins/lisa/skills/lisa-jira-write-ticket/SKILL.md +15 -0
- package/plugins/lisa/skills/lisa-learnings-audit/SKILL.md +6 -3
- package/plugins/lisa/skills/lisa-linear-access/SKILL.md +25 -3
- package/plugins/lisa/skills/lisa-linear-validate-issue/SKILL.md +22 -0
- package/plugins/lisa/skills/lisa-linear-write-issue/SKILL.md +15 -0
- package/plugins/lisa/skills/lisa-secrets-access/SKILL.md +20 -2
- package/plugins/lisa/skills/lisa-secrets-access/scripts/doctor-secrets.mjs +13 -9
- package/plugins/lisa/skills/lisa-secrets-access/scripts/note-format.mjs +179 -0
- package/plugins/lisa/skills/lisa-secrets-access/scripts/resolve-secret.mjs +32 -6
- package/plugins/lisa/skills/lisa-secrets-access/scripts/tools-from-notes.mjs +6 -2
- package/plugins/lisa/skills/lisa-setup-atlassian/SKILL.md +17 -0
- package/plugins/lisa/skills/lisa-setup-linear/SKILL.md +17 -0
- package/plugins/lisa/skills/lisa-setup-notion/SKILL.md +17 -0
- package/plugins/lisa-agy/agents/learnings-synthesizer.md +1 -1
- package/plugins/lisa-agy/agents/skill-evaluator.md +1 -1
- package/plugins/lisa-agy/hooks/block-instruction-file-edits.sh +1 -1
- package/plugins/lisa-agy/hooks/block-shell-json-parsing.sh +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-debrief/SKILL.md +1 -1
- package/plugins/lisa-agy/skills/lisa-debrief-apply/SKILL.md +3 -3
- package/plugins/lisa-agy/skills/lisa-doctor/SKILL.md +4 -1
- package/plugins/lisa-agy/skills/lisa-github-read-issue/SKILL.md +1 -0
- package/plugins/lisa-agy/skills/lisa-github-validate-issue/SKILL.md +22 -0
- package/plugins/lisa-agy/skills/lisa-github-write-issue/SKILL.md +15 -0
- package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +6 -0
- package/plugins/lisa-agy/skills/lisa-improve-harness/SKILL.md +2 -2
- package/plugins/lisa-agy/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
- package/plugins/lisa-agy/skills/lisa-jira-write-ticket/SKILL.md +15 -0
- package/plugins/lisa-agy/skills/lisa-learnings-audit/SKILL.md +6 -3
- package/plugins/lisa-agy/skills/lisa-linear-access/SKILL.md +25 -3
- package/plugins/lisa-agy/skills/lisa-linear-validate-issue/SKILL.md +22 -0
- package/plugins/lisa-agy/skills/lisa-linear-write-issue/SKILL.md +15 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/SKILL.md +20 -2
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/doctor-secrets.mjs +13 -9
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/note-format.mjs +179 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/resolve-secret.mjs +32 -6
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/tools-from-notes.mjs +6 -2
- package/plugins/lisa-agy/skills/lisa-setup-atlassian/SKILL.md +17 -0
- package/plugins/lisa-agy/skills/lisa-setup-linear/SKILL.md +17 -0
- package/plugins/lisa-agy/skills/lisa-setup-notion/SKILL.md +17 -0
- 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/agents/learnings-synthesizer.agent.md +1 -1
- package/plugins/lisa-copilot/agents/skill-evaluator.agent.md +1 -1
- package/plugins/lisa-copilot/hooks/block-instruction-file-edits.sh +1 -1
- package/plugins/lisa-copilot/hooks/block-shell-json-parsing.sh +1 -1
- package/plugins/lisa-copilot/rules/eager/config-resolution.md +6 -3
- package/plugins/lisa-copilot/rules/eager/derived-branch-plan.md +45 -0
- package/plugins/lisa-copilot/rules/eager/project-learnings.md +3 -2
- package/plugins/lisa-copilot/rules/reference/config-resolution.md +26 -2
- package/plugins/lisa-copilot/rules/reference/derived-branch-plan.md +112 -0
- package/plugins/lisa-copilot/rules/reference/intent-routing.md +1 -1
- package/plugins/lisa-copilot/rules/reference/project-learnings.md +6 -4
- package/plugins/lisa-copilot/rules/reference/promotion-contract.md +2 -1
- package/plugins/lisa-copilot/skills/lisa-debrief/SKILL.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-debrief-apply/SKILL.md +3 -3
- package/plugins/lisa-copilot/skills/lisa-doctor/SKILL.md +4 -1
- package/plugins/lisa-copilot/skills/lisa-github-read-issue/SKILL.md +1 -0
- package/plugins/lisa-copilot/skills/lisa-github-validate-issue/SKILL.md +22 -0
- package/plugins/lisa-copilot/skills/lisa-github-write-issue/SKILL.md +15 -0
- package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +6 -0
- package/plugins/lisa-copilot/skills/lisa-improve-harness/SKILL.md +2 -2
- package/plugins/lisa-copilot/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
- package/plugins/lisa-copilot/skills/lisa-jira-write-ticket/SKILL.md +15 -0
- package/plugins/lisa-copilot/skills/lisa-learnings-audit/SKILL.md +6 -3
- package/plugins/lisa-copilot/skills/lisa-linear-access/SKILL.md +25 -3
- package/plugins/lisa-copilot/skills/lisa-linear-validate-issue/SKILL.md +22 -0
- package/plugins/lisa-copilot/skills/lisa-linear-write-issue/SKILL.md +15 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/SKILL.md +20 -2
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/doctor-secrets.mjs +13 -9
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/note-format.mjs +179 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/resolve-secret.mjs +32 -6
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/tools-from-notes.mjs +6 -2
- package/plugins/lisa-copilot/skills/lisa-setup-atlassian/SKILL.md +17 -0
- package/plugins/lisa-copilot/skills/lisa-setup-linear/SKILL.md +17 -0
- package/plugins/lisa-copilot/skills/lisa-setup-notion/SKILL.md +17 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/agents/learnings-synthesizer.md +1 -1
- package/plugins/lisa-cursor/agents/skill-evaluator.md +1 -1
- package/plugins/lisa-cursor/hooks/block-instruction-file-edits.sh +1 -1
- package/plugins/lisa-cursor/hooks/block-shell-json-parsing.sh +1 -1
- package/plugins/lisa-cursor/rules/config-resolution-reference.mdc +26 -2
- package/plugins/lisa-cursor/rules/config-resolution.mdc +6 -3
- package/plugins/lisa-cursor/rules/derived-branch-plan-reference.mdc +117 -0
- package/plugins/lisa-cursor/rules/derived-branch-plan.mdc +50 -0
- package/plugins/lisa-cursor/rules/intent-routing-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/project-learnings-reference.mdc +6 -4
- package/plugins/lisa-cursor/rules/project-learnings.mdc +3 -2
- package/plugins/lisa-cursor/rules/promotion-contract-reference.mdc +2 -1
- package/plugins/lisa-cursor/skills/lisa-debrief/SKILL.md +1 -1
- package/plugins/lisa-cursor/skills/lisa-debrief-apply/SKILL.md +3 -3
- package/plugins/lisa-cursor/skills/lisa-doctor/SKILL.md +4 -1
- package/plugins/lisa-cursor/skills/lisa-github-read-issue/SKILL.md +1 -0
- package/plugins/lisa-cursor/skills/lisa-github-validate-issue/SKILL.md +22 -0
- package/plugins/lisa-cursor/skills/lisa-github-write-issue/SKILL.md +15 -0
- package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +6 -0
- package/plugins/lisa-cursor/skills/lisa-improve-harness/SKILL.md +2 -2
- package/plugins/lisa-cursor/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
- package/plugins/lisa-cursor/skills/lisa-jira-write-ticket/SKILL.md +15 -0
- package/plugins/lisa-cursor/skills/lisa-learnings-audit/SKILL.md +6 -3
- package/plugins/lisa-cursor/skills/lisa-linear-access/SKILL.md +25 -3
- package/plugins/lisa-cursor/skills/lisa-linear-validate-issue/SKILL.md +22 -0
- package/plugins/lisa-cursor/skills/lisa-linear-write-issue/SKILL.md +15 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/SKILL.md +20 -2
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/doctor-secrets.mjs +13 -9
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/note-format.mjs +179 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/resolve-secret.mjs +32 -6
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/tools-from-notes.mjs +6 -2
- package/plugins/lisa-cursor/skills/lisa-setup-atlassian/SKILL.md +17 -0
- package/plugins/lisa-cursor/skills/lisa-setup-linear/SKILL.md +17 -0
- package/plugins/lisa-cursor/skills/lisa-setup-notion/SKILL.md +17 -0
- 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/hooks/block-suppress-directives.sh +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-copilot/hooks/block-suppress-directives.sh +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/hooks/block-suppress-directives.sh +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/agents/learnings-synthesizer.md +1 -1
- package/plugins/src/base/agents/skill-evaluator.md +1 -1
- package/plugins/src/base/hooks/block-instruction-file-edits.sh +1 -1
- package/plugins/src/base/hooks/block-shell-json-parsing.sh +1 -1
- package/plugins/src/base/rules/eager/config-resolution.md +6 -3
- package/plugins/src/base/rules/eager/derived-branch-plan.md +45 -0
- package/plugins/src/base/rules/eager/project-learnings.md +3 -2
- package/plugins/src/base/rules/reference/config-resolution.md +26 -2
- package/plugins/src/base/rules/reference/derived-branch-plan.md +112 -0
- package/plugins/src/base/rules/reference/intent-routing.md +1 -1
- package/plugins/src/base/rules/reference/project-learnings.md +6 -4
- package/plugins/src/base/rules/reference/promotion-contract.md +2 -1
- package/plugins/src/base/skills/lisa-debrief/SKILL.md +1 -1
- package/plugins/src/base/skills/lisa-debrief-apply/SKILL.md +3 -3
- package/plugins/src/base/skills/lisa-doctor/SKILL.md +4 -1
- package/plugins/src/base/skills/lisa-github-read-issue/SKILL.md +1 -0
- package/plugins/src/base/skills/lisa-github-validate-issue/SKILL.md +22 -0
- package/plugins/src/base/skills/lisa-github-write-issue/SKILL.md +15 -0
- package/plugins/src/base/skills/lisa-implement/SKILL.md +6 -0
- package/plugins/src/base/skills/lisa-improve-harness/SKILL.md +2 -2
- package/plugins/src/base/skills/lisa-jira-validate-ticket/SKILL.md +22 -0
- package/plugins/src/base/skills/lisa-jira-write-ticket/SKILL.md +15 -0
- package/plugins/src/base/skills/lisa-learnings-audit/SKILL.md +6 -3
- package/plugins/src/base/skills/lisa-linear-access/SKILL.md +25 -3
- package/plugins/src/base/skills/lisa-linear-validate-issue/SKILL.md +22 -0
- package/plugins/src/base/skills/lisa-linear-write-issue/SKILL.md +15 -0
- package/plugins/src/base/skills/lisa-secrets-access/SKILL.md +20 -2
- package/plugins/src/base/skills/lisa-secrets-access/scripts/doctor-secrets.mjs +13 -9
- package/plugins/src/base/skills/lisa-secrets-access/scripts/note-format.mjs +179 -0
- package/plugins/src/base/skills/lisa-secrets-access/scripts/resolve-secret.mjs +32 -6
- package/plugins/src/base/skills/lisa-secrets-access/scripts/tools-from-notes.mjs +6 -2
- package/plugins/src/base/skills/lisa-setup-atlassian/SKILL.md +17 -0
- package/plugins/src/base/skills/lisa-setup-linear/SKILL.md +17 -0
- package/plugins/src/base/skills/lisa-setup-notion/SKILL.md +17 -0
- package/plugins/src/typescript/hooks/block-suppress-directives.sh +1 -1
- package/scripts/github-status-check.sh +22 -7
- package/all/create-only/.claude/rules/PROJECT_RULES.md +0 -17
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
import { createHash } from "node:crypto";
|
|
21
21
|
|
|
22
|
+
import { validateNote } from "./note-format.mjs";
|
|
22
23
|
import { fetchAll, fetchRotatable } from "./providers.mjs";
|
|
23
24
|
import { readMaterialized } from "./resolve-secret.mjs";
|
|
24
25
|
import { readConfig } from "./surfaces.mjs";
|
|
@@ -118,20 +119,23 @@ export function checkNaming(provider, report) {
|
|
|
118
119
|
}
|
|
119
120
|
|
|
120
121
|
/**
|
|
121
|
-
* Assert every secret carries a usage note.
|
|
122
|
+
* Assert every secret carries a usage note in the documented format.
|
|
123
|
+
*
|
|
124
|
+
* This blocks rather than warns, and the promotion is the point. The contract
|
|
125
|
+
* has always said the note "must exist and be well-formed", enforced statically
|
|
126
|
+
* by `doctor` — but a warning enforces nothing, so a vault of empty notes
|
|
127
|
+
* reported clean and stayed empty. A check that never fails is a check nobody
|
|
128
|
+
* acts on.
|
|
129
|
+
*
|
|
130
|
+
* The grammar lives in `note-format.mjs` so this and `resolve-secret.mjs verify`
|
|
131
|
+
* cannot disagree about what a well-formed note is.
|
|
122
132
|
* @param {Map<string, object>} provider Provider view.
|
|
123
133
|
* @param {Function} report Finding collector.
|
|
124
134
|
*/
|
|
125
135
|
export function checkNotes(provider, report) {
|
|
126
136
|
for (const [name, entry] of provider) {
|
|
127
|
-
|
|
128
|
-
report(
|
|
129
|
-
"warn",
|
|
130
|
-
name,
|
|
131
|
-
"has no usage note. An agent cannot learn this credential's scope " +
|
|
132
|
-
"without one, and inferring it from the name is exactly the guess " +
|
|
133
|
-
"that writes to the wrong system"
|
|
134
|
-
);
|
|
137
|
+
for (const defect of validateNote(entry?.note)) {
|
|
138
|
+
report(defect.level, name, defect.message);
|
|
135
139
|
}
|
|
136
140
|
}
|
|
137
141
|
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate a credential's usage note against the format the contract documents.
|
|
3
|
+
*
|
|
4
|
+
* The contract states Rule A — "the note must exist and be well-formed",
|
|
5
|
+
* enforced statically by `verify` and by `doctor`. Only half of that was ever
|
|
6
|
+
* true. Both consumers tested the note for emptiness and nothing tested its
|
|
7
|
+
* shape, so a note that promised a scope and delivered a bare colon, or a
|
|
8
|
+
* `tool:` line naming a URL instead of a CLI, passed every check Lisa had. The
|
|
9
|
+
* prose described an enforcement that did not exist, which is worse than no
|
|
10
|
+
* rule: readers stopped checking because they believed something else was.
|
|
11
|
+
*
|
|
12
|
+
* This module is that missing half, and it lives in one place on purpose. The
|
|
13
|
+
* same argument the values file makes for keeping its writer and its parser
|
|
14
|
+
* together applies here: `tools-from-notes.mjs` decides what a `tool:` line
|
|
15
|
+
* *is*, and if this validator held a second copy of that pattern, a note could
|
|
16
|
+
* pass doctor and then be silently ignored at install time with nothing to
|
|
17
|
+
* reveal the disagreement. So the matcher is defined here once and imported
|
|
18
|
+
* there.
|
|
19
|
+
*
|
|
20
|
+
* Messages are written for the person who has to fix the vault, who is often
|
|
21
|
+
* not an engineer. Each one names the secret's defect and the edit that clears
|
|
22
|
+
* it, never only the rule that was broken.
|
|
23
|
+
* @module note-format
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A `key: value` metadata line.
|
|
28
|
+
*
|
|
29
|
+
* The key must be a single bare lowercase token. That is what separates
|
|
30
|
+
* `scope: read-write` from prose that happens to contain a colon, such as
|
|
31
|
+
* "Attio CRM: system of record for sales" — a sentence has spaces before its
|
|
32
|
+
* colon, and a capitalised word opening a sentence is prose, not a field. The
|
|
33
|
+
* discriminator is deliberately generous: mistaking a field for prose only
|
|
34
|
+
* loosens a check, while mistaking prose for a field would fail a good note.
|
|
35
|
+
*/
|
|
36
|
+
const FIELD_LINE = /^[ \t]*([a-z][a-z0-9_-]*)[ \t]*:[ \t]*(.*)$/;
|
|
37
|
+
|
|
38
|
+
/** Matches `tool: name` or `tools: a, b`, case-insensitively, on one line. */
|
|
39
|
+
const TOOL_LINE_SINGLE = /^[ \t]*tools?[ \t]*:[ \t]*(.*)$/i;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The same matcher, global and multiline, for scanning a whole note.
|
|
43
|
+
*
|
|
44
|
+
* Derived from the single-line pattern rather than written out again, so the
|
|
45
|
+
* two can never disagree about what a tool line is.
|
|
46
|
+
*/
|
|
47
|
+
export const TOOL_LINE = new RegExp(TOOL_LINE_SINGLE.source, "gim");
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A bare CLI name: what a `tool:` entry is permitted to be.
|
|
51
|
+
*
|
|
52
|
+
* Names are matched against Lisa's catalogue and never executed, so this is not
|
|
53
|
+
* the security boundary — the catalogue is. It is the check that tells an
|
|
54
|
+
* operator their entry will be silently dropped, which is the failure mode
|
|
55
|
+
* worth catching: a note asking for `sonar (for CI)` installs nothing, and the
|
|
56
|
+
* session fails much later for a reason that looks unrelated.
|
|
57
|
+
*/
|
|
58
|
+
const TOOL_NAME = /^[a-z0-9][a-z0-9._+-]*$/;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Whether a line is structured metadata rather than a sentence.
|
|
62
|
+
* @param {string} line One line of a note.
|
|
63
|
+
* @returns {boolean} True when the line is a field or a tool line.
|
|
64
|
+
*/
|
|
65
|
+
function isMetadataLine(line) {
|
|
66
|
+
return FIELD_LINE.test(line) || TOOL_LINE_SINGLE.test(line);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Check the entries on one `tool:` / `tools:` line.
|
|
71
|
+
* @param {string} value The text after the colon.
|
|
72
|
+
* @param {object[]} defects Collector to append to.
|
|
73
|
+
*/
|
|
74
|
+
function checkToolLine(value, defects) {
|
|
75
|
+
if (!value.trim()) {
|
|
76
|
+
defects.push({
|
|
77
|
+
level: "error",
|
|
78
|
+
code: "empty-tool-line",
|
|
79
|
+
message:
|
|
80
|
+
"has a `tool:` line that names no tool. Either name the command-line " +
|
|
81
|
+
"tool this credential is for (for example `tool: sonar`) or delete " +
|
|
82
|
+
"the line — as written it asks for a tool and supplies none, so " +
|
|
83
|
+
"nothing gets installed",
|
|
84
|
+
});
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (const raw of value.split(",")) {
|
|
89
|
+
const entry = raw.trim();
|
|
90
|
+
if (!entry) {
|
|
91
|
+
// A trailing or doubled comma is sloppy punctuation, not a broken note:
|
|
92
|
+
// the reader drops the empty entry and installs the rest correctly.
|
|
93
|
+
// Blocking a vault over a comma would teach operators the check is noise.
|
|
94
|
+
defects.push({
|
|
95
|
+
level: "warn",
|
|
96
|
+
code: "stray-separator",
|
|
97
|
+
message:
|
|
98
|
+
"has a `tool:` line with a stray comma. Nothing breaks, but tidy it " +
|
|
99
|
+
"so the list reads as the tools it actually names",
|
|
100
|
+
});
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
if (!TOOL_NAME.test(entry.toLowerCase())) {
|
|
104
|
+
defects.push({
|
|
105
|
+
level: "error",
|
|
106
|
+
code: "bad-tool-name",
|
|
107
|
+
message:
|
|
108
|
+
`has a \`tool:\` entry that is not a tool name: "${entry}". Each ` +
|
|
109
|
+
"entry must be one plain name such as `sonar` or `gh`, separated by " +
|
|
110
|
+
"commas — no descriptions, links, versions, or punctuation. As " +
|
|
111
|
+
"written this entry is ignored, so the tool never gets installed",
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Report every way a usage note departs from the documented format.
|
|
119
|
+
*
|
|
120
|
+
* Returns defects rather than throwing so a caller can list a whole vault's
|
|
121
|
+
* problems in one pass. An operator fixing a vault wants every edit at once,
|
|
122
|
+
* not one error per run.
|
|
123
|
+
* @param {unknown} note The note text stored beside the secret.
|
|
124
|
+
* @returns {{level: string, code: string, message: string}[]} Defects found.
|
|
125
|
+
*/
|
|
126
|
+
export function validateNote(note) {
|
|
127
|
+
if (typeof note !== "string" || !note.trim()) {
|
|
128
|
+
// One cause, one instruction. Also accusing an absent note of lacking a
|
|
129
|
+
// first line would leave the operator guessing which fix comes first.
|
|
130
|
+
return [
|
|
131
|
+
{
|
|
132
|
+
level: "error",
|
|
133
|
+
code: "missing-note",
|
|
134
|
+
message:
|
|
135
|
+
"has no usage note. Write one in the secret's own description field " +
|
|
136
|
+
"in the vault: a first line saying what this credential is for and " +
|
|
137
|
+
"what it can reach, then `key: value` lines such as `scope:`, " +
|
|
138
|
+
"`owner:`, and `tool:`. Guessing a credential's purpose from its " +
|
|
139
|
+
"name is exactly the guess that writes to the wrong system",
|
|
140
|
+
},
|
|
141
|
+
];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const defects = [];
|
|
145
|
+
const lines = note.split(/\r?\n/);
|
|
146
|
+
const populated = lines.filter(line => line.trim());
|
|
147
|
+
|
|
148
|
+
if (!populated.some(line => !isMetadataLine(line))) {
|
|
149
|
+
defects.push({
|
|
150
|
+
level: "error",
|
|
151
|
+
code: "no-prose",
|
|
152
|
+
message:
|
|
153
|
+
"has no plain-language first line. Add a sentence at the top saying " +
|
|
154
|
+
"what this credential is for and what it can reach — the `key: value` " +
|
|
155
|
+
"lines below it list details, but they never say what the thing is",
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
for (const line of populated) {
|
|
160
|
+
const tool = TOOL_LINE_SINGLE.exec(line);
|
|
161
|
+
if (tool) {
|
|
162
|
+
checkToolLine(tool[1], defects);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const field = FIELD_LINE.exec(line);
|
|
166
|
+
if (field && !field[2].trim()) {
|
|
167
|
+
defects.push({
|
|
168
|
+
level: "error",
|
|
169
|
+
code: "empty-field",
|
|
170
|
+
message:
|
|
171
|
+
`has an empty \`${field[1]}:\` line. Either fill in the value or ` +
|
|
172
|
+
"delete the line — a field with nothing after the colon promises a " +
|
|
173
|
+
"fact the reader then cannot find anywhere",
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return defects;
|
|
179
|
+
}
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
import { existsSync, readFileSync } from "node:fs";
|
|
31
31
|
|
|
32
32
|
import { parseEnv } from "./envfile.mjs";
|
|
33
|
+
import { validateNote } from "./note-format.mjs";
|
|
33
34
|
import { ENV_KEY, fetchAll } from "./providers.mjs";
|
|
34
35
|
import { materializedPaths, readConfig } from "./surfaces.mjs";
|
|
35
36
|
|
|
@@ -109,6 +110,19 @@ function sourceOf(name, provider, file) {
|
|
|
109
110
|
return " ";
|
|
110
111
|
}
|
|
111
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Say what is wrong with a note, not merely that something is.
|
|
115
|
+
*
|
|
116
|
+
* "NO NOTE" against a note that exists but is malformed sends the operator
|
|
117
|
+
* looking for a missing field they already wrote.
|
|
118
|
+
* @param {object} [fault] The blocking defect, if any.
|
|
119
|
+
* @returns {string} A status label for the verify table.
|
|
120
|
+
*/
|
|
121
|
+
function noteStatus(fault) {
|
|
122
|
+
if (!fault) return "noted";
|
|
123
|
+
return fault.code === "missing-note" ? "NO NOTE" : `NOTE ${fault.code}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
112
126
|
/**
|
|
113
127
|
* Verify every declared secret, mirroring the ladder's own order.
|
|
114
128
|
*
|
|
@@ -119,12 +133,20 @@ function sourceOf(name, provider, file) {
|
|
|
119
133
|
* This command is read-only. Proving that a rotating credential can actually be
|
|
120
134
|
* written back requires a write, so that check lives in the rotation path
|
|
121
135
|
* rather than here.
|
|
136
|
+
*
|
|
137
|
+
* The two views are parameters so a test can supply them. Reaching for the
|
|
138
|
+
* provider CLI is the one thing in here that cannot run in a test, and a check
|
|
139
|
+
* nobody can test is how the note rule stayed unenforced for so long.
|
|
122
140
|
* @param {object} cfg Resolved configuration.
|
|
141
|
+
* @param {Map<string, object>} [provider] Provider view.
|
|
142
|
+
* @param {Map<string, string>} [file] Materialized view.
|
|
123
143
|
* @returns {number} Count of secrets that failed.
|
|
124
144
|
*/
|
|
125
|
-
function verify(
|
|
126
|
-
|
|
127
|
-
|
|
145
|
+
export function verify(
|
|
146
|
+
cfg,
|
|
147
|
+
provider = fetchAll(cfg),
|
|
148
|
+
file = readMaterialized(cfg)
|
|
149
|
+
) {
|
|
128
150
|
const names = cfg.require ?? [
|
|
129
151
|
...new Set([...provider.keys(), ...file.keys()]),
|
|
130
152
|
];
|
|
@@ -139,13 +161,17 @@ function verify(cfg) {
|
|
|
139
161
|
provider.get(name)?.value
|
|
140
162
|
);
|
|
141
163
|
const named = ENV_KEY.test(name);
|
|
142
|
-
|
|
143
|
-
|
|
164
|
+
// Presence is not well-formedness. Testing `Boolean(note)` passed a note
|
|
165
|
+
// that was a single stray character, which is exactly the note an agent
|
|
166
|
+
// learns nothing from. Same validator doctor uses, so the two agree.
|
|
167
|
+
const defects = validateNote(provider.get(name)?.note);
|
|
168
|
+
const noteFault = defects.find(d => d.level === "error");
|
|
169
|
+
if (!resolves || !named || noteFault) bad += 1;
|
|
144
170
|
console.log(
|
|
145
171
|
` ${name.padEnd(30)} ${resolves ? "resolves" : "MISSING "} ` +
|
|
146
172
|
`${sourceOf(name, provider, file)} ` +
|
|
147
173
|
`${named ? "name ok" : "NAME NOT UPPER_SNAKE"} ` +
|
|
148
|
-
`${
|
|
174
|
+
`${noteStatus(noteFault)}` +
|
|
149
175
|
`${rotating.has(name) ? " rotating" : ""}`
|
|
150
176
|
);
|
|
151
177
|
}
|
|
@@ -21,11 +21,15 @@
|
|
|
21
21
|
* its note. Selecting from a known set means the worst a hostile note can do is
|
|
22
22
|
* ask for a CLI Lisa already ships a pinned, checksummed entry for — it can
|
|
23
23
|
* never introduce a URL, a version, or a command.
|
|
24
|
+
* The matcher itself lives in `note-format.mjs` alongside the validator that
|
|
25
|
+
* checks these lines, for the same reason the values file keeps its writer and
|
|
26
|
+
* its parser in one module: two copies of the pattern would let a note pass
|
|
27
|
+
* `doctor` and then be silently ignored here, with nothing to reveal the
|
|
28
|
+
* disagreement.
|
|
24
29
|
* @module tools-from-notes
|
|
25
30
|
*/
|
|
26
31
|
|
|
27
|
-
|
|
28
|
-
const TOOL_LINE = /^[ \t]*tools?[ \t]*:[ \t]*(.+)$/gim;
|
|
32
|
+
import { TOOL_LINE } from "./note-format.mjs";
|
|
29
33
|
|
|
30
34
|
/**
|
|
31
35
|
* Collect the tool names a set of notes asks for.
|
|
@@ -77,6 +77,23 @@ read_token() {
|
|
|
77
77
|
local slug=$(echo "$email" | tr '[:upper:]@.' '[:lower:]__')
|
|
78
78
|
local varname="ATLASSIAN_API_TOKEN_${slug}"
|
|
79
79
|
[ -n "${!varname}" ] && { echo "${!varname}"; return; }
|
|
80
|
+
# Preferred path: the single secrets chokepoint. It owns the one-store rule
|
|
81
|
+
# and the surface ladder, so anything it can answer must not be read out of an
|
|
82
|
+
# OS keychain here — a second reader is how the same credential ends up living
|
|
83
|
+
# in two places and drifting. Keep this identical to `atlassian-access`.
|
|
84
|
+
local resolver
|
|
85
|
+
for resolver in .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs \
|
|
86
|
+
.agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs; do
|
|
87
|
+
if [ -f "$resolver" ]; then
|
|
88
|
+
local via_lisa
|
|
89
|
+
via_lisa=$(node "$resolver" get ATLASSIAN_API_TOKEN 2>/dev/null) \
|
|
90
|
+
&& [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
|
|
91
|
+
break
|
|
92
|
+
fi
|
|
93
|
+
done
|
|
94
|
+
# Legacy fallback: the OS keychain written by the guided flow below, for
|
|
95
|
+
# projects that have not adopted a credentials provider. Reached only when the
|
|
96
|
+
# chokepoint is absent or has no entry.
|
|
80
97
|
case "$(uname -s)" in
|
|
81
98
|
Darwin) security find-generic-password -s lisa-atlassian -a "$email" -w 2>/dev/null ;;
|
|
82
99
|
Linux) command -v secret-tool >/dev/null && secret-tool lookup service lisa-atlassian account "$email" 2>/dev/null ;;
|
|
@@ -89,6 +89,23 @@ read_linear_key() { # $1=workspace slug
|
|
|
89
89
|
local slug; slug=$(echo "$ws" | tr '[:upper:]-' '[:lower:]_')
|
|
90
90
|
local varname="LINEAR_API_KEY_${slug}"
|
|
91
91
|
[ -n "${!varname}" ] && { echo "${!varname}"; return; }
|
|
92
|
+
# Preferred path: the single secrets chokepoint. It owns the one-store rule
|
|
93
|
+
# and the surface ladder, so anything it can answer must not be read out of an
|
|
94
|
+
# OS keychain here — a second reader is how the same credential ends up living
|
|
95
|
+
# in two places and drifting. Keep this identical to `linear-access`.
|
|
96
|
+
local resolver
|
|
97
|
+
for resolver in .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs \
|
|
98
|
+
.agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs; do
|
|
99
|
+
if [ -f "$resolver" ]; then
|
|
100
|
+
local via_lisa
|
|
101
|
+
via_lisa=$(node "$resolver" get LINEAR_API_KEY 2>/dev/null) \
|
|
102
|
+
&& [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
|
|
103
|
+
break
|
|
104
|
+
fi
|
|
105
|
+
done
|
|
106
|
+
# Legacy fallback: the OS keychain written by the guided flow below, for
|
|
107
|
+
# projects that have not adopted a credentials provider. Reached only when the
|
|
108
|
+
# chokepoint is absent or has no entry.
|
|
92
109
|
case "$(uname -s)" in
|
|
93
110
|
Darwin) security find-generic-password -s lisa-linear -a "$ws" -w 2>/dev/null ;;
|
|
94
111
|
Linux) command -v secret-tool >/dev/null && secret-tool lookup service lisa-linear account "$ws" 2>/dev/null ;;
|
|
@@ -164,6 +164,23 @@ read_notion_token() {
|
|
|
164
164
|
local slug=$(echo "$workspace" | tr '[:upper:]-' '[:lower:]_')
|
|
165
165
|
local varname="NOTION_API_TOKEN_${slug}"
|
|
166
166
|
[ -n "${!varname}" ] && { echo "${!varname}"; return; }
|
|
167
|
+
# Preferred path: the single secrets chokepoint. It owns the one-store rule
|
|
168
|
+
# and the surface ladder, so anything it can answer must not be read out of an
|
|
169
|
+
# OS keychain here — a second reader is how the same credential ends up living
|
|
170
|
+
# in two places and drifting. Keep this identical to `notion-access`.
|
|
171
|
+
local resolver
|
|
172
|
+
for resolver in .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs \
|
|
173
|
+
.agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs; do
|
|
174
|
+
if [ -f "$resolver" ]; then
|
|
175
|
+
local via_lisa
|
|
176
|
+
via_lisa=$(node "$resolver" get NOTION_API_TOKEN 2>/dev/null) \
|
|
177
|
+
&& [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
|
|
178
|
+
break
|
|
179
|
+
fi
|
|
180
|
+
done
|
|
181
|
+
# Legacy fallback: the OS keychain written by the guided flow below, for
|
|
182
|
+
# projects that have not adopted a credentials provider. Reached only when the
|
|
183
|
+
# chokepoint is absent or has no entry.
|
|
167
184
|
case "$(uname -s)" in
|
|
168
185
|
Darwin) security find-generic-password -s lisa-notion -a "$workspace" -w 2>/dev/null ;;
|
|
169
186
|
Linux) command -v secret-tool >/dev/null && secret-tool lookup service lisa-notion account "$workspace" 2>/dev/null ;;
|
|
@@ -44,7 +44,7 @@ Map every finding to exactly one category. When a finding could fit two, pick th
|
|
|
44
44
|
|
|
45
45
|
A finding that does not fit any of the eight is itself a signal — surface it under an additional ad-hoc category `Uncategorized` with a note explaining why no category fit. Better to surface than to drop.
|
|
46
46
|
|
|
47
|
-
The destination column is a **hint for the human triaging the row**, not the routing decision: `lisa-debrief-apply` routes on the category, never on this column. Keep the hint honest anyway — a human decides Accept/Reject partly on where the learning will land. The three knowledge categories (recurring gotcha, process friction, convention drift) all land in the committed learnings ledger through the executable contract. Machine-local auto-memory (`project_*.md`, `MEMORY.md`),
|
|
47
|
+
The destination column is a **hint for the human triaging the row**, not the routing decision: `lisa-debrief-apply` routes on the category, never on this column. Keep the hint honest anyway — a human decides Accept/Reject partly on where the learning will land. The three knowledge categories (recurring gotcha, process friction, convention drift) all land in the committed learnings ledger through the executable contract. Machine-local auto-memory (`project_*.md`, `MEMORY.md`), the host-rules directory `.agents/rules/`, and `AGENTS.md` (whose `CLAUDE.md` is only a `@AGENTS.md` pointer) are **never** destinations — the first is invisible to cloud runs and teammates, and the latter two are human-authored surfaces that agents do not write.
|
|
48
48
|
|
|
49
49
|
## Dedupe rules
|
|
50
50
|
|
|
@@ -18,7 +18,7 @@ Rungs are ordered by context cost and enforcement strength. Decision vocabulary:
|
|
|
18
18
|
| Rung | Recommendation | Destination | Enters context | Admission policy |
|
|
19
19
|
| --- | --- | --- | --- | --- |
|
|
20
20
|
| 6 | EXECUTABLE-CONTROL | Lint / ast-grep / type / test / hook / `package.lisa.json` force | Never — diagnostic fires on violation | Mechanically decidable + stable + recurred |
|
|
21
|
-
| 5 | EAGER-RULE |
|
|
21
|
+
| 5 | EAGER-RULE | Host rules: `.agents/rules/` (project scope) · plugin `rules/eager/` (upstream scope) | Unconditionally, every session | **Earned by failure evidence** (repeated misses despite retrievability); demotion-biased |
|
|
22
22
|
| 4 | SKILL | `.claude/skills/<name>/SKILL.md` | Description eager; body on invoke | Procedural, complex, recognizable trigger |
|
|
23
23
|
| 3 | WIKI | Wiki page + index entry | Only when routed to | Durable reference knowledge |
|
|
24
24
|
| 2 | KEEP-IN-LEDGER | `PROJECT_LEARNINGS.md` (bounded contract projection) | Bounded projection only | Default landing zone; probationary, expiring |
|
|
@@ -117,7 +117,7 @@ WHERE IT GOES INSTEAD — take the first one that fits:
|
|
|
117
117
|
That is a project rule — but do not write it yourself. Capture it with
|
|
118
118
|
\`/lisa:persist-learning\` so it lands in the learnings ledger with provenance
|
|
119
119
|
and a confidence score. The gardener (\`/lisa:learnings:audit\`) then proposes
|
|
120
|
-
promotion into \`.
|
|
120
|
+
promotion into the host-rules directory \`.agents/rules/\` as a human-gated ticket.
|
|
121
121
|
|
|
122
122
|
2. It changes how an existing skill should behave. Edit that skill's SKILL.md.
|
|
123
123
|
Knowledge belongs next to the procedure it modifies, not in a global preamble.
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# Text tools (grep/sed/cut/awk) break on valid JSON — multiline values, escaped
|
|
4
4
|
# quotes, reordered keys, nested objects — producing silently wrong output
|
|
5
5
|
# instead of errors. jq is the required tool for structural JSON reads and
|
|
6
|
-
# writes in shell. Promoted from
|
|
6
|
+
# writes in shell. Promoted from host-rules prose to an executable
|
|
7
7
|
# control by the learnings gardener (issue #1787).
|
|
8
8
|
#
|
|
9
9
|
# Precision-first: this hook fires only on high-confidence STRUCTURAL parsing
|
|
@@ -37,9 +37,12 @@ This is the shared slug for the config-bound-transitions policy (the `leaf-only-
|
|
|
37
37
|
|
|
38
38
|
## Project rules and learnings
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
Host-authored project rules live in the fixed, agent-neutral directory
|
|
41
|
+
`.agents/rules/` — not a configurable file. Every agent reaches it through the
|
|
42
|
+
Lisa-managed pointer block in `AGENTS.md`, so it is read once, on demand. A
|
|
43
|
+
project that still carries the retired `.claude/rules/PROJECT_RULES.md` keeps
|
|
44
|
+
it, untouched and authoritative; the pointer names it too. Automated
|
|
45
|
+
learnings never append to host rules: they use the separate machine-managed
|
|
43
46
|
ledger resolved from `.lisa.config.json` — the optional `learnings.file`
|
|
44
47
|
override, else the default `.lisa/PROJECT_LEARNINGS.md`. The ledger lives in the
|
|
45
48
|
cold `.lisa/` tree (never an auto-loaded rules directory) and is consumed only
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Derived Branch Plan (load-bearing)
|
|
2
|
+
|
|
3
|
+
**Every applicable leaf work item shows the branch it will be built from and the branch its PR lands in, as a generated `Branch Plan` section — and that section is derived data, not a second authority.** The authority is `## Target Backend Environment` resolved forward through `.lisa.config.json` `deploy.branches`. A work item that displays branches nobody can trace back to that mapping, or a flow that reads the displayed branches instead of recomputing them, is a contract violation.
|
|
4
|
+
|
|
5
|
+
**One vendor-neutral contract, cited by** `lisa-jira-write-ticket`, `lisa-github-write-issue`, `lisa-linear-write-issue`, `lisa-jira-validate-ticket`, `lisa-github-validate-issue`, `lisa-linear-validate-issue`, and `lisa-implement` (the `leaf-only-lifecycle` / `repo-scope-split` precedent: one shared slug, never divergent per-vendor prose).
|
|
6
|
+
|
|
7
|
+
## Why derived, and not two branch fields
|
|
8
|
+
|
|
9
|
+
The operator need is real: reading a ticket should not require resolving a config file to learn where the work starts and where it lands. But Lisa already owns that fact. Adding independent literal branch fields would create **a second source of truth that can drift** — and the failure mode of that drift is not a cosmetic mismatch, it is code silently shipped to the wrong environment. So the branches are rendered for humans and recomputed for machines. The rendered branches are **never read as input** by any flow, at any phase — a surface that needs the base branch derives it again from the environment.
|
|
10
|
+
|
|
11
|
+
## The rendered section
|
|
12
|
+
|
|
13
|
+
Exactly three lines, identical across vendors (`h2. Branch Plan` in JIRA wiki markup, `## Branch Plan` in GitHub/Linear markdown):
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
Branch from: <branch>
|
|
17
|
+
PR into: <branch>
|
|
18
|
+
Derived from: Target Backend Environment <env> via .lisa.config.json deploy.branches
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The third line is the **derivation provenance line**. It is what marks the section machine-authored: a `Branch Plan` without it is treated as hand-authored, and hand-authored branches are never obeyed.
|
|
22
|
+
|
|
23
|
+
`Branch from` and `PR into` name the **same branch by construction** — implementation checks the feature branch out of `origin/<base>` and targets the PR back into `<base>`. A plan naming two different branches is malformed and fails validation. (A fix that genuinely lands on one branch and must reach another travels as a linked follow-up work item with its own branch plan, never as a divergence inside one plan.)
|
|
24
|
+
|
|
25
|
+
## Derivation
|
|
26
|
+
|
|
27
|
+
1. Resolve the environment under the existing `Target Backend Environment` grammar (`config-resolution`, `pre-flight-autofill`) — human-confirmed wins, then validated `Inferred:`, then `Assumption:`.
|
|
28
|
+
2. Map that exact configured key forward through `deploy.branches`. The key must map uniquely and the mapped branch must exist on the remote.
|
|
29
|
+
3. Render both fields to that branch plus the provenance line.
|
|
30
|
+
|
|
31
|
+
A missing or **ambiguous** mapping, a key that is **not unique**, or a branch absent from the remote **stops** the flow — exactly as `lisa-implement` already stops. **Never default independently to `main`, to the remote default branch, or to any other branch** to keep a write from failing. The remote default is a legitimate input only through the `Assumption:` fallback the environment grammar already defines, and it must be recorded as such.
|
|
32
|
+
|
|
33
|
+
## Exemption
|
|
34
|
+
|
|
35
|
+
Work that declares no runtime behavior change — `runtime_behavior_change = false`, i.e. doc-only, config-only, and type-only items — carries **no `Target Backend Environment`**, therefore has nothing to derive from and **requires no branch plan**. Its **absence is correct**, not a missing section, and no gate may demand one. Containers (an Epic, or a Story/Spike still holding child work) are exempt for the same reason: they are not built directly.
|
|
36
|
+
|
|
37
|
+
The exemption is one-way. Exempt work that *carries* a branch plan fails validation — branches asserted for work that declared it has no runtime target are hand-authored by definition, which is the second-authority failure this rule exists to prevent.
|
|
38
|
+
|
|
39
|
+
## Conflict, legacy, and drift
|
|
40
|
+
|
|
41
|
+
- **Conflict** — a present plan that disagrees with the recomputed plan **fails**; the flow never silently picks a side. The remediation is always to correct the environment, never to edit the branches.
|
|
42
|
+
- **Legacy** — an item written before this rule has no plan. At claim time the implementing agent derives it, **writes the assumption onto the item as a comment**, and proceeds. Never a silent guess.
|
|
43
|
+
- **Drift** — config changed after the plan was rendered. Current config wins; the stale plan is re-rendered and never followed.
|
|
44
|
+
|
|
45
|
+
Full derivation table, gate S19 semantics, the claim-time arms, and the vendor rendering details: [reference/derived-branch-plan.md](../reference/derived-branch-plan.md).
|
|
@@ -15,7 +15,8 @@ receive the contract's bounded projection, not the full file.
|
|
|
15
15
|
|
|
16
16
|
Missing learnings are a silent no-op. Malformed, non-canonical, unsafe, or
|
|
17
17
|
over-budget learnings produce one readable warning, apply no entry, and must not
|
|
18
|
-
block unrelated task work. Never append automated learnings to
|
|
19
|
-
`PROJECT_RULES.md
|
|
18
|
+
block unrelated task work. Never append automated learnings to host rules
|
|
19
|
+
(`.agents/rules/`, or a surviving legacy `PROJECT_RULES.md`); project learnings
|
|
20
|
+
are a separate machine-managed document.
|
|
20
21
|
|
|
21
22
|
Full prose: [reference/project-learnings.md](../reference/project-learnings.md).
|
|
@@ -33,7 +33,6 @@ fi
|
|
|
33
33
|
{
|
|
34
34
|
"tracker": "jira",
|
|
35
35
|
"source": "notion",
|
|
36
|
-
"projectRulesFile": ".claude/rules/PROJECT_RULES.md",
|
|
37
36
|
|
|
38
37
|
"atlassian": { "cloudId": "<uuid>", "site": "<host>" },
|
|
39
38
|
"jira": {
|
|
@@ -179,12 +178,21 @@ fi
|
|
|
179
178
|
|-------|----------|---------|-------|
|
|
180
179
|
| `tracker` | **yes** | — | Destination for ticket writes. One of `"jira"`, `"github"`, `"linear"`. Missing → fail with instruction to run the matching `/lisa:setup:*` skill. |
|
|
181
180
|
| `source` | no | — | Default PRD source for batch skills (`/lisa:intake`) and arg-less single-PRD skills. One of `"notion"`, `"confluence"`, `"linear"`, `"github"`, `"jira"`. Explicit URLs/keys passed to a skill always win over `source`; this is a default, not a lock. |
|
|
182
|
-
|
|
|
181
|
+
| ~~`projectRulesFile`~~ | — | — | **Retired.** Host-authored rules now live in the fixed, agent-neutral directory `.agents/rules/` — not a configurable file. An existing key is still parsed and preserved so installed projects keep applying, but nothing serves rules from it. See **Host rules** below. |
|
|
183
182
|
| `learnings.file` | no | `.lisa/PROJECT_LEARNINGS.md` | Safe repo-relative Markdown path for the machine-managed learnings ledger, overriding the `.lisa/` default. Rejected if it resolves inside any auto-loaded rules tree (`.claude/rules`, `.cursor/rules`, `.github/instructions`, `.agents/rules`) — the ledger must stay out of eager context. |
|
|
184
183
|
| `health.schedule` | no | `off` | Health cadence contract: `off`, `daily`, or `weekly`. `lisa sync` populates the default with `_lisaSync.populated` provenance and rejects values outside this closed vocabulary. Runtime results live only at the gitignored `.lisa/health/latest.json`, never in either config file. |
|
|
185
184
|
| `usage` | no | — | Optional token/cost pricing metadata consumed by the `usage-accounting` rule. Missing pricing never blocks a lifecycle flow; Lisa records token counts with `estimated_cost: null` when no trustworthy price source is configured. |
|
|
186
185
|
| `wiki` | no | — | Wiki location for the `wiki-knowledge-source` rule. Omit for a local in-repo wiki (`wiki/`). See **Wiki source** below. |
|
|
187
186
|
|
|
187
|
+
### Host rules (`.agents/rules/`)
|
|
188
|
+
|
|
189
|
+
Host-authored operating rules live in `.agents/rules/` — **fixed, not configurable**. One directory, every agent.
|
|
190
|
+
|
|
191
|
+
- **Not a native auto-load tree.** `.claude/rules`, `.cursor/rules`, and `.github/instructions` are; `.agents/rules` deliberately is not. Every agent reaches it through the single Lisa-managed pointer block in `AGENTS.md` (Claude via the `@AGENTS.md` import in `CLAUDE.md`), so no agent loads host rules twice.
|
|
192
|
+
- **Host-owned.** Lisa never writes rule bodies into `.agents/rules/`, and never edits or deletes a file there. Lisa's own rules originate in its plugins and arrive by their own route.
|
|
193
|
+
- **Not the learnings ledger.** `.agents/rules` is reserved in the auto-loaded-tree blocklist, so a `learnings.file` override can never resolve inside it.
|
|
194
|
+
- **Transition.** A project that still has the retired `.claude/rules/PROJECT_RULES.md` keeps it, untouched and authoritative. The pointer block names it so agents whose runtime does not auto-load `.claude/rules/` still find it. Moving that content is a human-gated decision, never an automated rewrite.
|
|
195
|
+
|
|
188
196
|
### Wiki source (`wiki`)
|
|
189
197
|
|
|
190
198
|
Declares **where this repo's LLM Wiki lives** so the query/ingest skills can resolve and (for a remote wiki) mirror it. `wiki.source` has two shapes — **local** (`path`) and **remote** (`url`) — and the block belongs in the **consumer** repo's `.lisa.config.json`, not in `wiki/lisa-wiki.config.json` (which describes a wiki from the inside and is unavailable until a remote wiki is mirrored — chicken-and-egg). The whole `wiki` block is optional; omit it and the resolver falls back to the in-repo `wiki/` convention.
|
|
@@ -847,6 +855,22 @@ read_notion_token() {
|
|
|
847
855
|
local slug=$(echo "$workspace" | tr '[:upper:]-' '[:lower:]_')
|
|
848
856
|
local varname="NOTION_API_TOKEN_${slug}"
|
|
849
857
|
[ -n "${!varname}" ] && { echo "${!varname}"; return; }
|
|
858
|
+
# Preferred path: the single secrets chokepoint, which owns the one-store rule
|
|
859
|
+
# and the surface ladder. An agent following this reference must try it before
|
|
860
|
+
# any keychain — a second reader is how one credential ends up in two places.
|
|
861
|
+
local resolver
|
|
862
|
+
for resolver in .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs \
|
|
863
|
+
.agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs; do
|
|
864
|
+
if [ -f "$resolver" ]; then
|
|
865
|
+
local via_lisa
|
|
866
|
+
via_lisa=$(node "$resolver" get NOTION_API_TOKEN 2>/dev/null) \
|
|
867
|
+
&& [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
|
|
868
|
+
break
|
|
869
|
+
fi
|
|
870
|
+
done
|
|
871
|
+
# Legacy fallback: the OS keychain written by the guided setup flow, for
|
|
872
|
+
# projects with no credentials provider. Reached only when the chokepoint is
|
|
873
|
+
# absent or has no entry.
|
|
850
874
|
case "$(uname -s)" in
|
|
851
875
|
Darwin) security find-generic-password -s lisa-notion -a "$workspace" -w 2>/dev/null ;;
|
|
852
876
|
Linux) command -v secret-tool >/dev/null && \
|