@codyswann/lisa 4.81.3 → 4.81.4

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 (159) hide show
  1. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  2. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  3. package/dist/core/upstream-evidence-manifest.js +44 -8
  4. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  5. package/package.json +4 -4
  6. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  7. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  8. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/SKILL.md +2 -2
  9. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-manager.mjs +5 -76
  10. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-setup.mjs +165 -0
  11. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/cli-usage.mjs +32 -8
  12. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/clipboard.mjs +119 -0
  13. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/config.mjs +18 -36
  14. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/launch.mjs +1 -0
  15. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/repo-detect.mjs +205 -0
  16. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/session-manager.mjs +16 -13
  17. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/setup-checks.mjs +168 -0
  18. package/plugins/lisa/.codex-plugin/skills/lisa-session-manager/scripts/token.mjs +98 -28
  19. package/plugins/lisa/.codex-plugin/skills/lisa-setup-session-manager/SKILL.md +44 -21
  20. package/plugins/lisa/skills/lisa-session-manager/SKILL.md +2 -2
  21. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-manager.mjs +5 -76
  22. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-setup.mjs +165 -0
  23. package/plugins/lisa/skills/lisa-session-manager/scripts/cli-usage.mjs +32 -8
  24. package/plugins/lisa/skills/lisa-session-manager/scripts/clipboard.mjs +119 -0
  25. package/plugins/lisa/skills/lisa-session-manager/scripts/config.mjs +18 -36
  26. package/plugins/lisa/skills/lisa-session-manager/scripts/launch.mjs +1 -0
  27. package/plugins/lisa/skills/lisa-session-manager/scripts/repo-detect.mjs +205 -0
  28. package/plugins/lisa/skills/lisa-session-manager/scripts/session-manager.mjs +16 -13
  29. package/plugins/lisa/skills/lisa-session-manager/scripts/setup-checks.mjs +168 -0
  30. package/plugins/lisa/skills/lisa-session-manager/scripts/token.mjs +98 -28
  31. package/plugins/lisa/skills/lisa-setup-session-manager/SKILL.md +44 -21
  32. package/plugins/lisa-agy/plugin.json +1 -1
  33. package/plugins/lisa-agy/skills/lisa-session-manager/SKILL.md +2 -2
  34. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-manager.mjs +5 -76
  35. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-setup.mjs +165 -0
  36. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/cli-usage.mjs +32 -8
  37. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/clipboard.mjs +119 -0
  38. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/config.mjs +18 -36
  39. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/launch.mjs +1 -0
  40. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/repo-detect.mjs +205 -0
  41. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/session-manager.mjs +16 -13
  42. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/setup-checks.mjs +168 -0
  43. package/plugins/lisa-agy/skills/lisa-session-manager/scripts/token.mjs +98 -28
  44. package/plugins/lisa-agy/skills/lisa-setup-session-manager/SKILL.md +44 -21
  45. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  46. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  47. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  48. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  49. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  50. package/plugins/lisa-cdk-devin/.claude-plugin/plugin.json +1 -1
  51. package/plugins/lisa-cdk-devin/.devin-plugin/plugin.json +1 -1
  52. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  53. package/plugins/lisa-copilot/skills/lisa-session-manager/SKILL.md +2 -2
  54. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-manager.mjs +5 -76
  55. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-setup.mjs +165 -0
  56. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/cli-usage.mjs +32 -8
  57. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/clipboard.mjs +119 -0
  58. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/config.mjs +18 -36
  59. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/launch.mjs +1 -0
  60. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/repo-detect.mjs +205 -0
  61. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/session-manager.mjs +16 -13
  62. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/setup-checks.mjs +168 -0
  63. package/plugins/lisa-copilot/skills/lisa-session-manager/scripts/token.mjs +98 -28
  64. package/plugins/lisa-copilot/skills/lisa-setup-session-manager/SKILL.md +44 -21
  65. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  66. package/plugins/lisa-cursor/skills/lisa-session-manager/SKILL.md +2 -2
  67. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-manager.mjs +5 -76
  68. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-setup.mjs +165 -0
  69. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/cli-usage.mjs +32 -8
  70. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/clipboard.mjs +119 -0
  71. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/config.mjs +18 -36
  72. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/launch.mjs +1 -0
  73. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/repo-detect.mjs +205 -0
  74. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/session-manager.mjs +16 -13
  75. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/setup-checks.mjs +168 -0
  76. package/plugins/lisa-cursor/skills/lisa-session-manager/scripts/token.mjs +98 -28
  77. package/plugins/lisa-cursor/skills/lisa-setup-session-manager/SKILL.md +44 -21
  78. package/plugins/lisa-devin/.claude-plugin/plugin.json +1 -1
  79. package/plugins/lisa-devin/.devin-plugin/plugin.json +1 -1
  80. package/plugins/lisa-devin/skills/lisa-session-manager/SKILL.md +2 -2
  81. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-manager.mjs +5 -76
  82. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-setup.mjs +165 -0
  83. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/cli-usage.mjs +32 -8
  84. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/clipboard.mjs +119 -0
  85. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/config.mjs +18 -36
  86. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/launch.mjs +1 -0
  87. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/repo-detect.mjs +205 -0
  88. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/session-manager.mjs +16 -13
  89. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/setup-checks.mjs +168 -0
  90. package/plugins/lisa-devin/skills/lisa-session-manager/scripts/token.mjs +98 -28
  91. package/plugins/lisa-devin/skills/lisa-setup-session-manager/SKILL.md +44 -21
  92. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  93. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  94. package/plugins/lisa-expo-agy/plugin.json +1 -1
  95. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  96. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  97. package/plugins/lisa-expo-devin/.claude-plugin/plugin.json +1 -1
  98. package/plugins/lisa-expo-devin/.devin-plugin/plugin.json +1 -1
  99. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  100. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  101. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  102. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  103. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  104. package/plugins/lisa-harper-fabric-devin/.claude-plugin/plugin.json +1 -1
  105. package/plugins/lisa-harper-fabric-devin/.devin-plugin/plugin.json +1 -1
  106. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  107. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  108. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  109. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  110. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  111. package/plugins/lisa-nestjs-devin/.claude-plugin/plugin.json +1 -1
  112. package/plugins/lisa-nestjs-devin/.devin-plugin/plugin.json +1 -1
  113. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  114. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  115. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  116. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  117. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  118. package/plugins/lisa-openclaw-devin/.claude-plugin/plugin.json +1 -1
  119. package/plugins/lisa-openclaw-devin/.devin-plugin/plugin.json +1 -1
  120. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  121. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  122. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  123. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  124. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  125. package/plugins/lisa-phaser-devin/.claude-plugin/plugin.json +1 -1
  126. package/plugins/lisa-phaser-devin/.devin-plugin/plugin.json +1 -1
  127. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  128. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  129. package/plugins/lisa-rails-agy/plugin.json +1 -1
  130. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  131. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  132. package/plugins/lisa-rails-devin/.claude-plugin/plugin.json +1 -1
  133. package/plugins/lisa-rails-devin/.devin-plugin/plugin.json +1 -1
  134. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  135. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  136. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  137. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  138. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  139. package/plugins/lisa-typescript-devin/.claude-plugin/plugin.json +1 -1
  140. package/plugins/lisa-typescript-devin/.devin-plugin/plugin.json +1 -1
  141. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  142. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  143. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  144. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  145. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  146. package/plugins/lisa-wiki-devin/.claude-plugin/plugin.json +1 -1
  147. package/plugins/lisa-wiki-devin/.devin-plugin/plugin.json +1 -1
  148. package/plugins/src/base/skills/lisa-session-manager/SKILL.md +2 -2
  149. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-manager.mjs +5 -76
  150. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-setup.mjs +165 -0
  151. package/plugins/src/base/skills/lisa-session-manager/scripts/cli-usage.mjs +32 -8
  152. package/plugins/src/base/skills/lisa-session-manager/scripts/clipboard.mjs +119 -0
  153. package/plugins/src/base/skills/lisa-session-manager/scripts/config.mjs +18 -36
  154. package/plugins/src/base/skills/lisa-session-manager/scripts/launch.mjs +1 -0
  155. package/plugins/src/base/skills/lisa-session-manager/scripts/repo-detect.mjs +205 -0
  156. package/plugins/src/base/skills/lisa-session-manager/scripts/session-manager.mjs +16 -13
  157. package/plugins/src/base/skills/lisa-session-manager/scripts/setup-checks.mjs +168 -0
  158. package/plugins/src/base/skills/lisa-session-manager/scripts/token.mjs +98 -28
  159. package/plugins/src/base/skills/lisa-setup-session-manager/SKILL.md +44 -21
