@codyswann/lisa 2.322.4 → 2.322.5

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 (64) hide show
  1. package/dist/core/upstream-evidence-manifest.js +2 -2
  2. package/package.json +1 -1
  3. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  4. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  5. package/plugins/lisa/.codex-plugin/skills/lisa-remote-dispatch/SKILL.md +3 -0
  6. package/plugins/lisa/.codex-plugin/skills/lisa-remote-dispatch/scripts/dispatch.mjs +27 -3
  7. package/plugins/lisa/skills/lisa-remote-dispatch/SKILL.md +4 -1
  8. package/plugins/lisa/skills/lisa-remote-dispatch/scripts/dispatch.mjs +27 -3
  9. package/plugins/lisa-agy/plugin.json +1 -1
  10. package/plugins/lisa-agy/skills/lisa-remote-dispatch/SKILL.md +4 -1
  11. package/plugins/lisa-agy/skills/lisa-remote-dispatch/scripts/dispatch.mjs +27 -3
  12. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  13. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  14. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  15. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  16. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  17. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  18. package/plugins/lisa-copilot/skills/lisa-remote-dispatch/SKILL.md +4 -1
  19. package/plugins/lisa-copilot/skills/lisa-remote-dispatch/scripts/dispatch.mjs +27 -3
  20. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  21. package/plugins/lisa-cursor/skills/lisa-remote-dispatch/SKILL.md +4 -1
  22. package/plugins/lisa-cursor/skills/lisa-remote-dispatch/scripts/dispatch.mjs +27 -3
  23. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  24. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  25. package/plugins/lisa-expo-agy/plugin.json +1 -1
  26. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  27. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  28. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  29. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  30. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  31. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  32. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  33. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  34. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  35. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  36. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  37. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  38. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  39. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  40. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  41. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  42. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  43. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  44. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  45. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  46. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  47. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  48. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  50. package/plugins/lisa-rails-agy/plugin.json +1 -1
  51. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  52. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  54. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  55. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  56. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  57. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  58. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  59. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  60. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  61. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  62. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  63. package/plugins/src/base/skills/lisa-remote-dispatch/SKILL.md +4 -1
  64. package/plugins/src/base/skills/lisa-remote-dispatch/scripts/dispatch.mjs +27 -3
