@stackmemoryai/stackmemory 0.5.58 → 0.5.61
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 +105 -1
- package/dist/scripts/benchmark-performance.js +48 -0
- package/dist/scripts/benchmark-performance.js.map +7 -0
- package/dist/scripts/check-redis.js +42 -0
- package/dist/scripts/check-redis.js.map +7 -0
- package/dist/scripts/initialize.js +116 -0
- package/dist/scripts/initialize.js.map +7 -0
- package/dist/scripts/list-linear-tasks.js +124 -0
- package/dist/scripts/list-linear-tasks.js.map +7 -0
- package/dist/scripts/measure-handoff-impact.js +340 -0
- package/dist/scripts/measure-handoff-impact.js.map +7 -0
- package/dist/scripts/query-chromadb.js +160 -0
- package/dist/scripts/query-chromadb.js.map +7 -0
- package/dist/scripts/show-linear-summary.js +119 -0
- package/dist/scripts/show-linear-summary.js.map +7 -0
- package/dist/scripts/simple-swarm-demo.js +90 -0
- package/dist/scripts/simple-swarm-demo.js.map +7 -0
- package/dist/scripts/status.js +155 -0
- package/dist/scripts/status.js.map +7 -0
- package/dist/scripts/test-chromadb-sync.js +192 -0
- package/dist/scripts/test-chromadb-sync.js.map +7 -0
- package/dist/scripts/test-ralph-iteration-fix.js +86 -0
- package/dist/scripts/test-ralph-iteration-fix.js.map +7 -0
- package/dist/scripts/test-ralph-iterations.js +121 -0
- package/dist/scripts/test-ralph-iterations.js.map +7 -0
- package/dist/scripts/test-redis-storage.js +389 -0
- package/dist/scripts/test-redis-storage.js.map +7 -0
- package/dist/scripts/test-simple-ralph-state-sync.js +115 -0
- package/dist/scripts/test-simple-ralph-state-sync.js.map +7 -0
- package/dist/scripts/test-swarm-fixes.js +125 -0
- package/dist/scripts/test-swarm-fixes.js.map +7 -0
- package/dist/scripts/test-swarm-tui.js +23 -0
- package/dist/scripts/test-swarm-tui.js.map +7 -0
- package/dist/scripts/test-tui-shortcuts.js +52 -0
- package/dist/scripts/test-tui-shortcuts.js.map +7 -0
- package/dist/scripts/validate-tui-shortcuts.js +60 -0
- package/dist/scripts/validate-tui-shortcuts.js.map +7 -0
- package/dist/src/agents/core/agent-task-manager.js.map +7 -0
- package/dist/src/agents/verifiers/base-verifier.js.map +7 -0
- package/dist/src/agents/verifiers/formatter-verifier.js.map +7 -0
- package/dist/src/agents/verifiers/llm-judge.js.map +7 -0
- package/dist/src/cli/auto-detect.js.map +7 -0
- package/dist/src/cli/claude-sm-danger.js.map +7 -0
- package/dist/src/cli/claude-sm.js +1236 -0
- package/dist/src/cli/claude-sm.js.map +7 -0
- package/dist/src/cli/codex-sm-danger.js.map +7 -0
- package/dist/src/cli/codex-sm.js.map +7 -0
- package/dist/src/cli/commands/api.js.map +7 -0
- package/dist/src/cli/commands/auto-background.js.map +7 -0
- package/dist/src/cli/commands/cleanup-processes.js.map +7 -0
- package/dist/src/cli/commands/clear.js.map +7 -0
- package/dist/src/cli/commands/config.js.map +7 -0
- package/dist/src/cli/commands/context-rehydrate.js.map +7 -0
- package/dist/src/cli/commands/context.js.map +7 -0
- package/dist/src/cli/commands/daemon.js.map +7 -0
- package/dist/src/cli/commands/dashboard.js.map +7 -0
- package/dist/src/cli/commands/db.js.map +7 -0
- package/dist/src/cli/commands/decision.js.map +7 -0
- package/dist/src/cli/commands/discovery.js.map +7 -0
- package/dist/src/cli/commands/handoff.js.map +7 -0
- package/dist/src/cli/commands/hooks.js.map +7 -0
- package/dist/src/cli/commands/linear.js.map +7 -0
- package/dist/src/cli/commands/log.js.map +7 -0
- package/dist/src/cli/commands/login.js.map +7 -0
- package/dist/src/cli/commands/migrate.js.map +7 -0
- package/dist/src/cli/commands/model.js.map +7 -0
- package/dist/src/cli/commands/onboard.js.map +7 -0
- package/dist/src/cli/commands/projects.js.map +7 -0
- package/dist/src/cli/commands/ralph.js.map +7 -0
- package/dist/src/cli/commands/retrieval.js.map +7 -0
- package/dist/src/cli/commands/search.js +173 -0
- package/dist/src/cli/commands/search.js.map +7 -0
- package/dist/src/cli/commands/service.js.map +7 -0
- package/dist/src/cli/commands/session.js.map +7 -0
- package/dist/src/cli/commands/settings.js.map +7 -0
- package/dist/src/cli/commands/setup.js.map +7 -0
- package/dist/src/cli/commands/shell.js.map +7 -0
- package/dist/src/cli/commands/signup.js.map +7 -0
- package/dist/src/cli/commands/skills.js.map +7 -0
- package/dist/src/cli/commands/sms-notify.js.map +7 -0
- package/dist/src/cli/commands/storage-tier.js.map +7 -0
- package/dist/src/cli/commands/sweep.js.map +7 -0
- package/dist/src/cli/commands/tasks.js.map +7 -0
- package/dist/src/cli/commands/worktree.js.map +7 -0
- package/dist/src/cli/index.js +609 -0
- package/dist/src/cli/index.js.map +7 -0
- package/dist/src/cli/opencode-sm.js.map +7 -0
- package/dist/src/cli/utils/viewer.js.map +7 -0
- package/dist/src/core/config/config-manager.js.map +7 -0
- package/dist/src/core/config/feature-flags.js.map +7 -0
- package/dist/src/core/config/storage-config.js.map +7 -0
- package/dist/src/core/config/types.js.map +7 -0
- package/dist/src/core/context/auto-context.js.map +7 -0
- package/dist/src/core/context/dual-stack-manager.js.map +7 -0
- package/dist/src/core/context/enhanced-rehydration.js.map +7 -0
- package/dist/src/core/context/frame-database.js.map +7 -0
- package/dist/src/core/context/frame-digest.js.map +7 -0
- package/dist/src/core/context/frame-handoff-manager.js.map +7 -0
- package/dist/src/core/context/frame-lifecycle-hooks.js.map +7 -0
- package/dist/src/core/context/frame-recovery.js.map +7 -0
- package/dist/src/core/context/frame-stack.js.map +7 -0
- package/dist/src/core/context/index.js.map +7 -0
- package/dist/src/core/context/permission-manager.js.map +7 -0
- package/dist/src/core/context/recursive-context-manager.js.map +7 -0
- package/dist/src/core/context/refactored-frame-manager.js.map +7 -0
- package/dist/src/core/context/shared-context-layer.js.map +7 -0
- package/dist/src/core/context/stack-merge-resolver.js.map +7 -0
- package/dist/src/core/context/validation.js.map +7 -0
- package/dist/src/core/database/batch-operations.js.map +7 -0
- package/dist/src/core/database/connection-pool.js.map +7 -0
- package/dist/src/core/database/database-adapter.js.map +7 -0
- package/dist/src/core/database/migration-manager.js.map +7 -0
- package/dist/src/core/database/query-cache.js.map +7 -0
- package/dist/src/core/database/query-router.js.map +7 -0
- package/dist/src/core/database/sqlite-adapter.js +738 -0
- package/dist/src/core/database/sqlite-adapter.js.map +7 -0
- package/dist/src/core/digest/enhanced-hybrid-digest.js.map +7 -0
- package/dist/src/core/digest/frame-digest-integration.js.map +7 -0
- package/dist/src/core/digest/hybrid-digest-generator.js.map +7 -0
- package/dist/src/core/digest/index.js.map +7 -0
- package/dist/src/core/digest/types.js.map +7 -0
- package/dist/src/core/errors/error-utils.js +208 -0
- package/dist/src/core/errors/error-utils.js.map +7 -0
- package/dist/src/core/errors/index.js +521 -0
- package/dist/src/core/errors/index.js.map +7 -0
- package/dist/src/core/errors/recovery.js.map +7 -0
- package/dist/src/core/execution/parallel-executor.js.map +7 -0
- package/dist/src/core/extensions/custom-tools.js +567 -0
- package/dist/src/core/extensions/custom-tools.js.map +7 -0
- package/dist/src/core/extensions/index.js +55 -0
- package/dist/src/core/extensions/index.js.map +7 -0
- package/dist/src/core/extensions/loader.js +709 -0
- package/dist/src/core/extensions/loader.js.map +7 -0
- package/dist/src/core/extensions/plugin-system.js +506 -0
- package/dist/src/core/extensions/plugin-system.js.map +7 -0
- package/dist/src/core/extensions/provider-adapter.js +617 -0
- package/dist/src/core/extensions/provider-adapter.js.map +7 -0
- package/dist/src/core/extensions/sandbox-runtime.js +664 -0
- package/dist/src/core/extensions/sandbox-runtime.js.map +7 -0
- package/dist/src/core/frame/workflow-templates.js.map +7 -0
- package/dist/src/core/merge/conflict-detector.js.map +7 -0
- package/dist/src/core/merge/index.js.map +7 -0
- package/dist/src/core/merge/resolution-engine.js.map +7 -0
- package/dist/src/core/merge/stack-diff.js.map +7 -0
- package/dist/src/core/merge/unified-merge-resolver.js +303 -0
- package/dist/src/core/merge/unified-merge-resolver.js.map +7 -0
- package/dist/src/core/models/fallback-monitor.js.map +7 -0
- package/dist/src/core/models/model-router.js.map +7 -0
- package/dist/src/core/monitoring/error-handler.js.map +7 -0
- package/dist/src/core/monitoring/logger.js +202 -0
- package/dist/src/core/monitoring/logger.js.map +7 -0
- package/dist/src/core/monitoring/metrics.js.map +7 -0
- package/dist/src/core/monitoring/progress-tracker.js.map +7 -0
- package/dist/src/core/monitoring/session-monitor.js.map +7 -0
- package/dist/src/core/performance/context-cache.js.map +7 -0
- package/dist/src/core/performance/index.js.map +7 -0
- package/dist/src/core/performance/lazy-context-loader.js.map +7 -0
- package/dist/src/core/performance/monitor.js.map +7 -0
- package/dist/src/core/performance/optimized-frame-context.js.map +7 -0
- package/dist/src/core/performance/performance-benchmark.js.map +7 -0
- package/dist/src/core/performance/performance-profiler.js.map +7 -0
- package/dist/src/core/performance/streaming-jsonl-parser.js.map +7 -0
- package/dist/src/core/persistence/postgres-adapter.js.map +7 -0
- package/dist/src/core/projects/project-isolation.js.map +7 -0
- package/dist/src/core/projects/project-manager.js.map +7 -0
- package/dist/src/core/query/query-parser.js.map +7 -0
- package/dist/src/core/query/query-templates.js.map +7 -0
- package/dist/src/core/retrieval/context-retriever.js.map +7 -0
- package/dist/src/core/retrieval/index.js.map +7 -0
- package/dist/src/core/retrieval/llm-context-retrieval.js.map +7 -0
- package/dist/src/core/retrieval/llm-provider.js.map +7 -0
- package/dist/src/core/retrieval/retrieval-audit.js.map +7 -0
- package/dist/src/core/retrieval/summary-generator.js.map +7 -0
- package/dist/src/core/retrieval/types.js.map +7 -0
- package/dist/src/core/security/index.js +35 -0
- package/dist/src/core/security/index.js.map +7 -0
- package/dist/src/core/security/input-sanitizer.js +321 -0
- package/dist/src/core/security/input-sanitizer.js.map +7 -0
- package/dist/src/core/session/clear-survival.js.map +7 -0
- package/dist/src/core/session/enhanced-handoff.js.map +7 -0
- package/dist/src/core/session/handoff-generator.js.map +7 -0
- package/dist/src/core/session/index.js.map +7 -0
- package/dist/src/core/session/session-manager.js.map +7 -0
- package/dist/src/core/skills/index.js.map +7 -0
- package/dist/src/core/skills/skill-storage.js.map +7 -0
- package/dist/src/core/skills/types.js.map +7 -0
- package/dist/src/core/storage/chromadb-adapter.js +380 -0
- package/dist/src/core/storage/chromadb-adapter.js.map +7 -0
- package/dist/src/core/storage/infinite-storage.js.map +7 -0
- package/dist/src/core/storage/remote-storage.js.map +7 -0
- package/dist/src/core/storage/two-tier-storage.js.map +7 -0
- package/dist/src/core/trace/cli-trace-wrapper.js.map +7 -0
- package/dist/src/core/trace/db-trace-wrapper.js.map +7 -0
- package/dist/src/core/trace/debug-trace.js.map +7 -0
- package/dist/src/core/trace/index.js.map +7 -0
- package/dist/src/core/trace/linear-api-wrapper.js.map +7 -0
- package/dist/src/core/trace/trace-detector.js.map +7 -0
- package/dist/src/core/trace/trace-store.js.map +7 -0
- package/dist/src/core/trace/types.js.map +7 -0
- package/dist/src/core/utils/async-mutex.js.map +7 -0
- package/dist/src/core/utils/compression.js.map +7 -0
- package/dist/src/core/utils/update-checker.js.map +7 -0
- package/dist/src/core/worktree/worktree-manager.js.map +7 -0
- package/dist/src/daemon/daemon-config.js.map +7 -0
- package/dist/src/daemon/services/context-service.js.map +7 -0
- package/dist/src/daemon/services/linear-service.js.map +7 -0
- package/dist/src/daemon/session-daemon.js.map +7 -0
- package/dist/src/daemon/unified-daemon.js.map +7 -0
- package/dist/src/features/analytics/api/analytics-api.js.map +7 -0
- package/dist/src/features/analytics/core/analytics-service.js.map +7 -0
- package/dist/src/features/analytics/index.js.map +7 -0
- package/dist/src/features/analytics/queries/metrics-queries.js.map +7 -0
- package/dist/src/features/browser/browser-mcp.js.map +7 -0
- package/dist/src/features/sweep/index.js.map +7 -0
- package/dist/src/features/sweep/prediction-client.js.map +7 -0
- package/dist/src/features/sweep/prompt-builder.js.map +7 -0
- package/dist/src/features/sweep/pty-wrapper.js.map +7 -0
- package/dist/src/features/sweep/state-watcher.js.map +7 -0
- package/dist/src/features/sweep/status-bar.js.map +7 -0
- package/dist/src/features/sweep/sweep-server-manager.js.map +7 -0
- package/dist/src/features/sweep/tab-interceptor.js.map +7 -0
- package/dist/src/features/sweep/types.js.map +7 -0
- package/dist/src/features/tasks/linear-task-manager.js.map +7 -0
- package/dist/src/features/tasks/task-aware-context.js.map +7 -0
- package/dist/src/features/tui/simple-monitor.js.map +7 -0
- package/dist/src/features/tui/swarm-monitor.js.map +7 -0
- package/dist/src/features/web/client/stores/task-store.js.map +7 -0
- package/dist/src/features/web/server/index.js.map +7 -0
- package/dist/src/hooks/auto-background.js.map +7 -0
- package/dist/src/hooks/claude-code-whatsapp-hook.js.map +7 -0
- package/dist/src/hooks/config.js.map +7 -0
- package/dist/src/hooks/daemon.js.map +7 -0
- package/dist/src/hooks/events.js.map +7 -0
- package/dist/src/hooks/index.js.map +7 -0
- package/dist/src/hooks/linear-task-picker.js.map +7 -0
- package/dist/src/hooks/schemas.js.map +7 -0
- package/dist/src/hooks/secure-fs.js.map +7 -0
- package/dist/src/hooks/security-logger.js.map +7 -0
- package/dist/src/hooks/session-summary.js.map +7 -0
- package/dist/src/hooks/sms-action-runner.js.map +7 -0
- package/dist/src/hooks/sms-notify.js.map +7 -0
- package/dist/src/hooks/sms-watcher.js.map +7 -0
- package/dist/src/hooks/sms-webhook.js.map +7 -0
- package/dist/src/hooks/whatsapp-commands.js.map +7 -0
- package/dist/src/hooks/whatsapp-scheduler.js.map +7 -0
- package/dist/src/hooks/whatsapp-sync.js.map +7 -0
- package/dist/src/index.js.map +7 -0
- package/dist/src/integrations/anthropic/client.js.map +7 -0
- package/dist/src/integrations/claude-code/agent-bridge.js.map +7 -0
- package/dist/src/integrations/claude-code/enhanced-pre-clear-hooks.js.map +7 -0
- package/dist/src/integrations/claude-code/lifecycle-hooks.js.map +7 -0
- package/dist/src/integrations/claude-code/post-task-hooks.js.map +7 -0
- package/dist/src/integrations/claude-code/subagent-client-stub.js.map +7 -0
- package/dist/src/integrations/claude-code/subagent-client.js.map +7 -0
- package/dist/src/integrations/claude-code/task-coordinator.js.map +7 -0
- package/dist/src/integrations/linear/auth.js.map +7 -0
- package/dist/src/integrations/linear/auto-sync.js.map +7 -0
- package/dist/src/integrations/linear/client.js +634 -0
- package/dist/src/integrations/linear/client.js.map +7 -0
- package/dist/src/integrations/linear/config.js.map +7 -0
- package/dist/src/integrations/linear/migration.js.map +7 -0
- package/dist/src/integrations/linear/oauth-server.js.map +7 -0
- package/dist/src/integrations/linear/rest-client.js.map +7 -0
- package/dist/src/integrations/linear/sync-manager.js.map +7 -0
- package/dist/src/integrations/linear/sync-service.js.map +7 -0
- package/dist/src/integrations/linear/sync.js.map +7 -0
- package/dist/src/integrations/linear/unified-sync.js.map +7 -0
- package/dist/src/integrations/linear/webhook-handler.js.map +7 -0
- package/dist/src/integrations/linear/webhook-server.js.map +7 -0
- package/dist/src/integrations/linear/webhook.js.map +7 -0
- package/dist/src/integrations/mcp/handlers/code-execution-handlers.js.map +7 -0
- package/dist/src/integrations/mcp/handlers/context-handlers.js.map +7 -0
- package/dist/src/integrations/mcp/handlers/discovery-handlers.js.map +7 -0
- package/dist/src/integrations/mcp/handlers/index.js.map +7 -0
- package/dist/src/integrations/mcp/handlers/linear-handlers.js.map +7 -0
- package/dist/src/integrations/mcp/handlers/skill-handlers.js.map +7 -0
- package/dist/src/integrations/mcp/handlers/task-handlers.js.map +7 -0
- package/dist/src/integrations/mcp/handlers/trace-handlers.js.map +7 -0
- package/dist/src/integrations/mcp/index.js.map +7 -0
- package/dist/src/integrations/mcp/middleware/tool-scoring.js.map +7 -0
- package/dist/src/integrations/mcp/refactored-server.js.map +7 -0
- package/dist/src/integrations/mcp/remote-server.js +682 -0
- package/dist/src/integrations/mcp/remote-server.js.map +7 -0
- package/dist/src/integrations/mcp/schemas.js.map +7 -0
- package/dist/src/integrations/mcp/server.js +1975 -0
- package/dist/src/integrations/mcp/server.js.map +7 -0
- package/dist/src/integrations/mcp/tool-definitions-code.js.map +7 -0
- package/dist/src/integrations/mcp/tool-definitions.js.map +7 -0
- package/dist/src/integrations/ralph/bridge/ralph-stackmemory-bridge.js.map +7 -0
- package/dist/src/integrations/ralph/context/context-budget-manager.js.map +7 -0
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js.map +7 -0
- package/dist/src/integrations/ralph/coordination/enhanced-coordination.js.map +7 -0
- package/dist/src/integrations/ralph/index.js.map +7 -0
- package/dist/src/integrations/ralph/learning/pattern-learner.js.map +7 -0
- package/dist/src/integrations/ralph/lifecycle/iteration-lifecycle.js.map +7 -0
- package/dist/src/integrations/ralph/monitoring/swarm-dashboard.js.map +7 -0
- package/dist/src/integrations/ralph/monitoring/swarm-registry.js.map +7 -0
- package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js.map +7 -0
- package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js.map +7 -0
- package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js.map +7 -0
- package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js.map +7 -0
- package/dist/src/integrations/ralph/performance/performance-optimizer.js.map +7 -0
- package/dist/src/integrations/ralph/recovery/crash-recovery.js.map +7 -0
- package/dist/src/integrations/ralph/state/state-reconciler.js.map +7 -0
- package/dist/src/integrations/ralph/swarm/git-workflow-manager.js.map +7 -0
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js.map +7 -0
- package/dist/src/integrations/ralph/types.js +5 -0
- package/dist/src/integrations/ralph/visualization/ralph-debugger.js.map +7 -0
- package/dist/src/mcp/stackmemory-mcp-server.js.map +7 -0
- package/dist/src/middleware/exponential-rate-limiter.js.map +7 -0
- package/dist/src/models/user.model.js.map +7 -0
- package/dist/src/servers/production/auth-middleware.js.map +7 -0
- package/dist/src/services/config-service.js.map +7 -0
- package/dist/src/services/context-service.js.map +7 -0
- package/dist/src/skills/api-discovery.js.map +7 -0
- package/dist/src/skills/api-skill.js.map +7 -0
- package/dist/src/skills/claude-skills.js.map +7 -0
- package/dist/src/skills/dashboard-launcher.js.map +7 -0
- package/dist/src/skills/recursive-agent-orchestrator.js.map +7 -0
- package/dist/src/skills/repo-ingestion-skill.js +632 -0
- package/dist/src/skills/repo-ingestion-skill.js.map +7 -0
- package/dist/src/skills/unified-rlm-orchestrator.js.map +7 -0
- package/dist/src/types/task.js.map +7 -0
- package/dist/src/utils/env.js.map +7 -0
- package/dist/src/utils/formatting.js.map +7 -0
- package/dist/src/utils/process-cleanup.js.map +7 -0
- package/package.json +13 -9
- package/scripts/background-sync-manager.js +145 -83
- package/scripts/claude-sm-autostart.js +17 -12
- package/scripts/gepa/README.md +275 -0
- package/scripts/gepa/config.json +53 -0
- package/scripts/gepa/evals/coding-tasks.jsonl +5 -0
- package/scripts/gepa/evals/fixtures/buggy-loop.js +18 -0
- package/scripts/gepa/evals/fixtures/callback-hell.js +53 -0
- package/scripts/gepa/generations/gen-000/baseline.md +124 -0
- package/scripts/gepa/hooks/auto-optimize.js +494 -0
- package/scripts/gepa/hooks/eval-tracker.js +203 -0
- package/scripts/gepa/hooks/reflect.js +311 -0
- package/scripts/gepa/optimize.js +611 -0
- package/scripts/gepa/state.json +14 -0
- package/scripts/initialize.ts +16 -7
- package/scripts/install.sh +14 -62
- package/scripts/status.ts +111 -46
- package/scripts/test-pre-publish-quick.sh +1 -1
- package/dist/agents/core/agent-task-manager.js.map +0 -7
- package/dist/agents/testing-agent.js +0 -614
- package/dist/agents/testing-agent.js.map +0 -7
- package/dist/agents/verifiers/base-verifier.js.map +0 -7
- package/dist/agents/verifiers/formatter-verifier.js.map +0 -7
- package/dist/agents/verifiers/llm-judge.js.map +0 -7
- package/dist/cli/auto-detect.js.map +0 -7
- package/dist/cli/browser-test.js +0 -33
- package/dist/cli/browser-test.js.map +0 -7
- package/dist/cli/claude-sm-danger.js.map +0 -7
- package/dist/cli/claude-sm.js +0 -1156
- package/dist/cli/claude-sm.js.map +0 -7
- package/dist/cli/codex-sm-danger.js.map +0 -7
- package/dist/cli/codex-sm.js.map +0 -7
- package/dist/cli/commands/api.js.map +0 -7
- package/dist/cli/commands/auto-background.js.map +0 -7
- package/dist/cli/commands/cleanup-processes.js.map +0 -7
- package/dist/cli/commands/clear.js.map +0 -7
- package/dist/cli/commands/config.js.map +0 -7
- package/dist/cli/commands/context-rehydrate.js.map +0 -7
- package/dist/cli/commands/context.js.map +0 -7
- package/dist/cli/commands/daemon.js.map +0 -7
- package/dist/cli/commands/dashboard.js.map +0 -7
- package/dist/cli/commands/db.js.map +0 -7
- package/dist/cli/commands/decision.js.map +0 -7
- package/dist/cli/commands/discovery.js.map +0 -7
- package/dist/cli/commands/handoff.js.map +0 -7
- package/dist/cli/commands/hooks.js.map +0 -7
- package/dist/cli/commands/linear-unified.js +0 -353
- package/dist/cli/commands/linear-unified.js.map +0 -7
- package/dist/cli/commands/linear.js.map +0 -7
- package/dist/cli/commands/log.js.map +0 -7
- package/dist/cli/commands/login.js.map +0 -7
- package/dist/cli/commands/migrate.js.map +0 -7
- package/dist/cli/commands/model.js.map +0 -7
- package/dist/cli/commands/monitor.js +0 -313
- package/dist/cli/commands/monitor.js.map +0 -7
- package/dist/cli/commands/onboard.js.map +0 -7
- package/dist/cli/commands/projects.js.map +0 -7
- package/dist/cli/commands/quality.js +0 -413
- package/dist/cli/commands/quality.js.map +0 -7
- package/dist/cli/commands/ralph.js.map +0 -7
- package/dist/cli/commands/retrieval.js.map +0 -7
- package/dist/cli/commands/search.js +0 -156
- package/dist/cli/commands/search.js.map +0 -7
- package/dist/cli/commands/service.js.map +0 -7
- package/dist/cli/commands/session.js.map +0 -7
- package/dist/cli/commands/settings.js.map +0 -7
- package/dist/cli/commands/setup.js.map +0 -7
- package/dist/cli/commands/shell.js.map +0 -7
- package/dist/cli/commands/signup.js.map +0 -7
- package/dist/cli/commands/skills.js.map +0 -7
- package/dist/cli/commands/sms-notify.js.map +0 -7
- package/dist/cli/commands/storage-tier.js.map +0 -7
- package/dist/cli/commands/storage.js +0 -360
- package/dist/cli/commands/storage.js.map +0 -7
- package/dist/cli/commands/sweep.js.map +0 -7
- package/dist/cli/commands/tasks.js.map +0 -7
- package/dist/cli/commands/test.js +0 -286
- package/dist/cli/commands/test.js.map +0 -7
- package/dist/cli/commands/workflow.js +0 -142
- package/dist/cli/commands/workflow.js.map +0 -7
- package/dist/cli/commands/worktree.js.map +0 -7
- package/dist/cli/index.js +0 -594
- package/dist/cli/index.js.map +0 -7
- package/dist/cli/opencode-sm.js.map +0 -7
- package/dist/cli/utils/viewer.js.map +0 -7
- package/dist/core/analytics/team-analytics.js +0 -378
- package/dist/core/analytics/team-analytics.js.map +0 -7
- package/dist/core/config/config-manager.js.map +0 -7
- package/dist/core/config/feature-flags.js.map +0 -7
- package/dist/core/config/storage-config.js.map +0 -7
- package/dist/core/config/types.js.map +0 -7
- package/dist/core/context/auto-context.js.map +0 -7
- package/dist/core/context/dual-stack-manager.js.map +0 -7
- package/dist/core/context/enhanced-rehydration.js.map +0 -7
- package/dist/core/context/frame-database.js.map +0 -7
- package/dist/core/context/frame-digest.js.map +0 -7
- package/dist/core/context/frame-handoff-manager.js.map +0 -7
- package/dist/core/context/frame-lifecycle-hooks.js.map +0 -7
- package/dist/core/context/frame-manager.js +0 -1069
- package/dist/core/context/frame-manager.js.map +0 -7
- package/dist/core/context/frame-recovery.js.map +0 -7
- package/dist/core/context/frame-stack.js.map +0 -7
- package/dist/core/context/incremental-gc.js +0 -290
- package/dist/core/context/incremental-gc.js.map +0 -7
- package/dist/core/context/index.js.map +0 -7
- package/dist/core/context/model-aware-compaction.js +0 -623
- package/dist/core/context/model-aware-compaction.js.map +0 -7
- package/dist/core/context/permission-manager.js.map +0 -7
- package/dist/core/context/recursive-context-manager.js.map +0 -7
- package/dist/core/context/refactored-frame-manager.js.map +0 -7
- package/dist/core/context/shared-context-layer.js.map +0 -7
- package/dist/core/context/stack-merge-resolver.js.map +0 -7
- package/dist/core/context/validation.js.map +0 -7
- package/dist/core/database/batch-operations.js.map +0 -7
- package/dist/core/database/connection-pool.js.map +0 -7
- package/dist/core/database/database-adapter.js.map +0 -7
- package/dist/core/database/migration-manager.js.map +0 -7
- package/dist/core/database/paradedb-adapter.js +0 -990
- package/dist/core/database/paradedb-adapter.js.map +0 -7
- package/dist/core/database/query-cache.js.map +0 -7
- package/dist/core/database/query-router.js.map +0 -7
- package/dist/core/database/sqlite-adapter.js +0 -728
- package/dist/core/database/sqlite-adapter.js.map +0 -7
- package/dist/core/digest/enhanced-hybrid-digest.js.map +0 -7
- package/dist/core/digest/frame-digest-integration.js.map +0 -7
- package/dist/core/digest/hybrid-digest-generator.js.map +0 -7
- package/dist/core/digest/index.js.map +0 -7
- package/dist/core/digest/types.js.map +0 -7
- package/dist/core/errors/index.js +0 -512
- package/dist/core/errors/index.js.map +0 -7
- package/dist/core/errors/recovery.js.map +0 -7
- package/dist/core/execution/parallel-executor.js.map +0 -7
- package/dist/core/frame/workflow-templates.js.map +0 -7
- package/dist/core/merge/conflict-detector.js.map +0 -7
- package/dist/core/merge/index.js.map +0 -7
- package/dist/core/merge/resolution-engine.js.map +0 -7
- package/dist/core/merge/stack-diff.js.map +0 -7
- package/dist/core/models/fallback-monitor.js.map +0 -7
- package/dist/core/models/model-router.js.map +0 -7
- package/dist/core/monitoring/error-handler.js.map +0 -7
- package/dist/core/monitoring/logger.js +0 -150
- package/dist/core/monitoring/logger.js.map +0 -7
- package/dist/core/monitoring/metrics.js.map +0 -7
- package/dist/core/monitoring/progress-tracker.js.map +0 -7
- package/dist/core/monitoring/session-monitor.js.map +0 -7
- package/dist/core/performance/context-cache.js.map +0 -7
- package/dist/core/performance/index.js.map +0 -7
- package/dist/core/performance/lazy-context-loader.js.map +0 -7
- package/dist/core/performance/monitor.js.map +0 -7
- package/dist/core/performance/optimized-frame-context.js.map +0 -7
- package/dist/core/performance/performance-benchmark.js.map +0 -7
- package/dist/core/performance/performance-profiler.js.map +0 -7
- package/dist/core/performance/streaming-jsonl-parser.js.map +0 -7
- package/dist/core/persistence/postgres-adapter.js.map +0 -7
- package/dist/core/projects/project-isolation.js.map +0 -7
- package/dist/core/projects/project-manager.js.map +0 -7
- package/dist/core/query/query-parser.js.map +0 -7
- package/dist/core/query/query-templates.js.map +0 -7
- package/dist/core/retrieval/context-retriever.js.map +0 -7
- package/dist/core/retrieval/graph-retrieval.js +0 -662
- package/dist/core/retrieval/graph-retrieval.js.map +0 -7
- package/dist/core/retrieval/hierarchical-retrieval.js +0 -656
- package/dist/core/retrieval/hierarchical-retrieval.js.map +0 -7
- package/dist/core/retrieval/index.js.map +0 -7
- package/dist/core/retrieval/llm-context-retrieval.js.map +0 -7
- package/dist/core/retrieval/llm-provider.js.map +0 -7
- package/dist/core/retrieval/retrieval-audit.js.map +0 -7
- package/dist/core/retrieval/retrieval-benchmarks.js +0 -521
- package/dist/core/retrieval/retrieval-benchmarks.js.map +0 -7
- package/dist/core/retrieval/summary-generator.js.map +0 -7
- package/dist/core/retrieval/types.js.map +0 -7
- package/dist/core/session/clear-survival.js.map +0 -7
- package/dist/core/session/enhanced-handoff.js.map +0 -7
- package/dist/core/session/handoff-generator.js.map +0 -7
- package/dist/core/session/index.js.map +0 -7
- package/dist/core/session/session-manager.js.map +0 -7
- package/dist/core/skills/index.js.map +0 -7
- package/dist/core/skills/skill-storage.js.map +0 -7
- package/dist/core/skills/types.js.map +0 -7
- package/dist/core/storage/chromadb-adapter.js +0 -354
- package/dist/core/storage/chromadb-adapter.js.map +0 -7
- package/dist/core/storage/infinite-storage.js.map +0 -7
- package/dist/core/storage/railway-optimized-storage.js +0 -591
- package/dist/core/storage/railway-optimized-storage.js.map +0 -7
- package/dist/core/storage/remote-storage.js.map +0 -7
- package/dist/core/storage/two-tier-storage.js.map +0 -7
- package/dist/core/trace/cli-trace-wrapper.js.map +0 -7
- package/dist/core/trace/db-trace-wrapper.js.map +0 -7
- package/dist/core/trace/debug-trace.js.map +0 -7
- package/dist/core/trace/index.js.map +0 -7
- package/dist/core/trace/linear-api-wrapper.js.map +0 -7
- package/dist/core/trace/trace-demo.js +0 -154
- package/dist/core/trace/trace-demo.js.map +0 -7
- package/dist/core/trace/trace-detector.demo.js +0 -142
- package/dist/core/trace/trace-detector.demo.js.map +0 -7
- package/dist/core/trace/trace-detector.js.map +0 -7
- package/dist/core/trace/trace-store.js.map +0 -7
- package/dist/core/trace/types.js.map +0 -7
- package/dist/core/utils/async-mutex.js.map +0 -7
- package/dist/core/utils/compression.js.map +0 -7
- package/dist/core/utils/update-checker.js.map +0 -7
- package/dist/core/worktree/worktree-manager.js.map +0 -7
- package/dist/daemon/daemon-config.js.map +0 -7
- package/dist/daemon/services/context-service.js.map +0 -7
- package/dist/daemon/services/linear-service.js.map +0 -7
- package/dist/daemon/session-daemon.js.map +0 -7
- package/dist/daemon/unified-daemon.js.map +0 -7
- package/dist/features/analytics/api/analytics-api.js.map +0 -7
- package/dist/features/analytics/core/analytics-service.js.map +0 -7
- package/dist/features/analytics/index.js.map +0 -7
- package/dist/features/analytics/queries/metrics-queries.js.map +0 -7
- package/dist/features/browser/browser-mcp.js.map +0 -7
- package/dist/features/sweep/index.js.map +0 -7
- package/dist/features/sweep/prediction-client.js.map +0 -7
- package/dist/features/sweep/prompt-builder.js.map +0 -7
- package/dist/features/sweep/pty-wrapper.js.map +0 -7
- package/dist/features/sweep/state-watcher.js.map +0 -7
- package/dist/features/sweep/status-bar.js.map +0 -7
- package/dist/features/sweep/sweep-server-manager.js.map +0 -7
- package/dist/features/sweep/tab-interceptor.js.map +0 -7
- package/dist/features/sweep/types.js.map +0 -7
- package/dist/features/tasks/linear-task-manager.js.map +0 -7
- package/dist/features/tasks/task-aware-context.js.map +0 -7
- package/dist/features/tui/simple-monitor.js.map +0 -7
- package/dist/features/tui/swarm-monitor.js.map +0 -7
- package/dist/features/web/client/stores/task-store.js.map +0 -7
- package/dist/features/web/server/index.js.map +0 -7
- package/dist/hooks/auto-background.js.map +0 -7
- package/dist/hooks/claude-code-whatsapp-hook.js.map +0 -7
- package/dist/hooks/config.js.map +0 -7
- package/dist/hooks/daemon.js.map +0 -7
- package/dist/hooks/events.js.map +0 -7
- package/dist/hooks/index.js.map +0 -7
- package/dist/hooks/linear-task-picker.js.map +0 -7
- package/dist/hooks/schemas.js.map +0 -7
- package/dist/hooks/secure-fs.js.map +0 -7
- package/dist/hooks/security-logger.js.map +0 -7
- package/dist/hooks/session-summary.js.map +0 -7
- package/dist/hooks/sms-action-runner.js.map +0 -7
- package/dist/hooks/sms-notify.js.map +0 -7
- package/dist/hooks/sms-watcher.js.map +0 -7
- package/dist/hooks/sms-webhook.js.map +0 -7
- package/dist/hooks/whatsapp-commands.js.map +0 -7
- package/dist/hooks/whatsapp-scheduler.js.map +0 -7
- package/dist/hooks/whatsapp-sync.js.map +0 -7
- package/dist/index.js.map +0 -7
- package/dist/integrations/anthropic/client.js.map +0 -7
- package/dist/integrations/claude-code/agent-bridge.js.map +0 -7
- package/dist/integrations/claude-code/enhanced-pre-clear-hooks.js.map +0 -7
- package/dist/integrations/claude-code/lifecycle-hooks.js.map +0 -7
- package/dist/integrations/claude-code/post-task-hooks.js.map +0 -7
- package/dist/integrations/claude-code/subagent-client-stub.js.map +0 -7
- package/dist/integrations/claude-code/subagent-client.js.map +0 -7
- package/dist/integrations/claude-code/task-coordinator.js.map +0 -7
- package/dist/integrations/linear/auth.js.map +0 -7
- package/dist/integrations/linear/auto-sync.js.map +0 -7
- package/dist/integrations/linear/client.js +0 -630
- package/dist/integrations/linear/client.js.map +0 -7
- package/dist/integrations/linear/config.js.map +0 -7
- package/dist/integrations/linear/migration.js.map +0 -7
- package/dist/integrations/linear/oauth-server.js.map +0 -7
- package/dist/integrations/linear/rest-client.js.map +0 -7
- package/dist/integrations/linear/sync-manager.js.map +0 -7
- package/dist/integrations/linear/sync-service.js.map +0 -7
- package/dist/integrations/linear/sync.js.map +0 -7
- package/dist/integrations/linear/unified-sync.js.map +0 -7
- package/dist/integrations/linear/webhook-handler.js.map +0 -7
- package/dist/integrations/linear/webhook-server.js.map +0 -7
- package/dist/integrations/linear/webhook.js.map +0 -7
- package/dist/integrations/mcp/handlers/code-execution-handlers.js.map +0 -7
- package/dist/integrations/mcp/handlers/context-handlers.js.map +0 -7
- package/dist/integrations/mcp/handlers/discovery-handlers.js.map +0 -7
- package/dist/integrations/mcp/handlers/index.js.map +0 -7
- package/dist/integrations/mcp/handlers/linear-handlers.js.map +0 -7
- package/dist/integrations/mcp/handlers/skill-handlers.js.map +0 -7
- package/dist/integrations/mcp/handlers/task-handlers.js.map +0 -7
- package/dist/integrations/mcp/handlers/trace-handlers.js.map +0 -7
- package/dist/integrations/mcp/index.js.map +0 -7
- package/dist/integrations/mcp/middleware/tool-scoring.js.map +0 -7
- package/dist/integrations/mcp/refactored-server.js.map +0 -7
- package/dist/integrations/mcp/remote-server.js +0 -691
- package/dist/integrations/mcp/remote-server.js.map +0 -7
- package/dist/integrations/mcp/schemas.js.map +0 -7
- package/dist/integrations/mcp/server.js +0 -1960
- package/dist/integrations/mcp/server.js.map +0 -7
- package/dist/integrations/mcp/tool-definitions-code.js.map +0 -7
- package/dist/integrations/mcp/tool-definitions.js.map +0 -7
- package/dist/integrations/mcp/trace-test.js +0 -48
- package/dist/integrations/mcp/trace-test.js.map +0 -7
- package/dist/integrations/pg-aiguide/embedding-provider.js +0 -189
- package/dist/integrations/pg-aiguide/embedding-provider.js.map +0 -7
- package/dist/integrations/pg-aiguide/semantic-search.js +0 -187
- package/dist/integrations/pg-aiguide/semantic-search.js.map +0 -7
- package/dist/integrations/pg-aiguide/timescale-analytics.js +0 -224
- package/dist/integrations/pg-aiguide/timescale-analytics.js.map +0 -7
- package/dist/integrations/ralph/bridge/ralph-stackmemory-bridge.js.map +0 -7
- package/dist/integrations/ralph/context/context-budget-manager.js.map +0 -7
- package/dist/integrations/ralph/context/stackmemory-context-loader.js.map +0 -7
- package/dist/integrations/ralph/coordination/enhanced-coordination.js.map +0 -7
- package/dist/integrations/ralph/index.js.map +0 -7
- package/dist/integrations/ralph/learning/pattern-learner.js.map +0 -7
- package/dist/integrations/ralph/lifecycle/iteration-lifecycle.js.map +0 -7
- package/dist/integrations/ralph/monitoring/swarm-dashboard.js.map +0 -7
- package/dist/integrations/ralph/monitoring/swarm-registry.js.map +0 -7
- package/dist/integrations/ralph/orchestration/multi-loop-orchestrator.js.map +0 -7
- package/dist/integrations/ralph/patterns/compounding-engineering-pattern.js.map +0 -7
- package/dist/integrations/ralph/patterns/extended-coherence-sessions.js.map +0 -7
- package/dist/integrations/ralph/patterns/oracle-worker-pattern.js.map +0 -7
- package/dist/integrations/ralph/performance/performance-optimizer.js.map +0 -7
- package/dist/integrations/ralph/ralph-integration-demo.js +0 -182
- package/dist/integrations/ralph/ralph-integration-demo.js.map +0 -7
- package/dist/integrations/ralph/recovery/crash-recovery.js.map +0 -7
- package/dist/integrations/ralph/state/state-reconciler.js.map +0 -7
- package/dist/integrations/ralph/swarm/git-workflow-manager.js.map +0 -7
- package/dist/integrations/ralph/swarm/swarm-coordinator.js.map +0 -7
- package/dist/integrations/ralph/visualization/ralph-debugger.js.map +0 -7
- package/dist/mcp/stackmemory-mcp-server.js.map +0 -7
- package/dist/middleware/exponential-rate-limiter.js.map +0 -7
- package/dist/models/user.model.js.map +0 -7
- package/dist/servers/production/auth-middleware.js.map +0 -7
- package/dist/servers/railway/config.js +0 -55
- package/dist/servers/railway/config.js.map +0 -7
- package/dist/servers/railway/index-enhanced.js +0 -160
- package/dist/servers/railway/index-enhanced.js.map +0 -7
- package/dist/servers/railway/index.js +0 -1349
- package/dist/servers/railway/index.js.map +0 -7
- package/dist/servers/railway/simple.js +0 -64
- package/dist/servers/railway/simple.js.map +0 -7
- package/dist/servers/railway/storage-test.js +0 -459
- package/dist/servers/railway/storage-test.js.map +0 -7
- package/dist/services/config-service.js.map +0 -7
- package/dist/services/context-service.js.map +0 -7
- package/dist/skills/api-discovery.js.map +0 -7
- package/dist/skills/api-skill.js.map +0 -7
- package/dist/skills/claude-skills.js.map +0 -7
- package/dist/skills/dashboard-launcher.js.map +0 -7
- package/dist/skills/recursive-agent-orchestrator.js.map +0 -7
- package/dist/skills/repo-ingestion-skill.js +0 -609
- package/dist/skills/repo-ingestion-skill.js.map +0 -7
- package/dist/skills/security-secrets-scanner.js +0 -284
- package/dist/skills/security-secrets-scanner.js.map +0 -7
- package/dist/skills/unified-rlm-orchestrator.js.map +0 -7
- package/dist/utils/env.js.map +0 -7
- package/dist/utils/formatting.js.map +0 -7
- package/dist/utils/process-cleanup.js.map +0 -7
- package/dist/validation/schemas.js +0 -222
- package/dist/validation/schemas.js.map +0 -7
- /package/dist/{agents → src/agents}/core/agent-task-manager.js +0 -0
- /package/dist/{agents → src/agents}/verifiers/base-verifier.js +0 -0
- /package/dist/{agents → src/agents}/verifiers/formatter-verifier.js +0 -0
- /package/dist/{agents → src/agents}/verifiers/llm-judge.js +0 -0
- /package/dist/{cli → src/cli}/auto-detect.js +0 -0
- /package/dist/{cli → src/cli}/claude-sm-danger.js +0 -0
- /package/dist/{cli → src/cli}/codex-sm-danger.js +0 -0
- /package/dist/{cli → src/cli}/codex-sm.js +0 -0
- /package/dist/{cli → src/cli}/commands/api.js +0 -0
- /package/dist/{cli → src/cli}/commands/auto-background.js +0 -0
- /package/dist/{cli → src/cli}/commands/cleanup-processes.js +0 -0
- /package/dist/{cli → src/cli}/commands/clear.js +0 -0
- /package/dist/{cli → src/cli}/commands/config.js +0 -0
- /package/dist/{cli → src/cli}/commands/context-rehydrate.js +0 -0
- /package/dist/{cli → src/cli}/commands/context.js +0 -0
- /package/dist/{cli → src/cli}/commands/daemon.js +0 -0
- /package/dist/{cli → src/cli}/commands/dashboard.js +0 -0
- /package/dist/{cli → src/cli}/commands/db.js +0 -0
- /package/dist/{cli → src/cli}/commands/decision.js +0 -0
- /package/dist/{cli → src/cli}/commands/discovery.js +0 -0
- /package/dist/{cli → src/cli}/commands/handoff.js +0 -0
- /package/dist/{cli → src/cli}/commands/hooks.js +0 -0
- /package/dist/{cli → src/cli}/commands/linear.js +0 -0
- /package/dist/{cli → src/cli}/commands/log.js +0 -0
- /package/dist/{cli → src/cli}/commands/login.js +0 -0
- /package/dist/{cli → src/cli}/commands/migrate.js +0 -0
- /package/dist/{cli → src/cli}/commands/model.js +0 -0
- /package/dist/{cli → src/cli}/commands/onboard.js +0 -0
- /package/dist/{cli → src/cli}/commands/projects.js +0 -0
- /package/dist/{cli → src/cli}/commands/ralph.js +0 -0
- /package/dist/{cli → src/cli}/commands/retrieval.js +0 -0
- /package/dist/{cli → src/cli}/commands/service.js +0 -0
- /package/dist/{cli → src/cli}/commands/session.js +0 -0
- /package/dist/{cli → src/cli}/commands/settings.js +0 -0
- /package/dist/{cli → src/cli}/commands/setup.js +0 -0
- /package/dist/{cli → src/cli}/commands/shell.js +0 -0
- /package/dist/{cli → src/cli}/commands/signup.js +0 -0
- /package/dist/{cli → src/cli}/commands/skills.js +0 -0
- /package/dist/{cli → src/cli}/commands/sms-notify.js +0 -0
- /package/dist/{cli → src/cli}/commands/storage-tier.js +0 -0
- /package/dist/{cli → src/cli}/commands/sweep.js +0 -0
- /package/dist/{cli → src/cli}/commands/tasks.js +0 -0
- /package/dist/{cli → src/cli}/commands/worktree.js +0 -0
- /package/dist/{cli → src/cli}/opencode-sm.js +0 -0
- /package/dist/{cli → src/cli}/utils/viewer.js +0 -0
- /package/dist/{core → src/core}/config/config-manager.js +0 -0
- /package/dist/{core → src/core}/config/feature-flags.js +0 -0
- /package/dist/{core → src/core}/config/storage-config.js +0 -0
- /package/dist/{core → src/core}/config/types.js +0 -0
- /package/dist/{core → src/core}/context/auto-context.js +0 -0
- /package/dist/{core → src/core}/context/dual-stack-manager.js +0 -0
- /package/dist/{core → src/core}/context/enhanced-rehydration.js +0 -0
- /package/dist/{core → src/core}/context/frame-database.js +0 -0
- /package/dist/{core → src/core}/context/frame-digest.js +0 -0
- /package/dist/{core → src/core}/context/frame-handoff-manager.js +0 -0
- /package/dist/{core → src/core}/context/frame-lifecycle-hooks.js +0 -0
- /package/dist/{core → src/core}/context/frame-recovery.js +0 -0
- /package/dist/{core → src/core}/context/frame-stack.js +0 -0
- /package/dist/{core → src/core}/context/frame-types.js +0 -0
- /package/dist/{core → src/core}/context/frame-types.js.map +0 -0
- /package/dist/{core → src/core}/context/index.js +0 -0
- /package/dist/{core → src/core}/context/permission-manager.js +0 -0
- /package/dist/{core → src/core}/context/recursive-context-manager.js +0 -0
- /package/dist/{core → src/core}/context/refactored-frame-manager.js +0 -0
- /package/dist/{core → src/core}/context/shared-context-layer.js +0 -0
- /package/dist/{core → src/core}/context/stack-merge-resolver.js +0 -0
- /package/dist/{core → src/core}/context/validation.js +0 -0
- /package/dist/{core → src/core}/database/batch-operations.js +0 -0
- /package/dist/{core → src/core}/database/connection-pool.js +0 -0
- /package/dist/{core → src/core}/database/database-adapter.js +0 -0
- /package/dist/{core → src/core}/database/migration-manager.js +0 -0
- /package/dist/{core → src/core}/database/query-cache.js +0 -0
- /package/dist/{core → src/core}/database/query-router.js +0 -0
- /package/dist/{core → src/core}/digest/enhanced-hybrid-digest.js +0 -0
- /package/dist/{core → src/core}/digest/frame-digest-integration.js +0 -0
- /package/dist/{core → src/core}/digest/hybrid-digest-generator.js +0 -0
- /package/dist/{core → src/core}/digest/index.js +0 -0
- /package/dist/{core → src/core}/digest/types.js +0 -0
- /package/dist/{core → src/core}/errors/recovery.js +0 -0
- /package/dist/{core → src/core}/execution/parallel-executor.js +0 -0
- /package/dist/{core/merge → src/core/extensions}/types.js +0 -0
- /package/dist/{core/merge → src/core/extensions}/types.js.map +0 -0
- /package/dist/{core → src/core}/frame/workflow-templates.js +0 -0
- /package/dist/{core → src/core}/merge/conflict-detector.js +0 -0
- /package/dist/{core → src/core}/merge/index.js +0 -0
- /package/dist/{core → src/core}/merge/resolution-engine.js +0 -0
- /package/dist/{core → src/core}/merge/stack-diff.js +0 -0
- /package/dist/{core → src/core/merge}/types.js +0 -0
- /package/dist/{core → src/core/merge}/types.js.map +0 -0
- /package/dist/{core → src/core}/models/fallback-monitor.js +0 -0
- /package/dist/{core → src/core}/models/model-router.js +0 -0
- /package/dist/{core → src/core}/monitoring/error-handler.js +0 -0
- /package/dist/{core → src/core}/monitoring/metrics.js +0 -0
- /package/dist/{core → src/core}/monitoring/progress-tracker.js +0 -0
- /package/dist/{core → src/core}/monitoring/session-monitor.js +0 -0
- /package/dist/{core → src/core}/performance/context-cache.js +0 -0
- /package/dist/{core → src/core}/performance/index.js +0 -0
- /package/dist/{core → src/core}/performance/lazy-context-loader.js +0 -0
- /package/dist/{core → src/core}/performance/monitor.js +0 -0
- /package/dist/{core → src/core}/performance/optimized-frame-context.js +0 -0
- /package/dist/{core → src/core}/performance/performance-benchmark.js +0 -0
- /package/dist/{core → src/core}/performance/performance-profiler.js +0 -0
- /package/dist/{core → src/core}/performance/streaming-jsonl-parser.js +0 -0
- /package/dist/{core → src/core}/persistence/postgres-adapter.js +0 -0
- /package/dist/{core → src/core}/projects/project-isolation.js +0 -0
- /package/dist/{core → src/core}/projects/project-manager.js +0 -0
- /package/dist/{core → src/core}/query/query-parser.js +0 -0
- /package/dist/{core → src/core}/query/query-templates.js +0 -0
- /package/dist/{core → src/core}/retrieval/context-retriever.js +0 -0
- /package/dist/{core → src/core}/retrieval/index.js +0 -0
- /package/dist/{core → src/core}/retrieval/llm-context-retrieval.js +0 -0
- /package/dist/{core → src/core}/retrieval/llm-provider.js +0 -0
- /package/dist/{core → src/core}/retrieval/retrieval-audit.js +0 -0
- /package/dist/{core → src/core}/retrieval/summary-generator.js +0 -0
- /package/dist/{core → src/core}/retrieval/types.js +0 -0
- /package/dist/{core → src/core}/session/clear-survival.js +0 -0
- /package/dist/{core → src/core}/session/enhanced-handoff.js +0 -0
- /package/dist/{core → src/core}/session/handoff-generator.js +0 -0
- /package/dist/{core → src/core}/session/index.js +0 -0
- /package/dist/{core → src/core}/session/session-manager.js +0 -0
- /package/dist/{core → src/core}/skills/index.js +0 -0
- /package/dist/{core → src/core}/skills/skill-storage.js +0 -0
- /package/dist/{core → src/core}/skills/types.js +0 -0
- /package/dist/{core → src/core}/storage/infinite-storage.js +0 -0
- /package/dist/{core → src/core}/storage/remote-storage.js +0 -0
- /package/dist/{core → src/core}/storage/two-tier-storage.js +0 -0
- /package/dist/{core → src/core}/trace/cli-trace-wrapper.js +0 -0
- /package/dist/{core → src/core}/trace/db-trace-wrapper.js +0 -0
- /package/dist/{core → src/core}/trace/debug-trace.js +0 -0
- /package/dist/{core → src/core}/trace/index.js +0 -0
- /package/dist/{core → src/core}/trace/linear-api-wrapper.js +0 -0
- /package/dist/{core → src/core}/trace/trace-detector.js +0 -0
- /package/dist/{core → src/core}/trace/trace-store.js +0 -0
- /package/dist/{core → src/core}/trace/types.js +0 -0
- /package/dist/{integrations/linear → src/core}/types.js +0 -0
- /package/dist/{integrations/linear → src/core}/types.js.map +0 -0
- /package/dist/{core → src/core}/utils/async-mutex.js +0 -0
- /package/dist/{core → src/core}/utils/compression.js +0 -0
- /package/dist/{core → src/core}/utils/update-checker.js +0 -0
- /package/dist/{core → src/core}/worktree/worktree-manager.js +0 -0
- /package/dist/{daemon → src/daemon}/daemon-config.js +0 -0
- /package/dist/{daemon → src/daemon}/services/context-service.js +0 -0
- /package/dist/{daemon → src/daemon}/services/linear-service.js +0 -0
- /package/dist/{daemon → src/daemon}/session-daemon.js +0 -0
- /package/dist/{daemon → src/daemon}/unified-daemon.js +0 -0
- /package/dist/{features → src/features}/analytics/api/analytics-api.js +0 -0
- /package/dist/{features → src/features}/analytics/core/analytics-service.js +0 -0
- /package/dist/{features → src/features}/analytics/index.js +0 -0
- /package/dist/{features → src/features}/analytics/queries/metrics-queries.js +0 -0
- /package/dist/{features → src/features}/analytics/types/metrics.js +0 -0
- /package/dist/{features → src/features}/analytics/types/metrics.js.map +0 -0
- /package/dist/{features → src/features}/browser/browser-mcp.js +0 -0
- /package/dist/{features → src/features}/sweep/index.js +0 -0
- /package/dist/{features → src/features}/sweep/prediction-client.js +0 -0
- /package/dist/{features → src/features}/sweep/prompt-builder.js +0 -0
- /package/dist/{features → src/features}/sweep/pty-wrapper.js +0 -0
- /package/dist/{features → src/features}/sweep/state-watcher.js +0 -0
- /package/dist/{features → src/features}/sweep/status-bar.js +0 -0
- /package/dist/{features → src/features}/sweep/sweep-server-manager.js +0 -0
- /package/dist/{features → src/features}/sweep/tab-interceptor.js +0 -0
- /package/dist/{features → src/features}/sweep/types.js +0 -0
- /package/dist/{features → src/features}/tasks/linear-task-manager.js +0 -0
- /package/dist/{features → src/features}/tasks/task-aware-context.js +0 -0
- /package/dist/{features → src/features}/tui/simple-monitor.js +0 -0
- /package/dist/{features → src/features}/tui/swarm-monitor.js +0 -0
- /package/dist/{features → src/features}/web/client/stores/task-store.js +0 -0
- /package/dist/{features → src/features}/web/server/index.js +0 -0
- /package/dist/{hooks → src/hooks}/auto-background.js +0 -0
- /package/dist/{hooks → src/hooks}/claude-code-whatsapp-hook.js +0 -0
- /package/dist/{hooks → src/hooks}/config.js +0 -0
- /package/dist/{hooks → src/hooks}/daemon.js +0 -0
- /package/dist/{hooks → src/hooks}/events.js +0 -0
- /package/dist/{hooks → src/hooks}/index.js +0 -0
- /package/dist/{hooks → src/hooks}/linear-task-picker.js +0 -0
- /package/dist/{hooks → src/hooks}/schemas.js +0 -0
- /package/dist/{hooks → src/hooks}/secure-fs.js +0 -0
- /package/dist/{hooks → src/hooks}/security-logger.js +0 -0
- /package/dist/{hooks → src/hooks}/session-summary.js +0 -0
- /package/dist/{hooks → src/hooks}/sms-action-runner.js +0 -0
- /package/dist/{hooks → src/hooks}/sms-notify.js +0 -0
- /package/dist/{hooks → src/hooks}/sms-watcher.js +0 -0
- /package/dist/{hooks → src/hooks}/sms-webhook.js +0 -0
- /package/dist/{hooks → src/hooks}/whatsapp-commands.js +0 -0
- /package/dist/{hooks → src/hooks}/whatsapp-scheduler.js +0 -0
- /package/dist/{hooks → src/hooks}/whatsapp-sync.js +0 -0
- /package/dist/{index.js → src/index.js} +0 -0
- /package/dist/{integrations → src/integrations}/anthropic/client.js +0 -0
- /package/dist/{integrations → src/integrations}/claude-code/agent-bridge.js +0 -0
- /package/dist/{integrations → src/integrations}/claude-code/enhanced-pre-clear-hooks.js +0 -0
- /package/dist/{integrations → src/integrations}/claude-code/lifecycle-hooks.js +0 -0
- /package/dist/{integrations → src/integrations}/claude-code/post-task-hooks.js +0 -0
- /package/dist/{integrations → src/integrations}/claude-code/subagent-client-stub.js +0 -0
- /package/dist/{integrations → src/integrations}/claude-code/subagent-client.js +0 -0
- /package/dist/{integrations → src/integrations}/claude-code/task-coordinator.js +0 -0
- /package/dist/{integrations → src/integrations}/linear/auth.js +0 -0
- /package/dist/{integrations → src/integrations}/linear/auto-sync.js +0 -0
- /package/dist/{integrations → src/integrations}/linear/config.js +0 -0
- /package/dist/{integrations → src/integrations}/linear/migration.js +0 -0
- /package/dist/{integrations → src/integrations}/linear/oauth-server.js +0 -0
- /package/dist/{integrations → src/integrations}/linear/rest-client.js +0 -0
- /package/dist/{integrations → src/integrations}/linear/sync-manager.js +0 -0
- /package/dist/{integrations → src/integrations}/linear/sync-service.js +0 -0
- /package/dist/{integrations → src/integrations}/linear/sync.js +0 -0
- /package/dist/{integrations/ralph → src/integrations/linear}/types.js +0 -0
- /package/dist/{integrations/ralph → src/integrations/linear}/types.js.map +0 -0
- /package/dist/{integrations → src/integrations}/linear/unified-sync.js +0 -0
- /package/dist/{integrations → src/integrations}/linear/webhook-handler.js +0 -0
- /package/dist/{integrations → src/integrations}/linear/webhook-server.js +0 -0
- /package/dist/{integrations → src/integrations}/linear/webhook.js +0 -0
- /package/dist/{integrations → src/integrations}/mcp/handlers/code-execution-handlers.js +0 -0
- /package/dist/{integrations → src/integrations}/mcp/handlers/context-handlers.js +0 -0
- /package/dist/{integrations → src/integrations}/mcp/handlers/discovery-handlers.js +0 -0
- /package/dist/{integrations → src/integrations}/mcp/handlers/index.js +0 -0
- /package/dist/{integrations → src/integrations}/mcp/handlers/linear-handlers.js +0 -0
- /package/dist/{integrations → src/integrations}/mcp/handlers/skill-handlers.js +0 -0
- /package/dist/{integrations → src/integrations}/mcp/handlers/task-handlers.js +0 -0
- /package/dist/{integrations → src/integrations}/mcp/handlers/trace-handlers.js +0 -0
- /package/dist/{integrations → src/integrations}/mcp/index.js +0 -0
- /package/dist/{integrations → src/integrations}/mcp/middleware/tool-scoring.js +0 -0
- /package/dist/{integrations → src/integrations}/mcp/refactored-server.js +0 -0
- /package/dist/{integrations → src/integrations}/mcp/schemas.js +0 -0
- /package/dist/{integrations → src/integrations}/mcp/tool-definitions-code.js +0 -0
- /package/dist/{integrations → src/integrations}/mcp/tool-definitions.js +0 -0
- /package/dist/{integrations → src/integrations}/ralph/bridge/ralph-stackmemory-bridge.js +0 -0
- /package/dist/{integrations → src/integrations}/ralph/context/context-budget-manager.js +0 -0
- /package/dist/{integrations → src/integrations}/ralph/context/stackmemory-context-loader.js +0 -0
- /package/dist/{integrations → src/integrations}/ralph/coordination/enhanced-coordination.js +0 -0
- /package/dist/{integrations → src/integrations}/ralph/index.js +0 -0
- /package/dist/{integrations → src/integrations}/ralph/learning/pattern-learner.js +0 -0
- /package/dist/{integrations → src/integrations}/ralph/lifecycle/iteration-lifecycle.js +0 -0
- /package/dist/{integrations → src/integrations}/ralph/monitoring/swarm-dashboard.js +0 -0
- /package/dist/{integrations → src/integrations}/ralph/monitoring/swarm-registry.js +0 -0
- /package/dist/{integrations → src/integrations}/ralph/orchestration/multi-loop-orchestrator.js +0 -0
- /package/dist/{integrations → src/integrations}/ralph/patterns/compounding-engineering-pattern.js +0 -0
- /package/dist/{integrations → src/integrations}/ralph/patterns/extended-coherence-sessions.js +0 -0
- /package/dist/{integrations → src/integrations}/ralph/patterns/oracle-worker-pattern.js +0 -0
- /package/dist/{integrations → src/integrations}/ralph/performance/performance-optimizer.js +0 -0
- /package/dist/{integrations → src/integrations}/ralph/recovery/crash-recovery.js +0 -0
- /package/dist/{integrations → src/integrations}/ralph/state/state-reconciler.js +0 -0
- /package/dist/{integrations → src/integrations}/ralph/swarm/git-workflow-manager.js +0 -0
- /package/dist/{integrations → src/integrations}/ralph/swarm/swarm-coordinator.js +0 -0
- /package/dist/{types/task.js.map → src/integrations/ralph/types.js.map} +0 -0
- /package/dist/{integrations → src/integrations}/ralph/visualization/ralph-debugger.js +0 -0
- /package/dist/{mcp → src/mcp}/stackmemory-mcp-server.js +0 -0
- /package/dist/{middleware → src/middleware}/exponential-rate-limiter.js +0 -0
- /package/dist/{models → src/models}/user.model.js +0 -0
- /package/dist/{servers → src/servers}/production/auth-middleware.js +0 -0
- /package/dist/{services → src/services}/config-service.js +0 -0
- /package/dist/{services → src/services}/context-service.js +0 -0
- /package/dist/{skills → src/skills}/api-discovery.js +0 -0
- /package/dist/{skills → src/skills}/api-skill.js +0 -0
- /package/dist/{skills → src/skills}/claude-skills.js +0 -0
- /package/dist/{skills → src/skills}/dashboard-launcher.js +0 -0
- /package/dist/{skills → src/skills}/recursive-agent-orchestrator.js +0 -0
- /package/dist/{skills → src/skills}/unified-rlm-orchestrator.js +0 -0
- /package/dist/{types → src/types}/task.js +0 -0
- /package/dist/{utils → src/utils}/env.js +0 -0
- /package/dist/{utils → src/utils}/formatting.js +0 -0
- /package/dist/{utils → src/utils}/process-cleanup.js +0 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/hooks/linear-task-picker.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Linear Task Picker\n * Picks the next best task from Linear queue, prioritizing tasks with test/validation requirements\n */\n\nimport { LinearClient, LinearIssue } from '../integrations/linear/client.js';\nimport { LinearAuthManager } from '../integrations/linear/auth.js';\n\nexport interface TaskSuggestion {\n id: string;\n identifier: string; // e.g., \"STA-123\"\n title: string;\n priority: number;\n hasTestRequirements: boolean;\n estimatedPoints?: number;\n url: string;\n score: number;\n}\n\nexport interface PickerOptions {\n teamId?: string;\n preferTestTasks?: boolean;\n limit?: number;\n}\n\n// Keywords indicating test/validation requirements\nconst TEST_KEYWORDS = [\n 'test',\n 'spec',\n 'unit test',\n 'integration test',\n 'e2e',\n 'end-to-end',\n 'jest',\n 'vitest',\n 'mocha',\n];\n\nconst VALIDATION_KEYWORDS = [\n 'validate',\n 'verify',\n 'verification',\n 'acceptance criteria',\n 'ac:',\n 'acceptance:',\n 'given when then',\n 'criteria:',\n];\n\nconst QA_KEYWORDS = ['qa', 'quality', 'regression', 'coverage', 'assertion'];\n\n// Labels that indicate test requirements\nconst TEST_LABELS = [\n 'needs-tests',\n 'test-required',\n 'qa-review',\n 'has-ac',\n 'acceptance-criteria',\n 'tdd',\n 'testing',\n];\n\n/**\n * Check if text contains any of the keywords (case-insensitive)\n */\nfunction containsKeywords(text: string, keywords: string[]): boolean {\n const lowerText = text.toLowerCase();\n return keywords.some((kw) => lowerText.includes(kw.toLowerCase()));\n}\n\n/**\n * Score a task based on test/validation requirements\n */\nfunction scoreTask(issue: LinearIssue, preferTestTasks: boolean): number {\n let score = 0;\n const description = issue.description || '';\n const title = issue.title || '';\n const fullText = `${title} ${description}`;\n\n // +10 if has test/validation keywords in description\n if (containsKeywords(fullText, TEST_KEYWORDS)) {\n score += preferTestTasks ? 10 : 5;\n }\n\n if (containsKeywords(fullText, VALIDATION_KEYWORDS)) {\n score += preferTestTasks ? 8 : 4;\n }\n\n if (containsKeywords(fullText, QA_KEYWORDS)) {\n score += preferTestTasks ? 5 : 2;\n }\n\n // +5 if has test-related labels\n const labelNames =\n issue.labels?.nodes?.map((l: { name: string }) => l.name.toLowerCase()) ||\n [];\n const hasTestLabel = TEST_LABELS.some((tl) =>\n labelNames.some((ln: string) => ln.includes(tl))\n );\n if (hasTestLabel) {\n score += preferTestTasks ? 5 : 3;\n }\n\n // +3 for higher priority (urgent=1, high=2)\n if (issue.priority === 1) {\n score += 5; // Urgent\n } else if (issue.priority === 2) {\n score += 3; // High\n } else if (issue.priority === 3) {\n score += 1; // Medium\n }\n\n // +2 if has acceptance criteria pattern\n if (\n description.includes('## Acceptance') ||\n description.includes('### AC') ||\n description.includes('- [ ]')\n ) {\n score += 2;\n }\n\n // +1 if has estimate (indicates well-scoped)\n if (issue.estimate) {\n score += 1;\n }\n\n return score;\n}\n\n/**\n * Get Linear client instance\n * Returns null if credentials are missing or invalid\n */\nfunction getLinearClient(): LinearClient | null {\n // Try API key first - must be valid format (lin_api_*)\n const apiKey = process.env['LINEAR_API_KEY'];\n if (apiKey && apiKey.startsWith('lin_api_')) {\n return new LinearClient({ apiKey });\n }\n\n // Fall back to OAuth\n try {\n const authManager = new LinearAuthManager();\n const tokens = authManager.loadTokens();\n if (tokens?.accessToken) {\n return new LinearClient({ accessToken: tokens.accessToken });\n }\n } catch {\n // Auth not available\n }\n\n return null;\n}\n\n/**\n * Pick the next best task from Linear\n */\nexport async function pickNextLinearTask(\n options: PickerOptions = {}\n): Promise<TaskSuggestion | null> {\n const client = getLinearClient();\n if (!client) {\n return null;\n }\n\n const { teamId, preferTestTasks = true, limit = 20 } = options;\n\n try {\n // Fetch backlog and unstarted issues\n const [backlogIssues, unstartedIssues] = await Promise.all([\n client.getIssues({ teamId, stateType: 'backlog', limit }),\n client.getIssues({ teamId, stateType: 'unstarted', limit }),\n ]);\n\n const allIssues = [...backlogIssues, ...unstartedIssues];\n\n // Filter out assigned issues (we want unassigned ones)\n const unassignedIssues = allIssues.filter((issue) => !issue.assignee);\n\n if (unassignedIssues.length === 0) {\n // If no unassigned, consider all\n if (allIssues.length === 0) {\n return null;\n }\n }\n\n const issuesToScore =\n unassignedIssues.length > 0 ? unassignedIssues : allIssues;\n\n // Score and sort\n const scoredIssues = issuesToScore.map((issue) => ({\n issue,\n score: scoreTask(issue, preferTestTasks),\n }));\n\n scoredIssues.sort((a, b) => b.score - a.score);\n\n const best = scoredIssues[0];\n if (!best) {\n return null;\n }\n\n const description = best.issue.description || '';\n const hasTestRequirements =\n containsKeywords(description, TEST_KEYWORDS) ||\n containsKeywords(description, VALIDATION_KEYWORDS);\n\n return {\n id: best.issue.id,\n identifier: best.issue.identifier,\n title: best.issue.title,\n priority: best.issue.priority,\n hasTestRequirements,\n estimatedPoints: best.issue.estimate,\n url: best.issue.url,\n score: best.score,\n };\n } catch (error) {\n // Silent fail for auth errors (401/403) - expected when not configured\n const isAuthError =\n error instanceof Error &&\n (error.message.includes('401') || error.message.includes('403'));\n if (!isAuthError) {\n console.error('[linear-task-picker] Error fetching tasks:', error);\n }\n return null;\n }\n}\n\n/**\n * Get multiple task suggestions (for showing options)\n */\nexport async function getTopTaskSuggestions(\n options: PickerOptions = {},\n count: number = 3\n): Promise<TaskSuggestion[]> {\n const client = getLinearClient();\n if (!client) {\n return [];\n }\n\n const { teamId, preferTestTasks = true, limit = 30 } = options;\n\n try {\n const [backlogIssues, unstartedIssues] = await Promise.all([\n client.getIssues({ teamId, stateType: 'backlog', limit }),\n client.getIssues({ teamId, stateType: 'unstarted', limit }),\n ]);\n\n const allIssues = [...backlogIssues, ...unstartedIssues];\n const unassignedIssues = allIssues.filter((issue) => !issue.assignee);\n const issuesToScore =\n unassignedIssues.length > 0 ? unassignedIssues : allIssues;\n\n const scoredIssues = issuesToScore.map((issue) => ({\n issue,\n score: scoreTask(issue, preferTestTasks),\n }));\n\n scoredIssues.sort((a, b) => b.score - a.score);\n\n return scoredIssues.slice(0, count).map(({ issue, score }) => {\n const description = issue.description || '';\n const hasTestRequirements =\n containsKeywords(description, TEST_KEYWORDS) ||\n containsKeywords(description, VALIDATION_KEYWORDS);\n\n return {\n id: issue.id,\n identifier: issue.identifier,\n title: issue.title,\n priority: issue.priority,\n hasTestRequirements,\n estimatedPoints: issue.estimate,\n url: issue.url,\n score,\n };\n });\n } catch (error) {\n // Silent fail for auth errors (401/403) - expected when not configured\n const isAuthError =\n error instanceof Error &&\n (error.message.includes('401') || error.message.includes('403'));\n if (!isAuthError) {\n console.error('[linear-task-picker] Error fetching tasks:', error);\n }\n return [];\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/hooks/schemas.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Zod schemas for hook configuration validation\n * Prevents malformed or malicious configs from being loaded\n */\n\nimport { z } from 'zod';\nimport { logConfigInvalid } from './security-logger.js';\n\n// SMS/WhatsApp notification schemas\nexport const PromptOptionSchema = z.object({\n key: z.string().max(10),\n label: z.string().max(200),\n action: z.string().max(500).optional(),\n});\n\nexport const PendingPromptSchema = z.object({\n id: z.string().max(32),\n timestamp: z\n .string()\n .datetime({ offset: true })\n .or(z.string().regex(/^\\d{4}-\\d{2}-\\d{2}T/)),\n message: z.string().max(1000),\n options: z.array(PromptOptionSchema).max(10),\n type: z.enum(['options', 'yesno', 'freeform']),\n callback: z.string().max(500).optional(),\n expiresAt: z\n .string()\n .datetime({ offset: true })\n .or(z.string().regex(/^\\d{4}-\\d{2}-\\d{2}T/)),\n});\n\nexport const NotifyOnSchema = z.object({\n taskComplete: z.boolean(),\n reviewReady: z.boolean(),\n error: z.boolean(),\n custom: z.boolean(),\n contextSync: z.boolean().optional().default(true),\n});\n\nexport const QuietHoursSchema = z.object({\n enabled: z.boolean(),\n start: z.string().regex(/^\\d{2}:\\d{2}$/),\n end: z.string().regex(/^\\d{2}:\\d{2}$/),\n});\n\nexport const SMSConfigSchema = z.object({\n enabled: z.boolean(),\n channel: z.enum(['whatsapp', 'sms']),\n accountSid: z.string().max(100).optional(),\n authToken: z.string().max(100).optional(),\n smsFromNumber: z.string().max(20).optional(),\n smsToNumber: z.string().max(20).optional(),\n whatsappFromNumber: z.string().max(30).optional(),\n whatsappToNumber: z.string().max(30).optional(),\n fromNumber: z.string().max(20).optional(),\n toNumber: z.string().max(20).optional(),\n webhookUrl: z.string().url().max(500).optional(),\n notifyOn: NotifyOnSchema,\n quietHours: QuietHoursSchema.optional(),\n responseTimeout: z.number().int().min(30).max(3600),\n pendingPrompts: z.array(PendingPromptSchema).max(100),\n});\n\n// Action queue schemas\nexport const PendingActionSchema = z.object({\n id: z.string().max(32),\n promptId: z.string().max(32),\n response: z.string().max(1000),\n action: z.string().max(500),\n timestamp: z\n .string()\n .datetime({ offset: true })\n .or(z.string().regex(/^\\d{4}-\\d{2}-\\d{2}T/)),\n status: z.enum(['pending', 'running', 'completed', 'failed']),\n result: z.string().max(10000).optional(),\n error: z.string().max(1000).optional(),\n});\n\nexport const ActionQueueSchema = z.object({\n actions: z.array(PendingActionSchema).max(1000),\n lastChecked: z\n .string()\n .datetime({ offset: true })\n .or(z.string().regex(/^\\d{4}-\\d{2}-\\d{2}T/)),\n});\n\n// Auto-background config schema\nexport const AutoBackgroundConfigSchema = z.object({\n enabled: z.boolean(),\n timeoutMs: z.number().int().min(1000).max(600000),\n alwaysBackground: z.array(z.string().max(200)).max(100),\n neverBackground: z.array(z.string().max(200)).max(100),\n verbose: z.boolean().optional(),\n});\n\n// WhatsApp Sync Options schema\nexport const SyncOptionsSchema = z.object({\n autoSyncOnClose: z.boolean(),\n minFrameDuration: z.number().int().min(0).max(3600), // 0 to 1 hour\n includeDecisions: z.boolean(),\n includeFiles: z.boolean(),\n includeTests: z.boolean(),\n maxDigestLength: z.number().int().min(100).max(1000), // WhatsApp limit ~4096 chars\n});\n\n// WhatsApp Schedule Config schema\nexport const ScheduleConfigSchema = z.object({\n type: z.enum(['daily', 'hourly', 'interval']),\n time: z\n .string()\n .regex(/^\\d{2}:\\d{2}$/)\n .optional(), // \"HH:MM\" for daily\n intervalMinutes: z.number().int().min(5).max(1440).optional(), // 5 min to 24 hours\n includeInactive: z.boolean(), // Include when no activity\n quietHoursRespect: z.boolean(), // Respect quiet hours setting\n});\n\n// WhatsApp Schedule storage schema\nexport const ScheduleSchema = z.object({\n id: z.string().max(32),\n config: ScheduleConfigSchema,\n enabled: z.boolean(),\n lastRun: z\n .string()\n .datetime({ offset: true })\n .or(z.string().regex(/^\\d{4}-\\d{2}-\\d{2}T/))\n .optional(),\n nextRun: z\n .string()\n .datetime({ offset: true })\n .or(z.string().regex(/^\\d{4}-\\d{2}-\\d{2}T/))\n .optional(),\n createdAt: z\n .string()\n .datetime({ offset: true })\n .or(z.string().regex(/^\\d{4}-\\d{2}-\\d{2}T/)),\n});\n\nexport const ScheduleStorageSchema = z.object({\n schedules: z.array(ScheduleSchema).max(10),\n lastChecked: z\n .string()\n .datetime({ offset: true })\n .or(z.string().regex(/^\\d{4}-\\d{2}-\\d{2}T/)),\n});\n\n// WhatsApp Command schema\nexport const WhatsAppCommandSchema = z.object({\n name: z.string().max(50),\n description: z.string().max(200),\n enabled: z.boolean(),\n action: z.string().max(500).optional(), // Safe action to execute\n requiresArg: z.boolean().optional(),\n argPattern: z.string().max(100).optional(), // Regex pattern for arg validation\n});\n\nexport const WhatsAppCommandsConfigSchema = z.object({\n enabled: z.boolean(),\n commands: z.array(WhatsAppCommandSchema).max(50),\n});\n\n// Model Router schemas\nexport const ModelProviderSchema = z.enum([\n 'anthropic',\n 'qwen',\n 'openai',\n 'ollama',\n 'custom',\n]);\n\nexport const ModelConfigSchema = z.object({\n provider: ModelProviderSchema,\n model: z.string().max(100),\n baseUrl: z.string().url().max(500).optional(),\n apiKeyEnv: z.string().max(100),\n headers: z.record(z.string().max(500)).optional(),\n params: z.record(z.unknown()).optional(),\n});\n\nexport const ModelRouterConfigSchema = z.object({\n enabled: z.boolean(),\n defaultProvider: ModelProviderSchema,\n taskRouting: z\n .object({\n plan: ModelProviderSchema.optional(),\n think: ModelProviderSchema.optional(),\n code: ModelProviderSchema.optional(),\n review: ModelProviderSchema.optional(),\n })\n .optional()\n .default({}),\n fallback: z.object({\n enabled: z.boolean(),\n provider: ModelProviderSchema,\n onRateLimit: z.boolean(),\n onError: z.boolean(),\n onTimeout: z.boolean(),\n maxRetries: z.number().int().min(0).max(10),\n retryDelayMs: z.number().int().min(100).max(30000),\n }),\n providers: z\n .object({\n anthropic: ModelConfigSchema.optional(),\n qwen: ModelConfigSchema.optional(),\n openai: ModelConfigSchema.optional(),\n ollama: ModelConfigSchema.optional(),\n custom: ModelConfigSchema.optional(),\n })\n .optional()\n .default({}),\n thinkingMode: z.object({\n enabled: z.boolean(),\n budget: z.number().int().min(1000).max(100000).optional(),\n temperature: z.number().min(0).max(2).optional(),\n topP: z.number().min(0).max(1).optional(),\n }),\n});\n\n// Type exports\nexport type ModelRouterConfigValidated = z.infer<\n typeof ModelRouterConfigSchema\n>;\nexport type SMSConfigValidated = z.infer<typeof SMSConfigSchema>;\nexport type ActionQueueValidated = z.infer<typeof ActionQueueSchema>;\nexport type AutoBackgroundConfigValidated = z.infer<\n typeof AutoBackgroundConfigSchema\n>;\nexport type SyncOptionsValidated = z.infer<typeof SyncOptionsSchema>;\nexport type ScheduleConfigValidated = z.infer<typeof ScheduleConfigSchema>;\nexport type ScheduleValidated = z.infer<typeof ScheduleSchema>;\nexport type ScheduleStorageValidated = z.infer<typeof ScheduleStorageSchema>;\nexport type WhatsAppCommandValidated = z.infer<typeof WhatsAppCommandSchema>;\nexport type WhatsAppCommandsConfigValidated = z.infer<\n typeof WhatsAppCommandsConfigSchema\n>;\n\n/**\n * Safely parse and validate config, returning default on failure\n */\nexport function parseConfigSafe<T>(\n schema: z.ZodSchema<T>,\n data: unknown,\n defaultValue: T,\n configName: string\n): T {\n const result = schema.safeParse(data);\n if (result.success) {\n return result.data;\n }\n const errors = result.error.issues.map(\n (i) => `${i.path.join('.')}: ${i.message}`\n );\n logConfigInvalid(configName, errors);\n console.error(`[hooks] Invalid ${configName} config:`, errors.join(', '));\n return defaultValue;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/hooks/secure-fs.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Secure file system utilities for hooks\n * Ensures config files have restricted permissions (0600)\n */\n\nimport {\n writeFileSync,\n mkdirSync,\n chmodSync,\n existsSync,\n renameSync,\n unlinkSync,\n} from 'fs';\nimport { dirname, join } from 'path';\nimport { randomBytes } from 'crypto';\n\n/**\n * Write file with secure permissions (0600 - user read/write only)\n * Uses atomic write pattern: write to temp file, then rename\n * This prevents corruption if process crashes mid-write\n */\nexport function writeFileSecure(filePath: string, data: string): void {\n const dir = dirname(filePath);\n\n // Create directory with secure permissions if needed\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n }\n\n // Generate temp file path in same directory (required for atomic rename)\n const tempPath = join(dir, `.tmp-${randomBytes(8).toString('hex')}`);\n\n try {\n // Write to temp file first\n writeFileSync(tempPath, data);\n\n // Set secure permissions on temp file\n chmodSync(tempPath, 0o600);\n\n // Atomic rename (same filesystem, so this is atomic on POSIX)\n renameSync(tempPath, filePath);\n } catch (error) {\n // Clean up temp file on failure\n try {\n if (existsSync(tempPath)) {\n unlinkSync(tempPath);\n }\n } catch {\n // Ignore cleanup errors\n }\n throw error;\n }\n}\n\n/**\n * Ensure directory exists with secure permissions (0700)\n */\nexport function ensureSecureDir(dirPath: string): void {\n if (!existsSync(dirPath)) {\n mkdirSync(dirPath, { recursive: true, mode: 0o700 });\n } else {\n // Set permissions on existing directory\n try {\n chmodSync(dirPath, 0o700);\n } catch {\n // Ignore if we can't change permissions (not owner)\n }\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;AAKA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,SAAS,YAAY;AAC9B,SAAS,mBAAmB;AAOrB,SAAS,gBAAgB,UAAkB,MAAoB;AACpE,QAAM,MAAM,QAAQ,QAAQ;AAG5B,MAAI,CAAC,WAAW,GAAG,GAAG;AACpB,cAAU,KAAK,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAAA,EACjD;AAGA,QAAM,WAAW,KAAK,KAAK,QAAQ,YAAY,CAAC,EAAE,SAAS,KAAK,CAAC,EAAE;AAEnE,MAAI;AAEF,kBAAc,UAAU,IAAI;AAG5B,cAAU,UAAU,GAAK;AAGzB,eAAW,UAAU,QAAQ;AAAA,EAC/B,SAAS,OAAO;AAEd,QAAI;AACF,UAAI,WAAW,QAAQ,GAAG;AACxB,mBAAW,QAAQ;AAAA,MACrB;AAAA,IACF,QAAQ;AAAA,IAER;AACA,UAAM;AAAA,EACR;AACF;AAKO,SAAS,gBAAgB,SAAuB;AACrD,MAAI,CAAC,WAAW,OAAO,GAAG;AACxB,cAAU,SAAS,EAAE,WAAW,MAAM,MAAM,IAAM,CAAC;AAAA,EACrD,OAAO;AAEL,QAAI;AACF,gBAAU,SAAS,GAAK;AAAA,IAC1B,QAAQ;AAAA,IAER;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/hooks/security-logger.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Security Event Logger for hooks\n * Logs security-relevant events for audit trail\n */\n\nimport { appendFileSync, existsSync, readFileSync, writeFileSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport { ensureSecureDir } from './secure-fs.js';\n\nconst LOG_DIR = join(homedir(), '.stackmemory', 'logs');\nconst SECURITY_LOG = join(LOG_DIR, 'security.log');\nconst MAX_LOG_ENTRIES = 10000;\n\nexport type SecurityEventType =\n | 'auth_success'\n | 'auth_failure'\n | 'rate_limit'\n | 'action_allowed'\n | 'action_blocked'\n | 'config_invalid'\n | 'config_loaded'\n | 'webhook_request'\n | 'signature_invalid'\n | 'body_too_large'\n | 'content_type_invalid'\n | 'cleanup';\n\nexport interface SecurityEvent {\n timestamp: string;\n type: SecurityEventType;\n source: string;\n message: string;\n details?: Record<string, unknown>;\n ip?: string;\n}\n\nlet logCount = 0;\n\n/**\n * Log a security event\n */\nexport function logSecurityEvent(\n type: SecurityEventType,\n source: string,\n message: string,\n details?: Record<string, unknown>,\n ip?: string\n): void {\n try {\n ensureSecureDir(LOG_DIR);\n\n const event: SecurityEvent = {\n timestamp: new Date().toISOString(),\n type,\n source,\n message,\n ...(details && { details }),\n ...(ip && { ip: maskIp(ip) }),\n };\n\n const logLine = JSON.stringify(event) + '\\n';\n appendFileSync(SECURITY_LOG, logLine, { mode: 0o600 });\n\n logCount++;\n\n // Rotate log if too large (simple rotation - truncate)\n if (logCount > MAX_LOG_ENTRIES) {\n rotateLog();\n }\n } catch {\n // Don't let logging failures break the application\n }\n}\n\n/**\n * Mask IP address for privacy (keep first two octets)\n */\nfunction maskIp(ip: string): string {\n if (!ip) return 'unknown';\n\n // Handle IPv6 localhost\n if (ip === '::1' || ip === '::ffff:127.0.0.1') return '127.0.0.x';\n\n // Handle IPv4\n const parts = ip.replace('::ffff:', '').split('.');\n if (parts.length === 4) {\n return `${parts[0]}.${parts[1]}.x.x`;\n }\n\n // Handle IPv6 - mask last 64 bits\n if (ip.includes(':')) {\n const segments = ip.split(':');\n if (segments.length >= 4) {\n return segments.slice(0, 4).join(':') + ':x:x:x:x';\n }\n }\n\n return 'masked';\n}\n\n/**\n * Simple log rotation - keep last half of entries\n */\nfunction rotateLog(): void {\n try {\n if (existsSync(SECURITY_LOG)) {\n const content = readFileSync(SECURITY_LOG, 'utf8');\n const lines = content.trim().split('\\n');\n const keepLines = lines.slice(-MAX_LOG_ENTRIES / 2);\n writeFileSync(SECURITY_LOG, keepLines.join('\\n') + '\\n', { mode: 0o600 });\n logCount = keepLines.length;\n }\n } catch {\n // Ignore rotation errors\n }\n}\n\n// Convenience functions for common events\n\nexport function logAuthSuccess(\n source: string,\n details?: Record<string, unknown>\n): void {\n logSecurityEvent(\n 'auth_success',\n source,\n 'Authentication successful',\n details\n );\n}\n\nexport function logAuthFailure(\n source: string,\n reason: string,\n ip?: string,\n details?: Record<string, unknown>\n): void {\n logSecurityEvent(\n 'auth_failure',\n source,\n `Authentication failed: ${reason}`,\n details,\n ip\n );\n}\n\nexport function logRateLimit(source: string, ip: string): void {\n logSecurityEvent('rate_limit', source, 'Rate limit exceeded', undefined, ip);\n}\n\nexport function logActionAllowed(source: string, action: string): void {\n logSecurityEvent(\n 'action_allowed',\n source,\n `Action executed: ${action.substring(0, 100)}`\n );\n}\n\nexport function logActionBlocked(\n source: string,\n action: string,\n reason: string\n): void {\n logSecurityEvent('action_blocked', source, `Action blocked: ${reason}`, {\n action: action.substring(0, 100),\n });\n}\n\nexport function logConfigInvalid(source: string, errors: string[]): void {\n logSecurityEvent('config_invalid', source, 'Invalid config rejected', {\n errors: errors.slice(0, 5),\n });\n}\n\nexport function logWebhookRequest(\n source: string,\n method: string,\n path: string,\n ip?: string\n): void {\n logSecurityEvent(\n 'webhook_request',\n source,\n `${method} ${path}`,\n undefined,\n ip\n );\n}\n\nexport function logSignatureInvalid(source: string, ip?: string): void {\n logSecurityEvent(\n 'signature_invalid',\n source,\n 'Invalid request signature',\n undefined,\n ip\n );\n}\n\nexport function logBodyTooLarge(\n source: string,\n size: number,\n ip?: string\n): void {\n logSecurityEvent(\n 'body_too_large',\n source,\n `Request body too large: ${size} bytes`,\n undefined,\n ip\n );\n}\n\nexport function logContentTypeInvalid(\n source: string,\n contentType: string,\n ip?: string\n): void {\n logSecurityEvent(\n 'content_type_invalid',\n source,\n `Invalid content type: ${contentType}`,\n undefined,\n ip\n );\n}\n\nexport function logCleanup(\n source: string,\n expiredPrompts: number,\n oldActions: number\n): void {\n if (expiredPrompts > 0 || oldActions > 0) {\n logSecurityEvent('cleanup', source, 'Cleanup completed', {\n expiredPrompts,\n oldActions,\n });\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/hooks/session-summary.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Session Summary Generator\n * Generates intelligent suggestions for what to do next after a Claude session\n */\n\nimport { execSync } from 'child_process';\nimport { pickNextLinearTask, TaskSuggestion } from './linear-task-picker.js';\n\nexport interface SessionContext {\n instanceId: string;\n exitCode: number | null;\n sessionStartTime: number;\n worktreePath?: string;\n branch?: string;\n task?: string;\n}\n\nexport interface Suggestion {\n key: string;\n label: string;\n action: string;\n priority: number;\n}\n\nexport interface SessionSummary {\n duration: string;\n exitCode: number | null;\n branch: string;\n status: 'success' | 'error' | 'interrupted';\n suggestions: Suggestion[];\n linearTask?: TaskSuggestion;\n}\n\n/**\n * Format duration in human-readable form\n */\nfunction formatDuration(ms: number): string {\n const seconds = Math.floor(ms / 1000);\n const minutes = Math.floor(seconds / 60);\n const hours = Math.floor(minutes / 60);\n\n if (hours > 0) {\n return `${hours}h ${minutes % 60}min`;\n }\n if (minutes > 0) {\n return `${minutes}min`;\n }\n return `${seconds}s`;\n}\n\n/**\n * Get current git branch\n */\nfunction getCurrentBranch(): string {\n try {\n return execSync('git rev-parse --abbrev-ref HEAD', {\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n } catch {\n return 'unknown';\n }\n}\n\n/**\n * Check for uncommitted changes\n */\nfunction hasUncommittedChanges(): { changed: boolean; count: number } {\n try {\n const status = execSync('git status --porcelain', {\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n const lines = status.trim().split('\\n').filter(Boolean);\n return { changed: lines.length > 0, count: lines.length };\n } catch {\n return { changed: false, count: 0 };\n }\n}\n\n/**\n * Check if we're in a worktree\n */\nfunction isInWorktree(): boolean {\n try {\n execSync('git rev-parse --is-inside-work-tree', {\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n // Check if it's a worktree (not the main repo)\n const gitDir = execSync('git rev-parse --git-dir', {\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe'],\n }).trim();\n return gitDir.includes('.git/worktrees/');\n } catch {\n return false;\n }\n}\n\n/**\n * Check if tests exist and might need running\n */\nfunction hasTestScript(): boolean {\n try {\n const packageJson = execSync('cat package.json', {\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n const pkg = JSON.parse(packageJson);\n return !!(pkg.scripts?.test || pkg.scripts?.['test:run']);\n } catch {\n return false;\n }\n}\n\n/**\n * Generate suggestions based on session context\n */\nasync function generateSuggestions(\n context: SessionContext\n): Promise<Suggestion[]> {\n const suggestions: Suggestion[] = [];\n let keyIndex = 1;\n\n const changes = hasUncommittedChanges();\n const inWorktree = isInWorktree();\n const hasTests = hasTestScript();\n\n // Error case - suggest reviewing logs\n if (context.exitCode !== 0 && context.exitCode !== null) {\n suggestions.push({\n key: String(keyIndex++),\n label: 'Review error logs',\n action: 'cat ~/.claude/logs/claude-*.log | tail -50',\n priority: 100,\n });\n }\n\n // Uncommitted changes - suggest commit or PR\n if (changes.changed) {\n suggestions.push({\n key: String(keyIndex++),\n label: `Commit changes (${changes.count} files)`,\n action: 'git add -A && git commit',\n priority: 90,\n });\n\n // If on feature branch, suggest PR\n const branch = getCurrentBranch();\n if (branch !== 'main' && branch !== 'master' && branch !== 'unknown') {\n suggestions.push({\n key: String(keyIndex++),\n label: 'Create PR',\n action: 'gh pr create --fill',\n priority: 80,\n });\n }\n }\n\n // If tests exist and changes were made, suggest running tests\n if (hasTests && changes.changed) {\n suggestions.push({\n key: String(keyIndex++),\n label: 'Run tests',\n action: 'npm run test:run',\n priority: 85,\n });\n }\n\n // Worktree-specific suggestions\n if (inWorktree) {\n suggestions.push({\n key: String(keyIndex++),\n label: 'Merge to main',\n action: 'cwm', // custom alias\n priority: 70,\n });\n }\n\n // Try to get next Linear task\n try {\n const linearTask = await pickNextLinearTask({ preferTestTasks: true });\n if (linearTask) {\n suggestions.push({\n key: String(keyIndex++),\n label: `Start: ${linearTask.identifier} - ${linearTask.title.substring(0, 40)}${linearTask.title.length > 40 ? '...' : ''}${linearTask.hasTestRequirements ? ' (has tests)' : ''}`,\n action: `stackmemory task start ${linearTask.id} --assign-me`,\n priority: 60,\n });\n }\n } catch {\n // Linear not available, skip\n }\n\n // Long session suggestion\n const durationMs = Date.now() - context.sessionStartTime;\n if (durationMs > 30 * 60 * 1000) {\n // > 30 minutes\n suggestions.push({\n key: String(keyIndex++),\n label: 'Take a break',\n action: 'echo \"Great work! Time for a coffee break.\"',\n priority: 10,\n });\n }\n\n // Sort by priority (highest first) and re-key\n suggestions.sort((a, b) => b.priority - a.priority);\n\n // Ensure minimum 2 options always\n if (suggestions.length < 2) {\n // Add default options if not enough suggestions\n if (suggestions.length === 0) {\n suggestions.push({\n key: '1',\n label: 'Start new Claude session',\n action: 'claude-sm',\n priority: 50,\n });\n }\n if (suggestions.length < 2) {\n suggestions.push({\n key: '2',\n label: 'View session logs',\n action: 'cat ~/.claude/logs/claude-*.log | tail -30',\n priority: 40,\n });\n }\n }\n\n suggestions.forEach((s, i) => {\n s.key = String(i + 1);\n });\n\n return suggestions;\n}\n\n/**\n * Generate full session summary\n */\nexport async function generateSessionSummary(\n context: SessionContext\n): Promise<SessionSummary> {\n const durationMs = Date.now() - context.sessionStartTime;\n const duration = formatDuration(durationMs);\n const branch = context.branch || getCurrentBranch();\n\n let status: 'success' | 'error' | 'interrupted' = 'success';\n if (context.exitCode !== 0 && context.exitCode !== null) {\n status = 'error';\n }\n\n const suggestions = await generateSuggestions(context);\n\n // Extract linear task if present\n let linearTask: TaskSuggestion | undefined;\n try {\n linearTask = await pickNextLinearTask({ preferTestTasks: true });\n } catch {\n // Linear not available\n }\n\n return {\n duration,\n exitCode: context.exitCode,\n branch,\n status,\n suggestions,\n linearTask,\n };\n}\n\n/**\n * Format session summary as WhatsApp message\n */\nexport function formatSummaryMessage(\n summary: SessionSummary,\n sessionId?: string\n): string {\n const statusEmoji = summary.status === 'success' ? '' : '';\n const exitInfo =\n summary.exitCode !== null ? ` | Exit: ${summary.exitCode}` : '';\n const sessionInfo = sessionId ? ` | Session: ${sessionId}` : '';\n\n let message = `Claude session complete ${statusEmoji}\\n`;\n message += `Duration: ${summary.duration}${exitInfo}${sessionInfo}\\n`;\n message += `Branch: ${summary.branch}\\n`;\n\n // Add Claude Code session URL if session ID is available\n if (sessionId) {\n message += `View: https://claude.ai/chat/${sessionId}\\n`;\n }\n\n message += '\\n';\n\n if (summary.suggestions.length > 0) {\n message += `What to do next:\\n`;\n for (const s of summary.suggestions.slice(0, 4)) {\n message += `${s.key}. ${s.label}\\n`;\n }\n message += `\\nReply with number or custom action`;\n } else {\n message += `No pending actions. Nice work!`;\n }\n\n return message;\n}\n\n/**\n * Get action for a suggestion key\n */\nexport function getActionForKey(\n suggestions: Suggestion[],\n key: string\n): string | null {\n const suggestion = suggestions.find((s) => s.key === key);\n return suggestion?.action || null;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/hooks/sms-action-runner.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * SMS Action Runner - Executes actions based on SMS responses\n * Bridges SMS responses to Claude Code actions\n *\n * Security: Uses allowlist-based action execution to prevent command injection\n */\n\nimport { existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport { execFileSync } from 'child_process';\nimport { randomBytes } from 'crypto';\nimport { writeFileSecure, ensureSecureDir } from './secure-fs.js';\nimport { ActionQueueSchema, parseConfigSafe } from './schemas.js';\nimport { LinearClient } from '../integrations/linear/client.js';\nimport { LinearAuthManager } from '../integrations/linear/auth.js';\n\n/**\n * Parse a command string into an array of arguments, respecting quotes\n * Handles single quotes, double quotes, and escaped characters\n *\n * Examples:\n * 'echo \"hello world\"' -> ['echo', 'hello world']\n * \"git commit -m 'fix bug'\" -> ['git', 'commit', '-m', 'fix bug']\n * 'npm run build' -> ['npm', 'run', 'build']\n */\nfunction parseCommandArgs(command: string): string[] {\n const args: string[] = [];\n let current = '';\n let inSingleQuote = false;\n let inDoubleQuote = false;\n let escaped = false;\n\n for (let i = 0; i < command.length; i++) {\n const char = command[i];\n\n if (escaped) {\n current += char;\n escaped = false;\n continue;\n }\n\n if (char === '\\\\' && !inSingleQuote) {\n escaped = true;\n continue;\n }\n\n if (char === \"'\" && !inDoubleQuote) {\n inSingleQuote = !inSingleQuote;\n continue;\n }\n\n if (char === '\"' && !inSingleQuote) {\n inDoubleQuote = !inDoubleQuote;\n continue;\n }\n\n if (char === ' ' && !inSingleQuote && !inDoubleQuote) {\n if (current.length > 0) {\n args.push(current);\n current = '';\n }\n continue;\n }\n\n current += char;\n }\n\n if (current.length > 0) {\n args.push(current);\n }\n\n return args;\n}\n\n// Allowlist of safe action patterns\nconst SAFE_ACTION_PATTERNS: Array<{\n pattern: RegExp;\n validate?: (match: RegExpMatchArray) => boolean;\n}> = [\n // Git/GitHub CLI commands (limited to safe operations)\n { pattern: /^gh pr (view|list|status|checks) (\\d+)$/ },\n { pattern: /^gh pr review (\\d+) --approve$/ },\n { pattern: /^gh pr merge (\\d+) --squash$/ },\n { pattern: /^gh issue (view|list) (\\d+)?$/ },\n\n // NPM commands (limited to safe operations)\n { pattern: /^npm run (build|test|lint|lint:fix|test:run)$/ },\n { pattern: /^npm (test|run build)$/ },\n\n // StackMemory commands\n { pattern: /^stackmemory (status|notify check|context list)$/ },\n // Task start with optional --assign-me flag (Linear task ID is UUID format)\n {\n pattern: /^stackmemory task start ([a-f0-9-]{36})( --assign-me)?$/,\n },\n // Additional StackMemory commands for mobile/WhatsApp\n { pattern: /^stackmemory context show$/ },\n { pattern: /^stackmemory task list$/ },\n\n // Git commands\n { pattern: /^git (status|diff|log|branch)( --[a-z-]+)*$/ },\n { pattern: /^git add -A && git commit$/ },\n { pattern: /^gh pr create --fill$/ },\n // Git log with line limit for mobile-friendly output\n { pattern: /^git log --oneline -\\d{1,2}$/ },\n\n // WhatsApp/Mobile quick commands\n { pattern: /^status$/i },\n { pattern: /^tasks$/i },\n { pattern: /^context$/i },\n { pattern: /^help$/i },\n { pattern: /^sync$/i },\n\n // Claude Code launcher\n { pattern: /^claude-sm$/ },\n\n // Log viewing (safe read-only)\n { pattern: /^tail -\\d+ ~\\/\\.claude\\/logs\\/\\*\\.log$/ },\n\n // Custom aliases (cwm = claude worktree merge)\n { pattern: /^cwm$/ },\n\n // Simple echo/confirmation (no variables)\n {\n pattern:\n /^echo \"?(Done|OK|Confirmed|Acknowledged|Great work! Time for a coffee break\\.)\"?$/,\n },\n];\n\n/**\n * Check if an action is in the allowlist\n */\nfunction isActionAllowed(action: string): boolean {\n const trimmed = action.trim();\n return SAFE_ACTION_PATTERNS.some(({ pattern, validate }) => {\n const match = trimmed.match(pattern);\n if (!match) return false;\n if (validate && !validate(match)) return false;\n return true;\n });\n}\n\nexport interface ActionResult {\n success: boolean;\n output?: string;\n error?: string;\n}\n\nexport interface PendingAction {\n id: string;\n promptId: string;\n response: string;\n action: string;\n timestamp: string;\n status: 'pending' | 'running' | 'completed' | 'failed';\n result?: string;\n error?: string;\n}\n\nexport interface ActionQueue {\n actions: PendingAction[];\n lastChecked: string;\n}\n\nconst QUEUE_PATH = join(homedir(), '.stackmemory', 'sms-action-queue.json');\n\nconst DEFAULT_QUEUE: ActionQueue = {\n actions: [],\n lastChecked: new Date().toISOString(),\n};\n\nexport function loadActionQueue(): ActionQueue {\n try {\n if (existsSync(QUEUE_PATH)) {\n const data = JSON.parse(readFileSync(QUEUE_PATH, 'utf8'));\n return parseConfigSafe(\n ActionQueueSchema,\n data,\n DEFAULT_QUEUE,\n 'action-queue'\n );\n }\n } catch {\n // Use defaults\n }\n return { ...DEFAULT_QUEUE, lastChecked: new Date().toISOString() };\n}\n\nexport function saveActionQueue(queue: ActionQueue): void {\n try {\n ensureSecureDir(join(homedir(), '.stackmemory'));\n writeFileSecure(QUEUE_PATH, JSON.stringify(queue, null, 2));\n } catch {\n // Silently fail\n }\n}\n\nexport function queueAction(\n promptId: string,\n response: string,\n action: string\n): string {\n const queue = loadActionQueue();\n // Use cryptographically secure random ID\n const id = randomBytes(8).toString('hex');\n\n queue.actions.push({\n id,\n promptId,\n response,\n action,\n timestamp: new Date().toISOString(),\n status: 'pending',\n });\n\n saveActionQueue(queue);\n return id;\n}\n\n/**\n * Get Linear client if available\n * Returns null if credentials are missing or invalid\n */\nfunction getLinearClient(): LinearClient | null {\n // Try API key first - must be valid format (lin_api_*)\n const apiKey = process.env['LINEAR_API_KEY'];\n if (apiKey && apiKey.startsWith('lin_api_')) {\n return new LinearClient({ apiKey });\n }\n\n try {\n const authManager = new LinearAuthManager();\n const tokens = authManager.loadTokens();\n if (tokens?.accessToken) {\n return new LinearClient({ accessToken: tokens.accessToken });\n }\n } catch {\n // Auth not available\n }\n\n return null;\n}\n\n/**\n * Handle special actions that require API calls instead of shell commands\n */\nasync function handleSpecialAction(action: string): Promise<{\n handled: boolean;\n success?: boolean;\n output?: string;\n error?: string;\n}> {\n // Handle stackmemory task start command\n const taskStartMatch = action.match(\n /^stackmemory task start ([a-f0-9-]{36})( --assign-me)?$/\n );\n if (taskStartMatch) {\n const issueId = taskStartMatch[1];\n const client = getLinearClient();\n\n if (!client) {\n return {\n handled: true,\n success: false,\n error:\n 'Linear not configured. Set LINEAR_API_KEY or run stackmemory linear setup.',\n };\n }\n\n try {\n const result = await client.startIssue(issueId);\n if (result.success && result.issue) {\n return {\n handled: true,\n success: true,\n output: `Started: ${result.issue.identifier} - ${result.issue.title}`,\n };\n }\n return {\n handled: true,\n success: false,\n error: result.error || 'Failed to start issue',\n };\n } catch (err) {\n return {\n handled: true,\n success: false,\n error: err instanceof Error ? err.message : 'Unknown error',\n };\n }\n }\n\n return { handled: false };\n}\n\n/**\n * Execute an action safely using allowlist validation\n * This prevents command injection by only allowing pre-approved commands\n */\nexport async function executeActionSafe(\n action: string,\n _response: string\n): Promise<{ success: boolean; output?: string; error?: string }> {\n // Check if action is in allowlist\n if (!isActionAllowed(action)) {\n console.error(`[sms-action] Action not in allowlist: ${action}`);\n return {\n success: false,\n error: `Action not allowed. Only pre-approved commands can be executed via SMS.`,\n };\n }\n\n // Check for special actions that need API calls\n const specialResult = await handleSpecialAction(action);\n if (specialResult.handled) {\n return {\n success: specialResult.success || false,\n output: specialResult.output,\n error: specialResult.error,\n };\n }\n\n try {\n console.log(`[sms-action] Executing safe action: ${action}`);\n\n // Parse the action into command and args, respecting quotes\n const parts = parseCommandArgs(action);\n if (parts.length === 0) {\n return { success: false, error: 'Empty command' };\n }\n\n const cmd = parts[0];\n const args = parts.slice(1);\n\n // Use execFileSync for commands without shell interpretation\n // This prevents shell injection even if the allowlist is somehow bypassed\n const output = execFileSync(cmd, args, {\n encoding: 'utf8',\n timeout: 60000,\n stdio: ['pipe', 'pipe', 'pipe'],\n shell: false, // Explicitly disable shell\n });\n\n return { success: true, output };\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n return { success: false, error };\n }\n}\n\nexport function getPendingActions(): PendingAction[] {\n const queue = loadActionQueue();\n return queue.actions.filter((a) => a.status === 'pending');\n}\n\nexport function markActionRunning(id: string): void {\n const queue = loadActionQueue();\n const action = queue.actions.find((a) => a.id === id);\n if (action) {\n action.status = 'running';\n saveActionQueue(queue);\n }\n}\n\nexport function markActionCompleted(\n id: string,\n result?: string,\n error?: string\n): void {\n const queue = loadActionQueue();\n const action = queue.actions.find((a) => a.id === id);\n if (action) {\n action.status = error ? 'failed' : 'completed';\n action.result = result;\n action.error = error;\n saveActionQueue(queue);\n }\n}\n\nexport async function executeAction(action: PendingAction): Promise<{\n success: boolean;\n output?: string;\n error?: string;\n}> {\n markActionRunning(action.id);\n\n // Use the safe execution path to prevent command injection\n const result = await executeActionSafe(action.action, action.response);\n\n if (result.success) {\n markActionCompleted(action.id, result.output);\n } else {\n markActionCompleted(action.id, undefined, result.error);\n }\n\n return result;\n}\n\nexport async function processAllPendingActions(): Promise<{\n processed: number;\n succeeded: number;\n failed: number;\n}> {\n const pending = getPendingActions();\n let succeeded = 0;\n let failed = 0;\n\n for (const action of pending) {\n const result = await executeAction(action);\n if (result.success) {\n succeeded++;\n } else {\n failed++;\n }\n }\n\n return { processed: pending.length, succeeded, failed };\n}\n\n// Clean up old completed actions (keep last 50)\nexport function cleanupOldActions(): number {\n const queue = loadActionQueue();\n const completed = queue.actions.filter(\n (a) => a.status === 'completed' || a.status === 'failed'\n );\n\n if (completed.length > 50) {\n const toRemove = completed.slice(0, completed.length - 50);\n queue.actions = queue.actions.filter(\n (a) => !toRemove.find((r) => r.id === a.id)\n );\n saveActionQueue(queue);\n return toRemove.length;\n }\n\n return 0;\n}\n\n/**\n * Action Templates - Common actions for SMS responses\n *\n * SECURITY NOTE: These templates return command strings that must be\n * validated against SAFE_ACTION_PATTERNS before execution.\n * Templates that accept user input are removed to prevent injection.\n */\nexport const ACTION_TEMPLATES = {\n // Git/PR actions (PR numbers must be validated as integers)\n approvePR: (prNumber: string) => {\n // Validate PR number is numeric only\n if (!/^\\d+$/.test(prNumber)) {\n throw new Error('Invalid PR number');\n }\n return `gh pr review ${prNumber} --approve`;\n },\n mergePR: (prNumber: string) => {\n if (!/^\\d+$/.test(prNumber)) {\n throw new Error('Invalid PR number');\n }\n return `gh pr merge ${prNumber} --squash`;\n },\n viewPR: (prNumber: string) => {\n if (!/^\\d+$/.test(prNumber)) {\n throw new Error('Invalid PR number');\n }\n return `gh pr view ${prNumber}`;\n },\n\n // Build actions (no user input)\n rebuild: () => `npm run build`,\n retest: () => `npm run test:run`,\n lint: () => `npm run lint:fix`,\n\n // Status actions (no user input)\n status: () => `stackmemory status`,\n checkNotifications: () => `stackmemory notify check`,\n\n // REMOVED for security - these templates allowed arbitrary user input:\n // - requestChanges (allowed arbitrary message)\n // - closePR (could be used maliciously)\n // - deploy/rollback (too dangerous for SMS)\n // - verifyDeployment (allowed arbitrary URL)\n // - notifySlack (allowed arbitrary message - command injection)\n // - notifyTeam (allowed arbitrary message - command injection)\n};\n\n/**\n * Create action string from template\n */\nexport function createAction(\n template: keyof typeof ACTION_TEMPLATES,\n ...args: string[]\n): string {\n const fn = ACTION_TEMPLATES[template];\n if (typeof fn === 'function') {\n return (fn as (...args: string[]) => string)(...args);\n }\n return fn;\n}\n\n/**\n * Watch for new actions and execute them\n */\nexport function startActionWatcher(intervalMs: number = 5000): NodeJS.Timeout {\n console.log(\n `[sms-action] Starting action watcher (interval: ${intervalMs}ms)`\n );\n\n return setInterval(() => {\n const pending = getPendingActions();\n if (pending.length > 0) {\n console.log(`[sms-action] Found ${pending.length} pending action(s)`);\n processAllPendingActions();\n }\n }, intervalMs);\n}\n\n/**\n * Integration with SMS webhook - queue action when response received\n */\nexport function handleSMSResponse(\n promptId: string,\n response: string,\n action?: string\n): void {\n if (action) {\n const actionId = queueAction(promptId, response, action);\n console.log(`[sms-action] Queued action ${actionId}: ${action}`);\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/hooks/sms-notify.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * SMS Notification Hook for StackMemory\n * Sends text messages when tasks are ready for review\n * Supports interactive prompts with numbered options or yes/no\n *\n * Optional feature - requires Twilio setup\n */\n\nimport { existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport { config as loadDotenv } from 'dotenv';\nimport { writeFileSecure, ensureSecureDir } from './secure-fs.js';\nimport { SMSConfigSchema, parseConfigSafe } from './schemas.js';\n\nexport type MessageChannel = 'whatsapp' | 'sms';\n\nexport interface SMSConfig {\n enabled: boolean;\n // Preferred channel: whatsapp is cheaper for back-and-forth conversations\n channel: MessageChannel;\n // Twilio credentials (from env or config)\n accountSid?: string;\n authToken?: string;\n // SMS numbers\n smsFromNumber?: string;\n smsToNumber?: string;\n // WhatsApp numbers (Twilio prefixes with 'whatsapp:' automatically)\n whatsappFromNumber?: string;\n whatsappToNumber?: string;\n // Legacy fields (backwards compatibility)\n fromNumber?: string;\n toNumber?: string;\n // Webhook URL for receiving responses\n webhookUrl?: string;\n // Notification preferences\n notifyOn: {\n taskComplete: boolean;\n reviewReady: boolean;\n error: boolean;\n custom: boolean;\n contextSync: boolean;\n };\n // Quiet hours (don't send during these times)\n quietHours?: {\n enabled: boolean;\n start: string; // \"22:00\"\n end: string; // \"08:00\"\n };\n // Response timeout (seconds)\n responseTimeout: number;\n // Pending prompts awaiting response\n pendingPrompts: PendingPrompt[];\n}\n\nexport interface PendingPrompt {\n id: string;\n timestamp: string;\n message: string;\n options: PromptOption[];\n type: 'options' | 'yesno' | 'freeform';\n callback?: string; // Command to run with response\n expiresAt: string;\n}\n\nexport interface PromptOption {\n key: string; // \"1\", \"2\", \"y\", \"n\", etc.\n label: string;\n action?: string; // Command to execute\n}\n\nexport interface NotificationPayload {\n type: 'task_complete' | 'review_ready' | 'error' | 'custom' | 'context_sync';\n title: string;\n message: string;\n prompt?: {\n type: 'options' | 'yesno' | 'freeform';\n options?: PromptOption[];\n question?: string;\n };\n metadata?: Record<string, unknown>;\n}\n\nconst CONFIG_PATH = join(homedir(), '.stackmemory', 'sms-notify.json');\n\nconst DEFAULT_CONFIG: SMSConfig = {\n enabled: false,\n channel: 'whatsapp', // WhatsApp is cheaper for conversations\n notifyOn: {\n taskComplete: true,\n reviewReady: true,\n error: true,\n custom: true,\n contextSync: true,\n },\n quietHours: {\n enabled: false,\n start: '22:00',\n end: '08:00',\n },\n responseTimeout: 300, // 5 minutes\n pendingPrompts: [],\n};\n\nexport function loadSMSConfig(): SMSConfig {\n // Load .env files (project, home, global) - suppress debug logs\n loadDotenv({ path: join(process.cwd(), '.env'), debug: false });\n loadDotenv({ path: join(process.cwd(), '.env.local'), debug: false });\n loadDotenv({ path: join(homedir(), '.env'), debug: false });\n loadDotenv({ path: join(homedir(), '.stackmemory', '.env'), debug: false });\n\n try {\n if (existsSync(CONFIG_PATH)) {\n const data = readFileSync(CONFIG_PATH, 'utf8');\n const parsed = JSON.parse(data);\n // Validate with zod schema, fall back to defaults on invalid config\n const validated = parseConfigSafe(\n SMSConfigSchema,\n { ...DEFAULT_CONFIG, ...parsed },\n DEFAULT_CONFIG,\n 'sms-notify'\n );\n applyEnvVars(validated);\n return validated;\n }\n } catch {\n // Use defaults\n }\n\n // Check environment variables\n const config = { ...DEFAULT_CONFIG };\n applyEnvVars(config);\n return config;\n}\n\n// Check what's missing for notifications to work\nexport function getMissingConfig(): {\n missing: string[];\n configured: string[];\n ready: boolean;\n} {\n const config = loadSMSConfig();\n const missing: string[] = [];\n const configured: string[] = [];\n\n // Check credentials\n if (config.accountSid) {\n configured.push('TWILIO_ACCOUNT_SID');\n } else {\n missing.push('TWILIO_ACCOUNT_SID');\n }\n\n if (config.authToken) {\n configured.push('TWILIO_AUTH_TOKEN');\n } else {\n missing.push('TWILIO_AUTH_TOKEN');\n }\n\n // Check channel-specific numbers\n const channel = config.channel || 'whatsapp';\n\n if (channel === 'whatsapp') {\n const from = config.whatsappFromNumber || config.fromNumber;\n const to = config.whatsappToNumber || config.toNumber;\n\n if (from) {\n configured.push('TWILIO_WHATSAPP_FROM');\n } else {\n missing.push('TWILIO_WHATSAPP_FROM');\n }\n\n if (to) {\n configured.push('TWILIO_WHATSAPP_TO');\n } else {\n missing.push('TWILIO_WHATSAPP_TO');\n }\n } else {\n const from = config.smsFromNumber || config.fromNumber;\n const to = config.smsToNumber || config.toNumber;\n\n if (from) {\n configured.push('TWILIO_SMS_FROM');\n } else {\n missing.push('TWILIO_SMS_FROM');\n }\n\n if (to) {\n configured.push('TWILIO_SMS_TO');\n } else {\n missing.push('TWILIO_SMS_TO');\n }\n }\n\n return {\n missing,\n configured,\n ready: missing.length === 0,\n };\n}\n\nfunction applyEnvVars(config: SMSConfig): void {\n // Twilio credentials\n if (process.env['TWILIO_ACCOUNT_SID']) {\n config.accountSid = process.env['TWILIO_ACCOUNT_SID'];\n }\n if (process.env['TWILIO_AUTH_TOKEN']) {\n config.authToken = process.env['TWILIO_AUTH_TOKEN'];\n }\n\n // SMS numbers\n if (process.env['TWILIO_SMS_FROM'] || process.env['TWILIO_FROM_NUMBER']) {\n config.smsFromNumber =\n process.env['TWILIO_SMS_FROM'] || process.env['TWILIO_FROM_NUMBER'];\n }\n if (process.env['TWILIO_SMS_TO'] || process.env['TWILIO_TO_NUMBER']) {\n config.smsToNumber =\n process.env['TWILIO_SMS_TO'] || process.env['TWILIO_TO_NUMBER'];\n }\n\n // WhatsApp numbers\n if (process.env['TWILIO_WHATSAPP_FROM']) {\n config.whatsappFromNumber = process.env['TWILIO_WHATSAPP_FROM'];\n }\n if (process.env['TWILIO_WHATSAPP_TO']) {\n config.whatsappToNumber = process.env['TWILIO_WHATSAPP_TO'];\n }\n\n // Legacy support\n if (process.env['TWILIO_FROM_NUMBER']) {\n config.fromNumber = process.env['TWILIO_FROM_NUMBER'];\n }\n if (process.env['TWILIO_TO_NUMBER']) {\n config.toNumber = process.env['TWILIO_TO_NUMBER'];\n }\n\n // Channel preference\n if (process.env['TWILIO_CHANNEL']) {\n config.channel = process.env['TWILIO_CHANNEL'] as MessageChannel;\n }\n}\n\nexport function saveSMSConfig(config: SMSConfig): void {\n try {\n ensureSecureDir(join(homedir(), '.stackmemory'));\n // Don't save sensitive credentials to file\n const safeConfig = { ...config };\n delete safeConfig.accountSid;\n delete safeConfig.authToken;\n writeFileSecure(CONFIG_PATH, JSON.stringify(safeConfig, null, 2));\n } catch {\n // Silently fail\n }\n}\n\nfunction isQuietHours(config: SMSConfig): boolean {\n if (!config.quietHours?.enabled) return false;\n\n const now = new Date();\n const currentTime = now.getHours() * 60 + now.getMinutes();\n\n const [startH, startM] = config.quietHours.start.split(':').map(Number);\n const [endH, endM] = config.quietHours.end.split(':').map(Number);\n\n const startTime = startH * 60 + startM;\n const endTime = endH * 60 + endM;\n\n // Handle overnight quiet hours (e.g., 22:00 - 08:00)\n if (startTime > endTime) {\n return currentTime >= startTime || currentTime < endTime;\n }\n\n return currentTime >= startTime && currentTime < endTime;\n}\n\nfunction generatePromptId(): string {\n return Math.random().toString(36).substring(2, 10);\n}\n\nfunction formatPromptMessage(payload: NotificationPayload): string {\n let message = `${payload.title}\\n\\n${payload.message}`;\n\n if (payload.prompt) {\n message += '\\n\\n';\n\n if (payload.prompt.question) {\n message += `${payload.prompt.question}\\n`;\n }\n\n if (payload.prompt.type === 'yesno') {\n message += 'Reply Y for Yes, N for No';\n } else if (payload.prompt.type === 'options' && payload.prompt.options) {\n payload.prompt.options.forEach((opt) => {\n message += `${opt.key}. ${opt.label}\\n`;\n });\n message += '\\nReply with number to select';\n } else if (payload.prompt.type === 'freeform') {\n message += 'Reply with your response';\n }\n }\n\n // Always append session URL if available\n return appendSessionUrl(message);\n}\n\nfunction getChannelNumbers(config: SMSConfig): {\n from: string;\n to: string;\n channel: MessageChannel;\n} | null {\n const channel = config.channel || 'whatsapp';\n\n if (channel === 'whatsapp') {\n // Try WhatsApp first\n const from = config.whatsappFromNumber || config.fromNumber;\n const to = config.whatsappToNumber || config.toNumber;\n if (from && to) {\n // Twilio requires 'whatsapp:' prefix for WhatsApp numbers\n return {\n from: from.startsWith('whatsapp:') ? from : `whatsapp:${from}`,\n to: to.startsWith('whatsapp:') ? to : `whatsapp:${to}`,\n channel: 'whatsapp',\n };\n }\n }\n\n // Fall back to SMS\n const from = config.smsFromNumber || config.fromNumber;\n const to = config.smsToNumber || config.toNumber;\n if (from && to) {\n return { from, to, channel: 'sms' };\n }\n\n return null;\n}\n\nexport async function sendNotification(\n payload: NotificationPayload,\n channelOverride?: MessageChannel\n): Promise<{\n success: boolean;\n promptId?: string;\n channel?: MessageChannel;\n error?: string;\n}> {\n const config = loadSMSConfig();\n\n if (!config.enabled) {\n return { success: false, error: 'Notifications disabled' };\n }\n\n // Check notification type is enabled\n const typeMap: Record<string, keyof typeof config.notifyOn> = {\n task_complete: 'taskComplete',\n review_ready: 'reviewReady',\n error: 'error',\n custom: 'custom',\n context_sync: 'contextSync',\n };\n\n if (!config.notifyOn[typeMap[payload.type]]) {\n return {\n success: false,\n error: `Notifications for ${payload.type} disabled`,\n };\n }\n\n // Check quiet hours\n if (isQuietHours(config)) {\n return { success: false, error: 'Quiet hours active' };\n }\n\n // Validate credentials\n if (!config.accountSid || !config.authToken) {\n return {\n success: false,\n error:\n 'Missing Twilio credentials. Set TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN',\n };\n }\n\n // Get channel numbers (prefer WhatsApp)\n const originalChannel = config.channel;\n if (channelOverride) {\n config.channel = channelOverride;\n }\n\n const numbers = getChannelNumbers(config);\n config.channel = originalChannel; // Restore\n\n if (!numbers) {\n return {\n success: false,\n error:\n config.channel === 'whatsapp'\n ? 'Missing WhatsApp numbers. Set TWILIO_WHATSAPP_FROM and TWILIO_WHATSAPP_TO'\n : 'Missing SMS numbers. Set TWILIO_SMS_FROM and TWILIO_SMS_TO',\n };\n }\n\n const message = formatPromptMessage(payload);\n let promptId: string | undefined;\n\n // Store pending prompt if interactive\n if (payload.prompt) {\n promptId = generatePromptId();\n const expiresAt = new Date(\n Date.now() + config.responseTimeout * 1000\n ).toISOString();\n\n const pendingPrompt: PendingPrompt = {\n id: promptId,\n timestamp: new Date().toISOString(),\n message: payload.message,\n options: payload.prompt.options || [],\n type: payload.prompt.type,\n expiresAt,\n };\n\n config.pendingPrompts.push(pendingPrompt);\n saveSMSConfig(config);\n }\n\n try {\n // Use Twilio API (same endpoint for SMS and WhatsApp)\n const twilioUrl = `https://api.twilio.com/2010-04-01/Accounts/${config.accountSid}/Messages.json`;\n\n const response = await fetch(twilioUrl, {\n method: 'POST',\n headers: {\n Authorization:\n 'Basic ' +\n Buffer.from(`${config.accountSid}:${config.authToken}`).toString(\n 'base64'\n ),\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n From: numbers.from,\n To: numbers.to,\n Body: message,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.text();\n return {\n success: false,\n channel: numbers.channel,\n error: `Twilio error: ${errorData}`,\n };\n }\n\n return { success: true, promptId, channel: numbers.channel };\n } catch (err) {\n return {\n success: false,\n channel: numbers.channel,\n error: `Failed to send ${numbers.channel}: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n}\n\n// Backwards compatible alias\nexport async function sendSMSNotification(\n payload: NotificationPayload\n): Promise<{ success: boolean; promptId?: string; error?: string }> {\n return sendNotification(payload);\n}\n\nexport function processIncomingResponse(\n from: string,\n body: string\n): {\n matched: boolean;\n prompt?: PendingPrompt;\n response?: string;\n action?: string;\n} {\n const config = loadSMSConfig();\n\n // Normalize response\n const response = body.trim().toLowerCase();\n\n // Find matching pending prompt (most recent first)\n const now = new Date();\n const validPrompts = config.pendingPrompts.filter(\n (p) => new Date(p.expiresAt) > now\n );\n\n if (validPrompts.length === 0) {\n return { matched: false };\n }\n\n // Get most recent prompt\n const prompt = validPrompts[validPrompts.length - 1];\n\n let matchedOption: PromptOption | undefined;\n\n if (prompt.type === 'yesno') {\n if (response === 'y' || response === 'yes') {\n matchedOption = { key: 'y', label: 'Yes' };\n } else if (response === 'n' || response === 'no') {\n matchedOption = { key: 'n', label: 'No' };\n }\n } else if (prompt.type === 'options') {\n matchedOption = prompt.options.find(\n (opt) => opt.key.toLowerCase() === response\n );\n } else if (prompt.type === 'freeform') {\n matchedOption = { key: response, label: response };\n }\n\n // Remove processed prompt\n config.pendingPrompts = config.pendingPrompts.filter(\n (p) => p.id !== prompt.id\n );\n saveSMSConfig(config);\n\n if (matchedOption) {\n return {\n matched: true,\n prompt,\n response: matchedOption.key,\n action: matchedOption.action,\n };\n }\n\n return { matched: false, prompt };\n}\n\n// Get session ID from environment or generate short ID\nfunction getSessionId(): string {\n return (\n process.env['CLAUDE_INSTANCE_ID'] ||\n process.env['STACKMEMORY_SESSION_ID'] ||\n Math.random().toString(36).substring(2, 8)\n );\n}\n\n// Get Claude session URL if available\nexport function getSessionUrl(): string | undefined {\n // Check for remote session URL in environment\n const sessionId = process.env['CLAUDE_SESSION_ID'];\n if (sessionId?.startsWith('session_')) {\n return `https://claude.ai/code/${sessionId}`;\n }\n // Check for explicit URL\n return process.env['CLAUDE_SESSION_URL'];\n}\n\n// Format message with session URL\nfunction appendSessionUrl(message: string): string {\n const url = getSessionUrl();\n if (url) {\n return `${message}\\n\\nSession: ${url}`;\n }\n return message;\n}\n\n// Convenience functions for common notifications\n\nexport async function notifyReviewReady(\n title: string,\n description: string,\n options?: { label: string; action?: string }[]\n): Promise<{ success: boolean; promptId?: string; error?: string }> {\n const sessionId = getSessionId();\n\n // Ensure minimum 2 options\n let finalOptions = options || [];\n if (finalOptions.length < 2) {\n const defaults = [\n { label: 'Approve', action: 'echo \"Approved\"' },\n { label: 'Request changes', action: 'echo \"Changes requested\"' },\n ];\n finalOptions = [...finalOptions, ...defaults].slice(\n 0,\n Math.max(2, finalOptions.length)\n );\n }\n\n const payload: NotificationPayload = {\n type: 'review_ready',\n title: `[Claude ${sessionId}] Review Ready: ${title}`,\n message: description,\n prompt: {\n type: 'options',\n options: finalOptions.map((opt, i) => ({\n key: String(i + 1),\n label: opt.label,\n action: opt.action,\n })),\n question: 'What would you like to do?',\n },\n };\n\n return sendSMSNotification(payload);\n}\n\nexport async function notifyWithYesNo(\n title: string,\n question: string,\n yesAction?: string,\n noAction?: string\n): Promise<{ success: boolean; promptId?: string; error?: string }> {\n const sessionId = getSessionId();\n return sendSMSNotification({\n type: 'custom',\n title: `[Claude ${sessionId}] ${title}`,\n message: question,\n prompt: {\n type: 'yesno',\n options: [\n { key: 'y', label: 'Yes', action: yesAction },\n { key: 'n', label: 'No', action: noAction },\n ],\n },\n });\n}\n\nexport async function notifyTaskComplete(\n taskName: string,\n summary: string\n): Promise<{ success: boolean; promptId?: string; error?: string }> {\n const sessionId = getSessionId();\n return sendSMSNotification({\n type: 'task_complete',\n title: `[Claude ${sessionId}] Task Complete: ${taskName}`,\n message: summary,\n prompt: {\n type: 'options',\n options: [\n { key: '1', label: 'Start next task', action: 'claude-sm' },\n { key: '2', label: 'View details', action: 'stackmemory status' },\n ],\n },\n });\n}\n\nexport async function notifyError(\n error: string,\n context?: string\n): Promise<{ success: boolean; promptId?: string; error?: string }> {\n const sessionId = getSessionId();\n return sendSMSNotification({\n type: 'error',\n title: `[Claude ${sessionId}] Error Alert`,\n message: context ? `${error}\\n\\nContext: ${context}` : error,\n prompt: {\n type: 'options',\n options: [\n { key: '1', label: 'Retry', action: 'claude-sm' },\n {\n key: '2',\n label: 'View logs',\n action: 'tail -50 ~/.claude/logs/*.log',\n },\n ],\n },\n });\n}\n\n// Clean up expired prompts\nexport function cleanupExpiredPrompts(): number {\n const config = loadSMSConfig();\n const now = new Date();\n const before = config.pendingPrompts.length;\n\n config.pendingPrompts = config.pendingPrompts.filter(\n (p) => new Date(p.expiresAt) > now\n );\n\n const removed = before - config.pendingPrompts.length;\n if (removed > 0) {\n saveSMSConfig(config);\n }\n\n return removed;\n}\n\n// ============================================================================\n// SIMPLIFIED API - Use these for basic notifications\n// ============================================================================\n\n/**\n * Send a simple status notification\n * Always includes session URL if available\n */\nexport async function notify(\n message: string\n): Promise<{ success: boolean; error?: string }> {\n const sessionId = getSessionId();\n return sendNotification({\n type: 'custom',\n title: `[Claude ${sessionId}]`,\n message,\n });\n}\n\n/**\n * Send a notification with A/B choice (1 or 2)\n * Always includes session URL if available\n */\nexport async function notifyChoice(\n message: string,\n optionA: string,\n optionB: string\n): Promise<{ success: boolean; promptId?: string; error?: string }> {\n const sessionId = getSessionId();\n return sendNotification({\n type: 'custom',\n title: `[Claude ${sessionId}]`,\n message,\n prompt: {\n type: 'options',\n options: [\n { key: '1', label: optionA },\n { key: '2', label: optionB },\n ],\n },\n });\n}\n\n/**\n * Send a notification with Yes/No choice\n * Always includes session URL if available\n */\nexport async function notifyYesNo(\n message: string\n): Promise<{ success: boolean; promptId?: string; error?: string }> {\n const sessionId = getSessionId();\n return sendNotification({\n type: 'custom',\n title: `[Claude ${sessionId}]`,\n message,\n prompt: { type: 'yesno' },\n });\n}\n\n/**\n * Send step completion notification\n * Always includes session URL if available\n */\nexport async function notifyStep(\n step: string,\n status: 'done' | 'failed' | 'waiting' = 'done'\n): Promise<{ success: boolean; error?: string }> {\n const sessionId = getSessionId();\n const symbol = status === 'done' ? '\u2713' : status === 'failed' ? '\u2717' : '\u23F3';\n return sendNotification({\n type: 'task_complete',\n title: `[Claude ${sessionId}]`,\n message: `${symbol} ${step}`,\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["from", "to"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/hooks/sms-watcher.ts"],
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\n/**\n * SMS Response Watcher\n * Watches for incoming SMS/WhatsApp responses and triggers notifications\n *\n * Run in background: stackmemory notify watch-responses &\n */\n\nimport { existsSync, readFileSync, watchFile, writeFileSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport { execSync } from 'child_process';\n\nconst RESPONSE_PATH = join(\n homedir(),\n '.stackmemory',\n 'sms-latest-response.json'\n);\nconst SIGNAL_PATH = join(homedir(), '.stackmemory', 'sms-signal.txt');\n\ninterface SMSResponse {\n promptId: string;\n response: string;\n timestamp: string;\n}\n\nlet lastProcessedTimestamp = '';\n\nfunction checkForResponse(): SMSResponse | null {\n try {\n if (existsSync(RESPONSE_PATH)) {\n const data = JSON.parse(\n readFileSync(RESPONSE_PATH, 'utf8')\n ) as SMSResponse;\n\n // Only process new responses\n if (data.timestamp !== lastProcessedTimestamp) {\n lastProcessedTimestamp = data.timestamp;\n return data;\n }\n }\n } catch {\n // Ignore errors\n }\n return null;\n}\n\nfunction triggerNotification(response: SMSResponse): void {\n const message = `SMS Response: \"${response.response}\"`;\n\n // macOS notification\n try {\n execSync(\n `osascript -e 'display notification \"${message}\" with title \"StackMemory\"'`,\n {\n stdio: 'ignore',\n }\n );\n } catch {\n // Ignore if not on macOS\n }\n\n // Terminal bell\n process.stdout.write('\\x07');\n\n // Write to signal file (for other processes to detect)\n try {\n writeFileSync(\n SIGNAL_PATH,\n JSON.stringify({\n type: 'sms_response',\n response: response.response,\n promptId: response.promptId,\n timestamp: new Date().toISOString(),\n })\n );\n } catch {\n // Ignore\n }\n\n // Output to terminal\n console.log(`\\n[SMS] User responded: \"${response.response}\"`);\n console.log(`[SMS] Run: stackmemory notify run-actions\\n`);\n}\n\nexport function startResponseWatcher(intervalMs: number = 2000): void {\n console.log('[SMS Watcher] Watching for responses...');\n console.log('[SMS Watcher] Press Ctrl+C to stop\\n');\n\n // Initial check\n const initial = checkForResponse();\n if (initial) {\n triggerNotification(initial);\n }\n\n // Poll for changes\n setInterval(() => {\n const response = checkForResponse();\n if (response) {\n triggerNotification(response);\n }\n }, intervalMs);\n}\n\n// Also watch file for immediate notification\nexport function startFileWatcher(): void {\n console.log('[SMS Watcher] Watching for responses (file mode)...');\n\n watchFile(RESPONSE_PATH, { interval: 1000 }, () => {\n const response = checkForResponse();\n if (response) {\n triggerNotification(response);\n }\n });\n}\n\n// CLI entry\nif (process.argv[1]?.includes('sms-watcher')) {\n startResponseWatcher();\n}\n\nexport { checkForResponse, triggerNotification };\n"],
|
|
5
|
+
"mappings": ";;;;;AAQA,SAAS,YAAY,cAAc,WAAW,qBAAqB;AACnE,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,SAAS,gBAAgB;AAEzB,MAAM,gBAAgB;AAAA,EACpB,QAAQ;AAAA,EACR;AAAA,EACA;AACF;AACA,MAAM,cAAc,KAAK,QAAQ,GAAG,gBAAgB,gBAAgB;AAQpE,IAAI,yBAAyB;AAE7B,SAAS,mBAAuC;AAC9C,MAAI;AACF,QAAI,WAAW,aAAa,GAAG;AAC7B,YAAM,OAAO,KAAK;AAAA,QAChB,aAAa,eAAe,MAAM;AAAA,MACpC;AAGA,UAAI,KAAK,cAAc,wBAAwB;AAC7C,iCAAyB,KAAK;AAC9B,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AACA,SAAO;AACT;AAEA,SAAS,oBAAoB,UAA6B;AACxD,QAAM,UAAU,kBAAkB,SAAS,QAAQ;AAGnD,MAAI;AACF;AAAA,MACE,uCAAuC,OAAO;AAAA,MAC9C;AAAA,QACE,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAGA,UAAQ,OAAO,MAAM,MAAM;AAG3B,MAAI;AACF;AAAA,MACE;AAAA,MACA,KAAK,UAAU;AAAA,QACb,MAAM;AAAA,QACN,UAAU,SAAS;AAAA,QACnB,UAAU,SAAS;AAAA,QACnB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,MACpC,CAAC;AAAA,IACH;AAAA,EACF,QAAQ;AAAA,EAER;AAGA,UAAQ,IAAI;AAAA,yBAA4B,SAAS,QAAQ,GAAG;AAC5D,UAAQ,IAAI;AAAA,CAA6C;AAC3D;AAEO,SAAS,qBAAqB,aAAqB,KAAY;AACpE,UAAQ,IAAI,yCAAyC;AACrD,UAAQ,IAAI,sCAAsC;AAGlD,QAAM,UAAU,iBAAiB;AACjC,MAAI,SAAS;AACX,wBAAoB,OAAO;AAAA,EAC7B;AAGA,cAAY,MAAM;AAChB,UAAM,WAAW,iBAAiB;AAClC,QAAI,UAAU;AACZ,0BAAoB,QAAQ;AAAA,IAC9B;AAAA,EACF,GAAG,UAAU;AACf;AAGO,SAAS,mBAAyB;AACvC,UAAQ,IAAI,qDAAqD;AAEjE,YAAU,eAAe,EAAE,UAAU,IAAK,GAAG,MAAM;AACjD,UAAM,WAAW,iBAAiB;AAClC,QAAI,UAAU;AACZ,0BAAoB,QAAQ;AAAA,IAC9B;AAAA,EACF,CAAC;AACH;AAGA,IAAI,QAAQ,KAAK,CAAC,GAAG,SAAS,aAAa,GAAG;AAC5C,uBAAqB;AACvB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/hooks/sms-webhook.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * SMS Webhook Handler for receiving Twilio responses\n * Can run as standalone server or integrate with existing Express app\n *\n * Security features:\n * - Twilio signature verification\n * - Rate limiting per IP\n * - Body size limits\n * - Content-type validation\n * - Safe action execution (no shell injection)\n */\n\nimport { createServer, IncomingMessage, ServerResponse } from 'http';\nimport { parse as parseUrl } from 'url';\nimport { existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport { createHmac } from 'crypto';\nimport { execFileSync } from 'child_process';\nimport {\n processIncomingResponse,\n loadSMSConfig,\n cleanupExpiredPrompts,\n sendNotification,\n} from './sms-notify.js';\nimport {\n queueAction,\n executeActionSafe,\n cleanupOldActions,\n type ActionResult,\n} from './sms-action-runner.js';\nimport {\n isCommand,\n processCommand,\n sendCommandResponse,\n} from './whatsapp-commands.js';\nimport { writeFileSecure, ensureSecureDir } from './secure-fs.js';\nimport {\n logWebhookRequest,\n logRateLimit,\n logSignatureInvalid,\n logBodyTooLarge,\n logContentTypeInvalid,\n logActionAllowed,\n logActionBlocked,\n logCleanup,\n} from './security-logger.js';\n\n// Cleanup interval (5 minutes)\nconst CLEANUP_INTERVAL_MS = 5 * 60 * 1000;\n\n// Input validation constants\nconst MAX_SMS_BODY_LENGTH = 1000;\nconst MAX_PHONE_LENGTH = 50; // WhatsApp format: whatsapp:+12345678901\n\n// Security constants\nconst MAX_BODY_SIZE = 50 * 1024; // 50KB max body\nconst RATE_LIMIT_WINDOW_MS = 60 * 1000; // 1 minute\nconst RATE_LIMIT_MAX_REQUESTS = 30; // 30 requests per minute per IP\nconst ACTION_TIMEOUT_MS = 60000; // 60 second timeout for action execution\n\n/**\n * Execute action with timeout to prevent hanging requests\n */\nasync function executeActionWithTimeout(\n action: string,\n response: string\n): Promise<ActionResult> {\n return Promise.race([\n executeActionSafe(action, response),\n new Promise<ActionResult>((_, reject) =>\n setTimeout(\n () =>\n reject(new Error(`Action timed out after ${ACTION_TIMEOUT_MS}ms`)),\n ACTION_TIMEOUT_MS\n )\n ),\n ]).catch((error) => ({\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }));\n}\n\n// Rate limiting store - persisted to disk to survive restarts\nconst RATE_LIMIT_PATH = join(homedir(), '.stackmemory', 'rate-limits.json');\n\ninterface RateLimitRecord {\n count: number;\n resetTime: number;\n}\n\ninterface RateLimitStore {\n [ip: string]: RateLimitRecord;\n}\n\n// In-memory cache with periodic persistence\nlet rateLimitCache: RateLimitStore = {};\nlet rateLimitCacheDirty = false;\n\nfunction loadRateLimits(): RateLimitStore {\n try {\n if (existsSync(RATE_LIMIT_PATH)) {\n const data = JSON.parse(readFileSync(RATE_LIMIT_PATH, 'utf8'));\n // Clean up expired entries on load\n const now = Date.now();\n const cleaned: RateLimitStore = {};\n for (const [ip, record] of Object.entries(data)) {\n const r = record as RateLimitRecord;\n if (r.resetTime > now) {\n cleaned[ip] = r;\n }\n }\n return cleaned;\n }\n } catch {\n // Use empty store on error\n }\n return {};\n}\n\nfunction saveRateLimits(): void {\n if (!rateLimitCacheDirty) return;\n try {\n ensureSecureDir(join(homedir(), '.stackmemory'));\n writeFileSecure(RATE_LIMIT_PATH, JSON.stringify(rateLimitCache));\n rateLimitCacheDirty = false;\n } catch {\n // Ignore save errors - rate limiting is best-effort\n }\n}\n\n// Persist rate limits periodically (every 30 seconds)\nsetInterval(saveRateLimits, 30000);\n\n// Load on startup\nrateLimitCache = loadRateLimits();\n\nfunction checkRateLimit(ip: string): boolean {\n const now = Date.now();\n const record = rateLimitCache[ip];\n\n if (!record || now > record.resetTime) {\n rateLimitCache[ip] = { count: 1, resetTime: now + RATE_LIMIT_WINDOW_MS };\n rateLimitCacheDirty = true;\n return true;\n }\n\n if (record.count >= RATE_LIMIT_MAX_REQUESTS) {\n return false;\n }\n\n record.count++;\n rateLimitCacheDirty = true;\n return true;\n}\n\n// Twilio signature verification\nfunction verifyTwilioSignature(\n url: string,\n params: Record<string, string>,\n signature: string\n): boolean {\n const authToken = process.env['TWILIO_AUTH_TOKEN'];\n if (!authToken) {\n // Only allow bypass in explicit development mode\n const isDev =\n process.env['NODE_ENV'] === 'development' ||\n process.env['SKIP_TWILIO_VERIFICATION'] === 'true';\n\n if (isDev) {\n console.warn(\n '[sms-webhook] TWILIO_AUTH_TOKEN not set, skipping verification (dev mode)'\n );\n return true;\n }\n\n // In production, reject requests without auth token configured\n console.error(\n '[sms-webhook] TWILIO_AUTH_TOKEN not set - rejecting request in production'\n );\n return false;\n }\n\n // Build the data string (URL + sorted params)\n const sortedKeys = Object.keys(params).sort();\n let data = url;\n for (const key of sortedKeys) {\n data += key + params[key];\n }\n\n // Calculate expected signature\n const hmac = createHmac('sha1', authToken);\n hmac.update(data);\n const expectedSignature = hmac.digest('base64');\n\n return signature === expectedSignature;\n}\n\ninterface TwilioWebhookPayload {\n From: string;\n To: string;\n Body: string;\n MessageSid: string;\n}\n\nfunction parseFormData(body: string): Record<string, string> {\n const params = new URLSearchParams(body);\n const result: Record<string, string> = {};\n params.forEach((value, key) => {\n result[key] = value;\n });\n return result;\n}\n\n// Store response for Claude hook to pick up\nfunction storeLatestResponse(\n promptId: string,\n response: string,\n action?: string\n): void {\n ensureSecureDir(join(homedir(), '.stackmemory'));\n const responsePath = join(\n homedir(),\n '.stackmemory',\n 'sms-latest-response.json'\n );\n writeFileSecure(\n responsePath,\n JSON.stringify({\n promptId,\n response,\n action,\n timestamp: new Date().toISOString(),\n })\n );\n}\n\n/**\n * Store incoming request for Claude to pick up\n * Used when a WhatsApp/SMS message arrives without a pending prompt\n */\nfunction storeIncomingRequest(from: string, message: string): void {\n ensureSecureDir(join(homedir(), '.stackmemory'));\n const requestPath = join(\n homedir(),\n '.stackmemory',\n 'sms-incoming-request.json'\n );\n writeFileSecure(\n requestPath,\n JSON.stringify({\n from,\n message,\n timestamp: new Date().toISOString(),\n processed: false,\n })\n );\n}\n\n/**\n * Get pending incoming request (if any)\n */\nexport function getIncomingRequest(): {\n from: string;\n message: string;\n timestamp: string;\n processed: boolean;\n} | null {\n const requestPath = join(\n homedir(),\n '.stackmemory',\n 'sms-incoming-request.json'\n );\n if (!existsSync(requestPath)) {\n return null;\n }\n try {\n const data = JSON.parse(readFileSync(requestPath, 'utf-8'));\n if (data.processed) {\n return null;\n }\n return data;\n } catch {\n return null;\n }\n}\n\n/**\n * Mark incoming request as processed\n */\nexport function markRequestProcessed(): void {\n const requestPath = join(\n homedir(),\n '.stackmemory',\n 'sms-incoming-request.json'\n );\n if (!existsSync(requestPath)) {\n return;\n }\n try {\n const data = JSON.parse(readFileSync(requestPath, 'utf-8'));\n data.processed = true;\n writeFileSecure(requestPath, JSON.stringify(data));\n } catch {\n // Ignore errors\n }\n}\n\nexport async function handleSMSWebhook(payload: TwilioWebhookPayload): Promise<{\n response: string;\n action?: string;\n queued?: boolean;\n}> {\n const { From, Body } = payload;\n\n // Input length validation\n if (Body && Body.length > MAX_SMS_BODY_LENGTH) {\n console.log(`[sms-webhook] Body too long: ${Body.length} chars`);\n return { response: 'Message too long. Max 1000 characters.' };\n }\n\n if (From && From.length > MAX_PHONE_LENGTH) {\n console.log(\n `[sms-webhook] Phone number too long: ${From.length} chars (max ${MAX_PHONE_LENGTH}): ${From.substring(0, 30)}...`\n );\n return { response: 'Invalid phone number.' };\n }\n\n console.log(`[sms-webhook] Received from ${From}: ${Body}`);\n\n // Check for command prefix before prompt matching\n if (isCommand(Body)) {\n console.log(`[sms-webhook] Processing command: ${Body}`);\n const cmdResult = await processCommand(From, Body);\n\n if (cmdResult.handled) {\n // Send response back if we have one\n if (cmdResult.response) {\n // Don't await - fire and forget the response notification\n sendCommandResponse(cmdResult.response).catch(console.error);\n }\n\n return {\n response: cmdResult.response || 'Command processed',\n action: cmdResult.action,\n queued: false,\n };\n }\n // If not handled, fall through to regular prompt matching\n }\n\n const result = processIncomingResponse(From, Body);\n\n if (!result.matched) {\n if (result.prompt) {\n return {\n response: `Invalid response. Expected: ${result.prompt.options.map((o) => o.key).join(', ')}`,\n };\n }\n // No pending prompt - store as new incoming request for Claude\n storeIncomingRequest(From, Body);\n console.log(\n `[sms-webhook] Stored new request from ${From}: ${Body.substring(0, 50)}...`\n );\n return { response: 'Got it! Your request has been queued.' };\n }\n\n // Store response for Claude hook\n storeLatestResponse(\n result.prompt?.id || 'unknown',\n result.response || Body,\n result.action\n );\n\n // Trigger notification to alert user/Claude\n triggerResponseNotification(result.response || Body);\n\n // Execute action safely if present (no shell injection, with timeout)\n if (result.action) {\n console.log(`[sms-webhook] Executing action: ${result.action}`);\n\n const actionResult = await executeActionWithTimeout(\n result.action,\n result.response || Body\n );\n\n if (actionResult.success) {\n logActionAllowed('sms-webhook', result.action);\n console.log(\n `[sms-webhook] Action completed: ${(actionResult.output || '').substring(0, 200)}`\n );\n\n return {\n response: `Done! Action executed successfully.`,\n action: result.action,\n queued: false,\n };\n } else {\n logActionBlocked(\n 'sms-webhook',\n result.action,\n actionResult.error || 'unknown'\n );\n console.log(`[sms-webhook] Action failed: ${actionResult.error}`);\n\n // Queue for retry\n queueAction(\n result.prompt?.id || 'unknown',\n result.response || Body,\n result.action\n );\n\n return {\n response: `Action failed, queued for retry: ${(actionResult.error || '').substring(0, 50)}`,\n action: result.action,\n queued: true,\n };\n }\n }\n\n return {\n response: `Received: ${result.response}. Next action will be triggered.`,\n };\n}\n\n// Escape string for AppleScript (prevent injection)\nfunction escapeAppleScript(str: string): string {\n return str\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\"/g, '\\\\\"')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n .substring(0, 200); // Limit length\n}\n\n// Trigger notification when response received\nfunction triggerResponseNotification(response: string): void {\n const safeMessage = escapeAppleScript(`SMS Response: ${response}`);\n\n // macOS notification using execFile (safer than execSync with shell)\n try {\n execFileSync(\n 'osascript',\n [\n '-e',\n `display notification \"${safeMessage}\" with title \"StackMemory\" sound name \"Glass\"`,\n ],\n { stdio: 'ignore', timeout: 5000 }\n );\n } catch {\n // Ignore if not on macOS\n }\n\n // Write signal file for other processes\n try {\n const signalPath = join(homedir(), '.stackmemory', 'sms-signal.txt');\n writeFileSecure(\n signalPath,\n JSON.stringify({\n type: 'sms_response',\n response,\n timestamp: new Date().toISOString(),\n })\n );\n } catch {\n // Ignore\n }\n\n console.log(`\\n*** SMS RESPONSE RECEIVED: \"${response}\" ***`);\n console.log(`*** Run: stackmemory notify run-actions ***\\n`);\n}\n\n// TwiML response helper\nfunction twimlResponse(message: string): string {\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Response>\n <Message>${escapeXml(message)}</Message>\n</Response>`;\n}\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n// Standalone webhook server\nexport function startWebhookServer(port: number = 3456): void {\n const server = createServer(\n async (req: IncomingMessage, res: ServerResponse) => {\n const url = parseUrl(req.url || '/', true);\n\n // Health check\n if (url.pathname === '/health') {\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ status: 'ok' }));\n return;\n }\n\n // SMS webhook endpoint (incoming messages)\n if (\n (url.pathname === '/sms' ||\n url.pathname === '/sms/incoming' ||\n url.pathname === '/webhook') &&\n req.method === 'POST'\n ) {\n const clientIp = req.socket.remoteAddress || 'unknown';\n\n // Log webhook request\n logWebhookRequest(\n 'sms-webhook',\n req.method || 'POST',\n url.pathname || '/sms',\n clientIp\n );\n\n // Rate limiting\n if (!checkRateLimit(clientIp)) {\n logRateLimit('sms-webhook', clientIp);\n res.writeHead(429, {\n 'Content-Type': 'text/xml',\n 'Retry-After': '60',\n });\n res.end(twimlResponse('Too many requests. Please try again later.'));\n return;\n }\n\n // Content-type validation\n const contentType = req.headers['content-type'] || '';\n if (!contentType.includes('application/x-www-form-urlencoded')) {\n logContentTypeInvalid('sms-webhook', contentType, clientIp);\n res.writeHead(400, { 'Content-Type': 'text/xml' });\n res.end(twimlResponse('Invalid content type'));\n return;\n }\n\n let body = '';\n let bodyTooLarge = false;\n\n req.on('data', (chunk) => {\n body += chunk;\n // Body size limit\n if (body.length > MAX_BODY_SIZE) {\n bodyTooLarge = true;\n logBodyTooLarge('sms-webhook', body.length, clientIp);\n req.destroy();\n }\n });\n\n req.on('end', async () => {\n if (bodyTooLarge) {\n res.writeHead(413, { 'Content-Type': 'text/xml' });\n res.end(twimlResponse('Request too large'));\n return;\n }\n\n try {\n const payload = parseFormData(\n body\n ) as unknown as TwilioWebhookPayload;\n\n // Verify Twilio signature\n const twilioSignature = req.headers['x-twilio-signature'] as string;\n const webhookUrl = `${req.headers['x-forwarded-proto'] || 'http'}://${req.headers.host}${req.url}`;\n\n if (\n twilioSignature &&\n !verifyTwilioSignature(\n webhookUrl,\n payload as unknown as Record<string, string>,\n twilioSignature\n )\n ) {\n logSignatureInvalid('sms-webhook', clientIp);\n console.error('[sms-webhook] Invalid Twilio signature');\n res.writeHead(401, { 'Content-Type': 'text/xml' });\n res.end(twimlResponse('Unauthorized'));\n return;\n }\n\n const result = await handleSMSWebhook(payload);\n\n res.writeHead(200, { 'Content-Type': 'text/xml' });\n res.end(twimlResponse(result.response));\n } catch (err) {\n console.error('[sms-webhook] Error:', err);\n res.writeHead(500, { 'Content-Type': 'text/xml' });\n res.end(twimlResponse('Error processing message'));\n }\n });\n return;\n }\n\n // Status callback endpoint (delivery status updates)\n if (url.pathname === '/sms/status' && req.method === 'POST') {\n let body = '';\n req.on('data', (chunk) => {\n body += chunk;\n });\n\n req.on('end', () => {\n try {\n const payload = parseFormData(body);\n console.log(\n `[sms-webhook] Status update: ${payload['MessageSid']} -> ${payload['MessageStatus']}`\n );\n\n // Store status for tracking\n const statusPath = join(\n homedir(),\n '.stackmemory',\n 'sms-status.json'\n );\n const statuses: Record<string, string> = existsSync(statusPath)\n ? JSON.parse(readFileSync(statusPath, 'utf8'))\n : {};\n statuses[payload['MessageSid']] = payload['MessageStatus'];\n writeFileSecure(statusPath, JSON.stringify(statuses, null, 2));\n\n res.writeHead(200, { 'Content-Type': 'text/plain' });\n res.end('OK');\n } catch (err) {\n console.error('[sms-webhook] Status error:', err);\n res.writeHead(500);\n res.end('Error');\n }\n });\n return;\n }\n\n // Server status endpoint\n if (url.pathname === '/status') {\n const config = loadSMSConfig();\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({\n enabled: config.enabled,\n pendingPrompts: config.pendingPrompts.length,\n })\n );\n return;\n }\n\n // Get pending incoming request endpoint\n if (url.pathname === '/request' && req.method === 'GET') {\n const request = getIncomingRequest();\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ request }));\n return;\n }\n\n // Mark request as processed endpoint\n if (url.pathname === '/request/ack' && req.method === 'POST') {\n markRequestProcessed();\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ success: true }));\n return;\n }\n\n // Send outgoing notification endpoint\n if (url.pathname === '/send' && req.method === 'POST') {\n let body = '';\n req.on('data', (chunk) => {\n body += chunk;\n if (body.length > MAX_BODY_SIZE) {\n req.destroy();\n }\n });\n\n req.on('end', async () => {\n try {\n const payload = JSON.parse(body);\n const message = payload.message || payload.body || '';\n const title = payload.title || 'Notification';\n const type = payload.type || 'custom';\n\n if (!message) {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({ success: false, error: 'Message required' })\n );\n return;\n }\n\n const result = await sendNotification({\n type: type as\n | 'task_complete'\n | 'review_ready'\n | 'error'\n | 'custom',\n title,\n message,\n });\n\n res.writeHead(result.success ? 200 : 500, {\n 'Content-Type': 'application/json',\n });\n res.end(JSON.stringify(result));\n } catch (err) {\n console.error('[sms-webhook] Send error:', err);\n res.writeHead(500, { 'Content-Type': 'application/json' });\n res.end(\n JSON.stringify({\n success: false,\n error: err instanceof Error ? err.message : 'Send failed',\n })\n );\n }\n });\n return;\n }\n\n res.writeHead(404);\n res.end('Not found');\n }\n );\n\n server.listen(port, () => {\n console.log(`[sms-webhook] Server listening on port ${port}`);\n console.log(\n `[sms-webhook] Incoming messages: http://localhost:${port}/sms/incoming`\n );\n console.log(\n `[sms-webhook] Status callback: http://localhost:${port}/sms/status`\n );\n console.log(`[sms-webhook] Configure these URLs in Twilio console`);\n\n // Start timed cleanup of expired prompts and old actions\n setInterval(() => {\n try {\n const expiredPrompts = cleanupExpiredPrompts();\n const oldActions = cleanupOldActions();\n if (expiredPrompts > 0 || oldActions > 0) {\n logCleanup('sms-webhook', expiredPrompts, oldActions);\n console.log(\n `[sms-webhook] Cleanup: ${expiredPrompts} expired prompts, ${oldActions} old actions`\n );\n }\n } catch {\n // Ignore cleanup errors\n }\n }, CLEANUP_INTERVAL_MS);\n console.log(\n `[sms-webhook] Cleanup interval: every ${CLEANUP_INTERVAL_MS / 1000}s`\n );\n });\n}\n\n// Express middleware for integration\nexport async function smsWebhookMiddleware(\n req: { body: TwilioWebhookPayload },\n res: { type: (t: string) => void; send: (s: string) => void }\n): Promise<void> {\n const result = await handleSMSWebhook(req.body);\n res.type('text/xml');\n res.send(twimlResponse(result.response));\n}\n\n// CLI entry\nif (process.argv[1]?.endsWith('sms-webhook.js')) {\n const port = parseInt(process.env['SMS_WEBHOOK_PORT'] || '3456', 10);\n startWebhookServer(port);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/hooks/whatsapp-commands.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * WhatsApp Inbound Command Processor\n * Process WhatsApp messages as commands\n */\n\nimport { existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport { execFileSync } from 'child_process';\nimport { writeFileSecure, ensureSecureDir } from './secure-fs.js';\nimport { WhatsAppCommandsConfigSchema, parseConfigSafe } from './schemas.js';\nimport { executeActionSafe } from './sms-action-runner.js';\nimport {\n syncContext,\n getFrameDigestData,\n generateMobileDigest,\n loadSyncOptions,\n} from './whatsapp-sync.js';\nimport { sendNotification } from './sms-notify.js';\n\n// Max input length for regex matching to prevent catastrophic backtracking\nconst MAX_REGEX_INPUT_LENGTH = 200;\n\n// Dangerous regex patterns that can cause ReDoS (catastrophic backtracking)\n// These patterns have nested quantifiers or overlapping alternatives\nconst DANGEROUS_PATTERNS = [\n /(\\+|\\*|\\?)\\s*(\\+|\\*|\\?)/, // Nested quantifiers like .+* or .*+\n /\\(\\?[^)]*\\)\\s*[+*]/, // Quantified groups with + or *\n /\\[[^\\]]*\\]\\s*[+*]\\s*[+*]/, // Character classes with nested quantifiers\n /(\\.\\*|\\.\\+)\\s*(\\.\\*|\\.\\+)/, // Overlapping .* or .+\n /\\(\\[[^\\]]+\\]\\+\\)\\+/, // Nested + with character class\n /\\(.*\\+\\).*\\+/, // Nested + quantifiers\n];\n\n/**\n * Check if a regex pattern might be vulnerable to ReDoS\n * Returns true if the pattern appears safe, false if potentially dangerous\n */\nfunction isPatternSafe(pattern: string): boolean {\n // Check against known dangerous patterns\n for (const dangerous of DANGEROUS_PATTERNS) {\n if (dangerous.test(pattern)) {\n console.warn(\n `[whatsapp-commands] Potentially dangerous regex pattern blocked: ${pattern}`\n );\n return false;\n }\n }\n\n // Additional heuristics: limit pattern complexity\n const quantifierCount = (pattern.match(/[+*?]/g) || []).length;\n const groupCount = (pattern.match(/\\(/g) || []).length;\n\n // If too many quantifiers or groups, consider it risky\n if (quantifierCount > 5 || groupCount > 3) {\n console.warn(\n `[whatsapp-commands] Complex regex pattern blocked: ${pattern} (${quantifierCount} quantifiers, ${groupCount} groups)`\n );\n return false;\n }\n\n return true;\n}\n\n/**\n * Safely test a regex pattern against input with ReDoS protection\n * Pre-validates the pattern for dangerous constructs before testing\n * Returns false if pattern is dangerous, invalid, or doesn't match\n */\nfunction safeRegexTest(pattern: string, input: string): boolean {\n // Pre-validate pattern for ReDoS safety BEFORE running it\n if (!isPatternSafe(pattern)) {\n return false;\n }\n\n // Truncate input to prevent catastrophic backtracking\n const safeInput = input.slice(0, MAX_REGEX_INPUT_LENGTH);\n\n try {\n const regex = new RegExp(pattern);\n return regex.test(safeInput);\n } catch {\n // Invalid regex pattern\n console.warn(`[whatsapp-commands] Invalid regex pattern: ${pattern}`);\n return false;\n }\n}\n\nexport interface WhatsAppCommand {\n name: string;\n description: string;\n enabled: boolean;\n action?: string; // Safe action to execute\n requiresArg?: boolean;\n argPattern?: string; // Regex pattern for arg validation\n}\n\nexport interface CommandsConfig {\n enabled: boolean;\n commands: WhatsAppCommand[];\n}\n\nexport interface CommandResult {\n handled: boolean;\n response?: string;\n action?: string;\n error?: string;\n}\n\nconst CONFIG_PATH = join(homedir(), '.stackmemory', 'whatsapp-commands.json');\nconst REMOTE_SESSIONS_PATH = join(\n homedir(),\n '.stackmemory',\n 'remote-sessions.json'\n);\n\n/**\n * Remote session tracking\n */\nexport interface RemoteSession {\n id: string;\n url: string;\n prompt: string;\n createdAt: string;\n status: 'active' | 'completed' | 'failed';\n lastActivity?: string;\n}\n\ninterface RemoteSessionsStore {\n sessions: RemoteSession[];\n}\n\nfunction loadRemoteSessions(): RemoteSessionsStore {\n try {\n if (existsSync(REMOTE_SESSIONS_PATH)) {\n return JSON.parse(readFileSync(REMOTE_SESSIONS_PATH, 'utf8'));\n }\n } catch {\n // Use defaults\n }\n return { sessions: [] };\n}\n\nfunction saveRemoteSessions(store: RemoteSessionsStore): void {\n try {\n ensureSecureDir(join(homedir(), '.stackmemory'));\n writeFileSecure(REMOTE_SESSIONS_PATH, JSON.stringify(store, null, 2));\n } catch {\n // Silently fail\n }\n}\n\nfunction addRemoteSession(session: RemoteSession): void {\n const store = loadRemoteSessions();\n // Keep last 20 sessions\n store.sessions = [session, ...store.sessions.slice(0, 19)];\n saveRemoteSessions(store);\n}\n\nexport function getRemoteSessions(): RemoteSession[] {\n return loadRemoteSessions().sessions;\n}\n\nexport function getActiveRemoteSessions(): RemoteSession[] {\n return loadRemoteSessions().sessions.filter((s) => s.status === 'active');\n}\n\n// Default supported commands - simplified for notifications + choices\nconst DEFAULT_COMMANDS: WhatsAppCommand[] = [\n {\n name: 'help',\n description: 'List available commands',\n enabled: true,\n },\n {\n name: 'status',\n description: 'Get current task/frame status',\n enabled: true,\n },\n {\n name: 'sessions',\n description: 'List active remote sessions with URLs',\n enabled: true,\n },\n {\n name: 'remote',\n description: 'Launch remote Claude session (requires task prompt)',\n enabled: true,\n requiresArg: true,\n },\n // Disabled by default - can be enabled in config if needed\n {\n name: 'context',\n description: 'Get latest context digest',\n enabled: false,\n },\n {\n name: 'sync',\n description: 'Push current context to WhatsApp',\n enabled: false,\n },\n {\n name: 'tasks',\n description: 'List active tasks',\n enabled: false,\n },\n];\n\nconst DEFAULT_CONFIG: CommandsConfig = {\n enabled: true,\n commands: DEFAULT_COMMANDS,\n};\n\n/**\n * Load commands config\n */\nexport function loadCommandsConfig(): CommandsConfig {\n try {\n if (existsSync(CONFIG_PATH)) {\n const data = JSON.parse(readFileSync(CONFIG_PATH, 'utf8'));\n return parseConfigSafe(\n WhatsAppCommandsConfigSchema,\n { ...DEFAULT_CONFIG, ...data },\n DEFAULT_CONFIG,\n 'whatsapp-commands'\n );\n }\n } catch {\n // Use defaults\n }\n return { ...DEFAULT_CONFIG };\n}\n\n/**\n * Save commands config\n */\nexport function saveCommandsConfig(config: CommandsConfig): void {\n try {\n ensureSecureDir(join(homedir(), '.stackmemory'));\n writeFileSecure(CONFIG_PATH, JSON.stringify(config, null, 2));\n } catch {\n // Silently fail\n }\n}\n\n/**\n * Check if a message is a command\n */\nexport function isCommand(message: string): boolean {\n const trimmed = message.trim().toLowerCase();\n\n // Check if it's a single word command\n const config = loadCommandsConfig();\n if (!config.enabled) return false;\n\n const words = trimmed.split(/\\s+/);\n const firstWord = words[0];\n\n return config.commands.some(\n (cmd) => cmd.enabled && cmd.name.toLowerCase() === firstWord\n );\n}\n\n/**\n * Parse command from message\n */\nfunction parseCommand(message: string): { name: string; arg?: string } | null {\n const trimmed = message.trim();\n const words = trimmed.split(/\\s+/);\n\n if (words.length === 0) return null;\n\n const name = words[0].toLowerCase();\n const arg = words.slice(1).join(' ').trim() || undefined;\n\n return { name, arg };\n}\n\n/**\n * Generate help text for available commands\n */\nfunction generateHelpText(config: CommandsConfig): string {\n const lines: string[] = ['Available commands:'];\n\n config.commands\n .filter((cmd) => cmd.enabled)\n .forEach((cmd) => {\n const argHint = cmd.requiresArg ? ' <arg>' : '';\n lines.push(` ${cmd.name}${argHint} - ${cmd.description}`);\n });\n\n lines.push('');\n lines.push('Reply with command name to execute');\n\n return lines.join('\\n');\n}\n\n/**\n * Handle the 'context' command specially\n */\nasync function handleContextCommand(): Promise<string> {\n const data = await getFrameDigestData();\n\n if (!data) {\n return 'No context available. Start a task first.';\n }\n\n const options = loadSyncOptions();\n return generateMobileDigest(data, options);\n}\n\n/**\n * Handle the 'sync' command specially\n */\nasync function handleSyncCommand(): Promise<string> {\n const result = await syncContext();\n\n if (result.success) {\n return `Context synced (${result.digestLength} chars)`;\n } else {\n return `Sync failed: ${result.error}`;\n }\n}\n\n/**\n * Handle the 'status' command - get current frame/task status\n */\nasync function handleStatusCommand(): Promise<string> {\n try {\n const data = await getFrameDigestData();\n if (!data) {\n return 'No active session. Start with: claude-sm';\n }\n\n const lines: string[] = [];\n lines.push(`Frame: ${data.name || data.frameId}`);\n lines.push(`Status: ${data.status}`);\n lines.push(`Files: ${data.filesModified?.length || 0} modified`);\n lines.push(`Tools: ${data.toolCallCount || 0} calls`);\n if (data.errors?.length > 0) {\n const unresolved = data.errors.filter((e) => !e.resolved).length;\n if (unresolved > 0) lines.push(`Errors: ${unresolved} unresolved`);\n }\n lines.push(`Duration: ${Math.round(data.durationSeconds / 60)}min`);\n\n return lines.join('\\n');\n } catch {\n return 'Status unavailable';\n }\n}\n\n/**\n * Handle the 'tasks' command - list recent decisions/risks\n */\nasync function handleTasksCommand(): Promise<string> {\n try {\n const data = await getFrameDigestData();\n if (!data) {\n return 'No active tasks';\n }\n\n const lines: string[] = [];\n\n if (data.decisions?.length > 0) {\n lines.push('Recent decisions:');\n data.decisions.slice(0, 3).forEach((d, i) => {\n lines.push(\n `${i + 1}. ${d.substring(0, 50)}${d.length > 50 ? '...' : ''}`\n );\n });\n }\n\n if (data.risks?.length > 0) {\n lines.push('');\n lines.push('Risks:');\n data.risks.slice(0, 2).forEach((r) => {\n lines.push(`- ${r.substring(0, 50)}${r.length > 50 ? '...' : ''}`);\n });\n }\n\n if (lines.length === 0) {\n return 'No active tasks or decisions';\n }\n\n return lines.join('\\n');\n } catch {\n return 'Tasks unavailable';\n }\n}\n\n/**\n * Handle the 'remote' command - launch a remote Claude session\n */\nasync function handleRemoteCommand(prompt: string): Promise<string> {\n try {\n // Sanitize prompt - remove any shell-dangerous characters\n const sanitizedPrompt = prompt\n .replace(/[`$\\\\]/g, '')\n .replace(/[\"']/g, \"'\")\n .substring(0, 500);\n\n if (!sanitizedPrompt.trim()) {\n return 'Please provide a task prompt. Usage: remote <your task>';\n }\n\n console.log(\n `[whatsapp-commands] Launching remote session: ${sanitizedPrompt.substring(0, 50)}...`\n );\n\n // Execute claude --remote with the prompt\n const output = execFileSync('claude', ['--remote', sanitizedPrompt], {\n encoding: 'utf8',\n timeout: 30000,\n stdio: ['pipe', 'pipe', 'pipe'],\n });\n\n // Parse session URL from output\n // Expected format contains: https://claude.ai/code/session_...\n const urlMatch = output.match(\n /https:\\/\\/claude\\.ai\\/code\\/session_[a-zA-Z0-9]+/\n );\n\n if (urlMatch) {\n const sessionUrl = urlMatch[0];\n const sessionId = sessionUrl.split('/').pop() || 'unknown';\n\n // Store the session\n addRemoteSession({\n id: sessionId,\n url: sessionUrl,\n prompt: sanitizedPrompt,\n createdAt: new Date().toISOString(),\n status: 'active',\n });\n\n return `Remote session launched!\\n\\n${sessionUrl}\\n\\nTask: ${sanitizedPrompt.substring(0, 100)}`;\n }\n\n // No URL found - return raw output\n return `Session launched:\\n${output.substring(0, 300)}`;\n } catch (err) {\n const error = err instanceof Error ? err.message : String(err);\n console.error(`[whatsapp-commands] Remote launch failed: ${error}`);\n return `Failed to launch remote session: ${error.substring(0, 100)}`;\n }\n}\n\n/**\n * Handle the 'sessions' command - list active remote sessions\n */\nfunction handleSessionsCommand(): string {\n const sessions = getActiveRemoteSessions();\n\n if (sessions.length === 0) {\n return 'No active remote sessions';\n }\n\n const lines: string[] = ['Active remote sessions:'];\n\n sessions.slice(0, 5).forEach((s, i) => {\n const age = Math.round(\n (Date.now() - new Date(s.createdAt).getTime()) / 60000\n );\n const ageStr = age < 60 ? `${age}m ago` : `${Math.round(age / 60)}h ago`;\n lines.push(`${i + 1}. ${s.prompt.substring(0, 40)}... (${ageStr})`);\n lines.push(` ${s.url}`);\n });\n\n return lines.join('\\n');\n}\n\n/**\n * Process an incoming WhatsApp command\n */\nexport async function processCommand(\n from: string,\n message: string\n): Promise<CommandResult> {\n const config = loadCommandsConfig();\n\n if (!config.enabled) {\n return { handled: false };\n }\n\n const parsed = parseCommand(message);\n if (!parsed) {\n return { handled: false };\n }\n\n const command = config.commands.find(\n (cmd) => cmd.enabled && cmd.name.toLowerCase() === parsed.name\n );\n\n if (!command) {\n return { handled: false };\n }\n\n // Handle special commands\n if (command.name === 'help') {\n const helpText = generateHelpText(config);\n return { handled: true, response: helpText };\n }\n\n if (command.name === 'context') {\n const contextText = await handleContextCommand();\n return { handled: true, response: contextText };\n }\n\n if (command.name === 'sync') {\n const syncText = await handleSyncCommand();\n return { handled: true, response: syncText };\n }\n\n if (command.name === 'status') {\n const statusText = await handleStatusCommand();\n return { handled: true, response: statusText };\n }\n\n if (command.name === 'tasks') {\n const tasksText = await handleTasksCommand();\n return { handled: true, response: tasksText };\n }\n\n if (command.name === 'remote') {\n if (!parsed.arg) {\n return {\n handled: true,\n response:\n 'Usage: remote <task prompt>\\nExample: remote Fix the login bug',\n error: 'Missing prompt',\n };\n }\n const remoteText = await handleRemoteCommand(parsed.arg);\n return { handled: true, response: remoteText };\n }\n\n if (command.name === 'sessions') {\n const sessionsText = handleSessionsCommand();\n return { handled: true, response: sessionsText };\n }\n\n // Check if argument is required\n if (command.requiresArg && !parsed.arg) {\n return {\n handled: true,\n response: `${command.name} requires an argument. Usage: ${command.name} <arg>`,\n error: 'Missing argument',\n };\n }\n\n // Validate argument pattern if specified (with ReDoS protection)\n if (command.argPattern && parsed.arg) {\n if (!safeRegexTest(command.argPattern, parsed.arg)) {\n return {\n handled: true,\n response: `Invalid argument format for ${command.name}`,\n error: 'Invalid argument format',\n };\n }\n }\n\n // Build the action command\n let action = command.action;\n\n if (action && parsed.arg) {\n // Special handling for PR commands\n if (command.name === 'approve') {\n action = `gh pr review ${parsed.arg} --approve`;\n } else if (command.name === 'merge') {\n action = `gh pr merge ${parsed.arg} --squash`;\n }\n }\n\n // Execute the action if defined\n if (action) {\n console.log(`[whatsapp-commands] Executing: ${action}`);\n\n const result = await executeActionSafe(action, message);\n\n if (result.success) {\n const output = result.output?.slice(0, 200) || 'Done';\n return {\n handled: true,\n response: `${command.name}: ${output}`,\n action,\n };\n } else {\n return {\n handled: true,\n response: `${command.name} failed: ${result.error?.slice(0, 100)}`,\n error: result.error,\n action,\n };\n }\n }\n\n return {\n handled: true,\n response: `Command ${command.name} acknowledged`,\n };\n}\n\n/**\n * Send command result back via WhatsApp\n */\nexport async function sendCommandResponse(\n response: string\n): Promise<{ success: boolean; error?: string }> {\n const result = await sendNotification({\n type: 'custom',\n title: 'Command Result',\n message: response,\n });\n\n return { success: result.success, error: result.error };\n}\n\n/**\n * Enable command processing\n */\nexport function enableCommands(): void {\n const config = loadCommandsConfig();\n config.enabled = true;\n saveCommandsConfig(config);\n}\n\n/**\n * Disable command processing\n */\nexport function disableCommands(): void {\n const config = loadCommandsConfig();\n config.enabled = false;\n saveCommandsConfig(config);\n}\n\n/**\n * Check if commands are enabled\n */\nexport function isCommandsEnabled(): boolean {\n const config = loadCommandsConfig();\n return config.enabled;\n}\n\n/**\n * Add a custom command\n */\nexport function addCommand(command: WhatsAppCommand): void {\n const config = loadCommandsConfig();\n\n // Check if command already exists\n const existingIndex = config.commands.findIndex(\n (c) => c.name.toLowerCase() === command.name.toLowerCase()\n );\n\n if (existingIndex >= 0) {\n config.commands[existingIndex] = command;\n } else {\n config.commands.push(command);\n }\n\n saveCommandsConfig(config);\n}\n\n/**\n * Remove a custom command\n */\nexport function removeCommand(name: string): boolean {\n const config = loadCommandsConfig();\n const initialLength = config.commands.length;\n\n config.commands = config.commands.filter(\n (c) => c.name.toLowerCase() !== name.toLowerCase()\n );\n\n if (config.commands.length < initialLength) {\n saveCommandsConfig(config);\n return true;\n }\n\n return false;\n}\n\n/**\n * Get list of available commands\n */\nexport function getAvailableCommands(): WhatsAppCommand[] {\n const config = loadCommandsConfig();\n return config.commands.filter((c) => c.enabled);\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/hooks/whatsapp-scheduler.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * WhatsApp Scheduled Digest Manager\n * Schedule periodic context digests\n */\n\nimport { existsSync, readFileSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport { randomBytes } from 'crypto';\nimport { writeFileSecure, ensureSecureDir } from './secure-fs.js';\nimport { ScheduleStorageSchema, parseConfigSafe } from './schemas.js';\nimport {\n getFrameDigestData,\n generateMobileDigest,\n loadSyncOptions,\n} from './whatsapp-sync.js';\nimport { sendNotification, loadSMSConfig } from './sms-notify.js';\n\nexport interface ScheduleConfig {\n type: 'daily' | 'hourly' | 'interval';\n time?: string; // \"HH:MM\" for daily\n intervalMinutes?: number; // for interval type\n includeInactive?: boolean; // include when no activity\n quietHoursRespect: boolean;\n}\n\nexport interface Schedule {\n id: string;\n config: ScheduleConfig;\n enabled: boolean;\n lastRun?: string;\n nextRun?: string;\n createdAt: string;\n}\n\ninterface ScheduleStorage {\n schedules: Schedule[];\n lastChecked: string;\n}\n\nconst STORAGE_PATH = join(homedir(), '.stackmemory', 'whatsapp-schedules.json');\n\nconst DEFAULT_STORAGE: ScheduleStorage = {\n schedules: [],\n lastChecked: new Date().toISOString(),\n};\n\n// Active scheduler interval handle\nlet schedulerInterval: NodeJS.Timeout | null = null;\n\n/**\n * Load schedule storage\n */\nfunction loadStorage(): ScheduleStorage {\n try {\n if (existsSync(STORAGE_PATH)) {\n const data = JSON.parse(readFileSync(STORAGE_PATH, 'utf8'));\n return parseConfigSafe(\n ScheduleStorageSchema,\n data,\n DEFAULT_STORAGE,\n 'whatsapp-schedules'\n );\n }\n } catch {\n // Use defaults\n }\n return { ...DEFAULT_STORAGE, lastChecked: new Date().toISOString() };\n}\n\n/**\n * Save schedule storage\n */\nfunction saveStorage(storage: ScheduleStorage): void {\n try {\n ensureSecureDir(join(homedir(), '.stackmemory'));\n writeFileSecure(STORAGE_PATH, JSON.stringify(storage, null, 2));\n } catch {\n // Silently fail\n }\n}\n\n/**\n * Generate unique schedule ID\n */\nfunction generateScheduleId(): string {\n return randomBytes(6).toString('hex');\n}\n\n/**\n * Parse time string to hours and minutes\n */\nfunction parseTime(time: string): { hours: number; minutes: number } | null {\n const match = time.match(/^(\\d{2}):(\\d{2})$/);\n if (!match) return null;\n\n const hours = parseInt(match[1], 10);\n const minutes = parseInt(match[2], 10);\n\n if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59) {\n return null;\n }\n\n return { hours, minutes };\n}\n\n/**\n * Calculate next run time for a schedule\n */\nfunction calculateNextRun(config: ScheduleConfig, fromDate?: Date): Date {\n const now = fromDate || new Date();\n\n switch (config.type) {\n case 'daily': {\n const time = config.time\n ? parseTime(config.time)\n : { hours: 9, minutes: 0 };\n if (!time) {\n throw new Error(`Invalid time format: ${config.time}`);\n }\n\n const next = new Date(now);\n next.setHours(time.hours, time.minutes, 0, 0);\n\n // If the time has passed today, schedule for tomorrow\n if (next <= now) {\n next.setDate(next.getDate() + 1);\n }\n\n return next;\n }\n\n case 'hourly': {\n const next = new Date(now);\n next.setMinutes(0, 0, 0);\n next.setHours(next.getHours() + 1);\n return next;\n }\n\n case 'interval': {\n const intervalMinutes = config.intervalMinutes || 60;\n const next = new Date(now.getTime() + intervalMinutes * 60 * 1000);\n return next;\n }\n\n default:\n throw new Error(`Unknown schedule type: ${config.type}`);\n }\n}\n\n/**\n * Check if current time is within quiet hours\n */\nfunction isQuietHours(): boolean {\n const smsConfig = loadSMSConfig();\n\n if (!smsConfig.quietHours?.enabled) {\n return false;\n }\n\n const now = new Date();\n const currentMinutes = now.getHours() * 60 + now.getMinutes();\n\n const startTime = parseTime(smsConfig.quietHours.start);\n const endTime = parseTime(smsConfig.quietHours.end);\n\n if (!startTime || !endTime) {\n return false;\n }\n\n const startMinutes = startTime.hours * 60 + startTime.minutes;\n const endMinutes = endTime.hours * 60 + endTime.minutes;\n\n // Handle overnight quiet hours (e.g., 22:00 - 08:00)\n if (startMinutes > endMinutes) {\n return currentMinutes >= startMinutes || currentMinutes < endMinutes;\n }\n\n return currentMinutes >= startMinutes && currentMinutes < endMinutes;\n}\n\n/**\n * Schedule a periodic digest\n */\nexport function scheduleDigest(config: ScheduleConfig): string {\n const storage = loadStorage();\n\n const id = generateScheduleId();\n const nextRun = calculateNextRun(config);\n\n const schedule: Schedule = {\n id,\n config,\n enabled: true,\n nextRun: nextRun.toISOString(),\n createdAt: new Date().toISOString(),\n };\n\n storage.schedules.push(schedule);\n saveStorage(storage);\n\n console.log(\n `[whatsapp-scheduler] Created schedule ${id}, next run: ${nextRun.toISOString()}`\n );\n\n return id;\n}\n\n/**\n * Cancel a schedule\n */\nexport function cancelSchedule(scheduleId: string): boolean {\n const storage = loadStorage();\n const initialLength = storage.schedules.length;\n\n storage.schedules = storage.schedules.filter((s) => s.id !== scheduleId);\n\n if (storage.schedules.length < initialLength) {\n saveStorage(storage);\n console.log(`[whatsapp-scheduler] Cancelled schedule ${scheduleId}`);\n return true;\n }\n\n return false;\n}\n\n/**\n * Enable/disable a schedule\n */\nexport function setScheduleEnabled(\n scheduleId: string,\n enabled: boolean\n): boolean {\n const storage = loadStorage();\n const schedule = storage.schedules.find((s) => s.id === scheduleId);\n\n if (!schedule) {\n return false;\n }\n\n schedule.enabled = enabled;\n\n if (enabled) {\n schedule.nextRun = calculateNextRun(schedule.config).toISOString();\n }\n\n saveStorage(storage);\n return true;\n}\n\n/**\n * List all schedules\n */\nexport function listSchedules(): Schedule[] {\n const storage = loadStorage();\n return storage.schedules;\n}\n\n/**\n * Get a specific schedule\n */\nexport function getSchedule(scheduleId: string): Schedule | undefined {\n const storage = loadStorage();\n return storage.schedules.find((s) => s.id === scheduleId);\n}\n\n/**\n * Generate activity summary for digest\n */\nasync function generateActivitySummary(): Promise<string | null> {\n const data = await getFrameDigestData();\n\n if (!data) {\n return null;\n }\n\n const options = loadSyncOptions();\n return generateMobileDigest(data, options);\n}\n\n/**\n * Run a scheduled digest now\n */\nexport async function runScheduledDigest(scheduleId: string): Promise<{\n success: boolean;\n sent: boolean;\n message?: string;\n error?: string;\n}> {\n const storage = loadStorage();\n const schedule = storage.schedules.find((s) => s.id === scheduleId);\n\n if (!schedule) {\n return {\n success: false,\n sent: false,\n error: `Schedule not found: ${scheduleId}`,\n };\n }\n\n if (!schedule.enabled) {\n return { success: false, sent: false, error: 'Schedule is disabled' };\n }\n\n // Check quiet hours\n if (schedule.config.quietHoursRespect && isQuietHours()) {\n return {\n success: true,\n sent: false,\n message: 'Skipped due to quiet hours',\n };\n }\n\n // Generate digest\n const digest = await generateActivitySummary();\n\n if (!digest && !schedule.config.includeInactive) {\n // Update next run time even if we don't send\n schedule.lastRun = new Date().toISOString();\n schedule.nextRun = calculateNextRun(schedule.config).toISOString();\n saveStorage(storage);\n\n return { success: true, sent: false, message: 'No activity to report' };\n }\n\n const message = digest || 'No recent activity. All systems idle.';\n\n // Send notification\n const result = await sendNotification({\n type: 'custom',\n title: 'Scheduled Digest',\n message,\n prompt: {\n type: 'options',\n options: [\n { key: '1', label: 'Details', action: 'stackmemory status' },\n { key: '2', label: 'Tasks', action: 'stackmemory task list' },\n ],\n },\n });\n\n // Update schedule\n schedule.lastRun = new Date().toISOString();\n schedule.nextRun = calculateNextRun(schedule.config).toISOString();\n saveStorage(storage);\n\n if (result.success) {\n return {\n success: true,\n sent: true,\n message: `Digest sent (${message.length} chars)`,\n };\n } else {\n return { success: false, sent: false, error: result.error };\n }\n}\n\n/**\n * Check and run due schedules\n */\nexport async function checkAndRunDueSchedules(): Promise<{\n checked: number;\n ran: number;\n errors: number;\n}> {\n const storage = loadStorage();\n const now = new Date();\n\n let ran = 0;\n let errors = 0;\n\n for (const schedule of storage.schedules) {\n if (!schedule.enabled || !schedule.nextRun) {\n continue;\n }\n\n const nextRun = new Date(schedule.nextRun);\n\n if (nextRun <= now) {\n console.log(`[whatsapp-scheduler] Running due schedule ${schedule.id}`);\n\n const result = await runScheduledDigest(schedule.id);\n\n if (result.success) {\n if (result.sent) {\n ran++;\n }\n } else {\n errors++;\n console.error(\n `[whatsapp-scheduler] Schedule ${schedule.id} failed: ${result.error}`\n );\n }\n }\n }\n\n // Update last checked\n storage.lastChecked = now.toISOString();\n saveStorage(storage);\n\n return { checked: storage.schedules.length, ran, errors };\n}\n\n/**\n * Start the scheduler daemon\n */\nexport function startScheduler(checkIntervalMs: number = 60000): void {\n if (schedulerInterval) {\n console.log('[whatsapp-scheduler] Scheduler already running');\n return;\n }\n\n console.log(\n `[whatsapp-scheduler] Starting scheduler (interval: ${checkIntervalMs}ms)`\n );\n\n // Run immediately\n checkAndRunDueSchedules().catch(console.error);\n\n // Then run on interval\n schedulerInterval = setInterval(() => {\n checkAndRunDueSchedules().catch(console.error);\n }, checkIntervalMs);\n}\n\n/**\n * Stop the scheduler daemon\n */\nexport function stopScheduler(): void {\n if (schedulerInterval) {\n clearInterval(schedulerInterval);\n schedulerInterval = null;\n console.log('[whatsapp-scheduler] Scheduler stopped');\n }\n}\n\n/**\n * Check if scheduler is running\n */\nexport function isSchedulerRunning(): boolean {\n return schedulerInterval !== null;\n}\n\n/**\n * Create a daily schedule at specified time\n */\nexport function scheduleDailyDigest(time: string): string {\n const parsed = parseTime(time);\n if (!parsed) {\n throw new Error(\n `Invalid time format: ${time}. Use HH:MM format (e.g., 09:00)`\n );\n }\n\n return scheduleDigest({\n type: 'daily',\n time,\n includeInactive: false,\n quietHoursRespect: true,\n });\n}\n\n/**\n * Create an hourly schedule\n */\nexport function scheduleHourlyDigest(): string {\n return scheduleDigest({\n type: 'hourly',\n includeInactive: false,\n quietHoursRespect: true,\n });\n}\n\n/**\n * Create an interval-based schedule\n */\nexport function scheduleIntervalDigest(intervalMinutes: number): string {\n if (intervalMinutes < 5 || intervalMinutes > 1440) {\n throw new Error('Interval must be between 5 and 1440 minutes');\n }\n\n return scheduleDigest({\n type: 'interval',\n intervalMinutes,\n includeInactive: false,\n quietHoursRespect: true,\n });\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|