@codyswann/lisa 2.324.0 → 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.
- package/all/copy-overwrite/scripts/lisa-enforcement-fallback.sh +71 -0
- package/all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh +102 -0
- package/all/copy-overwrite/scripts/lisa-hooks/block-shell-json-parsing.sh +220 -0
- package/all/copy-overwrite/scripts/lisa-hooks/parity-safety-net.sh +562 -0
- package/all/merge/.claude/settings.json +19 -4
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +42 -9
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/dist/standards/git-state.d.ts.map +1 -1
- package/dist/standards/git-state.js +10 -1
- package/dist/standards/git-state.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-detect-tooling/SKILL.md +60 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-detect-tooling/agents/openai.yaml +4 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-detect-tooling/scripts/detect-tooling.mjs +311 -0
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/SKILL.md +18 -2
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +74 -3
- package/plugins/lisa/.codex-plugin/skills/lisa-setup-remote-env/scripts/toolchain.mjs +50 -3
- package/plugins/lisa/commands/detect-tooling.md +7 -0
- package/plugins/lisa/rules/eager/tracked-work.md +1 -1
- package/plugins/lisa/rules/reference/tracked-work.md +1 -1
- package/plugins/lisa/skills/lisa-detect-tooling/SKILL.md +60 -0
- package/plugins/lisa/skills/lisa-detect-tooling/agents/openai.yaml +4 -0
- package/plugins/lisa/skills/lisa-detect-tooling/scripts/detect-tooling.mjs +311 -0
- package/plugins/lisa/skills/lisa-setup-remote-env/SKILL.md +18 -2
- package/plugins/lisa/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +74 -3
- package/plugins/lisa/skills/lisa-setup-remote-env/scripts/toolchain.mjs +50 -3
- package/plugins/lisa-agy/commands/lisa/detect-tooling.md +7 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-agy/skills/lisa-detect-tooling/SKILL.md +60 -0
- package/plugins/lisa-agy/skills/lisa-detect-tooling/scripts/detect-tooling.mjs +311 -0
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/SKILL.md +18 -2
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +74 -3
- package/plugins/lisa-agy/skills/lisa-setup-remote-env/scripts/toolchain.mjs +50 -3
- 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/detect-tooling.md +7 -0
- package/plugins/lisa-copilot/rules/eager/tracked-work.md +1 -1
- package/plugins/lisa-copilot/rules/reference/tracked-work.md +1 -1
- package/plugins/lisa-copilot/skills/lisa-detect-tooling/SKILL.md +60 -0
- package/plugins/lisa-copilot/skills/lisa-detect-tooling/scripts/detect-tooling.mjs +311 -0
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/SKILL.md +18 -2
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +74 -3
- package/plugins/lisa-copilot/skills/lisa-setup-remote-env/scripts/toolchain.mjs +50 -3
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/commands/lisa/detect-tooling.md +7 -0
- package/plugins/lisa-cursor/rules/tracked-work-reference.mdc +1 -1
- package/plugins/lisa-cursor/rules/tracked-work.mdc +1 -1
- package/plugins/lisa-cursor/skills/lisa-detect-tooling/SKILL.md +60 -0
- package/plugins/lisa-cursor/skills/lisa-detect-tooling/scripts/detect-tooling.mjs +311 -0
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/SKILL.md +18 -2
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +74 -3
- package/plugins/lisa-cursor/skills/lisa-setup-remote-env/scripts/toolchain.mjs +50 -3
- 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/detect-tooling.md +7 -0
- package/plugins/src/base/rules/eager/tracked-work.md +1 -1
- package/plugins/src/base/rules/reference/tracked-work.md +1 -1
- package/plugins/src/base/skills/lisa-detect-tooling/SKILL.md +60 -0
- package/plugins/src/base/skills/lisa-detect-tooling/scripts/detect-tooling.mjs +311 -0
- package/plugins/src/base/skills/lisa-setup-remote-env/SKILL.md +18 -2
- package/plugins/src/base/skills/lisa-setup-remote-env/scripts/setup-remote-env.mjs +74 -3
- package/plugins/src/base/skills/lisa-setup-remote-env/scripts/toolchain.mjs +50 -3
- package/scripts/build-plugins.sh +32 -0
- package/scripts/generate-upstream-evidence-manifest.mjs +50 -11
- package/scripts/lisa-enforcement-fallback.sh +16 -2
|
@@ -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
|
-
|
|
181
|
+
if (appliesToSurface(tool, surface))
|
|
182
|
+
plan.push(planRequired(tool, probe(tool.name)));
|
|
137
183
|
for (const tool of tools.install ?? [])
|
|
138
|
-
|
|
184
|
+
if (appliesToSurface(tool, surface))
|
|
185
|
+
plan.push(planInstallable(tool, probe(tool.name)));
|
|
139
186
|
return plan;
|
|
140
187
|
}
|
|
141
188
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.325.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.325.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.325.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.325.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.325.0",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: "Find the command-line tools this project needs but never declares, and propose pinned manifest entries for them. Reads npm scripts, MCP servers, credential usage notes and quality configuration, subtracts what remoteEnv.tools already covers, and prints proposals with evidence. Writes nothing and installs nothing — a tool reaches a machine only once a human has reviewed a pinned, checksummed entry."
|
|
3
|
+
allowed-tools: ["Skill"]
|
|
4
|
+
argument-hint: "[--json]"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Use the /lisa-detect-tooling skill to find undeclared command-line tooling and propose manifest entries for it. $ARGUMENTS
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
Before the first durable project mutation (code, tests, config, docs, committed research/plans/findings, commits, or PRs), establish exactly one live tracker leaf through `lisa-track`. Read-only discussion and orientation are exempt only while they produce no durable artifact.
|
|
4
4
|
|
|
5
|
-
The mandatory order is: live-validate an explicit ref, or conservatively search and create exactly one valid leaf through `lisa-tracker-write` when no unique match exists; idempotently claim it through `lisa-tracker-claim`; then persist and verify the worktree-local binding with `node scripts/lisa-work-item.mjs bind <ref>`.
|
|
5
|
+
The mandatory order is: live-validate an explicit ref, or conservatively search and create exactly one valid leaf through `lisa-tracker-write` when no unique match exists; idempotently claim it through `lisa-tracker-claim`; then persist and verify the worktree-local binding with `node scripts/lisa-work-item.mjs bind <ref>`. A tracker that answers no, and any claim or binding failure, blocks durable work; a tracker that cannot be reached does not block a commit — the offline checks still run, the skip is loud on stderr, and the required `Work-Item Traceability` check re-runs the live checks with credentials before anything merges.
|
|
6
6
|
|
|
7
7
|
Carry that canonical ref through the branch, every ordinary commit's `Work-Item:` trailer, the PR, usage/evidence, and `lisa-tracker-sync`. Hooks and CI never create tickets. Keep the binding through interruptions or blocked outcomes; run `node scripts/lisa-work-item.mjs clear` only after merge/deploy/verification, two-way linkage/evidence, and the tracker item have all reached true terminal completion.
|
|
@@ -11,7 +11,7 @@ Use `lisa-track` as the single entry point:
|
|
|
11
11
|
3. Idempotently claim the resolved leaf through `lisa-tracker-claim`, which reuses the vendor build-intake claim semantics and post-read verifies the claimed-or-later state.
|
|
12
12
|
4. Before any durable repository work, persist the canonical reference with `node scripts/lisa-work-item.mjs bind <ref>` and verify the worktree-local binding.
|
|
13
13
|
|
|
14
|
-
The sequence is strict: **live validate/create -> claim -> bind -> durable work**. A
|
|
14
|
+
The sequence is strict: **live validate/create -> claim -> bind -> durable work**. A tracker answer of no, and any claim or binding failure, blocks the work. A tracker that cannot be reached — `gh` absent, or its credential refused — does not block a commit: the offline checks still run, the skip is loud on stderr, and the required `Work-Item Traceability` check re-runs the live checks with credentials before anything merges. Any other tracker failure still blocks. Tool presence or stale session text is not access.
|
|
15
15
|
|
|
16
16
|
## One canonical identity
|
|
17
17
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: lisa-detect-tooling
|
|
3
|
+
description: "Find the command-line tools a project needs but never declares, and propose pinned manifest entries for them. Reads npm scripts, MCP servers, credential usage notes and quality configuration, subtracts whatever remoteEnv.tools already covers, and prints proposals with evidence. Writes nothing and installs nothing — a tool reaches a machine only when a human has reviewed a pinned, checksummed entry. Invoked by lisa-setup-remote-env before provisioning, and runnable on its own."
|
|
4
|
+
allowed-tools: ["Bash", "Read", "AskUserQuestion"]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Detect Tooling
|
|
8
|
+
|
|
9
|
+
Lisa provisions tooling through four unrelated mechanisms, and only one of them puts a binary on `PATH`:
|
|
10
|
+
|
|
11
|
+
| tool | how it arrives | what puts it on PATH |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| playwright, stryker | npm devDependency from a stack template | nothing — a local `node_modules` binary |
|
|
14
|
+
| maestro | npm **scripts** in the Expo template | **nothing** |
|
|
15
|
+
| sonar | `src/sonar/sonar-installer.ts` | nothing |
|
|
16
|
+
| linear | MCP server | nothing, and it needs browser OAuth |
|
|
17
|
+
| bws, gh | `remoteEnv.tools` — pinned and checksummed | this, and only this |
|
|
18
|
+
|
|
19
|
+
Nothing populates that last row. So a project can ship scripts that invoke `maestro`, wire an MCP server whose CLI it also shells out to, and configure Playwright thresholds, while the manifest that actually provisions binaries stays empty — and every one of those fails at the moment of use rather than at setup.
|
|
20
|
+
|
|
21
|
+
That is the same failure this repository has now paid for twice: `gh` was declared nowhere and a cloud session could not commit; `tar` was needed by an install method and asserted by nothing.
|
|
22
|
+
|
|
23
|
+
## What it does
|
|
24
|
+
|
|
25
|
+
Reads four signals, subtracts what `remoteEnv.tools` already declares, and prints what is left with the evidence for each:
|
|
26
|
+
|
|
27
|
+
- **npm scripts** that invoke a binary. The strongest signal there is — a script running `maestro test` is the project stating a dependency in executable form. Matched on the script *body*, not its name, because the name is a label.
|
|
28
|
+
- **MCP servers with a CLI equivalent.** An MCP server is not a substitute for the binary: several authenticate by browser OAuth, which a container cannot do, so a project relying on one remotely has *no* integration rather than a degraded one.
|
|
29
|
+
- **Credential usage notes.** A note explaining what a token is for usually names the program that consumes it. `lisa-secrets-access` already exposes these without touching a value, which makes them a first-class input rather than a trick.
|
|
30
|
+
- **Quality configuration**, where a threshold implies the tool that produces it.
|
|
31
|
+
|
|
32
|
+
## What it will not do
|
|
33
|
+
|
|
34
|
+
**It writes nothing and installs nothing.** Output is a proposal with `<pin>`, `<release url>` and `<sha256>` left for a human.
|
|
35
|
+
|
|
36
|
+
That boundary is the whole design. A tool should reach a machine because someone reviewed a pinned entry with a checksum, never because a detector was confident. Detection is evidence; the manifest is the decision. An auto-writing detector would quietly become a second, unreviewed install path — exactly what `assertPinned` exists to prevent.
|
|
37
|
+
|
|
38
|
+
## Surfaces
|
|
39
|
+
|
|
40
|
+
One declaration per tool, with an optional `surfaces` list, rather than separate blocks per surface. Most tools are needed *everywhere* — a Maestro or Sonar CLI is as required on a laptop as in a container — and duplicated blocks drift.
|
|
41
|
+
|
|
42
|
+
What genuinely differs is **consent**, not the list:
|
|
43
|
+
|
|
44
|
+
- A remote container is disposable and nobody is watching, so it provisions silently.
|
|
45
|
+
- A developer machine belongs to a person, so `--phase=toolchain` reports what is missing and installs nothing unless `--install-tools` is passed.
|
|
46
|
+
|
|
47
|
+
Omitting `surfaces` means every surface, because that is true of most tools and the cost of forgetting should be a redundant check rather than a silent absence.
|
|
48
|
+
|
|
49
|
+
A platform-specific pin is the exception worth knowing: a Linux release archive must be `surfaces: ["remote"]`, with a matching `require` entry for `local`, so a laptop asserts the tool without being offered a binary it cannot run.
|
|
50
|
+
|
|
51
|
+
## Usage
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
node scripts/detect-tooling.mjs # human-readable proposals
|
|
55
|
+
node scripts/detect-tooling.mjs --json # machine-readable
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Confirm each proposal with the operator, then hand it to them to apply. **This skill does not hold `Edit`**, deliberately: a skill that both decides a tool is needed and writes the entry that installs it is the second unreviewed install path this design exists to avoid. Granting `Edit` while documenting "writes nothing" would have made the prose the only thing stopping it.
|
|
59
|
+
|
|
60
|
+
When a proposal is wrong — the tool is genuinely unused, or arrives another way — say so and move on. A rejected proposal is a normal outcome, not a failure.
|