@@ -526,8 +526,8 @@ export const UPSTREAM_EVIDENCE_MANIFEST = Object.freeze({
526
526
  "plugins/src/base/skills/lisa-qa-queue/SKILL.md": "1d5f009f60004bf75b5d79bca370096ce40e1d41d585dc8afd4389f21bba84ae",
527
527
  "plugins/src/base/skills/lisa-quality-review/SKILL.md": "17ba45f4c4b7877c0b0c9a3c1cc1cb9e18c642ead74ff7263698d8034d3241da",
528
528
  "plugins/src/base/skills/lisa-queue-status/SKILL.md": "87c6d34d0511d4afd812d1af3fee80fa3b7aa47db8449760b12f2699fedc1d78",
529
- "plugins/src/base/skills/lisa-remote-dispatch/SKILL.md": "2cc999d2c3b61b10aabb849e0833fd58edb444c5013c05108dd58e5d7d60c4ba",
530
- "plugins/src/base/skills/lisa-remote-dispatch/scripts/dispatch.mjs": "c473e8eddf0fd5f220a5ea7ff9eb92f5482cfb2893e77edce59acc36671418a3",
529
+ "plugins/src/base/skills/lisa-remote-dispatch/SKILL.md": "f3c48120a206d01db45f849ca5b61690d572abc16bc36d559a4cacc8f9422c06",
530
+ "plugins/src/base/skills/lisa-remote-dispatch/scripts/dispatch.mjs": "475963ed64d9e72a6cbd6cbb61c8b57a419f2e1805e3ddfa76066047c590deeb",
531
531
  "plugins/src/base/skills/lisa-repair-intake/SKILL.md": "4f85ac1381631e9f250d9cf3239baba633ae0df9ba1959f8835ed662592e2d77",
532
532
  "plugins/src/base/skills/lisa-reproduce-bug/SKILL.md": "4d460993fac6021219ca23eee29b1b9afa7c37479dddc662b2fcfaca510edd68",
533
533
  "plugins/src/base/skills/lisa-research/SKILL.md": "19de7c5910b117c4b3b1bb7ae42cf9c8b6ff2a376b2e21961526bc7ebde18e85",
package/package.json CHANGED
@@ -115,7 +115,7 @@
115
115
  "brace-expansion": ">=5.0.9"
116
116
  },
117
117
  "name": "@codyswann/lisa",
118
- "version": "2.322.4",
118
+ "version": "2.322.5",
119
119
  "description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
120
120
  "main": "dist/index.js",
121
121
  "exports": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Universal governance: agents, skills, commands, hooks, and rules for all projects.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -16,9 +16,12 @@ It changes **where** work happens and nothing about **what** happens. The remote
16
16
  | --- | --- |
17
17
  | omitted / `local` | The calling skill proceeds normally. Nothing is dispatched. |
18
18
  | `codex-cloud` | Submit to the project's Codex Cloud environment and return. |
19
+ | `claude-web` | Fire the project's Claude routine, which starts a cloud session, and return. |
19
20
 
20
21
  Any other value is **rejected explicitly**. A silently ignored `executionEnv` would run the work locally while the operator believes it went remote, and nothing downstream would contradict that belief.
21
22
 
23
+ For the same reason, the parameter is accepted as both `executionEnv=…` and `--executionEnv=…`, and a *dashed* parameter this skill does not recognise is an error rather than payload. The bare form is the documented one, but the dashed form is what anyone who has used a CLI will type, and a misspelling of either is indistinguishable from not asking at all — which is the one failure an operator cannot act on. A bare `key=value` still belongs to the payload, since prose may legitimately contain an equals sign.
24
+
22
25
  If a behaviour must differ between local and remote, it belongs in the calling skill as an explicit branch — never here. The moment this file starts encoding domain behaviour, there are two implementations to keep in sync.
23
26
 
24
27
  ## The invocation stays thin
@@ -68,11 +68,25 @@ const LEDGER = join(".lisa", "remote-dispatch.json");
68
68
  /** This file's directory, for locating the sibling secrets skill. */
69
69
  const HERE = dirname(fileURLToPath(import.meta.url));
70
70
 
71
+ /** Every parameter this program reads. Anything else is a typo. */
72
+ const KNOWN_PARAMS = new Set(["executionEnv"]);
73
+
71
74
  /**
72
75
  * Split `key=value` parameters from the rest of an invocation.
73
76
  *
74
77
  * The remainder is passed through untouched. It is the caller's payload — a
75
78
  * ticket key, a description — and this program has no business interpreting it.
79
+ *
80
+ * A leading `--` is accepted and stripped. The bare form is the documented one,
81
+ * but `--executionEnv=claude-web` is what anyone who has used a CLI this decade
82
+ * types, and it previously fell through to the payload and left the surface at
83
+ * its default. That is precisely the outcome this skill says it exists to
84
+ * prevent: work runs locally while the operator believes it went remote, and
85
+ * nothing downstream contradicts them.
86
+ *
87
+ * An unrecognised parameter is rejected for the same reason. A misspelled
88
+ * `executionEnvv=claude-web` is indistinguishable from not asking at all, and
89
+ * silence is the one response that cannot be acted on.
76
90
  * @param {string} input Raw argument string.
77
91
  * @returns {{params: Record<string, string>, rest: string}} Parsed invocation.
78
92
  */
@@ -83,9 +97,19 @@ export function parseInvocation(input) {
83
97
  .trim()
84
98
  .split(/\s+/)
85
99
  .filter(Boolean)) {
86
- const match = /^([A-Za-z][A-Za-z0-9_]*)=(.*)$/.exec(token);
87
- if (match) params[match[1]] = match[2];
88
- else rest.push(token);
100
+ const match = /^(--)?([A-Za-z][A-Za-z0-9_]*)=(.*)$/.exec(token);
101
+ // Only a dashed token is required to be a known parameter. A bare `x=y` may
102
+ // legitimately be part of a payload — a description, a query — and this
103
+ // program has no business rejecting the caller's prose.
104
+ if (match && (match[1] || KNOWN_PARAMS.has(match[2]))) {
105
+ if (!KNOWN_PARAMS.has(match[2])) {
106
+ throw new Error(
107
+ `unknown parameter "${match[2]}".\n` +
108
+ `Supported: ${[...KNOWN_PARAMS].join(", ")}.`
109
+ );
110
+ }
111
+ params[match[2]] = match[3];
112
+ } else rest.push(token);
89
113
  }
90
114
  return { params, rest: rest.join(" ") };
91
115
  }
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: lisa-remote-dispatch
3
- description: "Route one unit of work to a remote execution surface. Reads the executionEnv parameter (local by default, codex-cloud today), verifies the environment is provisioned and bound to this repository, submits a thin skill invocation, records the task identifier to .lisa/remote-dispatch.json, and exits without polling. Routing only — the remote runs the identical skill from the identical repository. Composable and inline: other skills invoke it via the Skill tool rather than users calling it directly."
3
+ description: "Route one unit of work to a remote execution surface. Reads the executionEnv parameter (local by default, codex-cloud or claude-web today), verifies the environment is provisioned and bound to this repository, submits a thin skill invocation, records the task identifier to .lisa/remote-dispatch.json, and exits without polling. Routing only — the remote runs the identical skill from the identical repository. Composable and inline: other skills invoke it via the Skill tool rather than users calling it directly."
4
4
  allowed-tools: ["Bash", "Read", "Skill"]
