@defai.digital/ax-cli 4.4.6 → 4.4.10
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/bin/ax-cli +1 -5
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +158 -9
- package/dist/index.js.map +1 -1
- package/dist/setup.d.ts +27 -0
- package/dist/setup.d.ts.map +1 -0
- package/dist/setup.js +591 -0
- package/dist/setup.js.map +1 -0
- package/package.json +43 -135
- package/LICENSE +0 -22
- package/README.md +0 -387
- package/config-defaults/messages.yaml +0 -75
- package/config-defaults/models.yaml +0 -57
- package/config-defaults/prompts.yaml +0 -948
- package/config-defaults/settings.yaml +0 -157
- package/dist/agent/agent-executor.d.ts +0 -61
- package/dist/agent/agent-executor.js +0 -194
- package/dist/agent/agent-executor.js.map +0 -1
- package/dist/agent/agent-router.d.ts +0 -68
- package/dist/agent/agent-router.js +0 -242
- package/dist/agent/agent-router.js.map +0 -1
- package/dist/agent/context-manager.d.ts +0 -122
- package/dist/agent/context-manager.js +0 -406
- package/dist/agent/context-manager.js.map +0 -1
- package/dist/agent/core/index.d.ts +0 -8
- package/dist/agent/core/index.js +0 -9
- package/dist/agent/core/index.js.map +0 -1
- package/dist/agent/core/types.d.ts +0 -92
- package/dist/agent/core/types.js +0 -11
- package/dist/agent/core/types.js.map +0 -1
- package/dist/agent/dependency-resolver.d.ts +0 -90
- package/dist/agent/dependency-resolver.js +0 -366
- package/dist/agent/dependency-resolver.js.map +0 -1
- package/dist/agent/execution/index.d.ts +0 -9
- package/dist/agent/execution/index.js +0 -9
- package/dist/agent/execution/index.js.map +0 -1
- package/dist/agent/execution/tool-executor.d.ts +0 -93
- package/dist/agent/execution/tool-executor.js +0 -552
- package/dist/agent/execution/tool-executor.js.map +0 -1
- package/dist/agent/index.d.ts +0 -14
- package/dist/agent/index.js +0 -145
- package/dist/agent/index.js.map +0 -1
- package/dist/agent/llm-agent.d.ts +0 -368
- package/dist/agent/llm-agent.js +0 -1931
- package/dist/agent/llm-agent.js.map +0 -1
- package/dist/agent/loop-detector.d.ts +0 -72
- package/dist/agent/loop-detector.js +0 -335
- package/dist/agent/loop-detector.js.map +0 -1
- package/dist/agent/parallel-tools.d.ts +0 -69
- package/dist/agent/parallel-tools.js +0 -188
- package/dist/agent/parallel-tools.js.map +0 -1
- package/dist/agent/planning/index.d.ts +0 -9
- package/dist/agent/planning/index.js +0 -9
- package/dist/agent/planning/index.js.map +0 -1
- package/dist/agent/planning/plan-executor.d.ts +0 -79
- package/dist/agent/planning/plan-executor.js +0 -240
- package/dist/agent/planning/plan-executor.js.map +0 -1
- package/dist/agent/progress-tracker.d.ts +0 -94
- package/dist/agent/progress-tracker.js +0 -225
- package/dist/agent/progress-tracker.js.map +0 -1
- package/dist/agent/specialized/analysis-agent.d.ts +0 -11
- package/dist/agent/specialized/analysis-agent.js +0 -24
- package/dist/agent/specialized/analysis-agent.js.map +0 -1
- package/dist/agent/specialized/debug-agent.d.ts +0 -11
- package/dist/agent/specialized/debug-agent.js +0 -46
- package/dist/agent/specialized/debug-agent.js.map +0 -1
- package/dist/agent/specialized/documentation-agent.d.ts +0 -11
- package/dist/agent/specialized/documentation-agent.js +0 -24
- package/dist/agent/specialized/documentation-agent.js.map +0 -1
- package/dist/agent/specialized/index.d.ts +0 -11
- package/dist/agent/specialized/index.js +0 -12
- package/dist/agent/specialized/index.js.map +0 -1
- package/dist/agent/specialized/performance-agent.d.ts +0 -11
- package/dist/agent/specialized/performance-agent.js +0 -24
- package/dist/agent/specialized/performance-agent.js.map +0 -1
- package/dist/agent/specialized/refactoring-agent.d.ts +0 -11
- package/dist/agent/specialized/refactoring-agent.js +0 -24
- package/dist/agent/specialized/refactoring-agent.js.map +0 -1
- package/dist/agent/specialized/testing-agent.d.ts +0 -11
- package/dist/agent/specialized/testing-agent.js +0 -24
- package/dist/agent/specialized/testing-agent.js.map +0 -1
- package/dist/agent/status-reporter.d.ts +0 -114
- package/dist/agent/status-reporter.js +0 -335
- package/dist/agent/status-reporter.js.map +0 -1
- package/dist/agent/streaming/index.d.ts +0 -9
- package/dist/agent/streaming/index.js +0 -9
- package/dist/agent/streaming/index.js.map +0 -1
- package/dist/agent/streaming/stream-handler.d.ts +0 -62
- package/dist/agent/streaming/stream-handler.js +0 -217
- package/dist/agent/streaming/stream-handler.js.map +0 -1
- package/dist/agent/subagent-orchestrator.d.ts +0 -166
- package/dist/agent/subagent-orchestrator.js +0 -487
- package/dist/agent/subagent-orchestrator.js.map +0 -1
- package/dist/agent/subagent-types.d.ts +0 -261
- package/dist/agent/subagent-types.js +0 -257
- package/dist/agent/subagent-types.js.map +0 -1
- package/dist/agent/subagent.d.ts +0 -116
- package/dist/agent/subagent.js +0 -507
- package/dist/agent/subagent.js.map +0 -1
- package/dist/checkpoint/index.d.ts +0 -9
- package/dist/checkpoint/index.js +0 -11
- package/dist/checkpoint/index.js.map +0 -1
- package/dist/checkpoint/manager.d.ts +0 -101
- package/dist/checkpoint/manager.js +0 -407
- package/dist/checkpoint/manager.js.map +0 -1
- package/dist/checkpoint/storage.d.ts +0 -39
- package/dist/checkpoint/storage.js +0 -350
- package/dist/checkpoint/storage.js.map +0 -1
- package/dist/checkpoint/types.d.ts +0 -111
- package/dist/checkpoint/types.js +0 -17
- package/dist/checkpoint/types.js.map +0 -1
- package/dist/commands/cache.d.ts +0 -7
- package/dist/commands/cache.js +0 -284
- package/dist/commands/cache.js.map +0 -1
- package/dist/commands/custom-commands.d.ts +0 -77
- package/dist/commands/custom-commands.js +0 -251
- package/dist/commands/custom-commands.js.map +0 -1
- package/dist/commands/design.d.ts +0 -18
- package/dist/commands/design.js +0 -511
- package/dist/commands/design.js.map +0 -1
- package/dist/commands/doctor.d.ts +0 -6
- package/dist/commands/doctor.js +0 -773
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/frontend.d.ts +0 -9
- package/dist/commands/frontend.js +0 -645
- package/dist/commands/frontend.js.map +0 -1
- package/dist/commands/init/wizard.d.ts +0 -55
- package/dist/commands/init/wizard.js +0 -189
- package/dist/commands/init/wizard.js.map +0 -1
- package/dist/commands/init.d.ts +0 -8
- package/dist/commands/init.js +0 -195
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/mcp-migrate.d.ts +0 -9
- package/dist/commands/mcp-migrate.js +0 -175
- package/dist/commands/mcp-migrate.js.map +0 -1
- package/dist/commands/mcp.d.ts +0 -2
- package/dist/commands/mcp.js +0 -1292
- package/dist/commands/mcp.js.map +0 -1
- package/dist/commands/memory.d.ts +0 -6
- package/dist/commands/memory.js +0 -555
- package/dist/commands/memory.js.map +0 -1
- package/dist/commands/models.d.ts +0 -5
- package/dist/commands/models.js +0 -213
- package/dist/commands/models.js.map +0 -1
- package/dist/commands/plan.d.ts +0 -43
- package/dist/commands/plan.js +0 -362
- package/dist/commands/plan.js.map +0 -1
- package/dist/commands/rewind.d.ts +0 -19
- package/dist/commands/rewind.js +0 -221
- package/dist/commands/rewind.js.map +0 -1
- package/dist/commands/setup.d.ts +0 -14
- package/dist/commands/setup.js +0 -733
- package/dist/commands/setup.js.map +0 -1
- package/dist/commands/status.d.ts +0 -7
- package/dist/commands/status.js +0 -437
- package/dist/commands/status.js.map +0 -1
- package/dist/commands/templates.d.ts +0 -5
- package/dist/commands/templates.js +0 -245
- package/dist/commands/templates.js.map +0 -1
- package/dist/commands/update.d.ts +0 -49
- package/dist/commands/update.js +0 -366
- package/dist/commands/update.js.map +0 -1
- package/dist/commands/usage.d.ts +0 -8
- package/dist/commands/usage.js +0 -264
- package/dist/commands/usage.js.map +0 -1
- package/dist/commands/vscode.d.ts +0 -7
- package/dist/commands/vscode.js +0 -419
- package/dist/commands/vscode.js.map +0 -1
- package/dist/constants.d.ts +0 -236
- package/dist/constants.js +0 -288
- package/dist/constants.js.map +0 -1
- package/dist/design/figma-alias.d.ts +0 -170
- package/dist/design/figma-alias.js +0 -577
- package/dist/design/figma-alias.js.map +0 -1
- package/dist/design/figma-audit.d.ts +0 -40
- package/dist/design/figma-audit.js +0 -383
- package/dist/design/figma-audit.js.map +0 -1
- package/dist/design/figma-client.d.ts +0 -131
- package/dist/design/figma-client.js +0 -369
- package/dist/design/figma-client.js.map +0 -1
- package/dist/design/figma-map.d.ts +0 -29
- package/dist/design/figma-map.js +0 -346
- package/dist/design/figma-map.js.map +0 -1
- package/dist/design/figma-tokens.d.ts +0 -73
- package/dist/design/figma-tokens.js +0 -448
- package/dist/design/figma-tokens.js.map +0 -1
- package/dist/design/index.d.ts +0 -13
- package/dist/design/index.js +0 -20
- package/dist/design/index.js.map +0 -1
- package/dist/design/types.d.ts +0 -98
- package/dist/design/types.js +0 -9
- package/dist/design/types.js.map +0 -1
- package/dist/hooks/hook-runner.d.ts +0 -142
- package/dist/hooks/hook-runner.js +0 -436
- package/dist/hooks/hook-runner.js.map +0 -1
- package/dist/hooks/index.d.ts +0 -9
- package/dist/hooks/index.js +0 -10
- package/dist/hooks/index.js.map +0 -1
- package/dist/hooks/manager.d.ts +0 -84
- package/dist/hooks/manager.js +0 -348
- package/dist/hooks/manager.js.map +0 -1
- package/dist/hooks/types.d.ts +0 -134
- package/dist/hooks/types.js +0 -9
- package/dist/hooks/types.js.map +0 -1
- package/dist/index.d.ts +0 -2
- package/dist/ipc/index.d.ts +0 -9
- package/dist/ipc/index.js +0 -10
- package/dist/ipc/index.js.map +0 -1
- package/dist/ipc/vscode-client.d.ts +0 -200
- package/dist/ipc/vscode-client.js +0 -495
- package/dist/ipc/vscode-client.js.map +0 -1
- package/dist/llm/client.d.ts +0 -205
- package/dist/llm/client.js +0 -735
- package/dist/llm/client.js.map +0 -1
- package/dist/llm/tools.d.ts +0 -102
- package/dist/llm/tools.js +0 -275
- package/dist/llm/tools.js.map +0 -1
- package/dist/llm/types.d.ts +0 -428
- package/dist/llm/types.js +0 -194
- package/dist/llm/types.js.map +0 -1
- package/dist/mcp/automatosx-auto-discovery.d.ts +0 -66
- package/dist/mcp/automatosx-auto-discovery.js +0 -169
- package/dist/mcp/automatosx-auto-discovery.js.map +0 -1
- package/dist/mcp/automatosx-loader.d.ts +0 -99
- package/dist/mcp/automatosx-loader.js +0 -250
- package/dist/mcp/automatosx-loader.js.map +0 -1
- package/dist/mcp/cancellation.d.ts +0 -182
- package/dist/mcp/cancellation.js +0 -275
- package/dist/mcp/cancellation.js.map +0 -1
- package/dist/mcp/client-v2.d.ts +0 -500
- package/dist/mcp/client-v2.js +0 -1433
- package/dist/mcp/client-v2.js.map +0 -1
- package/dist/mcp/client.d.ts +0 -170
- package/dist/mcp/client.js +0 -232
- package/dist/mcp/client.js.map +0 -1
- package/dist/mcp/config-detector.d.ts +0 -90
- package/dist/mcp/config-detector.js +0 -250
- package/dist/mcp/config-detector.js.map +0 -1
- package/dist/mcp/config-migrator.d.ts +0 -68
- package/dist/mcp/config-migrator.js +0 -291
- package/dist/mcp/config-migrator.js.map +0 -1
- package/dist/mcp/config.d.ts +0 -24
- package/dist/mcp/config.js +0 -273
- package/dist/mcp/config.js.map +0 -1
- package/dist/mcp/constants.d.ts +0 -66
- package/dist/mcp/constants.js +0 -85
- package/dist/mcp/constants.js.map +0 -1
- package/dist/mcp/content-length-transport.d.ts +0 -106
- package/dist/mcp/content-length-transport.js +0 -413
- package/dist/mcp/content-length-transport.js.map +0 -1
- package/dist/mcp/debug.d.ts +0 -211
- package/dist/mcp/debug.js +0 -404
- package/dist/mcp/debug.js.map +0 -1
- package/dist/mcp/error-formatter.d.ts +0 -40
- package/dist/mcp/error-formatter.js +0 -207
- package/dist/mcp/error-formatter.js.map +0 -1
- package/dist/mcp/error-remediation.d.ts +0 -45
- package/dist/mcp/error-remediation.js +0 -291
- package/dist/mcp/error-remediation.js.map +0 -1
- package/dist/mcp/health.d.ts +0 -120
- package/dist/mcp/health.js +0 -267
- package/dist/mcp/health.js.map +0 -1
- package/dist/mcp/index.d.ts +0 -56
- package/dist/mcp/index.js +0 -89
- package/dist/mcp/index.js.map +0 -1
- package/dist/mcp/invariants.d.ts +0 -141
- package/dist/mcp/invariants.js +0 -243
- package/dist/mcp/invariants.js.map +0 -1
- package/dist/mcp/mutex-safe.d.ts +0 -151
- package/dist/mcp/mutex-safe.js +0 -260
- package/dist/mcp/mutex-safe.js.map +0 -1
- package/dist/mcp/progress.d.ts +0 -155
- package/dist/mcp/progress.js +0 -252
- package/dist/mcp/progress.js.map +0 -1
- package/dist/mcp/prompts.d.ts +0 -68
- package/dist/mcp/prompts.js +0 -129
- package/dist/mcp/prompts.js.map +0 -1
- package/dist/mcp/provider-mcp-loader.d.ts +0 -130
- package/dist/mcp/provider-mcp-loader.js +0 -292
- package/dist/mcp/provider-mcp-loader.js.map +0 -1
- package/dist/mcp/reconnection.d.ts +0 -101
- package/dist/mcp/reconnection.js +0 -253
- package/dist/mcp/reconnection.js.map +0 -1
- package/dist/mcp/registry.d.ts +0 -75
- package/dist/mcp/registry.js +0 -276
- package/dist/mcp/registry.js.map +0 -1
- package/dist/mcp/resources.d.ts +0 -58
- package/dist/mcp/resources.js +0 -144
- package/dist/mcp/resources.js.map +0 -1
- package/dist/mcp/schema-validator.d.ts +0 -82
- package/dist/mcp/schema-validator.js +0 -161
- package/dist/mcp/schema-validator.js.map +0 -1
- package/dist/mcp/ssrf-protection.d.ts +0 -86
- package/dist/mcp/ssrf-protection.js +0 -311
- package/dist/mcp/ssrf-protection.js.map +0 -1
- package/dist/mcp/subscriptions.d.ts +0 -168
- package/dist/mcp/subscriptions.js +0 -248
- package/dist/mcp/subscriptions.js.map +0 -1
- package/dist/mcp/templates.d.ts +0 -52
- package/dist/mcp/templates.js +0 -627
- package/dist/mcp/templates.js.map +0 -1
- package/dist/mcp/transports.d.ts +0 -80
- package/dist/mcp/transports.js +0 -237
- package/dist/mcp/transports.js.map +0 -1
- package/dist/mcp/type-safety.d.ts +0 -225
- package/dist/mcp/type-safety.js +0 -237
- package/dist/mcp/type-safety.js.map +0 -1
- package/dist/mcp/validation.d.ts +0 -29
- package/dist/mcp/validation.js +0 -339
- package/dist/mcp/validation.js.map +0 -1
- package/dist/mcp/zai-detector.d.ts +0 -63
- package/dist/mcp/zai-detector.js +0 -193
- package/dist/mcp/zai-detector.js.map +0 -1
- package/dist/mcp/zai-templates.d.ts +0 -90
- package/dist/mcp/zai-templates.js +0 -157
- package/dist/mcp/zai-templates.js.map +0 -1
- package/dist/memory/context-generator.d.ts +0 -84
- package/dist/memory/context-generator.js +0 -546
- package/dist/memory/context-generator.js.map +0 -1
- package/dist/memory/context-injector.d.ts +0 -97
- package/dist/memory/context-injector.js +0 -159
- package/dist/memory/context-injector.js.map +0 -1
- package/dist/memory/context-store.d.ts +0 -103
- package/dist/memory/context-store.js +0 -264
- package/dist/memory/context-store.js.map +0 -1
- package/dist/memory/index.d.ts +0 -43
- package/dist/memory/index.js +0 -49
- package/dist/memory/index.js.map +0 -1
- package/dist/memory/provider-context-store.d.ts +0 -127
- package/dist/memory/provider-context-store.js +0 -385
- package/dist/memory/provider-context-store.js.map +0 -1
- package/dist/memory/schemas.d.ts +0 -118
- package/dist/memory/schemas.js +0 -106
- package/dist/memory/schemas.js.map +0 -1
- package/dist/memory/stats-collector.d.ts +0 -73
- package/dist/memory/stats-collector.js +0 -170
- package/dist/memory/stats-collector.js.map +0 -1
- package/dist/memory/types.d.ts +0 -177
- package/dist/memory/types.js +0 -73
- package/dist/memory/types.js.map +0 -1
- package/dist/permissions/index.d.ts +0 -6
- package/dist/permissions/index.js +0 -7
- package/dist/permissions/index.js.map +0 -1
- package/dist/permissions/permission-manager.d.ts +0 -149
- package/dist/permissions/permission-manager.js +0 -410
- package/dist/permissions/permission-manager.js.map +0 -1
- package/dist/planner/dependency-resolver.d.ts +0 -72
- package/dist/planner/dependency-resolver.js +0 -272
- package/dist/planner/dependency-resolver.js.map +0 -1
- package/dist/planner/index.d.ts +0 -12
- package/dist/planner/index.js +0 -28
- package/dist/planner/index.js.map +0 -1
- package/dist/planner/plan-generator.d.ts +0 -74
- package/dist/planner/plan-generator.js +0 -244
- package/dist/planner/plan-generator.js.map +0 -1
- package/dist/planner/plan-storage.d.ts +0 -113
- package/dist/planner/plan-storage.js +0 -398
- package/dist/planner/plan-storage.js.map +0 -1
- package/dist/planner/prompts/planning-prompt.d.ts +0 -62
- package/dist/planner/prompts/planning-prompt.js +0 -414
- package/dist/planner/prompts/planning-prompt.js.map +0 -1
- package/dist/planner/task-planner.d.ts +0 -139
- package/dist/planner/task-planner.js +0 -532
- package/dist/planner/task-planner.js.map +0 -1
- package/dist/planner/token-estimator.d.ts +0 -63
- package/dist/planner/token-estimator.js +0 -295
- package/dist/planner/token-estimator.js.map +0 -1
- package/dist/planner/types.d.ts +0 -425
- package/dist/planner/types.js +0 -213
- package/dist/planner/types.js.map +0 -1
- package/dist/provider/config.d.ts +0 -227
- package/dist/provider/config.js +0 -430
- package/dist/provider/config.js.map +0 -1
- package/dist/schemas/api-schemas.d.ts +0 -45
- package/dist/schemas/api-schemas.js +0 -129
- package/dist/schemas/api-schemas.js.map +0 -1
- package/dist/schemas/confirmation-schemas.d.ts +0 -39
- package/dist/schemas/confirmation-schemas.js +0 -48
- package/dist/schemas/confirmation-schemas.js.map +0 -1
- package/dist/schemas/index-unified.d.ts +0 -12
- package/dist/schemas/index-unified.js +0 -17
- package/dist/schemas/index-unified.js.map +0 -1
- package/dist/schemas/index.d.ts +0 -83
- package/dist/schemas/index.js +0 -139
- package/dist/schemas/index.js.map +0 -1
- package/dist/schemas/settings-schemas.d.ts +0 -186
- package/dist/schemas/settings-schemas.js +0 -324
- package/dist/schemas/settings-schemas.js.map +0 -1
- package/dist/schemas/tool-schemas.d.ts +0 -127
- package/dist/schemas/tool-schemas.js +0 -84
- package/dist/schemas/tool-schemas.js.map +0 -1
- package/dist/schemas/yaml-schemas.d.ts +0 -231
- package/dist/schemas/yaml-schemas.js +0 -199
- package/dist/schemas/yaml-schemas.js.map +0 -1
- package/dist/sdk/errors.d.ts +0 -100
- package/dist/sdk/errors.js +0 -138
- package/dist/sdk/errors.js.map +0 -1
- package/dist/sdk/index.d.ts +0 -901
- package/dist/sdk/index.js +0 -1272
- package/dist/sdk/index.js.map +0 -1
- package/dist/sdk/progress-reporter.d.ts +0 -123
- package/dist/sdk/progress-reporter.js +0 -220
- package/dist/sdk/progress-reporter.js.map +0 -1
- package/dist/sdk/testing.d.ts +0 -427
- package/dist/sdk/testing.js +0 -725
- package/dist/sdk/testing.js.map +0 -1
- package/dist/sdk/tool-registry.d.ts +0 -194
- package/dist/sdk/tool-registry.js +0 -326
- package/dist/sdk/tool-registry.js.map +0 -1
- package/dist/sdk/types.d.ts +0 -53
- package/dist/sdk/types.js +0 -8
- package/dist/sdk/types.js.map +0 -1
- package/dist/sdk/unified-logger.d.ts +0 -173
- package/dist/sdk/unified-logger.js +0 -327
- package/dist/sdk/unified-logger.js.map +0 -1
- package/dist/sdk/version.d.ts +0 -163
- package/dist/sdk/version.js +0 -205
- package/dist/sdk/version.js.map +0 -1
- package/dist/tools/ask-user.d.ts +0 -126
- package/dist/tools/ask-user.js +0 -290
- package/dist/tools/ask-user.js.map +0 -1
- package/dist/tools/ax-agent.d.ts +0 -71
- package/dist/tools/ax-agent.js +0 -283
- package/dist/tools/ax-agent.js.map +0 -1
- package/dist/tools/bash-output.d.ts +0 -25
- package/dist/tools/bash-output.js +0 -146
- package/dist/tools/bash-output.js.map +0 -1
- package/dist/tools/bash.d.ts +0 -67
- package/dist/tools/bash.js +0 -522
- package/dist/tools/bash.js.map +0 -1
- package/dist/tools/confirmation-tool.d.ts +0 -16
- package/dist/tools/confirmation-tool.js +0 -76
- package/dist/tools/confirmation-tool.js.map +0 -1
- package/dist/tools/definitions/ask-user.d.ts +0 -8
- package/dist/tools/definitions/ask-user.js +0 -168
- package/dist/tools/definitions/ask-user.js.map +0 -1
- package/dist/tools/definitions/ax-agent.d.ts +0 -8
- package/dist/tools/definitions/ax-agent.js +0 -276
- package/dist/tools/definitions/ax-agent.js.map +0 -1
- package/dist/tools/definitions/bash-output.d.ts +0 -7
- package/dist/tools/definitions/bash-output.js +0 -78
- package/dist/tools/definitions/bash-output.js.map +0 -1
- package/dist/tools/definitions/bash.d.ts +0 -8
- package/dist/tools/definitions/bash.js +0 -152
- package/dist/tools/definitions/bash.js.map +0 -1
- package/dist/tools/definitions/create-file.d.ts +0 -7
- package/dist/tools/definitions/create-file.js +0 -129
- package/dist/tools/definitions/create-file.js.map +0 -1
- package/dist/tools/definitions/design.d.ts +0 -12
- package/dist/tools/definitions/design.js +0 -368
- package/dist/tools/definitions/design.js.map +0 -1
- package/dist/tools/definitions/index.d.ts +0 -49
- package/dist/tools/definitions/index.js +0 -87
- package/dist/tools/definitions/index.js.map +0 -1
- package/dist/tools/definitions/multi-edit.d.ts +0 -7
- package/dist/tools/definitions/multi-edit.js +0 -123
- package/dist/tools/definitions/multi-edit.js.map +0 -1
- package/dist/tools/definitions/search.d.ts +0 -7
- package/dist/tools/definitions/search.js +0 -159
- package/dist/tools/definitions/search.js.map +0 -1
- package/dist/tools/definitions/str-replace-editor.d.ts +0 -7
- package/dist/tools/definitions/str-replace-editor.js +0 -145
- package/dist/tools/definitions/str-replace-editor.js.map +0 -1
- package/dist/tools/definitions/todo.d.ts +0 -8
- package/dist/tools/definitions/todo.js +0 -261
- package/dist/tools/definitions/todo.js.map +0 -1
- package/dist/tools/definitions/view-file.d.ts +0 -7
- package/dist/tools/definitions/view-file.js +0 -111
- package/dist/tools/definitions/view-file.js.map +0 -1
- package/dist/tools/design-tool.d.ts +0 -68
- package/dist/tools/design-tool.js +0 -299
- package/dist/tools/design-tool.js.map +0 -1
- package/dist/tools/format-generators.d.ts +0 -62
- package/dist/tools/format-generators.js +0 -291
- package/dist/tools/format-generators.js.map +0 -1
- package/dist/tools/index.d.ts +0 -8
- package/dist/tools/index.js +0 -11
- package/dist/tools/index.js.map +0 -1
- package/dist/tools/priority-registry.d.ts +0 -124
- package/dist/tools/priority-registry.js +0 -401
- package/dist/tools/priority-registry.js.map +0 -1
- package/dist/tools/priority.d.ts +0 -158
- package/dist/tools/priority.js +0 -350
- package/dist/tools/priority.js.map +0 -1
- package/dist/tools/registry.d.ts +0 -146
- package/dist/tools/registry.js +0 -171
- package/dist/tools/registry.js.map +0 -1
- package/dist/tools/search.d.ts +0 -85
- package/dist/tools/search.js +0 -430
- package/dist/tools/search.js.map +0 -1
- package/dist/tools/text-editor.d.ts +0 -87
- package/dist/tools/text-editor.js +0 -1369
- package/dist/tools/text-editor.js.map +0 -1
- package/dist/tools/todo-tool.d.ts +0 -20
- package/dist/tools/todo-tool.js +0 -186
- package/dist/tools/todo-tool.js.map +0 -1
- package/dist/tools/types.d.ts +0 -175
- package/dist/tools/types.js +0 -11
- package/dist/tools/types.js.map +0 -1
- package/dist/types/index.d.ts +0 -30
- package/dist/types/index.js +0 -2
- package/dist/types/index.js.map +0 -1
- package/dist/types/project-analysis.d.ts +0 -84
- package/dist/types/project-analysis.js +0 -5
- package/dist/types/project-analysis.js.map +0 -1
- package/dist/types/template.d.ts +0 -53
- package/dist/types/template.js +0 -5
- package/dist/types/template.js.map +0 -1
- package/dist/ui/app.d.ts +0 -7
- package/dist/ui/app.js +0 -102
- package/dist/ui/app.js.map +0 -1
- package/dist/ui/components/api-key-input.d.ts +0 -7
- package/dist/ui/components/api-key-input.js +0 -92
- package/dist/ui/components/api-key-input.js.map +0 -1
- package/dist/ui/components/chat-history.d.ts +0 -12
- package/dist/ui/components/chat-history.js +0 -391
- package/dist/ui/components/chat-history.js.map +0 -1
- package/dist/ui/components/chat-input.d.ts +0 -13
- package/dist/ui/components/chat-input.js +0 -179
- package/dist/ui/components/chat-input.js.map +0 -1
- package/dist/ui/components/chat-interface.d.ts +0 -11
- package/dist/ui/components/chat-interface.js +0 -830
- package/dist/ui/components/chat-interface.js.map +0 -1
- package/dist/ui/components/collapsible-tool-result.d.ts +0 -42
- package/dist/ui/components/collapsible-tool-result.js +0 -216
- package/dist/ui/components/collapsible-tool-result.js.map +0 -1
- package/dist/ui/components/command-suggestions.d.ts +0 -29
- package/dist/ui/components/command-suggestions.js +0 -88
- package/dist/ui/components/command-suggestions.js.map +0 -1
- package/dist/ui/components/confirmation-dialog.d.ts +0 -11
- package/dist/ui/components/confirmation-dialog.js +0 -100
- package/dist/ui/components/confirmation-dialog.js.map +0 -1
- package/dist/ui/components/context-breakdown.d.ts +0 -23
- package/dist/ui/components/context-breakdown.js +0 -124
- package/dist/ui/components/context-breakdown.js.map +0 -1
- package/dist/ui/components/diff-renderer.d.ts +0 -13
- package/dist/ui/components/diff-renderer.js +0 -192
- package/dist/ui/components/diff-renderer.js.map +0 -1
- package/dist/ui/components/index.d.ts +0 -18
- package/dist/ui/components/index.js +0 -20
- package/dist/ui/components/index.js.map +0 -1
- package/dist/ui/components/keyboard-help.d.ts +0 -17
- package/dist/ui/components/keyboard-help.js +0 -122
- package/dist/ui/components/keyboard-help.js.map +0 -1
- package/dist/ui/components/keyboard-hints.d.ts +0 -35
- package/dist/ui/components/keyboard-hints.js +0 -142
- package/dist/ui/components/keyboard-hints.js.map +0 -1
- package/dist/ui/components/loading-spinner.d.ts +0 -9
- package/dist/ui/components/loading-spinner.js +0 -120
- package/dist/ui/components/loading-spinner.js.map +0 -1
- package/dist/ui/components/mcp-dashboard.d.ts +0 -15
- package/dist/ui/components/mcp-dashboard.js +0 -520
- package/dist/ui/components/mcp-dashboard.js.map +0 -1
- package/dist/ui/components/mcp-status.d.ts +0 -5
- package/dist/ui/components/mcp-status.js +0 -58
- package/dist/ui/components/mcp-status.js.map +0 -1
- package/dist/ui/components/model-selection.d.ts +0 -12
- package/dist/ui/components/model-selection.js +0 -17
- package/dist/ui/components/model-selection.js.map +0 -1
- package/dist/ui/components/phase-progress.d.ts +0 -21
- package/dist/ui/components/phase-progress.js +0 -185
- package/dist/ui/components/phase-progress.js.map +0 -1
- package/dist/ui/components/question-dialog.d.ts +0 -17
- package/dist/ui/components/question-dialog.js +0 -181
- package/dist/ui/components/question-dialog.js.map +0 -1
- package/dist/ui/components/quick-actions.d.ts +0 -12
- package/dist/ui/components/quick-actions.js +0 -171
- package/dist/ui/components/quick-actions.js.map +0 -1
- package/dist/ui/components/reasoning-display.d.ts +0 -36
- package/dist/ui/components/reasoning-display.js +0 -46
- package/dist/ui/components/reasoning-display.js.map +0 -1
- package/dist/ui/components/status-bar.d.ts +0 -47
- package/dist/ui/components/status-bar.js +0 -310
- package/dist/ui/components/status-bar.js.map +0 -1
- package/dist/ui/components/subagent-monitor.d.ts +0 -41
- package/dist/ui/components/subagent-monitor.js +0 -122
- package/dist/ui/components/subagent-monitor.js.map +0 -1
- package/dist/ui/components/toast-notification.d.ts +0 -197
- package/dist/ui/components/toast-notification.js +0 -190
- package/dist/ui/components/toast-notification.js.map +0 -1
- package/dist/ui/components/tool-group-display.d.ts +0 -19
- package/dist/ui/components/tool-group-display.js +0 -222
- package/dist/ui/components/tool-group-display.js.map +0 -1
- package/dist/ui/components/virtualized-chat-history.d.ts +0 -33
- package/dist/ui/components/virtualized-chat-history.js +0 -182
- package/dist/ui/components/virtualized-chat-history.js.map +0 -1
- package/dist/ui/components/welcome-panel.d.ts +0 -11
- package/dist/ui/components/welcome-panel.js +0 -225
- package/dist/ui/components/welcome-panel.js.map +0 -1
- package/dist/ui/hooks/use-chat-reducer.d.ts +0 -69
- package/dist/ui/hooks/use-chat-reducer.js +0 -118
- package/dist/ui/hooks/use-chat-reducer.js.map +0 -1
- package/dist/ui/hooks/use-enhanced-input.d.ts +0 -53
- package/dist/ui/hooks/use-enhanced-input.js +0 -1275
- package/dist/ui/hooks/use-enhanced-input.js.map +0 -1
- package/dist/ui/hooks/use-input-handler.d.ts +0 -79
- package/dist/ui/hooks/use-input-handler.js +0 -2251
- package/dist/ui/hooks/use-input-handler.js.map +0 -1
- package/dist/ui/hooks/use-input-history.d.ts +0 -9
- package/dist/ui/hooks/use-input-history.js +0 -168
- package/dist/ui/hooks/use-input-history.js.map +0 -1
- package/dist/ui/shared/max-sized-box.d.ts +0 -17
- package/dist/ui/shared/max-sized-box.js +0 -14
- package/dist/ui/shared/max-sized-box.js.map +0 -1
- package/dist/ui/themes/index.d.ts +0 -5
- package/dist/ui/themes/index.js +0 -5
- package/dist/ui/themes/index.js.map +0 -1
- package/dist/ui/themes/theme-registry.d.ts +0 -55
- package/dist/ui/themes/theme-registry.js +0 -202
- package/dist/ui/themes/theme-registry.js.map +0 -1
- package/dist/ui/utils/bracketed-paste-handler.d.ts +0 -97
- package/dist/ui/utils/bracketed-paste-handler.js +0 -322
- package/dist/ui/utils/bracketed-paste-handler.js.map +0 -1
- package/dist/ui/utils/change-summarizer.d.ts +0 -20
- package/dist/ui/utils/change-summarizer.js +0 -282
- package/dist/ui/utils/change-summarizer.js.map +0 -1
- package/dist/ui/utils/code-colorizer.d.ts +0 -9
- package/dist/ui/utils/code-colorizer.js +0 -13
- package/dist/ui/utils/code-colorizer.js.map +0 -1
- package/dist/ui/utils/colors.d.ts +0 -41
- package/dist/ui/utils/colors.js +0 -80
- package/dist/ui/utils/colors.js.map +0 -1
- package/dist/ui/utils/image-handler.d.ts +0 -29
- package/dist/ui/utils/image-handler.js +0 -129
- package/dist/ui/utils/image-handler.js.map +0 -1
- package/dist/ui/utils/markdown-renderer.d.ts +0 -4
- package/dist/ui/utils/markdown-renderer.js +0 -40
- package/dist/ui/utils/markdown-renderer.js.map +0 -1
- package/dist/ui/utils/semantic-action-detector.d.ts +0 -49
- package/dist/ui/utils/semantic-action-detector.js +0 -339
- package/dist/ui/utils/semantic-action-detector.js.map +0 -1
- package/dist/ui/utils/tool-grouper.d.ts +0 -94
- package/dist/ui/utils/tool-grouper.js +0 -618
- package/dist/ui/utils/tool-grouper.js.map +0 -1
- package/dist/utils/api-error.d.ts +0 -61
- package/dist/utils/api-error.js +0 -176
- package/dist/utils/api-error.js.map +0 -1
- package/dist/utils/audit-logger.d.ts +0 -206
- package/dist/utils/audit-logger.js +0 -286
- package/dist/utils/audit-logger.js.map +0 -1
- package/dist/utils/auto-accept-logger.d.ts +0 -175
- package/dist/utils/auto-accept-logger.js +0 -423
- package/dist/utils/auto-accept-logger.js.map +0 -1
- package/dist/utils/automatosx-detector.d.ts +0 -19
- package/dist/utils/automatosx-detector.js +0 -52
- package/dist/utils/automatosx-detector.js.map +0 -1
- package/dist/utils/background-task-manager.d.ts +0 -114
- package/dist/utils/background-task-manager.js +0 -470
- package/dist/utils/background-task-manager.js.map +0 -1
- package/dist/utils/cache.d.ts +0 -77
- package/dist/utils/cache.js +0 -180
- package/dist/utils/cache.js.map +0 -1
- package/dist/utils/command-security.d.ts +0 -85
- package/dist/utils/command-security.js +0 -210
- package/dist/utils/command-security.js.map +0 -1
- package/dist/utils/config-loader.d.ts +0 -190
- package/dist/utils/config-loader.js +0 -108
- package/dist/utils/config-loader.js.map +0 -1
- package/dist/utils/confirmation-service.d.ts +0 -51
- package/dist/utils/confirmation-service.js +0 -220
- package/dist/utils/confirmation-service.js.map +0 -1
- package/dist/utils/console-messenger.d.ts +0 -80
- package/dist/utils/console-messenger.js +0 -142
- package/dist/utils/console-messenger.js.map +0 -1
- package/dist/utils/custom-instructions.d.ts +0 -1
- package/dist/utils/custom-instructions.js +0 -24
- package/dist/utils/custom-instructions.js.map +0 -1
- package/dist/utils/encryption.d.ts +0 -86
- package/dist/utils/encryption.js +0 -236
- package/dist/utils/encryption.js.map +0 -1
- package/dist/utils/enhanced-error-messages.d.ts +0 -33
- package/dist/utils/enhanced-error-messages.js +0 -440
- package/dist/utils/enhanced-error-messages.js.map +0 -1
- package/dist/utils/error-handler.d.ts +0 -65
- package/dist/utils/error-handler.js +0 -148
- package/dist/utils/error-handler.js.map +0 -1
- package/dist/utils/error-translator.d.ts +0 -25
- package/dist/utils/error-translator.js +0 -203
- package/dist/utils/error-translator.js.map +0 -1
- package/dist/utils/external-editor.d.ts +0 -47
- package/dist/utils/external-editor.js +0 -179
- package/dist/utils/external-editor.js.map +0 -1
- package/dist/utils/file-cache.d.ts +0 -148
- package/dist/utils/file-cache.js +0 -413
- package/dist/utils/file-cache.js.map +0 -1
- package/dist/utils/file-lock.d.ts +0 -141
- package/dist/utils/file-lock.js +0 -554
- package/dist/utils/file-lock.js.map +0 -1
- package/dist/utils/file-mentions.d.ts +0 -68
- package/dist/utils/file-mentions.js +0 -225
- package/dist/utils/file-mentions.js.map +0 -1
- package/dist/utils/history-manager.d.ts +0 -52
- package/dist/utils/history-manager.js +0 -211
- package/dist/utils/history-manager.js.map +0 -1
- package/dist/utils/history-migration.d.ts +0 -9
- package/dist/utils/history-migration.js +0 -37
- package/dist/utils/history-migration.js.map +0 -1
- package/dist/utils/image-processor.d.ts +0 -33
- package/dist/utils/image-processor.js +0 -124
- package/dist/utils/image-processor.js.map +0 -1
- package/dist/utils/index.d.ts +0 -92
- package/dist/utils/index.js +0 -111
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/init-previewer.d.ts +0 -56
- package/dist/utils/init-previewer.js +0 -239
- package/dist/utils/init-previewer.js.map +0 -1
- package/dist/utils/init-validator.d.ts +0 -65
- package/dist/utils/init-validator.js +0 -252
- package/dist/utils/init-validator.js.map +0 -1
- package/dist/utils/input-sanitizer.d.ts +0 -210
- package/dist/utils/input-sanitizer.js +0 -362
- package/dist/utils/input-sanitizer.js.map +0 -1
- package/dist/utils/instruction-generator.d.ts +0 -21
- package/dist/utils/instruction-generator.js +0 -233
- package/dist/utils/instruction-generator.js.map +0 -1
- package/dist/utils/json-utils.d.ts +0 -72
- package/dist/utils/json-utils.js +0 -226
- package/dist/utils/json-utils.js.map +0 -1
- package/dist/utils/llm-optimized-instruction-generator.d.ts +0 -36
- package/dist/utils/llm-optimized-instruction-generator.js +0 -365
- package/dist/utils/llm-optimized-instruction-generator.js.map +0 -1
- package/dist/utils/message-optimizer.d.ts +0 -100
- package/dist/utils/message-optimizer.js +0 -297
- package/dist/utils/message-optimizer.js.map +0 -1
- package/dist/utils/onboarding-manager.d.ts +0 -45
- package/dist/utils/onboarding-manager.js +0 -131
- package/dist/utils/onboarding-manager.js.map +0 -1
- package/dist/utils/parallel-analyzer.d.ts +0 -123
- package/dist/utils/parallel-analyzer.js +0 -241
- package/dist/utils/parallel-analyzer.js.map +0 -1
- package/dist/utils/paste-utils.d.ts +0 -99
- package/dist/utils/paste-utils.js +0 -295
- package/dist/utils/paste-utils.js.map +0 -1
- package/dist/utils/path-helpers.d.ts +0 -8
- package/dist/utils/path-helpers.js +0 -35
- package/dist/utils/path-helpers.js.map +0 -1
- package/dist/utils/path-security.d.ts +0 -92
- package/dist/utils/path-security.js +0 -300
- package/dist/utils/path-security.js.map +0 -1
- package/dist/utils/path-utils.d.ts +0 -83
- package/dist/utils/path-utils.js +0 -122
- package/dist/utils/path-utils.js.map +0 -1
- package/dist/utils/path-validator.d.ts +0 -66
- package/dist/utils/path-validator.js +0 -141
- package/dist/utils/path-validator.js.map +0 -1
- package/dist/utils/performance.d.ts +0 -74
- package/dist/utils/performance.js +0 -133
- package/dist/utils/performance.js.map +0 -1
- package/dist/utils/process-pool.d.ts +0 -109
- package/dist/utils/process-pool.js +0 -332
- package/dist/utils/process-pool.js.map +0 -1
- package/dist/utils/progress-tracker.d.ts +0 -51
- package/dist/utils/progress-tracker.js +0 -152
- package/dist/utils/progress-tracker.js.map +0 -1
- package/dist/utils/project-analyzer.d.ts +0 -49
- package/dist/utils/project-analyzer.js +0 -396
- package/dist/utils/project-analyzer.js.map +0 -1
- package/dist/utils/prompt-builder.d.ts +0 -14
- package/dist/utils/prompt-builder.js +0 -100
- package/dist/utils/prompt-builder.js.map +0 -1
- package/dist/utils/provider-context.d.ts +0 -243
- package/dist/utils/provider-context.js +0 -421
- package/dist/utils/provider-context.js.map +0 -1
- package/dist/utils/provider-file-cache.d.ts +0 -91
- package/dist/utils/provider-file-cache.js +0 -165
- package/dist/utils/provider-file-cache.js.map +0 -1
- package/dist/utils/provider-settings.d.ts +0 -181
- package/dist/utils/provider-settings.js +0 -450
- package/dist/utils/provider-settings.js.map +0 -1
- package/dist/utils/rate-limiter.d.ts +0 -222
- package/dist/utils/rate-limiter.js +0 -338
- package/dist/utils/rate-limiter.js.map +0 -1
- package/dist/utils/retry-helper.d.ts +0 -81
- package/dist/utils/retry-helper.js +0 -244
- package/dist/utils/retry-helper.js.map +0 -1
- package/dist/utils/safety-rules.d.ts +0 -64
- package/dist/utils/safety-rules.js +0 -225
- package/dist/utils/safety-rules.js.map +0 -1
- package/dist/utils/settings-manager.d.ts +0 -256
- package/dist/utils/settings-manager.js +0 -967
- package/dist/utils/settings-manager.js.map +0 -1
- package/dist/utils/setup-validator.d.ts +0 -47
- package/dist/utils/setup-validator.js +0 -304
- package/dist/utils/setup-validator.js.map +0 -1
- package/dist/utils/string-utils.d.ts +0 -19
- package/dist/utils/string-utils.js +0 -28
- package/dist/utils/string-utils.js.map +0 -1
- package/dist/utils/template-manager.d.ts +0 -62
- package/dist/utils/template-manager.js +0 -366
- package/dist/utils/template-manager.js.map +0 -1
- package/dist/utils/text-utils.d.ts +0 -82
- package/dist/utils/text-utils.js +0 -203
- package/dist/utils/text-utils.js.map +0 -1
- package/dist/utils/token-counter.d.ts +0 -76
- package/dist/utils/token-counter.js +0 -231
- package/dist/utils/token-counter.js.map +0 -1
- package/dist/utils/usage-tracker.d.ts +0 -78
- package/dist/utils/usage-tracker.js +0 -126
- package/dist/utils/usage-tracker.js.map +0 -1
- package/dist/utils/version.d.ts +0 -14
- package/dist/utils/version.js +0 -70
- package/dist/utils/version.js.map +0 -1
|
@@ -1,1369 +0,0 @@
|
|
|
1
|
-
import fs from "fs-extra";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { writeFile as writeFilePromise } from "fs/promises";
|
|
4
|
-
import { ConfirmationService } from "../utils/confirmation-service.js";
|
|
5
|
-
import { validatePathSecure } from "../utils/path-security.js";
|
|
6
|
-
import { getMessageOptimizer } from "../utils/message-optimizer.js";
|
|
7
|
-
import { isDestructiveFileOperation } from "../utils/safety-rules.js";
|
|
8
|
-
import { getAutoAcceptLogger } from "../utils/auto-accept-logger.js";
|
|
9
|
-
import { getSettingsManager } from "../utils/settings-manager.js";
|
|
10
|
-
import { ErrorCategory, createToolError } from "../utils/error-handler.js";
|
|
11
|
-
import { getVSCodeIPCClient } from "../ipc/index.js";
|
|
12
|
-
// Configuration constants
|
|
13
|
-
const EDITOR_CONFIG = {
|
|
14
|
-
/** Number of context lines to show in diffs */
|
|
15
|
-
DIFF_CONTEXT_LINES: 3,
|
|
16
|
-
/** Minimum similarity score for fuzzy line matching */
|
|
17
|
-
LINE_SIMILARITY_THRESHOLD: 0.75,
|
|
18
|
-
/** Minimum similarity score for block matching */
|
|
19
|
-
BLOCK_SIMILARITY_THRESHOLD: 0.55,
|
|
20
|
-
/** Minimum similarity for suggestions */
|
|
21
|
-
SUGGESTION_SIMILARITY_THRESHOLD: 0.4,
|
|
22
|
-
/** File encoding */
|
|
23
|
-
FILE_ENCODING: "utf-8",
|
|
24
|
-
};
|
|
25
|
-
// Error messages (centralized for consistency)
|
|
26
|
-
// Include actionable recovery guidance to help LLM self-correct
|
|
27
|
-
const RECOVERY_HINT = "TIP: Use view_file first to get the exact content before editing.";
|
|
28
|
-
const ERROR_MESSAGES = {
|
|
29
|
-
FILE_NOT_FOUND: (filePath) => `File not found: ${filePath}. Use view_file to check the directory structure.`,
|
|
30
|
-
PATH_NOT_FOUND: (filePath) => `File or directory not found: ${filePath}`,
|
|
31
|
-
SECURITY_ERROR: (error) => `Security: ${error}`,
|
|
32
|
-
EMPTY_SEARCH_STRING: "Search string cannot be empty",
|
|
33
|
-
STRING_NOT_FOUND: (str) => `String not found in file: "${str}". Use view_file to see the exact file contents and copy the correct text.`,
|
|
34
|
-
STRING_NOT_FOUND_MULTI: "String not found in file. For multi-line replacements, use view_file to get the exact content including whitespace and indentation.",
|
|
35
|
-
FILE_MODIFIED_DURING_CONFIRMATION: "File was modified by another process during confirmation. Please retry the operation.",
|
|
36
|
-
NO_EDITS_TO_UNDO: "No edits to undo",
|
|
37
|
-
EDIT_CANCELLED: (operation) => `${operation} cancelled by user`,
|
|
38
|
-
NO_EDITS_PROVIDED: "No edits provided",
|
|
39
|
-
EMPTY_OLD_STR: (index) => `Edit ${index + 1}: old_str cannot be empty`,
|
|
40
|
-
RECOVERY_HINT,
|
|
41
|
-
/** Append recovery hint to any error message */
|
|
42
|
-
withRecoveryHint: (msg) => `${msg} ${RECOVERY_HINT}`,
|
|
43
|
-
};
|
|
44
|
-
/**
|
|
45
|
-
* Validate path security and return resolved path or error
|
|
46
|
-
* Centralizes the repeated pattern of path validation across all methods
|
|
47
|
-
*/
|
|
48
|
-
async function validateAndResolvePath(filePath) {
|
|
49
|
-
const pathValidation = await validatePathSecure(filePath);
|
|
50
|
-
if (!pathValidation.success) {
|
|
51
|
-
return {
|
|
52
|
-
success: false,
|
|
53
|
-
error: ERROR_MESSAGES.SECURITY_ERROR(pathValidation.error || 'Unknown error'),
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
// Path is guaranteed when success is true, but provide fallback for type safety
|
|
57
|
-
return { path: pathValidation.path || filePath };
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Check if file exists and return appropriate error if not
|
|
61
|
-
*/
|
|
62
|
-
async function checkFileExists(filePath) {
|
|
63
|
-
const resolvedPath = path.resolve(filePath);
|
|
64
|
-
if (!(await fs.pathExists(resolvedPath))) {
|
|
65
|
-
return {
|
|
66
|
-
success: false,
|
|
67
|
-
error: ERROR_MESSAGES.FILE_NOT_FOUND(filePath),
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Atomically write content to file using temp file + rename pattern
|
|
74
|
-
* Ensures either full write or no write (prevents partial writes)
|
|
75
|
-
*/
|
|
76
|
-
async function atomicWriteFile(filePath, content) {
|
|
77
|
-
const tempPath = `${filePath}.tmp.${Date.now()}`;
|
|
78
|
-
try {
|
|
79
|
-
await writeFilePromise(tempPath, content, EDITOR_CONFIG.FILE_ENCODING);
|
|
80
|
-
await fs.rename(tempPath, filePath);
|
|
81
|
-
}
|
|
82
|
-
catch (writeError) {
|
|
83
|
-
// Clean up temp file on failure
|
|
84
|
-
try {
|
|
85
|
-
await fs.unlink(tempPath);
|
|
86
|
-
}
|
|
87
|
-
catch {
|
|
88
|
-
// Ignore cleanup errors
|
|
89
|
-
}
|
|
90
|
-
throw writeError;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
export class TextEditorTool {
|
|
94
|
-
editHistory = [];
|
|
95
|
-
confirmationService = ConfirmationService.getInstance();
|
|
96
|
-
checkpointCallback;
|
|
97
|
-
async view(filePath, viewRange) {
|
|
98
|
-
try {
|
|
99
|
-
// SECURITY: Validate path to prevent traversal attacks (REQ-SEC-002)
|
|
100
|
-
const pathResult = await validateAndResolvePath(filePath);
|
|
101
|
-
if ('success' in pathResult)
|
|
102
|
-
return pathResult; // Return error if validation failed
|
|
103
|
-
const resolvedPath = pathResult.path;
|
|
104
|
-
if (await fs.pathExists(resolvedPath)) {
|
|
105
|
-
const stats = await fs.stat(resolvedPath);
|
|
106
|
-
if (stats.isDirectory()) {
|
|
107
|
-
const files = await fs.readdir(resolvedPath);
|
|
108
|
-
return {
|
|
109
|
-
success: true,
|
|
110
|
-
output: `Directory contents of ${filePath}:\n${files.join("\n")}`,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
const content = await fs.readFile(resolvedPath, "utf-8");
|
|
114
|
-
const lines = content.split("\n");
|
|
115
|
-
if (viewRange) {
|
|
116
|
-
const [start, end] = viewRange;
|
|
117
|
-
// Validate line range
|
|
118
|
-
if (start < 1) {
|
|
119
|
-
return {
|
|
120
|
-
success: false,
|
|
121
|
-
error: `Invalid start line: ${start}. Line numbers must be >= 1.`,
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
if (end < start) {
|
|
125
|
-
return {
|
|
126
|
-
success: false,
|
|
127
|
-
error: `Invalid line range: end (${end}) must be >= start (${start}).`,
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
if (start > lines.length) {
|
|
131
|
-
return {
|
|
132
|
-
success: false,
|
|
133
|
-
error: `Start line ${start} exceeds file length (${lines.length} lines).`,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
// Clamp end to file length to avoid misleading output
|
|
137
|
-
const actualEnd = Math.min(end, lines.length);
|
|
138
|
-
const selectedLines = lines.slice(start - 1, actualEnd);
|
|
139
|
-
const numberedLines = selectedLines
|
|
140
|
-
.map((line, idx) => `${start + idx}: ${line}`)
|
|
141
|
-
.join("\n");
|
|
142
|
-
return {
|
|
143
|
-
success: true,
|
|
144
|
-
output: `Lines ${start}-${actualEnd} of ${filePath}:\n${numberedLines}`,
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
|
-
// Format all lines with line numbers
|
|
148
|
-
const allNumberedLines = lines
|
|
149
|
-
.map((line, idx) => `${idx + 1}: ${line}`)
|
|
150
|
-
.join("\n");
|
|
151
|
-
const fullOutput = `Contents of ${filePath}:\n${allNumberedLines}`;
|
|
152
|
-
// Apply message optimization for large files
|
|
153
|
-
const optimizer = getMessageOptimizer();
|
|
154
|
-
const optimized = optimizer.optimizeToolOutput(fullOutput, 'read_file');
|
|
155
|
-
return {
|
|
156
|
-
success: true,
|
|
157
|
-
output: optimized.content,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
return {
|
|
162
|
-
success: false,
|
|
163
|
-
error: ERROR_MESSAGES.PATH_NOT_FOUND(filePath),
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
catch (error) {
|
|
168
|
-
return createToolError(ErrorCategory.FILE_OPERATION, `View file`, error, { filePath });
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
async strReplace(filePath, oldStr, newStr, replaceAll = false) {
|
|
172
|
-
try {
|
|
173
|
-
// SECURITY: Validate path to prevent traversal attacks (REQ-SEC-002)
|
|
174
|
-
const pathResult = await validateAndResolvePath(filePath);
|
|
175
|
-
if ('success' in pathResult)
|
|
176
|
-
return pathResult;
|
|
177
|
-
const resolvedPath = pathResult.path;
|
|
178
|
-
// Validate inputs
|
|
179
|
-
if (!oldStr) {
|
|
180
|
-
return {
|
|
181
|
-
success: false,
|
|
182
|
-
error: ERROR_MESSAGES.EMPTY_SEARCH_STRING,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
// Check file exists
|
|
186
|
-
const fileExistsError = await checkFileExists(resolvedPath);
|
|
187
|
-
if (fileExistsError) {
|
|
188
|
-
return fileExistsError;
|
|
189
|
-
}
|
|
190
|
-
// RACE CONDITION FIX: Read file with stat to get mtime for comparison
|
|
191
|
-
const statsBefore = await fs.stat(resolvedPath);
|
|
192
|
-
const content = await fs.readFile(resolvedPath, "utf-8");
|
|
193
|
-
const mtimeBefore = statsBefore.mtimeMs;
|
|
194
|
-
// Use actualOldStr to avoid modifying the parameter
|
|
195
|
-
let actualOldStr = oldStr;
|
|
196
|
-
if (!content.includes(oldStr)) {
|
|
197
|
-
// Try whitespace-tolerant matching first
|
|
198
|
-
const normalizedMatch = this.findNormalizedMatch(content, oldStr);
|
|
199
|
-
if (normalizedMatch) {
|
|
200
|
-
actualOldStr = normalizedMatch;
|
|
201
|
-
}
|
|
202
|
-
else if (oldStr.includes('\n')) {
|
|
203
|
-
// Try fuzzy matching for multi-line strings
|
|
204
|
-
const fuzzyResult = this.findFuzzyMatch(content, oldStr);
|
|
205
|
-
if (fuzzyResult) {
|
|
206
|
-
actualOldStr = fuzzyResult;
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
// Try line-by-line similarity matching
|
|
210
|
-
const similarMatch = this.findSimilarBlock(content, oldStr);
|
|
211
|
-
if (similarMatch.match) {
|
|
212
|
-
actualOldStr = similarMatch.match;
|
|
213
|
-
}
|
|
214
|
-
else {
|
|
215
|
-
// Provide helpful error with closest match info and recovery guidance
|
|
216
|
-
const baseMsg = similarMatch.suggestion
|
|
217
|
-
? `String not found in file. ${similarMatch.suggestion}`
|
|
218
|
-
: ERROR_MESSAGES.STRING_NOT_FOUND_MULTI;
|
|
219
|
-
return {
|
|
220
|
-
success: false,
|
|
221
|
-
error: ERROR_MESSAGES.withRecoveryHint(baseMsg),
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
// Single line - try to find similar line
|
|
228
|
-
const similarLine = this.findSimilarLine(content, oldStr);
|
|
229
|
-
if (similarLine.match) {
|
|
230
|
-
actualOldStr = similarLine.match;
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
const baseMsg = similarLine.suggestion
|
|
234
|
-
? `String not found in file: "${oldStr}". ${similarLine.suggestion}`
|
|
235
|
-
: ERROR_MESSAGES.STRING_NOT_FOUND(oldStr);
|
|
236
|
-
return {
|
|
237
|
-
success: false,
|
|
238
|
-
error: ERROR_MESSAGES.withRecoveryHint(baseMsg),
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
// Count occurrences using simple string splitting (more reliable than regex for multi-line strings)
|
|
244
|
-
const occurrences = content.split(actualOldStr).length - 1;
|
|
245
|
-
// Generate new content and diff for confirmation
|
|
246
|
-
const newContent = replaceAll
|
|
247
|
-
? content.split(actualOldStr).join(newStr)
|
|
248
|
-
: content.replace(actualOldStr, newStr);
|
|
249
|
-
const oldLinesForDiff = content.split("\n");
|
|
250
|
-
const newLinesForDiff = newContent.split("\n");
|
|
251
|
-
let diffContent = this.generateDiff(oldLinesForDiff, newLinesForDiff, filePath);
|
|
252
|
-
// Phase 2: Check if file operation is destructive
|
|
253
|
-
const { isDestructive, matchedOperations } = isDestructiveFileOperation(filePath, 'edit');
|
|
254
|
-
// Get auto-accept configuration
|
|
255
|
-
const autoAcceptConfig = getSettingsManager().getAutoAcceptConfig();
|
|
256
|
-
const isAutoAcceptEnabled = this.confirmationService.getSessionFlags().allOperations === true;
|
|
257
|
-
// Determine if we should always confirm despite auto-accept
|
|
258
|
-
const shouldAlwaysConfirm = isDestructive && matchedOperations.some(op => autoAcceptConfig?.alwaysConfirm?.includes(op.id));
|
|
259
|
-
// Add safety warning to diff content if destructive
|
|
260
|
-
if (isDestructive) {
|
|
261
|
-
const warnings = matchedOperations.map(op => ` - ${op.name}: ${op.description}`).join('\n');
|
|
262
|
-
diffContent = `⚠️ WARNING: This operation is flagged as destructive:\n${warnings}\n\n${diffContent}`;
|
|
263
|
-
}
|
|
264
|
-
const shouldProceed = await this.confirmationService.shouldProceed('file', {
|
|
265
|
-
operation: `Edit file${replaceAll && occurrences > 1 ? ` (${occurrences} occurrences)` : ''}`,
|
|
266
|
-
filename: filePath,
|
|
267
|
-
showVSCodeOpen: false,
|
|
268
|
-
content: diffContent,
|
|
269
|
-
alwaysConfirm: shouldAlwaysConfirm, // Force confirmation for destructive ops
|
|
270
|
-
// VS Code IPC diff preview fields
|
|
271
|
-
oldContent: content,
|
|
272
|
-
newContent: newContent,
|
|
273
|
-
diffOperation: 'edit',
|
|
274
|
-
});
|
|
275
|
-
// Phase 2: Log to audit logger
|
|
276
|
-
if (autoAcceptConfig && autoAcceptConfig.auditLog?.enabled) {
|
|
277
|
-
const logger = getAutoAcceptLogger();
|
|
278
|
-
logger.logFileOperation('edit', filePath, isDestructive, shouldProceed, // userConfirmed = true if user confirmed (shouldProceed=true)
|
|
279
|
-
isAutoAcceptEnabled && !shouldAlwaysConfirm, // autoAccepted = true if auto-accept AND not forced confirm
|
|
280
|
-
autoAcceptConfig.scope || 'session');
|
|
281
|
-
}
|
|
282
|
-
if (!shouldProceed) {
|
|
283
|
-
return {
|
|
284
|
-
success: false,
|
|
285
|
-
error: ERROR_MESSAGES.EDIT_CANCELLED("File edit"),
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
// RACE CONDITION FIX: Check if file was modified during confirmation
|
|
289
|
-
// This prevents data loss from concurrent modifications
|
|
290
|
-
const statsAfterConfirm = await fs.stat(resolvedPath);
|
|
291
|
-
if (statsAfterConfirm.mtimeMs !== mtimeBefore) {
|
|
292
|
-
return {
|
|
293
|
-
success: false,
|
|
294
|
-
error: ERROR_MESSAGES.FILE_MODIFIED_DURING_CONFIRMATION,
|
|
295
|
-
};
|
|
296
|
-
}
|
|
297
|
-
// Create checkpoint before modification
|
|
298
|
-
await this.createCheckpointIfNeeded(filePath, 'str_replace');
|
|
299
|
-
// Atomic write
|
|
300
|
-
await atomicWriteFile(resolvedPath, newContent);
|
|
301
|
-
this.editHistory.push({
|
|
302
|
-
command: "str_replace",
|
|
303
|
-
path: filePath,
|
|
304
|
-
old_str: oldStr,
|
|
305
|
-
new_str: newStr,
|
|
306
|
-
});
|
|
307
|
-
// Reveal the file in VS Code (like Claude Code)
|
|
308
|
-
const ipcClient = getVSCodeIPCClient();
|
|
309
|
-
ipcClient.revealFile(resolvedPath, 'edit');
|
|
310
|
-
return {
|
|
311
|
-
success: true,
|
|
312
|
-
output: diffContent,
|
|
313
|
-
};
|
|
314
|
-
}
|
|
315
|
-
catch (error) {
|
|
316
|
-
return createToolError(ErrorCategory.FILE_OPERATION, `Replace text`, error, { filePath });
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
async create(filePath, content) {
|
|
320
|
-
try {
|
|
321
|
-
// SECURITY: Validate path to prevent traversal attacks (REQ-SEC-002)
|
|
322
|
-
const pathResult = await validateAndResolvePath(filePath);
|
|
323
|
-
if ('success' in pathResult)
|
|
324
|
-
return pathResult;
|
|
325
|
-
const resolvedPath = pathResult.path;
|
|
326
|
-
// Create a diff-style preview for file creation
|
|
327
|
-
const contentLines = content.split("\n");
|
|
328
|
-
let diffContent = [
|
|
329
|
-
`Created ${filePath}`,
|
|
330
|
-
`--- /dev/null`,
|
|
331
|
-
`+++ b/${filePath}`,
|
|
332
|
-
`@@ -0,0 +1,${contentLines.length} @@`,
|
|
333
|
-
...contentLines.map((line) => `+${line}`),
|
|
334
|
-
].join("\n");
|
|
335
|
-
// Phase 2: Check if file operation is destructive
|
|
336
|
-
const { isDestructive, matchedOperations } = isDestructiveFileOperation(filePath, 'write');
|
|
337
|
-
// Get auto-accept configuration
|
|
338
|
-
const autoAcceptConfig = getSettingsManager().getAutoAcceptConfig();
|
|
339
|
-
const isAutoAcceptEnabled = this.confirmationService.getSessionFlags().allOperations === true;
|
|
340
|
-
// Determine if we should always confirm despite auto-accept
|
|
341
|
-
const shouldAlwaysConfirm = isDestructive && matchedOperations.some(op => autoAcceptConfig?.alwaysConfirm?.includes(op.id));
|
|
342
|
-
// Add safety warning to diff content if destructive
|
|
343
|
-
if (isDestructive) {
|
|
344
|
-
const warnings = matchedOperations.map(op => ` - ${op.name}: ${op.description}`).join('\n');
|
|
345
|
-
diffContent = `⚠️ WARNING: This operation is flagged as destructive:\n${warnings}\n\n${diffContent}`;
|
|
346
|
-
}
|
|
347
|
-
const shouldProceed = await this.confirmationService.shouldProceed('file', {
|
|
348
|
-
operation: "Write",
|
|
349
|
-
filename: filePath,
|
|
350
|
-
showVSCodeOpen: false,
|
|
351
|
-
content: diffContent,
|
|
352
|
-
alwaysConfirm: shouldAlwaysConfirm, // Force confirmation for destructive ops
|
|
353
|
-
// VS Code IPC diff preview fields
|
|
354
|
-
oldContent: '', // Empty for new file
|
|
355
|
-
newContent: content,
|
|
356
|
-
diffOperation: 'create',
|
|
357
|
-
});
|
|
358
|
-
// Phase 2: Log to audit logger
|
|
359
|
-
if (autoAcceptConfig && autoAcceptConfig.auditLog?.enabled) {
|
|
360
|
-
const logger = getAutoAcceptLogger();
|
|
361
|
-
logger.logFileOperation('write', filePath, isDestructive, shouldProceed, // userConfirmed = true if user confirmed (shouldProceed=true)
|
|
362
|
-
isAutoAcceptEnabled && !shouldAlwaysConfirm, // autoAccepted = true if auto-accept AND not forced confirm
|
|
363
|
-
autoAcceptConfig.scope || 'session');
|
|
364
|
-
}
|
|
365
|
-
if (!shouldProceed) {
|
|
366
|
-
return {
|
|
367
|
-
success: false,
|
|
368
|
-
error: ERROR_MESSAGES.EDIT_CANCELLED("File creation"),
|
|
369
|
-
};
|
|
370
|
-
}
|
|
371
|
-
// Create checkpoint before modification (will skip if file doesn't exist)
|
|
372
|
-
await this.createCheckpointIfNeeded(filePath, 'create');
|
|
373
|
-
const dir = path.dirname(resolvedPath);
|
|
374
|
-
await fs.ensureDir(dir);
|
|
375
|
-
// Atomic write
|
|
376
|
-
await atomicWriteFile(resolvedPath, content);
|
|
377
|
-
this.editHistory.push({
|
|
378
|
-
command: "create",
|
|
379
|
-
path: filePath,
|
|
380
|
-
content,
|
|
381
|
-
});
|
|
382
|
-
// Reveal the file in VS Code (like Claude Code)
|
|
383
|
-
const ipcClient = getVSCodeIPCClient();
|
|
384
|
-
ipcClient.revealFile(resolvedPath, 'create');
|
|
385
|
-
// Generate diff output using the same method as str_replace
|
|
386
|
-
const oldLines = []; // Empty for new files
|
|
387
|
-
const newLines = content.split("\n");
|
|
388
|
-
const diff = this.generateDiff(oldLines, newLines, filePath);
|
|
389
|
-
return {
|
|
390
|
-
success: true,
|
|
391
|
-
output: diff,
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
catch (error) {
|
|
395
|
-
return createToolError(ErrorCategory.FILE_OPERATION, `Create file`, error, { filePath });
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
async replaceLines(filePath, startLine, endLine, newContent) {
|
|
399
|
-
try {
|
|
400
|
-
// SECURITY: Validate path to prevent traversal attacks (REQ-SEC-002)
|
|
401
|
-
const pathResult = await validateAndResolvePath(filePath);
|
|
402
|
-
if ('success' in pathResult)
|
|
403
|
-
return pathResult;
|
|
404
|
-
const resolvedPath = pathResult.path;
|
|
405
|
-
const fileExistsError = await checkFileExists(resolvedPath);
|
|
406
|
-
if (fileExistsError) {
|
|
407
|
-
return fileExistsError;
|
|
408
|
-
}
|
|
409
|
-
const fileContent = await fs.readFile(resolvedPath, "utf-8");
|
|
410
|
-
const lines = fileContent.split("\n");
|
|
411
|
-
if (startLine < 1 || startLine > lines.length) {
|
|
412
|
-
return {
|
|
413
|
-
success: false,
|
|
414
|
-
error: `Invalid start line: ${startLine}. File has ${lines.length} lines.`,
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
if (endLine < startLine || endLine > lines.length) {
|
|
418
|
-
return {
|
|
419
|
-
success: false,
|
|
420
|
-
error: `Invalid end line: ${endLine}. Must be between ${startLine} and ${lines.length}.`,
|
|
421
|
-
};
|
|
422
|
-
}
|
|
423
|
-
const sessionFlags = this.confirmationService.getSessionFlags();
|
|
424
|
-
if (!sessionFlags.fileOperations && !sessionFlags.allOperations) {
|
|
425
|
-
const newLines = [...lines];
|
|
426
|
-
const replacementLines = newContent.split("\n");
|
|
427
|
-
newLines.splice(startLine - 1, endLine - startLine + 1, ...replacementLines);
|
|
428
|
-
const diffContent = this.generateDiff(lines, newLines, filePath);
|
|
429
|
-
const confirmationResult = await this.confirmationService.requestConfirmation({
|
|
430
|
-
operation: `Replace lines ${startLine}-${endLine}`,
|
|
431
|
-
filename: filePath,
|
|
432
|
-
showVSCodeOpen: false,
|
|
433
|
-
content: diffContent,
|
|
434
|
-
oldContent: fileContent,
|
|
435
|
-
newContent: newLines.join("\n"),
|
|
436
|
-
diffOperation: 'edit',
|
|
437
|
-
lineStart: startLine,
|
|
438
|
-
lineEnd: endLine,
|
|
439
|
-
}, "file");
|
|
440
|
-
if (!confirmationResult.confirmed) {
|
|
441
|
-
return {
|
|
442
|
-
success: false,
|
|
443
|
-
error: confirmationResult.feedback || ERROR_MESSAGES.EDIT_CANCELLED("Line replacement"),
|
|
444
|
-
};
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
// Create checkpoint before modification
|
|
448
|
-
await this.createCheckpointIfNeeded(filePath, 'replace_lines');
|
|
449
|
-
const replacementLines = newContent.split("\n");
|
|
450
|
-
lines.splice(startLine - 1, endLine - startLine + 1, ...replacementLines);
|
|
451
|
-
const newFileContent = lines.join("\n");
|
|
452
|
-
// Atomic write
|
|
453
|
-
await atomicWriteFile(resolvedPath, newFileContent);
|
|
454
|
-
this.editHistory.push({
|
|
455
|
-
command: "str_replace",
|
|
456
|
-
path: filePath,
|
|
457
|
-
old_str: `lines ${startLine}-${endLine}`,
|
|
458
|
-
new_str: newContent,
|
|
459
|
-
});
|
|
460
|
-
// Reveal the file in VS Code (like Claude Code)
|
|
461
|
-
const ipcClient = getVSCodeIPCClient();
|
|
462
|
-
ipcClient.revealFile(resolvedPath, 'edit');
|
|
463
|
-
const oldLines = fileContent.split("\n");
|
|
464
|
-
const diff = this.generateDiff(oldLines, lines, filePath);
|
|
465
|
-
return {
|
|
466
|
-
success: true,
|
|
467
|
-
output: diff,
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
catch (error) {
|
|
471
|
-
return createToolError(ErrorCategory.FILE_OPERATION, `Replace lines`, error, { filePath });
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
async insert(filePath, insertLine, content) {
|
|
475
|
-
try {
|
|
476
|
-
// SECURITY: Validate path to prevent traversal attacks (REQ-SEC-002)
|
|
477
|
-
const pathResult = await validateAndResolvePath(filePath);
|
|
478
|
-
if ('success' in pathResult)
|
|
479
|
-
return pathResult;
|
|
480
|
-
const resolvedPath = pathResult.path;
|
|
481
|
-
const fileExistsError = await checkFileExists(resolvedPath);
|
|
482
|
-
if (fileExistsError) {
|
|
483
|
-
return fileExistsError;
|
|
484
|
-
}
|
|
485
|
-
const fileContent = await fs.readFile(resolvedPath, "utf-8");
|
|
486
|
-
const lines = fileContent.split("\n");
|
|
487
|
-
// Validate insert line
|
|
488
|
-
if (insertLine < 1) {
|
|
489
|
-
return {
|
|
490
|
-
success: false,
|
|
491
|
-
error: `Invalid insert line: ${insertLine}. Line numbers must be >= 1.`,
|
|
492
|
-
};
|
|
493
|
-
}
|
|
494
|
-
if (insertLine > lines.length + 1) {
|
|
495
|
-
return {
|
|
496
|
-
success: false,
|
|
497
|
-
error: `Invalid insert line: ${insertLine}. File has ${lines.length} lines (can insert at line ${lines.length + 1} to append).`,
|
|
498
|
-
};
|
|
499
|
-
}
|
|
500
|
-
// Split content into individual lines for proper handling
|
|
501
|
-
// This ensures multi-line content is correctly inserted and can be undone
|
|
502
|
-
const contentLines = content.split("\n");
|
|
503
|
-
const newLines = [...lines];
|
|
504
|
-
newLines.splice(insertLine - 1, 0, ...contentLines);
|
|
505
|
-
const newContent = newLines.join("\n");
|
|
506
|
-
// Generate diff for confirmation
|
|
507
|
-
const diffContent = this.generateDiff(lines, newLines, filePath);
|
|
508
|
-
// Request confirmation (consistent with other editing methods)
|
|
509
|
-
const shouldProceed = await this.confirmationService.shouldProceed('file', {
|
|
510
|
-
operation: `Insert ${contentLines.length} line${contentLines.length !== 1 ? 's' : ''} at line ${insertLine}`,
|
|
511
|
-
filename: filePath,
|
|
512
|
-
showVSCodeOpen: false,
|
|
513
|
-
content: diffContent,
|
|
514
|
-
oldContent: fileContent,
|
|
515
|
-
newContent: newContent,
|
|
516
|
-
diffOperation: 'edit',
|
|
517
|
-
lineStart: insertLine,
|
|
518
|
-
lineEnd: insertLine,
|
|
519
|
-
});
|
|
520
|
-
if (!shouldProceed) {
|
|
521
|
-
return {
|
|
522
|
-
success: false,
|
|
523
|
-
error: ERROR_MESSAGES.EDIT_CANCELLED("Insert operation"),
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
// Create checkpoint before modification
|
|
527
|
-
await this.createCheckpointIfNeeded(filePath, 'insert');
|
|
528
|
-
// Atomic write
|
|
529
|
-
await atomicWriteFile(resolvedPath, newContent);
|
|
530
|
-
this.editHistory.push({
|
|
531
|
-
command: "insert",
|
|
532
|
-
path: filePath,
|
|
533
|
-
insert_line: insertLine,
|
|
534
|
-
content,
|
|
535
|
-
});
|
|
536
|
-
// Reveal the file in VS Code (like Claude Code)
|
|
537
|
-
const ipcClient = getVSCodeIPCClient();
|
|
538
|
-
ipcClient.revealFile(resolvedPath, 'edit');
|
|
539
|
-
return {
|
|
540
|
-
success: true,
|
|
541
|
-
output: diffContent,
|
|
542
|
-
};
|
|
543
|
-
}
|
|
544
|
-
catch (error) {
|
|
545
|
-
return createToolError(ErrorCategory.FILE_OPERATION, `Insert content`, error, { filePath });
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
/**
|
|
549
|
-
* Make multiple edits to a file in a single atomic operation.
|
|
550
|
-
* All edits are validated and previewed before applying.
|
|
551
|
-
* If any edit fails validation, the entire operation is aborted.
|
|
552
|
-
*/
|
|
553
|
-
async multiEdit(filePath, edits) {
|
|
554
|
-
try {
|
|
555
|
-
// SECURITY: Validate path to prevent traversal attacks
|
|
556
|
-
const pathResult = await validateAndResolvePath(filePath);
|
|
557
|
-
if ('success' in pathResult)
|
|
558
|
-
return pathResult;
|
|
559
|
-
const resolvedPath = pathResult.path;
|
|
560
|
-
// Validate inputs
|
|
561
|
-
if (!edits || edits.length === 0) {
|
|
562
|
-
return {
|
|
563
|
-
success: false,
|
|
564
|
-
error: ERROR_MESSAGES.NO_EDITS_PROVIDED,
|
|
565
|
-
};
|
|
566
|
-
}
|
|
567
|
-
for (let i = 0; i < edits.length; i++) {
|
|
568
|
-
if (!edits[i].old_str) {
|
|
569
|
-
return {
|
|
570
|
-
success: false,
|
|
571
|
-
error: `Edit ${i + 1}: old_str cannot be empty`,
|
|
572
|
-
};
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
// Check file exists
|
|
576
|
-
const fileExistsError = await checkFileExists(resolvedPath);
|
|
577
|
-
if (fileExistsError) {
|
|
578
|
-
return fileExistsError;
|
|
579
|
-
}
|
|
580
|
-
// Read file with stat to get mtime for race condition protection
|
|
581
|
-
const statsBefore = await fs.stat(resolvedPath);
|
|
582
|
-
const originalContent = await fs.readFile(resolvedPath, "utf-8");
|
|
583
|
-
const mtimeBefore = statsBefore.mtimeMs;
|
|
584
|
-
// Apply all edits sequentially to validate and generate preview
|
|
585
|
-
let workingContent = originalContent;
|
|
586
|
-
const appliedEdits = [];
|
|
587
|
-
for (let i = 0; i < edits.length; i++) {
|
|
588
|
-
const edit = edits[i];
|
|
589
|
-
let actualOldStr = edit.old_str;
|
|
590
|
-
if (!workingContent.includes(edit.old_str)) {
|
|
591
|
-
// Try whitespace-tolerant matching
|
|
592
|
-
const normalizedMatch = this.findNormalizedMatch(workingContent, edit.old_str);
|
|
593
|
-
if (normalizedMatch) {
|
|
594
|
-
actualOldStr = normalizedMatch;
|
|
595
|
-
}
|
|
596
|
-
else if (edit.old_str.includes('\n')) {
|
|
597
|
-
// Try fuzzy matching for multi-line strings
|
|
598
|
-
const fuzzyResult = this.findFuzzyMatch(workingContent, edit.old_str);
|
|
599
|
-
if (fuzzyResult) {
|
|
600
|
-
actualOldStr = fuzzyResult;
|
|
601
|
-
}
|
|
602
|
-
else {
|
|
603
|
-
const similarMatch = this.findSimilarBlock(workingContent, edit.old_str);
|
|
604
|
-
if (similarMatch.match) {
|
|
605
|
-
actualOldStr = similarMatch.match;
|
|
606
|
-
}
|
|
607
|
-
else {
|
|
608
|
-
const baseMsg = similarMatch.suggestion
|
|
609
|
-
? `Edit ${i + 1}: String not found. ${similarMatch.suggestion}`
|
|
610
|
-
: `Edit ${i + 1}: String not found in file (after applying previous edits).`;
|
|
611
|
-
return {
|
|
612
|
-
success: false,
|
|
613
|
-
error: ERROR_MESSAGES.withRecoveryHint(baseMsg),
|
|
614
|
-
};
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
else {
|
|
619
|
-
const similarLine = this.findSimilarLine(workingContent, edit.old_str);
|
|
620
|
-
if (similarLine.match) {
|
|
621
|
-
actualOldStr = similarLine.match;
|
|
622
|
-
}
|
|
623
|
-
else {
|
|
624
|
-
const baseMsg = similarLine.suggestion
|
|
625
|
-
? `Edit ${i + 1}: String not found: "${edit.old_str}". ${similarLine.suggestion}`
|
|
626
|
-
: `Edit ${i + 1}: String not found: "${edit.old_str}".`;
|
|
627
|
-
return {
|
|
628
|
-
success: false,
|
|
629
|
-
error: ERROR_MESSAGES.withRecoveryHint(baseMsg),
|
|
630
|
-
};
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
// Apply this edit to working content
|
|
635
|
-
workingContent = workingContent.replace(actualOldStr, edit.new_str);
|
|
636
|
-
appliedEdits.push({ ...edit, actualOldStr });
|
|
637
|
-
}
|
|
638
|
-
// Generate unified diff showing all changes
|
|
639
|
-
const oldLines = originalContent.split("\n");
|
|
640
|
-
const newLines = workingContent.split("\n");
|
|
641
|
-
let diffContent = this.generateDiff(oldLines, newLines, filePath);
|
|
642
|
-
// Add edit count to diff header
|
|
643
|
-
diffContent = `Multi-edit: ${edits.length} changes\n${diffContent}`;
|
|
644
|
-
// Check if file operation is destructive
|
|
645
|
-
const { isDestructive, matchedOperations } = isDestructiveFileOperation(filePath, 'edit');
|
|
646
|
-
// Get auto-accept configuration
|
|
647
|
-
const autoAcceptConfig = getSettingsManager().getAutoAcceptConfig();
|
|
648
|
-
const isAutoAcceptEnabled = this.confirmationService.getSessionFlags().allOperations === true;
|
|
649
|
-
const shouldAlwaysConfirm = isDestructive && matchedOperations.some(op => autoAcceptConfig?.alwaysConfirm?.includes(op.id));
|
|
650
|
-
if (isDestructive) {
|
|
651
|
-
const warnings = matchedOperations.map(op => ` - ${op.name}: ${op.description}`).join('\n');
|
|
652
|
-
diffContent = `⚠️ WARNING: This operation is flagged as destructive:\n${warnings}\n\n${diffContent}`;
|
|
653
|
-
}
|
|
654
|
-
const shouldProceed = await this.confirmationService.shouldProceed('file', {
|
|
655
|
-
operation: `Multi-edit (${edits.length} changes)`,
|
|
656
|
-
filename: filePath,
|
|
657
|
-
showVSCodeOpen: false,
|
|
658
|
-
content: diffContent,
|
|
659
|
-
alwaysConfirm: shouldAlwaysConfirm,
|
|
660
|
-
// VS Code IPC diff preview fields
|
|
661
|
-
oldContent: originalContent,
|
|
662
|
-
newContent: workingContent,
|
|
663
|
-
diffOperation: 'edit',
|
|
664
|
-
});
|
|
665
|
-
// Audit logging
|
|
666
|
-
if (autoAcceptConfig && autoAcceptConfig.auditLog?.enabled) {
|
|
667
|
-
const logger = getAutoAcceptLogger();
|
|
668
|
-
logger.logFileOperation('edit', filePath, isDestructive, shouldProceed, // BUG FIX: was !shouldProceed (inverted)
|
|
669
|
-
isAutoAcceptEnabled && !shouldAlwaysConfirm, autoAcceptConfig.scope || 'session');
|
|
670
|
-
}
|
|
671
|
-
if (!shouldProceed) {
|
|
672
|
-
return {
|
|
673
|
-
success: false,
|
|
674
|
-
error: ERROR_MESSAGES.EDIT_CANCELLED("Multi-edit"),
|
|
675
|
-
};
|
|
676
|
-
}
|
|
677
|
-
// Race condition check
|
|
678
|
-
const statsAfterConfirm = await fs.stat(resolvedPath);
|
|
679
|
-
if (statsAfterConfirm.mtimeMs !== mtimeBefore) {
|
|
680
|
-
return {
|
|
681
|
-
success: false,
|
|
682
|
-
error: ERROR_MESSAGES.FILE_MODIFIED_DURING_CONFIRMATION,
|
|
683
|
-
};
|
|
684
|
-
}
|
|
685
|
-
// Create checkpoint before modification
|
|
686
|
-
await this.createCheckpointIfNeeded(filePath, 'multi_edit');
|
|
687
|
-
// Atomic write
|
|
688
|
-
await atomicWriteFile(resolvedPath, workingContent);
|
|
689
|
-
// Record in edit history
|
|
690
|
-
this.editHistory.push({
|
|
691
|
-
command: "str_replace",
|
|
692
|
-
path: filePath,
|
|
693
|
-
old_str: `[multi-edit: ${edits.length} changes]`,
|
|
694
|
-
new_str: workingContent,
|
|
695
|
-
});
|
|
696
|
-
// Reveal the file in VS Code (like Claude Code)
|
|
697
|
-
const ipcClient = getVSCodeIPCClient();
|
|
698
|
-
ipcClient.revealFile(resolvedPath, 'edit');
|
|
699
|
-
return {
|
|
700
|
-
success: true,
|
|
701
|
-
output: diffContent,
|
|
702
|
-
};
|
|
703
|
-
}
|
|
704
|
-
catch (error) {
|
|
705
|
-
return createToolError(ErrorCategory.FILE_OPERATION, `Multi-edit file`, error, { filePath });
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
async undoEdit() {
|
|
709
|
-
if (this.editHistory.length === 0) {
|
|
710
|
-
return {
|
|
711
|
-
success: false,
|
|
712
|
-
error: ERROR_MESSAGES.NO_EDITS_TO_UNDO,
|
|
713
|
-
};
|
|
714
|
-
}
|
|
715
|
-
const lastEdit = this.editHistory.pop();
|
|
716
|
-
if (!lastEdit) {
|
|
717
|
-
return {
|
|
718
|
-
success: false,
|
|
719
|
-
error: "Failed to retrieve last edit",
|
|
720
|
-
};
|
|
721
|
-
}
|
|
722
|
-
try {
|
|
723
|
-
switch (lastEdit.command) {
|
|
724
|
-
case "str_replace":
|
|
725
|
-
if (lastEdit.path && lastEdit.old_str && lastEdit.new_str) {
|
|
726
|
-
const content = await fs.readFile(lastEdit.path, "utf-8");
|
|
727
|
-
const revertedContent = content.replace(lastEdit.new_str, lastEdit.old_str);
|
|
728
|
-
await writeFilePromise(lastEdit.path, revertedContent, "utf-8");
|
|
729
|
-
}
|
|
730
|
-
break;
|
|
731
|
-
case "create":
|
|
732
|
-
if (lastEdit.path) {
|
|
733
|
-
await fs.remove(lastEdit.path);
|
|
734
|
-
}
|
|
735
|
-
break;
|
|
736
|
-
case "insert":
|
|
737
|
-
if (lastEdit.path && lastEdit.insert_line && lastEdit.content !== undefined) {
|
|
738
|
-
const content = await fs.readFile(lastEdit.path, "utf-8");
|
|
739
|
-
const lines = content.split("\n");
|
|
740
|
-
// Count how many lines were inserted (content may be multi-line)
|
|
741
|
-
const insertedLineCount = lastEdit.content.split("\n").length;
|
|
742
|
-
lines.splice(lastEdit.insert_line - 1, insertedLineCount);
|
|
743
|
-
await writeFilePromise(lastEdit.path, lines.join("\n"), "utf-8");
|
|
744
|
-
}
|
|
745
|
-
break;
|
|
746
|
-
}
|
|
747
|
-
return {
|
|
748
|
-
success: true,
|
|
749
|
-
output: `Successfully undid ${lastEdit.command} operation`,
|
|
750
|
-
};
|
|
751
|
-
}
|
|
752
|
-
catch (error) {
|
|
753
|
-
return {
|
|
754
|
-
success: false,
|
|
755
|
-
error: `Error undoing edit: ${error.message}`,
|
|
756
|
-
};
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
findFuzzyMatch(content, searchStr) {
|
|
760
|
-
const functionMatch = searchStr.match(/function\s+(\w+)/);
|
|
761
|
-
if (!functionMatch || !functionMatch[1])
|
|
762
|
-
return null;
|
|
763
|
-
const functionName = functionMatch[1];
|
|
764
|
-
const contentLines = content.split('\n');
|
|
765
|
-
let functionStart = -1;
|
|
766
|
-
for (let i = 0; i < contentLines.length; i++) {
|
|
767
|
-
if (contentLines[i].includes(`function ${functionName}`) && contentLines[i].includes('{')) {
|
|
768
|
-
functionStart = i;
|
|
769
|
-
break;
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
if (functionStart === -1)
|
|
773
|
-
return null;
|
|
774
|
-
let braceCount = 0;
|
|
775
|
-
let functionEnd = functionStart;
|
|
776
|
-
let foundClosingBrace = false;
|
|
777
|
-
for (let i = functionStart; i < contentLines.length; i++) {
|
|
778
|
-
const line = contentLines[i];
|
|
779
|
-
for (const char of line) {
|
|
780
|
-
if (char === '{')
|
|
781
|
-
braceCount++;
|
|
782
|
-
if (char === '}')
|
|
783
|
-
braceCount--;
|
|
784
|
-
}
|
|
785
|
-
if (braceCount === 0 && i > functionStart) {
|
|
786
|
-
functionEnd = i;
|
|
787
|
-
foundClosingBrace = true;
|
|
788
|
-
break;
|
|
789
|
-
}
|
|
790
|
-
}
|
|
791
|
-
// BUG FIX: Reject unclosed functions (braces not balanced)
|
|
792
|
-
if (!foundClosingBrace && braceCount !== 0) {
|
|
793
|
-
return null;
|
|
794
|
-
}
|
|
795
|
-
const actualFunction = contentLines.slice(functionStart, functionEnd + 1).join('\n');
|
|
796
|
-
const searchNormalized = this.normalizeForComparison(searchStr);
|
|
797
|
-
const actualNormalized = this.normalizeForComparison(actualFunction);
|
|
798
|
-
if (this.isSimilarStructure(searchNormalized, actualNormalized)) {
|
|
799
|
-
return actualFunction;
|
|
800
|
-
}
|
|
801
|
-
return null;
|
|
802
|
-
}
|
|
803
|
-
normalizeForComparison(str) {
|
|
804
|
-
return str
|
|
805
|
-
.replace(/["'`]/g, '"')
|
|
806
|
-
.replace(/\s+/g, ' ')
|
|
807
|
-
.replace(/{\s+/g, '{ ')
|
|
808
|
-
.replace(/\s+}/g, ' }')
|
|
809
|
-
.replace(/;\s*/g, ';')
|
|
810
|
-
.trim();
|
|
811
|
-
}
|
|
812
|
-
isSimilarStructure(search, actual) {
|
|
813
|
-
const extractTokens = (str) => {
|
|
814
|
-
const tokens = str.match(/\b(function|console\.log|return|if|else|for|while)\b/g) || [];
|
|
815
|
-
return tokens;
|
|
816
|
-
};
|
|
817
|
-
const searchTokens = extractTokens(search);
|
|
818
|
-
const actualTokens = extractTokens(actual);
|
|
819
|
-
if (searchTokens.length !== actualTokens.length)
|
|
820
|
-
return false;
|
|
821
|
-
for (let i = 0; i < searchTokens.length; i++) {
|
|
822
|
-
if (searchTokens[i] !== actualTokens[i])
|
|
823
|
-
return false;
|
|
824
|
-
}
|
|
825
|
-
return true;
|
|
826
|
-
}
|
|
827
|
-
generateDiff(oldLines, newLines, filePath) {
|
|
828
|
-
const CONTEXT_LINES = EDITOR_CONFIG.DIFF_CONTEXT_LINES;
|
|
829
|
-
const changes = [];
|
|
830
|
-
let i = 0, j = 0;
|
|
831
|
-
while (i < oldLines.length || j < newLines.length) {
|
|
832
|
-
while (i < oldLines.length && j < newLines.length && oldLines[i] === newLines[j]) {
|
|
833
|
-
i++;
|
|
834
|
-
j++;
|
|
835
|
-
}
|
|
836
|
-
if (i < oldLines.length || j < newLines.length) {
|
|
837
|
-
const changeStart = { old: i, new: j };
|
|
838
|
-
let oldEnd = i;
|
|
839
|
-
let newEnd = j;
|
|
840
|
-
// BUG FIX: Add iteration counter to prevent O(n²) complexity on adversarial inputs
|
|
841
|
-
const maxIterations = oldLines.length + newLines.length;
|
|
842
|
-
let iterations = 0;
|
|
843
|
-
while (oldEnd < oldLines.length || newEnd < newLines.length) {
|
|
844
|
-
// Safety check: prevent excessive iterations
|
|
845
|
-
if (++iterations > maxIterations) {
|
|
846
|
-
break;
|
|
847
|
-
}
|
|
848
|
-
let matchFound = false;
|
|
849
|
-
let matchLength = 0;
|
|
850
|
-
for (let k = 0; k < Math.min(2, oldLines.length - oldEnd, newLines.length - newEnd); k++) {
|
|
851
|
-
if (oldEnd + k < oldLines.length &&
|
|
852
|
-
newEnd + k < newLines.length &&
|
|
853
|
-
oldLines[oldEnd + k] === newLines[newEnd + k]) {
|
|
854
|
-
matchLength++;
|
|
855
|
-
}
|
|
856
|
-
else {
|
|
857
|
-
break;
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
if (matchLength >= 2 || (oldEnd >= oldLines.length && newEnd >= newLines.length)) {
|
|
861
|
-
matchFound = true;
|
|
862
|
-
}
|
|
863
|
-
if (matchFound) {
|
|
864
|
-
break;
|
|
865
|
-
}
|
|
866
|
-
// Prevent infinite loop - ensure progress is made
|
|
867
|
-
let progress = false;
|
|
868
|
-
if (oldEnd < oldLines.length) {
|
|
869
|
-
oldEnd++;
|
|
870
|
-
progress = true;
|
|
871
|
-
}
|
|
872
|
-
if (newEnd < newLines.length) {
|
|
873
|
-
newEnd++;
|
|
874
|
-
progress = true;
|
|
875
|
-
}
|
|
876
|
-
if (!progress) {
|
|
877
|
-
// Both reached end, force exit to prevent infinite loop
|
|
878
|
-
break;
|
|
879
|
-
}
|
|
880
|
-
}
|
|
881
|
-
changes.push({
|
|
882
|
-
oldStart: changeStart.old,
|
|
883
|
-
oldEnd: oldEnd,
|
|
884
|
-
newStart: changeStart.new,
|
|
885
|
-
newEnd: newEnd
|
|
886
|
-
});
|
|
887
|
-
i = oldEnd;
|
|
888
|
-
j = newEnd;
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
const hunks = [];
|
|
892
|
-
let accumulatedOffset = 0;
|
|
893
|
-
for (let changeIdx = 0; changeIdx < changes.length; changeIdx++) {
|
|
894
|
-
const change = changes[changeIdx];
|
|
895
|
-
let contextStart = Math.max(0, change.oldStart - CONTEXT_LINES);
|
|
896
|
-
let contextEnd = Math.min(oldLines.length, change.oldEnd + CONTEXT_LINES);
|
|
897
|
-
if (hunks.length > 0) {
|
|
898
|
-
const lastHunk = hunks[hunks.length - 1];
|
|
899
|
-
const lastHunkEnd = lastHunk.oldStart + lastHunk.oldCount;
|
|
900
|
-
if (lastHunkEnd >= contextStart) {
|
|
901
|
-
const oldHunkEnd = lastHunk.oldStart + lastHunk.oldCount;
|
|
902
|
-
const newContextEnd = Math.min(oldLines.length, change.oldEnd + CONTEXT_LINES);
|
|
903
|
-
for (let idx = oldHunkEnd; idx < change.oldStart; idx++) {
|
|
904
|
-
lastHunk.lines.push({ type: ' ', content: oldLines[idx] });
|
|
905
|
-
}
|
|
906
|
-
for (let idx = change.oldStart; idx < change.oldEnd; idx++) {
|
|
907
|
-
lastHunk.lines.push({ type: '-', content: oldLines[idx] });
|
|
908
|
-
}
|
|
909
|
-
for (let idx = change.newStart; idx < change.newEnd; idx++) {
|
|
910
|
-
lastHunk.lines.push({ type: '+', content: newLines[idx] });
|
|
911
|
-
}
|
|
912
|
-
for (let idx = change.oldEnd; idx < newContextEnd && idx < oldLines.length; idx++) {
|
|
913
|
-
lastHunk.lines.push({ type: ' ', content: oldLines[idx] });
|
|
914
|
-
}
|
|
915
|
-
lastHunk.oldCount = newContextEnd - lastHunk.oldStart;
|
|
916
|
-
lastHunk.newCount = lastHunk.oldCount + (change.newEnd - change.newStart) - (change.oldEnd - change.oldStart);
|
|
917
|
-
continue;
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
const hunk = {
|
|
921
|
-
oldStart: contextStart + 1,
|
|
922
|
-
oldCount: contextEnd - contextStart,
|
|
923
|
-
newStart: contextStart + 1 + accumulatedOffset,
|
|
924
|
-
newCount: contextEnd - contextStart + (change.newEnd - change.newStart) - (change.oldEnd - change.oldStart),
|
|
925
|
-
lines: []
|
|
926
|
-
};
|
|
927
|
-
for (let idx = contextStart; idx < change.oldStart; idx++) {
|
|
928
|
-
hunk.lines.push({ type: ' ', content: oldLines[idx] });
|
|
929
|
-
}
|
|
930
|
-
for (let idx = change.oldStart; idx < change.oldEnd; idx++) {
|
|
931
|
-
hunk.lines.push({ type: '-', content: oldLines[idx] });
|
|
932
|
-
}
|
|
933
|
-
for (let idx = change.newStart; idx < change.newEnd; idx++) {
|
|
934
|
-
hunk.lines.push({ type: '+', content: newLines[idx] });
|
|
935
|
-
}
|
|
936
|
-
for (let idx = change.oldEnd; idx < contextEnd && idx < oldLines.length; idx++) {
|
|
937
|
-
hunk.lines.push({ type: ' ', content: oldLines[idx] });
|
|
938
|
-
}
|
|
939
|
-
hunks.push(hunk);
|
|
940
|
-
accumulatedOffset += (change.newEnd - change.newStart) - (change.oldEnd - change.oldStart);
|
|
941
|
-
}
|
|
942
|
-
let addedLines = 0;
|
|
943
|
-
let removedLines = 0;
|
|
944
|
-
for (const hunk of hunks) {
|
|
945
|
-
for (const line of hunk.lines) {
|
|
946
|
-
if (line.type === '+')
|
|
947
|
-
addedLines++;
|
|
948
|
-
if (line.type === '-')
|
|
949
|
-
removedLines++;
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
let summary = `Updated ${filePath}`;
|
|
953
|
-
if (addedLines > 0 && removedLines > 0) {
|
|
954
|
-
summary += ` with ${addedLines} addition${addedLines !== 1 ? "s" : ""} and ${removedLines} removal${removedLines !== 1 ? "s" : ""}`;
|
|
955
|
-
}
|
|
956
|
-
else if (addedLines > 0) {
|
|
957
|
-
summary += ` with ${addedLines} addition${addedLines !== 1 ? "s" : ""}`;
|
|
958
|
-
}
|
|
959
|
-
else if (removedLines > 0) {
|
|
960
|
-
summary += ` with ${removedLines} removal${removedLines !== 1 ? "s" : ""}`;
|
|
961
|
-
}
|
|
962
|
-
else if (changes.length === 0) {
|
|
963
|
-
return `No changes in ${filePath}`;
|
|
964
|
-
}
|
|
965
|
-
let diff = summary + "\n";
|
|
966
|
-
diff += `--- a/${filePath}\n`;
|
|
967
|
-
diff += `+++ b/${filePath}\n`;
|
|
968
|
-
for (const hunk of hunks) {
|
|
969
|
-
diff += `@@ -${hunk.oldStart},${hunk.oldCount} +${hunk.newStart},${hunk.newCount} @@\n`;
|
|
970
|
-
for (const line of hunk.lines) {
|
|
971
|
-
diff += `${line.type}${line.content}\n`;
|
|
972
|
-
}
|
|
973
|
-
}
|
|
974
|
-
return diff.trim();
|
|
975
|
-
}
|
|
976
|
-
getEditHistory() {
|
|
977
|
-
return [...this.editHistory];
|
|
978
|
-
}
|
|
979
|
-
/**
|
|
980
|
-
* Set checkpoint callback for automatic checkpoint creation before file modifications
|
|
981
|
-
*/
|
|
982
|
-
setCheckpointCallback(callback) {
|
|
983
|
-
this.checkpointCallback = callback;
|
|
984
|
-
}
|
|
985
|
-
/**
|
|
986
|
-
* Create checkpoint before modifying a file
|
|
987
|
-
*/
|
|
988
|
-
async createCheckpointIfNeeded(filePath, operation) {
|
|
989
|
-
if (!this.checkpointCallback) {
|
|
990
|
-
return;
|
|
991
|
-
}
|
|
992
|
-
try {
|
|
993
|
-
const resolvedPath = path.resolve(filePath);
|
|
994
|
-
// Read current file content if it exists
|
|
995
|
-
if (await fs.pathExists(resolvedPath)) {
|
|
996
|
-
const content = await fs.readFile(resolvedPath, "utf-8");
|
|
997
|
-
await this.checkpointCallback([{ path: filePath, content }], `Before ${operation}: ${filePath}`);
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
catch (error) {
|
|
1001
|
-
// Don't fail the operation if checkpoint creation fails
|
|
1002
|
-
console.warn(`Failed to create checkpoint: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
/**
|
|
1006
|
-
* Find a match by normalizing whitespace differences
|
|
1007
|
-
* Handles: trailing whitespace, tabs vs spaces, line ending differences, indentation changes
|
|
1008
|
-
*/
|
|
1009
|
-
findNormalizedMatch(content, searchStr) {
|
|
1010
|
-
// Normalize line endings first
|
|
1011
|
-
const normalizedContent = content.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
1012
|
-
const normalizedSearch = searchStr.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
1013
|
-
// If exact match after line ending normalization, find and return the actual string
|
|
1014
|
-
if (normalizedContent.includes(normalizedSearch)) {
|
|
1015
|
-
// Find the position in normalized content
|
|
1016
|
-
const startPos = normalizedContent.indexOf(normalizedSearch);
|
|
1017
|
-
const endPos = startPos + normalizedSearch.length;
|
|
1018
|
-
// Map position back to original content
|
|
1019
|
-
let originalStartPos = 0;
|
|
1020
|
-
let normalizedPos = 0;
|
|
1021
|
-
for (let i = 0; i < content.length && normalizedPos < startPos; i++) {
|
|
1022
|
-
if (content[i] === '\r' && content[i + 1] === '\n') {
|
|
1023
|
-
normalizedPos++; // \r\n becomes \n
|
|
1024
|
-
i++; // Skip the \n
|
|
1025
|
-
}
|
|
1026
|
-
else if (content[i] === '\r') {
|
|
1027
|
-
normalizedPos++; // \r becomes \n
|
|
1028
|
-
}
|
|
1029
|
-
else {
|
|
1030
|
-
normalizedPos++;
|
|
1031
|
-
}
|
|
1032
|
-
originalStartPos = i + 1;
|
|
1033
|
-
}
|
|
1034
|
-
// Find the end position in original
|
|
1035
|
-
let originalEndPos = originalStartPos;
|
|
1036
|
-
for (let i = originalStartPos; i < content.length && normalizedPos < endPos; i++) {
|
|
1037
|
-
if (content[i] === '\r' && content[i + 1] === '\n') {
|
|
1038
|
-
normalizedPos++;
|
|
1039
|
-
i++;
|
|
1040
|
-
}
|
|
1041
|
-
else if (content[i] === '\r') {
|
|
1042
|
-
normalizedPos++;
|
|
1043
|
-
}
|
|
1044
|
-
else {
|
|
1045
|
-
normalizedPos++;
|
|
1046
|
-
}
|
|
1047
|
-
originalEndPos = i + 1;
|
|
1048
|
-
}
|
|
1049
|
-
return content.substring(originalStartPos, originalEndPos);
|
|
1050
|
-
}
|
|
1051
|
-
const contentLines = content.split(/\r\n|\r|\n/);
|
|
1052
|
-
const searchLines = searchStr.split(/\r\n|\r|\n/);
|
|
1053
|
-
// Filter out empty lines from search for more flexible matching
|
|
1054
|
-
const searchLinesNonEmpty = searchLines.filter(l => l.trim().length > 0);
|
|
1055
|
-
// Try matching with trailing whitespace trimmed from each line
|
|
1056
|
-
for (let i = 0; i <= contentLines.length - searchLines.length; i++) {
|
|
1057
|
-
let match = true;
|
|
1058
|
-
for (let j = 0; j < searchLines.length; j++) {
|
|
1059
|
-
const contentLine = contentLines[i + j].trimEnd();
|
|
1060
|
-
const searchLine = searchLines[j].trimEnd();
|
|
1061
|
-
if (contentLine !== searchLine) {
|
|
1062
|
-
match = false;
|
|
1063
|
-
break;
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
if (match) {
|
|
1067
|
-
return this.extractOriginalBlock(content, i, searchLines.length);
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
// Try matching with all whitespace fully normalized (aggressive mode)
|
|
1071
|
-
// This handles indentation differences like 2-space vs 4-space
|
|
1072
|
-
for (let i = 0; i <= contentLines.length - searchLines.length; i++) {
|
|
1073
|
-
let match = true;
|
|
1074
|
-
for (let j = 0; j < searchLines.length; j++) {
|
|
1075
|
-
const contentLine = this.normalizeLineAggressively(contentLines[i + j]);
|
|
1076
|
-
const searchLine = this.normalizeLineAggressively(searchLines[j]);
|
|
1077
|
-
if (contentLine !== searchLine) {
|
|
1078
|
-
match = false;
|
|
1079
|
-
break;
|
|
1080
|
-
}
|
|
1081
|
-
}
|
|
1082
|
-
if (match) {
|
|
1083
|
-
return this.extractOriginalBlock(content, i, searchLines.length);
|
|
1084
|
-
}
|
|
1085
|
-
}
|
|
1086
|
-
// Try matching with blank lines skipped in both (handles extra/missing blank lines)
|
|
1087
|
-
const contentLinesNonEmpty = contentLines.map((l, idx) => ({ line: l, idx })).filter(x => x.line.trim().length > 0);
|
|
1088
|
-
if (searchLinesNonEmpty.length > 0 && searchLinesNonEmpty.length <= contentLinesNonEmpty.length) {
|
|
1089
|
-
for (let i = 0; i <= contentLinesNonEmpty.length - searchLinesNonEmpty.length; i++) {
|
|
1090
|
-
let match = true;
|
|
1091
|
-
for (let j = 0; j < searchLinesNonEmpty.length; j++) {
|
|
1092
|
-
const contentLine = this.normalizeLineAggressively(contentLinesNonEmpty[i + j].line);
|
|
1093
|
-
const searchLine = this.normalizeLineAggressively(searchLinesNonEmpty[j]);
|
|
1094
|
-
if (contentLine !== searchLine) {
|
|
1095
|
-
match = false;
|
|
1096
|
-
break;
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
if (match) {
|
|
1100
|
-
// Found a match - extract from first to last matching line
|
|
1101
|
-
const startIdx = contentLinesNonEmpty[i].idx;
|
|
1102
|
-
const endIdx = contentLinesNonEmpty[i + searchLinesNonEmpty.length - 1].idx;
|
|
1103
|
-
return this.extractOriginalBlock(content, startIdx, endIdx - startIdx + 1);
|
|
1104
|
-
}
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
return null;
|
|
1108
|
-
}
|
|
1109
|
-
/**
|
|
1110
|
-
* Aggressively normalize a line for matching - removes all indentation and collapses whitespace
|
|
1111
|
-
*/
|
|
1112
|
-
normalizeLineAggressively(line) {
|
|
1113
|
-
return line
|
|
1114
|
-
.trim() // Remove leading/trailing whitespace completely
|
|
1115
|
-
.replace(/\s+/g, ' ') // Collapse multiple spaces to single space
|
|
1116
|
-
.replace(/\s*([{}()[\];,:])\s*/g, '$1') // Remove spaces around punctuation
|
|
1117
|
-
.replace(/["'`]/g, '"'); // Normalize quote styles
|
|
1118
|
-
}
|
|
1119
|
-
/**
|
|
1120
|
-
* Extract a block of lines from original content, preserving original line endings
|
|
1121
|
-
*/
|
|
1122
|
-
extractOriginalBlock(content, startLineIdx, numLines) {
|
|
1123
|
-
// Find line positions in original content
|
|
1124
|
-
const linePositions = [];
|
|
1125
|
-
let pos = 0;
|
|
1126
|
-
let lineStart = 0;
|
|
1127
|
-
while (pos <= content.length) {
|
|
1128
|
-
if (pos === content.length || content[pos] === '\n' || content[pos] === '\r') {
|
|
1129
|
-
const lineEnd = pos;
|
|
1130
|
-
linePositions.push({ start: lineStart, end: lineEnd });
|
|
1131
|
-
if (pos < content.length) {
|
|
1132
|
-
// Skip line ending
|
|
1133
|
-
if (content[pos] === '\r' && content[pos + 1] === '\n') {
|
|
1134
|
-
pos += 2;
|
|
1135
|
-
}
|
|
1136
|
-
else {
|
|
1137
|
-
pos += 1;
|
|
1138
|
-
}
|
|
1139
|
-
lineStart = pos;
|
|
1140
|
-
}
|
|
1141
|
-
else {
|
|
1142
|
-
break;
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
else {
|
|
1146
|
-
pos++;
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
// Extract the requested lines
|
|
1150
|
-
// BUG FIX: Also validate numLines to prevent negative endLineIdx calculation
|
|
1151
|
-
if (startLineIdx >= linePositions.length || numLines <= 0) {
|
|
1152
|
-
return '';
|
|
1153
|
-
}
|
|
1154
|
-
const endLineIdx = Math.min(startLineIdx + numLines - 1, linePositions.length - 1);
|
|
1155
|
-
const blockStart = linePositions[startLineIdx].start;
|
|
1156
|
-
const blockEnd = linePositions[endLineIdx].end;
|
|
1157
|
-
return content.substring(blockStart, blockEnd);
|
|
1158
|
-
}
|
|
1159
|
-
/**
|
|
1160
|
-
* Normalize indentation by converting tabs to spaces and trimming trailing whitespace
|
|
1161
|
-
*/
|
|
1162
|
-
normalizeIndentation(line) {
|
|
1163
|
-
return line.replace(/\t/g, ' ').trimEnd();
|
|
1164
|
-
}
|
|
1165
|
-
/**
|
|
1166
|
-
* Find a similar block of code using line-by-line similarity matching
|
|
1167
|
-
* Uses aggressive normalization and wider search windows
|
|
1168
|
-
*/
|
|
1169
|
-
findSimilarBlock(content, searchStr) {
|
|
1170
|
-
const contentLines = content.split(/\r?\n/);
|
|
1171
|
-
const searchLines = searchStr.split(/\r?\n/);
|
|
1172
|
-
if (searchLines.length === 0) {
|
|
1173
|
-
return { match: null };
|
|
1174
|
-
}
|
|
1175
|
-
// Filter out blank lines for more robust matching
|
|
1176
|
-
const searchLinesNonEmpty = searchLines.filter(l => l.trim().length > 0);
|
|
1177
|
-
if (searchLinesNonEmpty.length === 0) {
|
|
1178
|
-
return { match: null };
|
|
1179
|
-
}
|
|
1180
|
-
// Find the best matching block
|
|
1181
|
-
let bestMatch = null;
|
|
1182
|
-
const minScore = EDITOR_CONFIG.BLOCK_SIMILARITY_THRESHOLD;
|
|
1183
|
-
// Normalize first search line for initial matching
|
|
1184
|
-
const firstSearchLineNorm = this.normalizeLineAggressively(searchLinesNonEmpty[0]);
|
|
1185
|
-
for (let i = 0; i < contentLines.length; i++) {
|
|
1186
|
-
const firstContentLineNorm = this.normalizeLineAggressively(contentLines[i]);
|
|
1187
|
-
// Quick check: first non-empty line should have some similarity (using normalized comparison)
|
|
1188
|
-
if (firstContentLineNorm.length === 0)
|
|
1189
|
-
continue;
|
|
1190
|
-
if (this.lineSimilarityNormalized(firstContentLineNorm, firstSearchLineNorm) < EDITOR_CONFIG.SUGGESTION_SIMILARITY_THRESHOLD) {
|
|
1191
|
-
continue;
|
|
1192
|
-
}
|
|
1193
|
-
// Try to match the block starting at this position - wider window (+15 instead of +5)
|
|
1194
|
-
const blockEnd = Math.min(i + searchLines.length + 15, contentLines.length);
|
|
1195
|
-
for (let endIdx = Math.max(i, i + searchLinesNonEmpty.length - 3); endIdx <= blockEnd; endIdx++) {
|
|
1196
|
-
if (endIdx < i)
|
|
1197
|
-
continue;
|
|
1198
|
-
const candidateLines = contentLines.slice(i, endIdx + 1);
|
|
1199
|
-
const score = this.blockSimilarityNormalized(candidateLines, searchLinesNonEmpty);
|
|
1200
|
-
if (score >= minScore && (!bestMatch || score > bestMatch.score)) {
|
|
1201
|
-
bestMatch = { startIdx: i, endIdx, score };
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1205
|
-
if (bestMatch && bestMatch.score >= minScore) {
|
|
1206
|
-
return { match: contentLines.slice(bestMatch.startIdx, bestMatch.endIdx + 1).join('\n') };
|
|
1207
|
-
}
|
|
1208
|
-
// If no match found, try to provide a helpful suggestion
|
|
1209
|
-
const suggestion = this.findClosestMatchSuggestion(contentLines, searchLines);
|
|
1210
|
-
return { match: null, suggestion };
|
|
1211
|
-
}
|
|
1212
|
-
/**
|
|
1213
|
-
* Line similarity using normalized comparison
|
|
1214
|
-
*/
|
|
1215
|
-
lineSimilarityNormalized(norm1, norm2) {
|
|
1216
|
-
if (norm1 === norm2)
|
|
1217
|
-
return 1;
|
|
1218
|
-
if (norm1.length === 0 && norm2.length === 0)
|
|
1219
|
-
return 1;
|
|
1220
|
-
if (norm1.length === 0 || norm2.length === 0)
|
|
1221
|
-
return 0;
|
|
1222
|
-
const maxLen = Math.max(norm1.length, norm2.length);
|
|
1223
|
-
const distance = this.levenshteinDistance(norm1, norm2);
|
|
1224
|
-
return 1 - (distance / maxLen);
|
|
1225
|
-
}
|
|
1226
|
-
/**
|
|
1227
|
-
* Block similarity using normalized lines and skipping blank lines
|
|
1228
|
-
*/
|
|
1229
|
-
blockSimilarityNormalized(candidateLines, searchLinesNonEmpty) {
|
|
1230
|
-
if (searchLinesNonEmpty.length === 0)
|
|
1231
|
-
return 0;
|
|
1232
|
-
// Normalize and filter candidate lines
|
|
1233
|
-
const candidateNonEmpty = candidateLines
|
|
1234
|
-
.map(l => this.normalizeLineAggressively(l))
|
|
1235
|
-
.filter(l => l.length > 0);
|
|
1236
|
-
if (candidateNonEmpty.length === 0)
|
|
1237
|
-
return 0;
|
|
1238
|
-
// Use LCS-style matching for better tolerance
|
|
1239
|
-
let matchedCount = 0;
|
|
1240
|
-
let ci = 0;
|
|
1241
|
-
for (const searchLine of searchLinesNonEmpty) {
|
|
1242
|
-
const searchNorm = this.normalizeLineAggressively(searchLine);
|
|
1243
|
-
if (searchNorm.length === 0)
|
|
1244
|
-
continue;
|
|
1245
|
-
// Look for a matching line in remaining candidates
|
|
1246
|
-
while (ci < candidateNonEmpty.length) {
|
|
1247
|
-
const sim = this.lineSimilarityNormalized(candidateNonEmpty[ci], searchNorm);
|
|
1248
|
-
ci++;
|
|
1249
|
-
if (sim >= 0.5) {
|
|
1250
|
-
matchedCount++;
|
|
1251
|
-
break;
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
return matchedCount / searchLinesNonEmpty.length;
|
|
1256
|
-
}
|
|
1257
|
-
/**
|
|
1258
|
-
* Calculate similarity between two lines (0-1)
|
|
1259
|
-
*/
|
|
1260
|
-
lineSimilarity(line1, line2) {
|
|
1261
|
-
const s1 = line1.trim();
|
|
1262
|
-
const s2 = line2.trim();
|
|
1263
|
-
if (s1 === s2)
|
|
1264
|
-
return 1;
|
|
1265
|
-
if (s1.length === 0 && s2.length === 0)
|
|
1266
|
-
return 1;
|
|
1267
|
-
if (s1.length === 0 || s2.length === 0)
|
|
1268
|
-
return 0;
|
|
1269
|
-
// Use Levenshtein distance-based similarity
|
|
1270
|
-
const maxLen = Math.max(s1.length, s2.length);
|
|
1271
|
-
const distance = this.levenshteinDistance(s1, s2);
|
|
1272
|
-
return 1 - (distance / maxLen);
|
|
1273
|
-
}
|
|
1274
|
-
/**
|
|
1275
|
-
* Levenshtein distance for string similarity
|
|
1276
|
-
*/
|
|
1277
|
-
levenshteinDistance(s1, s2) {
|
|
1278
|
-
const m = s1.length;
|
|
1279
|
-
const n = s2.length;
|
|
1280
|
-
// Use two rows instead of full matrix for memory efficiency
|
|
1281
|
-
let prev = new Array(n + 1);
|
|
1282
|
-
let curr = new Array(n + 1);
|
|
1283
|
-
for (let j = 0; j <= n; j++) {
|
|
1284
|
-
prev[j] = j;
|
|
1285
|
-
}
|
|
1286
|
-
for (let i = 1; i <= m; i++) {
|
|
1287
|
-
curr[0] = i;
|
|
1288
|
-
for (let j = 1; j <= n; j++) {
|
|
1289
|
-
if (s1[i - 1] === s2[j - 1]) {
|
|
1290
|
-
curr[j] = prev[j - 1];
|
|
1291
|
-
}
|
|
1292
|
-
else {
|
|
1293
|
-
curr[j] = 1 + Math.min(prev[j - 1], prev[j], curr[j - 1]);
|
|
1294
|
-
}
|
|
1295
|
-
}
|
|
1296
|
-
[prev, curr] = [curr, prev];
|
|
1297
|
-
}
|
|
1298
|
-
return prev[n];
|
|
1299
|
-
}
|
|
1300
|
-
/**
|
|
1301
|
-
* Find and suggest the closest matching location
|
|
1302
|
-
*/
|
|
1303
|
-
findClosestMatchSuggestion(contentLines, searchLines) {
|
|
1304
|
-
if (searchLines.length === 0)
|
|
1305
|
-
return undefined;
|
|
1306
|
-
const firstSearchLine = searchLines[0].trim();
|
|
1307
|
-
let bestLineIdx = -1;
|
|
1308
|
-
let bestSim = 0;
|
|
1309
|
-
for (let i = 0; i < contentLines.length; i++) {
|
|
1310
|
-
const sim = this.lineSimilarity(contentLines[i], firstSearchLine);
|
|
1311
|
-
if (sim > bestSim) {
|
|
1312
|
-
bestSim = sim;
|
|
1313
|
-
bestLineIdx = i;
|
|
1314
|
-
}
|
|
1315
|
-
}
|
|
1316
|
-
if (bestLineIdx >= 0 && bestSim >= EDITOR_CONFIG.SUGGESTION_SIMILARITY_THRESHOLD) {
|
|
1317
|
-
const actualLine = contentLines[bestLineIdx].trim();
|
|
1318
|
-
const lineNum = bestLineIdx + 1;
|
|
1319
|
-
if (bestSim < 0.9) {
|
|
1320
|
-
return `Did you mean line ${lineNum}? Found: "${actualLine.substring(0, 60)}${actualLine.length > 60 ? '...' : ''}"`;
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
return undefined;
|
|
1324
|
-
}
|
|
1325
|
-
/**
|
|
1326
|
-
* Find a similar single line in the content
|
|
1327
|
-
*/
|
|
1328
|
-
findSimilarLine(content, searchStr) {
|
|
1329
|
-
const lines = content.split(/\r?\n/);
|
|
1330
|
-
const searchTrimmed = searchStr.trim();
|
|
1331
|
-
// First try exact match with trimmed whitespace
|
|
1332
|
-
for (const line of lines) {
|
|
1333
|
-
if (line.trim() === searchTrimmed) {
|
|
1334
|
-
return { match: line };
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
// Try normalized indentation match
|
|
1338
|
-
const searchNormalized = this.normalizeIndentation(searchStr);
|
|
1339
|
-
for (const line of lines) {
|
|
1340
|
-
if (this.normalizeIndentation(line) === searchNormalized) {
|
|
1341
|
-
return { match: line };
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
// Find best similar line
|
|
1345
|
-
let bestLine = null;
|
|
1346
|
-
let bestSim = 0;
|
|
1347
|
-
const minSim = EDITOR_CONFIG.LINE_SIMILARITY_THRESHOLD;
|
|
1348
|
-
for (const line of lines) {
|
|
1349
|
-
const sim = this.lineSimilarity(line, searchStr);
|
|
1350
|
-
if (sim > bestSim) {
|
|
1351
|
-
bestSim = sim;
|
|
1352
|
-
bestLine = line;
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
|
-
if (bestLine && bestSim >= minSim) {
|
|
1356
|
-
return { match: bestLine };
|
|
1357
|
-
}
|
|
1358
|
-
// Provide suggestion if we found something close
|
|
1359
|
-
if (bestLine && bestSim >= 0.5) {
|
|
1360
|
-
const lineIdx = lines.indexOf(bestLine);
|
|
1361
|
-
return {
|
|
1362
|
-
match: null,
|
|
1363
|
-
suggestion: `Did you mean line ${lineIdx + 1}? Found: "${bestLine.trim().substring(0, 50)}${bestLine.trim().length > 50 ? '...' : ''}"`
|
|
1364
|
-
};
|
|
1365
|
-
}
|
|
1366
|
-
return { match: null };
|
|
1367
|
-
}
|
|
1368
|
-
}
|
|
1369
|
-
//# sourceMappingURL=text-editor.js.map
|