@swarmify/agents-cli 1.12.0 → 1.13.1
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/CHANGELOG.md +33 -0
- package/LICENSE +21 -0
- package/README.md +212 -232
- package/dist/commands/__tests__/sessions.test.js +90 -20
- package/dist/commands/__tests__/sessions.test.js.map +1 -1
- package/dist/commands/cloud.d.ts +11 -0
- package/dist/commands/cloud.d.ts.map +1 -0
- package/dist/commands/cloud.js +363 -0
- package/dist/commands/cloud.js.map +1 -0
- package/dist/commands/commands.d.ts +9 -0
- package/dist/commands/commands.d.ts.map +1 -1
- package/dist/commands/commands.js +330 -139
- package/dist/commands/commands.js.map +1 -1
- package/dist/commands/daemon.d.ts +8 -0
- package/dist/commands/daemon.d.ts.map +1 -1
- package/dist/commands/daemon.js +38 -222
- package/dist/commands/daemon.js.map +1 -1
- package/dist/commands/drive.d.ts +8 -0
- package/dist/commands/drive.d.ts.map +1 -1
- package/dist/commands/drive.js +70 -7
- package/dist/commands/drive.js.map +1 -1
- package/dist/commands/exec.d.ts +9 -1
- package/dist/commands/exec.d.ts.map +1 -1
- package/dist/commands/exec.js +198 -24
- package/dist/commands/exec.js.map +1 -1
- package/dist/commands/factory.d.ts +11 -0
- package/dist/commands/factory.d.ts.map +1 -0
- package/dist/commands/factory.js +445 -0
- package/dist/commands/factory.js.map +1 -0
- package/dist/commands/fork.d.ts +8 -0
- package/dist/commands/fork.d.ts.map +1 -1
- package/dist/commands/fork.js +38 -4
- package/dist/commands/fork.js.map +1 -1
- package/dist/commands/hooks.d.ts +9 -0
- package/dist/commands/hooks.d.ts.map +1 -1
- package/dist/commands/hooks.js +252 -18
- package/dist/commands/hooks.js.map +1 -1
- package/dist/commands/init.d.ts +15 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +137 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/mcp.d.ts +9 -0
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +250 -169
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/models.d.ts +11 -0
- package/dist/commands/models.d.ts.map +1 -0
- package/dist/commands/models.js +170 -0
- package/dist/commands/models.js.map +1 -0
- package/dist/commands/packages.d.ts +8 -0
- package/dist/commands/packages.d.ts.map +1 -1
- package/dist/commands/packages.js +155 -14
- package/dist/commands/packages.js.map +1 -1
- package/dist/commands/permissions.d.ts +9 -0
- package/dist/commands/permissions.d.ts.map +1 -1
- package/dist/commands/permissions.js +72 -13
- package/dist/commands/permissions.js.map +1 -1
- package/dist/commands/plugins.d.ts +8 -0
- package/dist/commands/plugins.d.ts.map +1 -1
- package/dist/commands/plugins.js +265 -44
- package/dist/commands/plugins.js.map +1 -1
- package/dist/commands/profiles.d.ts +12 -0
- package/dist/commands/profiles.d.ts.map +1 -0
- package/dist/commands/profiles.js +255 -0
- package/dist/commands/profiles.js.map +1 -0
- package/dist/commands/pty.d.ts +1 -0
- package/dist/commands/pty.d.ts.map +1 -1
- package/dist/commands/pty.js +133 -22
- package/dist/commands/pty.js.map +1 -1
- package/dist/commands/pull.d.ts +8 -0
- package/dist/commands/pull.d.ts.map +1 -1
- package/dist/commands/pull.js +103 -14
- package/dist/commands/pull.js.map +1 -1
- package/dist/commands/push.d.ts +8 -0
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +37 -3
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/refresh-memory.d.ts +16 -0
- package/dist/commands/refresh-memory.d.ts.map +1 -0
- package/dist/commands/refresh-memory.js +52 -0
- package/dist/commands/refresh-memory.js.map +1 -0
- package/dist/commands/resource-view.d.ts +39 -0
- package/dist/commands/resource-view.d.ts.map +1 -0
- package/dist/commands/resource-view.js +197 -0
- package/dist/commands/resource-view.js.map +1 -0
- package/dist/commands/routines.d.ts +8 -0
- package/dist/commands/routines.d.ts.map +1 -1
- package/dist/commands/routines.js +163 -34
- package/dist/commands/routines.js.map +1 -1
- package/dist/commands/rules.d.ts +9 -0
- package/dist/commands/rules.d.ts.map +1 -1
- package/dist/commands/rules.js +83 -12
- package/dist/commands/rules.js.map +1 -1
- package/dist/commands/secrets.d.ts +11 -0
- package/dist/commands/secrets.d.ts.map +1 -0
- package/dist/commands/secrets.js +352 -0
- package/dist/commands/secrets.js.map +1 -0
- package/dist/commands/sessions-picker.d.ts +18 -0
- package/dist/commands/sessions-picker.d.ts.map +1 -0
- package/dist/commands/sessions-picker.js +265 -0
- package/dist/commands/sessions-picker.js.map +1 -0
- package/dist/commands/sessions.d.ts +15 -0
- package/dist/commands/sessions.d.ts.map +1 -1
- package/dist/commands/sessions.js +700 -263
- package/dist/commands/sessions.js.map +1 -1
- package/dist/commands/skills.d.ts +9 -0
- package/dist/commands/skills.d.ts.map +1 -1
- package/dist/commands/skills.js +355 -233
- package/dist/commands/skills.js.map +1 -1
- package/dist/commands/status.d.ts +7 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +7 -0
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/subagents.d.ts +8 -0
- package/dist/commands/subagents.d.ts.map +1 -1
- package/dist/commands/subagents.js +214 -74
- package/dist/commands/subagents.js.map +1 -1
- package/dist/commands/sync.d.ts +8 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +15 -7
- package/dist/commands/sync.js.map +1 -1
- package/dist/commands/teams-picker.d.ts +18 -0
- package/dist/commands/teams-picker.d.ts.map +1 -0
- package/dist/commands/teams-picker.js +290 -0
- package/dist/commands/teams-picker.js.map +1 -0
- package/dist/commands/teams.d.ts +18 -0
- package/dist/commands/teams.d.ts.map +1 -0
- package/dist/commands/teams.js +1098 -0
- package/dist/commands/teams.js.map +1 -0
- package/dist/commands/utils.d.ts +20 -0
- package/dist/commands/utils.d.ts.map +1 -1
- package/dist/commands/utils.js +34 -0
- package/dist/commands/utils.js.map +1 -1
- package/dist/commands/versions.d.ts +8 -0
- package/dist/commands/versions.d.ts.map +1 -1
- package/dist/commands/versions.js +71 -8
- package/dist/commands/versions.js.map +1 -1
- package/dist/commands/view.d.ts +36 -1
- package/dist/commands/view.d.ts.map +1 -1
- package/dist/commands/view.js +375 -15
- package/dist/commands/view.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +136 -51
- package/dist/index.js.map +1 -1
- package/dist/lib/__tests__/bugfixes.test.js +3 -3
- package/dist/lib/__tests__/bugfixes.test.js.map +1 -1
- package/dist/lib/__tests__/exec.test.js +125 -19
- package/dist/lib/__tests__/exec.test.js.map +1 -1
- package/dist/lib/__tests__/hooks.test.d.ts +2 -0
- package/dist/lib/__tests__/hooks.test.d.ts.map +1 -0
- package/dist/lib/__tests__/hooks.test.js +203 -0
- package/dist/lib/__tests__/hooks.test.js.map +1 -0
- package/dist/lib/__tests__/memory-compile.test.d.ts +2 -0
- package/dist/lib/__tests__/memory-compile.test.d.ts.map +1 -0
- package/dist/lib/__tests__/memory-compile.test.js +95 -0
- package/dist/lib/__tests__/memory-compile.test.js.map +1 -0
- package/dist/lib/__tests__/models.test.d.ts +2 -0
- package/dist/lib/__tests__/models.test.d.ts.map +1 -0
- package/dist/lib/__tests__/models.test.js +239 -0
- package/dist/lib/__tests__/models.test.js.map +1 -0
- package/dist/lib/__tests__/rotate.test.d.ts +2 -0
- package/dist/lib/__tests__/rotate.test.d.ts.map +1 -0
- package/dist/lib/__tests__/rotate.test.js +80 -0
- package/dist/lib/__tests__/rotate.test.js.map +1 -0
- package/dist/lib/__tests__/secrets-bundles.test.d.ts +2 -0
- package/dist/lib/__tests__/secrets-bundles.test.d.ts.map +1 -0
- package/dist/lib/__tests__/secrets-bundles.test.js +104 -0
- package/dist/lib/__tests__/secrets-bundles.test.js.map +1 -0
- package/dist/lib/__tests__/secrets.test.d.ts +2 -0
- package/dist/lib/__tests__/secrets.test.d.ts.map +1 -0
- package/dist/lib/__tests__/secrets.test.js +90 -0
- package/dist/lib/__tests__/secrets.test.js.map +1 -0
- package/dist/lib/__tests__/shims.test.d.ts +2 -0
- package/dist/lib/__tests__/shims.test.d.ts.map +1 -0
- package/dist/lib/__tests__/shims.test.js +39 -0
- package/dist/lib/__tests__/shims.test.js.map +1 -0
- package/dist/lib/__tests__/usage.test.js +4 -2
- package/dist/lib/__tests__/usage.test.js.map +1 -1
- package/dist/lib/__tests__/versions.test.d.ts +2 -0
- package/dist/lib/__tests__/versions.test.d.ts.map +1 -0
- package/dist/lib/__tests__/versions.test.js +63 -0
- package/dist/lib/__tests__/versions.test.js.map +1 -0
- package/dist/lib/agents.d.ts +53 -1
- package/dist/lib/agents.d.ts.map +1 -1
- package/dist/lib/agents.js +178 -37
- package/dist/lib/agents.js.map +1 -1
- package/dist/lib/artifact-actions.d.ts +8 -3
- package/dist/lib/artifact-actions.d.ts.map +1 -1
- package/dist/lib/artifact-actions.js +8 -5
- package/dist/lib/artifact-actions.js.map +1 -1
- package/dist/lib/cloud/codex.d.ts +26 -0
- package/dist/lib/cloud/codex.d.ts.map +1 -0
- package/dist/lib/cloud/codex.js +237 -0
- package/dist/lib/cloud/codex.js.map +1 -0
- package/dist/lib/cloud/factory.d.ts +32 -0
- package/dist/lib/cloud/factory.d.ts.map +1 -0
- package/dist/lib/cloud/factory.js +43 -0
- package/dist/lib/cloud/factory.js.map +1 -0
- package/dist/lib/cloud/registry.d.ts +16 -0
- package/dist/lib/cloud/registry.d.ts.map +1 -0
- package/dist/lib/cloud/registry.js +68 -0
- package/dist/lib/cloud/registry.js.map +1 -0
- package/dist/lib/cloud/rush.d.ts +37 -0
- package/dist/lib/cloud/rush.d.ts.map +1 -0
- package/dist/lib/cloud/rush.js +230 -0
- package/dist/lib/cloud/rush.js.map +1 -0
- package/dist/lib/cloud/rush.test.d.ts +2 -0
- package/dist/lib/cloud/rush.test.d.ts.map +1 -0
- package/dist/lib/cloud/rush.test.js +63 -0
- package/dist/lib/cloud/rush.test.js.map +1 -0
- package/dist/lib/cloud/store.d.ts +23 -0
- package/dist/lib/cloud/store.d.ts.map +1 -0
- package/dist/lib/cloud/store.js +116 -0
- package/dist/lib/cloud/store.js.map +1 -0
- package/dist/lib/cloud/stream.d.ts +24 -0
- package/dist/lib/cloud/stream.d.ts.map +1 -0
- package/dist/lib/cloud/stream.js +145 -0
- package/dist/lib/cloud/stream.js.map +1 -0
- package/dist/lib/cloud/types.d.ts +109 -0
- package/dist/lib/cloud/types.d.ts.map +1 -0
- package/dist/lib/cloud/types.js +33 -0
- package/dist/lib/cloud/types.js.map +1 -0
- package/dist/lib/cloud/types.test.d.ts +2 -0
- package/dist/lib/cloud/types.test.d.ts.map +1 -0
- package/dist/lib/cloud/types.test.js +41 -0
- package/dist/lib/cloud/types.test.js.map +1 -0
- package/dist/lib/commands.d.ts +67 -0
- package/dist/lib/commands.d.ts.map +1 -1
- package/dist/lib/commands.js +161 -2
- package/dist/lib/commands.js.map +1 -1
- package/dist/lib/convert.d.ts +10 -0
- package/dist/lib/convert.d.ts.map +1 -1
- package/dist/lib/convert.js +9 -0
- package/dist/lib/convert.js.map +1 -1
- package/dist/lib/daemon.d.ts +21 -0
- package/dist/lib/daemon.d.ts.map +1 -1
- package/dist/lib/daemon.js +21 -0
- package/dist/lib/daemon.js.map +1 -1
- package/dist/lib/drive-sync.d.ts +18 -0
- package/dist/lib/drive-sync.d.ts.map +1 -1
- package/dist/lib/drive-sync.js +16 -0
- package/dist/lib/drive-sync.js.map +1 -1
- package/dist/lib/exec.d.ts +64 -3
- package/dist/lib/exec.d.ts.map +1 -1
- package/dist/lib/exec.js +218 -80
- package/dist/lib/exec.js.map +1 -1
- package/dist/lib/factory/__tests__/config.test.d.ts +2 -0
- package/dist/lib/factory/__tests__/config.test.d.ts.map +1 -0
- package/dist/lib/factory/__tests__/config.test.js +128 -0
- package/dist/lib/factory/__tests__/config.test.js.map +1 -0
- package/dist/lib/factory/config.d.ts +49 -0
- package/dist/lib/factory/config.d.ts.map +1 -0
- package/dist/lib/factory/config.js +127 -0
- package/dist/lib/factory/config.js.map +1 -0
- package/dist/lib/factory.js +1 -1
- package/dist/lib/factory.js.map +1 -1
- package/dist/lib/git.d.ts +16 -1
- package/dist/lib/git.d.ts.map +1 -1
- package/dist/lib/git.js +33 -4
- package/dist/lib/git.js.map +1 -1
- package/dist/lib/help.d.ts +7 -0
- package/dist/lib/help.d.ts.map +1 -1
- package/dist/lib/help.js +3 -0
- package/dist/lib/help.js.map +1 -1
- package/dist/lib/hooks.d.ts +59 -3
- package/dist/lib/hooks.d.ts.map +1 -1
- package/dist/lib/hooks.js +413 -33
- package/dist/lib/hooks.js.map +1 -1
- package/dist/lib/ledger/__tests__/local.test.d.ts +2 -0
- package/dist/lib/ledger/__tests__/local.test.d.ts.map +1 -0
- package/dist/lib/ledger/__tests__/local.test.js +177 -0
- package/dist/lib/ledger/__tests__/local.test.js.map +1 -0
- package/dist/lib/ledger/__tests__/sync.test.d.ts +2 -0
- package/dist/lib/ledger/__tests__/sync.test.d.ts.map +1 -0
- package/dist/lib/ledger/__tests__/sync.test.js +117 -0
- package/dist/lib/ledger/__tests__/sync.test.js.map +1 -0
- package/dist/lib/ledger/index.d.ts +18 -0
- package/dist/lib/ledger/index.d.ts.map +1 -0
- package/dist/lib/ledger/index.js +32 -0
- package/dist/lib/ledger/index.js.map +1 -0
- package/dist/lib/ledger/local.d.ts +22 -0
- package/dist/lib/ledger/local.d.ts.map +1 -0
- package/dist/lib/ledger/local.js +333 -0
- package/dist/lib/ledger/local.js.map +1 -0
- package/dist/lib/ledger/r2.d.ts +41 -0
- package/dist/lib/ledger/r2.d.ts.map +1 -0
- package/dist/lib/ledger/r2.js +335 -0
- package/dist/lib/ledger/r2.js.map +1 -0
- package/dist/lib/ledger/sync.d.ts +33 -0
- package/dist/lib/ledger/sync.d.ts.map +1 -0
- package/dist/lib/ledger/sync.js +106 -0
- package/dist/lib/ledger/sync.js.map +1 -0
- package/dist/lib/ledger/types.d.ts +100 -0
- package/dist/lib/ledger/types.d.ts.map +1 -0
- package/dist/lib/ledger/types.js +21 -0
- package/dist/lib/ledger/types.js.map +1 -0
- package/dist/lib/manifest.d.ts +6 -0
- package/dist/lib/manifest.d.ts.map +1 -1
- package/dist/lib/manifest.js +12 -0
- package/dist/lib/manifest.js.map +1 -1
- package/dist/lib/markdown.d.ts.map +1 -1
- package/dist/lib/markdown.js +6 -0
- package/dist/lib/markdown.js.map +1 -1
- package/dist/lib/mcp.d.ts +0 -9
- package/dist/lib/mcp.d.ts.map +1 -1
- package/dist/lib/mcp.js +0 -20
- package/dist/lib/mcp.js.map +1 -1
- package/dist/lib/memory-compile.d.ts +65 -0
- package/dist/lib/memory-compile.d.ts.map +1 -0
- package/dist/lib/memory-compile.js +174 -0
- package/dist/lib/memory-compile.js.map +1 -0
- package/dist/lib/memory.d.ts +8 -0
- package/dist/lib/memory.d.ts.map +1 -1
- package/dist/lib/memory.js +8 -0
- package/dist/lib/memory.js.map +1 -1
- package/dist/lib/models.d.ts +98 -0
- package/dist/lib/models.d.ts.map +1 -0
- package/dist/lib/models.js +728 -0
- package/dist/lib/models.js.map +1 -0
- package/dist/lib/permissions.d.ts +24 -1
- package/dist/lib/permissions.d.ts.map +1 -1
- package/dist/lib/permissions.js +52 -3
- package/dist/lib/permissions.js.map +1 -1
- package/dist/lib/picker.d.ts +27 -0
- package/dist/lib/picker.d.ts.map +1 -0
- package/dist/lib/picker.js +110 -0
- package/dist/lib/picker.js.map +1 -0
- package/dist/lib/plugins.d.ts +22 -0
- package/dist/lib/plugins.d.ts.map +1 -1
- package/dist/lib/plugins.js +114 -0
- package/dist/lib/plugins.js.map +1 -1
- package/dist/lib/profiles-keychain.d.ts +11 -0
- package/dist/lib/profiles-keychain.d.ts.map +1 -0
- package/dist/lib/profiles-keychain.js +14 -0
- package/dist/lib/profiles-keychain.js.map +1 -0
- package/dist/lib/profiles-presets.d.ts +25 -0
- package/dist/lib/profiles-presets.d.ts.map +1 -0
- package/dist/lib/profiles-presets.js +104 -0
- package/dist/lib/profiles-presets.js.map +1 -0
- package/dist/lib/profiles.d.ts +70 -0
- package/dist/lib/profiles.d.ts.map +1 -0
- package/dist/lib/profiles.js +145 -0
- package/dist/lib/profiles.js.map +1 -0
- package/dist/lib/pty-client.d.ts +1 -0
- package/dist/lib/pty-client.d.ts.map +1 -1
- package/dist/lib/pty-client.js.map +1 -1
- package/dist/lib/pty-server.d.ts +5 -0
- package/dist/lib/pty-server.d.ts.map +1 -1
- package/dist/lib/pty-server.js +5 -0
- package/dist/lib/pty-server.js.map +1 -1
- package/dist/lib/registry.d.ts +17 -0
- package/dist/lib/registry.d.ts.map +1 -1
- package/dist/lib/registry.js +17 -0
- package/dist/lib/registry.js.map +1 -1
- package/dist/lib/resources.d.ts +5 -0
- package/dist/lib/resources.d.ts.map +1 -1
- package/dist/lib/resources.js.map +1 -1
- package/dist/lib/rotate.d.ts +58 -0
- package/dist/lib/rotate.d.ts.map +1 -0
- package/dist/lib/rotate.js +93 -0
- package/dist/lib/rotate.js.map +1 -0
- package/dist/lib/routines.d.ts +30 -1
- package/dist/lib/routines.d.ts.map +1 -1
- package/dist/lib/routines.js +31 -4
- package/dist/lib/routines.js.map +1 -1
- package/dist/lib/runner.d.ts +14 -0
- package/dist/lib/runner.d.ts.map +1 -1
- package/dist/lib/runner.js +45 -11
- package/dist/lib/runner.js.map +1 -1
- package/dist/lib/sandbox.d.ts +16 -0
- package/dist/lib/sandbox.d.ts.map +1 -1
- package/dist/lib/sandbox.js +19 -2
- package/dist/lib/sandbox.js.map +1 -1
- package/dist/lib/scheduler.d.ts +8 -0
- package/dist/lib/scheduler.d.ts.map +1 -1
- package/dist/lib/scheduler.js +8 -0
- package/dist/lib/scheduler.js.map +1 -1
- package/dist/lib/secrets-bundles.d.ts +38 -0
- package/dist/lib/secrets-bundles.d.ts.map +1 -0
- package/dist/lib/secrets-bundles.js +176 -0
- package/dist/lib/secrets-bundles.js.map +1 -0
- package/dist/lib/secrets.d.ts +53 -0
- package/dist/lib/secrets.d.ts.map +1 -0
- package/dist/lib/secrets.js +140 -0
- package/dist/lib/secrets.js.map +1 -0
- package/dist/lib/session/__tests__/db.test.d.ts +2 -0
- package/dist/lib/session/__tests__/db.test.d.ts.map +1 -0
- package/dist/lib/session/__tests__/db.test.js +54 -0
- package/dist/lib/session/__tests__/db.test.js.map +1 -0
- package/dist/lib/session/__tests__/discover.test.js +54 -91
- package/dist/lib/session/__tests__/discover.test.js.map +1 -1
- package/dist/lib/session/__tests__/prompt.test.d.ts +2 -0
- package/dist/lib/session/__tests__/prompt.test.d.ts.map +1 -0
- package/dist/lib/session/__tests__/prompt.test.js +44 -0
- package/dist/lib/session/__tests__/prompt.test.js.map +1 -0
- package/dist/lib/session/__tests__/render.test.d.ts +2 -0
- package/dist/lib/session/__tests__/render.test.d.ts.map +1 -0
- package/dist/lib/session/__tests__/render.test.js +602 -0
- package/dist/lib/session/__tests__/render.test.js.map +1 -0
- package/dist/lib/session/artifacts.d.ts +15 -0
- package/dist/lib/session/artifacts.d.ts.map +1 -0
- package/dist/lib/session/artifacts.js +86 -0
- package/dist/lib/session/artifacts.js.map +1 -0
- package/dist/lib/session/db.d.ts +140 -0
- package/dist/lib/session/db.d.ts.map +1 -0
- package/dist/lib/session/db.js +599 -0
- package/dist/lib/session/db.js.map +1 -0
- package/dist/lib/session/discover.d.ts +44 -32
- package/dist/lib/session/discover.d.ts.map +1 -1
- package/dist/lib/session/discover.js +418 -464
- package/dist/lib/session/discover.js.map +1 -1
- package/dist/lib/session/parse.d.ts +11 -0
- package/dist/lib/session/parse.d.ts.map +1 -1
- package/dist/lib/session/parse.js +50 -0
- package/dist/lib/session/parse.js.map +1 -1
- package/dist/lib/session/prompt.d.ts +10 -0
- package/dist/lib/session/prompt.d.ts.map +1 -1
- package/dist/lib/session/prompt.js +38 -2
- package/dist/lib/session/prompt.js.map +1 -1
- package/dist/lib/session/prompt.test.d.ts +2 -0
- package/dist/lib/session/prompt.test.d.ts.map +1 -0
- package/dist/lib/session/prompt.test.js +57 -0
- package/dist/lib/session/prompt.test.js.map +1 -0
- package/dist/lib/session/render.d.ts +91 -10
- package/dist/lib/session/render.d.ts.map +1 -1
- package/dist/lib/session/render.js +708 -180
- package/dist/lib/session/render.js.map +1 -1
- package/dist/lib/session/team-filter.d.ts +35 -0
- package/dist/lib/session/team-filter.d.ts.map +1 -0
- package/dist/lib/session/team-filter.js +75 -0
- package/dist/lib/session/team-filter.js.map +1 -0
- package/dist/lib/session/team-filter.test.d.ts +2 -0
- package/dist/lib/session/team-filter.test.d.ts.map +1 -0
- package/dist/lib/session/team-filter.test.js +157 -0
- package/dist/lib/session/team-filter.test.js.map +1 -0
- package/dist/lib/session/types.d.ts +48 -6
- package/dist/lib/session/types.d.ts.map +1 -1
- package/dist/lib/session/types.js +9 -0
- package/dist/lib/session/types.js.map +1 -1
- package/dist/lib/shims.d.ts +101 -2
- package/dist/lib/shims.d.ts.map +1 -1
- package/dist/lib/shims.js +282 -25
- package/dist/lib/shims.js.map +1 -1
- package/dist/lib/skills.d.ts +68 -0
- package/dist/lib/skills.d.ts.map +1 -1
- package/dist/lib/skills.js +267 -1
- package/dist/lib/skills.js.map +1 -1
- package/dist/lib/state.d.ts +41 -2
- package/dist/lib/state.d.ts.map +1 -1
- package/dist/lib/state.js +63 -4
- package/dist/lib/state.js.map +1 -1
- package/dist/lib/subagents.d.ts +9 -0
- package/dist/lib/subagents.d.ts.map +1 -1
- package/dist/lib/subagents.js +9 -1
- package/dist/lib/subagents.js.map +1 -1
- package/dist/lib/teams/__tests__/oracle.test.d.ts +2 -0
- package/dist/lib/teams/__tests__/oracle.test.d.ts.map +1 -0
- package/dist/lib/teams/__tests__/oracle.test.js +89 -0
- package/dist/lib/teams/__tests__/oracle.test.js.map +1 -0
- package/dist/lib/teams/__tests__/supervisor.test.d.ts +2 -0
- package/dist/lib/teams/__tests__/supervisor.test.d.ts.map +1 -0
- package/dist/lib/teams/__tests__/supervisor.test.js +179 -0
- package/dist/lib/teams/__tests__/supervisor.test.js.map +1 -0
- package/dist/lib/teams/agents.d.ts +247 -0
- package/dist/lib/teams/agents.d.ts.map +1 -0
- package/dist/lib/teams/agents.js +1244 -0
- package/dist/lib/teams/agents.js.map +1 -0
- package/dist/lib/teams/api.d.ts +91 -0
- package/dist/lib/teams/api.d.ts.map +1 -0
- package/dist/lib/teams/api.js +239 -0
- package/dist/lib/teams/api.js.map +1 -0
- package/dist/lib/teams/cloud.d.ts +11 -0
- package/dist/lib/teams/cloud.d.ts.map +1 -0
- package/dist/lib/teams/cloud.js +169 -0
- package/dist/lib/teams/cloud.js.map +1 -0
- package/dist/lib/teams/debug.d.ts +8 -0
- package/dist/lib/teams/debug.d.ts.map +1 -0
- package/dist/lib/teams/debug.js +12 -0
- package/dist/lib/teams/debug.js.map +1 -0
- package/dist/lib/teams/file_ops.d.ts +13 -0
- package/dist/lib/teams/file_ops.d.ts.map +1 -0
- package/dist/lib/teams/file_ops.js +66 -0
- package/dist/lib/teams/file_ops.js.map +1 -0
- package/dist/lib/teams/index.d.ts +16 -0
- package/dist/lib/teams/index.d.ts.map +1 -0
- package/dist/lib/teams/index.js +15 -0
- package/dist/lib/teams/index.js.map +1 -0
- package/dist/lib/teams/oracle.d.ts +20 -0
- package/dist/lib/teams/oracle.d.ts.map +1 -0
- package/dist/lib/teams/oracle.js +59 -0
- package/dist/lib/teams/oracle.js.map +1 -0
- package/dist/lib/teams/parsers.d.ts +9 -0
- package/dist/lib/teams/parsers.d.ts.map +1 -0
- package/dist/lib/teams/parsers.js +837 -0
- package/dist/lib/teams/parsers.js.map +1 -0
- package/dist/lib/teams/persistence.d.ts +43 -0
- package/dist/lib/teams/persistence.d.ts.map +1 -0
- package/dist/lib/teams/persistence.js +299 -0
- package/dist/lib/teams/persistence.js.map +1 -0
- package/dist/lib/teams/ralph.d.ts +8 -0
- package/dist/lib/teams/ralph.d.ts.map +1 -0
- package/dist/lib/teams/ralph.js +59 -0
- package/dist/lib/teams/ralph.js.map +1 -0
- package/dist/lib/teams/registry.d.ts +18 -0
- package/dist/lib/teams/registry.d.ts.map +1 -0
- package/dist/lib/teams/registry.js +68 -0
- package/dist/lib/teams/registry.js.map +1 -0
- package/dist/lib/teams/summarizer.d.ts +73 -0
- package/dist/lib/teams/summarizer.d.ts.map +1 -0
- package/dist/lib/teams/summarizer.js +780 -0
- package/dist/lib/teams/summarizer.js.map +1 -0
- package/dist/lib/teams/supervisor.d.ts +49 -0
- package/dist/lib/teams/supervisor.d.ts.map +1 -0
- package/dist/lib/teams/supervisor.js +74 -0
- package/dist/lib/teams/supervisor.js.map +1 -0
- package/dist/lib/template.d.ts +8 -5
- package/dist/lib/template.d.ts.map +1 -1
- package/dist/lib/template.js +8 -5
- package/dist/lib/template.js.map +1 -1
- package/dist/lib/types.d.ts +58 -1
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js +16 -1
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/usage.d.ts +48 -0
- package/dist/lib/usage.d.ts.map +1 -1
- package/dist/lib/usage.js +106 -14
- package/dist/lib/usage.js.map +1 -1
- package/dist/lib/versions.d.ts +12 -1
- package/dist/lib/versions.d.ts.map +1 -1
- package/dist/lib/versions.js +124 -41
- package/dist/lib/versions.js.map +1 -1
- package/package.json +20 -6
- package/scripts/postinstall.js +1 -1
- package/scripts/rebuild-sqlite.sh +46 -0
- package/dist/commands/sessions.test.d.ts +0 -2
- package/dist/commands/sessions.test.d.ts.map +0 -1
- package/dist/commands/sessions.test.js +0 -53
- package/dist/commands/sessions.test.js.map +0 -1
|
@@ -1,88 +1,354 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session discovery, search, and rendering commands.
|
|
3
|
+
*
|
|
4
|
+
* Implements `agents sessions` -- the unified interface for finding, browsing,
|
|
5
|
+
* and reading agent conversation transcripts across Claude, Codex, Gemini,
|
|
6
|
+
* and OpenCode. Supports interactive picker mode, text/path search, markdown
|
|
7
|
+
* and JSON rendering, role/turn filtering, artifact inspection, and session
|
|
8
|
+
* resume via agent-native CLI flags.
|
|
9
|
+
*/
|
|
1
10
|
import * as fs from 'fs';
|
|
2
11
|
import * as os from 'os';
|
|
3
12
|
import * as path from 'path';
|
|
13
|
+
import { spawn } from 'child_process';
|
|
4
14
|
import chalk from 'chalk';
|
|
5
15
|
import ora from 'ora';
|
|
6
|
-
import { search } from '@inquirer/prompts';
|
|
7
16
|
import { SESSION_AGENTS } from '../lib/session/types.js';
|
|
8
|
-
import {
|
|
17
|
+
import { discoverArtifacts, readArtifact, resolveArtifact } from '../lib/session/artifacts.js';
|
|
18
|
+
import { discoverSessions, countSessionsInScope, resolveSessionById, searchContentIndex } from '../lib/session/discover.js';
|
|
19
|
+
import { filterTeamSessions } from '../lib/session/team-filter.js';
|
|
9
20
|
import { parseSession } from '../lib/session/parse.js';
|
|
10
|
-
import {
|
|
21
|
+
import { renderConversationMarkdown, renderSummary, renderSummaryHeader, computeSummaryStats, renderJson, filterEvents, parseRoleList } from '../lib/session/render.js';
|
|
11
22
|
import { renderMarkdown } from '../lib/markdown.js';
|
|
12
23
|
import { colorAgent } from '../lib/agents.js';
|
|
13
|
-
import {
|
|
14
|
-
|
|
24
|
+
import { resolveVersion } from '../lib/versions.js';
|
|
25
|
+
import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
|
|
26
|
+
import { sessionPicker } from './sessions-picker.js';
|
|
27
|
+
const SESSION_AGENT_FILTER_HELP = `Filter by agent, e.g. claude, codex, claude@2.0.65`;
|
|
15
28
|
const CLAUDE_RESUME_MATCH_WINDOW_MS = 10 * 60_000;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
29
|
+
const LOAD_VERBS = ['Loading', 'Scanning', 'Gathering', 'Indexing', 'Reading'];
|
|
30
|
+
const FIND_VERBS = ['Finding', 'Searching', 'Locating', 'Matching'];
|
|
31
|
+
/** Build a spinner-backed progress tracker that cycles through verbs while scanning sessions. */
|
|
32
|
+
function createScanProgressTracker(verbs, suffix, spinner) {
|
|
33
|
+
const counts = new Map();
|
|
34
|
+
let verbIndex = 0;
|
|
35
|
+
const render = () => {
|
|
36
|
+
if (!spinner)
|
|
37
|
+
return;
|
|
38
|
+
const verb = verbs[verbIndex % verbs.length];
|
|
39
|
+
const parts = [];
|
|
40
|
+
for (const agent of SESSION_AGENTS) {
|
|
41
|
+
const c = counts.get(agent);
|
|
42
|
+
if (!c || c.total === 0)
|
|
43
|
+
continue;
|
|
44
|
+
parts.push(`${agent} ${c.parsed}/${c.total}`);
|
|
45
|
+
}
|
|
46
|
+
const base = `${verb} ${suffix}...`;
|
|
47
|
+
spinner.text = parts.length > 0 ? `${base} (${parts.join(' · ')})` : base;
|
|
48
|
+
};
|
|
49
|
+
const interval = spinner
|
|
50
|
+
? setInterval(() => {
|
|
51
|
+
verbIndex++;
|
|
52
|
+
render();
|
|
53
|
+
}, 900)
|
|
54
|
+
: null;
|
|
55
|
+
render();
|
|
56
|
+
return {
|
|
57
|
+
onProgress: (progress) => {
|
|
58
|
+
counts.set(progress.agent, { parsed: progress.parsed, total: progress.total });
|
|
59
|
+
render();
|
|
60
|
+
},
|
|
61
|
+
stop: () => {
|
|
62
|
+
if (interval)
|
|
63
|
+
clearInterval(interval);
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const PICKER_RECENT_COUNT = 15;
|
|
68
|
+
const PICKER_POOL_LIMIT = 200;
|
|
69
|
+
/**
|
|
70
|
+
* Detect whether a positional argument looks like a filesystem path.
|
|
71
|
+
* Naked paths (., ./, ../, /, ~) filter sessions by project directory.
|
|
72
|
+
* Everything else is treated as a search query string.
|
|
73
|
+
*/
|
|
74
|
+
function isPathLike(query) {
|
|
75
|
+
return query === '.' || query.startsWith('./') || query.startsWith('../')
|
|
76
|
+
|| query.startsWith('/') || query.startsWith('~');
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Resolve a path-like query to an absolute directory path.
|
|
80
|
+
*/
|
|
81
|
+
function resolvePathFilter(query) {
|
|
82
|
+
const expanded = query.startsWith('~')
|
|
83
|
+
? path.join(os.homedir(), query.slice(1))
|
|
84
|
+
: query;
|
|
85
|
+
return path.resolve(expanded);
|
|
86
|
+
}
|
|
87
|
+
function formatBytes(n) {
|
|
88
|
+
if (n < 1024)
|
|
89
|
+
return `${n} B`;
|
|
90
|
+
if (n < 1024 * 1024)
|
|
91
|
+
return `${(n / 1024).toFixed(1)} KB`;
|
|
92
|
+
return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
|
93
|
+
}
|
|
94
|
+
async function renderArtifactsForSession(session, listAll, name) {
|
|
95
|
+
const artifacts = discoverArtifacts(session);
|
|
96
|
+
if (name !== undefined) {
|
|
97
|
+
const artifact = resolveArtifact(artifacts, name);
|
|
98
|
+
if (!artifact) {
|
|
99
|
+
console.error(chalk.red(`No artifact matching "${name}" in session ${session.shortId}.`));
|
|
100
|
+
if (artifacts.length > 0) {
|
|
101
|
+
console.error(chalk.gray('Available artifacts:'));
|
|
102
|
+
for (const a of artifacts) {
|
|
103
|
+
console.error(chalk.gray(` ${a.path}`));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
if (!artifact.exists) {
|
|
109
|
+
console.error(chalk.red(`Artifact exists in session history but the file is no longer on disk: ${artifact.path}`));
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
process.stdout.write(readArtifact(artifact));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (artifacts.length === 0) {
|
|
116
|
+
console.log(chalk.gray('No file-write artifacts found in this session.'));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const agentColor = colorAgent(session.agent);
|
|
120
|
+
console.log('');
|
|
121
|
+
console.log(agentColor(session.agent) +
|
|
122
|
+
chalk.gray(` · ${session.shortId} · ${formatRelativeTime(session.timestamp)}`));
|
|
123
|
+
console.log(chalk.gray('─'.repeat(72)));
|
|
124
|
+
for (const a of artifacts) {
|
|
125
|
+
const exists = a.exists ? chalk.green('yes') : chalk.red('no');
|
|
126
|
+
const size = a.exists && a.sizeBytes !== undefined ? chalk.cyan(formatBytes(a.sizeBytes)) : chalk.gray('-');
|
|
127
|
+
const tool = chalk.yellow(padRight(a.tool, 10));
|
|
128
|
+
const when = chalk.gray(formatRelativeTime(a.timestamp));
|
|
129
|
+
const p = chalk.white(a.path);
|
|
130
|
+
console.log(` ${exists} ${size.padEnd(10)} ${tool} ${when.padEnd(16)} ${p}`);
|
|
131
|
+
}
|
|
132
|
+
console.log(chalk.gray(`\n${artifacts.length} artifact${artifacts.length !== 1 ? 's' : ''}.`));
|
|
133
|
+
}
|
|
134
|
+
/** Main action handler for `agents sessions`. Routes to picker, table, or single-session render. */
|
|
135
|
+
async function sessionsAction(query, options) {
|
|
136
|
+
let filterOpts;
|
|
137
|
+
try {
|
|
138
|
+
filterOpts = buildFilterOptions(options);
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
console.error(chalk.red(err.message));
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
const { agent, version } = parseAgentFilter(options.agent);
|
|
145
|
+
// Path-like queries filter by project directory instead of text search.
|
|
146
|
+
let pathFilter;
|
|
147
|
+
let searchQuery;
|
|
148
|
+
if (query && isPathLike(query)) {
|
|
149
|
+
const resolved = resolvePathFilter(query);
|
|
150
|
+
if (!fs.existsSync(resolved)) {
|
|
151
|
+
console.log(chalk.yellow(`Path not found: ${resolved}`));
|
|
152
|
+
console.log(chalk.gray('Did you mean to search? Use quotes: agents sessions "' + query + '"'));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
pathFilter = fs.realpathSync(resolved);
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
searchQuery = query;
|
|
159
|
+
}
|
|
160
|
+
// Artifact flags require a session query.
|
|
161
|
+
if ((options.artifacts || options.artifact !== undefined) && !query) {
|
|
162
|
+
console.error(chalk.red('--artifacts and --artifact require a session ID or query.'));
|
|
163
|
+
process.exit(1);
|
|
164
|
+
}
|
|
165
|
+
const mode = resolveViewMode(options, filterOpts);
|
|
166
|
+
// --markdown or any filter flag forces single-session render.
|
|
167
|
+
const wantsRender = mode === 'markdown' || hasAnyFilter(filterOpts);
|
|
168
|
+
// Artifact-list or artifact-read paths: widen scope and resolve session globally.
|
|
169
|
+
if ((options.artifacts || options.artifact !== undefined) && searchQuery) {
|
|
170
|
+
await renderArtifactsGlobal(searchQuery, options.artifacts ?? false, options.artifact, { agent: options.agent, project: options.project });
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
// When the user explicitly asks to render (via mode flag), resolve the
|
|
174
|
+
// query globally so sessions outside the default cwd/30d window are found.
|
|
175
|
+
if (wantsRender && searchQuery) {
|
|
176
|
+
await renderOneSession(searchQuery, mode, { agent: options.agent, project: options.project, filter: filterOpts });
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
// Interactive picker loads a deep pool but shows only recent sessions
|
|
180
|
+
// until the user starts typing. Non-interactive/JSON uses the explicit limit.
|
|
181
|
+
const isInteractive = !options.json && isInteractiveTerminal();
|
|
182
|
+
const limit = parseInt(options.limit || (isInteractive ? String(PICKER_POOL_LIMIT) : '50'), 10);
|
|
183
|
+
const since = options.since ?? (isInteractive && !options.all ? '30d' : undefined);
|
|
184
|
+
const spinner = options.json ? null : ora().start();
|
|
185
|
+
const tracker = createScanProgressTracker(LOAD_VERBS, 'sessions', spinner);
|
|
20
186
|
try {
|
|
21
|
-
|
|
187
|
+
// Team-origin filter is pushed down to SQL so the LIMIT applies AFTER it.
|
|
188
|
+
// Without this, a dev dir with heavy SDK spawn activity (Task subagents,
|
|
189
|
+
// `agents run`, team agents) can fill the top-N window entirely with
|
|
190
|
+
// hidden rows and make real CLI sessions appear to vanish.
|
|
191
|
+
const scope = {
|
|
22
192
|
agent,
|
|
23
|
-
|
|
193
|
+
version,
|
|
194
|
+
all: pathFilter ? undefined : options.all,
|
|
24
195
|
cwd: process.cwd(),
|
|
196
|
+
cwdPrefix: pathFilter,
|
|
25
197
|
project: options.project,
|
|
26
|
-
|
|
27
|
-
since: options.since,
|
|
198
|
+
since,
|
|
28
199
|
until: options.until,
|
|
200
|
+
};
|
|
201
|
+
let sessions = await discoverSessions({
|
|
202
|
+
...scope,
|
|
203
|
+
limit,
|
|
204
|
+
excludeTeamOrigin: !options.teams,
|
|
205
|
+
onProgress: tracker.onProgress,
|
|
29
206
|
});
|
|
207
|
+
tracker.stop();
|
|
30
208
|
spinner?.stop();
|
|
209
|
+
// Version filter is pushed down to SQL via scope.version above; no
|
|
210
|
+
// post-filter needed. Defensive: the team-origin SQL filter covers the
|
|
211
|
+
// ~100% case, but classifyTeamSession also recognizes sessions with a
|
|
212
|
+
// meta.json in ~/.agents/teams/agents whose is_team_origin flag was
|
|
213
|
+
// never set (legacy rows). Keep the in-memory pass so those are still
|
|
214
|
+
// enriched/hidden.
|
|
215
|
+
const { visible: visibleSessions } = filterTeamSessions(sessions, !!options.teams);
|
|
216
|
+
sessions = visibleSessions;
|
|
217
|
+
const hiddenCount = options.teams
|
|
218
|
+
? 0
|
|
219
|
+
: countSessionsInScope({ ...scope, onlyTeamOrigin: true });
|
|
220
|
+
// Smart ID routing: a bare query that resolves to one session renders
|
|
221
|
+
// directly. If nothing matches in the scoped window and the query looks
|
|
222
|
+
// like a session ID, widen to global scope (incl. Claude /resume history).
|
|
223
|
+
if (searchQuery) {
|
|
224
|
+
const idMatches = resolveSessionById(sessions, searchQuery);
|
|
225
|
+
if (idMatches.length === 1) {
|
|
226
|
+
await renderSession(idMatches[0], mode, filterOpts);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (idMatches.length === 0 && looksLikeSessionId(searchQuery)) {
|
|
230
|
+
await renderOneSession(searchQuery, mode, { agent: options.agent, project: options.project, filter: filterOpts });
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
31
234
|
if (options.json) {
|
|
32
|
-
const
|
|
33
|
-
|
|
235
|
+
const filtered = searchQuery ? filterSessionsByQuery(sessions, searchQuery) : sessions;
|
|
236
|
+
const serializable = filtered.map(s => {
|
|
237
|
+
const { _matchedTerms, _bm25Score, ...rest } = s;
|
|
34
238
|
return rest;
|
|
35
239
|
});
|
|
36
240
|
process.stdout.write(JSON.stringify(serializable, null, 2) + '\n');
|
|
37
241
|
return;
|
|
38
242
|
}
|
|
39
243
|
if (sessions.length === 0) {
|
|
40
|
-
|
|
244
|
+
if (pathFilter) {
|
|
245
|
+
console.log(chalk.gray(`No sessions found for ${pathFilter}.`));
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
console.log(chalk.gray(formatNoSessionsMessage(options.all, options.project)));
|
|
249
|
+
}
|
|
250
|
+
if (hiddenCount > 0) {
|
|
251
|
+
console.log(chalk.gray(formatTeamHiddenFooter(hiddenCount)));
|
|
252
|
+
}
|
|
41
253
|
return;
|
|
42
254
|
}
|
|
43
|
-
if (
|
|
44
|
-
const
|
|
255
|
+
if (isInteractiveTerminal()) {
|
|
256
|
+
const message = pathFilter
|
|
257
|
+
? `Search sessions (${path.basename(pathFilter)}):`
|
|
258
|
+
: formatSearchMessage(options);
|
|
259
|
+
const picked = await pickSessionInteractive(sessions, message, searchQuery, hiddenCount);
|
|
45
260
|
if (picked) {
|
|
46
|
-
await
|
|
261
|
+
await handlePickedSession(picked);
|
|
47
262
|
return;
|
|
48
263
|
}
|
|
264
|
+
return;
|
|
49
265
|
}
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
padRight('Agent', 18) +
|
|
54
|
-
padRight('Project', 16) +
|
|
55
|
-
padRight('When', 14) +
|
|
56
|
-
padRight('Msgs', 8) +
|
|
57
|
-
padRight('Tokens', 10) +
|
|
58
|
-
'Topic'));
|
|
59
|
-
for (const session of sessions) {
|
|
60
|
-
const agentColor = colorAgent(session.agent);
|
|
61
|
-
const when = formatRelativeTime(session.timestamp);
|
|
62
|
-
const project = session.project || '-';
|
|
63
|
-
const account = session.account || '';
|
|
64
|
-
const agentLabel = session.version
|
|
65
|
-
? `${session.agent}@${session.version}`
|
|
66
|
-
: session.agent;
|
|
67
|
-
const topic = session.topic || '';
|
|
68
|
-
console.log(chalk.white(padRight(session.shortId, 10)) +
|
|
69
|
-
chalk.gray(padRight(truncate(account, 18), 20)) +
|
|
70
|
-
agentColor(padRight(truncate(agentLabel, 16), 18)) +
|
|
71
|
-
chalk.cyan(padRight(truncate(project, 14), 16)) +
|
|
72
|
-
chalk.gray(padRight(when, 14)) +
|
|
73
|
-
chalk.gray(padRight(formatCompactMetric(session.messageCount), 8)) +
|
|
74
|
-
chalk.gray(padRight(formatCompactMetric(session.tokenCount), 10)) +
|
|
75
|
-
chalk.white(truncate(topic, 40)));
|
|
76
|
-
}
|
|
77
|
-
console.log(chalk.gray(`\n${sessions.length} session${sessions.length === 1 ? '' : 's'}. Use 'agents sessions view <id>' to read.`));
|
|
266
|
+
// Non-interactive fallback (piped output)
|
|
267
|
+
const filtered = searchQuery ? filterSessionsByQuery(sessions, searchQuery) : sessions;
|
|
268
|
+
printSessionTable(filtered, hiddenCount);
|
|
78
269
|
}
|
|
79
270
|
catch (err) {
|
|
271
|
+
tracker.stop();
|
|
80
272
|
spinner?.stop();
|
|
81
273
|
console.error(chalk.red(`Failed to discover sessions: ${err.message}`));
|
|
82
274
|
process.exit(1);
|
|
83
275
|
}
|
|
84
276
|
}
|
|
85
|
-
|
|
277
|
+
function looksLikeSessionId(query) {
|
|
278
|
+
return /^[0-9a-f-]{6,}$/i.test(query.trim());
|
|
279
|
+
}
|
|
280
|
+
function teamTag(session) {
|
|
281
|
+
const origin = session.teamOrigin;
|
|
282
|
+
if (!origin)
|
|
283
|
+
return '';
|
|
284
|
+
const parts = [origin.handle, origin.mode].filter(Boolean).join(' · ');
|
|
285
|
+
return parts ? `[${parts}] ` : '[team] ';
|
|
286
|
+
}
|
|
287
|
+
function printSessionTable(sessions, hiddenCount = 0) {
|
|
288
|
+
for (const session of sessions) {
|
|
289
|
+
const agentColor = colorAgent(session.agent);
|
|
290
|
+
const when = formatRelativeTime(session.timestamp);
|
|
291
|
+
const project = session.project || '-';
|
|
292
|
+
const tag = teamTag(session);
|
|
293
|
+
const label = session.label;
|
|
294
|
+
const topic = tag ? `${tag}${session.topic ?? ''}` : session.topic;
|
|
295
|
+
const versionStr = session.version || '-';
|
|
296
|
+
console.log(chalk.white(padRight(session.shortId, 10)) +
|
|
297
|
+
agentColor(padRight(truncate(session.agent, 8), 9)) +
|
|
298
|
+
chalk.yellow(padRight(truncate(versionStr, 7), 8)) +
|
|
299
|
+
chalk.cyan(padRight(truncate(project, 14), 16)) +
|
|
300
|
+
renderTopicCell(label, topic, '', 48, 50) +
|
|
301
|
+
chalk.gray(when));
|
|
302
|
+
}
|
|
303
|
+
const countLine = `${sessions.length} session${sessions.length === 1 ? '' : 's'}.`;
|
|
304
|
+
console.log(chalk.gray(`\n${countLine}`));
|
|
305
|
+
if (hiddenCount > 0) {
|
|
306
|
+
console.log(chalk.gray(formatTeamHiddenFooter(hiddenCount)));
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
function buildFilterOptions(options) {
|
|
310
|
+
const opts = {};
|
|
311
|
+
if (options.include)
|
|
312
|
+
opts.include = parseRoleList(options.include, '--include');
|
|
313
|
+
if (options.exclude)
|
|
314
|
+
opts.exclude = parseRoleList(options.exclude, '--exclude');
|
|
315
|
+
if (opts.include && opts.exclude) {
|
|
316
|
+
throw new Error('--include and --exclude are mutually exclusive');
|
|
317
|
+
}
|
|
318
|
+
const parseCount = (raw, flag) => {
|
|
319
|
+
const n = Number(raw);
|
|
320
|
+
if (!Number.isFinite(n) || !Number.isInteger(n) || n <= 0) {
|
|
321
|
+
throw new Error(`${flag} expects a positive integer, got "${raw}"`);
|
|
322
|
+
}
|
|
323
|
+
return n;
|
|
324
|
+
};
|
|
325
|
+
if (options.first !== undefined)
|
|
326
|
+
opts.first = parseCount(options.first, '--first');
|
|
327
|
+
if (options.last !== undefined)
|
|
328
|
+
opts.last = parseCount(options.last, '--last');
|
|
329
|
+
if (opts.first !== undefined && opts.last !== undefined) {
|
|
330
|
+
throw new Error('--first and --last are mutually exclusive');
|
|
331
|
+
}
|
|
332
|
+
return opts;
|
|
333
|
+
}
|
|
334
|
+
function hasAnyFilter(opts) {
|
|
335
|
+
return !!(opts.include?.length || opts.exclude?.length || opts.first !== undefined || opts.last !== undefined);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Default is summary. Any explicit format flag wins. When filters are present
|
|
339
|
+
* without a format, default to markdown since summary is an aggregate view
|
|
340
|
+
* that filters don't meaningfully narrow.
|
|
341
|
+
*/
|
|
342
|
+
function resolveViewMode(options, filters) {
|
|
343
|
+
if (options.markdown)
|
|
344
|
+
return 'markdown';
|
|
345
|
+
if (options.json)
|
|
346
|
+
return 'json';
|
|
347
|
+
if (hasAnyFilter(filters))
|
|
348
|
+
return 'markdown';
|
|
349
|
+
return 'summary';
|
|
350
|
+
}
|
|
351
|
+
async function renderSession(session, mode, filters) {
|
|
86
352
|
// OpenCode stores sessions in SQLite; filePath is "db_path#session_id"
|
|
87
353
|
const realPath = session.filePath.split('#')[0];
|
|
88
354
|
if (!fs.existsSync(realPath)) {
|
|
@@ -97,81 +363,115 @@ async function renderSession(session, mode) {
|
|
|
97
363
|
console.log(chalk.gray(`Time: ${session.timestamp}`));
|
|
98
364
|
return;
|
|
99
365
|
}
|
|
100
|
-
// Session header
|
|
101
|
-
const agentColor = colorAgent(session.agent);
|
|
102
|
-
console.log('');
|
|
103
|
-
console.log(agentColor(session.agent) +
|
|
104
|
-
(session.version ? chalk.yellow(` ${session.version}`) : '') +
|
|
105
|
-
(session.project ? chalk.cyan(` ${session.project}`) : '') +
|
|
106
|
-
chalk.gray(` ${formatRelativeTime(session.timestamp)}`) +
|
|
107
|
-
(session.account ? chalk.gray(` (${session.account})`) : ''));
|
|
108
|
-
console.log(chalk.gray('─'.repeat(60)));
|
|
109
366
|
const spinner = ora(`Parsing ${session.agent} session...`).start();
|
|
110
|
-
|
|
367
|
+
let events = parseSession(session.filePath, session.agent);
|
|
111
368
|
spinner.stop();
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
369
|
+
events = filterEvents(events, filters);
|
|
370
|
+
const agentColor = colorAgent(session.agent);
|
|
371
|
+
console.log('');
|
|
372
|
+
if (mode === 'summary') {
|
|
373
|
+
const stats = computeSummaryStats(events);
|
|
374
|
+
const modelStr = stats.models.length > 0 ? chalk.yellow(` ${stats.models.join(', ')}`) : '';
|
|
375
|
+
const branchStr = session.gitBranch ? chalk.gray(` (${session.gitBranch})`) : '';
|
|
376
|
+
const absTime = formatAbsoluteTime(session.timestamp);
|
|
377
|
+
console.log(agentColor(session.agent) +
|
|
378
|
+
(session.version ? chalk.yellow(` ${session.version}`) : '') +
|
|
379
|
+
modelStr +
|
|
380
|
+
(session.project ? chalk.cyan(` ${session.project}`) + branchStr : branchStr) +
|
|
381
|
+
chalk.gray(` ${absTime} (${formatRelativeTime(session.timestamp)})`) +
|
|
382
|
+
(session.account ? chalk.gray(` · ${session.account}`) : ''));
|
|
383
|
+
const statsLine = renderSummaryHeader(stats);
|
|
384
|
+
if (statsLine)
|
|
385
|
+
console.log(chalk.gray(statsLine));
|
|
386
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
387
|
+
process.stdout.write(renderSummary(events, session.cwd));
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (mode === 'markdown') {
|
|
391
|
+
console.log(agentColor(session.agent) +
|
|
392
|
+
(session.version ? chalk.yellow(` ${session.version}`) : '') +
|
|
393
|
+
(session.project ? chalk.cyan(` ${session.project}`) : '') +
|
|
394
|
+
chalk.gray(` ${formatRelativeTime(session.timestamp)}`) +
|
|
395
|
+
(session.account ? chalk.gray(` (${session.account})`) : ''));
|
|
396
|
+
console.log(chalk.gray('─'.repeat(60)));
|
|
397
|
+
process.stdout.write(renderMarkdown(renderConversationMarkdown(events)));
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
// json — no header, raw events only (pipeable)
|
|
401
|
+
process.stdout.write(renderJson(events));
|
|
402
|
+
}
|
|
403
|
+
function renderTopicCell(label, topic, query, visibleWidth, paddedWidth) {
|
|
404
|
+
const lbl = (label ?? '').trim();
|
|
405
|
+
const tpc = (topic ?? '').trim();
|
|
406
|
+
const sep = ' · ';
|
|
407
|
+
const raw = lbl && tpc ? `${lbl}${sep}${tpc}` : (lbl || tpc);
|
|
408
|
+
const visible = truncate(raw, visibleWidth);
|
|
409
|
+
const padding = ' '.repeat(Math.max(0, paddedWidth - visible.length));
|
|
410
|
+
const labelEnd = lbl ? Math.min(lbl.length, visible.length) : 0;
|
|
411
|
+
let matchStart = -1, matchEnd = -1;
|
|
412
|
+
const q = query.trim().toLowerCase();
|
|
413
|
+
if (q) {
|
|
414
|
+
const lower = visible.toLowerCase();
|
|
415
|
+
for (const term of q.split(/\s+/).filter(Boolean)) {
|
|
416
|
+
const idx = lower.indexOf(term);
|
|
417
|
+
if (idx !== -1) {
|
|
418
|
+
matchStart = idx;
|
|
419
|
+
matchEnd = idx + term.length;
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
const cuts = new Set([0, labelEnd, visible.length]);
|
|
425
|
+
if (matchStart >= 0) {
|
|
426
|
+
cuts.add(matchStart);
|
|
427
|
+
cuts.add(matchEnd);
|
|
428
|
+
}
|
|
429
|
+
const boundaries = [...cuts].sort((a, b) => a - b);
|
|
430
|
+
let out = '';
|
|
431
|
+
for (let i = 0; i < boundaries.length - 1; i++) {
|
|
432
|
+
const s = boundaries[i], e = boundaries[i + 1];
|
|
433
|
+
if (s >= e)
|
|
434
|
+
continue;
|
|
435
|
+
const text = visible.slice(s, e);
|
|
436
|
+
const isLabel = s < labelEnd;
|
|
437
|
+
const isMatch = matchStart >= 0 && s >= matchStart && e <= matchEnd;
|
|
438
|
+
out += (isMatch || isLabel) ? chalk.bold.white(text) : chalk.white(text);
|
|
439
|
+
}
|
|
440
|
+
return out + padding;
|
|
441
|
+
}
|
|
442
|
+
function formatPickerLabel(s, query) {
|
|
130
443
|
const agentColor = colorAgent(s.agent);
|
|
131
444
|
const when = formatRelativeTime(s.timestamp);
|
|
132
445
|
const project = s.project || '-';
|
|
133
|
-
const
|
|
134
|
-
const
|
|
135
|
-
const topic = s.topic
|
|
136
|
-
const
|
|
137
|
-
// Append matched terms badge
|
|
138
|
-
const matchBadge = matchTerms && matchTerms.length > 0
|
|
139
|
-
? chalk.yellow(` [${matchTerms.join(', ')}]`)
|
|
140
|
-
: '';
|
|
446
|
+
const tag = teamTag(s);
|
|
447
|
+
const label = s.label;
|
|
448
|
+
const topic = tag ? `${tag}${s.topic ?? ''}` : s.topic;
|
|
449
|
+
const versionStr = s.version || '-';
|
|
141
450
|
return (chalk.white(padRight(s.shortId, 10)) +
|
|
142
|
-
|
|
143
|
-
|
|
451
|
+
agentColor(padRight(truncate(s.agent, 8), 9)) +
|
|
452
|
+
chalk.yellow(padRight(truncate(versionStr, 7), 8)) +
|
|
144
453
|
chalk.cyan(padRight(truncate(project, 14), 16)) +
|
|
145
|
-
|
|
146
|
-
chalk.gray(
|
|
147
|
-
chalk.gray(padRight(formatCompactMetric(s.tokenCount), 10)) +
|
|
148
|
-
chalk.white(truncate(topic, 30)) +
|
|
149
|
-
matchBadge);
|
|
454
|
+
renderTopicCell(label, topic, query, 48, 50) +
|
|
455
|
+
chalk.gray(when));
|
|
150
456
|
}
|
|
151
|
-
async function
|
|
457
|
+
async function pickSessionInteractive(sessions, message = 'Search sessions:', initialSearch, hiddenCount = 0) {
|
|
458
|
+
if (hiddenCount > 0) {
|
|
459
|
+
console.log(chalk.gray(formatTeamHiddenFooter(hiddenCount)));
|
|
460
|
+
}
|
|
152
461
|
try {
|
|
153
|
-
return await
|
|
462
|
+
return await sessionPicker({
|
|
154
463
|
message,
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
: 'No sessions found',
|
|
163
|
-
value: null,
|
|
164
|
-
disabled: 'Keep typing',
|
|
165
|
-
}];
|
|
166
|
-
}
|
|
167
|
-
return matches.map(s => ({
|
|
168
|
-
name: formatPickerLabel(s),
|
|
169
|
-
value: s,
|
|
170
|
-
description: formatSearchDescription(s),
|
|
171
|
-
short: s.shortId,
|
|
172
|
-
}));
|
|
464
|
+
sessions,
|
|
465
|
+
filter: (query) => {
|
|
466
|
+
// No query: show the full pool (picker viewport still paginates via pageSize).
|
|
467
|
+
// Typing: search the full pool.
|
|
468
|
+
if (!query.trim())
|
|
469
|
+
return sessions;
|
|
470
|
+
return filterSessionsByQuery(sessions, query);
|
|
173
471
|
},
|
|
174
|
-
|
|
472
|
+
labelFor: (s, query) => formatPickerLabel(s, query),
|
|
473
|
+
pageSize: PICKER_RECENT_COUNT,
|
|
474
|
+
initialSearch,
|
|
175
475
|
});
|
|
176
476
|
}
|
|
177
477
|
catch (err) {
|
|
@@ -180,16 +480,81 @@ async function pickSession(sessions, message = 'Search sessions:') {
|
|
|
180
480
|
throw err;
|
|
181
481
|
}
|
|
182
482
|
}
|
|
483
|
+
async function handlePickedSession(picked) {
|
|
484
|
+
if (picked.action === 'view') {
|
|
485
|
+
await renderSession(picked.session, 'summary', {});
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
const cwd = picked.session.cwd && fs.existsSync(picked.session.cwd)
|
|
489
|
+
? picked.session.cwd
|
|
490
|
+
: process.cwd();
|
|
491
|
+
const activeVersion = resolveVersion(picked.session.agent, cwd) ?? undefined;
|
|
492
|
+
const resume = buildResumeCommand(picked.session, activeVersion);
|
|
493
|
+
if (!resume) {
|
|
494
|
+
console.log(chalk.yellow(`Resume is not supported for ${picked.session.agent} sessions yet. Showing summary instead.`));
|
|
495
|
+
await renderSession(picked.session, 'summary', {});
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
if (picked.session.version && activeVersion && picked.session.version !== activeVersion) {
|
|
499
|
+
console.log(chalk.gray(`Cross-version handoff: session is ${picked.session.agent} ${picked.session.version}, ` +
|
|
500
|
+
`default is ${activeVersion}. Starting fresh and passing /continue so the new agent ` +
|
|
501
|
+
`reads the prior transcript via 'agents sessions'.`));
|
|
502
|
+
}
|
|
503
|
+
console.log(chalk.gray(`Resuming: ${resume.join(' ')} (cwd: ${cwd})`));
|
|
504
|
+
await new Promise((resolve) => {
|
|
505
|
+
const child = spawn(resume[0], resume.slice(1), {
|
|
506
|
+
cwd,
|
|
507
|
+
stdio: 'inherit',
|
|
508
|
+
shell: false,
|
|
509
|
+
});
|
|
510
|
+
child.on('error', (err) => {
|
|
511
|
+
console.error(chalk.red(`Failed to launch ${resume[0]}: ${err.message}`));
|
|
512
|
+
if (err.code === 'ENOENT') {
|
|
513
|
+
console.error(chalk.gray(`Make sure '${resume[0]}' is on your PATH.`));
|
|
514
|
+
}
|
|
515
|
+
resolve();
|
|
516
|
+
});
|
|
517
|
+
child.on('close', () => resolve());
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Build the shell command that resumes a picked session.
|
|
522
|
+
*
|
|
523
|
+
* Cross-version handoff: when the session was created on a different version
|
|
524
|
+
* than the one the shim will launch (activeVersion), the session file lives in
|
|
525
|
+
* the other version's isolated home and `--resume <id>` would silently fail to
|
|
526
|
+
* find it. Fall back to a fresh session seeded with `/continue <id>`, which is
|
|
527
|
+
* wired (~/.claude/commands/continue.md) to read the prior transcript via
|
|
528
|
+
* `agents sessions <id>` — that reader is version-agnostic.
|
|
529
|
+
*/
|
|
530
|
+
export function buildResumeCommand(session, activeVersion) {
|
|
531
|
+
const versionMismatch = !!(session.version && activeVersion && session.version !== activeVersion);
|
|
532
|
+
switch (session.agent) {
|
|
533
|
+
case 'claude':
|
|
534
|
+
if (versionMismatch)
|
|
535
|
+
return ['claude', `/continue ${session.id}`];
|
|
536
|
+
return ['claude', '--resume', session.id];
|
|
537
|
+
case 'codex':
|
|
538
|
+
if (versionMismatch)
|
|
539
|
+
return ['codex', `/continue ${session.id}`];
|
|
540
|
+
return ['codex', 'resume', session.id];
|
|
541
|
+
case 'opencode':
|
|
542
|
+
return ['opencode', '--session', session.id];
|
|
543
|
+
case 'gemini':
|
|
544
|
+
case 'openclaw':
|
|
545
|
+
return null;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
183
548
|
function parseAgentFilter(agentName) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
549
|
+
if (!agentName)
|
|
550
|
+
return {};
|
|
551
|
+
const [name, version] = agentName.split('@', 2);
|
|
552
|
+
const agent = name;
|
|
553
|
+
if (!SESSION_AGENTS.includes(agent)) {
|
|
554
|
+
console.error(chalk.red(`Unknown agent: ${name}. Use: ${SESSION_AGENTS.join(', ')}`));
|
|
187
555
|
process.exit(1);
|
|
188
556
|
}
|
|
189
|
-
return agent;
|
|
190
|
-
}
|
|
191
|
-
function shouldUseFilteredSessionSearch(options) {
|
|
192
|
-
return isInteractiveTerminal() && Boolean(options.agent || options.project);
|
|
557
|
+
return { agent, version };
|
|
193
558
|
}
|
|
194
559
|
function formatSearchMessage(options) {
|
|
195
560
|
const filters = [];
|
|
@@ -201,15 +566,21 @@ function formatSearchMessage(options) {
|
|
|
201
566
|
return 'Search sessions:';
|
|
202
567
|
return `Search sessions (${filters.join(', ')}):`;
|
|
203
568
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
function filterSessionsByQuery(sessions, query) {
|
|
569
|
+
/** Filter and rank sessions by a multi-term search query across metadata and content. */
|
|
570
|
+
export function filterSessionsByQuery(sessions, query) {
|
|
208
571
|
const trimmed = query?.trim().toLowerCase() || '';
|
|
209
572
|
if (!trimmed)
|
|
210
573
|
return sessions;
|
|
211
574
|
const terms = trimmed.split(/\s+/).filter(Boolean);
|
|
212
575
|
const contentIndex = searchContentIndex(sessions, trimmed);
|
|
576
|
+
// If the query exactly matches a session label, short-circuit the structural
|
|
577
|
+
// scorer (which would otherwise surface every session whose topic happens to
|
|
578
|
+
// contain the same words) and return only the label hits.
|
|
579
|
+
const EXACT_LABEL_SCORE = 1_000_000;
|
|
580
|
+
const exactLabelHits = [...contentIndex.values()].filter(s => (s._bm25Score ?? 0) >= EXACT_LABEL_SCORE);
|
|
581
|
+
if (exactLabelHits.length > 0) {
|
|
582
|
+
return exactLabelHits.sort((a, b) => (b._bm25Score ?? 0) - (a._bm25Score ?? 0));
|
|
583
|
+
}
|
|
213
584
|
return sessions
|
|
214
585
|
.map(session => ({ session, score: scoreSessionQuery(session, terms) }))
|
|
215
586
|
.filter(entry => {
|
|
@@ -282,102 +653,131 @@ function scoreSessionQuery(session, terms) {
|
|
|
282
653
|
}
|
|
283
654
|
return score;
|
|
284
655
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
656
|
+
/**
|
|
657
|
+
* Narrow a session list by --project and --agent before search resolution.
|
|
658
|
+
* Without this, a query like "scoped search" could match sessions in BOTH
|
|
659
|
+
* the project you specified AND elsewhere, producing an ambiguity error
|
|
660
|
+
* even though the user already pointed at the correct scope.
|
|
661
|
+
*/
|
|
662
|
+
function applyScopeFilters(sessions, scope) {
|
|
663
|
+
let filtered = sessions;
|
|
664
|
+
if (scope.project) {
|
|
665
|
+
const projectQuery = scope.project.toLowerCase();
|
|
666
|
+
filtered = filtered.filter((s) => {
|
|
667
|
+
const project = (s.project || '').toLowerCase();
|
|
668
|
+
const cwd = (s.cwd || '').toLowerCase();
|
|
669
|
+
return project.includes(projectQuery) || cwd.includes(projectQuery);
|
|
670
|
+
});
|
|
671
|
+
}
|
|
672
|
+
if (scope.agent) {
|
|
673
|
+
// Accept "claude" or "claude@2.1.112". Version suffix narrows further.
|
|
674
|
+
const [wantAgent, wantVersion] = scope.agent.split('@');
|
|
675
|
+
filtered = filtered.filter((s) => {
|
|
676
|
+
if (s.agent !== wantAgent)
|
|
677
|
+
return false;
|
|
678
|
+
if (wantVersion && s.version !== wantVersion)
|
|
679
|
+
return false;
|
|
680
|
+
return true;
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
return filtered;
|
|
684
|
+
}
|
|
685
|
+
async function renderArtifactsGlobal(query, listAll, name, scope) {
|
|
686
|
+
const spinner = ora().start();
|
|
687
|
+
const tracker = createScanProgressTracker(FIND_VERBS, 'session', spinner);
|
|
296
688
|
try {
|
|
297
|
-
const
|
|
298
|
-
|
|
299
|
-
all: Boolean(idQuery) || options.all,
|
|
689
|
+
const discovered = await discoverSessions({
|
|
690
|
+
all: true,
|
|
300
691
|
cwd: process.cwd(),
|
|
301
|
-
project: options.project,
|
|
302
692
|
limit: 5000,
|
|
303
|
-
|
|
304
|
-
until: options.until,
|
|
693
|
+
onProgress: tracker.onProgress,
|
|
305
694
|
});
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
695
|
+
tracker.stop();
|
|
696
|
+
const allSessions = applyScopeFilters(discovered, scope);
|
|
697
|
+
const matches = resolveSessionById(allSessions, query);
|
|
698
|
+
const queryMatches = matches.length > 0 ? matches : filterSessionsByQuery(allSessions, query);
|
|
699
|
+
if (queryMatches.length === 0) {
|
|
309
700
|
spinner.stop();
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
]);
|
|
701
|
+
console.error(chalk.red(`No session found matching: ${query}`));
|
|
702
|
+
process.exit(1);
|
|
703
|
+
}
|
|
704
|
+
if (queryMatches.length > 1) {
|
|
705
|
+
spinner.stop();
|
|
706
|
+
console.error(chalk.red(`Multiple sessions match "${query}":`));
|
|
707
|
+
for (const m of queryMatches.slice(0, 10)) {
|
|
708
|
+
console.error(chalk.cyan(` ${m.shortId} ${m.id} ${m.label ?? m.topic ?? ''}`));
|
|
319
709
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
return;
|
|
323
|
-
session = picked;
|
|
710
|
+
console.error(chalk.gray('Pass a longer ID to narrow it down.'));
|
|
711
|
+
process.exit(1);
|
|
324
712
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
713
|
+
spinner.stop();
|
|
714
|
+
await renderArtifactsForSession(queryMatches[0], listAll, name);
|
|
715
|
+
}
|
|
716
|
+
catch (err) {
|
|
717
|
+
if (isPromptCancelled(err))
|
|
718
|
+
return;
|
|
719
|
+
tracker.stop();
|
|
720
|
+
spinner.stop();
|
|
721
|
+
console.error(chalk.red(`Failed to read session: ${err.message}`));
|
|
722
|
+
process.exit(1);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
async function renderOneSession(query, mode, scope) {
|
|
726
|
+
const spinner = ora().start();
|
|
727
|
+
const tracker = createScanProgressTracker(FIND_VERBS, 'session', spinner);
|
|
728
|
+
try {
|
|
729
|
+
const discovered = await discoverSessions({
|
|
730
|
+
all: true,
|
|
731
|
+
cwd: process.cwd(),
|
|
732
|
+
limit: 5000,
|
|
733
|
+
onProgress: tracker.onProgress,
|
|
734
|
+
});
|
|
735
|
+
tracker.stop();
|
|
736
|
+
const allSessions = applyScopeFilters(discovered, scope);
|
|
737
|
+
let session;
|
|
738
|
+
const matches = resolveSessionById(allSessions, query);
|
|
739
|
+
let queryMatches = matches.length > 0 ? matches : filterSessionsByQuery(allSessions, query);
|
|
740
|
+
if (queryMatches.length === 0) {
|
|
741
|
+
const contentResults = searchContentIndex(allSessions, query);
|
|
742
|
+
if (contentResults.size > 0) {
|
|
743
|
+
const matchedSessions = Array.from(contentResults.values())
|
|
744
|
+
.sort((a, b) => (b._bm25Score ?? 0) - (a._bm25Score ?? 0));
|
|
745
|
+
if (matchedSessions.length === 1) {
|
|
746
|
+
session = matchedSessions[0];
|
|
747
|
+
}
|
|
748
|
+
else {
|
|
749
|
+
queryMatches = matchedSessions;
|
|
341
750
|
}
|
|
342
751
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
352
|
-
else {
|
|
353
|
-
renderClaudeHistoryOnlyId(idQuery, historyEntry, allSessions);
|
|
354
|
-
process.exit(1);
|
|
355
|
-
}
|
|
752
|
+
}
|
|
753
|
+
if (queryMatches.length === 0 && !session) {
|
|
754
|
+
spinner.stop();
|
|
755
|
+
const historyEntry = findClaudeHistoryEntry(query);
|
|
756
|
+
if (historyEntry) {
|
|
757
|
+
const resumeMatch = resolveClaudeHistoryEntryToTranscript(historyEntry, allSessions);
|
|
758
|
+
if (resumeMatch) {
|
|
759
|
+
session = resumeMatch.session;
|
|
356
760
|
}
|
|
357
761
|
else {
|
|
358
|
-
|
|
359
|
-
console.error(chalk.gray('Run "agents sessions" to list available sessions.'));
|
|
762
|
+
renderClaudeHistoryOnlyId(query, historyEntry, allSessions);
|
|
360
763
|
process.exit(1);
|
|
361
764
|
}
|
|
362
765
|
}
|
|
363
|
-
|
|
364
|
-
|
|
766
|
+
else {
|
|
767
|
+
console.error(chalk.red(`No session found matching: ${query}`));
|
|
768
|
+
console.error(chalk.gray('Run "agents sessions" to browse sessions.'));
|
|
769
|
+
process.exit(1);
|
|
365
770
|
}
|
|
366
|
-
|
|
771
|
+
}
|
|
772
|
+
if (!session) {
|
|
773
|
+
if (queryMatches.length > 1) {
|
|
367
774
|
spinner.stop();
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
console.error(chalk.
|
|
371
|
-
for (const match of queryMatches.slice(0, 10)) {
|
|
372
|
-
console.error(chalk.cyan(` ${match.id}`));
|
|
373
|
-
}
|
|
374
|
-
process.exit(1);
|
|
775
|
+
console.error(chalk.red(`Multiple sessions match "${query}":`));
|
|
776
|
+
for (const match of queryMatches.slice(0, 10)) {
|
|
777
|
+
console.error(chalk.cyan(` ${match.shortId} ${match.id} ${match.label ?? match.topic ?? ''}`));
|
|
375
778
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (!picked)
|
|
379
|
-
return;
|
|
380
|
-
session = picked;
|
|
779
|
+
console.error(chalk.gray('Pass a longer ID to narrow it down.'));
|
|
780
|
+
process.exit(1);
|
|
381
781
|
}
|
|
382
782
|
else {
|
|
383
783
|
session = queryMatches[0];
|
|
@@ -387,74 +787,108 @@ async function viewAction(idQuery, options) {
|
|
|
387
787
|
throw new Error('Session resolution failed');
|
|
388
788
|
}
|
|
389
789
|
spinner.stop();
|
|
390
|
-
await renderSession(session, mode);
|
|
790
|
+
await renderSession(session, mode, scope.filter);
|
|
391
791
|
}
|
|
392
792
|
catch (err) {
|
|
393
793
|
if (isPromptCancelled(err))
|
|
394
794
|
return;
|
|
795
|
+
tracker.stop();
|
|
395
796
|
spinner.stop();
|
|
396
797
|
console.error(chalk.red(`Failed to read session: ${err.message}`));
|
|
397
798
|
process.exit(1);
|
|
398
799
|
}
|
|
399
800
|
}
|
|
801
|
+
/** Register the `agents sessions` command with all its options and help text. */
|
|
400
802
|
export function registerSessionsCommands(program) {
|
|
401
|
-
|
|
803
|
+
program
|
|
402
804
|
.command('sessions')
|
|
403
|
-
.
|
|
404
|
-
.
|
|
405
|
-
.option('--
|
|
406
|
-
.option('--
|
|
407
|
-
.option('--
|
|
408
|
-
.option('--
|
|
409
|
-
.option('
|
|
410
|
-
.option('--
|
|
411
|
-
.
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
.
|
|
416
|
-
.
|
|
417
|
-
.option('--
|
|
418
|
-
.option('--
|
|
419
|
-
.option('--
|
|
420
|
-
.
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
805
|
+
.argument('[query]', 'Session ID, search query, or path (., ../, /path) to filter by project')
|
|
806
|
+
.description('Find, browse, and read agent conversation transcripts across Claude, Codex, Gemini, and OpenCode.')
|
|
807
|
+
.option('-a, --agent <agent>', 'Filter by agent type and version (e.g., claude, codex@0.116.0)')
|
|
808
|
+
.option('--all', 'Include sessions from every directory (not just current project)')
|
|
809
|
+
.option('--teams', 'Include team-spawned sessions (hidden by default)')
|
|
810
|
+
.option('--project <name>', 'Filter by project name (searches across all directories)')
|
|
811
|
+
.option('--since <time>', 'Only sessions newer than this (e.g., 2h, 7d, 4w, or ISO date)')
|
|
812
|
+
.option('--until <time>', 'Only sessions older than this (ISO timestamp)')
|
|
813
|
+
.option('-n, --limit <n>', 'Maximum number of sessions to return', '50')
|
|
814
|
+
.option('--markdown', 'Render the session as markdown (user, assistant, thinking, tool calls)')
|
|
815
|
+
.option('--json', 'Output JSON (session list when browsing, event array when rendering one session)')
|
|
816
|
+
.option('--include <roles>', 'Only include these roles (comma-separated): user, assistant, thinking, tools')
|
|
817
|
+
.option('--exclude <roles>', 'Exclude these roles (comma-separated): user, assistant, thinking, tools')
|
|
818
|
+
.option('--first <n>', 'Keep only the first N turns (a turn starts at each user message)')
|
|
819
|
+
.option('--last <n>', 'Keep only the last N turns (a turn starts at each user message)')
|
|
820
|
+
.option('--artifacts', 'List all files written or edited during a session')
|
|
821
|
+
.option('--artifact <name>', 'Read a specific artifact by filename or path (outputs to stdout)')
|
|
822
|
+
.addHelpText('after', `
|
|
823
|
+
Examples:
|
|
824
|
+
# Interactive picker: browse and search recent sessions (TTY only)
|
|
825
|
+
agents sessions
|
|
826
|
+
|
|
827
|
+
# List sessions from current project (last 30 days, piped output shows table)
|
|
828
|
+
agents sessions | head -20
|
|
829
|
+
|
|
830
|
+
# Search sessions by text (topic, file paths, commands)
|
|
831
|
+
agents sessions "add auth middleware"
|
|
832
|
+
|
|
833
|
+
# Filter by project across all directories
|
|
834
|
+
agents sessions --project agents-cli --all
|
|
835
|
+
|
|
836
|
+
# Filter by agent and time window
|
|
837
|
+
agents sessions --agent claude --since 7d
|
|
838
|
+
|
|
839
|
+
# Filter sessions in a specific directory
|
|
840
|
+
agents sessions /Users/muqsit/src/my-project
|
|
841
|
+
|
|
842
|
+
# Default summary view for one session
|
|
843
|
+
agents sessions a1b2c3d4
|
|
844
|
+
|
|
845
|
+
# Full conversation (user + assistant + thinking + tools) as markdown
|
|
846
|
+
agents sessions a1b2c3d4 --markdown
|
|
847
|
+
|
|
848
|
+
# Same conversation as structured JSON events
|
|
849
|
+
agents sessions a1b2c3d4 --json
|
|
850
|
+
|
|
851
|
+
# Only user messages (filter flags auto-select markdown)
|
|
852
|
+
agents sessions a1b2c3d4 --include user
|
|
853
|
+
|
|
854
|
+
# Everything except thinking, as markdown
|
|
855
|
+
agents sessions a1b2c3d4 --exclude thinking --markdown
|
|
856
|
+
|
|
857
|
+
# Last 3 turns as markdown
|
|
858
|
+
agents sessions a1b2c3d4 --last 3
|
|
859
|
+
|
|
860
|
+
# First 10 turns, user messages only, as JSON
|
|
861
|
+
agents sessions a1b2c3d4 --first 10 --include user --json
|
|
862
|
+
|
|
863
|
+
# Export all recent sessions as JSON for analysis
|
|
864
|
+
agents sessions --since 30d --limit 200 --json > sessions.json
|
|
865
|
+
|
|
866
|
+
# Include team-spawned sessions in results
|
|
867
|
+
agents sessions --teams
|
|
868
|
+
|
|
869
|
+
Notes:
|
|
870
|
+
- --include and --exclude are mutually exclusive.
|
|
871
|
+
- --first and --last are mutually exclusive.
|
|
872
|
+
- A filter flag without --markdown/--json defaults to --markdown output.
|
|
873
|
+
`)
|
|
874
|
+
.action(async (query, options) => {
|
|
875
|
+
await sessionsAction(query, options);
|
|
446
876
|
});
|
|
447
877
|
}
|
|
448
|
-
function formatNoSessionsMessage(showAll,
|
|
878
|
+
function formatNoSessionsMessage(showAll, project) {
|
|
449
879
|
const projectQuery = project?.trim();
|
|
450
880
|
if (projectQuery) {
|
|
451
881
|
return `No sessions found for project "${projectQuery}".`;
|
|
452
882
|
}
|
|
453
883
|
if (showAll)
|
|
454
884
|
return 'No sessions found.';
|
|
455
|
-
const command =
|
|
885
|
+
const command = 'agents sessions --all';
|
|
456
886
|
return `No sessions found for ${process.cwd()}. Run "${command}" to see sessions from every directory.`;
|
|
457
887
|
}
|
|
888
|
+
function formatTeamHiddenFooter(hiddenCount) {
|
|
889
|
+
const noun = hiddenCount === 1 ? 'team session' : 'team sessions';
|
|
890
|
+
return `(${hiddenCount} ${noun} hidden — use --teams to show, or \`agents teams status\`)`;
|
|
891
|
+
}
|
|
458
892
|
function findClaudeHistoryEntry(idQuery) {
|
|
459
893
|
const historyPath = path.join(os.homedir(), '.claude', 'history.jsonl');
|
|
460
894
|
if (!fs.existsSync(historyPath))
|
|
@@ -512,7 +946,7 @@ function renderClaudeHistoryOnlyId(idQuery, historyEntry, allSessions) {
|
|
|
512
946
|
for (const session of relatedSessions) {
|
|
513
947
|
console.error(chalk.gray(` ${session.shortId} ${session.id} ${session.project || '-'} ${formatRelativeTime(session.timestamp)}`));
|
|
514
948
|
}
|
|
515
|
-
console.error(chalk.gray('Use one of the transcript IDs above with "agents sessions
|
|
949
|
+
console.error(chalk.gray('Use one of the transcript IDs above with "agents sessions <id>".'));
|
|
516
950
|
return;
|
|
517
951
|
}
|
|
518
952
|
if (historyEntry.display === '/resume') {
|
|
@@ -529,7 +963,13 @@ function findClaudeSessionsInProject(sessions, historyEntry) {
|
|
|
529
963
|
function findClaudeProjectSessions(sessions, historyEntry) {
|
|
530
964
|
if (!historyEntry.project)
|
|
531
965
|
return [];
|
|
532
|
-
|
|
966
|
+
// Resolve symlinks (e.g. macOS /var -> /private/var) so we match sessions
|
|
967
|
+
// whose cwd was canonicalized at scan time.
|
|
968
|
+
let projectRoot = historyEntry.project;
|
|
969
|
+
try {
|
|
970
|
+
projectRoot = fs.realpathSync(projectRoot);
|
|
971
|
+
}
|
|
972
|
+
catch { /* dir gone */ }
|
|
533
973
|
return sessions.filter(session => session.agent === 'claude' &&
|
|
534
974
|
typeof session.cwd === 'string' &&
|
|
535
975
|
isWithinProject(session.cwd, projectRoot));
|
|
@@ -610,24 +1050,21 @@ function sessionDistance(session, historyEntry) {
|
|
|
610
1050
|
// ---------------------------------------------------------------------------
|
|
611
1051
|
// Formatting helpers
|
|
612
1052
|
// ---------------------------------------------------------------------------
|
|
1053
|
+
function formatAbsoluteTime(isoTimestamp) {
|
|
1054
|
+
const d = new Date(isoTimestamp);
|
|
1055
|
+
if (isNaN(d.getTime()))
|
|
1056
|
+
return isoTimestamp;
|
|
1057
|
+
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
1058
|
+
const hh = String(d.getHours()).padStart(2, '0');
|
|
1059
|
+
const mm = String(d.getMinutes()).padStart(2, '0');
|
|
1060
|
+
return `${months[d.getMonth()]} ${d.getDate()} ${hh}:${mm}`;
|
|
1061
|
+
}
|
|
613
1062
|
function padRight(s, width) {
|
|
614
1063
|
return s.length >= width ? s + ' ' : s + ' '.repeat(width - s.length);
|
|
615
1064
|
}
|
|
616
1065
|
function truncate(s, max) {
|
|
617
1066
|
return s.length > max ? s.slice(0, max - 1) + '.' : s;
|
|
618
1067
|
}
|
|
619
|
-
function formatCompactMetric(value) {
|
|
620
|
-
if (value === undefined)
|
|
621
|
-
return '-';
|
|
622
|
-
if (value < 1000)
|
|
623
|
-
return String(value);
|
|
624
|
-
if (value < 1_000_000) {
|
|
625
|
-
const compact = value / 1000;
|
|
626
|
-
return compact >= 100 ? `${Math.round(compact)}k` : `${compact.toFixed(1).replace(/\.0$/, '')}k`;
|
|
627
|
-
}
|
|
628
|
-
const compact = value / 1_000_000;
|
|
629
|
-
return compact >= 100 ? `${Math.round(compact)}m` : `${compact.toFixed(1).replace(/\.0$/, '')}m`;
|
|
630
|
-
}
|
|
631
1068
|
function formatRelativeTime(isoTimestamp) {
|
|
632
1069
|
const now = Date.now();
|
|
633
1070
|
const then = new Date(isoTimestamp).getTime();
|