5
5
  ---
6
6
 
@@ -16,9 +16,12 @@ It changes **where** work happens and nothing about **what** happens. The remote
16
16
  | --- | --- |
17
17
  | omitted / `local` | The calling skill proceeds normally. Nothing is dispatched. |
18
18
  | `codex-cloud` | Submit to the project's Codex Cloud environment and return. |
19
+ | `claude-web` | Fire the project's Claude routine, which starts a cloud session, and return. |
19
20
 
20
21
  Any other value is **rejected explicitly**. A silently ignored `executionEnv` would run the work locally while the operator believes it went remote, and nothing downstream would contradict that belief.
21
22
 
23
+ For the same reason, the parameter is accepted as both `executionEnv=…` and `--executionEnv=…`, and a *dashed* parameter this skill does not recognise is an error rather than payload. The bare form is the documented one, but the dashed form is what anyone who has used a CLI will type, and a misspelling of either is indistinguishable from not asking at all — which is the one failure an operator cannot act on. A bare `key=value` still belongs to the payload, since prose may legitimately contain an equals sign.
24
+
22
25
  If a behaviour must differ between local and remote, it belongs in the calling skill as an explicit branch — never here. The moment this file starts encoding domain behaviour, there are two implementations to keep in sync.
23
26
 
24
27
  ## The invocation stays thin
@@ -68,11 +68,25 @@ const LEDGER = join(".lisa", "remote-dispatch.json");
68
68
  /** This file's directory, for locating the sibling secrets skill. */
69
69
  const HERE = dirname(fileURLToPath(import.meta.url));
70
70
 
71
+ /** Every parameter this program reads. Anything else is a typo. */
72
+ const KNOWN_PARAMS = new Set(["executionEnv"]);
73
+
71
74
  /**
72
75
  * Split `key=value` parameters from the rest of an invocation.
73
76
  *
74
77
  * The remainder is passed through untouched. It is the caller's payload — a
75
78
  * ticket key, a description — and this program has no business interpreting it.
79
+ *
80
+ * A leading `--` is accepted and stripped. The bare form is the documented one,
81
+ * but `--executionEnv=claude-web` is what anyone who has used a CLI this decade
82
+ * types, and it previously fell through to the payload and left the surface at
83
+ * its default. That is precisely the outcome this skill says it exists to
84
+ * prevent: work runs locally while the operator believes it went remote, and
85
+ * nothing downstream contradicts them.
86
+ *
87
+ * An unrecognised parameter is rejected for the same reason. A misspelled
88
+ * `executionEnvv=claude-web` is indistinguishable from not asking at all, and
89
+ * silence is the one response that cannot be acted on.
76
90
  * @param {string} input Raw argument string.
77
91
  * @returns {{params: Record<string, string>, rest: string}} Parsed invocation.
78
92
  */
