@prestyj/cli 5.10.0 → 5.12.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/README.md +3 -4
- package/assets/skills/compliance-guard/SKILL.md +251 -0
- package/assets/skills/compliance-guard/references/artifacts.md +186 -0
- package/assets/skills/compliance-guard/references/eu-uk.md +151 -0
- package/assets/skills/compliance-guard/references/exposure-triage.md +161 -0
- package/assets/skills/compliance-guard/references/lawsuit-vectors.md +141 -0
- package/assets/skills/compliance-guard/references/provenance.md +42 -0
- package/assets/skills/compliance-guard/references/sector-gates.md +173 -0
- package/assets/skills/compliance-guard/references/security-baseline.md +130 -0
- package/assets/skills/compliance-guard/references/trigger-map.md +163 -0
- package/assets/skills/compliance-guard/references/us.md +164 -0
- package/dist/app-sidecar-auth-broadcast.test.d.ts +2 -0
- package/dist/app-sidecar-auth-broadcast.test.d.ts.map +1 -0
- package/dist/app-sidecar-auth-broadcast.test.js +348 -0
- package/dist/app-sidecar-auth-broadcast.test.js.map +1 -0
- package/dist/app-sidecar-sessions.d.ts +5 -2
- package/dist/app-sidecar-sessions.d.ts.map +1 -1
- package/dist/app-sidecar-sessions.js +34 -8
- package/dist/app-sidecar-sessions.js.map +1 -1
- package/dist/app-sidecar-sessions.test.js +96 -1
- package/dist/app-sidecar-sessions.test.js.map +1 -1
- package/dist/app-sidecar.js +616 -173
- package/dist/app-sidecar.js.map +1 -1
- package/dist/chat-agents/jiwa.test.js +1 -1
- package/dist/cli/auth.d.ts.map +1 -1
- package/dist/cli/auth.js +33 -36
- package/dist/cli/auth.js.map +1 -1
- package/dist/cli/command-routing.d.ts +1 -1
- package/dist/cli/command-routing.d.ts.map +1 -1
- package/dist/cli/command-routing.js +1 -0
- package/dist/cli/command-routing.js.map +1 -1
- package/dist/cli/command-routing.test.js +1 -0
- package/dist/cli/command-routing.test.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +179 -48
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +18 -5
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +45 -76
- package/dist/config.js.map +1 -1
- package/dist/config.test.js +30 -13
- package/dist/config.test.js.map +1 -1
- package/dist/core/agent-notifications.d.ts +62 -0
- package/dist/core/agent-notifications.d.ts.map +1 -0
- package/dist/core/agent-notifications.js +92 -0
- package/dist/core/agent-notifications.js.map +1 -0
- package/dist/core/agent-notifications.test.d.ts +2 -0
- package/dist/core/agent-notifications.test.d.ts.map +1 -0
- package/dist/core/agent-notifications.test.js +97 -0
- package/dist/core/agent-notifications.test.js.map +1 -0
- package/dist/core/agent-session-checkpoint-flush.test.d.ts +2 -0
- package/dist/core/agent-session-checkpoint-flush.test.d.ts.map +1 -0
- package/dist/core/agent-session-checkpoint-flush.test.js +201 -0
- package/dist/core/agent-session-checkpoint-flush.test.js.map +1 -0
- package/dist/core/agent-session-compaction.test.js +323 -6
- package/dist/core/agent-session-compaction.test.js.map +1 -1
- package/dist/core/agent-session-crash-durability.test.d.ts +2 -0
- package/dist/core/agent-session-crash-durability.test.d.ts.map +1 -0
- package/dist/core/agent-session-crash-durability.test.js +102 -0
- package/dist/core/agent-session-crash-durability.test.js.map +1 -0
- package/dist/core/agent-session-marker-anchors.test.js +181 -2
- package/dist/core/agent-session-marker-anchors.test.js.map +1 -1
- package/dist/core/agent-session-model-switch.test.d.ts +2 -0
- package/dist/core/agent-session-model-switch.test.d.ts.map +1 -0
- package/dist/core/agent-session-model-switch.test.js +161 -0
- package/dist/core/agent-session-model-switch.test.js.map +1 -0
- package/dist/core/agent-session-notifications.test.d.ts +2 -0
- package/dist/core/agent-session-notifications.test.d.ts.map +1 -0
- package/dist/core/agent-session-notifications.test.js +114 -0
- package/dist/core/agent-session-notifications.test.js.map +1 -0
- package/dist/core/agent-session-process-gate.test.d.ts +2 -0
- package/dist/core/agent-session-process-gate.test.d.ts.map +1 -0
- package/dist/core/agent-session-process-gate.test.js +98 -0
- package/dist/core/agent-session-process-gate.test.js.map +1 -0
- package/dist/core/agent-session-queue.test.js +65 -0
- package/dist/core/agent-session-queue.test.js.map +1 -1
- package/dist/core/agent-session-review-coverage.test.js +80 -23
- package/dist/core/agent-session-review-coverage.test.js.map +1 -1
- package/dist/core/agent-session-slash-restore.test.d.ts +2 -0
- package/dist/core/agent-session-slash-restore.test.d.ts.map +1 -0
- package/dist/core/agent-session-slash-restore.test.js +121 -0
- package/dist/core/agent-session-slash-restore.test.js.map +1 -0
- package/dist/core/agent-session-tool-tiers.test.d.ts +2 -0
- package/dist/core/agent-session-tool-tiers.test.d.ts.map +1 -0
- package/dist/core/agent-session-tool-tiers.test.js +138 -0
- package/dist/core/agent-session-tool-tiers.test.js.map +1 -0
- package/dist/core/agent-session.d.ts +257 -13
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +1118 -241
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/agents.d.ts +29 -1
- package/dist/core/agents.d.ts.map +1 -1
- package/dist/core/agents.js +47 -97
- package/dist/core/agents.js.map +1 -1
- package/dist/core/agents.test.js +87 -7
- package/dist/core/agents.test.js.map +1 -1
- package/dist/core/api-benchmark.d.ts +3 -3
- package/dist/core/api-benchmark.js +7 -7
- package/dist/core/auth-providers.d.ts +37 -1
- package/dist/core/auth-providers.d.ts.map +1 -1
- package/dist/core/auth-providers.js +83 -3
- package/dist/core/auth-providers.js.map +1 -1
- package/dist/core/auto-update.test.js +1 -1
- package/dist/core/autopilot-gate.d.ts +2 -2
- package/dist/core/autopilot-gate.js +1 -1
- package/dist/core/bash-spawn-benchmark.js +6 -6
- package/dist/core/bundled-agents.d.ts +3 -0
- package/dist/core/bundled-agents.d.ts.map +1 -0
- package/dist/core/bundled-agents.js +252 -0
- package/dist/core/bundled-agents.js.map +1 -0
- package/dist/core/cache-warm-benchmark.d.ts +2 -2
- package/dist/core/cache-warm-benchmark.js +5 -5
- package/dist/core/code-retrieval-chunkers.d.ts +27 -0
- package/dist/core/code-retrieval-chunkers.d.ts.map +1 -0
- package/dist/core/code-retrieval-chunkers.js +264 -0
- package/dist/core/code-retrieval-chunkers.js.map +1 -0
- package/dist/core/code-retrieval-chunkers.test.d.ts +2 -0
- package/dist/core/code-retrieval-chunkers.test.d.ts.map +1 -0
- package/dist/core/code-retrieval-chunkers.test.js +203 -0
- package/dist/core/code-retrieval-chunkers.test.js.map +1 -0
- package/dist/core/code-retrieval.d.ts +6 -0
- package/dist/core/code-retrieval.d.ts.map +1 -1
- package/dist/core/code-retrieval.js +38 -1
- package/dist/core/code-retrieval.js.map +1 -1
- package/dist/core/compaction/active-context.test.js +21 -0
- package/dist/core/compaction/active-context.test.js.map +1 -1
- package/dist/core/compaction/compactor.d.ts +108 -8
- package/dist/core/compaction/compactor.d.ts.map +1 -1
- package/dist/core/compaction/compactor.js +423 -119
- package/dist/core/compaction/compactor.js.map +1 -1
- package/dist/core/compaction/compactor.test.js +360 -8
- package/dist/core/compaction/compactor.test.js.map +1 -1
- package/dist/core/compaction/policy.d.ts +17 -0
- package/dist/core/compaction/policy.d.ts.map +1 -0
- package/dist/core/compaction/policy.js +22 -0
- package/dist/core/compaction/policy.js.map +1 -0
- package/dist/core/compaction/policy.test.d.ts +2 -0
- package/dist/core/compaction/policy.test.d.ts.map +1 -0
- package/dist/core/compaction/policy.test.js +35 -0
- package/dist/core/compaction/policy.test.js.map +1 -0
- package/dist/core/compaction/query-aware-selector.d.ts +33 -0
- package/dist/core/compaction/query-aware-selector.d.ts.map +1 -0
- package/dist/core/compaction/query-aware-selector.js +202 -0
- package/dist/core/compaction/query-aware-selector.js.map +1 -0
- package/dist/core/compaction/query-aware-selector.test.d.ts +2 -0
- package/dist/core/compaction/query-aware-selector.test.d.ts.map +1 -0
- package/dist/core/compaction/query-aware-selector.test.js +96 -0
- package/dist/core/compaction/query-aware-selector.test.js.map +1 -0
- package/dist/core/compaction/tool-result-pruner.d.ts +24 -11
- package/dist/core/compaction/tool-result-pruner.d.ts.map +1 -1
- package/dist/core/compaction/tool-result-pruner.js +98 -26
- package/dist/core/compaction/tool-result-pruner.js.map +1 -1
- package/dist/core/compaction/tool-result-pruner.test.js +178 -6
- package/dist/core/compaction/tool-result-pruner.test.js.map +1 -1
- package/dist/core/encode-cwd.test.js +1 -1
- package/dist/core/event-bus.d.ts +23 -0
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js +10 -0
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +28 -8
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/plugin-bundles.d.ts +23 -0
- package/dist/core/extensions/plugin-bundles.d.ts.map +1 -0
- package/dist/core/extensions/plugin-bundles.js +228 -0
- package/dist/core/extensions/plugin-bundles.js.map +1 -0
- package/dist/core/extensions/plugin-bundles.test.d.ts +2 -0
- package/dist/core/extensions/plugin-bundles.test.d.ts.map +1 -0
- package/dist/core/extensions/plugin-bundles.test.js +110 -0
- package/dist/core/extensions/plugin-bundles.test.js.map +1 -0
- package/dist/core/fast-apply-benchmark.d.ts +2 -2
- package/dist/core/fast-apply-benchmark.js +6 -6
- package/dist/core/foreign-session-import.d.ts +120 -0
- package/dist/core/foreign-session-import.d.ts.map +1 -0
- package/dist/core/foreign-session-import.js +598 -0
- package/dist/core/foreign-session-import.js.map +1 -0
- package/dist/core/foreign-session-import.test.d.ts +2 -0
- package/dist/core/foreign-session-import.test.d.ts.map +1 -0
- package/dist/core/foreign-session-import.test.js +220 -0
- package/dist/core/foreign-session-import.test.js.map +1 -0
- package/dist/core/foreign-transcript-blocks.d.ts +51 -0
- package/dist/core/foreign-transcript-blocks.d.ts.map +1 -0
- package/dist/core/foreign-transcript-blocks.js +151 -0
- package/dist/core/foreign-transcript-blocks.js.map +1 -0
- package/dist/core/goal-lifecycle-smoke.test.js +2 -2
- package/dist/core/goal-store.js +1 -1
- package/dist/core/goal-store.test.js +2 -2
- package/dist/core/goal-verifier.test.js +2 -2
- package/dist/core/goal-worker-dev-server-lifecycle.test.js +1 -1
- package/dist/core/goal-worker.test.js +2 -2
- package/dist/core/hashline-edit-benchmark.d.ts +2 -2
- package/dist/core/hashline-edit-benchmark.js +7 -7
- package/dist/core/ideal-review.d.ts +20 -1
- package/dist/core/ideal-review.d.ts.map +1 -1
- package/dist/core/ideal-review.js +37 -2
- package/dist/core/ideal-review.js.map +1 -1
- package/dist/core/ideal-review.test.js +99 -3
- package/dist/core/ideal-review.test.js.map +1 -1
- package/dist/core/local-endpoint-store.test.js +1 -1
- package/dist/core/logger.d.ts +1 -1
- package/dist/core/logger.js +2 -2
- package/dist/core/logger.js.map +1 -1
- package/dist/core/loop-breaker.d.ts.map +1 -1
- package/dist/core/loop-breaker.js +1 -0
- package/dist/core/loop-breaker.js.map +1 -1
- package/dist/core/lsp/client-navigation.test.d.ts +2 -0
- package/dist/core/lsp/client-navigation.test.d.ts.map +1 -0
- package/dist/core/lsp/client-navigation.test.js +135 -0
- package/dist/core/lsp/client-navigation.test.js.map +1 -0
- package/dist/core/lsp/client.d.ts +84 -0
- package/dist/core/lsp/client.d.ts.map +1 -1
- package/dist/core/lsp/client.js +190 -3
- package/dist/core/lsp/client.js.map +1 -1
- package/dist/core/lsp/integration.test.js +2 -2
- package/dist/core/lsp/manager-navigation.test.d.ts +2 -0
- package/dist/core/lsp/manager-navigation.test.d.ts.map +1 -0
- package/dist/core/lsp/manager-navigation.test.js +121 -0
- package/dist/core/lsp/manager-navigation.test.js.map +1 -0
- package/dist/core/lsp/manager.d.ts +81 -9
- package/dist/core/lsp/manager.d.ts.map +1 -1
- package/dist/core/lsp/manager.js +183 -64
- package/dist/core/lsp/manager.js.map +1 -1
- package/dist/core/lsp/manager.test.js +18 -0
- package/dist/core/lsp/manager.test.js.map +1 -1
- package/dist/core/lsp/pool.d.ts +113 -0
- package/dist/core/lsp/pool.d.ts.map +1 -0
- package/dist/core/lsp/pool.js +255 -0
- package/dist/core/lsp/pool.js.map +1 -0
- package/dist/core/lsp/pool.test.d.ts +2 -0
- package/dist/core/lsp/pool.test.d.ts.map +1 -0
- package/dist/core/lsp/pool.test.js +286 -0
- package/dist/core/lsp/pool.test.js.map +1 -0
- package/dist/core/lsp/servers.d.ts.map +1 -1
- package/dist/core/lsp/servers.js +43 -3
- package/dist/core/lsp/servers.js.map +1 -1
- package/dist/core/lsp/servers.test.d.ts +2 -0
- package/dist/core/lsp/servers.test.d.ts.map +1 -0
- package/dist/core/lsp/servers.test.js +67 -0
- package/dist/core/lsp/servers.test.js.map +1 -0
- package/dist/core/lsp/windows.test.js +6 -18
- package/dist/core/lsp/windows.test.js.map +1 -1
- package/dist/core/mcp/catalog-cache.d.ts +63 -0
- package/dist/core/mcp/catalog-cache.d.ts.map +1 -0
- package/dist/core/mcp/catalog-cache.js +163 -0
- package/dist/core/mcp/catalog-cache.js.map +1 -0
- package/dist/core/mcp/catalog-cache.test.d.ts +2 -0
- package/dist/core/mcp/catalog-cache.test.d.ts.map +1 -0
- package/dist/core/mcp/catalog-cache.test.js +99 -0
- package/dist/core/mcp/catalog-cache.test.js.map +1 -0
- package/dist/core/mcp/client-session-recovery.test.d.ts +2 -0
- package/dist/core/mcp/client-session-recovery.test.d.ts.map +1 -0
- package/dist/core/mcp/client-session-recovery.test.js +231 -0
- package/dist/core/mcp/client-session-recovery.test.js.map +1 -0
- package/dist/core/mcp/client.d.ts +158 -0
- package/dist/core/mcp/client.d.ts.map +1 -1
- package/dist/core/mcp/client.js +478 -45
- package/dist/core/mcp/client.js.map +1 -1
- package/dist/core/mcp/content.d.ts +19 -0
- package/dist/core/mcp/content.d.ts.map +1 -0
- package/dist/core/mcp/content.js +172 -0
- package/dist/core/mcp/content.js.map +1 -0
- package/dist/core/mcp/content.test.d.ts +2 -0
- package/dist/core/mcp/content.test.d.ts.map +1 -0
- package/dist/core/mcp/content.test.js +135 -0
- package/dist/core/mcp/content.test.js.map +1 -0
- package/dist/core/mcp/deferred-catalog.d.ts +1 -1
- package/dist/core/mcp/deferred-catalog.js +1 -1
- package/dist/core/mcp/elicitation-bridge.d.ts +41 -0
- package/dist/core/mcp/elicitation-bridge.d.ts.map +1 -0
- package/dist/core/mcp/elicitation-bridge.js +52 -0
- package/dist/core/mcp/elicitation-bridge.js.map +1 -0
- package/dist/core/mcp/elicitation.test.d.ts +2 -0
- package/dist/core/mcp/elicitation.test.d.ts.map +1 -0
- package/dist/core/mcp/elicitation.test.js +158 -0
- package/dist/core/mcp/elicitation.test.js.map +1 -0
- package/dist/core/mcp/index.d.ts +5 -1
- package/dist/core/mcp/index.d.ts.map +1 -1
- package/dist/core/mcp/index.js +2 -0
- package/dist/core/mcp/index.js.map +1 -1
- package/dist/core/mcp/legacy-negotiation.test.d.ts +2 -0
- package/dist/core/mcp/legacy-negotiation.test.d.ts.map +1 -0
- package/dist/core/mcp/legacy-negotiation.test.js +80 -0
- package/dist/core/mcp/legacy-negotiation.test.js.map +1 -0
- package/dist/core/mcp/oauth-provider.d.ts +1 -2
- package/dist/core/mcp/oauth-provider.d.ts.map +1 -1
- package/dist/core/mcp/oauth-provider.js +6 -2
- package/dist/core/mcp/oauth-provider.js.map +1 -1
- package/dist/core/mcp/oauth-store.d.ts +1 -1
- package/dist/core/mcp/oauth-store.d.ts.map +1 -1
- package/dist/core/mcp/oauth-store.js.map +1 -1
- package/dist/core/mcp/oauth-store.test.js +2 -2
- package/dist/core/mcp/resolve-stdio.test.js +5 -5
- package/dist/core/mcp/session-elicitation-wiring.test.d.ts +2 -0
- package/dist/core/mcp/session-elicitation-wiring.test.d.ts.map +1 -0
- package/dist/core/mcp/session-elicitation-wiring.test.js +138 -0
- package/dist/core/mcp/session-elicitation-wiring.test.js.map +1 -0
- package/dist/core/mcp/shared-pool.d.ts +158 -0
- package/dist/core/mcp/shared-pool.d.ts.map +1 -0
- package/dist/core/mcp/shared-pool.js +235 -0
- package/dist/core/mcp/shared-pool.js.map +1 -0
- package/dist/core/mcp/shared-pool.test.d.ts +2 -0
- package/dist/core/mcp/shared-pool.test.d.ts.map +1 -0
- package/dist/core/mcp/shared-pool.test.js +425 -0
- package/dist/core/mcp/shared-pool.test.js.map +1 -0
- package/dist/core/mcp/store.test.js +5 -5
- package/dist/core/mcp/types.d.ts +12 -0
- package/dist/core/mcp/types.d.ts.map +1 -1
- package/dist/core/mcp/when-connected.test.d.ts +2 -0
- package/dist/core/mcp/when-connected.test.d.ts.map +1 -0
- package/dist/core/mcp/when-connected.test.js +46 -0
- package/dist/core/mcp/when-connected.test.js.map +1 -0
- package/dist/core/nolan-context.d.ts.map +1 -1
- package/dist/core/nolan-context.js +8 -0
- package/dist/core/nolan-context.js.map +1 -1
- package/dist/core/nolan-context.test.js +40 -0
- package/dist/core/nolan-context.test.js.map +1 -1
- package/dist/core/nolan-model.test.js +1 -1
- package/dist/core/nolan-prompt.js +8 -5
- package/dist/core/nolan-prompt.js.map +1 -1
- package/dist/core/nolan-prompt.test.js +5 -0
- package/dist/core/nolan-prompt.test.js.map +1 -1
- package/dist/core/oauth/xai.d.ts +2 -0
- package/dist/core/oauth/xai.d.ts.map +1 -0
- package/dist/core/oauth/xai.js +4 -0
- package/dist/core/oauth/xai.js.map +1 -0
- package/dist/core/persistent-shell.d.ts +3 -2
- package/dist/core/persistent-shell.d.ts.map +1 -1
- package/dist/core/persistent-shell.js +11 -4
- package/dist/core/persistent-shell.js.map +1 -1
- package/dist/core/persistent-shell.test.js +6 -6
- package/dist/core/process-gate.d.ts +45 -0
- package/dist/core/process-gate.d.ts.map +1 -0
- package/dist/core/process-gate.js +79 -0
- package/dist/core/process-gate.js.map +1 -0
- package/dist/core/process-gate.test.d.ts +2 -0
- package/dist/core/process-gate.test.d.ts.map +1 -0
- package/dist/core/process-gate.test.js +87 -0
- package/dist/core/process-gate.test.js.map +1 -0
- package/dist/core/process-manager-dev-server-repro.test.js +51 -12
- package/dist/core/process-manager-dev-server-repro.test.js.map +1 -1
- package/dist/core/process-manager-notifications.test.d.ts +2 -0
- package/dist/core/process-manager-notifications.test.d.ts.map +1 -0
- package/dist/core/process-manager-notifications.test.js +227 -0
- package/dist/core/process-manager-notifications.test.js.map +1 -0
- package/dist/core/process-manager.d.ts +72 -1
- package/dist/core/process-manager.d.ts.map +1 -1
- package/dist/core/process-manager.js +247 -4
- package/dist/core/process-manager.js.map +1 -1
- package/dist/core/progress/git-xp.test.js +2 -2
- package/dist/core/progress/ranks.d.ts +14 -3
- package/dist/core/progress/ranks.d.ts.map +1 -1
- package/dist/core/progress/ranks.js +181 -12
- package/dist/core/progress/ranks.js.map +1 -1
- package/dist/core/progress/ranks.test.js +48 -7
- package/dist/core/progress/ranks.test.js.map +1 -1
- package/dist/core/progress/rebuild.test.js +1 -1
- package/dist/core/progress/store.test.js +1 -1
- package/dist/core/progress/types.d.ts +2 -1
- package/dist/core/progress/types.d.ts.map +1 -1
- package/dist/core/project-discovery.d.ts +31 -6
- package/dist/core/project-discovery.d.ts.map +1 -1
- package/dist/core/project-discovery.js +349 -79
- package/dist/core/project-discovery.js.map +1 -1
- package/dist/core/project-discovery.test.js +128 -4
- package/dist/core/project-discovery.test.js.map +1 -1
- package/dist/core/prompt-commands.js +3 -3
- package/dist/core/radio.d.ts +1 -1
- package/dist/core/radio.d.ts.map +1 -1
- package/dist/core/radio.js +90 -6
- package/dist/core/radio.js.map +1 -1
- package/dist/core/radio.test.js +15 -2
- package/dist/core/radio.test.js.map +1 -1
- package/dist/core/regrounding.d.ts.map +1 -1
- package/dist/core/regrounding.js +1 -0
- package/dist/core/regrounding.js.map +1 -1
- package/dist/core/run-claim.d.ts +37 -0
- package/dist/core/run-claim.d.ts.map +1 -0
- package/dist/core/run-claim.js +46 -0
- package/dist/core/run-claim.js.map +1 -0
- package/dist/core/run-claim.test.d.ts +2 -0
- package/dist/core/run-claim.test.d.ts.map +1 -0
- package/dist/core/run-claim.test.js +107 -0
- package/dist/core/run-claim.test.js.map +1 -0
- package/dist/core/run-journal.test.d.ts +2 -0
- package/dist/core/run-journal.test.d.ts.map +1 -0
- package/dist/core/run-journal.test.js +195 -0
- package/dist/core/run-journal.test.js.map +1 -0
- package/dist/core/run-lifecycle.d.ts +22 -3
- package/dist/core/run-lifecycle.d.ts.map +1 -1
- package/dist/core/run-lifecycle.js +12 -3
- package/dist/core/run-lifecycle.js.map +1 -1
- package/dist/core/sandbox-domains.d.ts +14 -0
- package/dist/core/sandbox-domains.d.ts.map +1 -0
- package/dist/core/sandbox-domains.js +76 -0
- package/dist/core/sandbox-domains.js.map +1 -0
- package/dist/core/sandbox-feedback.d.ts +22 -0
- package/dist/core/sandbox-feedback.d.ts.map +1 -0
- package/dist/core/sandbox-feedback.js +69 -0
- package/dist/core/sandbox-feedback.js.map +1 -0
- package/dist/core/sandbox-feedback.test.d.ts +2 -0
- package/dist/core/sandbox-feedback.test.d.ts.map +1 -0
- package/dist/core/sandbox-feedback.test.js +32 -0
- package/dist/core/sandbox-feedback.test.js.map +1 -0
- package/dist/core/sandbox.d.ts +91 -0
- package/dist/core/sandbox.d.ts.map +1 -0
- package/dist/core/sandbox.js +254 -0
- package/dist/core/sandbox.js.map +1 -0
- package/dist/core/sandbox.test.d.ts +2 -0
- package/dist/core/sandbox.test.d.ts.map +1 -0
- package/dist/core/sandbox.test.js +157 -0
- package/dist/core/sandbox.test.js.map +1 -0
- package/dist/core/semantic-search-benchmark.d.ts +2 -2
- package/dist/core/semantic-search-benchmark.js +5 -5
- package/dist/core/session-compaction.d.ts +6 -0
- package/dist/core/session-compaction.d.ts.map +1 -1
- package/dist/core/session-compaction.js +11 -1
- package/dist/core/session-compaction.js.map +1 -1
- package/dist/core/session-compaction.test.js +19 -2
- package/dist/core/session-compaction.test.js.map +1 -1
- package/dist/core/session-export.d.ts.map +1 -1
- package/dist/core/session-export.js +12 -4
- package/dist/core/session-export.js.map +1 -1
- package/dist/core/session-history.d.ts +95 -6
- package/dist/core/session-history.d.ts.map +1 -1
- package/dist/core/session-history.js +261 -17
- package/dist/core/session-history.js.map +1 -1
- package/dist/core/session-history.test.js +173 -2
- package/dist/core/session-history.test.js.map +1 -1
- package/dist/core/session-manager.d.ts +223 -6
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +579 -74
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-manager.test.js +154 -10
- package/dist/core/session-manager.test.js.map +1 -1
- package/dist/core/session-notification-visibility.test.d.ts +2 -0
- package/dist/core/session-notification-visibility.test.d.ts.map +1 -0
- package/dist/core/session-notification-visibility.test.js +113 -0
- package/dist/core/session-notification-visibility.test.js.map +1 -0
- package/dist/core/session-preview.d.ts +5 -4
- package/dist/core/session-preview.d.ts.map +1 -1
- package/dist/core/session-preview.js +12 -5
- package/dist/core/session-preview.js.map +1 -1
- package/dist/core/session-restore-display.test.js +25 -0
- package/dist/core/session-restore-display.test.js.map +1 -1
- package/dist/core/session-storage-fd-leak.test.d.ts +2 -0
- package/dist/core/session-storage-fd-leak.test.d.ts.map +1 -0
- package/dist/core/session-storage-fd-leak.test.js +116 -0
- package/dist/core/session-storage-fd-leak.test.js.map +1 -0
- package/dist/core/session-storage.d.ts +22 -2
- package/dist/core/session-storage.d.ts.map +1 -1
- package/dist/core/session-storage.js +72 -9
- package/dist/core/session-storage.js.map +1 -1
- package/dist/core/session-storage.test.js +8 -3
- package/dist/core/session-storage.test.js.map +1 -1
- package/dist/core/settings-manager.d.ts +10 -2
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +36 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/shell-path.js +1 -1
- package/dist/core/shell.d.ts +1 -1
- package/dist/core/shell.js +2 -2
- package/dist/core/shell.test.js +2 -2
- package/dist/core/shutdown.d.ts +71 -0
- package/dist/core/shutdown.d.ts.map +1 -0
- package/dist/core/shutdown.js +144 -0
- package/dist/core/shutdown.js.map +1 -0
- package/dist/core/shutdown.test.d.ts +2 -0
- package/dist/core/shutdown.test.d.ts.map +1 -0
- package/dist/core/shutdown.test.js +160 -0
- package/dist/core/shutdown.test.js.map +1 -0
- package/dist/core/sidecar-error-reporter.js +1 -1
- package/dist/core/sidecar-error-reporter.test.js +2 -2
- package/dist/core/skills-routing.test.js +96 -0
- package/dist/core/skills-routing.test.js.map +1 -1
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +3 -0
- package/dist/core/skills.js.map +1 -1
- package/dist/core/steering.d.ts +12 -0
- package/dist/core/steering.d.ts.map +1 -1
- package/dist/core/steering.js +16 -0
- package/dist/core/steering.js.map +1 -1
- package/dist/core/subagent-manager.d.ts +22 -0
- package/dist/core/subagent-manager.d.ts.map +1 -1
- package/dist/core/subagent-manager.js +84 -13
- package/dist/core/subagent-manager.js.map +1 -1
- package/dist/core/subagent-manager.test.js +57 -15
- package/dist/core/subagent-manager.test.js.map +1 -1
- package/dist/core/subagent-store.test.js +3 -3
- package/dist/core/task-store.js +1 -1
- package/dist/core/verification-evidence.d.ts +17 -0
- package/dist/core/verification-evidence.d.ts.map +1 -0
- package/dist/core/verification-evidence.js +283 -0
- package/dist/core/verification-evidence.js.map +1 -0
- package/dist/core/verification-evidence.test.d.ts +2 -0
- package/dist/core/verification-evidence.test.d.ts.map +1 -0
- package/dist/core/verification-evidence.test.js +103 -0
- package/dist/core/verification-evidence.test.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/interactive.d.ts.map +1 -1
- package/dist/interactive.js +17 -3
- package/dist/interactive.js.map +1 -1
- package/dist/modes/acp-mode.d.ts +122 -0
- package/dist/modes/acp-mode.d.ts.map +1 -0
- package/dist/modes/acp-mode.js +1366 -0
- package/dist/modes/acp-mode.js.map +1 -0
- package/dist/modes/acp-mode.test.d.ts +2 -0
- package/dist/modes/acp-mode.test.d.ts.map +1 -0
- package/dist/modes/acp-mode.test.js +1065 -0
- package/dist/modes/acp-mode.test.js.map +1 -0
- package/dist/modes/agent-home-mode.d.ts.map +1 -1
- package/dist/modes/agent-home-mode.js +15 -11
- package/dist/modes/agent-home-mode.js.map +1 -1
- package/dist/modes/index.d.ts +1 -0
- package/dist/modes/index.d.ts.map +1 -1
- package/dist/modes/index.js +1 -0
- package/dist/modes/index.js.map +1 -1
- package/dist/modes/json-mode.d.ts +36 -0
- package/dist/modes/json-mode.d.ts.map +1 -1
- package/dist/modes/json-mode.js +39 -0
- package/dist/modes/json-mode.js.map +1 -1
- package/dist/modes/json-mode.test.d.ts +2 -0
- package/dist/modes/json-mode.test.d.ts.map +1 -0
- package/dist/modes/json-mode.test.js +84 -0
- package/dist/modes/json-mode.test.js.map +1 -0
- package/dist/modes/serve-mode.d.ts +1 -1
- package/dist/modes/serve-mode.d.ts.map +1 -1
- package/dist/modes/serve-mode.js +14 -9
- package/dist/modes/serve-mode.js.map +1 -1
- package/dist/modes/subagent-worker-mode.d.ts +8 -0
- package/dist/modes/subagent-worker-mode.d.ts.map +1 -1
- package/dist/modes/subagent-worker-mode.js +91 -11
- package/dist/modes/subagent-worker-mode.js.map +1 -1
- package/dist/modes/subagent-worker-mode.test.d.ts +2 -0
- package/dist/modes/subagent-worker-mode.test.d.ts.map +1 -0
- package/dist/modes/subagent-worker-mode.test.js +52 -0
- package/dist/modes/subagent-worker-mode.test.js.map +1 -0
- package/dist/session.d.ts +29 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +41 -0
- package/dist/session.js.map +1 -1
- package/dist/session.test.js +1 -1
- package/dist/system-prompt.d.ts +37 -1
- package/dist/system-prompt.d.ts.map +1 -1
- package/dist/system-prompt.js +123 -11
- package/dist/system-prompt.js.map +1 -1
- package/dist/system-prompt.test.js +90 -11
- package/dist/system-prompt.test.js.map +1 -1
- package/dist/system-prompt.tiering.test.d.ts +2 -0
- package/dist/system-prompt.tiering.test.d.ts.map +1 -0
- package/dist/system-prompt.tiering.test.js +105 -0
- package/dist/system-prompt.tiering.test.js.map +1 -0
- package/dist/tools/bash.d.ts +2 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +55 -16
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/code-nav.d.ts +30 -0
- package/dist/tools/code-nav.d.ts.map +1 -0
- package/dist/tools/code-nav.js +320 -0
- package/dist/tools/code-nav.js.map +1 -0
- package/dist/tools/code-nav.test.d.ts +2 -0
- package/dist/tools/code-nav.test.d.ts.map +1 -0
- package/dist/tools/code-nav.test.js +136 -0
- package/dist/tools/code-nav.test.js.map +1 -0
- package/dist/tools/edit.test.js +1 -1
- package/dist/tools/generate-image.d.ts +1 -1
- package/dist/tools/goals.d.ts +4 -4
- package/dist/tools/goals.test.js +2 -2
- package/dist/tools/grep-parity.test.d.ts +2 -0
- package/dist/tools/grep-parity.test.d.ts.map +1 -0
- package/dist/tools/grep-parity.test.js +92 -0
- package/dist/tools/grep-parity.test.js.map +1 -0
- package/dist/tools/grep-recall.test.d.ts +2 -0
- package/dist/tools/grep-recall.test.d.ts.map +1 -0
- package/dist/tools/grep-recall.test.js +114 -0
- package/dist/tools/grep-recall.test.js.map +1 -0
- package/dist/tools/grep-spawn.test.d.ts +2 -0
- package/dist/tools/grep-spawn.test.d.ts.map +1 -0
- package/dist/tools/grep-spawn.test.js +66 -0
- package/dist/tools/grep-spawn.test.js.map +1 -0
- package/dist/tools/grep.d.ts +13 -3
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +342 -41
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/grep.test.js +17 -14
- package/dist/tools/grep.test.js.map +1 -1
- package/dist/tools/index.d.ts +20 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +28 -18
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/index.test.d.ts +2 -0
- package/dist/tools/index.test.d.ts.map +1 -0
- package/dist/tools/index.test.js +55 -0
- package/dist/tools/index.test.js.map +1 -0
- package/dist/tools/plan-mode.test.js +1 -1
- package/dist/tools/prompt-hints.d.ts +9 -0
- package/dist/tools/prompt-hints.d.ts.map +1 -1
- package/dist/tools/prompt-hints.js +61 -8
- package/dist/tools/prompt-hints.js.map +1 -1
- package/dist/tools/read-only-bash.d.ts +3 -0
- package/dist/tools/read-only-bash.d.ts.map +1 -1
- package/dist/tools/read-only-bash.js +7 -9
- package/dist/tools/read-only-bash.js.map +1 -1
- package/dist/tools/safe-env.js +1 -1
- package/dist/tools/search-code.d.ts.map +1 -1
- package/dist/tools/search-code.js +7 -6
- package/dist/tools/search-code.js.map +1 -1
- package/dist/tools/search-code.test.js +8 -2
- package/dist/tools/search-code.test.js.map +1 -1
- package/dist/tools/skill.js +2 -1
- package/dist/tools/skill.js.map +1 -1
- package/dist/tools/subagent-control.d.ts.map +1 -1
- package/dist/tools/subagent-control.js +25 -6
- package/dist/tools/subagent-control.js.map +1 -1
- package/dist/tools/subagent-control.test.js +34 -1
- package/dist/tools/subagent-control.test.js.map +1 -1
- package/dist/tools/subagent-shared.d.ts +26 -1
- package/dist/tools/subagent-shared.d.ts.map +1 -1
- package/dist/tools/subagent-shared.js +40 -7
- package/dist/tools/subagent-shared.js.map +1 -1
- package/dist/tools/subagent-shared.test.js +43 -9
- package/dist/tools/subagent-shared.test.js.map +1 -1
- package/dist/tools/subagent.d.ts.map +1 -1
- package/dist/tools/subagent.js +54 -16
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/subagent.test.js +68 -3
- package/dist/tools/subagent.test.js.map +1 -1
- package/dist/tools/task-output.d.ts.map +1 -1
- package/dist/tools/task-output.js +3 -1
- package/dist/tools/task-output.js.map +1 -1
- package/dist/tools/task-send.test.js +19 -10
- package/dist/tools/task-send.test.js.map +1 -1
- package/dist/tools/tool-search.d.ts +12 -1
- package/dist/tools/tool-search.d.ts.map +1 -1
- package/dist/tools/tool-search.js +24 -4
- package/dist/tools/tool-search.js.map +1 -1
- package/dist/tools/tool-search.test.d.ts +2 -0
- package/dist/tools/tool-search.test.d.ts.map +1 -0
- package/dist/tools/tool-search.test.js +69 -0
- package/dist/tools/tool-search.test.js.map +1 -0
- package/dist/tools/tool-tiers.d.ts +42 -0
- package/dist/tools/tool-tiers.d.ts.map +1 -0
- package/dist/tools/tool-tiers.js +78 -0
- package/dist/tools/tool-tiers.js.map +1 -0
- package/dist/tools/tool-tiers.test.d.ts +2 -0
- package/dist/tools/tool-tiers.test.d.ts.map +1 -0
- package/dist/tools/tool-tiers.test.js +48 -0
- package/dist/tools/tool-tiers.test.js.map +1 -0
- package/dist/tools/web-fetch.d.ts +1 -1
- package/dist/tools/web-fetch.d.ts.map +1 -1
- package/dist/tools/web-fetch.js +5 -2
- package/dist/tools/web-fetch.js.map +1 -1
- package/dist/tools/write.test.js +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +27 -1
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/components/Banner.d.ts.map +1 -1
- package/dist/ui/components/Banner.js +24 -16
- package/dist/ui/components/Banner.js.map +1 -1
- package/dist/ui/components/ToolExecution.js +42 -34
- package/dist/ui/components/ToolExecution.js.map +1 -1
- package/dist/ui/components/force-color.d.ts +2 -0
- package/dist/ui/components/force-color.d.ts.map +1 -0
- package/dist/ui/components/force-color.js +11 -0
- package/dist/ui/components/force-color.js.map +1 -0
- package/dist/ui/components/themed-output.test.d.ts +2 -0
- package/dist/ui/components/themed-output.test.d.ts.map +1 -0
- package/dist/ui/components/themed-output.test.js +111 -0
- package/dist/ui/components/themed-output.test.js.map +1 -0
- package/dist/ui/error-item.d.ts +1 -1
- package/dist/ui/error-item.js +3 -3
- package/dist/ui/error-item.js.map +1 -1
- package/dist/ui/hooks/useAgentLoop.d.ts +3 -0
- package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
- package/dist/ui/hooks/useAgentLoop.js +45 -6
- package/dist/ui/hooks/useAgentLoop.js.map +1 -1
- package/dist/ui/login.js +1 -1
- package/dist/ui/login.js.map +1 -1
- package/dist/ui/render.d.ts +1 -1
- package/dist/ui/render.js +6 -6
- package/dist/ui/theme/dark-ansi.json +6 -0
- package/dist/ui/theme/dark-daltonized.json +6 -0
- package/dist/ui/theme/dark.json +6 -0
- package/dist/ui/theme/light-ansi.json +6 -0
- package/dist/ui/theme/light-daltonized.json +6 -0
- package/dist/ui/theme/light.json +6 -0
- package/dist/ui/theme/theme.d.ts +6 -0
- package/dist/ui/theme/theme.d.ts.map +1 -1
- package/dist/ui/tui-terminal-recorder.test.js +1 -1
- package/dist/utils/git.test.js +3 -3
- package/dist/utils/github.test.js +3 -3
- package/dist/utils/github.test.js.map +1 -1
- package/dist/utils/image.d.ts +26 -3
- package/dist/utils/image.d.ts.map +1 -1
- package/dist/utils/image.js +83 -14
- package/dist/utils/image.js.map +1 -1
- package/dist/utils/image.test.d.ts +2 -0
- package/dist/utils/image.test.d.ts.map +1 -0
- package/dist/utils/image.test.js +139 -0
- package/dist/utils/image.test.js.map +1 -0
- package/package.json +7 -6
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
|
-
import { createReadStream } from "node:fs";
|
|
2
|
+
import { createReadStream, realpathSync } from "node:fs";
|
|
3
3
|
import readline from "node:readline";
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
@@ -7,34 +7,45 @@ import { getAppPaths } from "../config.js";
|
|
|
7
7
|
import { encodeCwd, stripExtendedLengthPrefix } from "./encode-cwd.js";
|
|
8
8
|
import { getUserSessionPrompt } from "./session-preview.js";
|
|
9
9
|
import { isSessionPath, openSessionReadStream, resolveSessionPath } from "./session-storage.js";
|
|
10
|
+
import { parseForeignTranscript } from "./foreign-session-import.js";
|
|
10
11
|
/**
|
|
11
|
-
* Scan
|
|
12
|
-
* project,
|
|
13
|
-
* `sources` field lists every store the project appeared in so the picker can
|
|
14
|
-
* show a combined badge.
|
|
12
|
+
* Scan EZ Coder + Claude Code + Codex session stores and the user's project
|
|
13
|
+
* folders, returning one deduplicated row per project, most-recent first.
|
|
15
14
|
*/
|
|
16
|
-
export async function discoverProjects() {
|
|
15
|
+
export async function discoverProjects(options = {}) {
|
|
17
16
|
const [ez, cc, cx] = await Promise.all([
|
|
18
17
|
discoverEzcoderProjects(),
|
|
19
18
|
discoverClaudeProjects(),
|
|
20
19
|
discoverCodexProjects(),
|
|
21
20
|
]);
|
|
21
|
+
const fromSessions = [...ez, ...cc, ...cx];
|
|
22
|
+
const folders = await discoverFolderProjects(resolveProjectRoots(options.projectsRoot, options.extraRoots, fromSessions));
|
|
22
23
|
const byPath = new Map();
|
|
23
|
-
for (const p of [...
|
|
24
|
+
for (const p of [...fromSessions, ...folders]) {
|
|
24
25
|
const existing = byPath.get(p.path);
|
|
25
26
|
if (!existing) {
|
|
26
27
|
byPath.set(p.path, p);
|
|
27
28
|
continue;
|
|
28
29
|
}
|
|
30
|
+
// A folder's mtime is not activity: a checkout or a build touching the
|
|
31
|
+
// directory must not reorder a project above one you actually worked in.
|
|
32
|
+
// Session recency wins whenever any session store knows this project.
|
|
33
|
+
const merged = mergeSources(existing.sources, p.sources);
|
|
34
|
+
const sessionOnly = [existing, p].filter((row) => !isFolderOnly(row.sources));
|
|
29
35
|
byPath.set(p.path, {
|
|
30
36
|
name: existing.name,
|
|
31
37
|
path: existing.path,
|
|
32
|
-
lastActiveMs: Math.max(existing
|
|
38
|
+
lastActiveMs: Math.max(...(sessionOnly.length > 0 ? sessionOnly : [existing, p]).map((r) => r.lastActiveMs)),
|
|
33
39
|
lastActiveDisplay: "", // recomputed below
|
|
34
|
-
sources:
|
|
40
|
+
sources: merged,
|
|
35
41
|
});
|
|
36
42
|
}
|
|
37
|
-
|
|
43
|
+
// Hiding is by resolved path so a row dismissed once stays gone regardless of
|
|
44
|
+
// which store re-surfaces it later.
|
|
45
|
+
const hidden = new Set((options.hiddenPaths ?? []).map((p) => path.resolve(p)));
|
|
46
|
+
const merged = Array.from(byPath.values())
|
|
47
|
+
.filter((p) => !hidden.has(p.path))
|
|
48
|
+
.map((p) => ({
|
|
38
49
|
...p,
|
|
39
50
|
lastActiveDisplay: formatRelativeTime(p.lastActiveMs),
|
|
40
51
|
}));
|
|
@@ -45,7 +56,11 @@ const SOURCE_ORDER = {
|
|
|
45
56
|
ezcoder: 0,
|
|
46
57
|
"claude-code": 1,
|
|
47
58
|
codex: 2,
|
|
59
|
+
folder: 3,
|
|
48
60
|
};
|
|
61
|
+
function isFolderOnly(sources) {
|
|
62
|
+
return sources.length === 1 && sources[0] === "folder";
|
|
63
|
+
}
|
|
49
64
|
function mergeSources(a, b) {
|
|
50
65
|
const set = new Set([...a, ...b]);
|
|
51
66
|
return Array.from(set).sort((x, y) => SOURCE_ORDER[x] - SOURCE_ORDER[y]);
|
|
@@ -68,15 +83,17 @@ async function discoverEzcoderProjects() {
|
|
|
68
83
|
catch {
|
|
69
84
|
return [];
|
|
70
85
|
}
|
|
71
|
-
|
|
72
|
-
|
|
86
|
+
// Per-entry work is independent I/O (readdir + stat + a header read), so it
|
|
87
|
+
// runs concurrently; sequentially this dominated picker load time once a user
|
|
88
|
+
// had dozens of session stores.
|
|
89
|
+
const results = await mapConcurrent(entries, async (entry) => {
|
|
73
90
|
const dir = path.join(sessionsDir, entry);
|
|
74
|
-
const mtime = await
|
|
91
|
+
const mtime = await maxEzcoderSessionMtime(dir);
|
|
75
92
|
if (mtime === null)
|
|
76
|
-
|
|
77
|
-
const rawCwd = (await
|
|
93
|
+
return null;
|
|
94
|
+
const rawCwd = (await readFirstFromEzcoderDir(dir, ezcoderCwdExtractor)) ?? fallbackUnderscoreDecode(entry);
|
|
78
95
|
if (!rawCwd)
|
|
79
|
-
|
|
96
|
+
return null;
|
|
80
97
|
// Normalize traversal segments (e.g. an agent launched with cwd
|
|
81
98
|
// `.../src-tauri/../..`) so the basename isn't a stray "..", and drop any
|
|
82
99
|
// Windows extended-length prefix so a session recorded as `\\?\C:\proj`
|
|
@@ -84,16 +101,29 @@ async function discoverEzcoderProjects() {
|
|
|
84
101
|
// project as a plain `C:\proj` instead of listing a prefixed duplicate.
|
|
85
102
|
const cwd = path.resolve(stripExtendedLengthPrefix(rawCwd));
|
|
86
103
|
if (!(await isDirectory(cwd)))
|
|
87
|
-
|
|
88
|
-
|
|
104
|
+
return null;
|
|
105
|
+
return {
|
|
89
106
|
name: path.basename(cwd),
|
|
90
107
|
path: cwd,
|
|
91
108
|
lastActiveMs: mtime,
|
|
92
109
|
lastActiveDisplay: formatRelativeTime(mtime),
|
|
93
110
|
sources: ["ezcoder"],
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return results;
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
return results.filter((p) => p !== null);
|
|
114
|
+
}
|
|
115
|
+
/** Bounded-concurrency Promise.all for large session stores. */
|
|
116
|
+
const IO_CONCURRENCY = 32;
|
|
117
|
+
async function mapConcurrent(items, fn) {
|
|
118
|
+
const out = new Array(items.length);
|
|
119
|
+
let next = 0;
|
|
120
|
+
const workers = Array.from({ length: Math.min(IO_CONCURRENCY, items.length) }, async () => {
|
|
121
|
+
for (let i = next++; i < items.length; i = next++) {
|
|
122
|
+
out[i] = await fn(items[i]);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
await Promise.all(workers);
|
|
126
|
+
return out;
|
|
97
127
|
}
|
|
98
128
|
/**
|
|
99
129
|
* Is `value` an absolute path on any platform?
|
|
@@ -140,7 +170,7 @@ async function discoverClaudeProjects() {
|
|
|
140
170
|
catch {
|
|
141
171
|
return [];
|
|
142
172
|
}
|
|
143
|
-
const results = await
|
|
173
|
+
const results = await mapConcurrent(entries, async (entry) => {
|
|
144
174
|
const dir = path.join(projectsDir, entry);
|
|
145
175
|
const mtime = await maxJsonlMtime(dir);
|
|
146
176
|
if (mtime === null)
|
|
@@ -157,7 +187,7 @@ async function discoverClaudeProjects() {
|
|
|
157
187
|
lastActiveDisplay: formatRelativeTime(mtime),
|
|
158
188
|
sources: ["claude-code"],
|
|
159
189
|
};
|
|
160
|
-
})
|
|
190
|
+
});
|
|
161
191
|
return results.filter((p) => p !== null);
|
|
162
192
|
}
|
|
163
193
|
/**
|
|
@@ -176,28 +206,155 @@ async function discoverCodexProjects() {
|
|
|
176
206
|
return [];
|
|
177
207
|
// Process newest first so per-cwd we always start with the latest mtime.
|
|
178
208
|
files.sort((a, b) => b.mtime - a.mtime);
|
|
209
|
+
const cwds = await mapConcurrent(files, (f) => readFirstFromFile(f.path, codexCwdExtractor));
|
|
179
210
|
const byCwd = new Map();
|
|
180
|
-
|
|
181
|
-
const cwd =
|
|
211
|
+
files.forEach((f, i) => {
|
|
212
|
+
const cwd = cwds[i];
|
|
182
213
|
if (!cwd)
|
|
183
|
-
|
|
214
|
+
return;
|
|
184
215
|
const prev = byCwd.get(cwd);
|
|
185
216
|
if (prev === undefined || f.mtime > prev)
|
|
186
217
|
byCwd.set(cwd, f.mtime);
|
|
187
|
-
}
|
|
188
|
-
const results = []
|
|
189
|
-
for (const [cwd, mtime] of byCwd) {
|
|
218
|
+
});
|
|
219
|
+
const results = await mapConcurrent(Array.from(byCwd), async ([cwd, mtime]) => {
|
|
190
220
|
if (!(await isDirectory(cwd)))
|
|
191
|
-
|
|
192
|
-
|
|
221
|
+
return null;
|
|
222
|
+
return {
|
|
193
223
|
name: path.basename(cwd),
|
|
194
224
|
path: cwd,
|
|
195
225
|
lastActiveMs: mtime,
|
|
196
226
|
lastActiveDisplay: formatRelativeTime(mtime),
|
|
197
227
|
sources: ["codex"],
|
|
198
|
-
}
|
|
228
|
+
};
|
|
229
|
+
});
|
|
230
|
+
return results.filter((p) => p !== null);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* How many already-known projects must share a parent directory before that
|
|
234
|
+
* parent is treated as a project root in its own right. Users keep more than
|
|
235
|
+
* one "projects folder" (the configured root plus wherever the rest actually
|
|
236
|
+
* live), but only one is configurable — so a directory that has repeatedly
|
|
237
|
+
* acted like a root is inferred from evidence rather than guessed.
|
|
238
|
+
*/
|
|
239
|
+
const INFERRED_ROOT_MIN_PROJECTS = 3;
|
|
240
|
+
/** Directory names that are never a project of their own. */
|
|
241
|
+
const FOLDER_SCAN_IGNORED = new Set([
|
|
242
|
+
"node_modules",
|
|
243
|
+
"dist",
|
|
244
|
+
"build",
|
|
245
|
+
"out",
|
|
246
|
+
"target",
|
|
247
|
+
"vendor",
|
|
248
|
+
"coverage",
|
|
249
|
+
"tmp",
|
|
250
|
+
"temp",
|
|
251
|
+
"Library",
|
|
252
|
+
"Applications",
|
|
253
|
+
]);
|
|
254
|
+
/**
|
|
255
|
+
* Which directories should be scanned for project folders: the configured root,
|
|
256
|
+
* plus any parent that already holds several known projects.
|
|
257
|
+
*
|
|
258
|
+
* Scanning the home directory or a filesystem/temp root would list mail, music
|
|
259
|
+
* and scratch dirs as "projects", so those are never roots no matter how many
|
|
260
|
+
* sessions point inside them.
|
|
261
|
+
*/
|
|
262
|
+
function resolveProjectRoots(projectsRoot, extraRoots, discovered) {
|
|
263
|
+
const roots = new Set();
|
|
264
|
+
const configured = projectsRoot?.trim();
|
|
265
|
+
if (configured)
|
|
266
|
+
roots.add(path.resolve(configured));
|
|
267
|
+
for (const extra of extraRoots ?? []) {
|
|
268
|
+
const trimmed = extra.trim();
|
|
269
|
+
if (trimmed)
|
|
270
|
+
roots.add(path.resolve(trimmed));
|
|
199
271
|
}
|
|
200
|
-
|
|
272
|
+
const counts = new Map();
|
|
273
|
+
const seen = new Set();
|
|
274
|
+
for (const project of discovered) {
|
|
275
|
+
if (seen.has(project.path))
|
|
276
|
+
continue;
|
|
277
|
+
seen.add(project.path);
|
|
278
|
+
const parent = path.dirname(project.path);
|
|
279
|
+
if (parent === project.path)
|
|
280
|
+
continue;
|
|
281
|
+
counts.set(parent, (counts.get(parent) ?? 0) + 1);
|
|
282
|
+
}
|
|
283
|
+
for (const [parent, count] of counts) {
|
|
284
|
+
if (count >= INFERRED_ROOT_MIN_PROJECTS && !isUnscannableRoot(parent))
|
|
285
|
+
roots.add(parent);
|
|
286
|
+
}
|
|
287
|
+
return Array.from(roots);
|
|
288
|
+
}
|
|
289
|
+
function isUnscannableRoot(dir) {
|
|
290
|
+
const resolved = resolveExistingPath(dir);
|
|
291
|
+
if (resolved === resolveExistingPath(path.parse(resolved).root))
|
|
292
|
+
return true;
|
|
293
|
+
if (resolved === resolveExistingPath(os.homedir()))
|
|
294
|
+
return true;
|
|
295
|
+
// Scratch checkouts and test fixtures cluster as direct children of the temp
|
|
296
|
+
// dir, which would otherwise infer it as a root and list every stale
|
|
297
|
+
// `tmp.XXXX` as a project. Resolve symlinks before comparing because macOS
|
|
298
|
+
// reports the same temp directory through both `/var` and `/private/var`.
|
|
299
|
+
// Only the temp dir itself is barred — a real projects folder below it is
|
|
300
|
+
// still scannable.
|
|
301
|
+
return resolved === resolveExistingPath(os.tmpdir());
|
|
302
|
+
}
|
|
303
|
+
function resolveExistingPath(dir) {
|
|
304
|
+
const resolved = path.resolve(dir);
|
|
305
|
+
try {
|
|
306
|
+
return realpathSync(resolved);
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
return resolved;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* List the direct children of each project root as projects. Directory mtime
|
|
314
|
+
* stands in for "last active" — imprecise, but these rows are session-less by
|
|
315
|
+
* definition and merging keeps real session recency where it exists.
|
|
316
|
+
*/
|
|
317
|
+
async function discoverFolderProjects(roots) {
|
|
318
|
+
const candidates = [];
|
|
319
|
+
for (const root of roots) {
|
|
320
|
+
let entries;
|
|
321
|
+
try {
|
|
322
|
+
entries = await fs.readdir(root, { withFileTypes: true });
|
|
323
|
+
}
|
|
324
|
+
catch {
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
for (const entry of entries) {
|
|
328
|
+
if (!entry.isDirectory() && !entry.isSymbolicLink())
|
|
329
|
+
continue;
|
|
330
|
+
if (entry.name.startsWith("."))
|
|
331
|
+
continue;
|
|
332
|
+
if (FOLDER_SCAN_IGNORED.has(entry.name))
|
|
333
|
+
continue;
|
|
334
|
+
candidates.push({ name: entry.name, path: path.join(root, entry.name) });
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
// `stat` (not `lstat`) so a symlinked project folder resolves to its target:
|
|
338
|
+
// `readdir` reports a symlink as neither file nor directory, so checking the
|
|
339
|
+
// dirent alone would silently drop linked-in projects.
|
|
340
|
+
const rows = await mapConcurrent(candidates, async (c) => {
|
|
341
|
+
try {
|
|
342
|
+
const stats = await fs.stat(c.path);
|
|
343
|
+
if (!stats.isDirectory())
|
|
344
|
+
return null;
|
|
345
|
+
return {
|
|
346
|
+
name: c.name,
|
|
347
|
+
path: c.path,
|
|
348
|
+
lastActiveMs: stats.mtimeMs,
|
|
349
|
+
lastActiveDisplay: formatRelativeTime(stats.mtimeMs),
|
|
350
|
+
sources: ["folder"],
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
return rows.filter((r) => r !== null);
|
|
201
358
|
}
|
|
202
359
|
async function isDirectory(p) {
|
|
203
360
|
try {
|
|
@@ -220,10 +377,10 @@ async function maxJsonlMtime(dir) {
|
|
|
220
377
|
max = f.mtime;
|
|
221
378
|
return max > 0 ? max : null;
|
|
222
379
|
}
|
|
223
|
-
async function
|
|
380
|
+
async function maxEzcoderSessionMtime(dir) {
|
|
224
381
|
if (!(await isDirectory(dir)))
|
|
225
382
|
return null;
|
|
226
|
-
const files = await
|
|
383
|
+
const files = await collectEzcoderSessionFiles(dir, 2);
|
|
227
384
|
if (files.length === 0)
|
|
228
385
|
return null;
|
|
229
386
|
return Math.max(...files.map((file) => file.mtime));
|
|
@@ -262,7 +419,7 @@ async function collectJsonlFiles(dir, maxDepth) {
|
|
|
262
419
|
}
|
|
263
420
|
}
|
|
264
421
|
}
|
|
265
|
-
async function
|
|
422
|
+
async function collectEzcoderSessionFiles(dir, maxDepth) {
|
|
266
423
|
const byResolvedPath = new Map();
|
|
267
424
|
await walk(dir, 0);
|
|
268
425
|
return [...byResolvedPath.values()];
|
|
@@ -356,20 +513,27 @@ async function readFirstFromJsonlDir(dir, extractor) {
|
|
|
356
513
|
}
|
|
357
514
|
return null;
|
|
358
515
|
}
|
|
359
|
-
async function
|
|
360
|
-
const files = await
|
|
516
|
+
async function readFirstFromEzcoderDir(dir, extractor) {
|
|
517
|
+
const files = await collectEzcoderSessionFiles(dir, 2);
|
|
361
518
|
files.sort((a, b) => b.mtime - a.mtime);
|
|
362
519
|
for (const file of files) {
|
|
363
520
|
try {
|
|
364
|
-
const { stream } = await openSessionReadStream(file.path);
|
|
521
|
+
const { stream, close } = await openSessionReadStream(file.path);
|
|
365
522
|
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
523
|
+
try {
|
|
524
|
+
let lines = 0;
|
|
525
|
+
for await (const line of rl) {
|
|
526
|
+
if (++lines > 200)
|
|
527
|
+
break;
|
|
528
|
+
const value = extractor(line);
|
|
529
|
+
if (value)
|
|
530
|
+
return value;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
finally {
|
|
534
|
+
// Always via close(): destroying the gunzip alone strands the source fd.
|
|
535
|
+
rl.close();
|
|
536
|
+
close();
|
|
373
537
|
}
|
|
374
538
|
}
|
|
375
539
|
catch {
|
|
@@ -464,7 +628,7 @@ function formatRelativeTime(ms) {
|
|
|
464
628
|
*/
|
|
465
629
|
export async function listRecentSessions(cwd, limit = 5, sessionsDir = getAppPaths().sessionsDir) {
|
|
466
630
|
const dir = path.join(sessionsDir, encodeCwd(cwd));
|
|
467
|
-
const files = await
|
|
631
|
+
const files = await collectEzcoderSessionFiles(dir, 1);
|
|
468
632
|
if (files.length === 0)
|
|
469
633
|
return [];
|
|
470
634
|
files.sort((a, b) => b.mtime - a.mtime);
|
|
@@ -484,10 +648,109 @@ export async function listRecentSessions(cwd, limit = 5, sessionsDir = getAppPat
|
|
|
484
648
|
}
|
|
485
649
|
return out;
|
|
486
650
|
}
|
|
651
|
+
/**
|
|
652
|
+
* List the most recent Claude Code and Codex conversations for a project cwd.
|
|
653
|
+
*
|
|
654
|
+
* The project picker has always surfaced these stores (`discoverProjects`), so a
|
|
655
|
+
* project can appear *because* it has Claude Code history — and then show an
|
|
656
|
+
* empty session list, because that only read EZ Coder's own directory. These
|
|
657
|
+
* rows close that gap: each one points at the foreign transcript, tagged with
|
|
658
|
+
* its `source`, and the host imports it on click.
|
|
659
|
+
*
|
|
660
|
+
* Cheap by construction: a transcript is only opened if its cwd matches, and
|
|
661
|
+
* both the per-store file walk and the preview read are line-capped.
|
|
662
|
+
*/
|
|
663
|
+
export async function listForeignSessions(cwd, limit = 5, homeDir = os.homedir()) {
|
|
664
|
+
const [claude, codex] = await Promise.all([
|
|
665
|
+
listClaudeSessions(cwd, limit, homeDir),
|
|
666
|
+
listCodexSessions(cwd, limit, homeDir),
|
|
667
|
+
]);
|
|
668
|
+
return [...claude, ...codex]
|
|
669
|
+
.sort((left, right) => right.lastActiveMs - left.lastActiveMs)
|
|
670
|
+
.slice(0, limit)
|
|
671
|
+
.map(({ lastActiveMs: _lastActiveMs, ...session }) => session);
|
|
672
|
+
}
|
|
673
|
+
async function listClaudeSessions(cwd, limit, homeDir) {
|
|
674
|
+
const projectsDir = path.join(homeDir, ".claude", "projects");
|
|
675
|
+
if (!(await isDirectory(projectsDir)))
|
|
676
|
+
return [];
|
|
677
|
+
// Claude's directory encoding is ambiguous (every "/" becomes "-", colliding
|
|
678
|
+
// with real dashes), so we cannot map cwd → directory. Instead walk the files
|
|
679
|
+
// newest-first and keep the ones whose recorded cwd matches.
|
|
680
|
+
const files = await collectJsonlFiles(projectsDir, 3);
|
|
681
|
+
return collectMatchingForeignSessions(files, cwd, limit, "claude-code", claudeCwdExtractor);
|
|
682
|
+
}
|
|
683
|
+
async function listCodexSessions(cwd, limit, homeDir) {
|
|
684
|
+
const sessionsDir = path.join(homeDir, ".codex", "sessions");
|
|
685
|
+
if (!(await isDirectory(sessionsDir)))
|
|
686
|
+
return [];
|
|
687
|
+
// Layout is YYYY/MM/DD/*.jsonl — depth 4 covers it.
|
|
688
|
+
const files = await collectJsonlFiles(sessionsDir, 4);
|
|
689
|
+
return collectMatchingForeignSessions(files, cwd, limit, "codex", codexCwdExtractor);
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Newest-first scan for transcripts belonging to `cwd`. Stops as soon as
|
|
693
|
+
* `limit` matches are found so a large history costs only the files it reads.
|
|
694
|
+
*/
|
|
695
|
+
async function collectMatchingForeignSessions(files, cwd, limit, source, extractor) {
|
|
696
|
+
if (files.length === 0)
|
|
697
|
+
return [];
|
|
698
|
+
files.sort((left, right) => right.mtime - left.mtime);
|
|
699
|
+
const target = path.resolve(stripExtendedLengthPrefix(cwd));
|
|
700
|
+
const out = [];
|
|
701
|
+
for (const file of files) {
|
|
702
|
+
if (out.length >= limit)
|
|
703
|
+
break;
|
|
704
|
+
const recorded = await readFirstFromFile(file.path, extractor);
|
|
705
|
+
if (!recorded)
|
|
706
|
+
continue;
|
|
707
|
+
if (path.resolve(stripExtendedLengthPrefix(recorded)) !== target)
|
|
708
|
+
continue;
|
|
709
|
+
const summary = await readForeignSessionSummary(file.path, source);
|
|
710
|
+
if (!summary)
|
|
711
|
+
continue;
|
|
712
|
+
out.push({
|
|
713
|
+
...summary,
|
|
714
|
+
lastActiveDisplay: formatRelativeTime(file.mtime),
|
|
715
|
+
lastActiveMs: file.mtime,
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
return out;
|
|
719
|
+
}
|
|
720
|
+
/**
|
|
721
|
+
* Preview + message count for a foreign transcript, using the same parsers the
|
|
722
|
+
* importer uses — so the row's title is exactly the title the imported session
|
|
723
|
+
* ends up with (notably Cursor's `<user_query>` unwrapping).
|
|
724
|
+
*/
|
|
725
|
+
async function readForeignSessionSummary(file, source) {
|
|
726
|
+
let text;
|
|
727
|
+
try {
|
|
728
|
+
text = await fs.readFile(file, "utf-8");
|
|
729
|
+
}
|
|
730
|
+
catch {
|
|
731
|
+
return null;
|
|
732
|
+
}
|
|
733
|
+
try {
|
|
734
|
+
const parsed = parseForeignTranscript(text, source === "codex" ? "codex" : "claude");
|
|
735
|
+
if (parsed.messages.length === 0)
|
|
736
|
+
return null;
|
|
737
|
+
return {
|
|
738
|
+
id: path.basename(file).replace(/\.jsonl$/, ""),
|
|
739
|
+
path: file,
|
|
740
|
+
preview: parsed.preview ?? "(no prompt)",
|
|
741
|
+
messageCount: parsed.messages.length,
|
|
742
|
+
source,
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
catch {
|
|
746
|
+
// An unreadable transcript is skipped, never surfaced as a broken row.
|
|
747
|
+
return null;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
487
750
|
/** Single-pass parse of one session file: identity + count + activity + preview. */
|
|
488
751
|
async function readSessionSummary(file) {
|
|
489
752
|
try {
|
|
490
|
-
const { path: resolvedPath, stream } = await openSessionReadStream(file);
|
|
753
|
+
const { path: resolvedPath, stream, close } = await openSessionReadStream(file);
|
|
491
754
|
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
492
755
|
let id = "";
|
|
493
756
|
let conversationId = "";
|
|
@@ -497,41 +760,48 @@ async function readSessionSummary(file) {
|
|
|
497
760
|
let preview = "";
|
|
498
761
|
let label = "";
|
|
499
762
|
let valid = false;
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
try {
|
|
504
|
-
const entry = JSON.parse(line);
|
|
505
|
-
if (!valid) {
|
|
506
|
-
if (entry.type !== "session")
|
|
507
|
-
return null;
|
|
508
|
-
valid = true;
|
|
509
|
-
id = entry.id ?? "";
|
|
510
|
-
conversationId = entry.conversationId ?? id;
|
|
511
|
-
if (typeof entry.preview === "string") {
|
|
512
|
-
headerPreview = entry.preview.replace(/\s+/g, " ").trim().slice(0, 80);
|
|
513
|
-
}
|
|
514
|
-
if (entry.timestamp)
|
|
515
|
-
lastActivity = entry.timestamp;
|
|
763
|
+
try {
|
|
764
|
+
for await (const line of rl) {
|
|
765
|
+
if (!line)
|
|
516
766
|
continue;
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
767
|
+
try {
|
|
768
|
+
const entry = JSON.parse(line);
|
|
769
|
+
if (!valid) {
|
|
770
|
+
if (entry.type !== "session")
|
|
771
|
+
return null;
|
|
772
|
+
valid = true;
|
|
773
|
+
id = entry.id ?? "";
|
|
774
|
+
conversationId = entry.conversationId ?? id;
|
|
775
|
+
if (typeof entry.preview === "string") {
|
|
776
|
+
headerPreview = entry.preview.replace(/\s+/g, " ").trim().slice(0, 80);
|
|
777
|
+
}
|
|
778
|
+
if (entry.timestamp)
|
|
779
|
+
lastActivity = entry.timestamp;
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
if (entry.type === "label" && typeof entry.label === "string" && entry.label.trim()) {
|
|
783
|
+
label = entry.label.replace(/\s+/g, " ").trim().slice(0, 80);
|
|
784
|
+
}
|
|
785
|
+
else if (entry.type === "message") {
|
|
786
|
+
messageCount += 1;
|
|
787
|
+
if (entry.timestamp)
|
|
788
|
+
lastActivity = entry.timestamp;
|
|
789
|
+
if (!preview && entry.message?.role === "user") {
|
|
790
|
+
const text = getUserSessionPrompt(entry.message.content);
|
|
791
|
+
if (text)
|
|
792
|
+
preview = text.replace(/\s+/g, " ").trim().slice(0, 80);
|
|
793
|
+
}
|
|
529
794
|
}
|
|
530
795
|
}
|
|
796
|
+
catch {
|
|
797
|
+
// Skip malformed lines; archive migration preserves them byte-for-byte.
|
|
798
|
+
}
|
|
531
799
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
800
|
+
}
|
|
801
|
+
finally {
|
|
802
|
+
// `return null` above exits mid-stream; close() destroys gunzip + source.
|
|
803
|
+
rl.close();
|
|
804
|
+
close();
|
|
535
805
|
}
|
|
536
806
|
return valid
|
|
537
807
|
? {
|