@@ -42,11 +42,14 @@ writes `$SM`, and pass `--project-root "$ROOT"` (the absolute project root) on e
42
42
 
43
43
  1. Preview: `node "$SM/session-manager.mjs" init-config --project-root "$ROOT"`. It detects
44
44
  `projects/*/.lisa.config.json` and proposes `name`, `interval`, `repos`, `capacity`, `stall` and a
45
- per-project `notify.stateDir`. Show the block to the operator.
45
+ per-project `notify.stateDir`. Repo detection leaves out linked worktrees and keeps one checkout
46
+ per remote, so ticket worktrees and second clones under `projects/` are not proposed. Show the
47
+ block to the operator and ask whether any repo is missing or should not be routed.
46
48
  2. Write it: the same command with `--write`. It adds only non-secret fields and never overwrites an
47
49
  existing block (`reason: "exists"` means keep it).
48
- 3. Ask the operator to replace each repo's starter `hints` with the technologies and areas that
49
- identify it (e.g. `api`: `["NestJS","migrations"]`, `web`: `["Expo","screens"]`), and whether to
50
+ 3. Each repo's `hints` are a draft: its name plus one per well-known dependency (`NestJS`, `Expo`,
51
+ `AWS CDK`, ...). Ask the operator to correct them and add the areas that identify the repo
52
+ (e.g. `api`: `["NestJS","migrations"]`, `web`: `["Expo","screens"]`), and whether to
50
53
  set `sessionManager.trustedAuthorIds`. Without it, any author's Ready item reaches a
