@codyswann/lisa 2.316.1 → 2.317.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/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +149 -9
- package/dist/core/upstream-evidence-manifest.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-atlassian-access/SKILL.md +17 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-automation-status/SKILL.md +23 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-doctor/SKILL.md +33 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-implement/SKILL.md +17 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-notion-access/SKILL.md +17 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-remote-dispatch/SKILL.md +99 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-remote-dispatch/agents/openai.yaml +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-remote-dispatch/scripts/dispatch.mjs +264 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/SKILL.md +137 -51
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/doctor-secrets.mjs +217 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/envfile.mjs +78 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +105 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/providers.mjs +240 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/read-secret-note.mjs +82 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/resolve-secret.mjs +151 -176
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/rotate-secret.mjs +266 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/surfaces.mjs +147 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-secrets-access/scripts/validate-config.mjs +224 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-automations/SKILL.md +69 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-automations/scripts/generate-workflow.mjs +185 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/SKILL.md +150 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/agents/openai.yaml +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/assets/setup.sh +45 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +227 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/toolchain.mjs +169 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/verify-remote-env.mjs +186 -0
- package/plugins/lisa/commands/implement.md +5 -3
- package/plugins/lisa/commands/setup/remote-env.md +7 -0
- package/plugins/lisa/rules/eager/report-actionability.md +23 -0
- package/plugins/lisa/rules/reference/report-actionability.md +61 -0
- package/plugins/lisa/skills/lisa-atlassian-access/SKILL.md +17 -0
- package/plugins/lisa/skills/lisa-automation-status/SKILL.md +23 -0
- package/plugins/lisa/skills/lisa-doctor/SKILL.md +33 -0
- package/plugins/lisa/skills/lisa-implement/SKILL.md +17 -0
- package/plugins/lisa/skills/lisa-notion-access/SKILL.md +17 -0
- package/plugins/lisa/skills/lisa-remote-dispatch/SKILL.md +99 -0
- package/plugins/lisa/skills/lisa-remote-dispatch/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/lisa-remote-dispatch/scripts/dispatch.mjs +264 -0
- package/plugins/lisa/skills/lisa-secrets-access/SKILL.md +138 -52
- package/plugins/lisa/skills/lisa-secrets-access/scripts/doctor-secrets.mjs +217 -0
- package/plugins/lisa/skills/lisa-secrets-access/scripts/envfile.mjs +78 -0
- package/plugins/lisa/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +105 -0
- package/plugins/lisa/skills/lisa-secrets-access/scripts/providers.mjs +240 -0
- package/plugins/lisa/skills/lisa-secrets-access/scripts/read-secret-note.mjs +82 -0
- package/plugins/lisa/skills/lisa-secrets-access/scripts/resolve-secret.mjs +151 -176
- package/plugins/lisa/skills/lisa-secrets-access/scripts/rotate-secret.mjs +266 -0
- package/plugins/lisa/skills/lisa-secrets-access/scripts/surfaces.mjs +147 -0
- package/plugins/lisa/skills/lisa-secrets-access/scripts/validate-config.mjs +224 -0
- package/plugins/lisa/skills/lisa-setup-automations/SKILL.md +69 -0
- package/plugins/lisa/skills/lisa-setup-automations/scripts/generate-workflow.mjs +185 -0
- package/plugins/lisa/skills/lisa-setup-remote-env/SKILL.md +150 -0
- package/plugins/lisa/skills/lisa-setup-remote-env/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/lisa-setup-remote-env/assets/setup.sh +45 -0
- package/plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +227 -0
- package/plugins/lisa/skills/lisa-setup-remote-env/scripts/toolchain.mjs +169 -0
- package/plugins/lisa/skills/lisa-setup-remote-env/scripts/verify-remote-env.mjs +186 -0
- package/plugins/lisa-agy/commands/lisa/implement.md +5 -3
- package/plugins/lisa-agy/commands/lisa/setup/remote-env.md +7 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-atlassian-access/SKILL.md +17 -0
- package/plugins/lisa-agy/skills/lisa-automation-status/SKILL.md +23 -0
- package/plugins/lisa-agy/skills/lisa-doctor/SKILL.md +33 -0
- package/plugins/lisa-agy/skills/lisa-implement/SKILL.md +17 -0
- package/plugins/lisa-agy/skills/lisa-notion-access/SKILL.md +17 -0
- package/plugins/lisa-agy/skills/lisa-remote-dispatch/SKILL.md +99 -0
- package/plugins/lisa-agy/skills/lisa-remote-dispatch/scripts/dispatch.mjs +264 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/SKILL.md +138 -52
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/doctor-secrets.mjs +217 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/envfile.mjs +78 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +105 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/providers.mjs +240 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/read-secret-note.mjs +82 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/resolve-secret.mjs +151 -176
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/rotate-secret.mjs +266 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/surfaces.mjs +147 -0
- package/plugins/lisa-agy/skills/lisa-secrets-access/scripts/validate-config.mjs +224 -0
- package/plugins/lisa-agy/skills/lisa-setup-automations/SKILL.md +69 -0
- package/plugins/lisa-agy/skills/lisa-setup-automations/scripts/generate-workflow.mjs +185 -0
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/SKILL.md +150 -0
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/assets/setup.sh +45 -0
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +227 -0
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/toolchain.mjs +169 -0
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/verify-remote-env.mjs +186 -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/commands/lisa/implement.md +5 -3
- package/plugins/lisa-copilot/commands/lisa/setup/remote-env.md +7 -0
- package/plugins/lisa-copilot/rules/eager/report-actionability.md +23 -0
- package/plugins/lisa-copilot/rules/reference/report-actionability.md +61 -0
- package/plugins/lisa-copilot/skills/lisa-atlassian-access/SKILL.md +17 -0
- package/plugins/lisa-copilot/skills/lisa-automation-status/SKILL.md +23 -0
- package/plugins/lisa-copilot/skills/lisa-doctor/SKILL.md +33 -0
- package/plugins/lisa-copilot/skills/lisa-implement/SKILL.md +17 -0
- package/plugins/lisa-copilot/skills/lisa-notion-access/SKILL.md +17 -0
- package/plugins/lisa-copilot/skills/lisa-remote-dispatch/SKILL.md +99 -0
- package/plugins/lisa-copilot/skills/lisa-remote-dispatch/scripts/dispatch.mjs +264 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/SKILL.md +138 -52
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/doctor-secrets.mjs +217 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/envfile.mjs +78 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +105 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/providers.mjs +240 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/read-secret-note.mjs +82 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/resolve-secret.mjs +151 -176
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/rotate-secret.mjs +266 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/surfaces.mjs +147 -0
- package/plugins/lisa-copilot/skills/lisa-secrets-access/scripts/validate-config.mjs +224 -0
- package/plugins/lisa-copilot/skills/lisa-setup-automations/SKILL.md +69 -0
- package/plugins/lisa-copilot/skills/lisa-setup-automations/scripts/generate-workflow.mjs +185 -0
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/SKILL.md +150 -0
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/assets/setup.sh +45 -0
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +227 -0
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/toolchain.mjs +169 -0
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/verify-remote-env.mjs +186 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/commands/lisa/implement.md +5 -3
- package/plugins/lisa-cursor/commands/lisa/setup/remote-env.md +7 -0
- package/plugins/lisa-cursor/rules/report-actionability-reference.mdc +66 -0
- package/plugins/lisa-cursor/rules/report-actionability.mdc +28 -0
- package/plugins/lisa-cursor/skills/lisa-atlassian-access/SKILL.md +17 -0
- package/plugins/lisa-cursor/skills/lisa-automation-status/SKILL.md +23 -0
- package/plugins/lisa-cursor/skills/lisa-doctor/SKILL.md +33 -0
- package/plugins/lisa-cursor/skills/lisa-implement/SKILL.md +17 -0
- package/plugins/lisa-cursor/skills/lisa-notion-access/SKILL.md +17 -0
- package/plugins/lisa-cursor/skills/lisa-remote-dispatch/SKILL.md +99 -0
- package/plugins/lisa-cursor/skills/lisa-remote-dispatch/scripts/dispatch.mjs +264 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/SKILL.md +138 -52
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/doctor-secrets.mjs +217 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/envfile.mjs +78 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +105 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/providers.mjs +240 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/read-secret-note.mjs +82 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/resolve-secret.mjs +151 -176
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/rotate-secret.mjs +266 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/surfaces.mjs +147 -0
- package/plugins/lisa-cursor/skills/lisa-secrets-access/scripts/validate-config.mjs +224 -0
- package/plugins/lisa-cursor/skills/lisa-setup-automations/SKILL.md +69 -0
- package/plugins/lisa-cursor/skills/lisa-setup-automations/scripts/generate-workflow.mjs +185 -0
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/SKILL.md +150 -0
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/assets/setup.sh +45 -0
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +227 -0
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/toolchain.mjs +169 -0
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/verify-remote-env.mjs +186 -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-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/commands/implement.md +5 -3
- package/plugins/src/base/commands/setup/remote-env.md +7 -0
- package/plugins/src/base/rules/eager/report-actionability.md +23 -0
- package/plugins/src/base/rules/reference/report-actionability.md +61 -0
- package/plugins/src/base/skills/lisa-atlassian-access/SKILL.md +17 -0
- package/plugins/src/base/skills/lisa-automation-status/SKILL.md +23 -0
- package/plugins/src/base/skills/lisa-doctor/SKILL.md +33 -0
- package/plugins/src/base/skills/lisa-implement/SKILL.md +17 -0
- package/plugins/src/base/skills/lisa-notion-access/SKILL.md +17 -0
- package/plugins/src/base/skills/lisa-remote-dispatch/SKILL.md +99 -0
- package/plugins/src/base/skills/lisa-remote-dispatch/scripts/dispatch.mjs +264 -0
- package/plugins/src/base/skills/lisa-secrets-access/SKILL.md +138 -52
- package/plugins/src/base/skills/lisa-secrets-access/scripts/doctor-secrets.mjs +217 -0
- package/plugins/src/base/skills/lisa-secrets-access/scripts/envfile.mjs +78 -0
- package/plugins/src/base/skills/lisa-secrets-access/scripts/materialize-secrets.mjs +105 -0
- package/plugins/src/base/skills/lisa-secrets-access/scripts/providers.mjs +240 -0
- package/plugins/src/base/skills/lisa-secrets-access/scripts/read-secret-note.mjs +82 -0
- package/plugins/src/base/skills/lisa-secrets-access/scripts/resolve-secret.mjs +151 -176
- package/plugins/src/base/skills/lisa-secrets-access/scripts/rotate-secret.mjs +266 -0
- package/plugins/src/base/skills/lisa-secrets-access/scripts/surfaces.mjs +147 -0
- package/plugins/src/base/skills/lisa-secrets-access/scripts/validate-config.mjs +224 -0
- package/plugins/src/base/skills/lisa-setup-automations/SKILL.md +69 -0
- package/plugins/src/base/skills/lisa-setup-automations/scripts/generate-workflow.mjs +185 -0
- package/plugins/src/base/skills/lisa-setup-remote-env/SKILL.md +150 -0
- package/plugins/src/base/skills/lisa-setup-remote-env/assets/setup.sh +45 -0
- package/plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +227 -0
- package/plugins/src/base/skills/lisa-setup-remote-env/scripts/toolchain.mjs +169 -0
- package/plugins/src/base/skills/lisa-setup-remote-env/scripts/verify-remote-env.mjs +186 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Decide what a remote environment already has and what it still needs.
|
|
4
|
+
*
|
|
5
|
+
* Remote environments arrive with a base image that already provides much of
|
|
6
|
+
* what a project needs, so the manifest has two entry kinds rather than one:
|
|
7
|
+
*
|
|
8
|
+
* require — assert present, fail setup with a clear message if missing
|
|
9
|
+
* install — provision it, pinned and checksummed
|
|
10
|
+
*
|
|
11
|
+
* The distinction is not bookkeeping. A base image is **not a contract**: the
|
|
12
|
+
* vendor can change it. A project quietly depending on a preinstalled tool
|
|
13
|
+
* should break loudly at setup when that happens, rather than mysteriously
|
|
14
|
+
* mid-task weeks later. `require` is what converts an implicit assumption into
|
|
15
|
+
* an explicit check.
|
|
16
|
+
*
|
|
17
|
+
* Planning is separated from execution so the whole decision table is testable
|
|
18
|
+
* without a container, a network, or a real binary anywhere.
|
|
19
|
+
* @module toolchain
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Compare dotted version strings numerically.
|
|
24
|
+
*
|
|
25
|
+
* String comparison gets this wrong in the case that matters: "10" sorts before
|
|
26
|
+
* "9", so a base-image bump to a newer major would read as a downgrade and pass
|
|
27
|
+
* a minimum-version check it should fail.
|
|
28
|
+
* @param {string} a Left version.
|
|
29
|
+
* @param {string} b Right version.
|
|
30
|
+
* @returns {number} Negative, zero, or positive.
|
|
31
|
+
*/
|
|
32
|
+
export function compareVersions(a, b) {
|
|
33
|
+
const left = String(a).split(".").map(Number);
|
|
34
|
+
const right = String(b).split(".").map(Number);
|
|
35
|
+
for (let i = 0; i < Math.max(left.length, right.length); i += 1) {
|
|
36
|
+
const diff = (left[i] || 0) - (right[i] || 0);
|
|
37
|
+
if (diff) return diff;
|
|
38
|
+
}
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Pull a dotted version out of whatever a tool prints for `--version`.
|
|
44
|
+
*
|
|
45
|
+
* Tools are wildly inconsistent here ("bws 2.1.0", "v20.11.0", "Python 3.12.1"),
|
|
46
|
+
* and normalising with a regex is more honest than maintaining a per-tool parser
|
|
47
|
+
* table that silently rots.
|
|
48
|
+
* @param {string} output Raw `--version` output.
|
|
49
|
+
* @returns {string|null} The version, or null when none is recognisable.
|
|
50
|
+
*/
|
|
51
|
+
export function extractVersion(output) {
|
|
52
|
+
const match = /(\d+(?:\.\d+)*)/.exec(String(output ?? ""));
|
|
53
|
+
return match ? match[1] : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Classify one required tool against what the environment actually provides.
|
|
58
|
+
* @param {object} tool Manifest entry.
|
|
59
|
+
* @param {{version: string|null, present: boolean}} found Probe result.
|
|
60
|
+
* @returns {{name: string, action: string, reason: string}} The decision.
|
|
61
|
+
*/
|
|
62
|
+
function planRequired(tool, found) {
|
|
63
|
+
if (!found.present) {
|
|
64
|
+
return {
|
|
65
|
+
name: tool.name,
|
|
66
|
+
action: "missing",
|
|
67
|
+
reason:
|
|
68
|
+
`${tool.name} is required but not present. The base image is not a ` +
|
|
69
|
+
`contract — if it used to provide this, the vendor has changed it. ` +
|
|
70
|
+
`Either add an install entry or pin a different image.`,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (
|
|
74
|
+
tool.minVersion &&
|
|
75
|
+
compareVersions(found.version ?? "0", tool.minVersion) < 0
|
|
76
|
+
) {
|
|
77
|
+
return {
|
|
78
|
+
name: tool.name,
|
|
79
|
+
action: "missing",
|
|
80
|
+
reason:
|
|
81
|
+
`${tool.name} ${found.version ?? "(unknown)"} is older than the ` +
|
|
82
|
+
`required ${tool.minVersion}. Presence alone is not enough when a ` +
|
|
83
|
+
`project depends on a specific version.`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
name: tool.name,
|
|
88
|
+
action: "present",
|
|
89
|
+
reason: `${tool.name} ${found.version ?? ""}`.trim(),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Classify one installable tool against what is already on disk.
|
|
95
|
+
*
|
|
96
|
+
* Detect first, install second. Skipping a matching version is what makes setup
|
|
97
|
+
* and maintenance the same script, and it is the cheap path when a container
|
|
98
|
+
* resumes from cache rather than being built fresh.
|
|
99
|
+
* @param {object} tool Manifest entry.
|
|
100
|
+
* @param {{version: string|null, present: boolean}} found Probe result.
|
|
101
|
+
* @returns {{name: string, action: string, reason: string}} The decision.
|
|
102
|
+
*/
|
|
103
|
+
function planInstallable(tool, found) {
|
|
104
|
+
if (!tool.version) {
|
|
105
|
+
return {
|
|
106
|
+
name: tool.name,
|
|
107
|
+
action: "invalid",
|
|
108
|
+
reason: `${tool.name} has no pinned version. An unpinned install is not reproducible.`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (found.present && found.version === tool.version) {
|
|
112
|
+
return {
|
|
113
|
+
name: tool.name,
|
|
114
|
+
action: "skip",
|
|
115
|
+
reason: `${tool.name} ${tool.version} already installed`,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
name: tool.name,
|
|
120
|
+
action: "install",
|
|
121
|
+
reason: found.present
|
|
122
|
+
? `${tool.name} ${found.version ?? "(unknown)"} does not match pin ${tool.version}`
|
|
123
|
+
: `${tool.name} ${tool.version} not installed`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Produce the complete plan for a toolchain manifest.
|
|
129
|
+
* @param {{require?: object[], install?: object[]}} tools Manifest.
|
|
130
|
+
* @param {(name: string) => {version: string|null, present: boolean}} probe Version probe.
|
|
131
|
+
* @returns {Array<{name: string, action: string, reason: string}>} Ordered decisions.
|
|
132
|
+
*/
|
|
133
|
+
export function planToolchain(tools, probe) {
|
|
134
|
+
const plan = [];
|
|
135
|
+
for (const tool of tools.require ?? [])
|
|
136
|
+
plan.push(planRequired(tool, probe(tool.name)));
|
|
137
|
+
for (const tool of tools.install ?? [])
|
|
138
|
+
plan.push(planInstallable(tool, probe(tool.name)));
|
|
139
|
+
return plan;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Reject a manifest entry that could install something unverifiable.
|
|
144
|
+
*
|
|
145
|
+
* A pinned version with no checksum still trusts whatever the URL serves today.
|
|
146
|
+
* Requiring both, changed together in one reviewed commit, is what makes an
|
|
147
|
+
* unexpected archive fail before installation rather than after.
|
|
148
|
+
* @param {object} tool Manifest entry.
|
|
149
|
+
*/
|
|
150
|
+
export function assertPinned(tool) {
|
|
151
|
+
if (tool.install === "release-zip") {
|
|
152
|
+
if (!tool.url || !tool.sha256) {
|
|
153
|
+
throw new Error(
|
|
154
|
+
`${tool.name}: a release-zip install needs both url and sha256.\n` +
|
|
155
|
+
`A version bump must move the checksum in the same reviewed commit.`
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (tool.install === "npm-global") {
|
|
161
|
+
if (!tool.package)
|
|
162
|
+
throw new Error(`${tool.name}: npm-global install needs a package`);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
throw new Error(
|
|
166
|
+
`${tool.name}: unknown install method "${tool.install}".\n` +
|
|
167
|
+
`Supported: release-zip, npm-global.`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Prove a remote environment is actually configured the way it was meant to be.
|
|
4
|
+
*
|
|
5
|
+
* This runs identically whatever provisioned the environment — an API, a driven
|
|
6
|
+
* browser, or a human pasting emitted config. That is the point: trust comes
|
|
7
|
+
* from the read-back, not from the mechanism, so the weakest provisioning tier
|
|
8
|
+
* ends up as trustworthy as the strongest.
|
|
9
|
+
*
|
|
10
|
+
* Every assertion is made **without printing a value**. Presence, mode, and
|
|
11
|
+
* shape are all checkable without revealing content, and an environment proof
|
|
12
|
+
* that leaks the thing it is proving is not a proof worth having.
|
|
13
|
+
*
|
|
14
|
+
* One rule this encodes the hard way: never verify against a vendor's own UI.
|
|
15
|
+
* A dashboard counter reported zero tasks for an environment that had
|
|
16
|
+
* demonstrably completed one, because the underlying records carried no
|
|
17
|
+
* environment identifier. Assert against durable state instead.
|
|
18
|
+
*
|
|
19
|
+
* Usage:
|
|
20
|
+
* verify-remote-env.mjs
|
|
21
|
+
* @module verify-remote-env
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { execFileSync } from "node:child_process";
|
|
25
|
+
import { existsSync, statSync } from "node:fs";
|
|
26
|
+
|
|
27
|
+
import { readRemoteEnvConfig } from "./setup-remote-env.mjs";
|
|
28
|
+
import { extractVersion } from "./toolchain.mjs";
|
|
29
|
+
|
|
30
|
+
/** Collected results, so every check runs before anything reports failure. */
|
|
31
|
+
const results = [];
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Record one assertion.
|
|
35
|
+
* @param {boolean} ok Whether it held.
|
|
36
|
+
* @param {string} label What was checked.
|
|
37
|
+
* @param {string} [detail] Extra context, never a secret value.
|
|
38
|
+
*/
|
|
39
|
+
function check(ok, label, detail = "") {
|
|
40
|
+
results.push({ ok, label, detail });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Assert a path exists with an exact permission mode.
|
|
45
|
+
*
|
|
46
|
+
* The mode is the assertion, not merely existence. A world-readable secrets
|
|
47
|
+
* file is present and wrong, and "present" is what a weaker check would report.
|
|
48
|
+
* @param {string} path Filesystem path.
|
|
49
|
+
* @param {number} mode Expected permission bits.
|
|
50
|
+
* @param {string} label What this path is.
|
|
51
|
+
*/
|
|
52
|
+
function checkMode(path, mode, label) {
|
|
53
|
+
if (!existsSync(path)) {
|
|
54
|
+
check(false, label, "absent");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const actual = statSync(path).mode & 0o777;
|
|
58
|
+
check(
|
|
59
|
+
actual === mode,
|
|
60
|
+
label,
|
|
61
|
+
actual === mode
|
|
62
|
+
? `mode ${mode.toString(8)}`
|
|
63
|
+
: `mode ${actual.toString(8)}, expected ${mode.toString(8)}`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Run a sibling skill's script and capture its output.
|
|
69
|
+
* @param {string[]} args Node arguments.
|
|
70
|
+
* @returns {{ok: boolean, out: string}} Result.
|
|
71
|
+
*/
|
|
72
|
+
function node(args) {
|
|
73
|
+
try {
|
|
74
|
+
const out = execFileSync("node", args, {
|
|
75
|
+
encoding: "utf8",
|
|
76
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
77
|
+
});
|
|
78
|
+
return { ok: true, out: out.trim() };
|
|
79
|
+
} catch (err) {
|
|
80
|
+
return { ok: false, out: String(err.stderr ?? err.message).trim() };
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Assert each declared tool is present at its pinned or minimum version.
|
|
86
|
+
* @param {object} tools Toolchain manifest.
|
|
87
|
+
*/
|
|
88
|
+
function verifyToolchain(tools) {
|
|
89
|
+
for (const tool of tools.require ?? []) {
|
|
90
|
+
try {
|
|
91
|
+
const out = execFileSync(tool.name, ["--version"], {
|
|
92
|
+
encoding: "utf8",
|
|
93
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
94
|
+
});
|
|
95
|
+
check(true, `tool ${tool.name}`, extractVersion(out) ?? "present");
|
|
96
|
+
} catch {
|
|
97
|
+
check(false, `tool ${tool.name}`, "required but not present");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
for (const tool of tools.install ?? []) {
|
|
101
|
+
try {
|
|
102
|
+
const out = execFileSync(tool.name, ["--version"], {
|
|
103
|
+
encoding: "utf8",
|
|
104
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
105
|
+
});
|
|
106
|
+
const found = extractVersion(out);
|
|
107
|
+
check(
|
|
108
|
+
found === tool.version,
|
|
109
|
+
`tool ${tool.name}`,
|
|
110
|
+
`${found} (pin ${tool.version})`
|
|
111
|
+
);
|
|
112
|
+
} catch {
|
|
113
|
+
check(
|
|
114
|
+
false,
|
|
115
|
+
`tool ${tool.name}`,
|
|
116
|
+
`pinned ${tool.version} but not installed`
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Assert the working tree is clean.
|
|
124
|
+
*
|
|
125
|
+
* A remote environment that starts dirty will carry unrelated changes into
|
|
126
|
+
* whatever it produces, and the resulting diff is no longer attributable to the
|
|
127
|
+
* task that ran.
|
|
128
|
+
*/
|
|
129
|
+
function verifyCleanCheckout() {
|
|
130
|
+
try {
|
|
131
|
+
const out = execFileSync("git", ["status", "--porcelain"], {
|
|
132
|
+
encoding: "utf8",
|
|
133
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
134
|
+
});
|
|
135
|
+
check(
|
|
136
|
+
out.trim() === "",
|
|
137
|
+
"clean checkout",
|
|
138
|
+
out.trim() ? "working tree is dirty" : ""
|
|
139
|
+
);
|
|
140
|
+
} catch {
|
|
141
|
+
check(false, "clean checkout", "not a git repository");
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function main() {
|
|
146
|
+
const cfg = readRemoteEnvConfig();
|
|
147
|
+
const secretsDir = process.argv[2];
|
|
148
|
+
|
|
149
|
+
verifyToolchain(cfg.tools);
|
|
150
|
+
|
|
151
|
+
const surface = node([
|
|
152
|
+
new URL(
|
|
153
|
+
"../../lisa-secrets-access/scripts/resolve-secret.mjs",
|
|
154
|
+
import.meta.url
|
|
155
|
+
).pathname,
|
|
156
|
+
"surface",
|
|
157
|
+
]);
|
|
158
|
+
check(surface.ok, "surface detected", surface.out);
|
|
159
|
+
|
|
160
|
+
if (secretsDir) {
|
|
161
|
+
checkMode(secretsDir, 0o700, "secrets directory");
|
|
162
|
+
checkMode(`${secretsDir}/secrets.env`, 0o600, "values file");
|
|
163
|
+
checkMode(`${secretsDir}/secret-notes.json`, 0o600, "notes file");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
verifyCleanCheckout();
|
|
167
|
+
|
|
168
|
+
let failed = 0;
|
|
169
|
+
for (const r of results) {
|
|
170
|
+
if (!r.ok) failed += 1;
|
|
171
|
+
console.log(
|
|
172
|
+
` ${r.ok ? "ok " : "FAIL"} ${r.label.padEnd(28)} ${r.detail}`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
if (failed) throw new Error(`${failed} environment check(s) failed`);
|
|
176
|
+
console.log("\nRemote environment verified without revealing any value.");
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
180
|
+
try {
|
|
181
|
+
main();
|
|
182
|
+
} catch (err) {
|
|
183
|
+
console.error(err.message);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: "Implement a single work item end-to-end. Vendor-agnostic: given a work-item URL/key (JIRA, Linear, GitHub Issues) or description, reads it, determines work type (Build/Fix/Improve/Investigate), assembles an agent team, runs the full lifecycle through PR + evidence. For batch processing of all Status=Ready tickets in a queue, use /lisa:intake instead."
|
|
3
|
-
argument-hint: "<single-work-item-url | key | description>"
|
|
2
|
+
description: "Implement a single work item end-to-end. Vendor-agnostic: given a work-item URL/key (JIRA, Linear, GitHub Issues) or description, reads it, determines work type (Build/Fix/Improve/Investigate), assembles an agent team, runs the full lifecycle through PR + evidence. Pass executionEnv=codex-cloud to run the work on a remote surface instead of locally — it submits and returns without polling, so the laptop is a launcher rather than the execution substrate. For batch processing of all Status=Ready tickets in a queue, use /lisa:intake instead."
|
|
3
|
+
argument-hint: "[executionEnv=local|codex-cloud] <single-work-item-url | key | description>"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Use the /lisa-implement skill to take the work item from spec to shipped: read the source, determine work type, assemble an agent team, and run the full lifecycle through PR creation, code review, deploy, and empirical verification.
|
|
6
|
+
Use the /lisa-implement skill to take the work item from spec to shipped: read the source, determine work type, assemble an agent team, and run the full lifecycle through PR creation, code review, deploy, and empirical verification.
|
|
7
|
+
|
|
8
|
+
If `$ARGUMENTS` carries `executionEnv=` with anything other than `local`, route the work to that surface via /lisa-remote-dispatch and report the recorded task identifier instead of running the lifecycle locally. $ARGUMENTS
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Provision and verify a remote execution environment for this project — Codex Cloud today. Generates the repository-owned setup script that installs the declared toolchain, materializes secrets through lisa-secrets-access, and runs the project's own hook. Provisions by API where one exists, by driving the vendor console where one does not, and by emitting exact config otherwise, then proves the result with the same read-back regardless of tier. Run this before dispatching work with executionEnv."
|
|
3
|
+
allowed-tools: ["Skill"]
|
|
4
|
+
argument-hint: "[codex-cloud] [--verify]"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Use the /lisa-setup-remote-env skill to provision and verify a remote execution environment. $ARGUMENTS
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Report Actionability — Every Report Says What the Reader Must Do (load-bearing)
|
|
2
|
+
|
|
3
|
+
A report that describes real work accurately can still fail, because the reader cannot tell **which items need them and which are already handled**. Prose that mixes fixed and open items reads as a single undifferentiated pile of problems; the reader either acts on something already done, or assumes something open was handled. Both are caused by the report, not by the work.
|
|
4
|
+
|
|
5
|
+
The specific failure this rule exists to prevent, observed in a review cycle: six findings were returned, three were fixed, and the report described **two of them in detail without ever stating that six existed**. Every sentence in it was true. The reader still had to ask "did you fix them?" — and the answer was partly yes, partly no, and one finding had not even been read. A subset presented in the shape of a whole is worse than silence, because silence does not create false confidence.
|
|
6
|
+
|
|
7
|
+
## Mandatory
|
|
8
|
+
|
|
9
|
+
1. **State the denominator first.** Any report of findings, failures, checks, or review comments opens with the total — "6 findings: 3 fixed, 2 open, 1 needs your decision". A reader must never have to ask how many there were.
|
|
10
|
+
2. **Account for every item.** Each one gets an explicit disposition. Nothing is omitted because it is minor, because it was fixed, or because it is embarrassing. An item you have not yet read is itself a disposition — say **"not read yet"** rather than leaving it out.
|
|
11
|
+
3. **Label each item with who acts.** Exactly one of:
|
|
12
|
+
- **DONE** — handled; no action from the reader. Say so in the same breath as describing it.
|
|
13
|
+
- **OPEN** — needs work; state whether you are about to do it or waiting.
|
|
14
|
+
- **DECISION** — blocked on the reader; state the question and the options.
|
|
15
|
+
4. **Never describe a fixed item as if it were live.** If you are reporting a defect you already fixed — which is often right, because the reader may need to know it existed — mark it fixed in the *first* sentence, not the last.
|
|
16
|
+
5. **A subset must announce itself as one.** "Here are the two most serious" is fine. "Here is what the review said", when it was two of six, is not.
|
|
17
|
+
6. **Lead the whole report with the action line**, per `automation-runbook-contract` — which already requires a terminating flow to open with its outcome and the operator action. This rule extends that requirement to reports that do **not** terminate a flow: a mid-run status update, a review summary, a "here is what CI said". The originating incident was one of those, which is how it slipped past a contract that only bound final messages.
|
|
18
|
+
|
|
19
|
+
## Applies to
|
|
20
|
+
|
|
21
|
+
Code-review findings, CI failures, test results, audit output, security scans, deploy status, and any list of problems handed to a human. It applies equally to reports you are proud of and reports that expose your own mistake — the second kind is where the temptation to describe a flattering subset is strongest.
|
|
22
|
+
|
|
23
|
+
Detail, worked examples and the failure taxonomy: [reference/report-actionability.md](../reference/report-actionability.md).
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Report Actionability — Reference
|
|
2
|
+
|
|
3
|
+
Long-form body for [eager/report-actionability.md](../eager/report-actionability.md).
|
|
4
|
+
|
|
5
|
+
## The originating incident
|
|
6
|
+
|
|
7
|
+
A pull request received six review findings. Three were fixed in the working session: a critical ordering defect, a major copy inaccuracy, and a test that asserted call counts where it needed to assert call order.
|
|
8
|
+
|
|
9
|
+
The report to the owner described **two** of the six, in detail, with the reasoning behind each fix. It never said six existed. It never mentioned the three untouched, one of which was a Major finding that had not been opened at all.
|
|
10
|
+
|
|
11
|
+
Nothing in the report was false. The owner's next message was: *"so the findings... did you fix them?"* — followed by *"why did you tell me about those findings? It seems like you fixed them."*
|
|
12
|
+
|
|
13
|
+
Two distinct confusions, from one report:
|
|
14
|
+
|
|
15
|
+
- **Ambiguous disposition.** Items were described without being labelled fixed or open, so the reader could not tell whether the message was a status update or a request for action.
|
|
16
|
+
- **Implied completeness.** A subset was presented in the grammatical shape of a complete account ("the review caught…"), so the reader reasonably assumed the remainder did not exist.
|
|
17
|
+
|
|
18
|
+
## Why "everything I said was true" is not a defence
|
|
19
|
+
|
|
20
|
+
Accuracy per sentence and accuracy of the whole are different properties. A report is a claim about a **set**: reporting two members of a six-member set, in a frame that does not name the set's size, asserts something false about the set even when every individual sentence is true.
|
|
21
|
+
|
|
22
|
+
This is the reporting analogue of a check that passes because it never ran. The output looks like the real thing and carries the same authority.
|
|
23
|
+
|
|
24
|
+
## The temptation this rule resists
|
|
25
|
+
|
|
26
|
+
The subset a report drifts toward is rarely random. It skews toward:
|
|
27
|
+
|
|
28
|
+
- **items already fixed** (they make the report feel like progress),
|
|
29
|
+
- **items understood well** (they are easier to write about),
|
|
30
|
+
- **items that flatter** (a bug caught and fixed reads better than one not yet read).
|
|
31
|
+
|
|
32
|
+
The three omitted in the originating incident were: one not yet read, one only partially addressed and for an unrelated reason, and two dismissed internally as trivial formatting nits without ever saying so. Each omission was individually defensible. Together they produced a false picture.
|
|
33
|
+
|
|
34
|
+
## Worked example
|
|
35
|
+
|
|
36
|
+
**Wrong** — true, and misleading:
|
|
37
|
+
|
|
38
|
+
> The review caught a real bug. `stopAnalytics` called `opt_out_capturing()` then `reset(true)`, and reset clears the opt-out flag… [400 words] …Second finding: the banner said "nothing is stored" while we persist the refusal. Fixed both.
|
|
39
|
+
|
|
40
|
+
**Right:**
|
|
41
|
+
|
|
42
|
+
> **6 review findings: 3 fixed, 3 open. Nothing needed from you unless you want the open ones handled now.**
|
|
43
|
+
>
|
|
44
|
+
> Fixed — no action:
|
|
45
|
+
> - 🔴 `posthog.ts` — withdrawal did not stick; reset wiped the opt-out. Order corrected, test now asserts order, mutation-proven.
|
|
46
|
+
> - 🟠 `site.ts` — banner overstated what a refusal stores. Copy, privacy page and fixtures corrected.
|
|
47
|
+
> - nitpick — test asserted call counts, now asserts order.
|
|
48
|
+
>
|
|
49
|
+
> Open:
|
|
50
|
+
> - 🟠 `lib/consent/index.ts` — uses `localStorage` directly instead of the storage abstraction; consent will not persist on native. **I had not read this one until now.**
|
|
51
|
+
> - 🟡 `audit.ignore.config.json` — obsolete advisory exclusion still present.
|
|
52
|
+
> - 🟡 `CHANGELOG.md` ×2 — formatting.
|
|
53
|
+
|
|
54
|
+
The second is shorter and answers "must I do anything?" in its first line.
|
|
55
|
+
|
|
56
|
+
## Interaction with other rules
|
|
57
|
+
|
|
58
|
+
- **`automation-runbook-contract`** — requires a terminating flow to open with its run outcome and the operator action. That contract binds the FINAL message of a flow. This rule covers everything else: mid-run updates, review summaries, CI reports. The originating incident was a mid-run report, which is exactly why the existing contract did not catch it. Where both apply, the runbook contract's outcome vocabulary wins for the opening line and this rule governs the body.
|
|
59
|
+
- **`falsifiable-checks`** — that rule governs instruments that cannot fail. This one governs *accounts* that cannot be acted on. Both produce false confidence with no visible defect.
|
|
60
|
+
- **`stale-state-claims`** — a disposition recorded in a report expires exactly like a "not yet" comment. A finding reported OPEN and fixed an hour later must be re-reported, not left standing.
|
|
61
|
+
- **Corrections guidance** — reporting an error you made is required by this rule when it is one of the findings. Mark it fixed in the first sentence and move on; the rule demands completeness, never self-flagellation.
|
|
@@ -81,6 +81,23 @@ read_atlassian_token() {
|
|
|
81
81
|
local slug=$(echo "$email" | tr '[:upper:]@.' '[:lower:]__')
|
|
82
82
|
local varname="ATLASSIAN_API_TOKEN_${slug}"
|
|
83
83
|
[ -n "${!varname}" ] && { echo "${!varname}"; return; }
|
|
84
|
+
# Preferred path: the single secrets chokepoint. It owns the one-store rule
|
|
85
|
+
# and the surface ladder, so anything it can answer must not be read out of an
|
|
86
|
+
# OS keychain here — a second reader is how the same credential ends up living
|
|
87
|
+
# in two places and drifting.
|
|
88
|
+
local resolver
|
|
89
|
+
for resolver in .claude/skills/lisa-secrets-access/scripts/resolve-secret.mjs \
|
|
90
|
+
.agents/skills/lisa-secrets-access/scripts/resolve-secret.mjs; do
|
|
91
|
+
if [ -f "$resolver" ]; then
|
|
92
|
+
local via_lisa
|
|
93
|
+
via_lisa=$(node "$resolver" get ATLASSIAN_API_TOKEN 2>/dev/null) \
|
|
94
|
+
&& [ -n "$via_lisa" ] && { echo "$via_lisa"; return; }
|
|
95
|
+
break
|
|
96
|
+
fi
|
|
97
|
+
done
|
|
98
|
+
# Legacy fallback: the OS keychain written by the guided /lisa:setup:atlassian
|
|
99
|
+
# flow, for projects that have not adopted a credentials provider. Reached only
|
|
100
|
+
# when the chokepoint is absent or has no entry.
|
|
84
101
|
case "$(uname -s)" in
|
|
85
102
|
Darwin) security find-generic-password -s lisa-atlassian -a "$email" -w 2>/dev/null ;;
|
|
86
103
|
Linux) command -v secret-tool >/dev/null && secret-tool lookup service lisa-atlassian account "$email" 2>/dev/null ;;
|
|
@@ -43,6 +43,29 @@ Branch on the active runtime and prefer the runtime's native automation listing
|
|
|
43
43
|
|
|
44
44
|
The report must stay repo-scoped: inspect only automations whose names belong to the current repo's Lisa fleet prefix, and do not absorb unrelated automations into the result.
|
|
45
45
|
|
|
46
|
+
### GitHub Actions loops: check the clock itself
|
|
47
|
+
|
|
48
|
+
Loops declared with `scheduler: "github-actions"` in `.lisa.config.json` are **not** visible to any runtime-native listing. Inspect them separately, and report a **stopped clock** as a finding in its own right — not merely the absence of recent work.
|
|
49
|
+
|
|
50
|
+
Three distinct states, all of which look identical from the work side:
|
|
51
|
+
|
|
52
|
+
| Observation | State | Remediation |
|
|
53
|
+
| --- | --- | --- |
|
|
54
|
+
| Workflow file exists, no `schedule:` trigger | **registered-disabled** | Expected until the production path is proven once. Prove it with a manual dispatch, then set `enabled: true` and regenerate. |
|
|
55
|
+
| `schedule:` present, but no run in well over the declared cadence | **clock stopped** | GitHub **auto-disables scheduled workflows after 60 days of repository inactivity.** Re-enable it in the Actions UI, or push any commit. |
|
|
56
|
+
| Runs firing, none dispatching | **idle** | Healthy. The queue is empty. |
|
|
57
|
+
|
|
58
|
+
The middle row is the one worth building for. A quiet repository silently stops running its loops and *nothing announces it*, so an unattended factory can be dead for weeks while every individual signal still looks fine. Do not infer health from "no failures"; a clock that never fires produces no failures at all.
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
gh run list --workflow "lisa-<loop>.yml" --limit 5 \
|
|
62
|
+
--json createdAt,conclusion,event
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Compare the newest `schedule`-event run against the declared cron. Absent or far older than the cadence means the clock is the problem, and no amount of inspecting the queue will reveal it.
|
|
66
|
+
|
|
67
|
+
Also note that GitHub's scheduled triggers are **best-effort**: they are delayed under load, sometimes considerably. Judge against a generous multiple of the cadence rather than flagging a single late tick.
|
|
68
|
+
|
|
46
69
|
## What to report
|
|
47
70
|
|
|
48
71
|
For each expected automation, report:
|
|
@@ -419,10 +419,43 @@ The verdict ladder is:
|
|
|
419
419
|
- `READY_WITH_WARNINGS` — no `FAIL`, but one or more `WARN`.
|
|
420
420
|
- `NOT_READY` — one or more `FAIL`.
|
|
421
421
|
|
|
422
|
+
## Secrets configuration
|
|
423
|
+
|
|
424
|
+
Run the secrets health checks through the skill that owns the contract, rather than reimplementing
|
|
425
|
+
any part of it here:
|
|
426
|
+
|
|
427
|
+
```sh
|
|
428
|
+
node .claude/skills/lisa-secrets-access/scripts/validate-config.mjs # shape
|
|
429
|
+
node .claude/skills/lisa-secrets-access/scripts/doctor-secrets.mjs # health
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
Run the validator first. It checks only the *shape* of the `secrets`, `remoteEnv`, and
|
|
433
|
+
`automations` blocks — whether a declaration could ever be correct — and catches the failures that
|
|
434
|
+
would otherwise surface somewhere unhelpful: a container failing mid-setup, a scheduled loop that
|
|
435
|
+
never fires, a dispatch naming a surface nobody provisioned. The health check then asks whether the
|
|
436
|
+
credentials actually resolve.
|
|
437
|
+
|
|
438
|
+
It reports without ever printing a value, and compares two copies of the same credential by digest.
|
|
439
|
+
Map its findings into the doctor's own verdicts: `error` → `FAIL`, `warn` → `WARN`.
|
|
440
|
+
|
|
441
|
+
**A project with no credentials manager is `WARN`, never `FAIL`.** The `env` provider is a supported
|
|
442
|
+
state — the environment *is* the provider. A manager is the preferred path because it gives one
|
|
443
|
+
store, rotation, and an audit trail; it is not a requirement, and doctor must not act as though it
|
|
444
|
+
were.
|
|
445
|
+
|
|
446
|
+
The check worth understanding before triaging its output is the two-store one. A value present in
|
|
447
|
+
both the provider and a local copy is **not a duplicate** — it is two live credentials, one of them
|
|
448
|
+
untracked. Both authenticate, so the difference is invisible from either side, and "tidying up the
|
|
449
|
+
duplicate" deletes a working credential that no record accounts for. Treat a mismatch as
|
|
450
|
+
stop-and-ask, never as something to adjudicate automatically.
|
|
451
|
+
|
|
422
452
|
## Delegation and reuse
|
|
423
453
|
|
|
424
454
|
- Reuse `config-resolution` for config and lifecycle role defaults instead of inventing a second
|
|
425
455
|
schema.
|
|
456
|
+
- Reuse `lisa-secrets-access` for every secrets check. Doctor must never read a keychain, parse a
|
|
457
|
+
`.env`, or invoke a provider CLI itself — one chokepoint is what makes the single-store rule
|
|
458
|
+
enforceable, and a diagnostic that bypasses it is exactly the second reader that creates drift.
|
|
426
459
|
- Reuse the existing `github-project-v2` chokepoint for GitHub Project coordination checks instead
|
|
427
460
|
of inlining bespoke access logic.
|
|
428
461
|
- Reuse ideas from `lisa-wiki-doctor` for grouped verdict rendering where they fit, while keeping
|
|
@@ -5,6 +5,23 @@ description: This skill should be used for any non-trivial request — features,
|
|
|
5
5
|
|
|
6
6
|
# Implement: $ARGUMENTS
|
|
7
7
|
|
|
8
|
+
## Routing: `executionEnv` (check this first)
|
|
9
|
+
|
|
10
|
+
Before anything else — before team orchestration, before reading the input — check `$ARGUMENTS` for an `executionEnv=` parameter.
|
|
11
|
+
|
|
12
|
+
| Value | What you do |
|
|
13
|
+
| --- | --- |
|
|
14
|
+
| absent, or `executionEnv=local` | Nothing changes. Continue to team orchestration below. |
|
|
15
|
+
| `executionEnv=<surface>` | Invoke `lisa-remote-dispatch` with `$ARGUMENTS`, report what it returns, and **stop**. |
|
|
16
|
+
|
|
17
|
+
This check comes first because dispatching is the whole job in that case: the remote runs this identical skill from this identical repository, so forming a team locally would duplicate the work you are about to send away.
|
|
18
|
+
|
|
19
|
+
**Routing only.** `executionEnv` changes *where* the work happens and nothing about *what* happens. Do not vary the lifecycle, the gates, the review obligations, or the evidence requirements based on it. If a behaviour genuinely must differ, encode it here as an explicit branch — never as an unstated assumption in the dispatcher.
|
|
20
|
+
|
|
21
|
+
`lisa-remote-dispatch` rejects an unknown surface rather than falling back to local. Do not catch that and continue: a silently ignored `executionEnv` runs work locally while the operator believes it went remote, and nothing downstream contradicts that belief.
|
|
22
|
+
|
|
23
|
+
Dispatch is fire-and-record. It returns a task identifier and exits without polling; that identifier and the recorded ledger entry are the deliverable. Do not wait for the remote task, and do not report the work as complete — report it as dispatched.
|
|
24
|
+
|
|
8
25
|
## Orchestration: agent team
|
|
9
26
|
|
|
10
27
|
Implement is a **team-first** flow. Bug, Build, Improve, and Investigate-Only all compose multiple specialists (Reproduce → debug → fix → review → verify). Single-agent mode is not permitted based on task complexity — the only exception is when no team creation or subagent delegation tool is available in the current runtime (see no-team fallback in the paragraph below).
|