@@ -83,9 +97,19 @@ export function parseInvocation(input) {
83
97
  .trim()
84
98
  .split(/\s+/)
85
99
  .filter(Boolean)) {
86
- const match = /^([A-Za-z][A-Za-z0-9_]*)=(.*)$/.exec(token);
87
- if (match) params[match[1]] = match[2];
88
- else rest.push(token);
100
+ const match = /^(--)?([A-Za-z][A-Za-z0-9_]*)=(.*)$/.exec(token);
101
+ // Only a dashed token is required to be a known parameter. A bare `x=y` may
102
+ // legitimately be part of a payload — a description, a query — and this
103
+ // program has no business rejecting the caller's prose.
104
+ if (match && (match[1] || KNOWN_PARAMS.has(match[2]))) {
105
+ if (!KNOWN_PARAMS.has(match[2])) {
106
+ throw new Error(
107
+ `unknown parameter "${match[2]}".\n` +
108
+ `Supported: ${[...KNOWN_PARAMS].join(", ")}.`
109
+ );
110
+ }
111
+ params[match[2]] = match[3];
112
+ } else rest.push(token);
89
113
  }
90
114
  return { params, rest: rest.join(" ") };
91
115
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: lisa-remote-dispatch
3
- description: "Route one unit of work to a remote execution surface. Reads the executionEnv parameter (local by default, codex-cloud today), verifies the environment is provisioned and bound to this repository, submits a thin skill invocation, records the task identifier to .lisa/remote-dispatch.json, and exits without polling. Routing only — the remote runs the identical skill from the identical repository. Composable and inline: other skills invoke it via the Skill tool rather than users calling it directly."
3
+ description: "Route one unit of work to a remote execution surface. Reads the executionEnv parameter (local by default, codex-cloud or claude-web today), verifies the environment is provisioned and bound to this repository, submits a thin skill invocation, records the task identifier to .lisa/remote-dispatch.json, and exits without polling. Routing only — the remote runs the identical skill from the identical repository. Composable and inline: other skills invoke it via the Skill tool rather than users calling it directly."
4
4
  allowed-tools: ["Bash", "Read", "Skill"]
5
5
  ---
6
6
 
@@ -16,9 +16,12 @@ It changes **where** work happens and nothing about **what** happens. The remote
16
16
  | --- | --- |
17
17
  | omitted / `local` | The calling skill proceeds normally. Nothing is dispatched. |
18
18
  | `codex-cloud` | Submit to the project's Codex Cloud environment and return. |
19
+ | `claude-web` | Fire the project's Claude routine, which starts a cloud session, and return. |
19
20
 
20
21
  Any other value is **rejected explicitly**. A silently ignored `executionEnv` would run the work locally while the operator believes it went remote, and nothing downstream would contradict that belief.
21
22
 
23
+ For the same reason, the parameter is accepted as both `executionEnv=…` and `--executionEnv=…`, and a *dashed* parameter this skill does not recognise is an error rather than payload. The bare form is the documented one, but the dashed form is what anyone who has used a CLI will type, and a misspelling of either is indistinguishable from not asking at all — which is the one failure an operator cannot act on. A bare `key=value` still belongs to the payload, since prose may legitimately contain an equals sign.
24
+
22
25
  If a behaviour must differ between local and remote, it belongs in the calling skill as an explicit branch — never here. The moment this file starts encoding domain behaviour, there are two implementations to keep in sync.
23
26
 
24
27
  ## The invocation stays thin
@@ -68,11 +68,25 @@ const LEDGER = join(".lisa", "remote-dispatch.json");
68
68
  /** This file's directory, for locating the sibling secrets skill. */
69
69
  const HERE = dirname(fileURLToPath(import.meta.url));
70
70
 
71
+ /** Every parameter this program reads. Anything else is a typo. */
72
+ const KNOWN_PARAMS = new Set(["executionEnv"]);
73
+
71
74
  /**
72
75
  * Split `key=value` parameters from the rest of an invocation.
73
76
  *
74
77
  * The remainder is passed through untouched. It is the caller's payload — a
75
78
  * ticket key, a description — and this program has no business interpreting it.
79
+ *
80
+ * A leading `--` is accepted and stripped. The bare form is the documented one,
81
+ * but `--executionEnv=claude-web` is what anyone who has used a CLI this decade
82
+ * types, and it previously fell through to the payload and left the surface at
83
+ * its default. That is precisely the outcome this skill says it exists to
84
+ * prevent: work runs locally while the operator believes it went remote, and
85
+ * nothing downstream contradicts them.
86
+ *
87
+ * An unrecognised parameter is rejected for the same reason. A misspelled
88
+ * `executionEnvv=claude-web` is indistinguishable from not asking at all, and
89
+ * silence is the one response that cannot be acted on.
76
90
  * @param {string} input Raw argument string.
77
91
  * @returns {{params: Record<string, string>, rest: string}} Parsed invocation.
78
92
  */
