@evalops/maestro 0.10.50 → 0.10.51
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/dist/agent/agent-outcome.d.ts +54 -0
- package/dist/agent/agent-outcome.d.ts.map +1 -0
- package/dist/agent/agent-outcome.js +142 -0
- package/dist/agent/agent-outcome.js.map +1 -0
- package/dist/agent/agent-resume.d.ts.map +1 -1
- package/dist/agent/agent-resume.js +3 -3
- package/dist/agent/agent-resume.js.map +1 -1
- package/dist/agent/agent-run-summary.d.ts +37 -0
- package/dist/agent/agent-run-summary.d.ts.map +1 -0
- package/dist/agent/agent-run-summary.js +15 -0
- package/dist/agent/agent-run-summary.js.map +1 -0
- package/dist/agent/agent-work-board.d.ts +82 -0
- package/dist/agent/agent-work-board.d.ts.map +1 -0
- package/dist/agent/agent-work-board.js +312 -0
- package/dist/agent/agent-work-board.js.map +1 -0
- package/dist/agent/auto-compaction.d.ts +7 -2
- package/dist/agent/auto-compaction.d.ts.map +1 -1
- package/dist/agent/auto-compaction.js +23 -10
- package/dist/agent/auto-compaction.js.map +1 -1
- package/dist/agent/mission-artifacts.d.ts +62 -0
- package/dist/agent/mission-artifacts.d.ts.map +1 -0
- package/dist/agent/mission-artifacts.js +438 -0
- package/dist/agent/mission-artifacts.js.map +1 -0
- package/dist/agent/mission-manifest.d.ts +139 -3
- package/dist/agent/mission-manifest.d.ts.map +1 -1
- package/dist/agent/mission-manifest.js +549 -3
- package/dist/agent/mission-manifest.js.map +1 -1
- package/dist/agent/mission-store.d.ts +80 -0
- package/dist/agent/mission-store.d.ts.map +1 -0
- package/dist/agent/mission-store.js +603 -0
- package/dist/agent/mission-store.js.map +1 -0
- package/dist/agent/modes.d.ts +2 -2
- package/dist/agent/modes.d.ts.map +1 -1
- package/dist/agent/modes.js +10 -3
- package/dist/agent/modes.js.map +1 -1
- package/dist/agent/providers/codex-app-server.d.ts.map +1 -1
- package/dist/agent/providers/codex-app-server.js +1 -3
- package/dist/agent/providers/codex-app-server.js.map +1 -1
- package/dist/agent/providers/google-gemini-cli.js +203 -107
- package/dist/agent/providers/google.js +100 -41
- package/dist/agent/providers/scripted.d.ts.map +1 -1
- package/dist/agent/providers/scripted.js +1 -3
- package/dist/agent/providers/scripted.js.map +1 -1
- package/dist/agent/providers/validation.d.ts.map +1 -1
- package/dist/agent/providers/validation.js +1 -3
- package/dist/agent/providers/validation.js.map +1 -1
- package/dist/agent/providers/vertex.js +203 -107
- package/dist/agent/snapshot-store.d.ts +32 -0
- package/dist/agent/snapshot-store.d.ts.map +1 -0
- package/dist/agent/snapshot-store.js +195 -0
- package/dist/agent/snapshot-store.js.map +1 -0
- package/dist/agent/specialist-profiles.d.ts +36 -0
- package/dist/agent/specialist-profiles.d.ts.map +1 -0
- package/dist/agent/specialist-profiles.js +159 -0
- package/dist/agent/specialist-profiles.js.map +1 -0
- package/dist/agent/subagent-specs.d.ts +4 -1
- package/dist/agent/subagent-specs.d.ts.map +1 -1
- package/dist/agent/subagent-specs.js +81 -8
- package/dist/agent/subagent-specs.js.map +1 -1
- package/dist/agent/swarm/coverage-gate.d.ts +37 -0
- package/dist/agent/swarm/coverage-gate.d.ts.map +1 -0
- package/dist/agent/swarm/coverage-gate.js +48 -0
- package/dist/agent/swarm/coverage-gate.js.map +1 -0
- package/dist/agent/swarm/executor.d.ts +1 -0
- package/dist/agent/swarm/executor.d.ts.map +1 -1
- package/dist/agent/swarm/executor.js +151 -15
- package/dist/agent/swarm/executor.js.map +1 -1
- package/dist/agent/swarm/orchestrator-prompt.d.ts +25 -0
- package/dist/agent/swarm/orchestrator-prompt.d.ts.map +1 -0
- package/dist/agent/swarm/orchestrator-prompt.js +48 -0
- package/dist/agent/swarm/orchestrator-prompt.js.map +1 -0
- package/dist/agent/swarm/plan-parser.d.ts.map +1 -1
- package/dist/agent/swarm/plan-parser.js +27 -2
- package/dist/agent/swarm/plan-parser.js.map +1 -1
- package/dist/agent/swarm/types.d.ts +17 -0
- package/dist/agent/swarm/types.d.ts.map +1 -1
- package/dist/agent/swarm/types.js.map +1 -1
- package/dist/agent/swarm/validation-directive.d.ts +21 -0
- package/dist/agent/swarm/validation-directive.d.ts.map +1 -0
- package/dist/agent/swarm/validation-directive.js +25 -0
- package/dist/agent/swarm/validation-directive.js.map +1 -0
- package/dist/agent/transport/tool-execution-bridge.d.ts.map +1 -1
- package/dist/agent/transport/tool-execution-bridge.js +4 -1
- package/dist/agent/transport/tool-execution-bridge.js.map +1 -1
- package/dist/api/enterprise-routes.d.ts.map +1 -1
- package/dist/api/enterprise-routes.js +87 -10
- package/dist/api/enterprise-routes.js.map +1 -1
- package/dist/api/org-settings-merge.d.ts +12 -0
- package/dist/api/org-settings-merge.d.ts.map +1 -0
- package/dist/api/org-settings-merge.js +38 -0
- package/dist/api/org-settings-merge.js.map +1 -0
- package/dist/api/user-settings-merge.d.ts +16 -0
- package/dist/api/user-settings-merge.d.ts.map +1 -0
- package/dist/api/user-settings-merge.js +37 -0
- package/dist/api/user-settings-merge.js.map +1 -0
- package/dist/app-server/daemon-lifecycle-api.d.ts.map +1 -1
- package/dist/app-server/daemon-lifecycle-api.js +1 -3
- package/dist/app-server/daemon-lifecycle-api.js.map +1 -1
- package/dist/app-server/external-agent-import-api.d.ts.map +1 -1
- package/dist/app-server/external-agent-import-api.js +1 -3
- package/dist/app-server/external-agent-import-api.js.map +1 -1
- package/dist/app-server/network-governance-api.d.ts.map +1 -1
- package/dist/app-server/network-governance-api.js +1 -3
- package/dist/app-server/network-governance-api.js.map +1 -1
- package/dist/app-server/plugin-bundle-api.d.ts.map +1 -1
- package/dist/app-server/plugin-bundle-api.js +1 -3
- package/dist/app-server/plugin-bundle-api.js.map +1 -1
- package/dist/app-server/policy-control-api.d.ts.map +1 -1
- package/dist/app-server/policy-control-api.js +1 -3
- package/dist/app-server/policy-control-api.js.map +1 -1
- package/dist/app-server/protocol-modes-api.d.ts.map +1 -1
- package/dist/app-server/protocol-modes-api.js +1 -3
- package/dist/app-server/protocol-modes-api.js.map +1 -1
- package/dist/cli/args.d.ts +2 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +15 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/commands/a2a.d.ts.map +1 -1
- package/dist/cli/commands/a2a.js +1 -3
- package/dist/cli/commands/a2a.js.map +1 -1
- package/dist/cli/commands/agents.d.ts +4 -0
- package/dist/cli/commands/agents.d.ts.map +1 -1
- package/dist/cli/commands/agents.js +149 -0
- package/dist/cli/commands/agents.js.map +1 -1
- package/dist/cli/commands/mission.d.ts +4 -0
- package/dist/cli/commands/mission.d.ts.map +1 -0
- package/dist/cli/commands/mission.js +196 -0
- package/dist/cli/commands/mission.js.map +1 -0
- package/dist/cli/commands/value.d.ts +10 -0
- package/dist/cli/commands/value.d.ts.map +1 -0
- package/dist/cli/commands/value.js +33 -0
- package/dist/cli/commands/value.js.map +1 -0
- package/dist/cli/headless-protocol.d.ts.map +1 -1
- package/dist/cli/headless-protocol.js +1 -3
- package/dist/cli/headless-protocol.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +10 -0
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/system-prompt.d.ts.map +1 -1
- package/dist/cli/system-prompt.js +38 -7
- package/dist/cli/system-prompt.js.map +1 -1
- package/dist/cli-runtime.js +7756 -2800
- package/dist/cli-tui/client-tools/client-tool-controller.d.ts.map +1 -1
- package/dist/cli-tui/client-tools/client-tool-controller.js +1 -3
- package/dist/cli-tui/client-tools/client-tool-controller.js.map +1 -1
- package/dist/cli-tui/commands/undo-handlers.d.ts.map +1 -1
- package/dist/cli-tui/commands/undo-handlers.js +7 -2
- package/dist/cli-tui/commands/undo-handlers.js.map +1 -1
- package/dist/cli-tui/tui-renderer/skills-controller.d.ts +3 -0
- package/dist/cli-tui/tui-renderer/skills-controller.d.ts.map +1 -1
- package/dist/cli-tui/tui-renderer/skills-controller.js +77 -33
- package/dist/cli-tui/tui-renderer/skills-controller.js.map +1 -1
- package/dist/codex/app-server-client.d.ts.map +1 -1
- package/dist/codex/app-server-client.js +1 -3
- package/dist/codex/app-server-client.js.map +1 -1
- package/dist/codex/compatibility.d.ts.map +1 -1
- package/dist/codex/compatibility.js +1 -3
- package/dist/codex/compatibility.js.map +1 -1
- package/dist/codex/subagent-dispatch-table.d.ts +8 -0
- package/dist/codex/subagent-dispatch-table.d.ts.map +1 -1
- package/dist/codex/subagent-dispatch-table.js +23 -0
- package/dist/codex/subagent-dispatch-table.js.map +1 -1
- package/dist/context/manifest.d.ts.map +1 -1
- package/dist/context/manifest.js +1 -3
- package/dist/context/manifest.js.map +1 -1
- package/dist/conversation/render-model.d.ts +0 -36
- package/dist/conversation/render-model.d.ts.map +1 -1
- package/dist/conversation/render-model.js +1 -3
- package/dist/conversation/render-model.js.map +1 -1
- package/dist/customer-value/ambient-learning.d.ts +75 -0
- package/dist/customer-value/ambient-learning.d.ts.map +1 -0
- package/dist/customer-value/ambient-learning.js +472 -0
- package/dist/customer-value/ambient-learning.js.map +1 -0
- package/dist/customer-value/internal-helpers.d.ts +13 -0
- package/dist/customer-value/internal-helpers.d.ts.map +1 -0
- package/dist/customer-value/internal-helpers.js +70 -0
- package/dist/customer-value/internal-helpers.js.map +1 -0
- package/dist/customer-value/report.d.ts +282 -0
- package/dist/customer-value/report.d.ts.map +1 -0
- package/dist/customer-value/report.js +2085 -0
- package/dist/customer-value/report.js.map +1 -0
- package/dist/db/schema.d.ts +5 -1
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js.map +1 -1
- package/dist/evalops/agent-bootstrap.d.ts.map +1 -1
- package/dist/evalops/agent-bootstrap.js +1 -3
- package/dist/evalops/agent-bootstrap.js.map +1 -1
- package/dist/evalops/agent-mcp-metadata.d.ts.map +1 -1
- package/dist/evalops/agent-mcp-metadata.js +1 -3
- package/dist/evalops/agent-mcp-metadata.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/installers/maestro-review-workflow.d.ts +38 -0
- package/dist/installers/maestro-review-workflow.d.ts.map +1 -0
- package/dist/installers/maestro-review-workflow.js +163 -0
- package/dist/installers/maestro-review-workflow.js.map +1 -0
- package/dist/load-env.d.ts.map +1 -1
- package/dist/load-env.js +2 -1
- package/dist/load-env.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +92 -3
- package/dist/main.js.map +1 -1
- package/dist/mcp/config.d.ts.map +1 -1
- package/dist/mcp/config.js +1 -3
- package/dist/mcp/config.js.map +1 -1
- package/dist/mcp/elicitation.d.ts.map +1 -1
- package/dist/mcp/elicitation.js +1 -3
- package/dist/mcp/elicitation.js.map +1 -1
- package/dist/mcp/official-registry.d.ts.map +1 -1
- package/dist/mcp/official-registry.js +1 -3
- package/dist/mcp/official-registry.js.map +1 -1
- package/dist/mcp/workspace-trust.d.ts.map +1 -1
- package/dist/mcp/workspace-trust.js +1 -3
- package/dist/mcp/workspace-trust.js.map +1 -1
- package/dist/memory/service-client.d.ts.map +1 -1
- package/dist/memory/service-client.js +1 -3
- package/dist/memory/service-client.js.map +1 -1
- package/dist/models/config-loader.d.ts.map +1 -1
- package/dist/models/config-loader.js +1 -3
- package/dist/models/config-loader.js.map +1 -1
- package/dist/node_modules/@evalops/contracts/dist/index.d.ts +1 -0
- package/dist/node_modules/@evalops/contracts/dist/index.d.ts.map +1 -1
- package/dist/node_modules/@evalops/contracts/dist/index.js +1 -0
- package/dist/node_modules/@evalops/contracts/dist/index.js.map +1 -1
- package/dist/node_modules/@evalops/contracts/dist/maestro-settings.d.ts +61 -0
- package/dist/node_modules/@evalops/contracts/dist/maestro-settings.d.ts.map +1 -0
- package/dist/node_modules/@evalops/contracts/dist/maestro-settings.js +183 -0
- package/dist/node_modules/@evalops/contracts/dist/maestro-settings.js.map +1 -0
- package/dist/node_modules/@evalops/contracts/package.json +1 -1
- package/dist/node_modules/@evalops/tui/package.json +1 -1
- package/dist/oauth/connectors.d.ts.map +1 -1
- package/dist/oauth/connectors.js +1 -3
- package/dist/oauth/connectors.js.map +1 -1
- package/dist/oauth/evalops.d.ts.map +1 -1
- package/dist/oauth/evalops.js +1 -3
- package/dist/oauth/evalops.js.map +1 -1
- package/dist/platform/a2a-client.d.ts.map +1 -1
- package/dist/platform/a2a-client.js +1 -3
- package/dist/platform/a2a-client.js.map +1 -1
- package/dist/platform/a2a-cockpit.d.ts +1 -0
- package/dist/platform/a2a-cockpit.d.ts.map +1 -1
- package/dist/platform/a2a-cockpit.js +42 -11
- package/dist/platform/a2a-cockpit.js.map +1 -1
- package/dist/platform/a2a-completion-audit.d.ts +1 -1
- package/dist/platform/a2a-completion-audit.d.ts.map +1 -1
- package/dist/platform/a2a-completion-audit.js +7 -11
- package/dist/platform/a2a-completion-audit.js.map +1 -1
- package/dist/platform/a2a-maestro-peer.js +16 -0
- package/dist/platform/a2a-maestro-peer.js.map +1 -1
- package/dist/platform/a2a-peer-pairing.d.ts.map +1 -1
- package/dist/platform/a2a-peer-pairing.js +1 -3
- package/dist/platform/a2a-peer-pairing.js.map +1 -1
- package/dist/platform/a2a-peer-registry.d.ts.map +1 -1
- package/dist/platform/a2a-peer-registry.js +1 -3
- package/dist/platform/a2a-peer-registry.js.map +1 -1
- package/dist/platform/a2a-task-ledger.d.ts +20 -0
- package/dist/platform/a2a-task-ledger.d.ts.map +1 -1
- package/dist/platform/a2a-task-ledger.js +207 -11
- package/dist/platform/a2a-task-ledger.js.map +1 -1
- package/dist/platform/a2a-work-graph.d.ts.map +1 -1
- package/dist/platform/a2a-work-graph.js +1 -3
- package/dist/platform/a2a-work-graph.js.map +1 -1
- package/dist/platform/operating-layer.d.ts.map +1 -1
- package/dist/platform/operating-layer.js +14 -8
- package/dist/platform/operating-layer.js.map +1 -1
- package/dist/platform/tool-execution-client.d.ts.map +1 -1
- package/dist/platform/tool-execution-client.js +19 -2
- package/dist/platform/tool-execution-client.js.map +1 -1
- package/dist/providers/auth.d.ts.map +1 -1
- package/dist/providers/auth.js +1 -3
- package/dist/providers/auth.js.map +1 -1
- package/dist/runtime/env.d.ts +9 -1
- package/dist/runtime/env.d.ts.map +1 -1
- package/dist/runtime/env.js +63 -1
- package/dist/runtime/env.js.map +1 -1
- package/dist/safety/policy.d.ts.map +1 -1
- package/dist/safety/policy.js +1 -4
- package/dist/safety/policy.js.map +1 -1
- package/dist/sandbox/docker-sandbox.d.ts.map +1 -1
- package/dist/sandbox/docker-sandbox.js +6 -1
- package/dist/sandbox/docker-sandbox.js.map +1 -1
- package/dist/server/handlers/hosted-runner-drain.d.ts.map +1 -1
- package/dist/server/handlers/hosted-runner-drain.js +1 -3
- package/dist/server/handlers/hosted-runner-drain.js.map +1 -1
- package/dist/server/handlers/platform-a2a-push.d.ts.map +1 -1
- package/dist/server/handlers/platform-a2a-push.js +53 -2
- package/dist/server/handlers/platform-a2a-push.js.map +1 -1
- package/dist/server/handlers/runtime-app-server-ws.d.ts.map +1 -1
- package/dist/server/handlers/runtime-app-server-ws.js +1 -3
- package/dist/server/handlers/runtime-app-server-ws.js.map +1 -1
- package/dist/server/headless-runtime-service.d.ts +16 -0
- package/dist/server/headless-runtime-service.d.ts.map +1 -1
- package/dist/server/headless-runtime-service.js +232 -206
- package/dist/server/headless-runtime-service.js.map +1 -1
- package/dist/server/hosted-agent-runtime-progress.d.ts.map +1 -1
- package/dist/server/hosted-agent-runtime-progress.js +12 -3
- package/dist/server/hosted-agent-runtime-progress.js.map +1 -1
- package/dist/server/pending-request-payload.d.ts.map +1 -1
- package/dist/server/pending-request-payload.js +1 -3
- package/dist/server/pending-request-payload.js.map +1 -1
- package/dist/server/route-auth.d.ts.map +1 -1
- package/dist/server/route-auth.js +2 -0
- package/dist/server/route-auth.js.map +1 -1
- package/dist/server/session-serialization.d.ts.map +1 -1
- package/dist/server/session-serialization.js +1 -3
- package/dist/server/session-serialization.js.map +1 -1
- package/dist/services/compliance/normalize.d.ts.map +1 -1
- package/dist/services/compliance/normalize.js +1 -3
- package/dist/services/compliance/normalize.js.map +1 -1
- package/dist/services/intelligent-router/normalize.d.ts.map +1 -1
- package/dist/services/intelligent-router/normalize.js +1 -3
- package/dist/services/intelligent-router/normalize.js.map +1 -1
- package/dist/services/revenue-attribution/normalize.d.ts.map +1 -1
- package/dist/services/revenue-attribution/normalize.js +1 -3
- package/dist/services/revenue-attribution/normalize.js.map +1 -1
- package/dist/services/traces/normalize.d.ts.map +1 -1
- package/dist/services/traces/normalize.js +1 -3
- package/dist/services/traces/normalize.js.map +1 -1
- package/dist/services/workspace-config/normalize.d.ts.map +1 -1
- package/dist/services/workspace-config/normalize.js +1 -3
- package/dist/services/workspace-config/normalize.js.map +1 -1
- package/dist/skills/composer.d.ts.map +1 -1
- package/dist/skills/composer.js +13 -1
- package/dist/skills/composer.js.map +1 -1
- package/dist/skills/index.d.ts +1 -0
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +1 -0
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/learned-guidelines.d.ts +33 -0
- package/dist/skills/learned-guidelines.d.ts.map +1 -0
- package/dist/skills/learned-guidelines.js +291 -0
- package/dist/skills/learned-guidelines.js.map +1 -0
- package/dist/skills/linter.d.ts.map +1 -1
- package/dist/skills/linter.js +1 -3
- package/dist/skills/linter.js.map +1 -1
- package/dist/skills/runtime-activation.d.ts +1 -0
- package/dist/skills/runtime-activation.d.ts.map +1 -1
- package/dist/skills/runtime-activation.js +2 -3
- package/dist/skills/runtime-activation.js.map +1 -1
- package/dist/skills/tool.d.ts.map +1 -1
- package/dist/skills/tool.js +26 -21
- package/dist/skills/tool.js.map +1 -1
- package/dist/skills/trust-cache.d.ts +7 -0
- package/dist/skills/trust-cache.d.ts.map +1 -1
- package/dist/skills/trust-cache.js +16 -0
- package/dist/skills/trust-cache.js.map +1 -1
- package/dist/telemetry/agent-workforce-native-event.d.ts.map +1 -1
- package/dist/telemetry/agent-workforce-native-event.js +1 -3
- package/dist/telemetry/agent-workforce-native-event.js.map +1 -1
- package/dist/telemetry/maestro-event-bus.d.ts.map +1 -1
- package/dist/telemetry/maestro-event-bus.js +42 -81
- package/dist/telemetry/maestro-event-bus.js.map +1 -1
- package/dist/telemetry/maestro-platform-replay-fixture.d.ts.map +1 -1
- package/dist/telemetry/maestro-platform-replay-fixture.js +1 -3
- package/dist/telemetry/maestro-platform-replay-fixture.js.map +1 -1
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +54 -43
- package/dist/telemetry.js.map +1 -1
- package/dist/theme/theme-loader.d.ts.map +1 -1
- package/dist/theme/theme-loader.js +1 -3
- package/dist/theme/theme-loader.js.map +1 -1
- package/dist/tools/apply-patch.d.ts.map +1 -1
- package/dist/tools/apply-patch.js +76 -8
- package/dist/tools/apply-patch.js.map +1 -1
- package/dist/tools/diff-preview.d.ts.map +1 -1
- package/dist/tools/diff-preview.js +10 -1
- package/dist/tools/diff-preview.js.map +1 -1
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +17 -0
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/write.d.ts.map +1 -1
- package/dist/tools/write.js +9 -0
- package/dist/tools/write.js.map +1 -1
- package/dist/undo/tracker.d.ts +5 -0
- package/dist/undo/tracker.d.ts.map +1 -1
- package/dist/undo/tracker.js +13 -0
- package/dist/undo/tracker.js.map +1 -1
- package/dist/utils/fs.d.ts +1 -1
- package/dist/utils/fs.d.ts.map +1 -1
- package/dist/utils/fs.js +29 -2
- package/dist/utils/fs.js.map +1 -1
- package/dist/utils/git-stash.d.ts.map +1 -1
- package/dist/utils/git-stash.js +15 -8
- package/dist/utils/git-stash.js.map +1 -1
- package/dist/utils/json.d.ts +11 -0
- package/dist/utils/json.d.ts.map +1 -1
- package/dist/utils/json.js +9 -0
- package/dist/utils/json.js.map +1 -1
- package/dist/utils/shell-env.d.ts.map +1 -1
- package/dist/utils/shell-env.js +2 -0
- package/dist/utils/shell-env.js.map +1 -1
- package/dist/utils/url-extractor-git.d.ts +26 -0
- package/dist/utils/url-extractor-git.d.ts.map +1 -0
- package/dist/utils/url-extractor-git.js +259 -0
- package/dist/utils/url-extractor-git.js.map +1 -0
- package/dist/utils/url-extractor.d.ts.map +1 -1
- package/dist/utils/url-extractor.js +23 -251
- package/dist/utils/url-extractor.js.map +1 -1
- package/dist/version.json +2 -2
- package/package.json +310 -306
- package/skills/incident-triage/SKILL.md +19 -1
- package/skills/install-code-review/SKILL.md +69 -0
- package/skills/security-review/SKILL.md +68 -0
- package/skills/security-review/reference/rubric.md +85 -0
package/dist/undo/tracker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../src/undo/tracker.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * File change tracker for undo/rollback functionality.\n *\n * Tracks file modifications made by tools during a session,\n * enabling granular undo operations beyond git.\n */\n\nimport { execSync } from \"node:child_process\";\nimport { existsSync, mkdirSync, readFileSync, unlinkSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { writeTextFileAtomic } from \"../utils/fs.js\";\nimport { createLogger } from \"../utils/logger.js\";\nimport { sanitizeWithStaticMask } from \"../utils/secret-redactor.js\";\nimport type {\n\tChangeTrackerState,\n\tChangeType,\n\tCheckpoint,\n\tFileChange,\n\tUndoPreview,\n} from \"./types.js\";\n\nconst logger = createLogger(\"undo:tracker\");\n\nconst DEFAULT_MAX_CHANGES = 100;\n\nfunction cloneChange(change: FileChange): FileChange {\n\treturn { ...change };\n}\n\nfunction cloneCheckpoint(checkpoint: Checkpoint): Checkpoint {\n\treturn { ...checkpoint };\n}\n\nfunction cloneState(state: ChangeTrackerState): ChangeTrackerState {\n\treturn {\n\t\tchanges: state.changes.map(cloneChange),\n\t\tcheckpoints: state.checkpoints.map(cloneCheckpoint),\n\t\tmaxChanges: state.maxChanges,\n\t};\n}\n\n/**\n * Generates a unique change ID.\n */\nfunction generateChangeId(): string {\n\treturn `chg_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\n/**\n * Check if a path is inside a git repository.\n */\nfunction isGitTracked(filePath: string): boolean {\n\ttry {\n\t\tconst dir = dirname(filePath);\n\t\texecSync(\"git rev-parse --git-dir\", {\n\t\t\tcwd: dir,\n\t\t\tstdio: \"ignore\",\n\t\t});\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * File change tracker for session-based undo.\n */\nexport class ChangeTracker {\n\tprivate state: ChangeTrackerState;\n\n\tconstructor(maxChanges = DEFAULT_MAX_CHANGES) {\n\t\tthis.state = {\n\t\t\tchanges: [],\n\t\t\tcheckpoints: [],\n\t\t\tmaxChanges,\n\t\t};\n\t}\n\n\t/**\n\t * Record a file change before it happens.\n\t * Call this BEFORE writing/modifying/deleting a file.\n\t */\n\trecordChange(\n\t\tpath: string,\n\t\ttype: ChangeType,\n\t\ttoolName: string,\n\t\ttoolCallId: string,\n\t\tmessageId?: string,\n\t): string {\n\t\t// Read current content before change\n\t\tlet before: string | null = null;\n\t\tif (type !== \"create\" && existsSync(path)) {\n\t\t\ttry {\n\t\t\t\tbefore = readFileSync(path, \"utf-8\");\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\"Failed to read file for undo tracking\", {\n\t\t\t\t\tpath,\n\t\t\t\t\terror: sanitizeWithStaticMask(\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t\t),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tconst change: FileChange = {\n\t\t\tid: generateChangeId(),\n\t\t\ttype,\n\t\t\tpath,\n\t\t\tbefore,\n\t\t\tafter: null, // Will be set after the change\n\t\t\ttoolName,\n\t\t\ttoolCallId,\n\t\t\ttimestamp: Date.now(),\n\t\t\tisGitTracked: isGitTracked(path),\n\t\t\tmessageId,\n\t\t};\n\n\t\tthis.state.changes.push(change);\n\n\t\t// Trim old changes if over limit\n\t\twhile (this.state.changes.length > this.state.maxChanges) {\n\t\t\tthis.state.changes.shift();\n\t\t}\n\n\t\tlogger.debug(\"Recorded change\", {\n\t\t\tid: change.id,\n\t\t\ttype,\n\t\t\tpath,\n\t\t\ttoolName,\n\t\t});\n\n\t\treturn change.id;\n\t}\n\n\t/**\n\t * Finalize a change after it completes.\n\t * Call this AFTER writing/modifying a file to capture the new content.\n\t */\n\tfinalizeChange(changeId: string): void {\n\t\tconst change = this.state.changes.find((c) => c.id === changeId);\n\t\tif (!change) {\n\t\t\tlogger.warn(\"Change not found for finalization\", { changeId });\n\t\t\treturn;\n\t\t}\n\n\t\tif (change.type !== \"delete\" && existsSync(change.path)) {\n\t\t\ttry {\n\t\t\t\tchange.after = readFileSync(change.path, \"utf-8\");\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\"Failed to read file after change\", {\n\t\t\t\t\tpath: change.path,\n\t\t\t\t\terror: sanitizeWithStaticMask(\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t\t),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get all changes, optionally filtered.\n\t */\n\tgetChanges(options?: {\n\t\ttoolName?: string;\n\t\tsinceTimestamp?: number;\n\t\tpath?: string;\n\t}): FileChange[] {\n\t\tlet changes = this.state.changes;\n\n\t\tif (options?.toolName) {\n\t\t\tchanges = changes.filter((c) => c.toolName === options.toolName);\n\t\t}\n\t\tif (options?.sinceTimestamp) {\n\t\t\tconst since = options.sinceTimestamp;\n\t\t\tchanges = changes.filter((c) => c.timestamp >= since);\n\t\t}\n\t\tif (options?.path) {\n\t\t\tchanges = changes.filter((c) => c.path === options.path);\n\t\t}\n\n\t\treturn changes.map(cloneChange);\n\t}\n\n\t/**\n\t * Get the last N changes.\n\t */\n\tgetLastChanges(count: number): FileChange[] {\n\t\treturn this.state.changes.slice(-count).map(cloneChange);\n\t}\n\n\t/**\n\t * Preview what would be undone.\n\t */\n\tpreviewUndo(count: number): UndoPreview {\n\t\tconst changesToUndo = this.getLastChanges(count).reverse();\n\t\tconst restores: UndoPreview[\"restores\"] = [];\n\t\tconst conflicts: UndoPreview[\"conflicts\"] = [];\n\n\t\tfor (const change of changesToUndo) {\n\t\t\t// Check for conflicts (file changed externally)\n\t\t\tif (existsSync(change.path)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst currentContent = readFileSync(change.path, \"utf-8\");\n\t\t\t\t\tif (change.after !== null && currentContent !== change.after) {\n\t\t\t\t\t\tconflicts.push({\n\t\t\t\t\t\t\tpath: change.path,\n\t\t\t\t\t\t\treason: \"File has been modified since the tracked change\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// File exists but can't be read\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tswitch (change.type) {\n\t\t\t\tcase \"create\":\n\t\t\t\t\trestores.push({ path: change.path, action: \"delete\" });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"modify\":\n\t\t\t\t\trestores.push({ path: change.path, action: \"restore\" });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"delete\":\n\t\t\t\t\trestores.push({ path: change.path, action: \"recreate\" });\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tchanges: changesToUndo,\n\t\t\trestores,\n\t\t\tconflicts,\n\t\t};\n\t}\n\n\t/**\n\t * Undo the last N changes.\n\t * Returns the number of changes successfully undone.\n\t */\n\tundo(\n\t\tcount: number,\n\t\tforce = false,\n\t): {\n\t\tundone: number;\n\t\tskipped: number;\n\t\terrors: string[];\n\t} {\n\t\tconst preview = this.previewUndo(count);\n\t\tconst errors: string[] = [];\n\t\tlet undone = 0;\n\t\tlet skipped = 0;\n\n\t\tif (preview.conflicts.length > 0 && !force) {\n\t\t\treturn {\n\t\t\t\tundone: 0,\n\t\t\t\tskipped: preview.conflicts.length,\n\t\t\t\terrors: preview.conflicts.map((c) => `${c.path}: ${c.reason}`),\n\t\t\t};\n\t\t}\n\n\t\tconst changesToUndo = this.getLastChanges(count).reverse();\n\n\t\tfor (const change of changesToUndo) {\n\t\t\ttry {\n\t\t\t\tswitch (change.type) {\n\t\t\t\t\tcase \"create\":\n\t\t\t\t\t\t// Delete the created file\n\t\t\t\t\t\tif (existsSync(change.path)) {\n\t\t\t\t\t\t\tunlinkSync(change.path);\n\t\t\t\t\t\t\tundone++;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tskipped++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"modify\":\n\t\t\t\t\t\t// Restore previous content\n\t\t\t\t\t\tif (change.before !== null) {\n\t\t\t\t\t\t\twriteTextFileAtomic(change.path, change.before, {\n\t\t\t\t\t\t\t\tencoding: \"utf-8\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tundone++;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tskipped++;\n\t\t\t\t\t\t\terrors.push(`${change.path}: No previous content recorded`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"delete\":\n\t\t\t\t\t\t// Recreate the deleted file\n\t\t\t\t\t\tif (change.before !== null) {\n\t\t\t\t\t\t\tmkdirSync(dirname(change.path), { recursive: true });\n\t\t\t\t\t\t\twriteTextFileAtomic(change.path, change.before, {\n\t\t\t\t\t\t\t\tencoding: \"utf-8\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tundone++;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tskipped++;\n\t\t\t\t\t\t\terrors.push(`${change.path}: No content to restore`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t// Remove the change from tracking\n\t\t\t\tconst idx = this.state.changes.findIndex((c) => c.id === change.id);\n\t\t\t\tif (idx !== -1) {\n\t\t\t\t\tthis.state.changes.splice(idx, 1);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\terrors.push(\n\t\t\t\t\t`${change.path}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t);\n\t\t\t\tskipped++;\n\t\t\t}\n\t\t}\n\n\t\tlogger.info(\"Undo complete\", { undone, skipped, errors: errors.length });\n\n\t\treturn { undone, skipped, errors };\n\t}\n\n\t/**\n\t * Create a named checkpoint.\n\t */\n\tcreateCheckpoint(name: string, description?: string): Checkpoint {\n\t\tconst lastChange = this.state.changes[this.state.changes.length - 1];\n\t\tconst checkpoint: Checkpoint = {\n\t\t\tname,\n\t\t\tdescription,\n\t\t\ttimestamp: Date.now(),\n\t\t\tchangeId: lastChange?.id ?? \"\",\n\t\t\tchangeCount: this.state.changes.length,\n\t\t};\n\n\t\t// Remove existing checkpoint with same name\n\t\tthis.state.checkpoints = this.state.checkpoints.filter(\n\t\t\t(cp) => cp.name !== name,\n\t\t);\n\t\tthis.state.checkpoints.push(checkpoint);\n\n\t\tlogger.info(\"Checkpoint created\", {\n\t\t\tname,\n\t\t\tchangeCount: checkpoint.changeCount,\n\t\t});\n\n\t\treturn cloneCheckpoint(checkpoint);\n\t}\n\n\t/**\n\t * Get all checkpoints.\n\t */\n\tgetCheckpoints(): Checkpoint[] {\n\t\treturn this.state.checkpoints.map(cloneCheckpoint);\n\t}\n\n\t/**\n\t * Restore to a checkpoint (undo all changes after it).\n\t */\n\trestoreCheckpoint(\n\t\tname: string,\n\t\tforce = false,\n\t): {\n\t\tundone: number;\n\t\tskipped: number;\n\t\terrors: string[];\n\t} {\n\t\tconst checkpoint = this.state.checkpoints.find((cp) => cp.name === name);\n\t\tif (!checkpoint) {\n\t\t\treturn {\n\t\t\t\tundone: 0,\n\t\t\t\tskipped: 0,\n\t\t\t\terrors: [`Checkpoint not found: ${name}`],\n\t\t\t};\n\t\t}\n\n\t\t// Find how many changes to undo\n\t\tconst checkpointIdx = this.state.changes.findIndex(\n\t\t\t(c) => c.id === checkpoint.changeId,\n\t\t);\n\t\tconst changesToUndo =\n\t\t\tcheckpointIdx === -1\n\t\t\t\t? this.state.changes.length\n\t\t\t\t: this.state.changes.length - checkpointIdx - 1;\n\n\t\tif (changesToUndo <= 0) {\n\t\t\treturn { undone: 0, skipped: 0, errors: [] };\n\t\t}\n\n\t\treturn this.undo(changesToUndo, force);\n\t}\n\n\t/**\n\t * Clear all tracked changes.\n\t */\n\tclear(): void {\n\t\tthis.state.changes = [];\n\t\tthis.state.checkpoints = [];\n\t\tlogger.info(\"Change tracker cleared\");\n\t}\n\n\t/**\n\t * Get tracker statistics.\n\t */\n\tgetStats(): {\n\t\ttotalChanges: number;\n\t\tcheckpoints: number;\n\t\tbyTool: Record<string, number>;\n\t\tbyType: Record<ChangeType, number>;\n\t} {\n\t\tconst byTool: Record<string, number> = {};\n\t\tconst byType: Record<ChangeType, number> = {\n\t\t\tcreate: 0,\n\t\t\tmodify: 0,\n\t\t\tdelete: 0,\n\t\t};\n\n\t\tfor (const change of this.state.changes) {\n\t\t\tbyTool[change.toolName] = (byTool[change.toolName] ?? 0) + 1;\n\t\t\tbyType[change.type]++;\n\t\t}\n\n\t\treturn {\n\t\t\ttotalChanges: this.state.changes.length,\n\t\t\tcheckpoints: this.state.checkpoints.length,\n\t\t\tbyTool,\n\t\t\tbyType,\n\t\t};\n\t}\n\n\t/**\n\t * Export state for persistence.\n\t */\n\texport(): ChangeTrackerState {\n\t\treturn cloneState(this.state);\n\t}\n\n\t/**\n\t * Import state from persistence.\n\t */\n\timport(state: ChangeTrackerState): void {\n\t\tthis.state = cloneState(state);\n\t}\n}\n\n// Singleton instance for global tracking\nlet globalTracker: ChangeTracker | null = null;\n\nexport function getChangeTracker(): ChangeTracker {\n\tif (!globalTracker) {\n\t\tglobalTracker = new ChangeTracker();\n\t}\n\treturn globalTracker;\n}\n\nexport function resetChangeTracker(): void {\n\tglobalTracker = null;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tracker.js","sourceRoot":"","sources":["../../src/undo/tracker.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * File change tracker for undo/rollback functionality.\n *\n * Tracks file modifications made by tools during a session,\n * enabling granular undo operations beyond git.\n */\n\nimport { execSync } from \"node:child_process\";\nimport { existsSync, mkdirSync, readFileSync, unlinkSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { writeTextFileAtomic } from \"../utils/fs.js\";\nimport { createLogger } from \"../utils/logger.js\";\nimport { sanitizeWithStaticMask } from \"../utils/secret-redactor.js\";\nimport type {\n\tChangeTrackerState,\n\tChangeType,\n\tCheckpoint,\n\tFileChange,\n\tUndoPreview,\n} from \"./types.js\";\n\nconst logger = createLogger(\"undo:tracker\");\n\nconst DEFAULT_MAX_CHANGES = 100;\n\nfunction cloneChange(change: FileChange): FileChange {\n\treturn { ...change };\n}\n\nfunction cloneCheckpoint(checkpoint: Checkpoint): Checkpoint {\n\treturn { ...checkpoint };\n}\n\nfunction cloneState(state: ChangeTrackerState): ChangeTrackerState {\n\treturn {\n\t\tchanges: state.changes.map(cloneChange),\n\t\tcheckpoints: state.checkpoints.map(cloneCheckpoint),\n\t\tmaxChanges: state.maxChanges,\n\t};\n}\n\n/**\n * Generates a unique change ID.\n */\nfunction generateChangeId(): string {\n\treturn `chg_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\n/**\n * Check if a path is inside a git repository.\n */\nfunction isGitTracked(filePath: string): boolean {\n\ttry {\n\t\tconst dir = dirname(filePath);\n\t\texecSync(\"git rev-parse --git-dir\", {\n\t\t\tcwd: dir,\n\t\t\tstdio: \"ignore\",\n\t\t});\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * File change tracker for session-based undo.\n */\nexport class ChangeTracker {\n\tprivate state: ChangeTrackerState;\n\n\tconstructor(maxChanges = DEFAULT_MAX_CHANGES) {\n\t\tthis.state = {\n\t\t\tchanges: [],\n\t\t\tcheckpoints: [],\n\t\t\tmaxChanges,\n\t\t};\n\t}\n\n\t/**\n\t * Record a file change before it happens.\n\t * Call this BEFORE writing/modifying/deleting a file.\n\t */\n\trecordChange(\n\t\tpath: string,\n\t\ttype: ChangeType,\n\t\ttoolName: string,\n\t\ttoolCallId: string,\n\t\tmessageId?: string,\n\t): string {\n\t\t// Read current content before change\n\t\tlet before: string | null = null;\n\t\tif (type !== \"create\" && existsSync(path)) {\n\t\t\ttry {\n\t\t\t\tbefore = readFileSync(path, \"utf-8\");\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\"Failed to read file for undo tracking\", {\n\t\t\t\t\tpath,\n\t\t\t\t\terror: sanitizeWithStaticMask(\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t\t),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tconst change: FileChange = {\n\t\t\tid: generateChangeId(),\n\t\t\ttype,\n\t\t\tpath,\n\t\t\tbefore,\n\t\t\tafter: null, // Will be set after the change\n\t\t\ttoolName,\n\t\t\ttoolCallId,\n\t\t\ttimestamp: Date.now(),\n\t\t\tisGitTracked: isGitTracked(path),\n\t\t\tmessageId,\n\t\t};\n\n\t\tthis.state.changes.push(change);\n\n\t\t// Trim old changes if over limit\n\t\twhile (this.state.changes.length > this.state.maxChanges) {\n\t\t\tthis.state.changes.shift();\n\t\t}\n\n\t\tlogger.debug(\"Recorded change\", {\n\t\t\tid: change.id,\n\t\t\ttype,\n\t\t\tpath,\n\t\t\ttoolName,\n\t\t});\n\n\t\treturn change.id;\n\t}\n\n\t/**\n\t * Finalize a change after it completes.\n\t * Call this AFTER writing/modifying a file to capture the new content.\n\t */\n\tfinalizeChange(changeId: string): void {\n\t\tconst change = this.state.changes.find((c) => c.id === changeId);\n\t\tif (!change) {\n\t\t\tlogger.warn(\"Change not found for finalization\", { changeId });\n\t\t\treturn;\n\t\t}\n\n\t\tif (change.type !== \"delete\" && existsSync(change.path)) {\n\t\t\ttry {\n\t\t\t\tchange.after = readFileSync(change.path, \"utf-8\");\n\t\t\t} catch (error) {\n\t\t\t\tlogger.warn(\"Failed to read file after change\", {\n\t\t\t\t\tpath: change.path,\n\t\t\t\t\terror: sanitizeWithStaticMask(\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\t\t),\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get all changes, optionally filtered.\n\t */\n\tgetChanges(options?: {\n\t\ttoolName?: string;\n\t\tsinceTimestamp?: number;\n\t\tpath?: string;\n\t}): FileChange[] {\n\t\tlet changes = this.state.changes;\n\n\t\tif (options?.toolName) {\n\t\t\tchanges = changes.filter((c) => c.toolName === options.toolName);\n\t\t}\n\t\tif (options?.sinceTimestamp) {\n\t\t\tconst since = options.sinceTimestamp;\n\t\t\tchanges = changes.filter((c) => c.timestamp >= since);\n\t\t}\n\t\tif (options?.path) {\n\t\t\tchanges = changes.filter((c) => c.path === options.path);\n\t\t}\n\n\t\treturn changes.map(cloneChange);\n\t}\n\n\t/**\n\t * Get the last N changes.\n\t */\n\tgetLastChanges(count: number): FileChange[] {\n\t\treturn this.state.changes.slice(-count).map(cloneChange);\n\t}\n\n\t/**\n\t * Preview what would be undone.\n\t */\n\tpreviewUndo(count: number): UndoPreview {\n\t\tconst changesToUndo = this.getLastChanges(count).reverse();\n\t\tconst restores: UndoPreview[\"restores\"] = [];\n\t\tconst conflicts: UndoPreview[\"conflicts\"] = [];\n\n\t\tfor (const change of changesToUndo) {\n\t\t\t// Check for conflicts (file changed externally)\n\t\t\tif (existsSync(change.path)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst currentContent = readFileSync(change.path, \"utf-8\");\n\t\t\t\t\tif (change.after !== null && currentContent !== change.after) {\n\t\t\t\t\t\tconflicts.push({\n\t\t\t\t\t\t\tpath: change.path,\n\t\t\t\t\t\t\treason: \"File has been modified since the tracked change\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// File exists but can't be read\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tswitch (change.type) {\n\t\t\t\tcase \"create\":\n\t\t\t\t\trestores.push({ path: change.path, action: \"delete\" });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"modify\":\n\t\t\t\t\trestores.push({ path: change.path, action: \"restore\" });\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"delete\":\n\t\t\t\t\trestores.push({ path: change.path, action: \"recreate\" });\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tchanges: changesToUndo,\n\t\t\trestores,\n\t\t\tconflicts,\n\t\t};\n\t}\n\n\t/**\n\t * Undo the last N changes.\n\t * Returns the number of changes successfully undone.\n\t */\n\tundo(\n\t\tcount: number,\n\t\tforce = false,\n\t): {\n\t\tundone: number;\n\t\tskipped: number;\n\t\terrors: string[];\n\t} {\n\t\tconst preview = this.previewUndo(count);\n\t\tconst errors: string[] = [];\n\t\tlet undone = 0;\n\t\tlet skipped = 0;\n\n\t\tif (preview.conflicts.length > 0 && !force) {\n\t\t\treturn {\n\t\t\t\tundone: 0,\n\t\t\t\tskipped: preview.conflicts.length,\n\t\t\t\terrors: preview.conflicts.map((c) => `${c.path}: ${c.reason}`),\n\t\t\t};\n\t\t}\n\n\t\tconst changesToUndo = this.getLastChanges(count).reverse();\n\n\t\tfor (const change of changesToUndo) {\n\t\t\ttry {\n\t\t\t\tswitch (change.type) {\n\t\t\t\t\tcase \"create\":\n\t\t\t\t\t\t// Delete the created file\n\t\t\t\t\t\tif (existsSync(change.path)) {\n\t\t\t\t\t\t\tunlinkSync(change.path);\n\t\t\t\t\t\t\tundone++;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tskipped++;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"modify\":\n\t\t\t\t\t\t// Restore previous content\n\t\t\t\t\t\tif (change.before !== null) {\n\t\t\t\t\t\t\twriteTextFileAtomic(change.path, change.before, {\n\t\t\t\t\t\t\t\tencoding: \"utf-8\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tundone++;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tskipped++;\n\t\t\t\t\t\t\terrors.push(`${change.path}: No previous content recorded`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tcase \"delete\":\n\t\t\t\t\t\t// Recreate the deleted file\n\t\t\t\t\t\tif (change.before !== null) {\n\t\t\t\t\t\t\tmkdirSync(dirname(change.path), { recursive: true });\n\t\t\t\t\t\t\twriteTextFileAtomic(change.path, change.before, {\n\t\t\t\t\t\t\t\tencoding: \"utf-8\",\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\tundone++;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tskipped++;\n\t\t\t\t\t\t\terrors.push(`${change.path}: No content to restore`);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\t// Remove the change from tracking\n\t\t\t\tconst idx = this.state.changes.findIndex((c) => c.id === change.id);\n\t\t\t\tif (idx !== -1) {\n\t\t\t\t\tthis.state.changes.splice(idx, 1);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\terrors.push(\n\t\t\t\t\t`${change.path}: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t\t);\n\t\t\t\tskipped++;\n\t\t\t}\n\t\t}\n\n\t\tlogger.info(\"Undo complete\", { undone, skipped, errors: errors.length });\n\n\t\treturn { undone, skipped, errors };\n\t}\n\n\t/**\n\t * Create a named checkpoint.\n\t */\n\tcreateCheckpoint(name: string, description?: string): Checkpoint {\n\t\tconst lastChange = this.state.changes[this.state.changes.length - 1];\n\t\tconst checkpoint: Checkpoint = {\n\t\t\tname,\n\t\t\tdescription,\n\t\t\ttimestamp: Date.now(),\n\t\t\tchangeId: lastChange?.id ?? \"\",\n\t\t\tchangeCount: this.state.changes.length,\n\t\t};\n\n\t\t// Remove existing checkpoint with same name\n\t\tthis.state.checkpoints = this.state.checkpoints.filter(\n\t\t\t(cp) => cp.name !== name,\n\t\t);\n\t\tthis.state.checkpoints.push(checkpoint);\n\n\t\tlogger.info(\"Checkpoint created\", {\n\t\t\tname,\n\t\t\tchangeCount: checkpoint.changeCount,\n\t\t});\n\n\t\treturn cloneCheckpoint(checkpoint);\n\t}\n\n\t/**\n\t * Get all checkpoints.\n\t */\n\tgetCheckpoints(): Checkpoint[] {\n\t\treturn this.state.checkpoints.map(cloneCheckpoint);\n\t}\n\n\t/**\n\t * Restore to a checkpoint (undo all changes after it).\n\t */\n\trestoreCheckpoint(\n\t\tname: string,\n\t\tforce = false,\n\t): {\n\t\tundone: number;\n\t\tskipped: number;\n\t\terrors: string[];\n\t} {\n\t\tconst checkpoint = this.state.checkpoints.find((cp) => cp.name === name);\n\t\tif (!checkpoint) {\n\t\t\treturn {\n\t\t\t\tundone: 0,\n\t\t\t\tskipped: 0,\n\t\t\t\terrors: [`Checkpoint not found: ${name}`],\n\t\t\t};\n\t\t}\n\n\t\t// Find how many changes to undo\n\t\tconst checkpointIdx = this.state.changes.findIndex(\n\t\t\t(c) => c.id === checkpoint.changeId,\n\t\t);\n\t\tconst changesToUndo =\n\t\t\tcheckpointIdx === -1\n\t\t\t\t? this.state.changes.length\n\t\t\t\t: this.state.changes.length - checkpointIdx - 1;\n\n\t\tif (changesToUndo <= 0) {\n\t\t\treturn { undone: 0, skipped: 0, errors: [] };\n\t\t}\n\n\t\treturn this.undo(changesToUndo, force);\n\t}\n\n\t/**\n\t * Delete a checkpoint by name.\n\t * @returns true if a checkpoint was removed, false if none matched.\n\t */\n\tdeleteCheckpoint(name: string): boolean {\n\t\tconst before = this.state.checkpoints.length;\n\t\tthis.state.checkpoints = this.state.checkpoints.filter(\n\t\t\t(cp) => cp.name !== name,\n\t\t);\n\t\tconst removed = this.state.checkpoints.length < before;\n\t\tif (removed) {\n\t\t\tlogger.info(\"Checkpoint deleted\", { name });\n\t\t}\n\t\treturn removed;\n\t}\n\n\t/**\n\t * Clear all tracked changes.\n\t */\n\tclear(): void {\n\t\tthis.state.changes = [];\n\t\tthis.state.checkpoints = [];\n\t\tlogger.info(\"Change tracker cleared\");\n\t}\n\n\t/**\n\t * Get tracker statistics.\n\t */\n\tgetStats(): {\n\t\ttotalChanges: number;\n\t\tcheckpoints: number;\n\t\tbyTool: Record<string, number>;\n\t\tbyType: Record<ChangeType, number>;\n\t} {\n\t\tconst byTool: Record<string, number> = {};\n\t\tconst byType: Record<ChangeType, number> = {\n\t\t\tcreate: 0,\n\t\t\tmodify: 0,\n\t\t\tdelete: 0,\n\t\t};\n\n\t\tfor (const change of this.state.changes) {\n\t\t\tbyTool[change.toolName] = (byTool[change.toolName] ?? 0) + 1;\n\t\t\tbyType[change.type]++;\n\t\t}\n\n\t\treturn {\n\t\t\ttotalChanges: this.state.changes.length,\n\t\t\tcheckpoints: this.state.checkpoints.length,\n\t\t\tbyTool,\n\t\t\tbyType,\n\t\t};\n\t}\n\n\t/**\n\t * Export state for persistence.\n\t */\n\texport(): ChangeTrackerState {\n\t\treturn cloneState(this.state);\n\t}\n\n\t/**\n\t * Import state from persistence.\n\t */\n\timport(state: ChangeTrackerState): void {\n\t\tthis.state = cloneState(state);\n\t}\n}\n\n// Singleton instance for global tracking\nlet globalTracker: ChangeTracker | null = null;\n\nexport function getChangeTracker(): ChangeTracker {\n\tif (!globalTracker) {\n\t\tglobalTracker = new ChangeTracker();\n\t}\n\treturn globalTracker;\n}\n\nexport function resetChangeTracker(): void {\n\tglobalTracker = null;\n}\n"]}
|
package/dist/utils/fs.d.ts
CHANGED
|
@@ -63,7 +63,7 @@ export declare function readJsonFile<T = unknown>(path: string, options?: {
|
|
|
63
63
|
* Returns the rotated path on success, `null` if the source file
|
|
64
64
|
* didn't exist or the rotation failed.
|
|
65
65
|
*/
|
|
66
|
-
export declare function rotateCorruptJsonFile(path: string): string | null;
|
|
66
|
+
export declare function rotateCorruptJsonFile(path: string, expectedContent?: string): string | null;
|
|
67
67
|
/**
|
|
68
68
|
* Write JSON file with formatting and error handling
|
|
69
69
|
*/
|
package/dist/utils/fs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/utils/fs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAuBH,qBAAa,eAAgB,SAAQ,KAAK;aAGxB,IAAI,EAAE,MAAM;aACZ,SAAS,EAAE,MAAM;aACR,KAAK,CAAC,EAAE,KAAK;gBAHtC,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACR,KAAK,CAAC,EAAE,KAAK,YAAA;CAKvC;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOhD;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC3B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5D,MAAM,CAkCR;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,cAAc,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GAC/D,IAAI,CA+BN;AAED,iBAAS,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,cAAc,CAO/D;AAED,OAAO,EAAE,OAAO,EAAE,CAAC;AAEnB;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAG,OAAO,EACvC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;IAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAAO,GACzD,CAAC,
|
|
1
|
+
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/utils/fs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAuBH,qBAAa,eAAgB,SAAQ,KAAK;aAGxB,IAAI,EAAE,MAAM;aACZ,SAAS,EAAE,MAAM;aACR,KAAK,CAAC,EAAE,KAAK;gBAHtC,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACR,KAAK,CAAC,EAAE,KAAK,YAAA;CAKvC;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOhD;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC3B,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5D,MAAM,CAkCR;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,cAAc,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GAC/D,IAAI,CA+BN;AAED,iBAAS,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,cAAc,CAO/D;AAED,OAAO,EAAE,OAAO,EAAE,CAAC;AAEnB;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAG,OAAO,EACvC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;IAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAAO,GACzD,CAAC,CA4CH;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACpC,IAAI,EAAE,MAAM,EACZ,eAAe,CAAC,EAAE,MAAM,GACtB,MAAM,GAAG,IAAI,CAyCf;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,EACb,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GACtD,IAAI,CAiBN;AAED,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,OAAO,EACb,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GACtD,IAAI,CAEN;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAc5C;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC7B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,cAAc,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GAC/D,IAAI,CAkBN;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAClC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IACR,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACT,GACJ,IAAI,CAqCN"}
|
package/dist/utils/fs.js
CHANGED
|
@@ -143,7 +143,16 @@ export function readJsonFile(path, options = {}) {
|
|
|
143
143
|
error: result.error.message,
|
|
144
144
|
});
|
|
145
145
|
if (rotateOnParseFail) {
|
|
146
|
-
rotateCorruptJsonFile(path);
|
|
146
|
+
const rotatedPath = rotateCorruptJsonFile(path, content);
|
|
147
|
+
if (!rotatedPath) {
|
|
148
|
+
const repairedContent = readTextFile(path, { fallback: "" });
|
|
149
|
+
if (repairedContent !== "" && repairedContent !== content) {
|
|
150
|
+
const repaired = safeJsonParse(repairedContent, path);
|
|
151
|
+
if (repaired.success) {
|
|
152
|
+
return repaired.data;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
147
156
|
}
|
|
148
157
|
return fallback;
|
|
149
158
|
}
|
|
@@ -169,9 +178,27 @@ export function readJsonFile(path, options = {}) {
|
|
|
169
178
|
* Returns the rotated path on success, `null` if the source file
|
|
170
179
|
* didn't exist or the rotation failed.
|
|
171
180
|
*/
|
|
172
|
-
export function rotateCorruptJsonFile(path) {
|
|
181
|
+
export function rotateCorruptJsonFile(path, expectedContent) {
|
|
173
182
|
if (!fileExists(path))
|
|
174
183
|
return null;
|
|
184
|
+
if (expectedContent !== undefined) {
|
|
185
|
+
try {
|
|
186
|
+
const currentContent = readFileSync(path, "utf8");
|
|
187
|
+
if (currentContent !== expectedContent) {
|
|
188
|
+
logger.warn("Skipped corrupt JSON rotation; file changed after read", {
|
|
189
|
+
path,
|
|
190
|
+
});
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
logger.warn("Failed to re-read corrupt JSON before rotation", {
|
|
196
|
+
path,
|
|
197
|
+
error: error instanceof Error ? error.message : String(error),
|
|
198
|
+
});
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
175
202
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
176
203
|
// Append per-call randomness so two processes parsing the same
|
|
177
204
|
// corrupt file in the same millisecond produce different rotated
|
package/dist/utils/fs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/utils/fs.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EACN,SAAS,EACT,SAAS,EACT,UAAU,EACV,SAAS,EACT,SAAS,EACT,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,UAAU,EACV,aAAa,GACb,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,MAAM,MAAM,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC;AAExC,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACzC,YACC,OAAe,EACC,IAAY,EACZ,SAAiB,EACR,KAAa;QAEtC,KAAK,CAAC,OAAO,CAAC,CAAC;QAJC,SAAI,GAAJ,IAAI,CAAQ;QACZ,cAAS,GAAT,SAAS,CAAQ;QACR,UAAK,GAAL,KAAK,CAAQ;QAGtC,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAC/B,CAAC;CACD;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY;IACtC,IAAI,CAAC;QACJ,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,CAAC,KAAK,CAAC,6BAA6B,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC7D,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY;IAC5C,IAAI,CAAC;QACJ,MAAM,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAY;IAC5C,IAAI,CAAC;QACJ,MAAM,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAC3B,IAAY,EACZ,UAA4D,EAAE;IAE9D,MAAM,EAAE,QAAQ,GAAG,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IAEjD,IAAI,CAAC;QACJ,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC5B,OAAO,QAAQ,CAAC;YACjB,CAAC;YACD,MAAM,IAAI,eAAe,CAAC,mBAAmB,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QACpE,CAAC;QAED,OAAO,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACrC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,KAAK,YAAY,eAAe;YAAE,MAAM,KAAK,CAAC;QAElD,MAAM,OAAO,GAAG,IAAI,eAAe,CAClC,wBAAwB,IAAI,EAAE,EAC9B,IAAI,EACJ,MAAM,EACN,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAC1C,CAAC;QAEF,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC5B,MAAM,CAAC,IAAI,CAAC,kCAAkC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YACjE,OAAO,QAAQ,CAAC;QACjB,CAAC;QAED,MAAM,CAAC,KAAK,CACX,kBAAkB,EAClB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,EAC1C,EAAE,IAAI,EAAE,CACR,CAAC;QACF,MAAM,OAAO,CAAC;IACf,CAAC;AACF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAC5B,IAAY,EACZ,OAAe,EACf,UAA+D,EAAE;IAEjE,MAAM,EAAE,QAAQ,GAAG,OAAO,EAAE,UAAU,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAE1D,IAAI,CAAC;QACJ,IAAI,UAAU,EAAE,CAAC;YAChB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;YAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACrC,CAAC;QACF,CAAC;QAED,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,OAAO,GAAG,IAAI,eAAe,CAClC,yBAAyB,IAAI,EAAE,EAC/B,IAAI,EACJ,OAAO,EACP,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAC1C,CAAC;QACF,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,UAAU,KAAK,KAAK,EAAE,CAAC;YACvE,MAAM,CAAC,KAAK,CAAC,mBAAmB,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACpD,CAAC;aAAM,CAAC;YACP,MAAM,CAAC,KAAK,CACX,mBAAmB,EACnB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,EAC1C,EAAE,IAAI,EAAE,CACR,CAAC;QACH,CAAC;QACD,MAAM,OAAO,CAAC;IACf,CAAC;AACF,CAAC;AAED,SAAS,OAAO,CAAC,KAAc;IAC9B,OAAO,OAAO,CACb,KAAK;QACJ,OAAO,KAAK,KAAK,QAAQ;QACzB,MAAM,IAAI,KAAK;QACf,OAAQ,KAA+B,CAAC,IAAI,KAAK,QAAQ,CAC1D,CAAC;AACH,CAAC;AAED,OAAO,EAAE,OAAO,EAAE,CAAC;AAEnB;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,YAAY,CAC3B,IAAY,EACZ,UAAyD,EAAE;IAE3D,MAAM,EAAE,QAAQ,EAAE,iBAAiB,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;IAExD,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE;YAClC,QAAQ,EAAE,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS;SACjD,CAAC,CAAC;QAEH,IAAI,OAAO,KAAK,EAAE,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC9C,OAAO,QAAQ,CAAC;QACjB,CAAC;QAED,MAAM,MAAM,GAAG,aAAa,CAAI,OAAO,EAAE,IAAI,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACrB,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC5B,MAAM,CAAC,IAAI,CAAC,mCAAmC,EAAE;oBAChD,IAAI;oBACJ,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,OAAO;iBAC3B,CAAC,CAAC;gBACH,IAAI,iBAAiB,EAAE,CAAC;oBACvB,qBAAqB,CAAC,IAAI,CAAC,CAAC;gBAC7B,CAAC;gBACD,OAAO,QAAQ,CAAC;YACjB,CAAC;YACD,MAAM,MAAM,CAAC,KAAK,CAAC;QACpB,CAAC;QAED,OAAO,MAAM,CAAC,IAAI,CAAC;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC5B,MAAM,CAAC,IAAI,CAAC,0CAA0C,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YACzE,OAAO,QAAQ,CAAC;QACjB,CAAC;QACD,MAAM,KAAK,CAAC;IACb,CAAC;AACF,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY;IACjD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACjE,+DAA+D;IAC/D,iEAAiE;IACjE,8DAA8D;IAC9D,gEAAgE;IAChE,6BAA6B;IAC7B,MAAM,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC7C,MAAM,WAAW,GAAG,GAAG,IAAI,YAAY,SAAS,IAAI,KAAK,EAAE,CAAC;IAC5D,IAAI,CAAC;QACJ,UAAU,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,iDAAiD,EAAE;YAC9D,IAAI,EAAE,IAAI;YACV,EAAE,EAAE,WAAW;SACf,CAAC,CAAC;QACH,OAAO,WAAW,CAAC;IACpB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,CAAC,IAAI,CAAC,oCAAoC,EAAE;YACjD,IAAI;YACJ,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAC7D,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAC5B,IAAY,EACZ,IAAa,EACb,UAAsD,EAAE;IAExD,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAErD,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,MAAM;YACrB,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/B,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAE3B,mBAAmB,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,eAAe,CACxB,8BAA8B,IAAI,EAAE,EACpC,IAAI,EACJ,YAAY,EACZ,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAC1C,CAAC;IACH,CAAC;AACF,CAAC;AAED,MAAM,UAAU,mBAAmB,CAClC,IAAY,EACZ,IAAa,EACb,UAAsD,EAAE;IAExD,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;AACpC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACrC,IAAI,CAAC;QACJ,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACrC,MAAM,CAAC,KAAK,CAAC,mBAAmB,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,CAAC;IACF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,eAAe,CACxB,+BAA+B,IAAI,EAAE,EACrC,IAAI,EACJ,OAAO,EACP,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAC1C,CAAC;IACH,CAAC;AACF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAC7B,IAAY,EACZ,OAAe,EACf,UAA+D,EAAE;IAEjE,MAAM,EAAE,QAAQ,GAAG,OAAO,EAAE,UAAU,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAE1D,IAAI,CAAC;QACJ,IAAI,UAAU,EAAE,CAAC;YAChB,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1B,CAAC;QAED,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1E,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,IAAI,eAAe,CACxB,6BAA6B,IAAI,EAAE,EACnC,IAAI,EACJ,QAAQ,EACR,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAC1C,CAAC;IACH,CAAC;AACF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAClC,IAAY,EACZ,OAAe,EACf,UAKI,EAAE;IAEN,MAAM,EAAE,QAAQ,GAAG,OAAO,EAAE,UAAU,GAAG,IAAI,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IACxE,MAAM,QAAQ,GAAG,IAAI,CACpB,OAAO,CAAC,IAAI,CAAC,EACb,IAAI,QAAQ,CAAC,IAAI,CAAC,QAAQ,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CACvF,CAAC;IAEF,IAAI,CAAC;QACJ,IAAI,UAAU,EAAE,CAAC;YAChB,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1B,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC;QAC7D,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QACjE,IAAI,KAAK,EAAE,CAAC;YACX,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACpB,CAAC;QACD,uCAAuC;QACvC,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC3B,IAAI,KAAK,EAAE,CAAC;YACX,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9B,CAAC;IACF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,kCAAkC;QAClC,IAAI,CAAC;YACJ,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC1B,UAAU,CAAC,QAAQ,CAAC,CAAC;YACtB,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,wBAAwB;QACzB,CAAC;QACD,MAAM,IAAI,eAAe,CACxB,oCAAoC,IAAI,EAAE,EAC1C,IAAI,EACJ,cAAc,EACd,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAC1C,CAAC;IACH,CAAC;AACF,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY;IACrC,IAAI,CAAC;QACJ,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;YAAE,OAAO,SAAS,CAAC;QACxC,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,SAAS,CAAC;IAClB,CAAC;AACF,CAAC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC7B,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC;QACJ,SAAS,CAAC,EAAE,CAAC,CAAC;IACf,CAAC;YAAS,CAAC;QACV,SAAS,CAAC,EAAE,CAAC,CAAC;IACf,CAAC;AACF,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IAClC,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO;IACzC,IAAI,EAAsB,CAAC;IAC3B,IAAI,CAAC;QACJ,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QACzB,SAAS,CAAC,EAAE,CAAC,CAAC;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACzD,CAAC;YAAS,CAAC;QACV,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;YACtB,IAAI,CAAC;gBACJ,SAAS,CAAC,EAAE,CAAC,CAAC;YACf,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,MAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YAC5D,CAAC;QACF,CAAC;IACF,CAAC;AACF,CAAC","sourcesContent":["/**\n * Centralized file system utilities with consistent error handling.\n * Replaces scattered readFileSync/writeFileSync/existsSync calls.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport {\n\tconstants,\n\tcloseSync,\n\texistsSync,\n\tfsyncSync,\n\tmkdirSync,\n\topenSync,\n\treadFileSync,\n\trenameSync,\n\tstatSync,\n\tunlinkSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { access } from \"node:fs/promises\";\nimport { basename, dirname, join } from \"node:path\";\nimport { safeJsonParse, safeJsonStringify } from \"./json.js\";\nimport { createLogger } from \"./logger.js\";\n\nconst logger = createLogger(\"fs-utils\");\n\nexport class FileSystemError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly path: string,\n\t\tpublic readonly operation: string,\n\t\tpublic override readonly cause?: Error,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"FileSystemError\";\n\t}\n}\n\n/**\n * Safely check if a file exists\n */\nexport function fileExists(path: string): boolean {\n\ttry {\n\t\treturn existsSync(path);\n\t} catch (error) {\n\t\tlogger.debug(\"File existence check failed\", { path, error });\n\t\treturn false;\n\t}\n}\n\n/**\n * Safely check if a file is readable\n */\nexport async function isReadable(path: string): Promise<boolean> {\n\ttry {\n\t\tawait access(path, constants.R_OK);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Safely check if a file is writable\n */\nexport async function isWritable(path: string): Promise<boolean> {\n\ttry {\n\t\tawait access(path, constants.W_OK);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Read text file with error handling\n */\nexport function readTextFile(\n\tpath: string,\n\toptions: { encoding?: BufferEncoding; fallback?: string } = {},\n): string {\n\tconst { encoding = \"utf-8\", fallback } = options;\n\n\ttry {\n\t\tif (!fileExists(path)) {\n\t\t\tif (fallback !== undefined) {\n\t\t\t\treturn fallback;\n\t\t\t}\n\t\t\tthrow new FileSystemError(`File not found: ${path}`, path, \"read\");\n\t\t}\n\n\t\treturn readFileSync(path, encoding);\n\t} catch (error) {\n\t\tif (error instanceof FileSystemError) throw error;\n\n\t\tconst fsError = new FileSystemError(\n\t\t\t`Failed to read file: ${path}`,\n\t\t\tpath,\n\t\t\t\"read\",\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\n\t\tif (fallback !== undefined) {\n\t\t\tlogger.warn(\"File read failed, using fallback\", { path, error });\n\t\t\treturn fallback;\n\t\t}\n\n\t\tlogger.error(\n\t\t\t\"File read failed\",\n\t\t\terror instanceof Error ? error : undefined,\n\t\t\t{ path },\n\t\t);\n\t\tthrow fsError;\n\t}\n}\n\n/**\n * Write text file with error handling and automatic directory creation\n */\nexport function writeTextFile(\n\tpath: string,\n\tcontent: string,\n\toptions: { encoding?: BufferEncoding; createDirs?: boolean } = {},\n): void {\n\tconst { encoding = \"utf-8\", createDirs = true } = options;\n\n\ttry {\n\t\tif (createDirs) {\n\t\t\tconst dir = dirname(path);\n\t\t\tif (!fileExists(dir)) {\n\t\t\t\tmkdirSync(dir, { recursive: true });\n\t\t\t}\n\t\t}\n\n\t\twriteFileSync(path, content, encoding);\n\t\tlogger.debug(\"File written successfully\", { path, size: content.length });\n\t} catch (error) {\n\t\tconst fsError = new FileSystemError(\n\t\t\t`Failed to write file: ${path}`,\n\t\t\tpath,\n\t\t\t\"write\",\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t\tif (isErrno(error) && error.code === \"ENOENT\" && createDirs === false) {\n\t\t\tlogger.debug(\"File write failed\", { path, error });\n\t\t} else {\n\t\t\tlogger.error(\n\t\t\t\t\"File write failed\",\n\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t\t{ path },\n\t\t\t);\n\t\t}\n\t\tthrow fsError;\n\t}\n}\n\nfunction isErrno(error: unknown): error is NodeJS.ErrnoException {\n\treturn Boolean(\n\t\terror &&\n\t\t\ttypeof error === \"object\" &&\n\t\t\t\"code\" in error &&\n\t\t\ttypeof (error as NodeJS.ErrnoException).code === \"string\",\n\t);\n}\n\nexport { isErrno };\n\n/**\n * Read JSON file with parsing and error handling.\n *\n * When `rotateOnParseFail` is enabled, a file whose content fails to\n * parse as JSON is moved to a `<file>.corrupt.<iso-ts>` sibling\n * before the fallback is returned (#2631). This preserves forensic\n * evidence — instead of silently replacing user data with empty\n * state on the next write — and surfaces the bug in monitoring.\n *\n * The rotation does NOT fire on \"file not present\" (returns fallback\n * directly) or on \"file is empty string\" (also returns fallback);\n * it only fires when bytes exist but don't parse.\n */\nexport function readJsonFile<T = unknown>(\n\tpath: string,\n\toptions: { fallback?: T; rotateOnParseFail?: boolean } = {},\n): T {\n\tconst { fallback, rotateOnParseFail = false } = options;\n\n\ttry {\n\t\tconst content = readTextFile(path, {\n\t\t\tfallback: fallback !== undefined ? \"\" : undefined,\n\t\t});\n\n\t\tif (content === \"\" && fallback !== undefined) {\n\t\t\treturn fallback;\n\t\t}\n\n\t\tconst result = safeJsonParse<T>(content, path);\n\t\tif (!result.success) {\n\t\t\tif (fallback !== undefined) {\n\t\t\t\tlogger.warn(\"JSON parse failed, using fallback\", {\n\t\t\t\t\tpath,\n\t\t\t\t\terror: result.error.message,\n\t\t\t\t});\n\t\t\t\tif (rotateOnParseFail) {\n\t\t\t\t\trotateCorruptJsonFile(path);\n\t\t\t\t}\n\t\t\t\treturn fallback;\n\t\t\t}\n\t\t\tthrow result.error;\n\t\t}\n\n\t\treturn result.data;\n\t} catch (error) {\n\t\tif (fallback !== undefined) {\n\t\t\tlogger.warn(\"Failed to read JSON file, using fallback\", { path, error });\n\t\t\treturn fallback;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Rename a corrupt JSON state file to `<file>.corrupt.<iso-ts>` so\n * subsequent writes create a fresh valid file while the corrupted\n * bytes are preserved for forensics (#2631). Best-effort: failures\n * are logged and swallowed because rotation is a hygiene step, not\n * a load-bearing operation.\n *\n * Returns the rotated path on success, `null` if the source file\n * didn't exist or the rotation failed.\n */\nexport function rotateCorruptJsonFile(path: string): string | null {\n\tif (!fileExists(path)) return null;\n\tconst timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n\t// Append per-call randomness so two processes parsing the same\n\t// corrupt file in the same millisecond produce different rotated\n\t// names. `renameSync` overwrites the destination on POSIX, so\n\t// without the random suffix the second rename would clobber the\n\t// first's forensic evidence.\n\tconst nonce = randomBytes(4).toString(\"hex\");\n\tconst rotatedPath = `${path}.corrupt.${timestamp}.${nonce}`;\n\ttry {\n\t\trenameSync(path, rotatedPath);\n\t\tlogger.warn(\"Rotated corrupt JSON file aside; starting fresh\", {\n\t\t\tfrom: path,\n\t\t\tto: rotatedPath,\n\t\t});\n\t\treturn rotatedPath;\n\t} catch (error) {\n\t\tlogger.warn(\"Failed to rotate corrupt JSON file\", {\n\t\t\tpath,\n\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t});\n\t\treturn null;\n\t}\n}\n\n/**\n * Write JSON file with formatting and error handling\n */\nexport function writeJsonFile(\n\tpath: string,\n\tdata: unknown,\n\toptions: { pretty?: boolean; createDirs?: boolean } = {},\n): void {\n\tconst { pretty = true, createDirs = true } = options;\n\n\ttry {\n\t\tconst content = pretty\n\t\t\t? JSON.stringify(data, null, 2)\n\t\t\t: safeJsonStringify(data);\n\n\t\twriteTextFileAtomic(path, content, { createDirs });\n\t} catch (error) {\n\t\tthrow new FileSystemError(\n\t\t\t`Failed to write JSON file: ${path}`,\n\t\t\tpath,\n\t\t\t\"write-json\",\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n}\n\nexport function writeJsonFileAtomic(\n\tpath: string,\n\tdata: unknown,\n\toptions: { pretty?: boolean; createDirs?: boolean } = {},\n): void {\n\twriteJsonFile(path, data, options);\n}\n\n/**\n * Ensure a directory exists, creating it if necessary\n */\nexport function ensureDir(path: string): void {\n\ttry {\n\t\tif (!fileExists(path)) {\n\t\t\tmkdirSync(path, { recursive: true });\n\t\t\tlogger.debug(\"Directory created\", { path });\n\t\t}\n\t} catch (error) {\n\t\tthrow new FileSystemError(\n\t\t\t`Failed to create directory: ${path}`,\n\t\t\tpath,\n\t\t\t\"mkdir\",\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n}\n\n/**\n * Safely append to a file\n */\nexport function appendTextFile(\n\tpath: string,\n\tcontent: string,\n\toptions: { encoding?: BufferEncoding; createDirs?: boolean } = {},\n): void {\n\tconst { encoding = \"utf-8\", createDirs = true } = options;\n\n\ttry {\n\t\tif (createDirs) {\n\t\t\tensureDir(dirname(path));\n\t\t}\n\n\t\tconst existing = fileExists(path) ? readTextFile(path, { encoding }) : \"\";\n\t\twriteTextFile(path, existing + content, { encoding, createDirs: false });\n\t} catch (error) {\n\t\tthrow new FileSystemError(\n\t\t\t`Failed to append to file: ${path}`,\n\t\t\tpath,\n\t\t\t\"append\",\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n}\n\n/**\n * Atomic write - write to temp file then rename\n */\nexport function writeTextFileAtomic(\n\tpath: string,\n\tcontent: string,\n\toptions: {\n\t\tencoding?: BufferEncoding;\n\t\tcreateDirs?: boolean;\n\t\tfsync?: boolean;\n\t\tmode?: number;\n\t} = {},\n): void {\n\tconst { encoding = \"utf-8\", createDirs = true, fsync = true } = options;\n\tconst tempPath = join(\n\t\tdirname(path),\n\t\t`.${basename(path)}.tmp.${process.pid}.${Date.now()}.${randomBytes(6).toString(\"hex\")}`,\n\t);\n\n\ttry {\n\t\tif (createDirs) {\n\t\t\tensureDir(dirname(path));\n\t\t}\n\t\tconst mode = options.mode ?? existingFileMode(path) ?? 0o600;\n\t\twriteFileSync(tempPath, content, { encoding, flag: \"wx\", mode });\n\t\tif (fsync) {\n\t\t\tsyncFile(tempPath);\n\t\t}\n\t\t// Rename is atomic on most filesystems\n\t\trenameSync(tempPath, path);\n\t\tif (fsync) {\n\t\t\tsyncDirectory(dirname(path));\n\t\t}\n\t} catch (error) {\n\t\t// Clean up temp file if it exists\n\t\ttry {\n\t\t\tif (fileExists(tempPath)) {\n\t\t\t\tunlinkSync(tempPath);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore cleanup errors\n\t\t}\n\t\tthrow new FileSystemError(\n\t\t\t`Failed to write file atomically: ${path}`,\n\t\t\tpath,\n\t\t\t\"write-atomic\",\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n}\n\nfunction existingFileMode(path: string): number | undefined {\n\ttry {\n\t\tif (!fileExists(path)) return undefined;\n\t\treturn statSync(path).mode & 0o777;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction syncFile(path: string): void {\n\tconst fd = openSync(path, \"r+\");\n\ttry {\n\t\tfsyncSync(fd);\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n}\n\nfunction syncDirectory(path: string): void {\n\tif (process.platform === \"win32\") return;\n\tlet fd: number | undefined;\n\ttry {\n\t\tfd = openSync(path, \"r\");\n\t\tfsyncSync(fd);\n\t} catch (error) {\n\t\tlogger.debug(\"Directory fsync failed\", { path, error });\n\t} finally {\n\t\tif (fd !== undefined) {\n\t\t\ttry {\n\t\t\t\tcloseSync(fd);\n\t\t\t} catch (error) {\n\t\t\t\tlogger.debug(\"Directory fd close failed\", { path, error });\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/utils/fs.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Centralized file system utilities with consistent error handling.\n * Replaces scattered readFileSync/writeFileSync/existsSync calls.\n */\n\nimport { randomBytes } from \"node:crypto\";\nimport {\n\tconstants,\n\tcloseSync,\n\texistsSync,\n\tfsyncSync,\n\tmkdirSync,\n\topenSync,\n\treadFileSync,\n\trenameSync,\n\tstatSync,\n\tunlinkSync,\n\twriteFileSync,\n} from \"node:fs\";\nimport { access } from \"node:fs/promises\";\nimport { basename, dirname, join } from \"node:path\";\nimport { safeJsonParse, safeJsonStringify } from \"./json.js\";\nimport { createLogger } from \"./logger.js\";\n\nconst logger = createLogger(\"fs-utils\");\n\nexport class FileSystemError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly path: string,\n\t\tpublic readonly operation: string,\n\t\tpublic override readonly cause?: Error,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"FileSystemError\";\n\t}\n}\n\n/**\n * Safely check if a file exists\n */\nexport function fileExists(path: string): boolean {\n\ttry {\n\t\treturn existsSync(path);\n\t} catch (error) {\n\t\tlogger.debug(\"File existence check failed\", { path, error });\n\t\treturn false;\n\t}\n}\n\n/**\n * Safely check if a file is readable\n */\nexport async function isReadable(path: string): Promise<boolean> {\n\ttry {\n\t\tawait access(path, constants.R_OK);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Safely check if a file is writable\n */\nexport async function isWritable(path: string): Promise<boolean> {\n\ttry {\n\t\tawait access(path, constants.W_OK);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Read text file with error handling\n */\nexport function readTextFile(\n\tpath: string,\n\toptions: { encoding?: BufferEncoding; fallback?: string } = {},\n): string {\n\tconst { encoding = \"utf-8\", fallback } = options;\n\n\ttry {\n\t\tif (!fileExists(path)) {\n\t\t\tif (fallback !== undefined) {\n\t\t\t\treturn fallback;\n\t\t\t}\n\t\t\tthrow new FileSystemError(`File not found: ${path}`, path, \"read\");\n\t\t}\n\n\t\treturn readFileSync(path, encoding);\n\t} catch (error) {\n\t\tif (error instanceof FileSystemError) throw error;\n\n\t\tconst fsError = new FileSystemError(\n\t\t\t`Failed to read file: ${path}`,\n\t\t\tpath,\n\t\t\t\"read\",\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\n\t\tif (fallback !== undefined) {\n\t\t\tlogger.warn(\"File read failed, using fallback\", { path, error });\n\t\t\treturn fallback;\n\t\t}\n\n\t\tlogger.error(\n\t\t\t\"File read failed\",\n\t\t\terror instanceof Error ? error : undefined,\n\t\t\t{ path },\n\t\t);\n\t\tthrow fsError;\n\t}\n}\n\n/**\n * Write text file with error handling and automatic directory creation\n */\nexport function writeTextFile(\n\tpath: string,\n\tcontent: string,\n\toptions: { encoding?: BufferEncoding; createDirs?: boolean } = {},\n): void {\n\tconst { encoding = \"utf-8\", createDirs = true } = options;\n\n\ttry {\n\t\tif (createDirs) {\n\t\t\tconst dir = dirname(path);\n\t\t\tif (!fileExists(dir)) {\n\t\t\t\tmkdirSync(dir, { recursive: true });\n\t\t\t}\n\t\t}\n\n\t\twriteFileSync(path, content, encoding);\n\t\tlogger.debug(\"File written successfully\", { path, size: content.length });\n\t} catch (error) {\n\t\tconst fsError = new FileSystemError(\n\t\t\t`Failed to write file: ${path}`,\n\t\t\tpath,\n\t\t\t\"write\",\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t\tif (isErrno(error) && error.code === \"ENOENT\" && createDirs === false) {\n\t\t\tlogger.debug(\"File write failed\", { path, error });\n\t\t} else {\n\t\t\tlogger.error(\n\t\t\t\t\"File write failed\",\n\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t\t{ path },\n\t\t\t);\n\t\t}\n\t\tthrow fsError;\n\t}\n}\n\nfunction isErrno(error: unknown): error is NodeJS.ErrnoException {\n\treturn Boolean(\n\t\terror &&\n\t\t\ttypeof error === \"object\" &&\n\t\t\t\"code\" in error &&\n\t\t\ttypeof (error as NodeJS.ErrnoException).code === \"string\",\n\t);\n}\n\nexport { isErrno };\n\n/**\n * Read JSON file with parsing and error handling.\n *\n * When `rotateOnParseFail` is enabled, a file whose content fails to\n * parse as JSON is moved to a `<file>.corrupt.<iso-ts>` sibling\n * before the fallback is returned (#2631). This preserves forensic\n * evidence — instead of silently replacing user data with empty\n * state on the next write — and surfaces the bug in monitoring.\n *\n * The rotation does NOT fire on \"file not present\" (returns fallback\n * directly) or on \"file is empty string\" (also returns fallback);\n * it only fires when bytes exist but don't parse.\n */\nexport function readJsonFile<T = unknown>(\n\tpath: string,\n\toptions: { fallback?: T; rotateOnParseFail?: boolean } = {},\n): T {\n\tconst { fallback, rotateOnParseFail = false } = options;\n\n\ttry {\n\t\tconst content = readTextFile(path, {\n\t\t\tfallback: fallback !== undefined ? \"\" : undefined,\n\t\t});\n\n\t\tif (content === \"\" && fallback !== undefined) {\n\t\t\treturn fallback;\n\t\t}\n\n\t\tconst result = safeJsonParse<T>(content, path);\n\t\tif (!result.success) {\n\t\t\tif (fallback !== undefined) {\n\t\t\t\tlogger.warn(\"JSON parse failed, using fallback\", {\n\t\t\t\t\tpath,\n\t\t\t\t\terror: result.error.message,\n\t\t\t\t});\n\t\t\t\tif (rotateOnParseFail) {\n\t\t\t\t\tconst rotatedPath = rotateCorruptJsonFile(path, content);\n\t\t\t\t\tif (!rotatedPath) {\n\t\t\t\t\t\tconst repairedContent = readTextFile(path, { fallback: \"\" });\n\t\t\t\t\t\tif (repairedContent !== \"\" && repairedContent !== content) {\n\t\t\t\t\t\t\tconst repaired = safeJsonParse<T>(repairedContent, path);\n\t\t\t\t\t\t\tif (repaired.success) {\n\t\t\t\t\t\t\t\treturn repaired.data;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn fallback;\n\t\t\t}\n\t\t\tthrow result.error;\n\t\t}\n\n\t\treturn result.data;\n\t} catch (error) {\n\t\tif (fallback !== undefined) {\n\t\t\tlogger.warn(\"Failed to read JSON file, using fallback\", { path, error });\n\t\t\treturn fallback;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Rename a corrupt JSON state file to `<file>.corrupt.<iso-ts>` so\n * subsequent writes create a fresh valid file while the corrupted\n * bytes are preserved for forensics (#2631). Best-effort: failures\n * are logged and swallowed because rotation is a hygiene step, not\n * a load-bearing operation.\n *\n * Returns the rotated path on success, `null` if the source file\n * didn't exist or the rotation failed.\n */\nexport function rotateCorruptJsonFile(\n\tpath: string,\n\texpectedContent?: string,\n): string | null {\n\tif (!fileExists(path)) return null;\n\tif (expectedContent !== undefined) {\n\t\ttry {\n\t\t\tconst currentContent = readFileSync(path, \"utf8\");\n\t\t\tif (currentContent !== expectedContent) {\n\t\t\t\tlogger.warn(\"Skipped corrupt JSON rotation; file changed after read\", {\n\t\t\t\t\tpath,\n\t\t\t\t});\n\t\t\t\treturn null;\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tlogger.warn(\"Failed to re-read corrupt JSON before rotation\", {\n\t\t\t\tpath,\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t});\n\t\t\treturn null;\n\t\t}\n\t}\n\tconst timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n\t// Append per-call randomness so two processes parsing the same\n\t// corrupt file in the same millisecond produce different rotated\n\t// names. `renameSync` overwrites the destination on POSIX, so\n\t// without the random suffix the second rename would clobber the\n\t// first's forensic evidence.\n\tconst nonce = randomBytes(4).toString(\"hex\");\n\tconst rotatedPath = `${path}.corrupt.${timestamp}.${nonce}`;\n\ttry {\n\t\trenameSync(path, rotatedPath);\n\t\tlogger.warn(\"Rotated corrupt JSON file aside; starting fresh\", {\n\t\t\tfrom: path,\n\t\t\tto: rotatedPath,\n\t\t});\n\t\treturn rotatedPath;\n\t} catch (error) {\n\t\tlogger.warn(\"Failed to rotate corrupt JSON file\", {\n\t\t\tpath,\n\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t});\n\t\treturn null;\n\t}\n}\n\n/**\n * Write JSON file with formatting and error handling\n */\nexport function writeJsonFile(\n\tpath: string,\n\tdata: unknown,\n\toptions: { pretty?: boolean; createDirs?: boolean } = {},\n): void {\n\tconst { pretty = true, createDirs = true } = options;\n\n\ttry {\n\t\tconst content = pretty\n\t\t\t? JSON.stringify(data, null, 2)\n\t\t\t: safeJsonStringify(data);\n\n\t\twriteTextFileAtomic(path, content, { createDirs });\n\t} catch (error) {\n\t\tthrow new FileSystemError(\n\t\t\t`Failed to write JSON file: ${path}`,\n\t\t\tpath,\n\t\t\t\"write-json\",\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n}\n\nexport function writeJsonFileAtomic(\n\tpath: string,\n\tdata: unknown,\n\toptions: { pretty?: boolean; createDirs?: boolean } = {},\n): void {\n\twriteJsonFile(path, data, options);\n}\n\n/**\n * Ensure a directory exists, creating it if necessary\n */\nexport function ensureDir(path: string): void {\n\ttry {\n\t\tif (!fileExists(path)) {\n\t\t\tmkdirSync(path, { recursive: true });\n\t\t\tlogger.debug(\"Directory created\", { path });\n\t\t}\n\t} catch (error) {\n\t\tthrow new FileSystemError(\n\t\t\t`Failed to create directory: ${path}`,\n\t\t\tpath,\n\t\t\t\"mkdir\",\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n}\n\n/**\n * Safely append to a file\n */\nexport function appendTextFile(\n\tpath: string,\n\tcontent: string,\n\toptions: { encoding?: BufferEncoding; createDirs?: boolean } = {},\n): void {\n\tconst { encoding = \"utf-8\", createDirs = true } = options;\n\n\ttry {\n\t\tif (createDirs) {\n\t\t\tensureDir(dirname(path));\n\t\t}\n\n\t\tconst existing = fileExists(path) ? readTextFile(path, { encoding }) : \"\";\n\t\twriteTextFile(path, existing + content, { encoding, createDirs: false });\n\t} catch (error) {\n\t\tthrow new FileSystemError(\n\t\t\t`Failed to append to file: ${path}`,\n\t\t\tpath,\n\t\t\t\"append\",\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n}\n\n/**\n * Atomic write - write to temp file then rename\n */\nexport function writeTextFileAtomic(\n\tpath: string,\n\tcontent: string,\n\toptions: {\n\t\tencoding?: BufferEncoding;\n\t\tcreateDirs?: boolean;\n\t\tfsync?: boolean;\n\t\tmode?: number;\n\t} = {},\n): void {\n\tconst { encoding = \"utf-8\", createDirs = true, fsync = true } = options;\n\tconst tempPath = join(\n\t\tdirname(path),\n\t\t`.${basename(path)}.tmp.${process.pid}.${Date.now()}.${randomBytes(6).toString(\"hex\")}`,\n\t);\n\n\ttry {\n\t\tif (createDirs) {\n\t\t\tensureDir(dirname(path));\n\t\t}\n\t\tconst mode = options.mode ?? existingFileMode(path) ?? 0o600;\n\t\twriteFileSync(tempPath, content, { encoding, flag: \"wx\", mode });\n\t\tif (fsync) {\n\t\t\tsyncFile(tempPath);\n\t\t}\n\t\t// Rename is atomic on most filesystems\n\t\trenameSync(tempPath, path);\n\t\tif (fsync) {\n\t\t\tsyncDirectory(dirname(path));\n\t\t}\n\t} catch (error) {\n\t\t// Clean up temp file if it exists\n\t\ttry {\n\t\t\tif (fileExists(tempPath)) {\n\t\t\t\tunlinkSync(tempPath);\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore cleanup errors\n\t\t}\n\t\tthrow new FileSystemError(\n\t\t\t`Failed to write file atomically: ${path}`,\n\t\t\tpath,\n\t\t\t\"write-atomic\",\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n}\n\nfunction existingFileMode(path: string): number | undefined {\n\ttry {\n\t\tif (!fileExists(path)) return undefined;\n\t\treturn statSync(path).mode & 0o777;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction syncFile(path: string): void {\n\tconst fd = openSync(path, \"r+\");\n\ttry {\n\t\tfsyncSync(fd);\n\t} finally {\n\t\tcloseSync(fd);\n\t}\n}\n\nfunction syncDirectory(path: string): void {\n\tif (process.platform === \"win32\") return;\n\tlet fd: number | undefined;\n\ttry {\n\t\tfd = openSync(path, \"r\");\n\t\tfsyncSync(fd);\n\t} catch (error) {\n\t\tlogger.debug(\"Directory fsync failed\", { path, error });\n\t} finally {\n\t\tif (fd !== undefined) {\n\t\t\ttry {\n\t\t\t\tcloseSync(fd);\n\t\t\t} catch (error) {\n\t\t\t\tlogger.debug(\"Directory fd close failed\", { path, error });\n\t\t\t}\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-stash.d.ts","sourceRoot":"","sources":["../../src/utils/git-stash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAOH;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,iDAAiD;IACjD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,6CAA6C;IAC7C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,8DAA8D;IAC9D,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,wDAAwD;IACxD,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B;
|
|
1
|
+
{"version":3,"file":"git-stash.d.ts","sourceRoot":"","sources":["../../src/utils/git-stash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAOH;;GAEG;AACH,MAAM,WAAW,UAAU;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,iDAAiD;IACjD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,6CAA6C;IAC7C,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,8DAA8D;IAC9D,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,wDAAwD;IACxD,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC9B;AA6DD;;GAEG;AACH,cAAM,eAAe;IACpB,OAAO,CAAC,cAAc,CAAmC;IACzD,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,MAAM,CAKZ;IAEF;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,IAAI;IAIhD;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAOrC;;OAEG;IACG,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAmFhE;;OAEG;IACG,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAkCrD;;OAEG;IACG,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA0BlD;;OAEG;IACG,eAAe,CAAC,CAAC,EACtB,aAAa,EAAE,MAAM,EACrB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAClB,OAAO,CAAC,CAAC,CAAC;IAsBb;;OAEG;IACH,kBAAkB,IAAI,UAAU,EAAE;IAOlC;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IA6BxE;;OAEG;IACH,OAAO,CAAC,cAAc;IAQtB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAUxB;;OAEG;YACW,YAAY;IAS1B;;OAEG;IACH,QAAQ,IAAI;QACX,iBAAiB,EAAE,MAAM,CAAC;QAC1B,mBAAmB,EAAE,MAAM,CAAC;KAC5B;IAcD;;OAEG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIrC;;OAEG;IACH,KAAK,IAAI,IAAI;CAIb;AAED;;GAEG;AACH,eAAO,MAAM,QAAQ,iBAAwB,CAAC;AAE9C;;GAEG;AACH,wBAAsB,eAAe,CAAC,CAAC,EACtC,aAAa,EAAE,MAAM,EACrB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAClB,OAAO,CAAC,CAAC,CAAC,CAEZ"}
|
package/dist/utils/git-stash.js
CHANGED
|
@@ -33,18 +33,25 @@
|
|
|
33
33
|
* });
|
|
34
34
|
* ```
|
|
35
35
|
*/
|
|
36
|
-
import {
|
|
36
|
+
import { spawnSync } from "node:child_process";
|
|
37
37
|
import { createLogger } from "./logger.js";
|
|
38
38
|
const logger = createLogger("utils:git-stash");
|
|
39
|
+
/**
|
|
40
|
+
* Run a git command, throwing on non-zero exit (mirrors execSync's contract)
|
|
41
|
+
* but via argv array form so interpolated values can never reach a shell.
|
|
42
|
+
*/
|
|
43
|
+
function runGit(args, cwd) {
|
|
44
|
+
const result = spawnSync("git", args, { cwd, stdio: "pipe" });
|
|
45
|
+
if (result.status !== 0) {
|
|
46
|
+
throw new Error(`git ${args.join(" ")} failed (exit ${result.status})`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
39
49
|
/**
|
|
40
50
|
* Check if current directory is a git repository
|
|
41
51
|
*/
|
|
42
52
|
function isGitRepo(cwd) {
|
|
43
53
|
try {
|
|
44
|
-
|
|
45
|
-
cwd,
|
|
46
|
-
stdio: "pipe",
|
|
47
|
-
});
|
|
54
|
+
runGit(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
48
55
|
return true;
|
|
49
56
|
}
|
|
50
57
|
catch {
|
|
@@ -133,7 +140,7 @@ class GitStashManager {
|
|
|
133
140
|
if (this.config.includeIgnored) {
|
|
134
141
|
args.push("--all");
|
|
135
142
|
}
|
|
136
|
-
|
|
143
|
+
runGit(args, cwd);
|
|
137
144
|
// Get the stash ref
|
|
138
145
|
const refResult = spawnSync("git", ["stash", "list", "-n", "1"], {
|
|
139
146
|
cwd,
|
|
@@ -193,7 +200,7 @@ class GitStashManager {
|
|
|
193
200
|
logger.warn("Stash ref not found in git", { stashId });
|
|
194
201
|
return false;
|
|
195
202
|
}
|
|
196
|
-
|
|
203
|
+
runGit(["stash", "pop", currentRef], cwd);
|
|
197
204
|
// Remove from tracking
|
|
198
205
|
this.removeStashEntry(stashId);
|
|
199
206
|
logger.info("Stash restored", { stashId, ref: currentRef });
|
|
@@ -217,7 +224,7 @@ class GitStashManager {
|
|
|
217
224
|
try {
|
|
218
225
|
const currentRef = await this.findStashRef(entry.message, cwd);
|
|
219
226
|
if (currentRef) {
|
|
220
|
-
|
|
227
|
+
runGit(["stash", "drop", currentRef], cwd);
|
|
221
228
|
}
|
|
222
229
|
this.removeStashEntry(stashId);
|
|
223
230
|
logger.debug("Stash dropped", { stashId });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-stash.js","sourceRoot":"","sources":["../../src/utils/git-stash.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Git Stash Integration\n *\n * Automatically stashes changes before risky operations and restores them\n * on failure, providing a safety net for destructive edits.\n *\n * ## Features\n *\n * - Auto-stash before bulk edits\n * - Restore on failure or user request\n * - Track stash history per session\n * - Integration with undo system\n *\n * ## Usage\n *\n * ```typescript\n * import { gitStash } from \"./git-stash.js\";\n *\n * // Auto-stash before risky operation\n * const stashId = await gitStash.createSafetyStash(\"Before refactoring\");\n *\n * try {\n * await riskyOperation();\n * await gitStash.dropStash(stashId);\n * } catch (error) {\n * await gitStash.restoreStash(stashId);\n * throw error;\n * }\n *\n * // Or use the wrapper\n * await gitStash.withSafetyStash(\"refactoring\", async () => {\n * await riskyOperation();\n * });\n * ```\n */\n\nimport { execSync, spawnSync } from \"node:child_process\";\nimport { createLogger } from \"./logger.js\";\n\nconst logger = createLogger(\"utils:git-stash\");\n\n/**\n * Stash entry record\n */\nexport interface StashEntry {\n\tid: string;\n\tmessage: string;\n\ttimestamp: number;\n\tref: string;\n\tfiles: string[];\n\tsessionId?: string;\n}\n\n/**\n * Git stash configuration\n */\nexport interface GitStashConfig {\n\t/** Working directory (default: process.cwd()) */\n\tcwd?: string;\n\t/** Include untracked files (default: true) */\n\tincludeUntracked?: boolean;\n\t/** Include ignored files (default: false) */\n\tincludeIgnored?: boolean;\n\t/** Auto-drop stash on successful operation (default: true) */\n\tautoDropOnSuccess?: boolean;\n\t/** Maximum stashes to keep per session (default: 10) */\n\tmaxStashesPerSession?: number;\n}\n\n/**\n * Check if current directory is a git repository\n */\nfunction isGitRepo(cwd: string): boolean {\n\ttry {\n\t\texecSync(\"git rev-parse --is-inside-work-tree\", {\n\t\t\tcwd,\n\t\t\tstdio: \"pipe\",\n\t\t});\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Check if there are changes to stash\n */\nfunction hasChanges(cwd: string, includeUntracked: boolean): boolean {\n\ttry {\n\t\tconst result = spawnSync(\n\t\t\t\"git\",\n\t\t\t[\"status\", \"--porcelain\", includeUntracked ? \"-u\" : \"-uno\"],\n\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t);\n\t\treturn (result.stdout || \"\").trim().length > 0;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Get list of changed files\n */\nfunction getChangedFiles(cwd: string, includeUntracked: boolean): string[] {\n\ttry {\n\t\tconst result = spawnSync(\n\t\t\t\"git\",\n\t\t\t[\"status\", \"--porcelain\", includeUntracked ? \"-u\" : \"-uno\"],\n\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t);\n\t\treturn (result.stdout || \"\")\n\t\t\t.trim()\n\t\t\t.split(\"\\n\")\n\t\t\t.filter((line) => line.length > 0)\n\t\t\t.map((line) => line.slice(3));\n\t} catch {\n\t\treturn [];\n\t}\n}\n\n/**\n * Git stash manager\n */\nclass GitStashManager {\n\tprivate sessionStashes = new Map<string, StashEntry[]>();\n\tprivate currentSessionId: string | null = null;\n\tprivate config: GitStashConfig = {\n\t\tincludeUntracked: true,\n\t\tincludeIgnored: false,\n\t\tautoDropOnSuccess: true,\n\t\tmaxStashesPerSession: 10,\n\t};\n\n\t/**\n\t * Configure the stash manager\n\t */\n\tconfigure(config: Partial<GitStashConfig>): void {\n\t\tthis.config = { ...this.config, ...config };\n\t}\n\n\t/**\n\t * Set current session ID\n\t */\n\tsetSessionId(sessionId: string): void {\n\t\tthis.currentSessionId = sessionId;\n\t\tif (!this.sessionStashes.has(sessionId)) {\n\t\t\tthis.sessionStashes.set(sessionId, []);\n\t\t}\n\t}\n\n\t/**\n\t * Create a safety stash before risky operations\n\t */\n\tasync createSafetyStash(message: string): Promise<string | null> {\n\t\tconst cwd = this.config.cwd || process.cwd();\n\n\t\tif (!isGitRepo(cwd)) {\n\t\t\tlogger.debug(\"Not a git repository, skipping stash\");\n\t\t\treturn null;\n\t\t}\n\n\t\tconst includeUntracked = this.config.includeUntracked ?? true;\n\t\tif (!hasChanges(cwd, includeUntracked)) {\n\t\t\tlogger.debug(\"No changes to stash\");\n\t\t\treturn null;\n\t\t}\n\n\t\tconst files = getChangedFiles(cwd, includeUntracked);\n\t\tconst timestamp = Date.now();\n\t\tconst stashMessage = `[composer-safety] ${message} - ${new Date(timestamp).toISOString()}`;\n\n\t\ttry {\n\t\t\tconst args = [\"stash\", \"push\", \"-m\", stashMessage];\n\t\t\tif (includeUntracked) {\n\t\t\t\targs.push(\"--include-untracked\");\n\t\t\t}\n\t\t\tif (this.config.includeIgnored) {\n\t\t\t\targs.push(\"--all\");\n\t\t\t}\n\n\t\t\texecSync(`git ${args.join(\" \")}`, { cwd, stdio: \"pipe\" });\n\n\t\t\t// Get the stash ref\n\t\t\tconst refResult = spawnSync(\"git\", [\"stash\", \"list\", \"-n\", \"1\"], {\n\t\t\t\tcwd,\n\t\t\t\tencoding: \"utf-8\",\n\t\t\t});\n\t\t\tconst refMatch = (refResult.stdout || \"\").match(/^(stash@\\{\\d+\\})/);\n\t\t\tconst ref = refMatch ? refMatch[1]! : \"stash@{0}\";\n\n\t\t\tconst stashId = `stash_${timestamp}`;\n\t\t\tconst entry: StashEntry = {\n\t\t\t\tid: stashId,\n\t\t\t\tmessage,\n\t\t\t\ttimestamp,\n\t\t\t\tref,\n\t\t\t\tfiles,\n\t\t\t\tsessionId: this.currentSessionId || undefined,\n\t\t\t};\n\n\t\t\t// Track in session\n\t\t\tif (this.currentSessionId) {\n\t\t\t\tconst sessionStashes =\n\t\t\t\t\tthis.sessionStashes.get(this.currentSessionId) || [];\n\t\t\t\tsessionStashes.push(entry);\n\t\t\t\tthis.sessionStashes.set(this.currentSessionId, sessionStashes);\n\n\t\t\t\t// Limit stashes per session\n\t\t\t\tconst maxStashes = this.config.maxStashesPerSession || 10;\n\t\t\t\tif (sessionStashes.length > maxStashes) {\n\t\t\t\t\tconst toRemove = sessionStashes.slice(\n\t\t\t\t\t\t0,\n\t\t\t\t\t\tsessionStashes.length - maxStashes,\n\t\t\t\t\t);\n\t\t\t\t\tfor (const old of toRemove) {\n\t\t\t\t\t\tawait this.dropStash(old.id);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlogger.info(\"Safety stash created\", {\n\t\t\t\tstashId,\n\t\t\t\tmessage,\n\t\t\t\tfiles: files.length,\n\t\t\t});\n\n\t\t\treturn stashId;\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t\"Failed to create stash\",\n\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Restore a stash by ID\n\t */\n\tasync restoreStash(stashId: string): Promise<boolean> {\n\t\tconst cwd = this.config.cwd || process.cwd();\n\t\tconst entry = this.findStashEntry(stashId);\n\n\t\tif (!entry) {\n\t\t\tlogger.warn(\"Stash not found\", { stashId });\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\t// Find current ref for this stash\n\t\t\tconst currentRef = await this.findStashRef(entry.message, cwd);\n\t\t\tif (!currentRef) {\n\t\t\t\tlogger.warn(\"Stash ref not found in git\", { stashId });\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\texecSync(`git stash pop ${currentRef}`, { cwd, stdio: \"pipe\" });\n\n\t\t\t// Remove from tracking\n\t\t\tthis.removeStashEntry(stashId);\n\n\t\t\tlogger.info(\"Stash restored\", { stashId, ref: currentRef });\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t\"Failed to restore stash\",\n\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t\t{ stashId },\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Drop a stash by ID\n\t */\n\tasync dropStash(stashId: string): Promise<boolean> {\n\t\tconst cwd = this.config.cwd || process.cwd();\n\t\tconst entry = this.findStashEntry(stashId);\n\n\t\tif (!entry) {\n\t\t\tlogger.debug(\"Stash not found, already dropped\", { stashId });\n\t\t\treturn true;\n\t\t}\n\n\t\ttry {\n\t\t\tconst currentRef = await this.findStashRef(entry.message, cwd);\n\t\t\tif (currentRef) {\n\t\t\t\texecSync(`git stash drop ${currentRef}`, { cwd, stdio: \"pipe\" });\n\t\t\t}\n\n\t\t\tthis.removeStashEntry(stashId);\n\n\t\t\tlogger.debug(\"Stash dropped\", { stashId });\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tlogger.warn(\"Failed to drop stash\", { stashId, error });\n\t\t\tthis.removeStashEntry(stashId);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Execute operation with safety stash\n\t */\n\tasync withSafetyStash<T>(\n\t\toperationName: string,\n\t\tfn: () => Promise<T>,\n\t): Promise<T> {\n\t\tconst stashId = await this.createSafetyStash(`Before ${operationName}`);\n\n\t\ttry {\n\t\t\tconst result = await fn();\n\n\t\t\t// Drop stash on success if configured\n\t\t\tif (stashId && this.config.autoDropOnSuccess) {\n\t\t\t\tawait this.dropStash(stashId);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\t// Restore stash on failure\n\t\t\tif (stashId) {\n\t\t\t\tlogger.info(\"Operation failed, restoring stash\", { stashId });\n\t\t\t\tawait this.restoreStash(stashId);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * List stashes for current session\n\t */\n\tlistSessionStashes(): StashEntry[] {\n\t\tif (!this.currentSessionId) {\n\t\t\treturn [];\n\t\t}\n\t\treturn this.sessionStashes.get(this.currentSessionId) || [];\n\t}\n\n\t/**\n\t * List all git stashes\n\t */\n\tasync listGitStashes(): Promise<Array<{ ref: string; message: string }>> {\n\t\tconst cwd = this.config.cwd || process.cwd();\n\n\t\tif (!isGitRepo(cwd)) {\n\t\t\treturn [];\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = spawnSync(\"git\", [\"stash\", \"list\"], {\n\t\t\t\tcwd,\n\t\t\t\tencoding: \"utf-8\",\n\t\t\t});\n\n\t\t\treturn (result.stdout || \"\")\n\t\t\t\t.trim()\n\t\t\t\t.split(\"\\n\")\n\t\t\t\t.filter((line) => line.length > 0)\n\t\t\t\t.map((line) => {\n\t\t\t\t\tconst match = line.match(/^(stash@\\{\\d+\\}):\\s*(.+)$/);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tref: match ? match[1]! : line,\n\t\t\t\t\t\tmessage: match ? match[2]! : line,\n\t\t\t\t\t};\n\t\t\t\t});\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Find stash entry by ID\n\t */\n\tprivate findStashEntry(stashId: string): StashEntry | undefined {\n\t\tfor (const stashes of Array.from(this.sessionStashes.values())) {\n\t\t\tconst entry = stashes.find((s) => s.id === stashId);\n\t\t\tif (entry) return entry;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Remove stash entry from tracking\n\t */\n\tprivate removeStashEntry(stashId: string): void {\n\t\tfor (const [sessionId, stashes] of Array.from(this.sessionStashes)) {\n\t\t\tconst filtered = stashes.filter((s) => s.id !== stashId);\n\t\t\tif (filtered.length !== stashes.length) {\n\t\t\t\tthis.sessionStashes.set(sessionId, filtered);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Find current git ref for a stash by message\n\t */\n\tprivate async findStashRef(\n\t\tmessage: string,\n\t\t_cwd: string,\n\t): Promise<string | null> {\n\t\tconst stashes = await this.listGitStashes();\n\t\tconst match = stashes.find((s) => s.message.includes(message));\n\t\treturn match?.ref || null;\n\t}\n\n\t/**\n\t * Get stash statistics\n\t */\n\tgetStats(): {\n\t\tsessionStashCount: number;\n\t\ttotalTrackedStashes: number;\n\t} {\n\t\tlet totalTracked = 0;\n\t\tfor (const stashes of Array.from(this.sessionStashes.values())) {\n\t\t\ttotalTracked += stashes.length;\n\t\t}\n\n\t\treturn {\n\t\t\tsessionStashCount: this.currentSessionId\n\t\t\t\t? this.sessionStashes.get(this.currentSessionId)?.length || 0\n\t\t\t\t: 0,\n\t\t\ttotalTrackedStashes: totalTracked,\n\t\t};\n\t}\n\n\t/**\n\t * Clear all tracked stashes for a session\n\t */\n\tclearSession(sessionId: string): void {\n\t\tthis.sessionStashes.delete(sessionId);\n\t}\n\n\t/**\n\t * Reset manager state\n\t */\n\treset(): void {\n\t\tthis.sessionStashes.clear();\n\t\tthis.currentSessionId = null;\n\t}\n}\n\n/**\n * Global git stash manager instance\n */\nexport const gitStash = new GitStashManager();\n\n/**\n * Quick wrapper for operations with safety stash\n */\nexport async function withSafetyStash<T>(\n\toperationName: string,\n\tfn: () => Promise<T>,\n): Promise<T> {\n\treturn gitStash.withSafetyStash(operationName, fn);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"git-stash.js","sourceRoot":"","sources":["../../src/utils/git-stash.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Git Stash Integration\n *\n * Automatically stashes changes before risky operations and restores them\n * on failure, providing a safety net for destructive edits.\n *\n * ## Features\n *\n * - Auto-stash before bulk edits\n * - Restore on failure or user request\n * - Track stash history per session\n * - Integration with undo system\n *\n * ## Usage\n *\n * ```typescript\n * import { gitStash } from \"./git-stash.js\";\n *\n * // Auto-stash before risky operation\n * const stashId = await gitStash.createSafetyStash(\"Before refactoring\");\n *\n * try {\n * await riskyOperation();\n * await gitStash.dropStash(stashId);\n * } catch (error) {\n * await gitStash.restoreStash(stashId);\n * throw error;\n * }\n *\n * // Or use the wrapper\n * await gitStash.withSafetyStash(\"refactoring\", async () => {\n * await riskyOperation();\n * });\n * ```\n */\n\nimport { spawnSync } from \"node:child_process\";\nimport { createLogger } from \"./logger.js\";\n\nconst logger = createLogger(\"utils:git-stash\");\n\n/**\n * Stash entry record\n */\nexport interface StashEntry {\n\tid: string;\n\tmessage: string;\n\ttimestamp: number;\n\tref: string;\n\tfiles: string[];\n\tsessionId?: string;\n}\n\n/**\n * Git stash configuration\n */\nexport interface GitStashConfig {\n\t/** Working directory (default: process.cwd()) */\n\tcwd?: string;\n\t/** Include untracked files (default: true) */\n\tincludeUntracked?: boolean;\n\t/** Include ignored files (default: false) */\n\tincludeIgnored?: boolean;\n\t/** Auto-drop stash on successful operation (default: true) */\n\tautoDropOnSuccess?: boolean;\n\t/** Maximum stashes to keep per session (default: 10) */\n\tmaxStashesPerSession?: number;\n}\n\n/**\n * Run a git command, throwing on non-zero exit (mirrors execSync's contract)\n * but via argv array form so interpolated values can never reach a shell.\n */\nfunction runGit(args: string[], cwd: string): void {\n\tconst result = spawnSync(\"git\", args, { cwd, stdio: \"pipe\" });\n\tif (result.status !== 0) {\n\t\tthrow new Error(`git ${args.join(\" \")} failed (exit ${result.status})`);\n\t}\n}\n\n/**\n * Check if current directory is a git repository\n */\nfunction isGitRepo(cwd: string): boolean {\n\ttry {\n\t\trunGit([\"rev-parse\", \"--is-inside-work-tree\"], cwd);\n\t\treturn true;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Check if there are changes to stash\n */\nfunction hasChanges(cwd: string, includeUntracked: boolean): boolean {\n\ttry {\n\t\tconst result = spawnSync(\n\t\t\t\"git\",\n\t\t\t[\"status\", \"--porcelain\", includeUntracked ? \"-u\" : \"-uno\"],\n\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t);\n\t\treturn (result.stdout || \"\").trim().length > 0;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Get list of changed files\n */\nfunction getChangedFiles(cwd: string, includeUntracked: boolean): string[] {\n\ttry {\n\t\tconst result = spawnSync(\n\t\t\t\"git\",\n\t\t\t[\"status\", \"--porcelain\", includeUntracked ? \"-u\" : \"-uno\"],\n\t\t\t{ cwd, encoding: \"utf-8\" },\n\t\t);\n\t\treturn (result.stdout || \"\")\n\t\t\t.trim()\n\t\t\t.split(\"\\n\")\n\t\t\t.filter((line) => line.length > 0)\n\t\t\t.map((line) => line.slice(3));\n\t} catch {\n\t\treturn [];\n\t}\n}\n\n/**\n * Git stash manager\n */\nclass GitStashManager {\n\tprivate sessionStashes = new Map<string, StashEntry[]>();\n\tprivate currentSessionId: string | null = null;\n\tprivate config: GitStashConfig = {\n\t\tincludeUntracked: true,\n\t\tincludeIgnored: false,\n\t\tautoDropOnSuccess: true,\n\t\tmaxStashesPerSession: 10,\n\t};\n\n\t/**\n\t * Configure the stash manager\n\t */\n\tconfigure(config: Partial<GitStashConfig>): void {\n\t\tthis.config = { ...this.config, ...config };\n\t}\n\n\t/**\n\t * Set current session ID\n\t */\n\tsetSessionId(sessionId: string): void {\n\t\tthis.currentSessionId = sessionId;\n\t\tif (!this.sessionStashes.has(sessionId)) {\n\t\t\tthis.sessionStashes.set(sessionId, []);\n\t\t}\n\t}\n\n\t/**\n\t * Create a safety stash before risky operations\n\t */\n\tasync createSafetyStash(message: string): Promise<string | null> {\n\t\tconst cwd = this.config.cwd || process.cwd();\n\n\t\tif (!isGitRepo(cwd)) {\n\t\t\tlogger.debug(\"Not a git repository, skipping stash\");\n\t\t\treturn null;\n\t\t}\n\n\t\tconst includeUntracked = this.config.includeUntracked ?? true;\n\t\tif (!hasChanges(cwd, includeUntracked)) {\n\t\t\tlogger.debug(\"No changes to stash\");\n\t\t\treturn null;\n\t\t}\n\n\t\tconst files = getChangedFiles(cwd, includeUntracked);\n\t\tconst timestamp = Date.now();\n\t\tconst stashMessage = `[composer-safety] ${message} - ${new Date(timestamp).toISOString()}`;\n\n\t\ttry {\n\t\t\tconst args = [\"stash\", \"push\", \"-m\", stashMessage];\n\t\t\tif (includeUntracked) {\n\t\t\t\targs.push(\"--include-untracked\");\n\t\t\t}\n\t\t\tif (this.config.includeIgnored) {\n\t\t\t\targs.push(\"--all\");\n\t\t\t}\n\n\t\t\trunGit(args, cwd);\n\n\t\t\t// Get the stash ref\n\t\t\tconst refResult = spawnSync(\"git\", [\"stash\", \"list\", \"-n\", \"1\"], {\n\t\t\t\tcwd,\n\t\t\t\tencoding: \"utf-8\",\n\t\t\t});\n\t\t\tconst refMatch = (refResult.stdout || \"\").match(/^(stash@\\{\\d+\\})/);\n\t\t\tconst ref = refMatch ? refMatch[1]! : \"stash@{0}\";\n\n\t\t\tconst stashId = `stash_${timestamp}`;\n\t\t\tconst entry: StashEntry = {\n\t\t\t\tid: stashId,\n\t\t\t\tmessage,\n\t\t\t\ttimestamp,\n\t\t\t\tref,\n\t\t\t\tfiles,\n\t\t\t\tsessionId: this.currentSessionId || undefined,\n\t\t\t};\n\n\t\t\t// Track in session\n\t\t\tif (this.currentSessionId) {\n\t\t\t\tconst sessionStashes =\n\t\t\t\t\tthis.sessionStashes.get(this.currentSessionId) || [];\n\t\t\t\tsessionStashes.push(entry);\n\t\t\t\tthis.sessionStashes.set(this.currentSessionId, sessionStashes);\n\n\t\t\t\t// Limit stashes per session\n\t\t\t\tconst maxStashes = this.config.maxStashesPerSession || 10;\n\t\t\t\tif (sessionStashes.length > maxStashes) {\n\t\t\t\t\tconst toRemove = sessionStashes.slice(\n\t\t\t\t\t\t0,\n\t\t\t\t\t\tsessionStashes.length - maxStashes,\n\t\t\t\t\t);\n\t\t\t\t\tfor (const old of toRemove) {\n\t\t\t\t\t\tawait this.dropStash(old.id);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlogger.info(\"Safety stash created\", {\n\t\t\t\tstashId,\n\t\t\t\tmessage,\n\t\t\t\tfiles: files.length,\n\t\t\t});\n\n\t\t\treturn stashId;\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t\"Failed to create stash\",\n\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t);\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Restore a stash by ID\n\t */\n\tasync restoreStash(stashId: string): Promise<boolean> {\n\t\tconst cwd = this.config.cwd || process.cwd();\n\t\tconst entry = this.findStashEntry(stashId);\n\n\t\tif (!entry) {\n\t\t\tlogger.warn(\"Stash not found\", { stashId });\n\t\t\treturn false;\n\t\t}\n\n\t\ttry {\n\t\t\t// Find current ref for this stash\n\t\t\tconst currentRef = await this.findStashRef(entry.message, cwd);\n\t\t\tif (!currentRef) {\n\t\t\t\tlogger.warn(\"Stash ref not found in git\", { stashId });\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\trunGit([\"stash\", \"pop\", currentRef], cwd);\n\n\t\t\t// Remove from tracking\n\t\t\tthis.removeStashEntry(stashId);\n\n\t\t\tlogger.info(\"Stash restored\", { stashId, ref: currentRef });\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tlogger.error(\n\t\t\t\t\"Failed to restore stash\",\n\t\t\t\terror instanceof Error ? error : undefined,\n\t\t\t\t{ stashId },\n\t\t\t);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Drop a stash by ID\n\t */\n\tasync dropStash(stashId: string): Promise<boolean> {\n\t\tconst cwd = this.config.cwd || process.cwd();\n\t\tconst entry = this.findStashEntry(stashId);\n\n\t\tif (!entry) {\n\t\t\tlogger.debug(\"Stash not found, already dropped\", { stashId });\n\t\t\treturn true;\n\t\t}\n\n\t\ttry {\n\t\t\tconst currentRef = await this.findStashRef(entry.message, cwd);\n\t\t\tif (currentRef) {\n\t\t\t\trunGit([\"stash\", \"drop\", currentRef], cwd);\n\t\t\t}\n\n\t\t\tthis.removeStashEntry(stashId);\n\n\t\t\tlogger.debug(\"Stash dropped\", { stashId });\n\t\t\treturn true;\n\t\t} catch (error) {\n\t\t\tlogger.warn(\"Failed to drop stash\", { stashId, error });\n\t\t\tthis.removeStashEntry(stashId);\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Execute operation with safety stash\n\t */\n\tasync withSafetyStash<T>(\n\t\toperationName: string,\n\t\tfn: () => Promise<T>,\n\t): Promise<T> {\n\t\tconst stashId = await this.createSafetyStash(`Before ${operationName}`);\n\n\t\ttry {\n\t\t\tconst result = await fn();\n\n\t\t\t// Drop stash on success if configured\n\t\t\tif (stashId && this.config.autoDropOnSuccess) {\n\t\t\t\tawait this.dropStash(stashId);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\t// Restore stash on failure\n\t\t\tif (stashId) {\n\t\t\t\tlogger.info(\"Operation failed, restoring stash\", { stashId });\n\t\t\t\tawait this.restoreStash(stashId);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * List stashes for current session\n\t */\n\tlistSessionStashes(): StashEntry[] {\n\t\tif (!this.currentSessionId) {\n\t\t\treturn [];\n\t\t}\n\t\treturn this.sessionStashes.get(this.currentSessionId) || [];\n\t}\n\n\t/**\n\t * List all git stashes\n\t */\n\tasync listGitStashes(): Promise<Array<{ ref: string; message: string }>> {\n\t\tconst cwd = this.config.cwd || process.cwd();\n\n\t\tif (!isGitRepo(cwd)) {\n\t\t\treturn [];\n\t\t}\n\n\t\ttry {\n\t\t\tconst result = spawnSync(\"git\", [\"stash\", \"list\"], {\n\t\t\t\tcwd,\n\t\t\t\tencoding: \"utf-8\",\n\t\t\t});\n\n\t\t\treturn (result.stdout || \"\")\n\t\t\t\t.trim()\n\t\t\t\t.split(\"\\n\")\n\t\t\t\t.filter((line) => line.length > 0)\n\t\t\t\t.map((line) => {\n\t\t\t\t\tconst match = line.match(/^(stash@\\{\\d+\\}):\\s*(.+)$/);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tref: match ? match[1]! : line,\n\t\t\t\t\t\tmessage: match ? match[2]! : line,\n\t\t\t\t\t};\n\t\t\t\t});\n\t\t} catch {\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Find stash entry by ID\n\t */\n\tprivate findStashEntry(stashId: string): StashEntry | undefined {\n\t\tfor (const stashes of Array.from(this.sessionStashes.values())) {\n\t\t\tconst entry = stashes.find((s) => s.id === stashId);\n\t\t\tif (entry) return entry;\n\t\t}\n\t\treturn undefined;\n\t}\n\n\t/**\n\t * Remove stash entry from tracking\n\t */\n\tprivate removeStashEntry(stashId: string): void {\n\t\tfor (const [sessionId, stashes] of Array.from(this.sessionStashes)) {\n\t\t\tconst filtered = stashes.filter((s) => s.id !== stashId);\n\t\t\tif (filtered.length !== stashes.length) {\n\t\t\t\tthis.sessionStashes.set(sessionId, filtered);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Find current git ref for a stash by message\n\t */\n\tprivate async findStashRef(\n\t\tmessage: string,\n\t\t_cwd: string,\n\t): Promise<string | null> {\n\t\tconst stashes = await this.listGitStashes();\n\t\tconst match = stashes.find((s) => s.message.includes(message));\n\t\treturn match?.ref || null;\n\t}\n\n\t/**\n\t * Get stash statistics\n\t */\n\tgetStats(): {\n\t\tsessionStashCount: number;\n\t\ttotalTrackedStashes: number;\n\t} {\n\t\tlet totalTracked = 0;\n\t\tfor (const stashes of Array.from(this.sessionStashes.values())) {\n\t\t\ttotalTracked += stashes.length;\n\t\t}\n\n\t\treturn {\n\t\t\tsessionStashCount: this.currentSessionId\n\t\t\t\t? this.sessionStashes.get(this.currentSessionId)?.length || 0\n\t\t\t\t: 0,\n\t\t\ttotalTrackedStashes: totalTracked,\n\t\t};\n\t}\n\n\t/**\n\t * Clear all tracked stashes for a session\n\t */\n\tclearSession(sessionId: string): void {\n\t\tthis.sessionStashes.delete(sessionId);\n\t}\n\n\t/**\n\t * Reset manager state\n\t */\n\treset(): void {\n\t\tthis.sessionStashes.clear();\n\t\tthis.currentSessionId = null;\n\t}\n}\n\n/**\n * Global git stash manager instance\n */\nexport const gitStash = new GitStashManager();\n\n/**\n * Quick wrapper for operations with safety stash\n */\nexport async function withSafetyStash<T>(\n\toperationName: string,\n\tfn: () => Promise<T>,\n): Promise<T> {\n\treturn gitStash.withSafetyStash(operationName, fn);\n}\n"]}
|
package/dist/utils/json.d.ts
CHANGED
|
@@ -53,6 +53,17 @@ export declare function parseJsonWithGuard<T>(input: string, guard: (data: unkno
|
|
|
53
53
|
success: false;
|
|
54
54
|
error: JsonParseError;
|
|
55
55
|
};
|
|
56
|
+
/**
|
|
57
|
+
* Type alias for a record with unknown values.
|
|
58
|
+
*/
|
|
59
|
+
export type UnknownRecord = Record<string, unknown>;
|
|
60
|
+
/**
|
|
61
|
+
* Type guard: is `value` a plain record (non-null object that is not an array)?
|
|
62
|
+
*
|
|
63
|
+
* Canonical home for `isRecord`; prefer importing from here over re-defining
|
|
64
|
+
* locally. (`isPlainObject` below is the same predicate, retained for callers.)
|
|
65
|
+
*/
|
|
66
|
+
export declare function isRecord(value: unknown): value is UnknownRecord;
|
|
56
67
|
/**
|
|
57
68
|
* Type guard for checking if a value is a plain object
|
|
58
69
|
*/
|
package/dist/utils/json.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/utils/json.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,qBAAa,cAAe,SAAQ,KAAK;aAGvB,KAAK,EAAE,MAAM;aACJ,KAAK,CAAC,EAAE,KAAK;gBAFtC,OAAO,EAAE,MAAM,EACC,KAAK,EAAE,MAAM,EACJ,KAAK,CAAC,EAAE,KAAK,YAAA;CAKvC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,GAAG,OAAO,EACxC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,MAAM,GACd;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,CAkBxE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CAM5D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAChC,KAAK,EAAE,OAAO,EACd,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GACnD,MAAM,CAoER;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,GAAG,OAAO,EACzC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GACpC,CAAC,EAAE,CAoBL;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAClC,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC,GAC1E,IAAI,IAAI,CAAC,CAwBX;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAMtE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EACnC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,IAAI,CAAC,EACnC,OAAO,CAAC,EAAE,MAAM,GACd;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,CAiBxE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC5B,KAAK,EAAE,OAAO,GACZ,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAElC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAC1B,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,IAAI,CAAC,GACjC,KAAK,IAAI,CAAC,EAAE,CAEd;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAExD;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAExD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,CAE1D"}
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/utils/json.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,qBAAa,cAAe,SAAQ,KAAK;aAGvB,KAAK,EAAE,MAAM;aACJ,KAAK,CAAC,EAAE,KAAK;gBAFtC,OAAO,EAAE,MAAM,EACC,KAAK,EAAE,MAAM,EACJ,KAAK,CAAC,EAAE,KAAK,YAAA;CAKvC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,GAAG,OAAO,EACxC,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,MAAM,GACd;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,CAkBxE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,CAM5D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAChC,KAAK,EAAE,OAAO,EACd,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GACnD,MAAM,CAoER;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,GAAG,OAAO,EACzC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;IAAE,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GACpC,CAAC,EAAE,CAoBL;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAClC,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC,GAC1E,IAAI,IAAI,CAAC,CAwBX;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAMtE;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EACnC,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,IAAI,CAAC,EACnC,OAAO,CAAC,EAAE,MAAM,GACd;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,CAiBxE;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEpD;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAE/D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC5B,KAAK,EAAE,OAAO,GACZ,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAElC;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAC1B,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,IAAI,CAAC,GACjC,KAAK,IAAI,CAAC,EAAE,CAEd;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAExD;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAExD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,CAE1D"}
|
package/dist/utils/json.js
CHANGED
|
@@ -184,6 +184,15 @@ export function parseJsonWithGuard(input, guard, context) {
|
|
|
184
184
|
}
|
|
185
185
|
return { success: true, data: result.data };
|
|
186
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Type guard: is `value` a plain record (non-null object that is not an array)?
|
|
189
|
+
*
|
|
190
|
+
* Canonical home for `isRecord`; prefer importing from here over re-defining
|
|
191
|
+
* locally. (`isPlainObject` below is the same predicate, retained for callers.)
|
|
192
|
+
*/
|
|
193
|
+
export function isRecord(value) {
|
|
194
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
195
|
+
}
|
|
187
196
|
/**
|
|
188
197
|
* Type guard for checking if a value is a plain object
|
|
189
198
|
*/
|
package/dist/utils/json.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/utils/json.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Safe JSON parsing utilities with better error handling and validation.\n */\n\nimport { createLogger } from \"./logger.js\";\n\nconst logger = createLogger(\"json-utils\");\n\nexport class JsonParseError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly input: string,\n\t\tpublic override readonly cause?: Error,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"JsonParseError\";\n\t}\n}\n\n/**\n * Safely parse JSON with detailed error reporting\n */\nexport function safeJsonParse<T = unknown>(\n\tinput: string,\n\tcontext?: string,\n): { success: true; data: T } | { success: false; error: JsonParseError } {\n\ttry {\n\t\tconst data = JSON.parse(input) as T;\n\t\treturn { success: true, data };\n\t} catch (error) {\n\t\tconst message = context\n\t\t\t? `Failed to parse JSON in ${context}`\n\t\t\t: \"Failed to parse JSON\";\n\n\t\tconst parseError = new JsonParseError(\n\t\t\tmessage,\n\t\t\tinput.slice(0, 200), // Limit stored input\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\n\t\tlogger.debug(message, { inputLength: input.length, error });\n\t\treturn { success: false, error: parseError };\n\t}\n}\n\n/**\n * Parse JSON with a fallback value\n */\nexport function parseJsonOr<T>(input: string, fallback: T): T {\n\ttry {\n\t\treturn JSON.parse(input) as T;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\n/**\n * Safely stringify with circular reference handling\n */\nexport function safeJsonStringify(\n\tvalue: unknown,\n\toptions: { pretty?: boolean; maxDepth?: number } = {},\n): string {\n\tconst { pretty = false, maxDepth = 10 } = options;\n\tconst seen = new WeakSet();\n\tlet depth = 0;\n\n\tconst replacer = (_key: string, val: unknown): unknown => {\n\t\t// Handle primitives\n\t\tif (val === null || typeof val !== \"object\") {\n\t\t\treturn val;\n\t\t}\n\n\t\t// Check depth limit\n\t\tif (depth >= maxDepth) {\n\t\t\treturn \"[Max Depth Reached]\";\n\t\t}\n\n\t\t// Handle circular references\n\t\tif (seen.has(val as object)) {\n\t\t\treturn \"[Circular Reference]\";\n\t\t}\n\n\t\tseen.add(val as object);\n\t\tdepth++;\n\t\ttry {\n\t\t\t// Handle special types\n\t\t\tif (val instanceof Error) {\n\t\t\t\treturn {\n\t\t\t\t\tname: val.name,\n\t\t\t\t\tmessage: val.message,\n\t\t\t\t\tstack: val.stack,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (val instanceof Map) {\n\t\t\t\treturn Object.fromEntries(val);\n\t\t\t}\n\n\t\t\tif (val instanceof Set) {\n\t\t\t\treturn Array.from(val);\n\t\t\t}\n\n\t\t\tif (val instanceof Date) {\n\t\t\t\treturn val.toISOString();\n\t\t\t}\n\n\t\t\tif (val instanceof RegExp) {\n\t\t\t\treturn val.toString();\n\t\t\t}\n\n\t\t\tif (ArrayBuffer.isView(val)) {\n\t\t\t\treturn \"[Binary Data]\";\n\t\t\t}\n\n\t\t\treturn val;\n\t\t} finally {\n\t\t\tdepth--;\n\t\t}\n\t};\n\n\ttry {\n\t\treturn JSON.stringify(value, replacer, pretty ? 2 : undefined);\n\t} catch (error) {\n\t\tlogger.error(\n\t\t\t\"Failed to stringify value\",\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t\treturn \"[Stringify Failed]\";\n\t}\n}\n\n/**\n * Parse JSONL (JSON Lines) format\n */\nexport function parseJsonLines<T = unknown>(\n\tinput: string,\n\toptions: { skipErrors?: boolean } = {},\n): T[] {\n\tconst { skipErrors = false } = options;\n\tconst results: T[] = [];\n\tconst lines = input.split(\"\\n\");\n\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tconst rawLine = lines[i];\n\t\tif (!rawLine) continue;\n\t\tconst line = rawLine.trim();\n\t\tif (!line) continue;\n\n\t\tconst result = safeJsonParse<T>(line, `line ${i + 1}`);\n\t\tif (result.success) {\n\t\t\tresults.push(result.data);\n\t\t} else if (!skipErrors) {\n\t\t\tthrow \"error\" in result ? result.error : new Error(\"Parse error\");\n\t\t}\n\t}\n\n\treturn results;\n}\n\n/**\n * Validate JSON against a schema using simple type checking\n */\nexport function validateJsonShape<T>(\n\tdata: unknown,\n\tschema: Record<string, \"string\" | \"number\" | \"boolean\" | \"object\" | \"array\">,\n): data is T {\n\tif (typeof data !== \"object\" || data === null) {\n\t\treturn false;\n\t}\n\n\tconst obj = data as Record<string, unknown>;\n\n\tfor (const [key, expectedType] of Object.entries(schema)) {\n\t\tconst value = obj[key];\n\n\t\tif (expectedType === \"array\") {\n\t\t\tif (!Array.isArray(value)) return false;\n\t\t} else if (expectedType === \"object\") {\n\t\t\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else {\n\t\t\t// string, number, or boolean\n\t\t\tconst valueType = typeof value;\n\t\t\tif (valueType !== expectedType) return false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/**\n * Try to parse JSON, returning undefined on failure.\n * Useful for simple cases where you don't need error details.\n */\nexport function tryParseJson<T = unknown>(input: string): T | undefined {\n\ttry {\n\t\treturn JSON.parse(input) as T;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Parse JSON with validation using a type guard.\n * Returns the parsed and validated data or an error.\n */\nexport function parseJsonWithGuard<T>(\n\tinput: string,\n\tguard: (data: unknown) => data is T,\n\tcontext?: string,\n): { success: true; data: T } | { success: false; error: JsonParseError } {\n\tconst result = safeJsonParse(input, context);\n\tif (!result.success) {\n\t\treturn result;\n\t}\n\n\tif (!guard(result.data)) {\n\t\tconst message = context\n\t\t\t? `Invalid data structure in ${context}`\n\t\t\t: \"Invalid data structure\";\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: new JsonParseError(message, input.slice(0, 200)),\n\t\t};\n\t}\n\n\treturn { success: true, data: result.data };\n}\n\n/**\n * Type guard for checking if a value is a plain object\n */\nexport function isPlainObject(\n\tvalue: unknown,\n): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Type guard for checking if a value is an array of a specific type\n */\nexport function isArrayOf<T>(\n\tvalue: unknown,\n\tguard: (item: unknown) => item is T,\n): value is T[] {\n\treturn Array.isArray(value) && value.every(guard);\n}\n\n/**\n * Type guard for string\n */\nexport function isString(value: unknown): value is string {\n\treturn typeof value === \"string\";\n}\n\n/**\n * Type guard for number\n */\nexport function isNumber(value: unknown): value is number {\n\treturn typeof value === \"number\" && !Number.isNaN(value);\n}\n\n/**\n * Type guard for boolean\n */\nexport function isBoolean(value: unknown): value is boolean {\n\treturn typeof value === \"boolean\";\n}\n"]}
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/utils/json.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Safe JSON parsing utilities with better error handling and validation.\n */\n\nimport { createLogger } from \"./logger.js\";\n\nconst logger = createLogger(\"json-utils\");\n\nexport class JsonParseError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic readonly input: string,\n\t\tpublic override readonly cause?: Error,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"JsonParseError\";\n\t}\n}\n\n/**\n * Safely parse JSON with detailed error reporting\n */\nexport function safeJsonParse<T = unknown>(\n\tinput: string,\n\tcontext?: string,\n): { success: true; data: T } | { success: false; error: JsonParseError } {\n\ttry {\n\t\tconst data = JSON.parse(input) as T;\n\t\treturn { success: true, data };\n\t} catch (error) {\n\t\tconst message = context\n\t\t\t? `Failed to parse JSON in ${context}`\n\t\t\t: \"Failed to parse JSON\";\n\n\t\tconst parseError = new JsonParseError(\n\t\t\tmessage,\n\t\t\tinput.slice(0, 200), // Limit stored input\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\n\t\tlogger.debug(message, { inputLength: input.length, error });\n\t\treturn { success: false, error: parseError };\n\t}\n}\n\n/**\n * Parse JSON with a fallback value\n */\nexport function parseJsonOr<T>(input: string, fallback: T): T {\n\ttry {\n\t\treturn JSON.parse(input) as T;\n\t} catch {\n\t\treturn fallback;\n\t}\n}\n\n/**\n * Safely stringify with circular reference handling\n */\nexport function safeJsonStringify(\n\tvalue: unknown,\n\toptions: { pretty?: boolean; maxDepth?: number } = {},\n): string {\n\tconst { pretty = false, maxDepth = 10 } = options;\n\tconst seen = new WeakSet();\n\tlet depth = 0;\n\n\tconst replacer = (_key: string, val: unknown): unknown => {\n\t\t// Handle primitives\n\t\tif (val === null || typeof val !== \"object\") {\n\t\t\treturn val;\n\t\t}\n\n\t\t// Check depth limit\n\t\tif (depth >= maxDepth) {\n\t\t\treturn \"[Max Depth Reached]\";\n\t\t}\n\n\t\t// Handle circular references\n\t\tif (seen.has(val as object)) {\n\t\t\treturn \"[Circular Reference]\";\n\t\t}\n\n\t\tseen.add(val as object);\n\t\tdepth++;\n\t\ttry {\n\t\t\t// Handle special types\n\t\t\tif (val instanceof Error) {\n\t\t\t\treturn {\n\t\t\t\t\tname: val.name,\n\t\t\t\t\tmessage: val.message,\n\t\t\t\t\tstack: val.stack,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tif (val instanceof Map) {\n\t\t\t\treturn Object.fromEntries(val);\n\t\t\t}\n\n\t\t\tif (val instanceof Set) {\n\t\t\t\treturn Array.from(val);\n\t\t\t}\n\n\t\t\tif (val instanceof Date) {\n\t\t\t\treturn val.toISOString();\n\t\t\t}\n\n\t\t\tif (val instanceof RegExp) {\n\t\t\t\treturn val.toString();\n\t\t\t}\n\n\t\t\tif (ArrayBuffer.isView(val)) {\n\t\t\t\treturn \"[Binary Data]\";\n\t\t\t}\n\n\t\t\treturn val;\n\t\t} finally {\n\t\t\tdepth--;\n\t\t}\n\t};\n\n\ttry {\n\t\treturn JSON.stringify(value, replacer, pretty ? 2 : undefined);\n\t} catch (error) {\n\t\tlogger.error(\n\t\t\t\"Failed to stringify value\",\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t\treturn \"[Stringify Failed]\";\n\t}\n}\n\n/**\n * Parse JSONL (JSON Lines) format\n */\nexport function parseJsonLines<T = unknown>(\n\tinput: string,\n\toptions: { skipErrors?: boolean } = {},\n): T[] {\n\tconst { skipErrors = false } = options;\n\tconst results: T[] = [];\n\tconst lines = input.split(\"\\n\");\n\n\tfor (let i = 0; i < lines.length; i++) {\n\t\tconst rawLine = lines[i];\n\t\tif (!rawLine) continue;\n\t\tconst line = rawLine.trim();\n\t\tif (!line) continue;\n\n\t\tconst result = safeJsonParse<T>(line, `line ${i + 1}`);\n\t\tif (result.success) {\n\t\t\tresults.push(result.data);\n\t\t} else if (!skipErrors) {\n\t\t\tthrow \"error\" in result ? result.error : new Error(\"Parse error\");\n\t\t}\n\t}\n\n\treturn results;\n}\n\n/**\n * Validate JSON against a schema using simple type checking\n */\nexport function validateJsonShape<T>(\n\tdata: unknown,\n\tschema: Record<string, \"string\" | \"number\" | \"boolean\" | \"object\" | \"array\">,\n): data is T {\n\tif (typeof data !== \"object\" || data === null) {\n\t\treturn false;\n\t}\n\n\tconst obj = data as Record<string, unknown>;\n\n\tfor (const [key, expectedType] of Object.entries(schema)) {\n\t\tconst value = obj[key];\n\n\t\tif (expectedType === \"array\") {\n\t\t\tif (!Array.isArray(value)) return false;\n\t\t} else if (expectedType === \"object\") {\n\t\t\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t} else {\n\t\t\t// string, number, or boolean\n\t\t\tconst valueType = typeof value;\n\t\t\tif (valueType !== expectedType) return false;\n\t\t}\n\t}\n\n\treturn true;\n}\n\n/**\n * Try to parse JSON, returning undefined on failure.\n * Useful for simple cases where you don't need error details.\n */\nexport function tryParseJson<T = unknown>(input: string): T | undefined {\n\ttry {\n\t\treturn JSON.parse(input) as T;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Parse JSON with validation using a type guard.\n * Returns the parsed and validated data or an error.\n */\nexport function parseJsonWithGuard<T>(\n\tinput: string,\n\tguard: (data: unknown) => data is T,\n\tcontext?: string,\n): { success: true; data: T } | { success: false; error: JsonParseError } {\n\tconst result = safeJsonParse(input, context);\n\tif (!result.success) {\n\t\treturn result;\n\t}\n\n\tif (!guard(result.data)) {\n\t\tconst message = context\n\t\t\t? `Invalid data structure in ${context}`\n\t\t\t: \"Invalid data structure\";\n\t\treturn {\n\t\t\tsuccess: false,\n\t\t\terror: new JsonParseError(message, input.slice(0, 200)),\n\t\t};\n\t}\n\n\treturn { success: true, data: result.data };\n}\n\n/**\n * Type alias for a record with unknown values.\n */\nexport type UnknownRecord = Record<string, unknown>;\n\n/**\n * Type guard: is `value` a plain record (non-null object that is not an array)?\n *\n * Canonical home for `isRecord`; prefer importing from here over re-defining\n * locally. (`isPlainObject` below is the same predicate, retained for callers.)\n */\nexport function isRecord(value: unknown): value is UnknownRecord {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Type guard for checking if a value is a plain object\n */\nexport function isPlainObject(\n\tvalue: unknown,\n): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Type guard for checking if a value is an array of a specific type\n */\nexport function isArrayOf<T>(\n\tvalue: unknown,\n\tguard: (item: unknown) => item is T,\n): value is T[] {\n\treturn Array.isArray(value) && value.every(guard);\n}\n\n/**\n * Type guard for string\n */\nexport function isString(value: unknown): value is string {\n\treturn typeof value === \"string\";\n}\n\n/**\n * Type guard for number\n */\nexport function isNumber(value: unknown): value is number {\n\treturn typeof value === \"number\" && !Number.isNaN(value);\n}\n\n/**\n * Type guard for boolean\n */\nexport function isBoolean(value: unknown): value is boolean {\n\treturn typeof value === \"boolean\";\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shell-env.d.ts","sourceRoot":"","sources":["../../src/utils/shell-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,KAAK,sBAAsB,EAAc,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"shell-env.d.ts","sourceRoot":"","sources":["../../src/utils/shell-env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,KAAK,sBAAsB,EAAc,MAAM,oBAAoB,CAAC;AA0F7E,MAAM,WAAW,8BAA8B;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,sBAAsB,CAAC;CAChC;AA6DD;;GAEG;AACH,wBAAgB,2BAA2B,CAC1C,OAAO,EAAE,MAAM,CAAC,UAAU,EAC1B,MAAM,CAAC,EAAE,sBAAsB,GAC7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAyDxB;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACtC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EAC9C,OAAO,GAAE,8BAAmC,GAC1C,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAgBxB"}
|
package/dist/utils/shell-env.js
CHANGED
|
@@ -93,6 +93,8 @@ const PLATFORM_TRUSTED_TOOL_ENV_ALLOWLIST = [
|
|
|
93
93
|
"MAESTRO_AGENT_REGISTRY_SERVICE_URL",
|
|
94
94
|
"MAESTRO_AGENT_RUNTIME_ORG_ID",
|
|
95
95
|
"MAESTRO_AGENT_RUNTIME_WORKSPACE_ID",
|
|
96
|
+
"MAESTRO_OBJECTIVE_ID",
|
|
97
|
+
"MAESTRO_EVALOPS_OBJECTIVE_ID",
|
|
96
98
|
];
|
|
97
99
|
function matchesAny(name, patterns) {
|
|
98
100
|
return patterns.some((pattern) => minimatch(name, pattern, { nocase: true, dot: true }));
|