51
54
  bypass-permissions worker; say so plainly. The list holds stable tracker user ids as strings,
52
55
  never logins: the GitHub numeric user id (`gh api users/<login> --jq .id`), the Jira
@@ -61,8 +64,14 @@ The operator creates a bot with @BotFather, one per project. Then run
61
64
  - `stored: true`: done.
62
65
  - Exit 2 because a token already exists: keep it (re-running setup is safe). Only when the operator
63
66
  says the stored token is wrong or belongs to another project, re-run with `store-token --replace`.
64
- - `reason: "no-tty"` (an agent's Bash has no terminal): print `command` from the output and ask the
65
- operator to run it in their own terminal, where it prompts with echo off. Wait for them to confirm.
67
+ - `reason: "no-tty"` (an agent's Bash has no terminal): offer the operator both routes, and say
68
+ plainly that Claude Code's `!` prefix has no terminal either, so it fails the same way:
69
+ - **Clipboard (simplest):** they copy the token from the BotFather chat and tell you; then run
70
+ `clipboardCommand` from the output (`store-token --from-clipboard`). It validates the token,
71
+ stores it, never prints it, and clears the clipboard. On `stored: false`, say what `message`
72
+ reports and ask them to copy it again.
73
+ - **Separate terminal app:** print `command` and ask them to run it in Terminal, iTerm or similar,
74
+ outside the agent, where it prompts with echo off. Wait for them to confirm.
66
75
  - `empty` / `invalid`: say what was wrong and ask again.
67
76
 
68
77
  Never ask for the token in chat, never echo, log or commit it, and never put it in
@@ -97,14 +106,24 @@ If `.lisa/automations/session-manager.runbook.md` does not exist, copy
97
106
  `$SM/../references/runbook-template.md` there. Never overwrite an existing runbook: it is the
98
107
  project's. It is checked in, like every automation runbook (`automation-runbook-contract`).
99
108
 
100
- ## Step 7: Coexistence warning
101
-
102
- Tell the operator, before starting: if this project also runs the scheduled `intake-repair` loop, it
103
- can re-dispatch an item a local worker is still building (it treats a claimed item with no tracker
104
- activity for `intake.repair.staleAfterHours` as stalled). `intake-tickets` competes for the same
105
- Ready items. Recommend tearing down `intake-tickets` (`/lisa:tear-down-automations intake-tickets`)
106
- and either raising `intake.repair.staleAfterHours` above `stall.noOutputMinutes × stall.maxAttempts`
107
- or tearing down `intake-repair`.
109
+ ## Step 7: Check for competing intake loops
110
+
111
+ The scheduled `intake-repair` loop can re-dispatch an item a local worker is still building (it treats
112
+ a claimed item with no tracker activity for `intake.repair.staleAfterHours` as stalled), and
113
+ `intake-tickets` competes for the same Ready items. Find out whether either is running before you
114
+ start the manager; do not leave the operator to check.
115
+
116
+ 1. Run `node "$SM/session-manager.mjs" coexistence --project-root "$ROOT"`. For each loop it reports
117
+ the last run recorded in this project's run log and whether that is `recent`. `live` lists the
118
+ loops that ran recently.
119
+ 2. A run log is evidence, not proof: also list your runtime's scheduler for those loop names (Claude
120
+ Code: the `/schedule` listing of claude.ai routines; Codex: automations; a `lisa-<loop>.yml`
121
+ workflow with a `schedule:` trigger on GitHub Actions).
122
+ 3. Report what you found, per loop, in one line each. Only for a loop that is live (a recent run, or a
123
+ scheduler entry): recommend tearing down `intake-tickets` (`/lisa:tear-down-automations
124
+ intake-tickets`) and either raising `intake.repair.staleAfterHours` above
125
+ `stall.noOutputMinutes × stall.maxAttempts` or tearing down `intake-repair`. When neither is live,
126
+ say so and move on.
108
127
 
109
128
  ## Step 8: Accept workspace trust in each worker repo
110
129
 
@@ -113,12 +132,15 @@ folder?" prompt: until someone has accepted it in a directory, `claude --bg` ref
113
132
  and `spawn` exits 2 saying so. Do this once per routed repo (or once for the root on a single-repo
114
133
  project), on every machine that runs the manager:
115
134
 
116
- 1. List the directories workers start in. Single-repo: the project root. Multi-repo: for each key in
117
- `sessionManager.repos.entries`,
118
- `node "$SM/session-manager.mjs" repo-dir --repo <key> --raw --project-root "$ROOT"`.
119
- 2. Ask the operator, in their own terminal, to run `claude` once in that directory,
120
- accept the trust prompt, then exit. An agent's Bash cannot answer that prompt, so never try to
121
- accept it for them.
135
+ 1. Run `node "$SM/session-manager.mjs" trust-status --project-root "$ROOT"`. It lists every
136
+ directory workers start in (each routed repo, or the root on a single-repo project) with whether
137
+ Claude Code has already recorded trust for it. `needsTrust` lists the ones that are not
138
+ `trusted: true`. When it is empty, this step is done: say so.
139
+ 2. For each directory in `needsTrust`, ask the operator, in a separate terminal app (not the agent's
140
+ Bash or Claude Code's `!` prefix), to run `claude` once in that directory, accept the trust prompt,
141
+ then exit. An agent's Bash cannot answer that prompt, so never try to accept it for them. Then
142
+ re-run `trust-status` to confirm. (`trusted: "unknown"` means Claude Code's state file could not be
143
+ read; treat it as needing trust.)
122
144
  3. Re-run this step whenever a repo is added to `sessionManager.repos`.
123
145
 
124
146
  ## Step 9: Start and pair
@@ -152,5 +174,6 @@ confirm the "Session manager Telegram isolation?" check is green. Fix every warn
152
174
  `node "$SM/session-manager.mjs" status --project-root "$ROOT"` should show the manager running.
153
175
 
154
176
  Report: the config block written (or kept), where the token lives (the path, never the value), the
155
- plugin scope result, the runbook path, which repo dirs had workspace trust accepted, the manager's
156
- tmux session name, and the doctor result.
177
+ plugin scope result, the runbook path, which repo dirs were already trusted and which the operator
178
+ accepted trust in, what the competing-loop check found, the manager's tmux session name, and the
179
+ doctor result.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "4.81.3",
3
+ "version": "4.81.4",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -23,9 +23,9 @@ channels (Telegram) and cross-session messaging (`SendMessage`, `crossSessionInb
23
23
  | Runtime | lisa-setup-session-manager | lisa-session-manager (manager) | `managedBy=` |
24
24
  |---|---|---|---|
25
25
  | Claude Code | supported | supported (reference). Needs `claude` ≥ 2.1.224, `tmux`, `bun` | supported |
26
- | Codex | config, token and runbook are supported. `start` launches a Claude Code manager, so it works when the `claude` CLI is installed | the skill ships, but `cycle` refuses to run here (no `/loop`, channels or `SendMessage`); tell the operator to run `start` | rejected with the gap message |
26
+ | Codex | config, token (prompt or `--from-clipboard`), runbook, `trust-status` and `coexistence` are supported. `trust-status` reads Claude Code's trust record, which is the one that matters (workers are Claude Code); accepting trust needs the `claude` CLI. `coexistence` sees only run logs, so also list Codex automations. `start` launches a Claude Code manager, so it works when the `claude` CLI is installed | the skill ships, but `cycle` refuses to run here (no `/loop`, channels or `SendMessage`); tell the operator to run `start` | rejected with the gap message |
27
27
  | Cursor / Antigravity (agy) / Copilot / Devin | same as Codex | same as Codex | rejected |
28
- | OpenCode | config and token are supported; script resolution relies on the npm rung, so `queue` and `record-cycle` need the npm package | gap | rejected |
28
+ | OpenCode | same setup subcommands as Codex, but script resolution relies on the npm rung: the CLI imports plugin-level scripts the skill-folder copy lacks, so every subcommand needs the npm package | gap | rejected |
29
29
 
30
30
  On any runtime other than Claude Code, `cycle` stops with: "The session manager runs on Claude Code
31
31
  only (background sessions, channels, cross-session messaging). Run `/lisa:session-manager start`
@@ -1,6 +1,7 @@
1
1
  /**
2
- * Manager and setup subcommands of the session-manager CLI: start, stop,
3
- * status, repo-dir, validate, init-config and store-token.
2
+ * Manager subcommands of the session-manager CLI: start, stop, status,
3
+ * repo-dir and validate. The setup ones (init-config, store-token,
4
+ * trust-status, coexistence) live in cli-setup.mjs.
4
5
  *
5
6
  * `start` hosts the manager in tmux. It refuses (exit 2) unless the bot
6
7
  * token file exists with owner-only permissions in the project's own state
@@ -11,15 +12,11 @@
11
12
  * manager's tmux session: workers hold claimed work and keep running.
12
13
  * @module session-manager/cli-manager
13
14
  */
14
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
15
- import path from "node:path";
16
-
17
15
  import {
18
16
  CliError,
19
17
  EXIT,
20
18
  GLOBAL_CONFIG_FILE,
21
19
  asSetupError,
22
- asSetupErrorAsync,
23
20
  intFlag,
24
21
  listAgents,
25
22
  loadProject,
@@ -28,12 +25,8 @@ import {
28
25
  requireLisaRepo,
29
26
  run,
30
27
  } from "./cli-runtime.mjs";
31
- import { buildDefaultBlock, detectRepos, resolveRepoDir } from "./config.mjs";
32
- import {
33
- defaultProjectSlug,
34
- probeOwnSession,
35
- sessionSurvives,
36
- } from "./isolation.mjs";
28
+ import { resolveRepoDir } from "./config.mjs";
29
+ import { probeOwnSession, sessionSurvives } from "./isolation.mjs";
37
30
  import {
38
31
  assertTokenPresent,
39
32
  buildForegroundCommand,
@@ -41,7 +34,6 @@ import {
41
34
  buildManagerLaunch,
42
35
  buildStopManager,
43
36
  resolveStateDir,
44
- storeToken as storeTokenFile,
45
37
  } from "./launch.mjs";
46
38
  import { readLedger } from "./ledger.mjs";
47
39
  import { parseWorkerName, scrubWorkerEnv } from "./workers.mjs";
@@ -208,69 +200,6 @@ export function validate(ctx) {
208
200
  };
209
201
  }
210
202
 
211
- /**
212
- * `init-config [--slug <slug>] [--write]`: the default block for this
213
- * project, written to `.lisa.config.json` only with `--write` and only when
214
- * no block exists. Non-secret fields only; the token never goes here.
215
- * @param {object} ctx - CLI context
216
- * @returns {{ json: object }} `{written,block,...}`
217
- */
218
- export function initConfig(ctx) {
219
- const { global } = loadProject(ctx.root);
220
- if (global?.sessionManager) {
221
- return {
222
- json: { written: false, reason: "exists", block: global.sessionManager },
223
- };
224
- }
225
- const slug =
226
- ctx.values.slug ?? defaultProjectSlug(ctx.root, global?.github?.repo);
227
- const block = asSetupError(() =>
228
- buildDefaultBlock({
229
- projectSlug: slug,
230
- detectedRepos: detectRepos(ctx.root),
231
- })
232
- );
233
- if (!ctx.values.write) return { json: { written: false, block } };
234
-
235
- const file = path.join(ctx.root, GLOBAL_CONFIG_FILE);
236
- if (!existsSync(file)) {
237
- throw new CliError(
238
- `${file} does not exist; set up Lisa in this project first.`,
239
- EXIT.SETUP
240
- );
241
- }
242
- const current = JSON.parse(readFileSync(file, "utf8"));
243
- writeFileSync(
244
- file,
245
- `${JSON.stringify({ ...current, sessionManager: block }, null, 2)}\n`
246
- );
247
- return { json: { written: true, path: file, block } };
248
- }
249
-
250
- /**
251
- * `store-token [--replace]`: prompt for the bot token on the operator's
252
- * terminal and write `<stateDir>/.env`. An existing token is kept (exit 2)
253
- * unless `--replace` is given. With no TTY it prints the command the operator
254
- * must run themselves and exits 0 with `stored:false`.
255
- * @param {object} ctx - CLI context
256
- * @returns {Promise<{ json: object, exitCode: number }>} Outcome
257
- */
258
- export async function storeToken(ctx) {
259
- const config = requireConfig(ctx);
260
- const result = await asSetupErrorAsync(() =>
261
- storeTokenFile({
262
- stateDir: config.notify?.stateDir,
263
- scriptsDir: ctx.scriptsDir,
264
- projectRoot: ctx.root,
265
- replace: ctx.values.replace === true,
266
- env: ctx.env,
267
- homedir: ctx.homedir,
268
- })
269
- );
270
- const refused = result.stored === false && result.reason !== "no-tty";
271
- return { json: result, exitCode: refused ? EXIT.SETUP : EXIT.OK };
272
- }
273
-
274
203
  /**
275
204
  * `status`: is the manager's tmux session up, and what does the ledger say
276
205
  * about each worker it dispatched.
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Setup subcommands of the session-manager CLI: init-config, store-token,
3
+ * trust-status and coexistence. Each one answers a question
4
+ * `lisa-setup-session-manager` would otherwise put to the operator, so setup
5
+ * asks only what a script cannot find out.
6
+ * @module session-manager/cli-setup
7
+ */
8
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
9
+ import path from "node:path";
10
+
11
+ import {
12
+ CliError,
13
+ EXIT,
14
+ GLOBAL_CONFIG_FILE,
15
+ asSetupError,
16
+ asSetupErrorAsync,
17
+ loadProject,
18
+ requireConfig,
19
+ } from "./cli-runtime.mjs";
20
+ import {
21
+ buildDefaultBlock,
22
+ detectRepos,
23
+ draftHints,
24
+ resolveRepoDir,
25
+ } from "./config.mjs";
26
+ import { defaultProjectSlug } from "./isolation.mjs";
27
+ import {
28
+ storeToken as storeTokenFile,
29
+ storeTokenFromClipboard,
30
+ } from "./launch.mjs";
31
+ import {
32
+ coexistence as readCoexistence,
33
+ trustStatus as readTrust,
34
+ } from "./setup-checks.mjs";
35
+
36
+ /**
37
+ * `init-config [--slug <slug>] [--write]`: the default block for this
38
+ * project, written to `.lisa.config.json` only with `--write` and only when
39
+ * no block exists. Non-secret fields only; the token never goes here. Repos
40
+ * are the routable children `detectRepos` finds (linked worktrees and second
41
+ * clones of one remote left out), each with drafted hints.
42
+ * @param {object} ctx - CLI context
43
+ * @returns {{ json: object }} `{written,block,...}`
44
+ */
45
+ export function initConfig(ctx) {
46
+ const { global } = loadProject(ctx.root);
47
+ if (global?.sessionManager) {
48
+ return {
49
+ json: { written: false, reason: "exists", block: global.sessionManager },
50
+ };
51
+ }
52
+ const slug =
53
+ ctx.values.slug ?? defaultProjectSlug(ctx.root, global?.github?.repo);
54
+ const detectedRepos = detectRepos(ctx.root);
55
+ const hints = Object.fromEntries(
56
+ detectedRepos.map(name => [
57
+ name,
58
+ draftHints(path.join(ctx.root, "projects", name), name),
59
+ ])
60
+ );
61
+ const block = asSetupError(() =>
62
+ buildDefaultBlock({ projectSlug: slug, detectedRepos, hints })
63
+ );
64
+ if (!ctx.values.write) return { json: { written: false, block } };
65
+
66
+ const file = path.join(ctx.root, GLOBAL_CONFIG_FILE);
67
+ if (!existsSync(file)) {
68
+ throw new CliError(
69
+ `${file} does not exist; set up Lisa in this project first.`,
70
+ EXIT.SETUP
71
+ );
72
+ }
73
+ const current = JSON.parse(readFileSync(file, "utf8"));
74
+ writeFileSync(
75
+ file,
76
+ `${JSON.stringify({ ...current, sessionManager: block }, null, 2)}\n`
77
+ );
78
+ return { json: { written: true, path: file, block } };
79
+ }
80
+
81
+ /**
82
+ * `store-token [--replace] [--from-clipboard]`: store the bot token in the
83
+ * project's state dir. By default it prompts on the operator's terminal (echo
84
+ * off); with no TTY it prints the two commands that work instead and exits 0
85
+ * with `stored:false`. `--from-clipboard` reads the system clipboard and needs
86
+ * no TTY. An existing token is kept (exit 2) unless `--replace` is given.
87
+ * @param {object} ctx - CLI context
88
+ * @returns {Promise<{ json: object, exitCode: number }>} Outcome
89
+ */
90
+ export async function storeToken(ctx) {
91
+ const config = requireConfig(ctx);
92
+ const input = {
93
+ stateDir: config.notify?.stateDir,
94
+ scriptsDir: ctx.scriptsDir,
95
+ projectRoot: ctx.root,
96
+ replace: ctx.values.replace === true,
97
+ env: ctx.env,
98
+ homedir: ctx.homedir,
99
+ };
100
+ const result = ctx.values["from-clipboard"]
101
+ ? asSetupError(() => storeTokenFromClipboard(input))
102
+ : await asSetupErrorAsync(() => storeTokenFile(input));
103
+ const refused = result.stored === false && result.reason !== "no-tty";
104
+ return { json: result, exitCode: refused ? EXIT.SETUP : EXIT.OK };
105
+ }
106
+
107
+ /**
108
+ * Every directory a worker can start in: each routed repo, or the root on a
109
+ * single-repo project.
110
+ * @param {object} ctx - CLI context
111
+ * @param {object} config - Resolved block
112
+ * @returns {string[]} Absolute directories
113
+ */
114
+ function workerDirs(ctx, config) {
115
+ const keys = Object.keys(config.repos?.entries ?? {});
116
+ if (keys.length === 0) return [ctx.root];
117
+ return keys.map(key =>
118
+ asSetupError(() => resolveRepoDir(config, key, ctx.root))
119
+ );
120
+ }
121
+
122
+ /**
123
+ * `trust-status`: whether Claude Code has recorded workspace trust for each
124
+ * worker directory. Exit 0 either way; setup asks the operator only about the
125
+ * rows that are not `trusted: true`.
126
+ * @param {object} ctx - CLI context
127
+ * @returns {{ json: object }} `{file,dirs:[{dir,trusted}],needsTrust}`
128
+ */
129
+ export function trustStatus(ctx) {
130
+ const config = requireConfig(ctx);
131
+ const report = readTrust({
132
+ dirs: workerDirs(ctx, config),
133
+ env: ctx.env,
134
+ homedir: ctx.homedir,
135
+ });
136
+ return {
137
+ json: {
138
+ ...report,
139
+ needsTrust: report.dirs
140
+ .filter(row => row.trusted !== true)
141
+ .map(row => row.dir),
142
+ },
143
+ };
144
+ }
145
+
146
+ /**
147
+ * `coexistence`: what this project's run logs say about the scheduled intake
148
+ * loops that compete with the manager. Exit 0 either way.
149
+ * @param {object} ctx - CLI context
150
+ * @returns {{ json: object }} `{windowHours,loops:[...],live}`
151
+ */
152
+ export function coexistence(ctx) {
153
+ const config = requireConfig(ctx);
154
+ const report = readCoexistence({
155
+ root: ctx.root,
156
+ now: ctx.now(),
157
+ stall: config.stall,
158
+ });
159
+ return {
160
+ json: {
161
+ ...report,
162
+ live: report.loops.filter(row => row.recent).map(row => row.loop),
163
+ },
164
+ };
165
+ }
@@ -61,12 +61,19 @@ Manager
61
61
  stop tmux kill-session -t =<name> only, and only for a session started
62
62
  in this root (exit 2 otherwise); workers keep running.
63
63
  stdout {status:"stopped"|"not-running",name}.
64
- store-token [--replace]
64
+ store-token [--replace] [--from-clipboard]
65
65
  Prompts for the bot token on the terminal (echo off) and writes
66
66
  <stateDir>/.env. Exit 2 when a token is already there, unless
67
- --replace. With no TTY: exit 0, stdout {stored:false,
68
- reason:"no-tty",command} where command is the line the operator
69
- runs in their own terminal. Bad input: exit 2.
67
+ --replace. With no TTY (an agent's Bash, and Claude Code's !
68
+ prefix too): exit 0, stdout {stored:false,reason:"no-tty",
69
+ message,command,clipboardCommand}; command is the line the
70
+ operator runs in a separate terminal app.
71
+ --from-clipboard reads the token from the clipboard instead
72
+ (pbpaste; wl-paste, xclip or xsel on Linux), needs no TTY, and
73
+ clears the clipboard after a successful store. stdout
74
+ {stored:true,path,clipboardCleared}. The token is never printed.
75
+ Bad input, empty clipboard or no clipboard tool: exit 2,
76
+ {stored:false,reason,message}.
70
77
 
71
78
  Config and routing
72
79
  repo-dir [--repo <key>] [--raw]
@@ -75,10 +82,27 @@ Config and routing
75
82
  validate stdout {valid,errors}; exit 2 when invalid or absent.
76
83
  init-config [--slug <slug>] [--write]
77
84
  stdout {written,block[,reason:"exists"][,path]}. Detects
78
- projects/*/.lisa.config.json. Default slug: <github.repo or
79
- folder name>-<first 6 hex of sha256(realpath(root))>; --slug is
80
- used as given. --write adds the block to .lisa.config.json,
81
- never overwriting an existing one.
85
+ projects/*/.lisa.config.json, skipping linked worktrees (.git is
86
+ a file) and keeping one child per origin remote (the one named
87
+ after the repo, else the first by name). Each repo's hints are a
88
+ draft: its name, then one per well-known package.json dependency
89
+ (e.g. @nestjs/* -> NestJS, expo -> Expo, aws-cdk-lib -> AWS CDK).
90
+ Default slug: <github.repo or folder name>-<first 6 hex of
91
+ sha256(realpath(root))>; --slug is used as given. --write adds
92
+ the block to .lisa.config.json, never overwriting an existing one.
93
+ trust-status
94
+ stdout {file,dirs:[{dir,trusted:true|false|"unknown"}],
95
+ needsTrust:[dir]}: whether Claude Code has recorded workspace
96
+ trust (hasTrustDialogAccepted in $CLAUDE_CONFIG_DIR/.claude.json,
97
+ else ~/.claude.json) for each worker dir. "unknown" when that
98
+ file cannot be read. Read-only; never accepts trust.
99
+ coexistence
100
+ stdout {windowHours,loops:[{loop,log,lastRun:{ts,outcome}|null,
101
+ recent}],live:[loop]} for intake-tickets and intake-repair, from
102
+ .lisa/automations/runs/<loop>.jsonl. recent = last run within
103
+ windowHours (2 x stall.noOutputMinutes x stall.maxAttempts, at
104
+ least 24h). Run logs only: the runtime's scheduler is the
105
+ authority on whether a loop is still scheduled.
82
106
 
83
107
  Per cycle
84
108
  queue --tracker github [--me <id>] [--label <name>] [--github-repo <o/r>]
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Read and clear the system clipboard, for `store-token --from-clipboard`.
3
+ *
4
+ * `store-token` prompts on a TTY with echo off, and an agent's Bash has no
5
+ * TTY. Neither does Claude Code's `!` prefix, which is the first thing an
6
+ * operator inside Claude Code tries, so the only working route used to be a
7
+ * separate terminal app (CodySwannGT/lisa#4465). The clipboard is the route an
8
+ * operator reaches for instead: they copy the token from BotFather and the
9
+ * agent stores it without ever seeing it.
10
+ *
11
+ * What is read is a secret. It is returned to the caller and never placed in
12
+ * an error message, a log line or a return value of anything else here.
13
+ * @module session-manager/clipboard
14
+ */
15
+ import { spawnSync } from "node:child_process";
16
+
17
+ /** Upper bound for one clipboard tool call. */
18
+ const TOOL_TIMEOUT_MS = 5000;
19
+
20
+ /**
21
+ * Clipboard tools per platform, tried in order: the first one that runs is
22
+ * used for both reading and clearing.
23
+ */
24
+ const TOOLS = Object.freeze({
25
+ darwin: [{ name: "pbpaste", read: ["pbpaste"], clear: ["pbcopy"] }],
26
+ linux: [
27
+ {
28
+ name: "wl-paste",
29
+ read: ["wl-paste", "--no-newline"],
30
+ clear: ["wl-copy", "--clear"],
31
+ },
32
+ {
33
+ name: "xclip",
34
+ read: ["xclip", "-o", "-selection", "clipboard"],
35
+ clear: ["xclip", "-i", "-selection", "clipboard"],
36
+ },
37
+ {
38
+ name: "xsel",
39
+ read: ["xsel", "--clipboard", "--output"],
40
+ clear: ["xsel", "--clipboard", "--clear"],
41
+ },
42
+ ],
43
+ });
44
+
45
+ /**
46
+ * Run one tool with empty stdin.
47
+ *
48
+ * A clear (`capture: false`) discards the tool's output instead of piping it:
49
+ * `xclip -i` forks a background selection owner, and a child holding an
50
+ * inherited pipe makes `spawnSync` wait out the whole timeout. Any spawn
51
+ * error (a timeout included) is a failure, never a success.
52
+ * @param {string[]} argv - Command and arguments
53
+ * @param {{ capture?: boolean }} [options] - Whether to capture stdout
54
+ * @returns {{ status: number | null, stdout: string, missing: boolean }}
55
+ * Outcome; `missing` when the binary is not on PATH
56
+ */
57
+ function runTool([command, ...args], { capture = true } = {}) {
58
+ const result = spawnSync(command, args, {
59
+ input: "",
60
+ encoding: "utf8",
61
+ timeout: TOOL_TIMEOUT_MS,
62
+ stdio: ["pipe", capture ? "pipe" : "ignore", "ignore"],
63
+ });
64
+ return {
65
+ status: result.error ? null : result.status,
66
+ stdout: result.stdout ?? "",
67
+ missing: result.error?.code === "ENOENT",
68
+ };
69
+ }
70
+
71
+ /**
72
+ * Read the clipboard with the first available tool for `platform`.
73
+ * @param {{ platform?: string, run?: typeof runTool }} [seams] - Platform and
74
+ * the tool runner
75
+ * @returns {{ ok: true, text: string, clear: () => boolean } |
76
+ * { ok: false, reason: "unsupported" | "unavailable" | "failed",
77
+ * message: string }} The text and a function that clears the clipboard
78
+ * (true when it did), or why nothing was read
79
+ */
80
+ export function readClipboard(seams = {}) {
81
+ const platform = seams.platform ?? process.platform;
82
+ const run = seams.run ?? runTool;
83
+ const tools = TOOLS[platform];
84
+ if (!tools) {
85
+ return {
86
+ ok: false,
87
+ reason: "unsupported",
88
+ message: `Reading the clipboard is not supported on ${platform}.`,
89
+ };
90
+ }
91
+ const failed = [];
92
+ for (const tool of tools) {
93
+ const result = run(tool.read);
94
+ if (result.missing) continue;
95
+ // An installed tool can still fail where another works (wl-paste on an
96
+ // X11 session), so a failure moves on to the next tool.
97
+ if (result.status !== 0) {
98
+ failed.push(`${tool.name} exited ${result.status}`);
99
+ continue;
100
+ }
101
+ return {
102
+ ok: true,
103
+ text: result.stdout,
104
+ clear: () => run(tool.clear, { capture: false }).status === 0,
105
+ };
106
+ }
107
+ if (failed.length > 0) {
108
+ return {
109
+ ok: false,
110
+ reason: "failed",
111
+ message: `${failed.join("; ")}; nothing was read.`,
112
+ };
113
+ }
114
+ return {
115
+ ok: false,
116
+ reason: "unavailable",
117
+ message: `No clipboard tool found (tried ${tools.map(t => t.name).join(", ")}).`,
118
+ };
119
+ }