@@ -83,9 +97,19 @@ export function parseInvocation(input) {
83
97
  .trim()
84
98
  .split(/\s+/)
85
99
  .filter(Boolean)) {
86
- const match = /^([A-Za-z][A-Za-z0-9_]*)=(.*)$/.exec(token);
87
- if (match) params[match[1]] = match[2];
88
- else rest.push(token);
100
+ const match = /^(--)?([A-Za-z][A-Za-z0-9_]*)=(.*)$/.exec(token);
101
+ // Only a dashed token is required to be a known parameter. A bare `x=y` may
102
+ // legitimately be part of a payload — a description, a query — and this
103
+ // program has no business rejecting the caller's prose.
104
+ if (match && (match[1] || KNOWN_PARAMS.has(match[2]))) {
105
+ if (!KNOWN_PARAMS.has(match[2])) {
106
+ throw new Error(
107
+ `unknown parameter "${match[2]}".\n` +
108
+ `Supported: ${[...KNOWN_PARAMS].join(", ")}.`
109
+ );
110
+ }
111
+ params[match[2]] = match[3];
112
+ } else rest.push(token);
89
113
  }
90
114
  return { params, rest: rest.join(" ") };
91
115
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "AWS CDK-specific Lisa plugin.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: lisa-remote-dispatch
3
- description: "Route one unit of work to a remote execution surface. Reads the executionEnv parameter (local by default, codex-cloud today), verifies the environment is provisioned and bound to this repository, submits a thin skill invocation, records the task identifier to .lisa/remote-dispatch.json, and exits without polling. Routing only — the remote runs the identical skill from the identical repository. Composable and inline: other skills invoke it via the Skill tool rather than users calling it directly."
3
+ description: "Route one unit of work to a remote execution surface. Reads the executionEnv parameter (local by default, codex-cloud or claude-web today), verifies the environment is provisioned and bound to this repository, submits a thin skill invocation, records the task identifier to .lisa/remote-dispatch.json, and exits without polling. Routing only — the remote runs the identical skill from the identical repository. Composable and inline: other skills invoke it via the Skill tool rather than users calling it directly."
4
4
  allowed-tools: ["Bash", "Read", "Skill"]
5
5
  ---
6
6
 
@@ -16,9 +16,12 @@ It changes **where** work happens and nothing about **what** happens. The remote
16
16
  | --- | --- |
17
17
  | omitted / `local` | The calling skill proceeds normally. Nothing is dispatched. |
18
18
  | `codex-cloud` | Submit to the project's Codex Cloud environment and return. |
19
+ | `claude-web` | Fire the project's Claude routine, which starts a cloud session, and return. |
19
20
 
20
21
  Any other value is **rejected explicitly**. A silently ignored `executionEnv` would run the work locally while the operator believes it went remote, and nothing downstream would contradict that belief.
21
22
 
23
+ For the same reason, the parameter is accepted as both `executionEnv=…` and `--executionEnv=…`, and a *dashed* parameter this skill does not recognise is an error rather than payload. The bare form is the documented one, but the dashed form is what anyone who has used a CLI will type, and a misspelling of either is indistinguishable from not asking at all — which is the one failure an operator cannot act on. A bare `key=value` still belongs to the payload, since prose may legitimately contain an equals sign.
24
+
22
25
  If a behaviour must differ between local and remote, it belongs in the calling skill as an explicit branch — never here. The moment this file starts encoding domain behaviour, there are two implementations to keep in sync.
23
26
 
24
27
  ## The invocation stays thin
@@ -68,11 +68,25 @@ const LEDGER = join(".lisa", "remote-dispatch.json");
68
68
  /** This file's directory, for locating the sibling secrets skill. */
69
69
  const HERE = dirname(fileURLToPath(import.meta.url));
70
70
 
