@codyswann/lisa 2.324.1 → 2.325.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  2. package/dist/core/upstream-evidence-manifest.js +27 -3
  3. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  4. package/package.json +1 -1
  5. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  6. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  7. package/plugins/lisa/.codex-plugin/skills/lisa-detect-tooling/SKILL.md +60 -0
  8. package/plugins/lisa/.codex-plugin/skills/lisa-detect-tooling/agents/openai.yaml +4 -0
  9. package/plugins/lisa/.codex-plugin/skills/lisa-detect-tooling/scripts/detect-tooling.mjs +311 -0
  10. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/SKILL.md +18 -2
  11. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +74 -3
  12. package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/toolchain.mjs +50 -3
  13. package/plugins/lisa/commands/detect-tooling.md +7 -0
  14. package/plugins/lisa/skills/lisa-detect-tooling/SKILL.md +60 -0
  15. package/plugins/lisa/skills/lisa-detect-tooling/agents/openai.yaml +4 -0
  16. package/plugins/lisa/skills/lisa-detect-tooling/scripts/detect-tooling.mjs +311 -0
  17. package/plugins/lisa/skills/lisa-setup-remote-env/SKILL.md +18 -2
  18. package/plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +74 -3
  19. package/plugins/lisa/skills/lisa-setup-remote-env/scripts/toolchain.mjs +50 -3
  20. package/plugins/lisa-agy/commands/lisa/detect-tooling.md +7 -0
  21. package/plugins/lisa-agy/plugin.json +1 -1
  22. package/plugins/lisa-agy/skills/lisa-detect-tooling/SKILL.md +60 -0
  23. package/plugins/lisa-agy/skills/lisa-detect-tooling/scripts/detect-tooling.mjs +311 -0
  24. package/plugins/lisa-agy/skills/lisa-setup-remote-env/SKILL.md +18 -2
  25. package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +74 -3
  26. package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/toolchain.mjs +50 -3
  27. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  29. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  30. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  31. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-copilot/commands/lisa/detect-tooling.md +7 -0
  34. package/plugins/lisa-copilot/skills/lisa-detect-tooling/SKILL.md +60 -0
  35. package/plugins/lisa-copilot/skills/lisa-detect-tooling/scripts/detect-tooling.mjs +311 -0
  36. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/SKILL.md +18 -2
  37. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +74 -3
  38. package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/toolchain.mjs +50 -3
  39. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  40. package/plugins/lisa-cursor/commands/lisa/detect-tooling.md +7 -0
  41. package/plugins/lisa-cursor/skills/lisa-detect-tooling/SKILL.md +60 -0
  42. package/plugins/lisa-cursor/skills/lisa-detect-tooling/scripts/detect-tooling.mjs +311 -0
  43. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/SKILL.md +18 -2
  44. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +74 -3
  45. package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/toolchain.mjs +50 -3
  46. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  48. package/plugins/lisa-expo-agy/plugin.json +1 -1
  49. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  53. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  54. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  55. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  56. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  58. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  59. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  60. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  61. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  63. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  64. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  65. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  67. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  68. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  69. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  70. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  71. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  72. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  73. package/plugins/lisa-rails-agy/plugin.json +1 -1
  74. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  75. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  76. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  77. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  78. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  79. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  80. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  81. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  82. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  83. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  84. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  85. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  86. package/plugins/src/base/commands/detect-tooling.md +7 -0
  87. package/plugins/src/base/skills/lisa-detect-tooling/SKILL.md +60 -0
  88. package/plugins/src/base/skills/lisa-detect-tooling/scripts/detect-tooling.mjs +311 -0
  89. package/plugins/src/base/skills/lisa-setup-remote-env/SKILL.md +18 -2
  90. package/plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +74 -3
  91. package/plugins/src/base/skills/lisa-setup-remote-env/scripts/toolchain.mjs +50 -3
