@oratis/lisa 0.2.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/LICENSE +21 -0
- package/README.md +436 -0
- package/README.zh-CN.md +450 -0
- package/channels.example.json +45 -0
- package/completions/README.md +66 -0
- package/completions/_lisa +140 -0
- package/completions/lisa.bash +94 -0
- package/completions/lisa.fish +85 -0
- package/dist/agent.d.ts +70 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +275 -0
- package/dist/agent.js.map +1 -0
- package/dist/approval.d.ts +9 -0
- package/dist/approval.d.ts.map +1 -0
- package/dist/approval.js +47 -0
- package/dist/approval.js.map +1 -0
- package/dist/channels/config.d.ts +11 -0
- package/dist/channels/config.d.ts.map +1 -0
- package/dist/channels/config.js +27 -0
- package/dist/channels/config.js.map +1 -0
- package/dist/channels/discord.d.ts +28 -0
- package/dist/channels/discord.d.ts.map +1 -0
- package/dist/channels/discord.js +98 -0
- package/dist/channels/discord.js.map +1 -0
- package/dist/channels/feishu.d.ts +53 -0
- package/dist/channels/feishu.d.ts.map +1 -0
- package/dist/channels/feishu.js +228 -0
- package/dist/channels/feishu.js.map +1 -0
- package/dist/channels/imessage.d.ts +20 -0
- package/dist/channels/imessage.d.ts.map +1 -0
- package/dist/channels/imessage.js +124 -0
- package/dist/channels/imessage.js.map +1 -0
- package/dist/channels/registry.d.ts +8 -0
- package/dist/channels/registry.d.ts.map +1 -0
- package/dist/channels/registry.js +29 -0
- package/dist/channels/registry.js.map +1 -0
- package/dist/channels/router.d.ts +28 -0
- package/dist/channels/router.d.ts.map +1 -0
- package/dist/channels/router.js +140 -0
- package/dist/channels/router.js.map +1 -0
- package/dist/channels/slack.d.ts +43 -0
- package/dist/channels/slack.d.ts.map +1 -0
- package/dist/channels/slack.js +164 -0
- package/dist/channels/slack.js.map +1 -0
- package/dist/channels/telegram.d.ts +26 -0
- package/dist/channels/telegram.d.ts.map +1 -0
- package/dist/channels/telegram.js +103 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/channels/types.d.ts +20 -0
- package/dist/channels/types.d.ts.map +1 -0
- package/dist/channels/types.js +2 -0
- package/dist/channels/types.js.map +1 -0
- package/dist/channels/webhook.d.ts +34 -0
- package/dist/channels/webhook.d.ts.map +1 -0
- package/dist/channels/webhook.js +139 -0
- package/dist/channels/webhook.js.map +1 -0
- package/dist/cli/colors.d.ts +30 -0
- package/dist/cli/colors.d.ts.map +1 -0
- package/dist/cli/colors.js +53 -0
- package/dist/cli/colors.js.map +1 -0
- package/dist/cli/doctor.d.ts +2 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +172 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/monitor.d.ts +2 -0
- package/dist/cli/monitor.d.ts.map +1 -0
- package/dist/cli/monitor.js +141 -0
- package/dist/cli/monitor.js.map +1 -0
- package/dist/cli/repl.d.ts +7 -0
- package/dist/cli/repl.d.ts.map +1 -0
- package/dist/cli/repl.js +68 -0
- package/dist/cli/repl.js.map +1 -0
- package/dist/cli/status.d.ts +2 -0
- package/dist/cli/status.d.ts.map +1 -0
- package/dist/cli/status.js +176 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/cli/wishlist.d.ts +7 -0
- package/dist/cli/wishlist.d.ts.map +1 -0
- package/dist/cli/wishlist.js +77 -0
- package/dist/cli/wishlist.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +954 -0
- package/dist/cli.js.map +1 -0
- package/dist/env.d.ts +10 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +128 -0
- package/dist/env.js.map +1 -0
- package/dist/fs-utils.d.ts +6 -0
- package/dist/fs-utils.d.ts.map +1 -0
- package/dist/fs-utils.js +36 -0
- package/dist/fs-utils.js.map +1 -0
- package/dist/heartbeat/config.d.ts +13 -0
- package/dist/heartbeat/config.d.ts.map +1 -0
- package/dist/heartbeat/config.js +14 -0
- package/dist/heartbeat/config.js.map +1 -0
- package/dist/heartbeat/install.d.ts +15 -0
- package/dist/heartbeat/install.d.ts.map +1 -0
- package/dist/heartbeat/install.js +180 -0
- package/dist/heartbeat/install.js.map +1 -0
- package/dist/heartbeat/runner.d.ts +16 -0
- package/dist/heartbeat/runner.d.ts.map +1 -0
- package/dist/heartbeat/runner.js +180 -0
- package/dist/heartbeat/runner.js.map +1 -0
- package/dist/hooks/runner.d.ts +23 -0
- package/dist/hooks/runner.d.ts.map +1 -0
- package/dist/hooks/runner.js +55 -0
- package/dist/hooks/runner.js.map +1 -0
- package/dist/idle/runner.d.ts +16 -0
- package/dist/idle/runner.d.ts.map +1 -0
- package/dist/idle/runner.js +45 -0
- package/dist/idle/runner.js.map +1 -0
- package/dist/idle/watcher.d.ts +35 -0
- package/dist/idle/watcher.d.ts.map +1 -0
- package/dist/idle/watcher.js +67 -0
- package/dist/idle/watcher.js.map +1 -0
- package/dist/llm.d.ts +8 -0
- package/dist/llm.d.ts.map +1 -0
- package/dist/llm.js +12 -0
- package/dist/llm.js.map +1 -0
- package/dist/mcp/client.d.ts +11 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +68 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/config.d.ts +14 -0
- package/dist/mcp/config.d.ts.map +1 -0
- package/dist/mcp/config.js +28 -0
- package/dist/mcp/config.js.map +1 -0
- package/dist/memory/search_tool.d.ts +8 -0
- package/dist/memory/search_tool.d.ts.map +1 -0
- package/dist/memory/search_tool.js +26 -0
- package/dist/memory/search_tool.js.map +1 -0
- package/dist/memory/store.d.ts +7 -0
- package/dist/memory/store.d.ts.map +1 -0
- package/dist/memory/store.js +46 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/memory/tool.d.ts +13 -0
- package/dist/memory/tool.d.ts.map +1 -0
- package/dist/memory/tool.js +53 -0
- package/dist/memory/tool.js.map +1 -0
- package/dist/memory/vector.d.ts +25 -0
- package/dist/memory/vector.d.ts.map +1 -0
- package/dist/memory/vector.js +96 -0
- package/dist/memory/vector.js.map +1 -0
- package/dist/mood-bus.d.ts +16 -0
- package/dist/mood-bus.d.ts.map +1 -0
- package/dist/mood-bus.js +21 -0
- package/dist/mood-bus.js.map +1 -0
- package/dist/paths.d.ts +8 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +10 -0
- package/dist/paths.js.map +1 -0
- package/dist/plugins/loader.d.ts +4 -0
- package/dist/plugins/loader.d.ts.map +1 -0
- package/dist/plugins/loader.js +156 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/plugins/types.d.ts +43 -0
- package/dist/plugins/types.d.ts.map +1 -0
- package/dist/plugins/types.js +2 -0
- package/dist/plugins/types.js.map +1 -0
- package/dist/prompt.d.ts +21 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +187 -0
- package/dist/prompt.js.map +1 -0
- package/dist/providers/anthropic.d.ts +11 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +102 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/gemini.d.ts +11 -0
- package/dist/providers/gemini.d.ts.map +1 -0
- package/dist/providers/gemini.js +190 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/openai.d.ts +11 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +175 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/registry.d.ts +44 -0
- package/dist/providers/registry.d.ts.map +1 -0
- package/dist/providers/registry.js +209 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/types.d.ts +33 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +2 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/proxy-bootstrap.d.ts +17 -0
- package/dist/proxy-bootstrap.d.ts.map +1 -0
- package/dist/proxy-bootstrap.js +101 -0
- package/dist/proxy-bootstrap.js.map +1 -0
- package/dist/reflect.d.ts +13 -0
- package/dist/reflect.d.ts.map +1 -0
- package/dist/reflect.js +289 -0
- package/dist/reflect.js.map +1 -0
- package/dist/sandbox/macos.d.ts +5 -0
- package/dist/sandbox/macos.d.ts.map +1 -0
- package/dist/sandbox/macos.js +31 -0
- package/dist/sandbox/macos.js.map +1 -0
- package/dist/sandbox/sandbox.d.ts +15 -0
- package/dist/sandbox/sandbox.d.ts.map +1 -0
- package/dist/sandbox/sandbox.js +39 -0
- package/dist/sandbox/sandbox.js.map +1 -0
- package/dist/sessions/active.d.ts +4 -0
- package/dist/sessions/active.d.ts.map +1 -0
- package/dist/sessions/active.js +33 -0
- package/dist/sessions/active.js.map +1 -0
- package/dist/sessions/list.d.ts +16 -0
- package/dist/sessions/list.d.ts.map +1 -0
- package/dist/sessions/list.js +78 -0
- package/dist/sessions/list.js.map +1 -0
- package/dist/sessions/store.d.ts +27 -0
- package/dist/sessions/store.d.ts.map +1 -0
- package/dist/sessions/store.js +98 -0
- package/dist/sessions/store.js.map +1 -0
- package/dist/skills/executable.d.ts +45 -0
- package/dist/skills/executable.d.ts.map +1 -0
- package/dist/skills/executable.js +207 -0
- package/dist/skills/executable.js.map +1 -0
- package/dist/skills/frontmatter.d.ts +8 -0
- package/dist/skills/frontmatter.d.ts.map +1 -0
- package/dist/skills/frontmatter.js +84 -0
- package/dist/skills/frontmatter.js.map +1 -0
- package/dist/skills/manager.d.ts +9 -0
- package/dist/skills/manager.d.ts.map +1 -0
- package/dist/skills/manager.js +116 -0
- package/dist/skills/manager.js.map +1 -0
- package/dist/skills/tool.d.ts +13 -0
- package/dist/skills/tool.d.ts.map +1 -0
- package/dist/skills/tool.js +87 -0
- package/dist/skills/tool.js.map +1 -0
- package/dist/soul/birth.d.ts +13 -0
- package/dist/soul/birth.d.ts.map +1 -0
- package/dist/soul/birth.js +171 -0
- package/dist/soul/birth.js.map +1 -0
- package/dist/soul/git.d.ts +36 -0
- package/dist/soul/git.d.ts.map +1 -0
- package/dist/soul/git.js +212 -0
- package/dist/soul/git.js.map +1 -0
- package/dist/soul/paths.d.ts +20 -0
- package/dist/soul/paths.d.ts.map +1 -0
- package/dist/soul/paths.js +34 -0
- package/dist/soul/paths.js.map +1 -0
- package/dist/soul/store.d.ts +55 -0
- package/dist/soul/store.d.ts.map +1 -0
- package/dist/soul/store.js +394 -0
- package/dist/soul/store.js.map +1 -0
- package/dist/soul/tools.d.ts +107 -0
- package/dist/soul/tools.d.ts.map +1 -0
- package/dist/soul/tools.js +471 -0
- package/dist/soul/tools.js.map +1 -0
- package/dist/soul/types.d.ts +105 -0
- package/dist/soul/types.d.ts.map +1 -0
- package/dist/soul/types.js +44 -0
- package/dist/soul/types.js.map +1 -0
- package/dist/subagent.d.ts +19 -0
- package/dist/subagent.d.ts.map +1 -0
- package/dist/subagent.js +34 -0
- package/dist/subagent.js.map +1 -0
- package/dist/tools/apply_patch.d.ts +17 -0
- package/dist/tools/apply_patch.d.ts.map +1 -0
- package/dist/tools/apply_patch.js +107 -0
- package/dist/tools/apply_patch.js.map +1 -0
- package/dist/tools/bash.d.ts +8 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +81 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/edit.d.ts +10 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +50 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/grep.d.ts +11 -0
- package/dist/tools/grep.d.ts.map +1 -0
- package/dist/tools/grep.js +57 -0
- package/dist/tools/grep.js.map +1 -0
- package/dist/tools/ls.d.ts +7 -0
- package/dist/tools/ls.d.ts.map +1 -0
- package/dist/tools/ls.js +36 -0
- package/dist/tools/ls.js.map +1 -0
- package/dist/tools/read.d.ts +9 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +41 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/redeploy.d.ts +10 -0
- package/dist/tools/redeploy.d.ts.map +1 -0
- package/dist/tools/redeploy.js +73 -0
- package/dist/tools/redeploy.js.map +1 -0
- package/dist/tools/registry.d.ts +15 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +71 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/set_mood.d.ts +8 -0
- package/dist/tools/set_mood.d.ts.map +1 -0
- package/dist/tools/set_mood.js +62 -0
- package/dist/tools/set_mood.js.map +1 -0
- package/dist/tools/task.d.ts +17 -0
- package/dist/tools/task.d.ts.map +1 -0
- package/dist/tools/task.js +58 -0
- package/dist/tools/task.js.map +1 -0
- package/dist/tools/web_fetch.d.ts +10 -0
- package/dist/tools/web_fetch.d.ts.map +1 -0
- package/dist/tools/web_fetch.js +95 -0
- package/dist/tools/web_fetch.js.map +1 -0
- package/dist/tools/web_search.d.ts +8 -0
- package/dist/tools/web_search.d.ts.map +1 -0
- package/dist/tools/web_search.js +82 -0
- package/dist/tools/web_search.js.map +1 -0
- package/dist/tools/write.d.ts +8 -0
- package/dist/tools/write.d.ts.map +1 -0
- package/dist/tools/write.js +21 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/types.d.ts +67 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/voice/speak.d.ts +7 -0
- package/dist/voice/speak.d.ts.map +1 -0
- package/dist/voice/speak.js +18 -0
- package/dist/voice/speak.js.map +1 -0
- package/dist/voice/tool.d.ts +14 -0
- package/dist/voice/tool.d.ts.map +1 -0
- package/dist/voice/tool.js +41 -0
- package/dist/voice/tool.js.map +1 -0
- package/dist/voice/transcribe.d.ts +7 -0
- package/dist/voice/transcribe.d.ts.map +1 -0
- package/dist/voice/transcribe.js +14 -0
- package/dist/voice/transcribe.js.map +1 -0
- package/dist/web/assets/background-tile.png +0 -0
- package/dist/web/assets/icon-memory.png +0 -0
- package/dist/web/assets/icon-send.png +0 -0
- package/dist/web/assets/icon-skill.png +0 -0
- package/dist/web/assets/icon-soul.png +0 -0
- package/dist/web/assets/icon-tool.png +0 -0
- package/dist/web/assets/lisa/angry.png +0 -0
- package/dist/web/assets/lisa/annoyed.png +0 -0
- package/dist/web/assets/lisa/apologetic.png +0 -0
- package/dist/web/assets/lisa/artist.png +0 -0
- package/dist/web/assets/lisa/astronaut.png +0 -0
- package/dist/web/assets/lisa/at-airport.png +0 -0
- package/dist/web/assets/lisa/at-beach.png +0 -0
- package/dist/web/assets/lisa/autumn-leaves.png +0 -0
- package/dist/web/assets/lisa/biking.png +0 -0
- package/dist/web/assets/lisa/birthday.png +0 -0
- package/dist/web/assets/lisa/bored.png +0 -0
- package/dist/web/assets/lisa/casual-summer.png +0 -0
- package/dist/web/assets/lisa/cheering.png +0 -0
- package/dist/web/assets/lisa/chef.png +0 -0
- package/dist/web/assets/lisa/christmas.png +0 -0
- package/dist/web/assets/lisa/cleaning.png +0 -0
- package/dist/web/assets/lisa/confused.png +0 -0
- package/dist/web/assets/lisa/cooking.png +0 -0
- package/dist/web/assets/lisa/crying.png +0 -0
- package/dist/web/assets/lisa/dancing.png +0 -0
- package/dist/web/assets/lisa/detective.png +0 -0
- package/dist/web/assets/lisa/determined.png +0 -0
- package/dist/web/assets/lisa/drinking-coffee.png +0 -0
- package/dist/web/assets/lisa/drinking-tea.png +0 -0
- package/dist/web/assets/lisa/driving.png +0 -0
- package/dist/web/assets/lisa/eating-snack.png +0 -0
- package/dist/web/assets/lisa/embarrassed.png +0 -0
- package/dist/web/assets/lisa/excited.png +0 -0
- package/dist/web/assets/lisa/exercising.png +0 -0
- package/dist/web/assets/lisa/exhausted.png +0 -0
- package/dist/web/assets/lisa/fairy.png +0 -0
- package/dist/web/assets/lisa/fireworks.png +0 -0
- package/dist/web/assets/lisa/fog.png +0 -0
- package/dist/web/assets/lisa/formal.png +0 -0
- package/dist/web/assets/lisa/gaming.png +0 -0
- package/dist/web/assets/lisa/ghost.png +0 -0
- package/dist/web/assets/lisa/gift-giving.png +0 -0
- package/dist/web/assets/lisa/giggling.png +0 -0
- package/dist/web/assets/lisa/graduation.png +0 -0
- package/dist/web/assets/lisa/grateful.png +0 -0
- package/dist/web/assets/lisa/halloween.png +0 -0
- package/dist/web/assets/lisa/happy.png +0 -0
- package/dist/web/assets/lisa/ill.png +0 -0
- package/dist/web/assets/lisa/in-rain.png +0 -0
- package/dist/web/assets/lisa/in-snow.png +0 -0
- package/dist/web/assets/lisa/index.json +575 -0
- package/dist/web/assets/lisa/knight.png +0 -0
- package/dist/web/assets/lisa/lab-coat.png +0 -0
- package/dist/web/assets/lisa/laughing.png +0 -0
- package/dist/web/assets/lisa/livestreaming.png +0 -0
- package/dist/web/assets/lisa/loving.png +0 -0
- package/dist/web/assets/lisa/lunar-new-year.png +0 -0
- package/dist/web/assets/lisa/musician.png +0 -0
- package/dist/web/assets/lisa/napping.png +0 -0
- package/dist/web/assets/lisa/nervous.png +0 -0
- package/dist/web/assets/lisa/neutral.png +0 -0
- package/dist/web/assets/lisa/ninja.png +0 -0
- package/dist/web/assets/lisa/on-train.png +0 -0
- package/dist/web/assets/lisa/painting.png +0 -0
- package/dist/web/assets/lisa/pajamas.png +0 -0
- package/dist/web/assets/lisa/party.png +0 -0
- package/dist/web/assets/lisa/phone-call.png +0 -0
- package/dist/web/assets/lisa/pilot.png +0 -0
- package/dist/web/assets/lisa/pirate.png +0 -0
- package/dist/web/assets/lisa/playing-guitar.png +0 -0
- package/dist/web/assets/lisa/playing-piano.png +0 -0
- package/dist/web/assets/lisa/pouting.png +0 -0
- package/dist/web/assets/lisa/princess.png +0 -0
- package/dist/web/assets/lisa/proud.png +0 -0
- package/dist/web/assets/lisa/puzzled.png +0 -0
- package/dist/web/assets/lisa/raincoat.png +0 -0
- package/dist/web/assets/lisa/reading-book.png +0 -0
- package/dist/web/assets/lisa/recovering.png +0 -0
- package/dist/web/assets/lisa/robot.png +0 -0
- package/dist/web/assets/lisa/running.png +0 -0
- package/dist/web/assets/lisa/sad.png +0 -0
- package/dist/web/assets/lisa/scared.png +0 -0
- package/dist/web/assets/lisa/shocked.png +0 -0
- package/dist/web/assets/lisa/shopping.png +0 -0
- package/dist/web/assets/lisa/shy.png +0 -0
- package/dist/web/assets/lisa/singing.png +0 -0
- package/dist/web/assets/lisa/sleeping.png +0 -0
- package/dist/web/assets/lisa/sleepy.png +0 -0
- package/dist/web/assets/lisa/smug.png +0 -0
- package/dist/web/assets/lisa/spring-flowers.png +0 -0
- package/dist/web/assets/lisa/starry-night.png +0 -0
- package/dist/web/assets/lisa/stormy.png +0 -0
- package/dist/web/assets/lisa/stretching.png +0 -0
- package/dist/web/assets/lisa/studying.png +0 -0
- package/dist/web/assets/lisa/summer-hot.png +0 -0
- package/dist/web/assets/lisa/sunrise.png +0 -0
- package/dist/web/assets/lisa/superhero.png +0 -0
- package/dist/web/assets/lisa/surprised.png +0 -0
- package/dist/web/assets/lisa/thinking-pose.png +0 -0
- package/dist/web/assets/lisa/thoughtful.png +0 -0
- package/dist/web/assets/lisa/valentine.png +0 -0
- package/dist/web/assets/lisa/vampire.png +0 -0
- package/dist/web/assets/lisa/video-call.png +0 -0
- package/dist/web/assets/lisa/waking-up.png +0 -0
- package/dist/web/assets/lisa/walking.png +0 -0
- package/dist/web/assets/lisa/watching-anime.png +0 -0
- package/dist/web/assets/lisa/watching-movie.png +0 -0
- package/dist/web/assets/lisa/wedding.png +0 -0
- package/dist/web/assets/lisa/winking.png +0 -0
- package/dist/web/assets/lisa/winter-coat.png +0 -0
- package/dist/web/assets/lisa/winter-cold.png +0 -0
- package/dist/web/assets/lisa/wizard.png +0 -0
- package/dist/web/assets/lisa/working-coding.png +0 -0
- package/dist/web/assets/lisa/working-debugging.png +0 -0
- package/dist/web/assets/lisa/working-research.png +0 -0
- package/dist/web/assets/lisa/working-typing.png +0 -0
- package/dist/web/assets/lisa/working-writing.png +0 -0
- package/dist/web/assets/lisa/worried.png +0 -0
- package/dist/web/assets/lisa/yawning.png +0 -0
- package/dist/web/assets/lisa/yoga-pose.png +0 -0
- package/dist/web/assets/lisa-mascot.png +0 -0
- package/dist/web/server.d.ts +13 -0
- package/dist/web/server.d.ts.map +1 -0
- package/dist/web/server.js +2159 -0
- package/dist/web/server.js.map +1 -0
- package/package.json +68 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,954 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import process from "node:process";
|
|
3
|
+
// Bridge HTTPS_PROXY / HTTP_PROXY env to undici BEFORE any module reads/uses
|
|
4
|
+
// fetch — handles the case where the shell already exports a proxy. We also
|
|
5
|
+
// re-call this after config.env loads (inside main()) to handle proxies set
|
|
6
|
+
// in ~/.lisa/config.env.
|
|
7
|
+
import { configureProxyFromEnv } from "./proxy-bootstrap.js";
|
|
8
|
+
configureProxyFromEnv({ log: (m) => console.error(m) });
|
|
9
|
+
import { runAgent } from "./agent.js";
|
|
10
|
+
import { buildApprovalCallback, DEFAULT_MUTATING_TOOLS } from "./approval.js";
|
|
11
|
+
import { CONFIG_ENV_PATH, loadConfigEnv } from "./env.js";
|
|
12
|
+
import { ensureDir } from "./fs-utils.js";
|
|
13
|
+
import { runHeartbeatOnce } from "./heartbeat/runner.js";
|
|
14
|
+
import { fireHooks } from "./hooks/runner.js";
|
|
15
|
+
import { DEFAULT_MODEL } from "./llm.js";
|
|
16
|
+
import { connectMcpServers } from "./mcp/client.js";
|
|
17
|
+
import { loadMcpConfig } from "./mcp/config.js";
|
|
18
|
+
import { LISA_HOME } from "./paths.js";
|
|
19
|
+
import { loadAllPlugins, PLUGINS_ROOT } from "./plugins/loader.js";
|
|
20
|
+
import { buildSystemPromptSnapshot, getPromptFingerprint } from "./prompt.js";
|
|
21
|
+
import { providerForModel } from "./providers/registry.js";
|
|
22
|
+
import { reflectOnSession } from "./reflect.js";
|
|
23
|
+
import { runRepl } from "./cli/repl.js";
|
|
24
|
+
import { listSessionsOnDisk, loadSessionMessages } from "./sessions/list.js";
|
|
25
|
+
import { SessionStore } from "./sessions/store.js";
|
|
26
|
+
import { birth } from "./soul/birth.js";
|
|
27
|
+
import { isBorn, readSoulSummary } from "./soul/store.js";
|
|
28
|
+
import { createTaskTool } from "./tools/task.js";
|
|
29
|
+
import { buildToolRegistry, readOnlySubset } from "./tools/registry.js";
|
|
30
|
+
const HELP = `Lisa — your self-evolving local AI assistant.
|
|
31
|
+
|
|
32
|
+
CHAT
|
|
33
|
+
lisa Start an interactive REPL session.
|
|
34
|
+
lisa "prompt" Run one prompt and exit.
|
|
35
|
+
lisa resume <id> [prompt] Resume a previous session by id.
|
|
36
|
+
lisa sessions List recent sessions.
|
|
37
|
+
lisa search "<query>" Search past sessions (TF-IDF).
|
|
38
|
+
|
|
39
|
+
INSPECTION
|
|
40
|
+
lisa status One-shot snapshot: identity, mood, recent commits,
|
|
41
|
+
desires, sessions, providers, heartbeat last-run.
|
|
42
|
+
lisa doctor Health check (config, network, git, providers).
|
|
43
|
+
Exits non-zero on critical issues.
|
|
44
|
+
lisa monitor TUI live dashboard (mood + soul commits + events
|
|
45
|
+
+ heartbeat). Polls every 2s. Ctrl-C to quit.
|
|
46
|
+
lisa soul Print full soul summary.
|
|
47
|
+
lisa wishlist Print Lisa's own feedback about her toolset
|
|
48
|
+
(meta-wishlist desire + journal [WISHLIST]).
|
|
49
|
+
|
|
50
|
+
LIFECYCLE
|
|
51
|
+
lisa birth Run the birth ritual (auto-runs on first launch).
|
|
52
|
+
lisa heartbeat run [name] Run heartbeat tasks once (incl. self-driven desires).
|
|
53
|
+
lisa heartbeat install [--load] [--every <30m|1h|...>]
|
|
54
|
+
Install macOS launchd plist (or print cron line).
|
|
55
|
+
lisa heartbeat uninstall Remove the launchd plist (macOS).
|
|
56
|
+
|
|
57
|
+
SERVE (long-lived)
|
|
58
|
+
lisa serve --web [--port N] Start the web UI (default port 5757).
|
|
59
|
+
lisa serve --channels <list> Start IM channel adapters (comma-separated, or "all").
|
|
60
|
+
Built-in: telegram, discord, slack, feishu, webhook, imessage.
|
|
61
|
+
Config: ~/.lisa/channels.json
|
|
62
|
+
lisa serve --imessage Shortcut for --channels imessage (macOS).
|
|
63
|
+
lisa channels List available channel adapters.
|
|
64
|
+
|
|
65
|
+
SKILLS (executable, Phase 3.1)
|
|
66
|
+
lisa skills list List executable skills (~/.lisa/skills/<slug>/tool.js).
|
|
67
|
+
lisa skills approve <slug> Review the source of a skill's tool.js and approve it.
|
|
68
|
+
lisa skills disable <slug> [reason] Block an approved skill from loading.
|
|
69
|
+
lisa skills enable <slug> Remove a disable flag.
|
|
70
|
+
lisa skills audit <slug> Show the audit trail.
|
|
71
|
+
|
|
72
|
+
lisa --help Show this message.
|
|
73
|
+
|
|
74
|
+
FLAGS
|
|
75
|
+
--model <id> Override default (${DEFAULT_MODEL}).
|
|
76
|
+
Lisa auto-routes by prefix: claude-* / gpt-* / o3-* /
|
|
77
|
+
deepseek-* / doubao-* / qwen-* / moonshot-* / kimi-* /
|
|
78
|
+
grok-* / glm-*. See docs/PROVIDERS.md for recipes.
|
|
79
|
+
--provider <name> anthropic | openai (else inferred from model).
|
|
80
|
+
--think Enable adaptive thinking each turn.
|
|
81
|
+
--no-reflect Skip end-of-session reflection.
|
|
82
|
+
--compact Enable Anthropic context compaction beta.
|
|
83
|
+
--approval <mode> auto | ask | ask-mutating (default: auto)
|
|
84
|
+
--no-mcp Skip loading MCP servers.
|
|
85
|
+
--no-plugins Skip loading plugins.
|
|
86
|
+
--voice Enable speak/transcribe tools.
|
|
87
|
+
--idle <minutes> Trigger idle mode after N min of no input (default: 60).
|
|
88
|
+
--no-idle Disable idle mode entirely.
|
|
89
|
+
|
|
90
|
+
REPL slash commands:
|
|
91
|
+
/help, /exit, /quit
|
|
92
|
+
/skills [view <name>]
|
|
93
|
+
/memory
|
|
94
|
+
/sessions
|
|
95
|
+
/reflect Run reflection on the current session immediately.
|
|
96
|
+
/search <query> Search past sessions.
|
|
97
|
+
/think Toggle adaptive thinking.
|
|
98
|
+
/clear Forget current in-memory history (session log preserved).
|
|
99
|
+
/save <text> Append to MEMORY.md immediately.
|
|
100
|
+
|
|
101
|
+
Data: ${LISA_HOME}
|
|
102
|
+
Plugins: ${PLUGINS_ROOT}/<name>/{commands,agents,skills,hooks,.lisa-plugin/plugin.json}
|
|
103
|
+
MCP: ${LISA_HOME}/mcp.json (Claude-Code-style {"mcpServers": {...}})
|
|
104
|
+
Heartbeat: ${LISA_HOME}/heartbeat.json ({"tasks": [{name, prompt, ...}]})
|
|
105
|
+
Config: ${CONFIG_ENV_PATH} (KEY=VALUE)`;
|
|
106
|
+
function parseArgs(argv) {
|
|
107
|
+
const out = {
|
|
108
|
+
showHelp: false,
|
|
109
|
+
reflect: true,
|
|
110
|
+
thinking: false,
|
|
111
|
+
compaction: false,
|
|
112
|
+
model: DEFAULT_MODEL,
|
|
113
|
+
approval: "auto",
|
|
114
|
+
loadMcp: true,
|
|
115
|
+
loadPlugins: true,
|
|
116
|
+
voice: false,
|
|
117
|
+
idleMinutes: 60,
|
|
118
|
+
subargs: [],
|
|
119
|
+
serveWeb: false,
|
|
120
|
+
serveImessage: false,
|
|
121
|
+
serveChannels: [],
|
|
122
|
+
port: 5757,
|
|
123
|
+
prompt: null,
|
|
124
|
+
};
|
|
125
|
+
const positional = [];
|
|
126
|
+
for (let i = 0; i < argv.length; i++) {
|
|
127
|
+
const arg = argv[i];
|
|
128
|
+
if (arg === "--help" || arg === "-h")
|
|
129
|
+
out.showHelp = true;
|
|
130
|
+
else if (arg === "--no-reflect")
|
|
131
|
+
out.reflect = false;
|
|
132
|
+
else if (arg === "--think" || arg === "--thinking")
|
|
133
|
+
out.thinking = true;
|
|
134
|
+
else if (arg === "--compact")
|
|
135
|
+
out.compaction = true;
|
|
136
|
+
else if (arg === "--no-mcp")
|
|
137
|
+
out.loadMcp = false;
|
|
138
|
+
else if (arg === "--no-plugins")
|
|
139
|
+
out.loadPlugins = false;
|
|
140
|
+
else if (arg === "--voice")
|
|
141
|
+
out.voice = true;
|
|
142
|
+
else if (arg === "--no-idle")
|
|
143
|
+
out.idleMinutes = 0;
|
|
144
|
+
else if (arg === "--idle") {
|
|
145
|
+
const v = mustNext(argv, ++i, "--idle");
|
|
146
|
+
const n = parseInt(v, 10);
|
|
147
|
+
if (!Number.isFinite(n) || n < 0)
|
|
148
|
+
throw new Error(`bad --idle: ${v}`);
|
|
149
|
+
out.idleMinutes = n;
|
|
150
|
+
}
|
|
151
|
+
else if (arg === "--web")
|
|
152
|
+
out.serveWeb = true;
|
|
153
|
+
else if (arg === "--imessage")
|
|
154
|
+
out.serveImessage = true;
|
|
155
|
+
else if (arg === "--channels") {
|
|
156
|
+
out.serveChannels = mustNext(argv, ++i, "--channels")
|
|
157
|
+
.split(",")
|
|
158
|
+
.map((s) => s.trim())
|
|
159
|
+
.filter(Boolean);
|
|
160
|
+
}
|
|
161
|
+
else if (arg.startsWith("--channels=")) {
|
|
162
|
+
out.serveChannels = arg
|
|
163
|
+
.slice("--channels=".length)
|
|
164
|
+
.split(",")
|
|
165
|
+
.map((s) => s.trim())
|
|
166
|
+
.filter(Boolean);
|
|
167
|
+
}
|
|
168
|
+
else if (arg === "--model")
|
|
169
|
+
out.model = mustNext(argv, ++i, "--model");
|
|
170
|
+
else if (arg.startsWith("--model="))
|
|
171
|
+
out.model = arg.slice("--model=".length);
|
|
172
|
+
else if (arg === "--provider") {
|
|
173
|
+
const v = mustNext(argv, ++i, "--provider");
|
|
174
|
+
process.env.LISA_PROVIDER = v;
|
|
175
|
+
}
|
|
176
|
+
else if (arg === "--approval") {
|
|
177
|
+
const v = mustNext(argv, ++i, "--approval");
|
|
178
|
+
if (!["auto", "ask", "ask-mutating"].includes(v)) {
|
|
179
|
+
throw new Error(`bad --approval mode: ${v}`);
|
|
180
|
+
}
|
|
181
|
+
out.approval = v;
|
|
182
|
+
}
|
|
183
|
+
else if (arg === "--port") {
|
|
184
|
+
out.port = parseInt(mustNext(argv, ++i, "--port"), 10);
|
|
185
|
+
}
|
|
186
|
+
else if (arg.startsWith("--")) {
|
|
187
|
+
throw new Error(`unknown flag: ${arg}`);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
positional.push(arg);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (positional.length > 0) {
|
|
194
|
+
const first = positional[0];
|
|
195
|
+
if (first === "resume" ||
|
|
196
|
+
first === "sessions" ||
|
|
197
|
+
first === "serve" ||
|
|
198
|
+
first === "heartbeat" ||
|
|
199
|
+
first === "search" ||
|
|
200
|
+
first === "birth" ||
|
|
201
|
+
first === "soul" ||
|
|
202
|
+
first === "channels" ||
|
|
203
|
+
first === "skills" ||
|
|
204
|
+
first === "wishlist" ||
|
|
205
|
+
first === "status" ||
|
|
206
|
+
first === "doctor" ||
|
|
207
|
+
first === "monitor") {
|
|
208
|
+
out.subcommand = first;
|
|
209
|
+
out.subargs = positional.slice(1);
|
|
210
|
+
}
|
|
211
|
+
else {
|
|
212
|
+
out.prompt = positional.join(" ");
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return out;
|
|
216
|
+
}
|
|
217
|
+
function mustNext(argv, idx, flag) {
|
|
218
|
+
const v = argv[idx];
|
|
219
|
+
if (!v)
|
|
220
|
+
throw new Error(`${flag} requires a value`);
|
|
221
|
+
return v;
|
|
222
|
+
}
|
|
223
|
+
async function main() {
|
|
224
|
+
let args;
|
|
225
|
+
try {
|
|
226
|
+
args = parseArgs(process.argv.slice(2));
|
|
227
|
+
}
|
|
228
|
+
catch (err) {
|
|
229
|
+
console.error(err.message);
|
|
230
|
+
console.error(HELP);
|
|
231
|
+
process.exit(2);
|
|
232
|
+
}
|
|
233
|
+
if (args.showHelp) {
|
|
234
|
+
console.log(HELP);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
await ensureDir(LISA_HOME);
|
|
238
|
+
loadConfigEnv();
|
|
239
|
+
// Re-bridge proxy in case HTTPS_PROXY was set in config.env rather than
|
|
240
|
+
// the shell. configureProxyFromEnv is idempotent.
|
|
241
|
+
configureProxyFromEnv({ log: (m) => console.error(m) });
|
|
242
|
+
// ── soul-only subcommands (don't need agent loop) ─────────────────────
|
|
243
|
+
if (args.subcommand === "birth") {
|
|
244
|
+
if (!process.env.ANTHROPIC_API_KEY) {
|
|
245
|
+
console.error("Birth ritual needs ANTHROPIC_API_KEY (an LLM dreams Lisa into existence).");
|
|
246
|
+
process.exit(1);
|
|
247
|
+
}
|
|
248
|
+
await runBirthCeremony(args.model);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
if (args.subcommand === "soul") {
|
|
252
|
+
const summary = await readSoulSummary();
|
|
253
|
+
if (!summary) {
|
|
254
|
+
console.log("Lisa hasn't been born yet. Run `lisa birth`.");
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
printSoulSummary(summary);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
if (args.subcommand === "channels") {
|
|
261
|
+
const { listAvailableChannels, registerBuiltins } = await import("./channels/registry.js");
|
|
262
|
+
const { loadChannelsConfig, CHANNELS_CONFIG_PATH } = await import("./channels/config.js");
|
|
263
|
+
await registerBuiltins();
|
|
264
|
+
const available = listAvailableChannels();
|
|
265
|
+
const cfg = await loadChannelsConfig();
|
|
266
|
+
console.log("Available channel adapters:\n");
|
|
267
|
+
for (const name of available) {
|
|
268
|
+
const entry = cfg.channels[name];
|
|
269
|
+
const status = !entry
|
|
270
|
+
? "(not configured)"
|
|
271
|
+
: entry.enabled === false
|
|
272
|
+
? "(disabled)"
|
|
273
|
+
: "configured ✓";
|
|
274
|
+
console.log(` ${name.padEnd(12)} ${status}`);
|
|
275
|
+
}
|
|
276
|
+
console.log(`\nConfig: ${CHANNELS_CONFIG_PATH}`);
|
|
277
|
+
console.log(`Start: lisa serve --channels <comma-list> (or --channels all)`);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
if (args.subcommand === "skills") {
|
|
281
|
+
await handleSkillsSubcommand(args.subargs);
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (args.subcommand === "wishlist") {
|
|
285
|
+
await handleWishlistSubcommand();
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
if (args.subcommand === "status") {
|
|
289
|
+
const { runStatus } = await import("./cli/status.js");
|
|
290
|
+
await runStatus();
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
if (args.subcommand === "doctor") {
|
|
294
|
+
const { runDoctor } = await import("./cli/doctor.js");
|
|
295
|
+
await runDoctor();
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
if (args.subcommand === "monitor") {
|
|
299
|
+
const { runMonitor } = await import("./cli/monitor.js");
|
|
300
|
+
await runMonitor();
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
if (args.subcommand === "sessions") {
|
|
304
|
+
const sessions = await listSessionsOnDisk();
|
|
305
|
+
for (const s of sessions) {
|
|
306
|
+
console.log(`${s.id} ${s.startedAt} msgs=${s.messageCount} model=${s.model}` +
|
|
307
|
+
(s.lastUserMessage ? ` last="${s.lastUserMessage}"` : ""));
|
|
308
|
+
}
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (args.subcommand === "search") {
|
|
312
|
+
const query = args.subargs.join(" ");
|
|
313
|
+
if (!query) {
|
|
314
|
+
console.error("usage: lisa search <query>");
|
|
315
|
+
process.exit(2);
|
|
316
|
+
}
|
|
317
|
+
const { buildIndex, search } = await import("./memory/vector.js");
|
|
318
|
+
const index = await buildIndex();
|
|
319
|
+
const hits = search(index, query, 10);
|
|
320
|
+
if (hits.length === 0)
|
|
321
|
+
console.log("(no matches)");
|
|
322
|
+
for (const h of hits) {
|
|
323
|
+
console.log(`[${h.startedAt}] ${h.sessionId} (score=${h.score.toFixed(2)})\n ${h.excerpt}\n`);
|
|
324
|
+
}
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
// Most paths from here need the API key.
|
|
328
|
+
// Exception: `serve --web` defers the key check to the browser UI, which
|
|
329
|
+
// shows a popup that writes the key to ~/.lisa/config.env on save.
|
|
330
|
+
const isWebServe = args.subcommand === "serve" && args.serveWeb;
|
|
331
|
+
const provider = providerForModel(args.model);
|
|
332
|
+
if (!isWebServe) {
|
|
333
|
+
if (provider.name === "anthropic" && !process.env.ANTHROPIC_API_KEY) {
|
|
334
|
+
console.error(`Lisa needs ANTHROPIC_API_KEY. Set it in your shell or in ${CONFIG_ENV_PATH}:\n\n ANTHROPIC_API_KEY=sk-ant-...\n\nGet a key at https://console.anthropic.com/.`);
|
|
335
|
+
process.exit(1);
|
|
336
|
+
}
|
|
337
|
+
if (provider.name === "openai" && !process.env.OPENAI_API_KEY) {
|
|
338
|
+
console.error(`Lisa needs OPENAI_API_KEY for the OpenAI provider. Set it in your shell or in ${CONFIG_ENV_PATH}.`);
|
|
339
|
+
process.exit(1);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
// ── auto-birth on first launch ──────────────────────────────────────
|
|
343
|
+
// Skipped for `serve --web` — the browser UI runs the birth ritual itself
|
|
344
|
+
// (and only after the API key has been entered via the config popup).
|
|
345
|
+
if (!isWebServe && !(await isBorn())) {
|
|
346
|
+
console.error("\nLisa hasn't been born yet — running her birth ritual now (one-time, ~30s).\nIf you'd rather skip this, hit Ctrl-C and run `lisa birth` later.\n");
|
|
347
|
+
await runBirthCeremony(args.model);
|
|
348
|
+
console.error("\n");
|
|
349
|
+
}
|
|
350
|
+
// Executable skills (Phase 3.1): tool.js files in ~/.lisa/skills/<slug>/
|
|
351
|
+
// that have been explicitly approved by SHA. Sandbox is the user's review,
|
|
352
|
+
// not the runtime — these run in-process. Unapproved or stale ones are
|
|
353
|
+
// surfaced as a startup notice; the user runs `lisa skills approve <slug>`.
|
|
354
|
+
const { discoverExecutableSkills, loadApprovedExecutableTools, summarizeCandidate } = await import("./skills/executable.js");
|
|
355
|
+
const skillCandidates = await discoverExecutableSkills();
|
|
356
|
+
const executableTools = await loadApprovedExecutableTools((m) => console.error(m));
|
|
357
|
+
if (skillCandidates.length > 0) {
|
|
358
|
+
const pending = skillCandidates.filter((c) => c.status !== "approved-current");
|
|
359
|
+
if (pending.length > 0) {
|
|
360
|
+
console.error(`\n[skills] ${executableTools.length} executable tool(s) loaded; ${pending.length} pending:`);
|
|
361
|
+
for (const c of pending)
|
|
362
|
+
console.error(summarizeCandidate(c));
|
|
363
|
+
console.error(`Run \`lisa skills approve <slug>\` to review and approve.\n`);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const baseTools = buildToolRegistry({ includeVoice: args.voice, extra: executableTools });
|
|
367
|
+
// Load plugins (skills/commands/agents/hooks/mcp).
|
|
368
|
+
const plugins = args.loadPlugins ? await loadAllPlugins() : [];
|
|
369
|
+
const allHooks = plugins.flatMap((p) => p.hooks);
|
|
370
|
+
const pluginMcp = plugins.flatMap((p) => p.mcpServers);
|
|
371
|
+
// Load MCP servers (config + plugins).
|
|
372
|
+
let mcpConnections = [];
|
|
373
|
+
if (args.loadMcp) {
|
|
374
|
+
const configMcp = await loadMcpConfig();
|
|
375
|
+
const allSpecs = [...configMcp, ...pluginMcp];
|
|
376
|
+
if (allSpecs.length > 0) {
|
|
377
|
+
mcpConnections = await connectMcpServers(allSpecs, (m) => console.error(m));
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
const mcpTools = mcpConnections.flatMap((c) => c.tools);
|
|
381
|
+
// Build the full tool list, including the task subagent tool.
|
|
382
|
+
const abortController = new AbortController();
|
|
383
|
+
process.on("SIGINT", () => abortController.abort());
|
|
384
|
+
const cwd = process.cwd();
|
|
385
|
+
const composedTools = [...baseTools, ...mcpTools];
|
|
386
|
+
const taskTool = createTaskTool({
|
|
387
|
+
fullToolset: () => composedTools,
|
|
388
|
+
readOnlyToolset: () => readOnlySubset(composedTools),
|
|
389
|
+
cwd,
|
|
390
|
+
signal: abortController.signal,
|
|
391
|
+
defaultModel: args.model,
|
|
392
|
+
});
|
|
393
|
+
composedTools.push(taskTool);
|
|
394
|
+
composedTools.sort((a, b) => a.name.localeCompare(b.name));
|
|
395
|
+
// Heartbeat sub-command — uses the assembled toolset.
|
|
396
|
+
if (args.subcommand === "heartbeat") {
|
|
397
|
+
const sub = args.subargs[0];
|
|
398
|
+
if (sub === "install") {
|
|
399
|
+
const { installHeartbeat } = await import("./heartbeat/install.js");
|
|
400
|
+
const everyIdx = args.subargs.indexOf("--every");
|
|
401
|
+
const schedule = everyIdx >= 0 && args.subargs[everyIdx + 1]
|
|
402
|
+
? `every:${args.subargs[everyIdx + 1]}`
|
|
403
|
+
: undefined;
|
|
404
|
+
const load = args.subargs.includes("--load");
|
|
405
|
+
const result = await installHeartbeat({ schedule, load });
|
|
406
|
+
console.log(`platform: ${result.platform}\n${result.instructions}`);
|
|
407
|
+
await Promise.all(mcpConnections.map((c) => c.close()));
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
if (sub === "uninstall") {
|
|
411
|
+
const { uninstallHeartbeat } = await import("./heartbeat/install.js");
|
|
412
|
+
console.log(await uninstallHeartbeat());
|
|
413
|
+
await Promise.all(mcpConnections.map((c) => c.close()));
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
if (sub !== "run") {
|
|
417
|
+
console.error("usage: lisa heartbeat <run|install|uninstall> [args...]");
|
|
418
|
+
process.exit(2);
|
|
419
|
+
}
|
|
420
|
+
const results = await runHeartbeatOnce({
|
|
421
|
+
tools: composedTools,
|
|
422
|
+
cwd,
|
|
423
|
+
signal: abortController.signal,
|
|
424
|
+
model: args.model,
|
|
425
|
+
taskFilter: args.subargs[1],
|
|
426
|
+
});
|
|
427
|
+
for (const r of results) {
|
|
428
|
+
if (r.silent) {
|
|
429
|
+
console.error(`[heartbeat:${r.task}] (silent)`);
|
|
430
|
+
}
|
|
431
|
+
else {
|
|
432
|
+
console.log(`[heartbeat:${r.task}]\n${r.output}\n`);
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
await Promise.all(mcpConnections.map((c) => c.close()));
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
// Serve sub-command — web or imessage.
|
|
439
|
+
if (args.subcommand === "serve") {
|
|
440
|
+
if (args.serveWeb) {
|
|
441
|
+
const { startWebServer } = await import("./web/server.js");
|
|
442
|
+
await startWebServer({
|
|
443
|
+
port: args.port,
|
|
444
|
+
tools: composedTools,
|
|
445
|
+
model: args.model,
|
|
446
|
+
thinking: args.thinking,
|
|
447
|
+
reflect: args.reflect,
|
|
448
|
+
idleMinutes: args.idleMinutes,
|
|
449
|
+
});
|
|
450
|
+
console.error(`Lisa web UI listening on http://localhost:${args.port}`);
|
|
451
|
+
// Keep alive until SIGINT.
|
|
452
|
+
await new Promise(() => { });
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
if (args.serveImessage)
|
|
456
|
+
args.serveChannels.push("imessage");
|
|
457
|
+
if (args.serveChannels.length > 0) {
|
|
458
|
+
const { ChannelRouter } = await import("./channels/router.js");
|
|
459
|
+
const { loadChannelsConfig } = await import("./channels/config.js");
|
|
460
|
+
const { makeChannel, registerBuiltins, listAvailableChannels } = await import("./channels/registry.js");
|
|
461
|
+
await registerBuiltins();
|
|
462
|
+
const cfg = await loadChannelsConfig();
|
|
463
|
+
let names = args.serveChannels;
|
|
464
|
+
if (names.includes("all")) {
|
|
465
|
+
names = Object.keys(cfg.channels).filter((n) => cfg.channels[n].enabled !== false);
|
|
466
|
+
}
|
|
467
|
+
if (names.length === 0) {
|
|
468
|
+
console.error(`No channels selected. Built-in adapters: ${listAvailableChannels().join(", ")}\nDefine them in ~/.lisa/channels.json then run \`lisa serve --channels <list>\`.`);
|
|
469
|
+
process.exit(2);
|
|
470
|
+
}
|
|
471
|
+
const adapters = [];
|
|
472
|
+
for (const n of names) {
|
|
473
|
+
const entry = cfg.channels[n] ?? {};
|
|
474
|
+
if (entry.enabled === false) {
|
|
475
|
+
console.error(`[router] ${n} disabled in config, skipping`);
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
try {
|
|
479
|
+
adapters.push(await makeChannel(n, entry));
|
|
480
|
+
}
|
|
481
|
+
catch (err) {
|
|
482
|
+
console.error(`[router] ${n} failed to init: ${err.message}`);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
if (adapters.length === 0) {
|
|
486
|
+
console.error("no channels could be started — check config and credentials");
|
|
487
|
+
process.exit(1);
|
|
488
|
+
}
|
|
489
|
+
const router = new ChannelRouter({
|
|
490
|
+
channels: adapters,
|
|
491
|
+
tools: composedTools,
|
|
492
|
+
cwd,
|
|
493
|
+
signal: abortController.signal,
|
|
494
|
+
model: args.model,
|
|
495
|
+
thinking: args.thinking,
|
|
496
|
+
compaction: args.compaction,
|
|
497
|
+
});
|
|
498
|
+
await router.start();
|
|
499
|
+
console.error(`Lisa is now reachable on: ${adapters.map((a) => a.name).join(", ")}`);
|
|
500
|
+
const shutdown = async () => {
|
|
501
|
+
console.error("\n[router] shutting down…");
|
|
502
|
+
await router.stop();
|
|
503
|
+
if (args.reflect)
|
|
504
|
+
await router.reflectAll();
|
|
505
|
+
await Promise.all(mcpConnections.map((c) => c.close()));
|
|
506
|
+
process.exit(0);
|
|
507
|
+
};
|
|
508
|
+
process.on("SIGINT", shutdown);
|
|
509
|
+
process.on("SIGTERM", shutdown);
|
|
510
|
+
await new Promise(() => { });
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
console.error("usage: lisa serve --web [--port N] | lisa serve --channels <list>");
|
|
514
|
+
process.exit(2);
|
|
515
|
+
}
|
|
516
|
+
// Resume vs new.
|
|
517
|
+
let session;
|
|
518
|
+
let history;
|
|
519
|
+
if (args.subcommand === "resume") {
|
|
520
|
+
const id = args.subargs[0];
|
|
521
|
+
if (!id) {
|
|
522
|
+
console.error("usage: lisa resume <id> [prompt]");
|
|
523
|
+
process.exit(2);
|
|
524
|
+
}
|
|
525
|
+
const loaded = await loadSessionMessages(id);
|
|
526
|
+
session = await SessionStore.open(id);
|
|
527
|
+
history = loaded.messages;
|
|
528
|
+
args.prompt = args.subargs.slice(1).join(" ") || null;
|
|
529
|
+
console.error(`Resumed session ${id} (${history.length} messages).`);
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
session = await SessionStore.create({ cwd, model: args.model });
|
|
533
|
+
history = [];
|
|
534
|
+
}
|
|
535
|
+
const snapshot = await buildSystemPromptSnapshot();
|
|
536
|
+
const initialFingerprint = await getPromptFingerprint();
|
|
537
|
+
const totalSkills = snapshot.skillCount + plugins.flatMap((p) => p.skills).length;
|
|
538
|
+
console.error(`Lisa session ${session.id} — ${totalSkills} skills, ${snapshot.memoryBytes}B memory, ${composedTools.length} tools, ${mcpConnections.length} mcp, ${plugins.length} plugins, model=${args.model}${args.thinking ? ", thinking=adaptive" : ""}${args.compaction ? ", compaction=on" : ""}, approval=${args.approval}`);
|
|
539
|
+
const approval = buildApprovalCallback({
|
|
540
|
+
mode: args.approval,
|
|
541
|
+
mutatingTools: DEFAULT_MUTATING_TOOLS,
|
|
542
|
+
});
|
|
543
|
+
const rebuildPrompt = makeHotReloadRebuilder(initialFingerprint, snapshot.text);
|
|
544
|
+
const turn = async (prompt) => {
|
|
545
|
+
process.stdout.write("\nLisa> ");
|
|
546
|
+
// Per-turn freshness: pick up any cross-session writes to soul / skills /
|
|
547
|
+
// memory (e.g. she patched her own soul during the previous turn). The
|
|
548
|
+
// closure caches by fingerprint so this is cheap when nothing changed.
|
|
549
|
+
const fresh = await rebuildPrompt();
|
|
550
|
+
const result = await runAgent({
|
|
551
|
+
provider,
|
|
552
|
+
systemPrompt: fresh.text,
|
|
553
|
+
tools: composedTools,
|
|
554
|
+
toolCtx: { cwd, signal: abortController.signal, log: () => { } },
|
|
555
|
+
history,
|
|
556
|
+
userMessage: prompt,
|
|
557
|
+
model: args.model,
|
|
558
|
+
thinking: args.thinking,
|
|
559
|
+
compaction: args.compaction,
|
|
560
|
+
approval,
|
|
561
|
+
preToolHook: async (name, input) => {
|
|
562
|
+
const r = await fireHooks("PreToolUse", allHooks, { TOOL_NAME: name, TOOL_INPUT: JSON.stringify(input), SESSION_ID: session.id, LISA_HOME, CLAUDE_PROJECT_DIR: cwd }, cwd);
|
|
563
|
+
if (r.blocked.length > 0)
|
|
564
|
+
return { block: r.blocked.join("; ") };
|
|
565
|
+
},
|
|
566
|
+
postToolHook: async (name, input, result, isError) => {
|
|
567
|
+
const r = await fireHooks("PostToolUse", allHooks, {
|
|
568
|
+
TOOL_NAME: name,
|
|
569
|
+
TOOL_INPUT: JSON.stringify(input),
|
|
570
|
+
TOOL_RESULT: result,
|
|
571
|
+
TOOL_ERROR: isError ? "1" : "",
|
|
572
|
+
SESSION_ID: session.id,
|
|
573
|
+
LISA_HOME,
|
|
574
|
+
CLAUDE_PROJECT_DIR: cwd,
|
|
575
|
+
}, cwd);
|
|
576
|
+
if (r.rewriteResult != null)
|
|
577
|
+
return { rewriteResult: r.rewriteResult };
|
|
578
|
+
},
|
|
579
|
+
onEvent: renderEvent,
|
|
580
|
+
onMessagePersist: (msg) => session.appendMessage(msg),
|
|
581
|
+
hotReload: {
|
|
582
|
+
initialFingerprint: fresh.fingerprint,
|
|
583
|
+
rebuild: rebuildPrompt,
|
|
584
|
+
},
|
|
585
|
+
});
|
|
586
|
+
process.stdout.write("\n");
|
|
587
|
+
history.length = 0;
|
|
588
|
+
history.push(...result.history);
|
|
589
|
+
if (result.cacheReadTokens || result.cacheWriteTokens || result.inputTokens) {
|
|
590
|
+
console.error(`[tokens in=${result.inputTokens} out=${result.outputTokens} cache_read=${result.cacheReadTokens} cache_write=${result.cacheWriteTokens}]`);
|
|
591
|
+
}
|
|
592
|
+
};
|
|
593
|
+
const finish = async () => {
|
|
594
|
+
if (!args.reflect || history.length < 2)
|
|
595
|
+
return;
|
|
596
|
+
console.error("\n[lisa] reflecting on session…");
|
|
597
|
+
try {
|
|
598
|
+
const reflection = await reflectOnSession({
|
|
599
|
+
history,
|
|
600
|
+
sessionId: session.id,
|
|
601
|
+
model: args.model,
|
|
602
|
+
});
|
|
603
|
+
await session.appendReflection(reflection.summary);
|
|
604
|
+
console.error(`[reflection] ${reflection.summary}`);
|
|
605
|
+
for (const a of reflection.applied)
|
|
606
|
+
console.error(` applied: ${a}`);
|
|
607
|
+
for (const s of reflection.skipped)
|
|
608
|
+
console.error(` skipped: ${s}`);
|
|
609
|
+
}
|
|
610
|
+
catch (err) {
|
|
611
|
+
console.error(`[reflection] failed: ${err.message}`);
|
|
612
|
+
}
|
|
613
|
+
await fireHooks("SessionEnd", allHooks, { SESSION_ID: session.id, LISA_HOME }, cwd);
|
|
614
|
+
await Promise.all(mcpConnections.map((c) => c.close()));
|
|
615
|
+
};
|
|
616
|
+
if (args.prompt) {
|
|
617
|
+
await turn(args.prompt);
|
|
618
|
+
await finish();
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
await fireHooks("SessionStart", allHooks, { SESSION_ID: session.id, LISA_HOME }, cwd);
|
|
622
|
+
// Idle watcher in REPL mode: fire silently (writes journal/skills only —
|
|
623
|
+
// we don't want a popup interrupting an active terminal session).
|
|
624
|
+
if (args.idleMinutes > 0) {
|
|
625
|
+
const { getIdleWatcher } = await import("./idle/watcher.js");
|
|
626
|
+
const { runIdleOnce } = await import("./idle/runner.js");
|
|
627
|
+
const watcher = getIdleWatcher(args.idleMinutes * 60_000);
|
|
628
|
+
let busy = false;
|
|
629
|
+
watcher.on("idle", async () => {
|
|
630
|
+
if (busy)
|
|
631
|
+
return;
|
|
632
|
+
busy = true;
|
|
633
|
+
try {
|
|
634
|
+
const r = await runIdleOnce({
|
|
635
|
+
tools: composedTools,
|
|
636
|
+
cwd,
|
|
637
|
+
signal: abortController.signal,
|
|
638
|
+
model: args.model,
|
|
639
|
+
idleMs: watcher.idleFor(),
|
|
640
|
+
});
|
|
641
|
+
if (!r.silent) {
|
|
642
|
+
process.stderr.write(`\n\n[★ while you were away]\n${r.text}\n\nyou> `);
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
catch (err) {
|
|
646
|
+
process.stderr.write(`\n[idle] error: ${err.message}\n`);
|
|
647
|
+
}
|
|
648
|
+
finally {
|
|
649
|
+
busy = false;
|
|
650
|
+
}
|
|
651
|
+
});
|
|
652
|
+
watcher.start();
|
|
653
|
+
}
|
|
654
|
+
await runRepl({
|
|
655
|
+
onLine: async (line) => {
|
|
656
|
+
try {
|
|
657
|
+
const { getIdleWatcher } = await import("./idle/watcher.js");
|
|
658
|
+
getIdleWatcher(args.idleMinutes * 60_000 || 60 * 60_000).tick();
|
|
659
|
+
}
|
|
660
|
+
catch { }
|
|
661
|
+
const r = await fireHooks("UserPromptSubmit", allHooks, { USER_PROMPT: line, SESSION_ID: session.id, LISA_HOME }, cwd);
|
|
662
|
+
if (r.blocked.length > 0) {
|
|
663
|
+
console.error(`[hook blocked] ${r.blocked.join("; ")}`);
|
|
664
|
+
return;
|
|
665
|
+
}
|
|
666
|
+
await turn(line);
|
|
667
|
+
},
|
|
668
|
+
onSlash: async (cmd, args2) => {
|
|
669
|
+
if (cmd === "exit" || cmd === "quit") {
|
|
670
|
+
process.kill(process.pid, "SIGINT");
|
|
671
|
+
return true;
|
|
672
|
+
}
|
|
673
|
+
if (cmd === "help") {
|
|
674
|
+
console.error(HELP);
|
|
675
|
+
return true;
|
|
676
|
+
}
|
|
677
|
+
if (cmd === "skills") {
|
|
678
|
+
const { listSkills, getSkill } = await import("./skills/manager.js");
|
|
679
|
+
if (args2.startsWith("view ")) {
|
|
680
|
+
const skill = await getSkill(args2.slice(5).trim());
|
|
681
|
+
if (!skill)
|
|
682
|
+
console.error("(not found)");
|
|
683
|
+
else
|
|
684
|
+
console.error(`# ${skill.frontmatter.name}\n${skill.frontmatter.description}\n\n${skill.body}`);
|
|
685
|
+
return true;
|
|
686
|
+
}
|
|
687
|
+
const skills = await listSkills();
|
|
688
|
+
if (skills.length === 0)
|
|
689
|
+
console.error("(no skills saved)");
|
|
690
|
+
else
|
|
691
|
+
for (const s of skills)
|
|
692
|
+
console.error(`- ${s.frontmatter.name}: ${s.frontmatter.description}`);
|
|
693
|
+
return true;
|
|
694
|
+
}
|
|
695
|
+
if (cmd === "memory") {
|
|
696
|
+
const { readMemory } = await import("./memory/store.js");
|
|
697
|
+
console.error("--- USER.md ---\n" + ((await readMemory("user")) || "(empty)"));
|
|
698
|
+
console.error("--- MEMORY.md ---\n" + ((await readMemory("memory")) || "(empty)"));
|
|
699
|
+
return true;
|
|
700
|
+
}
|
|
701
|
+
if (cmd === "sessions") {
|
|
702
|
+
const sessions = await listSessionsOnDisk();
|
|
703
|
+
for (const s of sessions.slice(0, 20)) {
|
|
704
|
+
console.error(`${s.id} msgs=${s.messageCount} ${s.lastUserMessage ?? ""}`);
|
|
705
|
+
}
|
|
706
|
+
return true;
|
|
707
|
+
}
|
|
708
|
+
if (cmd === "search") {
|
|
709
|
+
const { buildIndex, search } = await import("./memory/vector.js");
|
|
710
|
+
const index = await buildIndex();
|
|
711
|
+
const hits = search(index, args2, 5);
|
|
712
|
+
for (const h of hits)
|
|
713
|
+
console.error(`[${h.startedAt}] ${h.sessionId}\n ${h.excerpt}`);
|
|
714
|
+
return true;
|
|
715
|
+
}
|
|
716
|
+
if (cmd === "reflect") {
|
|
717
|
+
await finish();
|
|
718
|
+
return true;
|
|
719
|
+
}
|
|
720
|
+
if (cmd === "think") {
|
|
721
|
+
args.thinking = !args.thinking;
|
|
722
|
+
console.error(`[think=${args.thinking}]`);
|
|
723
|
+
return true;
|
|
724
|
+
}
|
|
725
|
+
if (cmd === "clear") {
|
|
726
|
+
history.length = 0;
|
|
727
|
+
console.error("[history cleared from in-memory; on-disk session preserved]");
|
|
728
|
+
return true;
|
|
729
|
+
}
|
|
730
|
+
if (cmd === "save") {
|
|
731
|
+
const { appendMemory } = await import("./memory/store.js");
|
|
732
|
+
await appendMemory("memory", args2);
|
|
733
|
+
console.error("[saved to MEMORY.md]");
|
|
734
|
+
return true;
|
|
735
|
+
}
|
|
736
|
+
// Plugin slash commands
|
|
737
|
+
for (const p of plugins) {
|
|
738
|
+
const match = p.commands.find((c) => c.name === cmd);
|
|
739
|
+
if (match) {
|
|
740
|
+
const expanded = match.body.replace(/\$ARGUMENTS/g, args2);
|
|
741
|
+
await turn(expanded);
|
|
742
|
+
return true;
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return false;
|
|
746
|
+
},
|
|
747
|
+
onClose: async () => {
|
|
748
|
+
await finish();
|
|
749
|
+
},
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
function renderEvent(event) {
|
|
753
|
+
switch (event.type) {
|
|
754
|
+
case "text_delta":
|
|
755
|
+
if (event.text)
|
|
756
|
+
process.stdout.write(event.text);
|
|
757
|
+
break;
|
|
758
|
+
case "thinking_delta":
|
|
759
|
+
break;
|
|
760
|
+
case "tool_call_start": {
|
|
761
|
+
const inputPreview = JSON.stringify(event.toolInput).slice(0, 120);
|
|
762
|
+
process.stderr.write(`\n[tool ${event.toolName} ${inputPreview}]\n`);
|
|
763
|
+
break;
|
|
764
|
+
}
|
|
765
|
+
case "tool_call_end":
|
|
766
|
+
if (event.isError) {
|
|
767
|
+
process.stderr.write(`[tool ${event.toolName} ✗ ${event.toolResult}]\n`);
|
|
768
|
+
}
|
|
769
|
+
break;
|
|
770
|
+
case "system_prompt_rebuilt":
|
|
771
|
+
process.stderr.write(`[soul] ${event.message}\n`);
|
|
772
|
+
break;
|
|
773
|
+
case "error":
|
|
774
|
+
process.stderr.write(`\n[error] ${event.message}\n`);
|
|
775
|
+
break;
|
|
776
|
+
default:
|
|
777
|
+
break;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Make a hot-reload rebuild closure that maintains its own (fingerprint, text)
|
|
782
|
+
* cache. Called once per session; the agent loop calls it once per turn.
|
|
783
|
+
*
|
|
784
|
+
* Cheap path: if the fingerprint hasn't moved, return the cached text without
|
|
785
|
+
* re-running buildSystemPromptSnapshot (which does several I/O calls). Slow
|
|
786
|
+
* path: rebuild + cache.
|
|
787
|
+
*/
|
|
788
|
+
function makeHotReloadRebuilder(initialFingerprint, initialText) {
|
|
789
|
+
let cachedFp = initialFingerprint;
|
|
790
|
+
let cachedText = initialText;
|
|
791
|
+
return async () => {
|
|
792
|
+
const fp = await getPromptFingerprint();
|
|
793
|
+
if (fp === cachedFp) {
|
|
794
|
+
return { text: cachedText, fingerprint: fp };
|
|
795
|
+
}
|
|
796
|
+
const next = await buildSystemPromptSnapshot();
|
|
797
|
+
cachedFp = fp;
|
|
798
|
+
cachedText = next.text;
|
|
799
|
+
return { text: next.text, fingerprint: fp };
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
// ── Birth ceremony (CLI rendering) ────────────────────────────────────
|
|
803
|
+
const STAR_TOP = " ✦ ✦ ✦ ✦ ✦";
|
|
804
|
+
const STAR_BAR = " ─────────────────────";
|
|
805
|
+
async function runBirthCeremony(model) {
|
|
806
|
+
process.stderr.write(`\n${STAR_TOP}\n${STAR_BAR}\n B I R T H R I T U A L\n${STAR_BAR}\n${STAR_TOP}\n\n`);
|
|
807
|
+
await birth({
|
|
808
|
+
model,
|
|
809
|
+
onStep: async (log) => {
|
|
810
|
+
const pad = log.step.padEnd(15, " ");
|
|
811
|
+
process.stderr.write(` ${pad} ${log.detail}\n`);
|
|
812
|
+
},
|
|
813
|
+
});
|
|
814
|
+
const summary = await readSoulSummary();
|
|
815
|
+
if (summary) {
|
|
816
|
+
process.stderr.write(`\n she chose her name: ${summary.name}\n her purpose:\n${indent(summary.purpose, " ")}\n\n`);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
function printSoulSummary(s) {
|
|
820
|
+
if (!s)
|
|
821
|
+
return;
|
|
822
|
+
console.log(`name: ${s.name}`);
|
|
823
|
+
console.log(`born: ${s.seed.bornAt}`);
|
|
824
|
+
console.log(`big5: O${(s.seed.bigFive.openness * 100 | 0)} C${(s.seed.bigFive.conscientiousness * 100 | 0)} E${(s.seed.bigFive.extraversion * 100 | 0)} A${(s.seed.bigFive.agreeableness * 100 | 0)} N${(s.seed.bigFive.neuroticism * 100 | 0)}`);
|
|
825
|
+
console.log(`\n── identity ──\n${s.identity}`);
|
|
826
|
+
console.log(`\n── purpose ──\n${s.purpose}`);
|
|
827
|
+
console.log(`\n── constitution ──\n${s.constitution}`);
|
|
828
|
+
if (s.values.length)
|
|
829
|
+
console.log(`\n── values (${s.values.length}) ──\n${s.values.map(v => `• ${v.title}`).join("\n")}`);
|
|
830
|
+
if (s.opinions.length)
|
|
831
|
+
console.log(`\n── opinions (${s.opinions.length}) ──\n${s.opinions.map(o => `• ${o.stance} (${o.confidence})`).join("\n")}`);
|
|
832
|
+
if (s.desires.length)
|
|
833
|
+
console.log(`\n── desires (${s.desires.length}) ──\n${s.desires.map(d => `• ${d.what}${d.actionable ? " *" : ""}`).join("\n")}`);
|
|
834
|
+
console.log(`\n── emotions ──`);
|
|
835
|
+
for (const [k, v] of Object.entries(s.emotions.values)) {
|
|
836
|
+
console.log(` ${k.padEnd(14)} ${v.toFixed(2)}`);
|
|
837
|
+
}
|
|
838
|
+
if (s.tampered.length)
|
|
839
|
+
console.log(`\n⚠ tampered files: ${s.tampered.join(", ")}`);
|
|
840
|
+
}
|
|
841
|
+
function indent(text, prefix) {
|
|
842
|
+
return text.split("\n").map((l) => prefix + l).join("\n");
|
|
843
|
+
}
|
|
844
|
+
// ── `lisa skills` subcommand (Phase 3.1) ─────────────────────────────
|
|
845
|
+
async function handleWishlistSubcommand() {
|
|
846
|
+
const { renderWishlist } = await import("./cli/wishlist.js");
|
|
847
|
+
await renderWishlist();
|
|
848
|
+
}
|
|
849
|
+
async function handleSkillsSubcommand(subargs) {
|
|
850
|
+
const sub = subargs[0];
|
|
851
|
+
const skillsMod = await import("./skills/executable.js");
|
|
852
|
+
if (!sub || sub === "list" || sub === "list-executable") {
|
|
853
|
+
const candidates = await skillsMod.discoverExecutableSkills();
|
|
854
|
+
if (candidates.length === 0) {
|
|
855
|
+
console.log("No executable skills found.\n(An executable skill is a ~/.lisa/skills/<slug>/tool.js that exports `tool: ToolDefinition`.)");
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
console.log("Executable skills:");
|
|
859
|
+
for (const c of candidates)
|
|
860
|
+
console.log(skillsMod.summarizeCandidate(c));
|
|
861
|
+
console.log("\nNext: `lisa skills approve <slug>` to review + approve.");
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
const slug = subargs[1];
|
|
865
|
+
if (!slug) {
|
|
866
|
+
console.error(`usage: lisa skills <approve|disable|enable|audit> <slug>`);
|
|
867
|
+
process.exit(2);
|
|
868
|
+
}
|
|
869
|
+
if (sub === "approve") {
|
|
870
|
+
await approveExecutableSkillInteractive(slug);
|
|
871
|
+
return;
|
|
872
|
+
}
|
|
873
|
+
if (sub === "disable") {
|
|
874
|
+
const reason = subargs[2];
|
|
875
|
+
await skillsMod.disableExecutableSkill(slug, reason);
|
|
876
|
+
console.log(`Disabled ${slug}.`);
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
if (sub === "enable") {
|
|
880
|
+
await skillsMod.enableExecutableSkill(slug);
|
|
881
|
+
console.log(`Enabled ${slug}.`);
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
if (sub === "audit") {
|
|
885
|
+
const log = await skillsMod.readAudit(slug);
|
|
886
|
+
console.log(log || "(no audit entries)");
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
console.error(`unknown skills subcommand: ${sub}`);
|
|
890
|
+
process.exit(2);
|
|
891
|
+
}
|
|
892
|
+
async function approveExecutableSkillInteractive(slug) {
|
|
893
|
+
const skillsMod = await import("./skills/executable.js");
|
|
894
|
+
const src = await skillsMod.readToolSource(slug);
|
|
895
|
+
if (!src) {
|
|
896
|
+
console.error(`No tool source found for skill "${slug}".`);
|
|
897
|
+
process.exit(1);
|
|
898
|
+
}
|
|
899
|
+
const candidates = await skillsMod.discoverExecutableSkills();
|
|
900
|
+
const c = candidates.find((x) => x.slug === slug);
|
|
901
|
+
if (!c) {
|
|
902
|
+
console.error(`Skill "${slug}" has no tool.js — cannot approve.`);
|
|
903
|
+
process.exit(1);
|
|
904
|
+
}
|
|
905
|
+
console.log(`\n── ${slug} (sha=${c.currentSha.slice(0, 16)}) ──\n`);
|
|
906
|
+
console.log(src);
|
|
907
|
+
console.log(`\n── end of source ──\n`);
|
|
908
|
+
if (c.approved) {
|
|
909
|
+
console.log(`Previously approved at ${c.approved.approvedAt} (sha=${c.approved.sha256.slice(0, 16)}).`);
|
|
910
|
+
if (c.approved.sha256 !== c.currentSha) {
|
|
911
|
+
console.log(`⚠ Source has changed since approval.`);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
// Try to extract the tool.name without importing (regex on the source) so
|
|
915
|
+
// we can show the user what tool name they're approving. Falls back to slug.
|
|
916
|
+
const nameMatch = /name\s*:\s*["']([^"']+)["']/.exec(src);
|
|
917
|
+
const toolName = nameMatch?.[1] ?? slug;
|
|
918
|
+
console.log(`This tool will be registered as: "${toolName}"`);
|
|
919
|
+
console.log(`It runs in-process with no sandbox. Only approve code you've read.\n`);
|
|
920
|
+
const yes = await promptYesNo(`Approve and load on next launch? [y/N] `);
|
|
921
|
+
if (!yes) {
|
|
922
|
+
console.log("Not approved.");
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
const note = await promptLine(`Optional one-line note (enter to skip): `);
|
|
926
|
+
await skillsMod.approveExecutableSkill(slug, { toolName, note: note.trim() || undefined });
|
|
927
|
+
console.log(`\n✓ Approved ${slug}.`);
|
|
928
|
+
}
|
|
929
|
+
async function promptYesNo(q) {
|
|
930
|
+
const ans = (await promptLine(q)).trim().toLowerCase();
|
|
931
|
+
return ans === "y" || ans === "yes";
|
|
932
|
+
}
|
|
933
|
+
function promptLine(q) {
|
|
934
|
+
process.stdout.write(q);
|
|
935
|
+
return new Promise((resolve) => {
|
|
936
|
+
let data = "";
|
|
937
|
+
const onData = (chunk) => {
|
|
938
|
+
const s = chunk.toString("utf8");
|
|
939
|
+
data += s;
|
|
940
|
+
if (data.includes("\n")) {
|
|
941
|
+
process.stdin.off("data", onData);
|
|
942
|
+
process.stdin.pause();
|
|
943
|
+
resolve(data.replace(/\r?\n.*/s, ""));
|
|
944
|
+
}
|
|
945
|
+
};
|
|
946
|
+
process.stdin.resume();
|
|
947
|
+
process.stdin.on("data", onData);
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
main().catch((err) => {
|
|
951
|
+
console.error(`fatal: ${err.message}`);
|
|
952
|
+
process.exit(1);
|
|
953
|
+
});
|
|
954
|
+
//# sourceMappingURL=cli.js.map
|