71
+ /** Every parameter this program reads. Anything else is a typo. */
72
+ const KNOWN_PARAMS = new Set(["executionEnv"]);
73
+
71
74
  /**
72
75
  * Split `key=value` parameters from the rest of an invocation.
73
76
  *
74
77
  * The remainder is passed through untouched. It is the caller's payload — a
75
78
  * ticket key, a description — and this program has no business interpreting it.
79
+ *
80
+ * A leading `--` is accepted and stripped. The bare form is the documented one,
81
+ * but `--executionEnv=claude-web` is what anyone who has used a CLI this decade
82
+ * types, and it previously fell through to the payload and left the surface at
83
+ * its default. That is precisely the outcome this skill says it exists to
84
+ * prevent: work runs locally while the operator believes it went remote, and
85
+ * nothing downstream contradicts them.
86
+ *
87
+ * An unrecognised parameter is rejected for the same reason. A misspelled
88
+ * `executionEnvv=claude-web` is indistinguishable from not asking at all, and
89
+ * silence is the one response that cannot be acted on.
76
90
  * @param {string} input Raw argument string.
77
91
  * @returns {{params: Record<string, string>, rest: string}} Parsed invocation.
78
92
  */
@@ -83,9 +97,19 @@ export function parseInvocation(input) {
83
97
  .trim()
84
98
  .split(/\s+/)
85
99
  .filter(Boolean)) {
86
- const match = /^([A-Za-z][A-Za-z0-9_]*)=(.*)$/.exec(token);
87
- if (match) params[match[1]] = match[2];
88
- else rest.push(token);
100
+ const match = /^(--)?([A-Za-z][A-Za-z0-9_]*)=(.*)$/.exec(token);
101
+ // Only a dashed token is required to be a known parameter. A bare `x=y` may
102
+ // legitimately be part of a payload — a description, a query — and this
103
+ // program has no business rejecting the caller's prose.
104
+ if (match && (match[1] || KNOWN_PARAMS.has(match[2]))) {
105
+ if (!KNOWN_PARAMS.has(match[2])) {
106
+ throw new Error(
107
+ `unknown parameter "${match[2]}".\n` +
108
+ `Supported: ${[...KNOWN_PARAMS].join(", ")}.`
109
+ );
110
+ }
111
+ params[match[2]] = match[3];
112
+ } else rest.push(token);
89
113
  }
90
114
  return { params, rest: rest.join(" ") };
91
115
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: lisa-remote-dispatch
3
- description: "Route one unit of work to a remote execution surface. Reads the executionEnv parameter (local by default, codex-cloud today), verifies the environment is provisioned and bound to this repository, submits a thin skill invocation, records the task identifier to .lisa/remote-dispatch.json, and exits without polling. Routing only — the remote runs the identical skill from the identical repository. Composable and inline: other skills invoke it via the Skill tool rather than users calling it directly."
3
+ description: "Route one unit of work to a remote execution surface. Reads the executionEnv parameter (local by default, codex-cloud or claude-web today), verifies the environment is provisioned and bound to this repository, submits a thin skill invocation, records the task identifier to .lisa/remote-dispatch.json, and exits without polling. Routing only — the remote runs the identical skill from the identical repository. Composable and inline: other skills invoke it via the Skill tool rather than users calling it directly."
4
4
  allowed-tools: ["Bash", "Read", "Skill"]
5
5
  ---
6
6
 
@@ -16,9 +16,12 @@ It changes **where** work happens and nothing about **what** happens. The remote
16
16
  | --- | --- |
17
17
  | omitted / `local` | The calling skill proceeds normally. Nothing is dispatched. |
18
18
  | `codex-cloud` | Submit to the project's Codex Cloud environment and return. |
19
+ | `claude-web` | Fire the project's Claude routine, which starts a cloud session, and return. |
19
20
 
20
21
  Any other value is **rejected explicitly**. A silently ignored `executionEnv` would run the work locally while the operator believes it went remote, and nothing downstream would contradict that belief.
21
22
 
23
+ For the same reason, the parameter is accepted as both `executionEnv=…` and `--executionEnv=…`, and a *dashed* parameter this skill does not recognise is an error rather than payload. The bare form is the documented one, but the dashed form is what anyone who has used a CLI will type, and a misspelling of either is indistinguishable from not asking at all — which is the one failure an operator cannot act on. A bare `key=value` still belongs to the payload, since prose may legitimately contain an equals sign.
24
+
22
25
  If a behaviour must differ between local and remote, it belongs in the calling skill as an explicit branch — never here. The moment this file starts encoding domain behaviour, there are two implementations to keep in sync.
23
26
 
24
27
  ## The invocation stays thin
@@ -68,11 +68,25 @@ const LEDGER = join(".lisa", "remote-dispatch.json");
68
68
  /** This file's directory, for locating the sibling secrets skill. */