@@ -221,8 +221,9 @@ function installTool(tool, binDir) {
221
221
  * @param {boolean} dryRun Whether to plan only.
222
222
  * @returns {Array<object>} The plan that was applied.
223
223
  */
224
- function applyToolchain(tools, dryRun) {
225
- const plan = planToolchain(tools, probe);
224
+ function applyToolchain(tools, dryRun, options = {}) {
225
+ const { surface = "remote", consent = true } = options;
226
+ const plan = planToolchain(tools, probe, surface);
226
227
  const blocked = plan.filter(
227
228
  p => p.action === "missing" || p.action === "invalid"
228
229
  );
@@ -234,6 +235,34 @@ function applyToolchain(tools, dryRun) {
234
235
  mkdirSync(binDir, { recursive: true, mode: 0o755 });
235
236
  const byName = new Map((tools.install ?? []).map(t => [t.name, t]));
236
237
 
238
+ // Installing is a different act on a laptop than in a container. A container
239
+ // is disposable and provisioning it silently is the whole point; a developer
240
+ // machine belongs to a person, and putting pinned binaries in their
241
+ // ~/.local/bin without being asked is not ours to decide. So the same
242
+ // manifest drives both, and only the consent differs.
243
+ //
244
+ // Reporting still happens either way — knowing the machine diverges from what
245
+ // the project declares is most of the value, and is what nothing did before.
246
+ if (!consent) {
247
+ const missing = plan.filter(step => step.action === "install");
248
+ if (missing.length === 0) {
249
+ console.log(" every declared tool is already present");
250
+ return plan;
251
+ }
252
+ console.log(
253
+ ` ${missing.length} declared tool(s) are missing or below their pin:`
254
+ );
255
+ for (const step of missing) {
256
+ const tool = byName.get(step.name);
257
+ console.log(` ${step.name}${tool?.version ? ` ${tool.version}` : ""}`);
258
+ }
259
+ console.log(
260
+ "\n Not installing without confirmation. Re-run with --install-tools\n" +
261
+ " to provision them, or install them yourself."
262
+ );
263
+ return plan;
264
+ }
265
+
237
266
  // Installing a binary somewhere nothing looks is the same as not installing
238
267
  // it. `~/.local/bin` is on PATH by default on a developer workstation and is
239
268
  // NOT on a minimal container, so the toolchain step reported `install bws`,
@@ -526,6 +555,40 @@ export function emitClaudeWeb({ bootstrapKey }) {
526
555
  ].join("\n");
527
556
  }
528
557
 
558
+ /**
559
+ * Report tooling the project appears to need but has not declared.
560
+ *
561
+ * Called before the toolchain is applied, because the manifest is the only
562
+ * thing that puts a binary on PATH and nothing populates it — so provisioning
563
+ * "the declared toolchain" happily succeeds while the tool a project actually
564
+ * uses is absent, and fails later at the moment of use instead of here.
565
+ *
566
+ * Informational, never blocking. The detector proposes and a human decides, so
567
+ * an undeclared tool must not stop a setup that was asked to provision what IS
568
+ * declared; failing here would make a advisory signal into a gate nobody chose.
569
+ * Its own absence is silent for the same reason: an older installed copy of the
570
+ * skills has no detector, and that is not a reason to refuse to provision.
571
+ * @param {Function} [exec] Seam for tests.
572
+ */
573
+ function reportUndeclaredTooling(exec = execFileSync) {
574
+ let script;
575
+ try {
576
+ script = siblingScript("lisa-detect-tooling", "detect-tooling.mjs");
577
+ } catch {
578
+ return;
579
+ }
580
+ try {
581
+ const out = exec("node", [script], {
582
+ encoding: "utf8",
583
+ stdio: ["ignore", "pipe", "pipe"],
584
+ });
585
+ const text = String(out).trim();
586
+ if (text) console.log(`\n${text}\n`);
587
+ } catch {
588
+ // A detector that cannot run is a missing hint, not a failed setup.
589
+ }
590
+ }
591
+
529
592
  /**
530
593
  * Decide which phases this invocation runs.
531
594
  *
@@ -640,8 +703,16 @@ async function main() {
640
703
  }
641
704
 
642
705
  if (phases.includes("toolchain")) {
706
+ reportUndeclaredTooling();
643
707
  console.log("Toolchain:");
644
- applyToolchain(cfg.tools, dryRun);
708
+ // A remote container consents by construction: it is disposable, nobody is
709
+ // watching, and provisioning it is why the script exists. Locally the
710
+ // operator opts in per run.
711
+ const remote = Boolean(materializeAt);
712
+ applyToolchain(cfg.tools, dryRun, {
713
+ surface: remote ? "remote" : "local",
714
+ consent: remote || process.argv.includes("--install-tools"),
715
+ });
645
716
  }
646
717
 
647
718
  if (phases.includes("secrets")) {
@@ -124,18 +124,65 @@ function planInstallable(tool, found) {
124
124
  };
125
125
  }
126
126
 
127
+ /** Surfaces a manifest entry may name. */
128
+ const KNOWN_SURFACES = new Set(["local", "remote"]);
129
+
130
+ /**
131
+ * Whether a manifest entry applies to the surface being provisioned.
132
+ *
133
+ * One declaration per tool, not one block per surface. Most tools a project
134
+ * needs are needed *everywhere* — a Maestro or Sonar CLI is as required on a
135
+ * laptop as in a container — and duplicated blocks drift, which this repository
136
+ * has paid for more than once. What actually differs between surfaces is the
137
+ * install method and, more importantly, consent: a disposable container may
138
+ * install silently, a developer's machine may not.
139
+ *
140
+ * Omitting `surfaces` means every surface, because that is true of most tools
141
+ * and the failure of forgetting it should be "checked somewhere unnecessary"
142
+ * rather than "silently absent where it was needed".
143
+ * @param {object} tool Manifest entry.
144
+ * @param {string} surface Surface being provisioned.
145
+ * @returns {boolean} Whether this entry applies.
146
+ */
147
+ export function appliesToSurface(tool, surface) {
148
+ const surfaces = tool.surfaces;
149
+ if (surfaces === undefined) return true;
150
+ // A typo must not silently widen scope. Treating any non-array as "omitted"
151
+ // meant `surfaces: "remote"` — an easy thing to write — quietly applied the
152
+ // entry to every surface, which for a platform-specific archive means
153
+ // offering a Linux binary to a laptop. Absent means everywhere; malformed
154
+ // means stop.
155
+ if (!Array.isArray(surfaces)) {
156
+ throw new Error(
157
+ `${tool.name}: surfaces must be an array, got ${typeof surfaces}.\n` +
158
+ `Omit it to mean every surface; write ["remote"] or ["local"] to narrow.`
159
+ );
160
+ }
161
+ if (surfaces.length === 0) return true;
162
+ const unknown = surfaces.filter(entry => !KNOWN_SURFACES.has(entry));
163
+ if (unknown.length > 0) {
164
+ throw new Error(
165
+ `${tool.name}: unknown surface(s) ${unknown.join(", ")}.\n` +
166
+ `Known: ${[...KNOWN_SURFACES].join(", ")}.`
167
+ );
168
+ }
169
+ return surfaces.includes(surface);
170
+ }
171
+
127
172
  /**
128
173
  * Produce the complete plan for a toolchain manifest.
129
174
  * @param {{require?: object[], install?: object[]}} tools Manifest.
130
175
  * @param {(name: string) => {version: string|null, present: boolean}} probe Version probe.
131
176
  * @returns {Array<{name: string, action: string, reason: string}>} Ordered decisions.
132
177
  */
133
- export function planToolchain(tools, probe) {
178
+ export function planToolchain(tools, probe, surface = "remote") {
134
179
  const plan = [];
135
180
  for (const tool of tools.require ?? [])
136
- plan.push(planRequired(tool, probe(tool.name)));
181
+ if (appliesToSurface(tool, surface))
182
+ plan.push(planRequired(tool, probe(tool.name)));
137
183
  for (const tool of tools.install ?? [])
138
- plan.push(planInstallable(tool, probe(tool.name)));
184
+ if (appliesToSurface(tool, surface))
185
+ plan.push(planInstallable(tool, probe(tool.name)));
139
186
  return plan;
140
187
  }
141
188