mstro-app 0.4.52 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +129 -190
- package/PRIVACY.md +3 -3
- package/README.md +15 -6
- package/bin/commands/config.js +0 -1
- package/bin/mstro.js +1 -2
- package/bin/postinstall.js +0 -1
- package/dist/server/cli/headless/claude-invoker-process.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker-process.js +0 -1
- package/dist/server/cli/headless/claude-invoker-process.js.map +1 -1
- package/dist/server/cli/headless/claude-invoker-stall.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker-stall.js +7 -3
- package/dist/server/cli/headless/claude-invoker-stall.js.map +1 -1
- package/dist/server/cli/headless/claude-invoker-stream.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker-stream.js +0 -1
- package/dist/server/cli/headless/claude-invoker-stream.js.map +1 -1
- package/dist/server/cli/headless/claude-invoker-tools.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker-tools.js +0 -1
- package/dist/server/cli/headless/claude-invoker-tools.js.map +1 -1
- package/dist/server/cli/headless/claude-invoker.d.ts.map +1 -1
- package/dist/server/cli/headless/claude-invoker.js +1 -2
- package/dist/server/cli/headless/claude-invoker.js.map +1 -1
- package/dist/server/cli/headless/haiku-assessments.d.ts.map +1 -1
- package/dist/server/cli/headless/haiku-assessments.js +0 -1
- package/dist/server/cli/headless/haiku-assessments.js.map +1 -1
- package/dist/server/cli/headless/headless-logger.d.ts.map +1 -1
- package/dist/server/cli/headless/headless-logger.js +0 -1
- package/dist/server/cli/headless/headless-logger.js.map +1 -1
- package/dist/server/cli/headless/index.d.ts.map +1 -1
- package/dist/server/cli/headless/index.js +0 -1
- package/dist/server/cli/headless/index.js.map +1 -1
- package/dist/server/cli/headless/native-timeout-detector.d.ts.map +1 -1
- package/dist/server/cli/headless/native-timeout-detector.js +0 -1
- package/dist/server/cli/headless/native-timeout-detector.js.map +1 -1
- package/dist/server/cli/headless/output-utils.d.ts.map +1 -1
- package/dist/server/cli/headless/output-utils.js +0 -1
- package/dist/server/cli/headless/output-utils.js.map +1 -1
- package/dist/server/cli/headless/prompt-utils.d.ts.map +1 -1
- package/dist/server/cli/headless/prompt-utils.js +0 -1
- package/dist/server/cli/headless/prompt-utils.js.map +1 -1
- package/dist/server/cli/headless/resilient-runner.d.ts.map +1 -1
- package/dist/server/cli/headless/resilient-runner.js +0 -1
- package/dist/server/cli/headless/resilient-runner.js.map +1 -1
- package/dist/server/cli/headless/retry-strategies.d.ts.map +1 -1
- package/dist/server/cli/headless/retry-strategies.js +0 -1
- package/dist/server/cli/headless/retry-strategies.js.map +1 -1
- package/dist/server/cli/headless/runner.d.ts.map +1 -1
- package/dist/server/cli/headless/runner.js +63 -68
- package/dist/server/cli/headless/runner.js.map +1 -1
- package/dist/server/cli/headless/stall-assessor.d.ts.map +1 -1
- package/dist/server/cli/headless/stall-assessor.js +9 -5
- package/dist/server/cli/headless/stall-assessor.js.map +1 -1
- package/dist/server/cli/headless/tool-watchdog.d.ts.map +1 -1
- package/dist/server/cli/headless/tool-watchdog.js +0 -1
- package/dist/server/cli/headless/tool-watchdog.js.map +1 -1
- package/dist/server/cli/headless/types.d.ts.map +1 -1
- package/dist/server/cli/headless/types.js +0 -1
- package/dist/server/cli/headless/types.js.map +1 -1
- package/dist/server/cli/improvisation-attachments.d.ts.map +1 -1
- package/dist/server/cli/improvisation-attachments.js +0 -1
- package/dist/server/cli/improvisation-attachments.js.map +1 -1
- package/dist/server/cli/improvisation-history-store.d.ts +16 -0
- package/dist/server/cli/improvisation-history-store.d.ts.map +1 -0
- package/dist/server/cli/improvisation-history-store.js +51 -0
- package/dist/server/cli/improvisation-history-store.js.map +1 -0
- package/dist/server/cli/improvisation-movements.d.ts +31 -0
- package/dist/server/cli/improvisation-movements.d.ts.map +1 -0
- package/dist/server/cli/improvisation-movements.js +92 -0
- package/dist/server/cli/improvisation-movements.js.map +1 -0
- package/dist/server/cli/improvisation-output-queue.d.ts +13 -0
- package/dist/server/cli/improvisation-output-queue.d.ts.map +1 -0
- package/dist/server/cli/improvisation-output-queue.js +39 -0
- package/dist/server/cli/improvisation-output-queue.js.map +1 -0
- package/dist/server/cli/improvisation-retry.d.ts +21 -51
- package/dist/server/cli/improvisation-retry.d.ts.map +1 -1
- package/dist/server/cli/improvisation-retry.js +18 -434
- package/dist/server/cli/improvisation-retry.js.map +1 -1
- package/dist/server/cli/improvisation-session-manager.d.ts +10 -8
- package/dist/server/cli/improvisation-session-manager.d.ts.map +1 -1
- package/dist/server/cli/improvisation-session-manager.js +53 -149
- package/dist/server/cli/improvisation-session-manager.js.map +1 -1
- package/dist/server/cli/improvisation-types.d.ts.map +1 -1
- package/dist/server/cli/improvisation-types.js +0 -1
- package/dist/server/cli/improvisation-types.js.map +1 -1
- package/dist/server/cli/retry/retry-best-result.d.ts +4 -0
- package/dist/server/cli/retry/retry-best-result.d.ts.map +1 -0
- package/dist/server/cli/retry/retry-best-result.js +60 -0
- package/dist/server/cli/retry/retry-best-result.js.map +1 -0
- package/dist/server/cli/retry/retry-context-loss.d.ts +6 -0
- package/dist/server/cli/retry/retry-context-loss.d.ts.map +1 -0
- package/dist/server/cli/retry/retry-context-loss.js +67 -0
- package/dist/server/cli/retry/retry-context-loss.js.map +1 -0
- package/dist/server/cli/retry/retry-premature-completion.d.ts +5 -0
- package/dist/server/cli/retry/retry-premature-completion.d.ts.map +1 -0
- package/dist/server/cli/retry/retry-premature-completion.js +80 -0
- package/dist/server/cli/retry/retry-premature-completion.js.map +1 -0
- package/dist/server/cli/retry/retry-recovery-strategies.d.ts +13 -0
- package/dist/server/cli/retry/retry-recovery-strategies.d.ts.map +1 -0
- package/dist/server/cli/retry/retry-recovery-strategies.js +165 -0
- package/dist/server/cli/retry/retry-recovery-strategies.js.map +1 -0
- package/dist/server/cli/retry/retry-resume-strategy.d.ts +12 -0
- package/dist/server/cli/retry/retry-resume-strategy.d.ts.map +1 -0
- package/dist/server/cli/retry/retry-resume-strategy.js +21 -0
- package/dist/server/cli/retry/retry-resume-strategy.js.map +1 -0
- package/dist/server/cli/retry/retry-runner-factory.d.ts +11 -0
- package/dist/server/cli/retry/retry-runner-factory.d.ts.map +1 -0
- package/dist/server/cli/retry/retry-runner-factory.js +59 -0
- package/dist/server/cli/retry/retry-runner-factory.js.map +1 -0
- package/dist/server/cli/retry/retry-tool-results.d.ts +9 -0
- package/dist/server/cli/retry/retry-tool-results.d.ts.map +1 -0
- package/dist/server/cli/retry/retry-tool-results.js +23 -0
- package/dist/server/cli/retry/retry-tool-results.js.map +1 -0
- package/dist/server/cli/retry/retry-types.d.ts +30 -0
- package/dist/server/cli/retry/retry-types.d.ts.map +1 -0
- package/dist/server/cli/retry/retry-types.js +3 -0
- package/dist/server/cli/retry/retry-types.js.map +1 -0
- package/dist/server/index.js +21 -110
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/bouncer-cli.js +0 -1
- package/dist/server/mcp/bouncer-cli.js.map +1 -1
- package/dist/server/mcp/bouncer-haiku.d.ts.map +1 -1
- package/dist/server/mcp/bouncer-haiku.js +0 -1
- package/dist/server/mcp/bouncer-haiku.js.map +1 -1
- package/dist/server/mcp/bouncer-integration.d.ts.map +1 -1
- package/dist/server/mcp/bouncer-integration.js +0 -1
- package/dist/server/mcp/bouncer-integration.js.map +1 -1
- package/dist/server/mcp/security-analysis.d.ts.map +1 -1
- package/dist/server/mcp/security-analysis.js +0 -1
- package/dist/server/mcp/security-analysis.js.map +1 -1
- package/dist/server/mcp/security-audit.d.ts.map +1 -1
- package/dist/server/mcp/security-audit.js +0 -1
- package/dist/server/mcp/security-audit.js.map +1 -1
- package/dist/server/mcp/security-patterns.d.ts.map +1 -1
- package/dist/server/mcp/security-patterns.js +0 -1
- package/dist/server/mcp/security-patterns.js.map +1 -1
- package/dist/server/mcp/server.js +0 -1
- package/dist/server/mcp/server.js.map +1 -1
- package/dist/server/routes/files.d.ts.map +1 -1
- package/dist/server/routes/files.js +0 -1
- package/dist/server/routes/files.js.map +1 -1
- package/dist/server/routes/improvise.d.ts.map +1 -1
- package/dist/server/routes/improvise.js +0 -1
- package/dist/server/routes/improvise.js.map +1 -1
- package/dist/server/routes/index.d.ts.map +1 -1
- package/dist/server/routes/index.js +0 -1
- package/dist/server/routes/index.js.map +1 -1
- package/dist/server/routes/instances.d.ts.map +1 -1
- package/dist/server/routes/instances.js +0 -1
- package/dist/server/routes/instances.js.map +1 -1
- package/dist/server/routes/notifications.d.ts.map +1 -1
- package/dist/server/routes/notifications.js +0 -1
- package/dist/server/routes/notifications.js.map +1 -1
- package/dist/server/server-setup.d.ts +16 -1
- package/dist/server/server-setup.d.ts.map +1 -1
- package/dist/server/server-setup.js +107 -1
- package/dist/server/server-setup.js.map +1 -1
- package/dist/server/services/analytics.d.ts.map +1 -1
- package/dist/server/services/analytics.js +0 -1
- package/dist/server/services/analytics.js.map +1 -1
- package/dist/server/services/auth.d.ts.map +1 -1
- package/dist/server/services/auth.js +0 -1
- package/dist/server/services/auth.js.map +1 -1
- package/dist/server/services/client-id.d.ts.map +1 -1
- package/dist/server/services/client-id.js +0 -1
- package/dist/server/services/client-id.js.map +1 -1
- package/dist/server/services/file-explorer-ops.d.ts.map +1 -1
- package/dist/server/services/file-explorer-ops.js +0 -1
- package/dist/server/services/file-explorer-ops.js.map +1 -1
- package/dist/server/services/files.d.ts.map +1 -1
- package/dist/server/services/files.js +0 -1
- package/dist/server/services/files.js.map +1 -1
- package/dist/server/services/instances.d.ts.map +1 -1
- package/dist/server/services/instances.js +0 -1
- package/dist/server/services/instances.js.map +1 -1
- package/dist/server/services/pathUtils.d.ts.map +1 -1
- package/dist/server/services/pathUtils.js +0 -1
- package/dist/server/services/pathUtils.js.map +1 -1
- package/dist/server/services/plan/agent-loader.d.ts.map +1 -1
- package/dist/server/services/plan/agent-loader.js +0 -1
- package/dist/server/services/plan/agent-loader.js.map +1 -1
- package/dist/server/services/plan/board-config.d.ts +21 -0
- package/dist/server/services/plan/board-config.d.ts.map +1 -0
- package/dist/server/services/plan/board-config.js +111 -0
- package/dist/server/services/plan/board-config.js.map +1 -0
- package/dist/server/services/plan/composer.d.ts +1 -1
- package/dist/server/services/plan/composer.d.ts.map +1 -1
- package/dist/server/services/plan/composer.js +7 -6
- package/dist/server/services/plan/composer.js.map +1 -1
- package/dist/server/services/plan/config-installer.d.ts.map +1 -1
- package/dist/server/services/plan/config-installer.js +0 -1
- package/dist/server/services/plan/config-installer.js.map +1 -1
- package/dist/server/services/plan/dependency-resolver.d.ts.map +1 -1
- package/dist/server/services/plan/dependency-resolver.js +0 -1
- package/dist/server/services/plan/dependency-resolver.js.map +1 -1
- package/dist/server/services/plan/executor.d.ts +48 -48
- package/dist/server/services/plan/executor.d.ts.map +1 -1
- package/dist/server/services/plan/executor.js +202 -458
- package/dist/server/services/plan/executor.js.map +1 -1
- package/dist/server/services/plan/front-matter.d.ts.map +1 -1
- package/dist/server/services/plan/front-matter.js +0 -1
- package/dist/server/services/plan/front-matter.js.map +1 -1
- package/dist/server/services/plan/issue-classification.d.ts.map +1 -1
- package/dist/server/services/plan/issue-classification.js +0 -1
- package/dist/server/services/plan/issue-classification.js.map +1 -1
- package/dist/server/services/plan/issue-loader.d.ts +16 -0
- package/dist/server/services/plan/issue-loader.d.ts.map +1 -0
- package/dist/server/services/plan/issue-loader.js +45 -0
- package/dist/server/services/plan/issue-loader.js.map +1 -0
- package/dist/server/services/plan/issue-prompt-builder.d.ts.map +1 -1
- package/dist/server/services/plan/issue-prompt-builder.js +0 -1
- package/dist/server/services/plan/issue-prompt-builder.js.map +1 -1
- package/dist/server/services/plan/issue-retry.d.ts +3 -1
- package/dist/server/services/plan/issue-retry.d.ts.map +1 -1
- package/dist/server/services/plan/issue-retry.js +2 -1
- package/dist/server/services/plan/issue-retry.js.map +1 -1
- package/dist/server/services/plan/issue-writer.d.ts +34 -0
- package/dist/server/services/plan/issue-writer.d.ts.map +1 -0
- package/dist/server/services/plan/issue-writer.js +109 -0
- package/dist/server/services/plan/issue-writer.js.map +1 -0
- package/dist/server/services/plan/output-manager.js +2 -2
- package/dist/server/services/plan/output-manager.js.map +1 -1
- package/dist/server/services/plan/parser-core.d.ts.map +1 -1
- package/dist/server/services/plan/parser-core.js +0 -1
- package/dist/server/services/plan/parser-core.js.map +1 -1
- package/dist/server/services/plan/parser-migration.d.ts.map +1 -1
- package/dist/server/services/plan/parser-migration.js +0 -1
- package/dist/server/services/plan/parser-migration.js.map +1 -1
- package/dist/server/services/plan/parser.d.ts.map +1 -1
- package/dist/server/services/plan/parser.js +0 -1
- package/dist/server/services/plan/parser.js.map +1 -1
- package/dist/server/services/plan/progress-log.d.ts +11 -0
- package/dist/server/services/plan/progress-log.d.ts.map +1 -0
- package/dist/server/services/plan/progress-log.js +80 -0
- package/dist/server/services/plan/progress-log.js.map +1 -0
- package/dist/server/services/plan/prompt-builder.d.ts.map +1 -1
- package/dist/server/services/plan/prompt-builder.js +48 -32
- package/dist/server/services/plan/prompt-builder.js.map +1 -1
- package/dist/server/services/plan/readiness-planner.d.ts +15 -0
- package/dist/server/services/plan/readiness-planner.d.ts.map +1 -0
- package/dist/server/services/plan/readiness-planner.js +40 -0
- package/dist/server/services/plan/readiness-planner.js.map +1 -0
- package/dist/server/services/plan/review-gate.d.ts +31 -0
- package/dist/server/services/plan/review-gate.d.ts.map +1 -1
- package/dist/server/services/plan/review-gate.js +52 -3
- package/dist/server/services/plan/review-gate.js.map +1 -1
- package/dist/server/services/plan/state-reconciler.d.ts.map +1 -1
- package/dist/server/services/plan/state-reconciler.js +0 -1
- package/dist/server/services/plan/state-reconciler.js.map +1 -1
- package/dist/server/services/plan/types.d.ts.map +1 -1
- package/dist/server/services/plan/types.js +0 -1
- package/dist/server/services/plan/types.js.map +1 -1
- package/dist/server/services/plan/watcher.d.ts.map +1 -1
- package/dist/server/services/plan/watcher.js +0 -1
- package/dist/server/services/plan/watcher.js.map +1 -1
- package/dist/server/services/platform-credentials.d.ts.map +1 -1
- package/dist/server/services/platform-credentials.js +0 -1
- package/dist/server/services/platform-credentials.js.map +1 -1
- package/dist/server/services/platform-token-lifecycle.d.ts +70 -0
- package/dist/server/services/platform-token-lifecycle.d.ts.map +1 -0
- package/dist/server/services/platform-token-lifecycle.js +156 -0
- package/dist/server/services/platform-token-lifecycle.js.map +1 -0
- package/dist/server/services/platform.d.ts +25 -4
- package/dist/server/services/platform.d.ts.map +1 -1
- package/dist/server/services/platform.js +150 -92
- package/dist/server/services/platform.js.map +1 -1
- package/dist/server/services/sentry.d.ts.map +1 -1
- package/dist/server/services/sentry.js +0 -1
- package/dist/server/services/sentry.js.map +1 -1
- package/dist/server/services/settings.d.ts.map +1 -1
- package/dist/server/services/settings.js +0 -1
- package/dist/server/services/settings.js.map +1 -1
- package/dist/server/services/terminal/pty-manager.d.ts.map +1 -1
- package/dist/server/services/terminal/pty-manager.js +0 -1
- package/dist/server/services/terminal/pty-manager.js.map +1 -1
- package/dist/server/services/terminal/pty-utils.d.ts.map +1 -1
- package/dist/server/services/terminal/pty-utils.js +0 -1
- package/dist/server/services/terminal/pty-utils.js.map +1 -1
- package/dist/server/services/websocket/autocomplete.d.ts.map +1 -1
- package/dist/server/services/websocket/autocomplete.js +0 -1
- package/dist/server/services/websocket/autocomplete.js.map +1 -1
- package/dist/server/services/websocket/file-definition-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/file-definition-handlers.js +0 -1
- package/dist/server/services/websocket/file-definition-handlers.js.map +1 -1
- package/dist/server/services/websocket/file-download-handler.d.ts +17 -0
- package/dist/server/services/websocket/file-download-handler.d.ts.map +1 -0
- package/dist/server/services/websocket/file-download-handler.js +164 -0
- package/dist/server/services/websocket/file-download-handler.js.map +1 -0
- package/dist/server/services/websocket/file-explorer-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/file-explorer-handlers.js +0 -1
- package/dist/server/services/websocket/file-explorer-handlers.js.map +1 -1
- package/dist/server/services/websocket/file-search-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/file-search-handlers.js +0 -1
- package/dist/server/services/websocket/file-search-handlers.js.map +1 -1
- package/dist/server/services/websocket/file-upload-handler.d.ts +2 -3
- package/dist/server/services/websocket/file-upload-handler.d.ts.map +1 -1
- package/dist/server/services/websocket/file-upload-handler.js +4 -7
- package/dist/server/services/websocket/file-upload-handler.js.map +1 -1
- package/dist/server/services/websocket/file-utils.d.ts.map +1 -1
- package/dist/server/services/websocket/file-utils.js +0 -1
- package/dist/server/services/websocket/file-utils.js.map +1 -1
- package/dist/server/services/websocket/git-branch-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-branch-handlers.js +0 -1
- package/dist/server/services/websocket/git-branch-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-diff-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-diff-handlers.js +0 -1
- package/dist/server/services/websocket/git-diff-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-handlers.js +58 -6
- package/dist/server/services/websocket/git-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-head-watcher.d.ts.map +1 -1
- package/dist/server/services/websocket/git-head-watcher.js +0 -1
- package/dist/server/services/websocket/git-head-watcher.js.map +1 -1
- package/dist/server/services/websocket/git-log-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-log-handlers.js +0 -1
- package/dist/server/services/websocket/git-log-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-pr-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-pr-handlers.js +0 -1
- package/dist/server/services/websocket/git-pr-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-tag-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-tag-handlers.js +0 -1
- package/dist/server/services/websocket/git-tag-handlers.js.map +1 -1
- package/dist/server/services/websocket/git-utils.d.ts +18 -3
- package/dist/server/services/websocket/git-utils.d.ts.map +1 -1
- package/dist/server/services/websocket/git-utils.js +58 -8
- package/dist/server/services/websocket/git-utils.js.map +1 -1
- package/dist/server/services/websocket/git-worktree-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/git-worktree-handlers.js +258 -16
- package/dist/server/services/websocket/git-worktree-handlers.js.map +1 -1
- package/dist/server/services/websocket/handler-context.d.ts +15 -0
- package/dist/server/services/websocket/handler-context.d.ts.map +1 -1
- package/dist/server/services/websocket/handler-context.js +0 -1
- package/dist/server/services/websocket/handler-context.js.map +1 -1
- package/dist/server/services/websocket/handler.d.ts +7 -0
- package/dist/server/services/websocket/handler.d.ts.map +1 -1
- package/dist/server/services/websocket/handler.js +76 -15
- package/dist/server/services/websocket/handler.js.map +1 -1
- package/dist/server/services/websocket/index.d.ts.map +1 -1
- package/dist/server/services/websocket/index.js +0 -1
- package/dist/server/services/websocket/index.js.map +1 -1
- package/dist/server/services/websocket/msg-id-tracker.d.ts +21 -0
- package/dist/server/services/websocket/msg-id-tracker.d.ts.map +1 -0
- package/dist/server/services/websocket/msg-id-tracker.js +76 -0
- package/dist/server/services/websocket/msg-id-tracker.js.map +1 -0
- package/dist/server/services/websocket/plan-board-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-board-handlers.js +0 -1
- package/dist/server/services/websocket/plan-board-handlers.js.map +1 -1
- package/dist/server/services/websocket/plan-execution-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-execution-handlers.js +6 -2
- package/dist/server/services/websocket/plan-execution-handlers.js.map +1 -1
- package/dist/server/services/websocket/plan-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-handlers.js +0 -1
- package/dist/server/services/websocket/plan-handlers.js.map +1 -1
- package/dist/server/services/websocket/plan-helpers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-helpers.js +0 -1
- package/dist/server/services/websocket/plan-helpers.js.map +1 -1
- package/dist/server/services/websocket/plan-issue-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-issue-handlers.js +0 -1
- package/dist/server/services/websocket/plan-issue-handlers.js.map +1 -1
- package/dist/server/services/websocket/plan-sprint-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/plan-sprint-handlers.js +0 -1
- package/dist/server/services/websocket/plan-sprint-handlers.js.map +1 -1
- package/dist/server/services/websocket/quality-complexity.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-complexity.js +0 -1
- package/dist/server/services/websocket/quality-complexity.js.map +1 -1
- package/dist/server/services/websocket/quality-grading.d.ts +46 -0
- package/dist/server/services/websocket/quality-grading.d.ts.map +1 -0
- package/dist/server/services/websocket/quality-grading.js +482 -0
- package/dist/server/services/websocket/quality-grading.js.map +1 -0
- package/dist/server/services/websocket/quality-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-handlers.js +15 -4
- package/dist/server/services/websocket/quality-handlers.js.map +1 -1
- package/dist/server/services/websocket/quality-linting.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-linting.js +0 -1
- package/dist/server/services/websocket/quality-linting.js.map +1 -1
- package/dist/server/services/websocket/quality-persistence.d.ts +14 -0
- package/dist/server/services/websocket/quality-persistence.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-persistence.js +28 -12
- package/dist/server/services/websocket/quality-persistence.js.map +1 -1
- package/dist/server/services/websocket/quality-review-agent.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-review-agent.js +2 -3
- package/dist/server/services/websocket/quality-review-agent.js.map +1 -1
- package/dist/server/services/websocket/quality-service.d.ts +3 -1
- package/dist/server/services/websocket/quality-service.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-service.js +53 -58
- package/dist/server/services/websocket/quality-service.js.map +1 -1
- package/dist/server/services/websocket/quality-tools.d.ts +1 -1
- package/dist/server/services/websocket/quality-tools.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-tools.js +6 -3
- package/dist/server/services/websocket/quality-tools.js.map +1 -1
- package/dist/server/services/websocket/quality-types.d.ts +18 -2
- package/dist/server/services/websocket/quality-types.d.ts.map +1 -1
- package/dist/server/services/websocket/quality-types.js +0 -1
- package/dist/server/services/websocket/quality-types.js.map +1 -1
- package/dist/server/services/websocket/session-handlers.d.ts +48 -2
- package/dist/server/services/websocket/session-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/session-handlers.js +204 -66
- package/dist/server/services/websocket/session-handlers.js.map +1 -1
- package/dist/server/services/websocket/session-history.d.ts.map +1 -1
- package/dist/server/services/websocket/session-history.js +0 -1
- package/dist/server/services/websocket/session-history.js.map +1 -1
- package/dist/server/services/websocket/session-initialization.d.ts +2 -2
- package/dist/server/services/websocket/session-initialization.d.ts.map +1 -1
- package/dist/server/services/websocket/session-initialization.js +75 -18
- package/dist/server/services/websocket/session-initialization.js.map +1 -1
- package/dist/server/services/websocket/session-registry.d.ts +29 -1
- package/dist/server/services/websocket/session-registry.d.ts.map +1 -1
- package/dist/server/services/websocket/session-registry.js +53 -5
- package/dist/server/services/websocket/session-registry.js.map +1 -1
- package/dist/server/services/websocket/settings-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/settings-handlers.js +0 -1
- package/dist/server/services/websocket/settings-handlers.js.map +1 -1
- package/dist/server/services/websocket/skill-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/skill-handlers.js +0 -1
- package/dist/server/services/websocket/skill-handlers.js.map +1 -1
- package/dist/server/services/websocket/skill-watcher.d.ts.map +1 -1
- package/dist/server/services/websocket/skill-watcher.js +0 -1
- package/dist/server/services/websocket/skill-watcher.js.map +1 -1
- package/dist/server/services/websocket/tab-broadcast.d.ts +24 -0
- package/dist/server/services/websocket/tab-broadcast.d.ts.map +1 -0
- package/dist/server/services/websocket/tab-broadcast.js +12 -0
- package/dist/server/services/websocket/tab-broadcast.js.map +1 -0
- package/dist/server/services/websocket/tab-event-buffer.d.ts +103 -0
- package/dist/server/services/websocket/tab-event-buffer.d.ts.map +1 -0
- package/dist/server/services/websocket/tab-event-buffer.js +106 -0
- package/dist/server/services/websocket/tab-event-buffer.js.map +1 -0
- package/dist/server/services/websocket/tab-event-replay.d.ts +20 -0
- package/dist/server/services/websocket/tab-event-replay.d.ts.map +1 -0
- package/dist/server/services/websocket/tab-event-replay.js +20 -0
- package/dist/server/services/websocket/tab-event-replay.js.map +1 -0
- package/dist/server/services/websocket/tab-handlers.d.ts +0 -1
- package/dist/server/services/websocket/tab-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/tab-handlers.js +2 -10
- package/dist/server/services/websocket/tab-handlers.js.map +1 -1
- package/dist/server/services/websocket/terminal-handlers.d.ts.map +1 -1
- package/dist/server/services/websocket/terminal-handlers.js +39 -4
- package/dist/server/services/websocket/terminal-handlers.js.map +1 -1
- package/dist/server/services/websocket/types.d.ts +17 -8
- package/dist/server/services/websocket/types.d.ts.map +1 -1
- package/dist/server/services/websocket/types.js +8 -7
- package/dist/server/services/websocket/types.js.map +1 -1
- package/dist/server/utils/agent-manager.d.ts.map +1 -1
- package/dist/server/utils/agent-manager.js +0 -1
- package/dist/server/utils/agent-manager.js.map +1 -1
- package/dist/server/utils/paths.d.ts.map +1 -1
- package/dist/server/utils/paths.js +0 -1
- package/dist/server/utils/paths.js.map +1 -1
- package/dist/server/utils/port-manager.d.ts.map +1 -1
- package/dist/server/utils/port-manager.js +0 -1
- package/dist/server/utils/port-manager.js.map +1 -1
- package/dist/server/utils/port.d.ts.map +1 -1
- package/dist/server/utils/port.js +0 -1
- package/dist/server/utils/port.js.map +1 -1
- package/package.json +2 -2
- package/server/README.md +1 -1
- package/server/cli/headless/claude-invoker-process.ts +0 -1
- package/server/cli/headless/claude-invoker-stall.ts +7 -3
- package/server/cli/headless/claude-invoker-stream.ts +0 -1
- package/server/cli/headless/claude-invoker-tools.ts +0 -1
- package/server/cli/headless/claude-invoker.ts +1 -2
- package/server/cli/headless/haiku-assessments.ts +0 -1
- package/server/cli/headless/headless-logger.ts +0 -1
- package/server/cli/headless/index.ts +0 -1
- package/server/cli/headless/native-timeout-detector.ts +0 -1
- package/server/cli/headless/output-utils.ts +0 -1
- package/server/cli/headless/prompt-utils.ts +0 -1
- package/server/cli/headless/resilient-runner.ts +0 -1
- package/server/cli/headless/retry-strategies.ts +0 -1
- package/server/cli/headless/runner.ts +67 -73
- package/server/cli/headless/stall-assessor.ts +9 -5
- package/server/cli/headless/tool-watchdog.ts +0 -1
- package/server/cli/headless/types.ts +1 -2
- package/server/cli/improvisation-attachments.ts +0 -1
- package/server/cli/improvisation-history-store.ts +61 -0
- package/server/cli/improvisation-movements.ts +119 -0
- package/server/cli/improvisation-output-queue.ts +41 -0
- package/server/cli/improvisation-retry.ts +25 -601
- package/server/cli/improvisation-session-manager.ts +74 -161
- package/server/cli/improvisation-types.ts +0 -1
- package/server/cli/retry/retry-best-result.ts +69 -0
- package/server/cli/retry/retry-context-loss.ts +86 -0
- package/server/cli/retry/retry-premature-completion.ts +112 -0
- package/server/cli/retry/retry-recovery-strategies.ts +246 -0
- package/server/cli/retry/retry-resume-strategy.ts +32 -0
- package/server/cli/retry/retry-runner-factory.ts +69 -0
- package/server/cli/retry/retry-tool-results.ts +30 -0
- package/server/cli/retry/retry-types.ts +31 -0
- package/server/index.ts +37 -124
- package/server/mcp/bouncer-cli.ts +0 -1
- package/server/mcp/bouncer-haiku.ts +0 -1
- package/server/mcp/bouncer-integration.ts +0 -1
- package/server/mcp/security-analysis.ts +0 -1
- package/server/mcp/security-audit.ts +0 -1
- package/server/mcp/security-patterns.ts +0 -1
- package/server/mcp/server.ts +0 -1
- package/server/routes/files.ts +0 -1
- package/server/routes/improvise.ts +0 -1
- package/server/routes/index.ts +0 -1
- package/server/routes/instances.ts +0 -1
- package/server/routes/notifications.ts +0 -1
- package/server/server-setup.ts +126 -2
- package/server/services/analytics.ts +0 -1
- package/server/services/auth.ts +0 -1
- package/server/services/client-id.ts +0 -1
- package/server/services/file-explorer-ops.ts +0 -1
- package/server/services/files.ts +0 -1
- package/server/services/instances.ts +0 -1
- package/server/services/pathUtils.ts +0 -1
- package/server/services/plan/agent-loader.ts +0 -1
- package/server/services/plan/agents/assess-stall.md +11 -4
- package/server/services/plan/agents/code-review.md +13 -11
- package/server/services/plan/board-config.ts +121 -0
- package/server/services/plan/composer.ts +7 -6
- package/server/services/plan/config-installer.ts +0 -1
- package/server/services/plan/dependency-resolver.ts +0 -1
- package/server/services/plan/executor.ts +259 -470
- package/server/services/plan/front-matter.ts +0 -1
- package/server/services/plan/issue-classification.ts +0 -1
- package/server/services/plan/issue-loader.ts +63 -0
- package/server/services/plan/issue-prompt-builder.ts +0 -1
- package/server/services/plan/issue-retry.ts +5 -2
- package/server/services/plan/issue-writer.ts +136 -0
- package/server/services/plan/output-manager.ts +2 -2
- package/server/services/plan/parser-core.ts +0 -1
- package/server/services/plan/parser-migration.ts +0 -1
- package/server/services/plan/parser.ts +0 -1
- package/server/services/plan/progress-log.ts +91 -0
- package/server/services/plan/prompt-builder.ts +73 -36
- package/server/services/plan/readiness-planner.ts +49 -0
- package/server/services/plan/review-gate.ts +102 -3
- package/server/services/plan/state-reconciler.ts +0 -1
- package/server/services/plan/types.ts +0 -1
- package/server/services/plan/watcher.ts +0 -1
- package/server/services/platform-credentials.ts +0 -1
- package/server/services/platform-token-lifecycle.ts +171 -0
- package/server/services/platform.ts +168 -105
- package/server/services/sentry.ts +0 -1
- package/server/services/settings.ts +0 -1
- package/server/services/terminal/pty-manager.ts +0 -1
- package/server/services/terminal/pty-utils.ts +0 -1
- package/server/services/websocket/autocomplete.ts +0 -1
- package/server/services/websocket/file-definition-handlers.ts +0 -1
- package/server/services/websocket/file-download-handler.ts +190 -0
- package/server/services/websocket/file-explorer-handlers.ts +0 -1
- package/server/services/websocket/file-search-handlers.ts +0 -1
- package/server/services/websocket/file-upload-handler.ts +6 -5
- package/server/services/websocket/file-utils.ts +0 -1
- package/server/services/websocket/git-branch-handlers.ts +0 -1
- package/server/services/websocket/git-diff-handlers.ts +0 -1
- package/server/services/websocket/git-handlers.ts +66 -10
- package/server/services/websocket/git-head-watcher.ts +0 -1
- package/server/services/websocket/git-log-handlers.ts +0 -1
- package/server/services/websocket/git-pr-handlers.ts +0 -1
- package/server/services/websocket/git-tag-handlers.ts +0 -1
- package/server/services/websocket/git-utils.ts +69 -9
- package/server/services/websocket/git-worktree-handlers.ts +289 -19
- package/server/services/websocket/handler-context.ts +15 -1
- package/server/services/websocket/handler.ts +79 -16
- package/server/services/websocket/index.ts +0 -1
- package/server/services/websocket/msg-id-tracker.ts +83 -0
- package/server/services/websocket/plan-board-handlers.ts +0 -1
- package/server/services/websocket/plan-execution-handlers.ts +6 -2
- package/server/services/websocket/plan-handlers.ts +0 -1
- package/server/services/websocket/plan-helpers.ts +0 -1
- package/server/services/websocket/plan-issue-handlers.ts +0 -1
- package/server/services/websocket/plan-sprint-handlers.ts +0 -1
- package/server/services/websocket/quality-complexity.ts +0 -1
- package/server/services/websocket/quality-grading.ts +611 -0
- package/server/services/websocket/quality-handlers.ts +16 -4
- package/server/services/websocket/quality-linting.ts +0 -1
- package/server/services/websocket/quality-persistence.ts +30 -8
- package/server/services/websocket/quality-review-agent.ts +2 -3
- package/server/services/websocket/quality-service.ts +54 -55
- package/server/services/websocket/quality-tools.ts +11 -3
- package/server/services/websocket/quality-types.ts +21 -3
- package/server/services/websocket/session-handlers.ts +213 -69
- package/server/services/websocket/session-history.ts +0 -1
- package/server/services/websocket/session-initialization.ts +83 -20
- package/server/services/websocket/session-registry.ts +61 -5
- package/server/services/websocket/settings-handlers.ts +0 -1
- package/server/services/websocket/skill-handlers.ts +0 -1
- package/server/services/websocket/skill-watcher.ts +0 -1
- package/server/services/websocket/tab-broadcast.ts +37 -0
- package/server/services/websocket/tab-event-buffer.ts +158 -0
- package/server/services/websocket/tab-event-replay.ts +41 -0
- package/server/services/websocket/tab-handlers.ts +2 -10
- package/server/services/websocket/terminal-handlers.ts +39 -3
- package/server/services/websocket/types.ts +19 -7
- package/server/utils/agent-manager.ts +0 -1
- package/server/utils/paths.ts +0 -1
- package/server/utils/port-manager.ts +0 -1
- package/server/utils/port.ts +0 -1
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
+
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Multi-Dimensional Quality Grading
|
|
5
|
+
// ============================================================================
|
|
6
|
+
//
|
|
7
|
+
// Pure logic for the three-dimension grading model:
|
|
8
|
+
// - Security — severity-threshold (worst severity issue determines grade)
|
|
9
|
+
// - Reliability — severity-threshold, slightly more lenient than Security
|
|
10
|
+
// - Maintainability — density-based (issues / KLOC) with a severity escape hatch
|
|
11
|
+
//
|
|
12
|
+
// Rationale (why these specific bands):
|
|
13
|
+
// The previous single-score exponential-decay model conflated security holes
|
|
14
|
+
// with prettier complaints. Industry tools (SonarQube, Code Climate, CodeScene)
|
|
15
|
+
// all separate severity-driven dimensions from aggregate metrics so that a
|
|
16
|
+
// critical issue can never be "averaged away" by a clean lint score.
|
|
17
|
+
//
|
|
18
|
+
// All functions in this module are pure: same inputs -> same outputs, no I/O.
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Types
|
|
23
|
+
// ============================================================================
|
|
24
|
+
|
|
25
|
+
export type DimensionName = 'security' | 'reliability' | 'maintainability';
|
|
26
|
+
export type Grade = 'A' | 'B' | 'C' | 'D' | 'F' | 'N/A';
|
|
27
|
+
type Severity = 'critical' | 'high' | 'medium' | 'low';
|
|
28
|
+
|
|
29
|
+
export interface DimensionScore {
|
|
30
|
+
name: DimensionName;
|
|
31
|
+
score: number;
|
|
32
|
+
grade: Grade;
|
|
33
|
+
rationale: string;
|
|
34
|
+
available: boolean;
|
|
35
|
+
findingCount: number;
|
|
36
|
+
worstSeverity: Severity | null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface QualityGate {
|
|
40
|
+
passed: boolean;
|
|
41
|
+
failingConditions: string[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface QualityRating {
|
|
45
|
+
overall: { score: number; grade: Grade };
|
|
46
|
+
dimensions: DimensionScore[];
|
|
47
|
+
qualityGate: QualityGate;
|
|
48
|
+
gradeRationale: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// Category -> Dimension Mapping
|
|
53
|
+
// ============================================================================
|
|
54
|
+
|
|
55
|
+
const SECURITY_CATEGORIES = new Set<string>(['security']);
|
|
56
|
+
const RELIABILITY_CATEGORIES = new Set<string>(['bugs', 'logic', 'performance', 'complexity']);
|
|
57
|
+
const MAINTAINABILITY_CATEGORIES = new Set<string>([
|
|
58
|
+
'lint',
|
|
59
|
+
'linting',
|
|
60
|
+
'format',
|
|
61
|
+
'file-length',
|
|
62
|
+
'function-length',
|
|
63
|
+
'architecture',
|
|
64
|
+
'oop',
|
|
65
|
+
'maintainability',
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Map a finding category to one of the three quality dimensions.
|
|
70
|
+
* Unknown categories default to maintainability (the catch-all bucket) so
|
|
71
|
+
* that surprise categories never silently disappear from the grade.
|
|
72
|
+
*/
|
|
73
|
+
export function categoryToDimension(category: string): DimensionName {
|
|
74
|
+
if (SECURITY_CATEGORIES.has(category)) return 'security';
|
|
75
|
+
if (RELIABILITY_CATEGORIES.has(category)) return 'reliability';
|
|
76
|
+
if (MAINTAINABILITY_CATEGORIES.has(category)) return 'maintainability';
|
|
77
|
+
return 'maintainability';
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ============================================================================
|
|
81
|
+
// Legacy Fallback
|
|
82
|
+
// ============================================================================
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Score-to-grade conversion used by legacy callers that still operate on a
|
|
86
|
+
* single 0-100 number. The new multi-dimensional path computes grades
|
|
87
|
+
* directly from finding shape; this remains for backward compatibility.
|
|
88
|
+
*/
|
|
89
|
+
export function gradeFromScore(score: number): Grade {
|
|
90
|
+
if (score >= 90) return 'A';
|
|
91
|
+
if (score >= 80) return 'B';
|
|
92
|
+
if (score >= 70) return 'C';
|
|
93
|
+
if (score >= 60) return 'D';
|
|
94
|
+
return 'F';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ============================================================================
|
|
98
|
+
// Score Bands
|
|
99
|
+
// ============================================================================
|
|
100
|
+
|
|
101
|
+
const BAND_TOP: Record<Exclude<Grade, 'N/A'>, number> = {
|
|
102
|
+
A: 100,
|
|
103
|
+
B: 89,
|
|
104
|
+
C: 79,
|
|
105
|
+
D: 69,
|
|
106
|
+
F: 59,
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const BAND_BOTTOM: Record<Exclude<Grade, 'N/A'>, number> = {
|
|
110
|
+
A: 90,
|
|
111
|
+
B: 80,
|
|
112
|
+
C: 70,
|
|
113
|
+
D: 60,
|
|
114
|
+
F: 0,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Linearly interpolate a score within a grade's band.
|
|
119
|
+
*
|
|
120
|
+
* `position` is in [0, 1]: 0 means "as bad as this grade gets" (band bottom),
|
|
121
|
+
* 1 means "as good as this grade gets" (band top, just below the next grade).
|
|
122
|
+
*/
|
|
123
|
+
function scoreInBand(grade: Exclude<Grade, 'N/A'>, position: number): number {
|
|
124
|
+
const clamped = Math.max(0, Math.min(1, position));
|
|
125
|
+
const bottom = BAND_BOTTOM[grade];
|
|
126
|
+
const top = BAND_TOP[grade];
|
|
127
|
+
return Math.round(bottom + (top - bottom) * clamped);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ============================================================================
|
|
131
|
+
// Severity Helpers
|
|
132
|
+
// ============================================================================
|
|
133
|
+
|
|
134
|
+
function isSeverity(s: string): s is Severity {
|
|
135
|
+
return s === 'critical' || s === 'high' || s === 'medium' || s === 'low';
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
interface SeverityCounts {
|
|
139
|
+
critical: number;
|
|
140
|
+
high: number;
|
|
141
|
+
medium: number;
|
|
142
|
+
low: number;
|
|
143
|
+
total: number;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function countSeverities(findings: Array<{ severity: string }>): SeverityCounts {
|
|
147
|
+
const counts: SeverityCounts = { critical: 0, high: 0, medium: 0, low: 0, total: 0 };
|
|
148
|
+
for (const f of findings) {
|
|
149
|
+
if (!isSeverity(f.severity)) continue;
|
|
150
|
+
counts[f.severity]++;
|
|
151
|
+
counts.total++;
|
|
152
|
+
}
|
|
153
|
+
return counts;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function worstSeverity(counts: SeverityCounts): Severity | null {
|
|
157
|
+
if (counts.critical > 0) return 'critical';
|
|
158
|
+
if (counts.high > 0) return 'high';
|
|
159
|
+
if (counts.medium > 0) return 'medium';
|
|
160
|
+
if (counts.low > 0) return 'low';
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// ============================================================================
|
|
165
|
+
// Security Dimension
|
|
166
|
+
// ============================================================================
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Security grading — strictest of the three dimensions. Any medium-or-worse
|
|
170
|
+
* security finding immediately drops the grade below B because security
|
|
171
|
+
* issues can't be amortized over codebase size.
|
|
172
|
+
*
|
|
173
|
+
* Within-band score: more findings at the threshold severity -> lower score.
|
|
174
|
+
* The interpolation favors "fewer issues is meaningfully better" so 1 medium
|
|
175
|
+
* scores higher than 5 mediums even though both are grade C.
|
|
176
|
+
*/
|
|
177
|
+
function gradeSecurity(findings: Array<{ severity: string }>): DimensionScore {
|
|
178
|
+
const counts = countSeverities(findings);
|
|
179
|
+
const worst = worstSeverity(counts);
|
|
180
|
+
|
|
181
|
+
if (counts.total === 0) {
|
|
182
|
+
return {
|
|
183
|
+
name: 'security',
|
|
184
|
+
score: 100,
|
|
185
|
+
grade: 'A',
|
|
186
|
+
rationale: '0 security findings',
|
|
187
|
+
available: true,
|
|
188
|
+
findingCount: 0,
|
|
189
|
+
worstSeverity: null,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
let grade: Exclude<Grade, 'N/A'>;
|
|
194
|
+
let position: number;
|
|
195
|
+
let rationale: string;
|
|
196
|
+
|
|
197
|
+
if (counts.critical > 0) {
|
|
198
|
+
grade = 'F';
|
|
199
|
+
// F band: fewer criticals -> higher within-band, but still F.
|
|
200
|
+
position = 1 / (1 + counts.critical);
|
|
201
|
+
rationale = `${counts.critical} critical-severity security ${pluralize('issue', counts.critical)}`;
|
|
202
|
+
} else if (counts.high > 0) {
|
|
203
|
+
grade = 'D';
|
|
204
|
+
position = 1 / (1 + counts.high);
|
|
205
|
+
rationale = `${counts.high} high-severity security ${pluralize('issue', counts.high)}`;
|
|
206
|
+
} else if (counts.medium > 0) {
|
|
207
|
+
grade = 'C';
|
|
208
|
+
position = 1 / (1 + counts.medium);
|
|
209
|
+
rationale = `${counts.medium} medium-severity security ${pluralize('issue', counts.medium)}`;
|
|
210
|
+
} else {
|
|
211
|
+
// Only low-severity findings.
|
|
212
|
+
grade = 'B';
|
|
213
|
+
// 1 low -> top of B (89); more lows -> down toward 80.
|
|
214
|
+
position = 1 / Math.max(1, counts.low);
|
|
215
|
+
rationale = `${counts.low} low-severity security ${pluralize('issue', counts.low)}`;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
name: 'security',
|
|
220
|
+
score: scoreInBand(grade, position),
|
|
221
|
+
grade,
|
|
222
|
+
rationale,
|
|
223
|
+
available: true,
|
|
224
|
+
findingCount: counts.total,
|
|
225
|
+
worstSeverity: worst,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ============================================================================
|
|
230
|
+
// Reliability Dimension
|
|
231
|
+
// ============================================================================
|
|
232
|
+
|
|
233
|
+
interface BandResult {
|
|
234
|
+
grade: Exclude<Grade, 'N/A'>;
|
|
235
|
+
position: number;
|
|
236
|
+
rationale: string;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function reliabilityBandClean(counts: SeverityCounts): BandResult {
|
|
240
|
+
const position = counts.total === 0 ? 1 : 0.5;
|
|
241
|
+
const rationale = counts.total === 0 ? '0 reliability findings' : '1 low-severity reliability issue';
|
|
242
|
+
return { grade: 'A', position, rationale };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function reliabilityBandSevere(counts: SeverityCounts): BandResult | null {
|
|
246
|
+
if (counts.critical > 0) {
|
|
247
|
+
return {
|
|
248
|
+
grade: 'F',
|
|
249
|
+
position: 1 / (1 + counts.critical),
|
|
250
|
+
rationale: `${counts.critical} critical-severity ${pluralize('bug', counts.critical)}`,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
if (counts.high >= 2) {
|
|
254
|
+
return {
|
|
255
|
+
grade: 'D',
|
|
256
|
+
position: 1 / (1 + (counts.high - 1)),
|
|
257
|
+
rationale: `${counts.high} high-severity ${pluralize('bug', counts.high)}`,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function reliabilityBandMid(counts: SeverityCounts): BandResult {
|
|
264
|
+
if (counts.high >= 1) {
|
|
265
|
+
return {
|
|
266
|
+
grade: 'C',
|
|
267
|
+
position: 1 / (1 + counts.high),
|
|
268
|
+
rationale: `${counts.high} high-severity ${pluralize('bug', counts.high)}`,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
if (counts.medium >= 3) {
|
|
272
|
+
return {
|
|
273
|
+
grade: 'C',
|
|
274
|
+
position: 1 / Math.max(1, counts.medium - 2),
|
|
275
|
+
rationale: `${counts.medium} medium-severity reliability ${pluralize('issue', counts.medium)}`,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
if (counts.medium >= 1) {
|
|
279
|
+
return {
|
|
280
|
+
grade: 'B',
|
|
281
|
+
position: 1 / Math.max(1, counts.medium),
|
|
282
|
+
rationale: `${counts.medium} medium-severity reliability ${pluralize('issue', counts.medium)}`,
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
// Only low-severity findings, > 1 of them.
|
|
286
|
+
return {
|
|
287
|
+
grade: 'B',
|
|
288
|
+
position: 1 / Math.max(1, counts.low - 1),
|
|
289
|
+
rationale: `${counts.low} low-severity reliability ${pluralize('issue', counts.low)}`,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Reliability grading — slightly more lenient than Security because not every
|
|
295
|
+
* complexity warning is a runtime defect. A single low-severity logic issue
|
|
296
|
+
* still earns an A; medium issues escalate gradually.
|
|
297
|
+
*/
|
|
298
|
+
function gradeReliability(findings: Array<{ severity: string }>): DimensionScore {
|
|
299
|
+
const counts = countSeverities(findings);
|
|
300
|
+
const worst = worstSeverity(counts);
|
|
301
|
+
const isClean = counts.total === 0 || (counts.low <= 1 && counts.medium === 0 && counts.high === 0 && counts.critical === 0);
|
|
302
|
+
const band = isClean
|
|
303
|
+
? reliabilityBandClean(counts)
|
|
304
|
+
: reliabilityBandSevere(counts) ?? reliabilityBandMid(counts);
|
|
305
|
+
|
|
306
|
+
return {
|
|
307
|
+
name: 'reliability',
|
|
308
|
+
score: scoreInBand(band.grade, band.position),
|
|
309
|
+
grade: band.grade,
|
|
310
|
+
rationale: band.rationale,
|
|
311
|
+
available: true,
|
|
312
|
+
findingCount: counts.total,
|
|
313
|
+
worstSeverity: worst,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// ============================================================================
|
|
318
|
+
// Maintainability Dimension
|
|
319
|
+
// ============================================================================
|
|
320
|
+
|
|
321
|
+
interface MaintainabilityBand {
|
|
322
|
+
grade: Exclude<Grade, 'N/A'>;
|
|
323
|
+
position: number;
|
|
324
|
+
label: string;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function maintainabilityByCount(n: number): MaintainabilityBand {
|
|
328
|
+
const label = `${n} maintainability ${pluralize('issue', n)}`;
|
|
329
|
+
if (n <= 5) return { grade: 'A', position: 1 - n / 5, label };
|
|
330
|
+
if (n <= 15) return { grade: 'B', position: 1 - (n - 5) / 10, label };
|
|
331
|
+
if (n <= 30) return { grade: 'C', position: 1 - (n - 15) / 15, label };
|
|
332
|
+
if (n <= 60) return { grade: 'D', position: 1 - (n - 30) / 30, label };
|
|
333
|
+
return { grade: 'F', position: 1 / (1 + (n - 60) / 30), label };
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function maintainabilityByDensity(n: number, kloc: number): MaintainabilityBand {
|
|
337
|
+
const density = n / kloc;
|
|
338
|
+
const label = `${roundOne(density)} ${pluralize('issue', n)} / KLOC`;
|
|
339
|
+
if (density < 5) return { grade: 'A', position: 1 - density / 5, label };
|
|
340
|
+
if (density < 10) return { grade: 'B', position: 1 - (density - 5) / 5, label };
|
|
341
|
+
if (density < 25) return { grade: 'C', position: 1 - (density - 10) / 15, label };
|
|
342
|
+
if (density < 50) return { grade: 'D', position: 1 - (density - 25) / 25, label };
|
|
343
|
+
return { grade: 'F', position: 1 / (1 + (density - 50) / 25), label };
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
interface SeverityEscape {
|
|
347
|
+
grade: Exclude<Grade, 'N/A'>;
|
|
348
|
+
note: string;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function maintainabilityEscape(counts: SeverityCounts): SeverityEscape | null {
|
|
352
|
+
if (counts.critical > 0) {
|
|
353
|
+
return { grade: 'D', note: `${counts.critical} critical-severity ${pluralize('issue', counts.critical)}` };
|
|
354
|
+
}
|
|
355
|
+
if (counts.high > 0) {
|
|
356
|
+
return { grade: 'C', note: `${counts.high} high-severity ${pluralize('issue', counts.high)}` };
|
|
357
|
+
}
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Maintainability uses a density-based grade (issues per KLOC) once the
|
|
363
|
+
* codebase is at least 5 KLOC. For smaller codebases, density is too noisy
|
|
364
|
+
* (one extra lint issue moves density by 1.0+), so we fall back to absolute
|
|
365
|
+
* counts — preventing tiny projects from being unfairly penalized.
|
|
366
|
+
*
|
|
367
|
+
* Severity escape hatch: a single high-severity maintainability finding
|
|
368
|
+
* (e.g., a 1500-line file) caps the grade at C; a critical caps at D.
|
|
369
|
+
* "Worst wins" — we take min of density-grade and severity-cap.
|
|
370
|
+
*/
|
|
371
|
+
function gradeMaintainability(findings: Array<{ severity: string }>, totalLines: number): DimensionScore {
|
|
372
|
+
const counts = countSeverities(findings);
|
|
373
|
+
const kloc = Math.max(totalLines / 1000, 1.0);
|
|
374
|
+
|
|
375
|
+
if (counts.total === 0) {
|
|
376
|
+
return {
|
|
377
|
+
name: 'maintainability',
|
|
378
|
+
score: 100,
|
|
379
|
+
grade: 'A',
|
|
380
|
+
rationale: '0 maintainability findings',
|
|
381
|
+
available: true,
|
|
382
|
+
findingCount: 0,
|
|
383
|
+
worstSeverity: null,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const band = kloc < 5 ? maintainabilityByCount(counts.total) : maintainabilityByDensity(counts.total, kloc);
|
|
388
|
+
const severityCap = maintainabilityEscape(counts);
|
|
389
|
+
const useCap = severityCap && gradeIsWorse(severityCap.grade, band.grade);
|
|
390
|
+
const finalGrade = useCap ? severityCap.grade : band.grade;
|
|
391
|
+
const finalPosition = useCap ? 0.5 : band.position;
|
|
392
|
+
const rationale = useCap ? `${band.label}, ${severityCap.note}` : band.label;
|
|
393
|
+
|
|
394
|
+
return {
|
|
395
|
+
name: 'maintainability',
|
|
396
|
+
score: scoreInBand(finalGrade, finalPosition),
|
|
397
|
+
grade: finalGrade,
|
|
398
|
+
rationale,
|
|
399
|
+
available: true,
|
|
400
|
+
findingCount: counts.total,
|
|
401
|
+
worstSeverity: worstSeverity(counts),
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// ============================================================================
|
|
406
|
+
// Grade Comparison Helpers
|
|
407
|
+
// ============================================================================
|
|
408
|
+
|
|
409
|
+
const GRADE_RANK: Record<Exclude<Grade, 'N/A'>, number> = {
|
|
410
|
+
F: 1,
|
|
411
|
+
D: 2,
|
|
412
|
+
C: 3,
|
|
413
|
+
B: 4,
|
|
414
|
+
A: 5,
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
function gradeIsWorse(a: Exclude<Grade, 'N/A'>, b: Exclude<Grade, 'N/A'>): boolean {
|
|
418
|
+
return GRADE_RANK[a] < GRADE_RANK[b];
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function worstOf(grades: Array<Exclude<Grade, 'N/A'>>): Exclude<Grade, 'N/A'> {
|
|
422
|
+
let worst: Exclude<Grade, 'N/A'> = 'A';
|
|
423
|
+
for (const g of grades) {
|
|
424
|
+
if (gradeIsWorse(g, worst)) worst = g;
|
|
425
|
+
}
|
|
426
|
+
return worst;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// ============================================================================
|
|
430
|
+
// Misc Helpers
|
|
431
|
+
// ============================================================================
|
|
432
|
+
|
|
433
|
+
function pluralize(word: string, n: number): string {
|
|
434
|
+
return n === 1 ? word : `${word}s`;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function roundOne(n: number): number {
|
|
438
|
+
return Math.round(n * 10) / 10;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
function dimensionDisplayName(name: DimensionName): string {
|
|
442
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function naDimension(name: DimensionName): DimensionScore {
|
|
446
|
+
return {
|
|
447
|
+
name,
|
|
448
|
+
score: 0,
|
|
449
|
+
grade: 'N/A',
|
|
450
|
+
rationale: 'No tools available to evaluate',
|
|
451
|
+
available: false,
|
|
452
|
+
findingCount: 0,
|
|
453
|
+
worstSeverity: null,
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// ============================================================================
|
|
458
|
+
// Top-Level Entry Point
|
|
459
|
+
// ============================================================================
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Compute the full multi-dimensional quality rating from the merged finding
|
|
463
|
+
* set. Callers can override availability in two ways:
|
|
464
|
+
* - `availableDimensions`: hard whitelist — only listed dims are graded.
|
|
465
|
+
* - `forceNA`: forces specific dims to N/A even if they would otherwise
|
|
466
|
+
* auto-detect as available. Use this when the underlying tools didn't
|
|
467
|
+
* run (e.g., no linter installed -> Maintainability has limited coverage).
|
|
468
|
+
*
|
|
469
|
+
* Default availability rules:
|
|
470
|
+
* - maintainability is always available (lint/format/length checks always run)
|
|
471
|
+
* - security/reliability are available iff at least one finding maps there
|
|
472
|
+
*
|
|
473
|
+
* Overall score uses min(avg, worst) so a single bad dimension caps the
|
|
474
|
+
* total — you cannot earn a great overall score by averaging away a hole.
|
|
475
|
+
*/
|
|
476
|
+
function bucketByDimension(
|
|
477
|
+
findings: Array<{ severity: string; category: string }>,
|
|
478
|
+
): {
|
|
479
|
+
security: Array<{ severity: string; category: string }>;
|
|
480
|
+
reliability: Array<{ severity: string; category: string }>;
|
|
481
|
+
maintainability: Array<{ severity: string; category: string }>;
|
|
482
|
+
} {
|
|
483
|
+
const security: Array<{ severity: string; category: string }> = [];
|
|
484
|
+
const reliability: Array<{ severity: string; category: string }> = [];
|
|
485
|
+
const maintainability: Array<{ severity: string; category: string }> = [];
|
|
486
|
+
for (const f of findings) {
|
|
487
|
+
const dim = categoryToDimension(f.category);
|
|
488
|
+
if (dim === 'security') security.push(f);
|
|
489
|
+
else if (dim === 'reliability') reliability.push(f);
|
|
490
|
+
else maintainability.push(f);
|
|
491
|
+
}
|
|
492
|
+
return { security, reliability, maintainability };
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function isDimensionAvailable(
|
|
496
|
+
dim: DimensionName,
|
|
497
|
+
hasFindings: boolean,
|
|
498
|
+
options?: { availableDimensions?: Set<DimensionName>; forceNA?: Set<DimensionName> },
|
|
499
|
+
): boolean {
|
|
500
|
+
if (options?.forceNA?.has(dim)) return false;
|
|
501
|
+
const explicit = options?.availableDimensions;
|
|
502
|
+
if (explicit) return explicit.has(dim);
|
|
503
|
+
// Auto-detect: maintainability always on, security/reliability iff findings exist.
|
|
504
|
+
return dim === 'maintainability' ? true : hasFindings;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function computeOverall(availableDims: DimensionScore[]): { grade: Grade; score: number } {
|
|
508
|
+
if (availableDims.length === 0) {
|
|
509
|
+
return { grade: 'N/A', score: 0 };
|
|
510
|
+
}
|
|
511
|
+
const grades = availableDims.map((d) => d.grade as Exclude<Grade, 'N/A'>);
|
|
512
|
+
const scores = availableDims.map((d) => d.score);
|
|
513
|
+
const avg = scores.reduce((s, n) => s + n, 0) / scores.length;
|
|
514
|
+
return {
|
|
515
|
+
grade: worstOf(grades),
|
|
516
|
+
score: Math.round(Math.min(avg, Math.min(...scores))),
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
export function computeQualityRating(
|
|
521
|
+
allFindings: Array<{ severity: string; category: string }>,
|
|
522
|
+
totalLines: number,
|
|
523
|
+
options?: { availableDimensions?: Set<DimensionName>; forceNA?: Set<DimensionName> },
|
|
524
|
+
): QualityRating {
|
|
525
|
+
const buckets = bucketByDimension(allFindings);
|
|
526
|
+
|
|
527
|
+
const security = isDimensionAvailable('security', buckets.security.length > 0, options)
|
|
528
|
+
? gradeSecurity(buckets.security)
|
|
529
|
+
: naDimension('security');
|
|
530
|
+
const reliability = isDimensionAvailable('reliability', buckets.reliability.length > 0, options)
|
|
531
|
+
? gradeReliability(buckets.reliability)
|
|
532
|
+
: naDimension('reliability');
|
|
533
|
+
const maintainability = isDimensionAvailable('maintainability', true, options)
|
|
534
|
+
? gradeMaintainability(buckets.maintainability, totalLines)
|
|
535
|
+
: naDimension('maintainability');
|
|
536
|
+
|
|
537
|
+
const dimensions: DimensionScore[] = [security, reliability, maintainability];
|
|
538
|
+
const availableDims = dimensions.filter((d) => d.available);
|
|
539
|
+
const overall = computeOverall(availableDims);
|
|
540
|
+
|
|
541
|
+
// Quality gate.
|
|
542
|
+
const qualityGate = computeQualityGate(security, reliability);
|
|
543
|
+
|
|
544
|
+
// Grade rationale.
|
|
545
|
+
const gradeRationale = computeGradeRationale(availableDims, overall.grade, allFindings.length);
|
|
546
|
+
|
|
547
|
+
return {
|
|
548
|
+
overall,
|
|
549
|
+
dimensions,
|
|
550
|
+
qualityGate,
|
|
551
|
+
gradeRationale,
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// ============================================================================
|
|
556
|
+
// Quality Gate
|
|
557
|
+
// ============================================================================
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* The Quality Gate is a coarse PASS/FAIL signal layered on top of the grades.
|
|
561
|
+
* It only fires for the most user-actionable thresholds — any medium+ security
|
|
562
|
+
* finding, or any critical bug. N/A dimensions never trigger a fail (we don't
|
|
563
|
+
* fail on missing data).
|
|
564
|
+
*/
|
|
565
|
+
function computeQualityGate(security: DimensionScore, reliability: DimensionScore): QualityGate {
|
|
566
|
+
const failingConditions: string[] = [];
|
|
567
|
+
|
|
568
|
+
if (security.available && (security.grade === 'C' || security.grade === 'D' || security.grade === 'F')) {
|
|
569
|
+
failingConditions.push(`Security grade ${security.grade} — ${security.rationale}`);
|
|
570
|
+
}
|
|
571
|
+
if (reliability.available && reliability.grade === 'F') {
|
|
572
|
+
failingConditions.push(`Reliability grade F — ${reliability.rationale}`);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
return {
|
|
576
|
+
passed: failingConditions.length === 0,
|
|
577
|
+
failingConditions,
|
|
578
|
+
};
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// ============================================================================
|
|
582
|
+
// Grade Rationale
|
|
583
|
+
// ============================================================================
|
|
584
|
+
|
|
585
|
+
function computeGradeRationale(
|
|
586
|
+
availableDims: DimensionScore[],
|
|
587
|
+
overallGrade: Grade,
|
|
588
|
+
totalFindingCount: number,
|
|
589
|
+
): string {
|
|
590
|
+
if (totalFindingCount === 0) {
|
|
591
|
+
return 'Clean — no findings detected';
|
|
592
|
+
}
|
|
593
|
+
if (availableDims.length === 0 || overallGrade === 'N/A') {
|
|
594
|
+
return 'No dimensions available to grade';
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// All available dimensions equal -> "consistent quality".
|
|
598
|
+
const firstGrade = availableDims[0].grade;
|
|
599
|
+
const allEqual = availableDims.every((d) => d.grade === firstGrade);
|
|
600
|
+
if (allEqual) {
|
|
601
|
+
return `All dimensions ${firstGrade} — consistent quality`;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// Find the dimension that pinned the overall grade (worst available).
|
|
605
|
+
const worstDim =
|
|
606
|
+
availableDims.find((d) => d.grade === overallGrade) ??
|
|
607
|
+
// Fallback shouldn't fire since overallGrade was derived from availableDims.
|
|
608
|
+
availableDims[0];
|
|
609
|
+
|
|
610
|
+
return `Capped at ${overallGrade} by ${dimensionDisplayName(worstDim.name)} (${worstDim.rationale})`;
|
|
611
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// Copyright (c) 2025-present Mstro, Inc. All rights reserved.
|
|
2
|
-
// Licensed under the MIT License. See LICENSE file for details.
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* Quality Handlers — WebSocket message router for quality scanning
|
|
@@ -173,6 +172,15 @@ async function handleSaveDirectories(
|
|
|
173
172
|
}
|
|
174
173
|
|
|
175
174
|
persistence.saveConfig(directories);
|
|
175
|
+
|
|
176
|
+
// Broadcast the updated set so every paired web (on any Fly instance)
|
|
177
|
+
// reflects the change. This is how Quality subdirectory tabs stay in
|
|
178
|
+
// sync across devices — the type is listed in the cross-instance
|
|
179
|
+
// set in `server/src/relay/handlers/clientHandlers.ts`.
|
|
180
|
+
ctx.broadcastToAll({
|
|
181
|
+
type: 'qualityDirectoriesUpdated',
|
|
182
|
+
data: { directories },
|
|
183
|
+
});
|
|
176
184
|
} catch (error) {
|
|
177
185
|
ctx.send(ws, {
|
|
178
186
|
type: 'qualityError',
|
|
@@ -245,9 +253,11 @@ async function handleScan(
|
|
|
245
253
|
|
|
246
254
|
const resultData = { path: reportPath, results };
|
|
247
255
|
try {
|
|
248
|
-
|
|
256
|
+
// Broadcast so every device sees the new scan — the Quality view on
|
|
257
|
+
// another device otherwise stays stuck on the previous scan results.
|
|
258
|
+
ctx.broadcastToAll({ type: 'qualityScanResults', data: resultData });
|
|
249
259
|
} catch {
|
|
250
|
-
//
|
|
260
|
+
// Broadcast failed — save as pending for delivery on reconnect
|
|
251
261
|
persistence.addPendingResult({
|
|
252
262
|
type: 'scanResults',
|
|
253
263
|
path: reportPath,
|
|
@@ -288,7 +298,9 @@ async function handleInstallTools(
|
|
|
288
298
|
|
|
289
299
|
const { tools, ecosystem } = await installTools(dirPath, toolNames);
|
|
290
300
|
|
|
291
|
-
|
|
301
|
+
// Broadcast so every device sees the install result (status of tools
|
|
302
|
+
// changes orchestra-wide once installed on the machine).
|
|
303
|
+
ctx.broadcastToAll({
|
|
292
304
|
type: 'qualityInstallComplete',
|
|
293
305
|
data: { path: reportPath, tools, ecosystem },
|
|
294
306
|
});
|