69
69
  const HERE = dirname(fileURLToPath(import.meta.url));
70
70
 
71
+ /** Every parameter this program reads. Anything else is a typo. */
72
+ const KNOWN_PARAMS = new Set(["executionEnv"]);
73
+
71
74
  /**
72
75
  * Split `key=value` parameters from the rest of an invocation.
73
76
  *
74
77
  * The remainder is passed through untouched. It is the caller's payload — a
75
78
  * ticket key, a description — and this program has no business interpreting it.
79
+ *
80
+ * A leading `--` is accepted and stripped. The bare form is the documented one,
81
+ * but `--executionEnv=claude-web` is what anyone who has used a CLI this decade
82
+ * types, and it previously fell through to the payload and left the surface at
83
+ * its default. That is precisely the outcome this skill says it exists to
84
+ * prevent: work runs locally while the operator believes it went remote, and
85
+ * nothing downstream contradicts them.
86
+ *
87
+ * An unrecognised parameter is rejected for the same reason. A misspelled
88
+ * `executionEnvv=claude-web` is indistinguishable from not asking at all, and
89
+ * silence is the one response that cannot be acted on.
76
90
  * @param {string} input Raw argument string.
77
91
  * @returns {{params: Record<string, string>, rest: string}} Parsed invocation.
78
92
  */
@@ -83,9 +97,19 @@ export function parseInvocation(input) {
83
97
  .trim()
84
98
  .split(/\s+/)
85
99
  .filter(Boolean)) {
86
- const match = /^([A-Za-z][A-Za-z0-9_]*)=(.*)$/.exec(token);
87
- if (match) params[match[1]] = match[2];
88
- else rest.push(token);
100
+ const match = /^(--)?([A-Za-z][A-Za-z0-9_]*)=(.*)$/.exec(token);
101
+ // Only a dashed token is required to be a known parameter. A bare `x=y` may
102
+ // legitimately be part of a payload — a description, a query — and this
103
+ // program has no business rejecting the caller's prose.
104
+ if (match && (match[1] || KNOWN_PARAMS.has(match[2]))) {
105
+ if (!KNOWN_PARAMS.has(match[2])) {
106
+ throw new Error(
107
+ `unknown parameter "${match[2]}".\n` +
108
+ `Supported: ${[...KNOWN_PARAMS].join(", ")}.`
109
+ );
110
+ }
111
+ params[match[2]] = match[3];
112
+ } else rest.push(token);
89
113
  }
90
114
  return { params, rest: rest.join(" ") };
91
115
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Expo and React Native-specific skills, agents, rules, and MCP servers.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Harper/Fabric-specific Lisa rules for TypeScript component apps.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-harper-fabric",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Harper/Fabric-specific rules for TypeScript component apps",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "NestJS-specific skills and migration write-protection hooks.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-openclaw",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
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.322.4",
3
+ "version": "2.322.5",
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.322.4",
3
+ "version": "2.322.5",
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.322.4",
3
+ "version": "2.322.5",
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.322.4",
3
+ "version": "2.322.5",
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-phaser",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-phaser",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Phaser 4 game-development rules for TypeScript projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Ruby on Rails-specific skills and hooks for RuboCop and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "TypeScript-specific hooks for formatting, linting, and ast-grep scanning on edit.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, ast-grep scanning, and error-suppression blocking on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "Distributable LLM Wiki kernel — ingest, query, lint, and maintain a git-native markdown knowledge base across Claude and Codex.",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-wiki",
3
- "version": "2.322.4",
3
+ "version": "2.322.5",
4
4
  "description": "LLM Wiki — a distributable, git-native markdown knowledge base for Claude Code and Codex",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: lisa-remote-dispatch
3
- description: "Route one unit of work to a remote execution surface. Reads the executionEnv parameter (local by default, codex-cloud today), verifies the environment is provisioned and bound to this repository, submits a thin skill invocation, records the task identifier to .lisa/remote-dispatch.json, and exits without polling. Routing only — the remote runs the identical skill from the identical repository. Composable and inline: other skills invoke it via the Skill tool rather than users calling it directly."
3
+ description: "Route one unit of work to a remote execution surface. Reads the executionEnv parameter (local by default, codex-cloud or claude-web today), verifies the environment is provisioned and bound to this repository, submits a thin skill invocation, records the task identifier to .lisa/remote-dispatch.json, and exits without polling. Routing only — the remote runs the identical skill from the identical repository. Composable and inline: other skills invoke it via the Skill tool rather than users calling it directly."
4
4
  allowed-tools: ["Bash", "Read", "Skill"]
5
5
  ---
6
6
 
@@ -16,9 +16,12 @@ It changes **where** work happens and nothing about **what** happens. The remote
16
16
  | --- | --- |
17
17
  | omitted / `local` | The calling skill proceeds normally. Nothing is dispatched. |
18
18
  | `codex-cloud` | Submit to the project's Codex Cloud environment and return. |
19
+ | `claude-web` | Fire the project's Claude routine, which starts a cloud session, and return. |
19
20
 
20
21
  Any other value is **rejected explicitly**. A silently ignored `executionEnv` would run the work locally while the operator believes it went remote, and nothing downstream would contradict that belief.
21
22
 
23
+ For the same reason, the parameter is accepted as both `executionEnv=…` and `--executionEnv=…`, and a *dashed* parameter this skill does not recognise is an error rather than payload. The bare form is the documented one, but the dashed form is what anyone who has used a CLI will type, and a misspelling of either is indistinguishable from not asking at all — which is the one failure an operator cannot act on. A bare `key=value` still belongs to the payload, since prose may legitimately contain an equals sign.
24
+
22
25
  If a behaviour must differ between local and remote, it belongs in the calling skill as an explicit branch — never here. The moment this file starts encoding domain behaviour, there are two implementations to keep in sync.
23
26
 
24
27
  ## The invocation stays thin
@@ -68,11 +68,25 @@ const LEDGER = join(".lisa", "remote-dispatch.json");
68
68
  /** This file's directory, for locating the sibling secrets skill. */
69
69
  const HERE = dirname(fileURLToPath(import.meta.url));
70
70
 
71
+ /** Every parameter this program reads. Anything else is a typo. */
72
+ const KNOWN_PARAMS = new Set(["executionEnv"]);
73
+
71
74
  /**
72
75
  * Split `key=value` parameters from the rest of an invocation.
73
76
  *
74
77
  * The remainder is passed through untouched. It is the caller's payload — a
75
78
  * ticket key, a description — and this program has no business interpreting it.
79
+ *
80
+ * A leading `--` is accepted and stripped. The bare form is the documented one,
81
+ * but `--executionEnv=claude-web` is what anyone who has used a CLI this decade
82
+ * types, and it previously fell through to the payload and left the surface at
83
+ * its default. That is precisely the outcome this skill says it exists to
84
+ * prevent: work runs locally while the operator believes it went remote, and
85
+ * nothing downstream contradicts them.
86
+ *
87
+ * An unrecognised parameter is rejected for the same reason. A misspelled
88
+ * `executionEnvv=claude-web` is indistinguishable from not asking at all, and
89
+ * silence is the one response that cannot be acted on.
76
90
  * @param {string} input Raw argument string.
77
91
  * @returns {{params: Record<string, string>, rest: string}} Parsed invocation.
78
92
  */
@@ -83,9 +97,19 @@ export function parseInvocation(input) {
83
97
  .trim()
84
98
  .split(/\s+/)
85
99
  .filter(Boolean)) {
86
- const match = /^([A-Za-z][A-Za-z0-9_]*)=(.*)$/.exec(token);
87
- if (match) params[match[1]] = match[2];
88
- else rest.push(token);
100
+ const match = /^(--)?([A-Za-z][A-Za-z0-9_]*)=(.*)$/.exec(token);
101
+ // Only a dashed token is required to be a known parameter. A bare `x=y` may
102
+ // legitimately be part of a payload — a description, a query — and this
103
+ // program has no business rejecting the caller's prose.
104
+ if (match && (match[1] || KNOWN_PARAMS.has(match[2]))) {
105
+ if (!KNOWN_PARAMS.has(match[2])) {
106
+ throw new Error(
107
+ `unknown parameter "${match[2]}".\n` +
108
+ `Supported: ${[...KNOWN_PARAMS].join(", ")}.`
109
+ );
110
+ }
111
+ params[match[2]] = match[3];
112
+ } else rest.push(token);
89
113
  }
90
114
  return { params, rest: rest.join(" ") };
91
115
  }