@infinityi/engine-lib 1.0.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -445
- package/dist/agent/index.js +14 -8
- package/dist/context/index.d.ts +1 -1
- package/dist/context/index.js +8 -5
- package/dist/context/providers.d.ts +3 -3
- package/dist/context/types.d.ts +16 -1
- package/dist/errors.d.ts +18 -0
- package/dist/errors.js +6 -1
- package/dist/events/index.js +2 -1
- package/dist/execution/index.js +9 -4
- package/dist/execution/types.d.ts +14 -0
- package/dist/index-37x76zdn.js +4 -0
- package/dist/index-4c15ysa8.js +84 -0
- package/dist/{index-pwr8179t.js → index-7nb9qwa1.js} +23 -14
- package/dist/{index-jxgj4z08.js → index-9egrqxkf.js} +4 -0
- package/dist/index-9rr5dkzh.js +0 -0
- package/dist/{index-w34cbktd.js → index-a67ej96j.js} +1 -0
- package/dist/{index-64tt9696.js → index-aed6fe4z.js} +3 -3
- package/dist/{index-7690reng.js → index-ajr3nk10.js} +15 -1
- package/dist/index-c6kgts41.js +0 -0
- package/dist/{index-xsv43c5j.js → index-e14g2e48.js} +2 -2
- package/dist/index-k0q7x4mz.js +54 -0
- package/dist/index-mnx5mqbs.js +102 -0
- package/dist/index-mr1hs98z.js +661 -0
- package/dist/{index-yrqrxwjt.js → index-nvdsr51v.js} +4 -38
- package/dist/{index-jp2b31xs.js → index-vwcyawfk.js} +4 -4
- package/dist/index-ycjsqfpq.js +39 -0
- package/dist/{index-bqg01r42.js → index-zdggff0y.js} +183 -180
- package/dist/index.d.ts +2 -2
- package/dist/index.js +49 -38
- package/dist/lifecycle/index.js +2 -0
- package/dist/messages/index.js +1 -0
- package/dist/providers/index.js +5 -4
- package/dist/retrieval/chunking.d.ts +8 -0
- package/dist/retrieval/context.d.ts +4 -0
- package/dist/retrieval/index.d.ts +19 -0
- package/dist/retrieval/index.js +747 -0
- package/dist/retrieval/loaders.d.ts +8 -0
- package/dist/retrieval/memory.d.ts +29 -0
- package/dist/retrieval/retriever.d.ts +8 -0
- package/dist/retrieval/types.d.ts +253 -0
- package/dist/retrieval/utils.d.ts +11 -0
- package/dist/retrieval/vector-store.d.ts +13 -0
- package/dist/runtime/index.js +2 -1
- package/dist/schema/index.js +10 -4
- package/dist/session/index.js +1 -0
- package/dist/session-stores/codec.d.ts +8 -0
- package/dist/session-stores/forge-data.d.ts +43 -0
- package/dist/session-stores/hooks.d.ts +4 -0
- package/dist/session-stores/ids.d.ts +3 -0
- package/dist/session-stores/index.d.ts +20 -0
- package/dist/session-stores/index.js +1382 -0
- package/dist/session-stores/jsonl.d.ts +26 -0
- package/dist/session-stores/redis.d.ts +45 -0
- package/dist/session-stores/types.d.ts +45 -0
- package/dist/session-stores/versioning.d.ts +6 -0
- package/dist/testing/conformance.js +5 -4
- package/dist/testing/index.js +1 -0
- package/dist/tools/index.js +5 -3
- package/dist/tools-fs/define.d.ts +2 -0
- package/dist/tools-fs/files.d.ts +40 -0
- package/dist/tools-fs/git.d.ts +14 -0
- package/dist/tools-fs/index.d.ts +19 -0
- package/dist/tools-fs/index.js +1695 -0
- package/dist/tools-fs/policy.d.ts +32 -0
- package/dist/tools-fs/schemas.d.ts +31 -0
- package/dist/tools-fs/search.d.ts +45 -0
- package/dist/tools-fs/symbols.d.ts +12 -0
- package/dist/tools-fs/types.d.ts +151 -0
- package/dist/tools-http/client.d.ts +12 -0
- package/dist/tools-http/define.d.ts +12 -0
- package/dist/tools-http/events.d.ts +22 -0
- package/dist/tools-http/index.d.ts +24 -0
- package/dist/tools-http/index.js +18 -0
- package/dist/tools-http/policy.d.ts +52 -0
- package/dist/tools-http/types.d.ts +116 -0
- package/dist/tools-shell/define.d.ts +23 -0
- package/dist/tools-shell/events.d.ts +38 -0
- package/dist/tools-shell/exec.d.ts +37 -0
- package/dist/tools-shell/index.d.ts +30 -0
- package/dist/tools-shell/index.js +327 -0
- package/dist/tools-shell/policy.d.ts +42 -0
- package/dist/tools-shell/types.d.ts +121 -0
- package/dist/tools-web/define.d.ts +11 -0
- package/dist/tools-web/html.d.ts +22 -0
- package/dist/tools-web/index.d.ts +21 -0
- package/dist/tools-web/index.js +472 -0
- package/dist/tools-web/types.d.ts +81 -0
- package/docs/README.md +58 -10
- package/docs/api/.nojekyll +1 -0
- package/docs/api/assets/hierarchy.js +1 -0
- package/docs/api/assets/highlight.css +99 -0
- package/docs/api/assets/icons.js +18 -0
- package/docs/api/assets/icons.svg +1 -0
- package/docs/api/assets/main.js +60 -0
- package/docs/api/assets/navigation.js +1 -0
- package/docs/api/assets/search.js +1 -0
- package/docs/api/assets/style.css +1633 -0
- package/docs/api/classes/errors.AgentError.html +47 -0
- package/docs/api/classes/errors.CancelledError.html +46 -0
- package/docs/api/classes/errors.ContextWindowError.html +48 -0
- package/docs/api/classes/errors.ExecutionError.html +46 -0
- package/docs/api/classes/errors.FilesystemPolicyError.html +48 -0
- package/docs/api/classes/errors.MaxHandoffsExceededError.html +47 -0
- package/docs/api/classes/errors.MaxStepsExceededError.html +47 -0
- package/docs/api/classes/errors.ProviderError.html +47 -0
- package/docs/api/classes/errors.SchemaValidationError.html +47 -0
- package/docs/api/classes/errors.ShellPolicyError.html +50 -0
- package/docs/api/classes/errors.ToolError.html +47 -0
- package/docs/api/classes/errors.ToolValidationError.html +48 -0
- package/docs/api/classes/index.InMemorySessionStore.html +11 -0
- package/docs/api/classes/index.Secret.html +20 -0
- package/docs/api/classes/providers.StreamAccumulator.html +8 -0
- package/docs/api/classes/session-stores.FilesystemJsonlSessionStore.html +13 -0
- package/docs/api/classes/session-stores.ForgeDataSessionStore.html +14 -0
- package/docs/api/classes/session-stores.RedisSessionStore.html +12 -0
- package/docs/api/classes/tools-http.HttpPolicyError.html +41 -0
- package/docs/api/functions/agent.createToolRegistry.html +3 -0
- package/docs/api/functions/agent.handoffProviderTools.html +3 -0
- package/docs/api/functions/agent.handoffToolName.html +2 -0
- package/docs/api/functions/agent.resolveHandoffTargets.html +8 -0
- package/docs/api/functions/context.applyContextWindow.html +5 -0
- package/docs/api/functions/context.estimateTokens.html +3 -0
- package/docs/api/functions/context.resolveContext.html +5 -0
- package/docs/api/functions/events.createRunTelemetry.html +3 -0
- package/docs/api/functions/events.eventFields.html +4 -0
- package/docs/api/functions/events.eventPayload.html +5 -0
- package/docs/api/functions/execution.addUsage.html +2 -0
- package/docs/api/functions/execution.emptyUsage.html +2 -0
- package/docs/api/functions/index.asSchema.html +4 -0
- package/docs/api/functions/index.asTool.html +8 -0
- package/docs/api/functions/index.assistant.html +2 -0
- package/docs/api/functions/index.createAgentRegistry.html +5 -0
- package/docs/api/functions/index.createAnthropic.html +5 -0
- package/docs/api/functions/index.createEventHub.html +2 -0
- package/docs/api/functions/index.createGoogle.html +5 -0
- package/docs/api/functions/index.createOpenAI.html +5 -0
- package/docs/api/functions/index.createOpenAICompatible.html +7 -0
- package/docs/api/functions/index.createSession.html +3 -0
- package/docs/api/functions/index.defineAgent.html +7 -0
- package/docs/api/functions/index.defineTool.html +7 -0
- package/docs/api/functions/index.dynamicContext.html +2 -0
- package/docs/api/functions/index.fromJsonSchema.html +3 -0
- package/docs/api/functions/index.isSecret.html +4 -0
- package/docs/api/functions/index.loggingSubscriber.html +4 -0
- package/docs/api/functions/index.messageBusSubscriber.html +6 -0
- package/docs/api/functions/index.normalizeContent.html +2 -0
- package/docs/api/functions/index.resolveSecret.html +4 -0
- package/docs/api/functions/index.runAgent.html +19 -0
- package/docs/api/functions/index.staticContext.html +3 -0
- package/docs/api/functions/index.summarizeOldest.html +4 -0
- package/docs/api/functions/index.system.html +2 -0
- package/docs/api/functions/index.text.html +2 -0
- package/docs/api/functions/index.toJsonSchema.html +2 -0
- package/docs/api/functions/index.toolResult.html +2 -0
- package/docs/api/functions/index.truncateOldest.html +5 -0
- package/docs/api/functions/index.user.html +2 -0
- package/docs/api/functions/index.validateJsonSchema.html +3 -0
- package/docs/api/functions/lifecycle.agentRuntimeComponent.html +5 -0
- package/docs/api/functions/providers.collectStream.html +2 -0
- package/docs/api/functions/providers.createProvider.html +4 -0
- package/docs/api/functions/providers.createProviderHttp.html +3 -0
- package/docs/api/functions/providers.defaultProviderResilience.html +3 -0
- package/docs/api/functions/providers.openSseStream.html +6 -0
- package/docs/api/functions/providers.parseSse.html +2 -0
- package/docs/api/functions/providers.toProviderError.html +2 -0
- package/docs/api/functions/session-stores.createPostgresSessionStore.html +1 -0
- package/docs/api/functions/session-stores.createSqliteSessionStore.html +1 -0
- package/docs/api/functions/session-stores.isCloseableSessionStore.html +1 -0
- package/docs/api/functions/session-stores.isVersionedSessionStore.html +1 -0
- package/docs/api/functions/session-stores.migrateSessionStore.html +1 -0
- package/docs/api/functions/session-stores.withSessionStoreHooks.html +2 -0
- package/docs/api/functions/testing.byteStreamOf.html +2 -0
- package/docs/api/functions/testing.collectProviderStream.html +2 -0
- package/docs/api/functions/testing.conversation.html +2 -0
- package/docs/api/functions/testing.expectValid.html +3 -0
- package/docs/api/functions/testing.inMemorySessionStore.html +2 -0
- package/docs/api/functions/testing.jsonFetch.html +3 -0
- package/docs/api/functions/testing.mockProvider.html +3 -0
- package/docs/api/functions/testing.scriptedProvider.html +4 -0
- package/docs/api/functions/testing.sseFetch.html +3 -0
- package/docs/api/functions/testing.textResult.html +3 -0
- package/docs/api/functions/testing.toolCallResult.html +3 -0
- package/docs/api/functions/testing_conformance.runProviderConformance.html +3 -0
- package/docs/api/functions/tools-fs.filesystemTools.html +1 -0
- package/docs/api/functions/tools-http.createHttpToolClient.html +2 -0
- package/docs/api/functions/tools-http.httpTools.html +2 -0
- package/docs/api/functions/tools-shell.shellTools.html +4 -0
- package/docs/api/functions/tools-web.webTools.html +2 -0
- package/docs/api/functions/tools.renderToolContent.html +6 -0
- package/docs/api/functions/tools.toProviderTool.html +3 -0
- package/docs/api/functions/tools.toToolResultMessage.html +4 -0
- package/docs/api/hierarchy.html +1 -0
- package/docs/api/index.html +1 -0
- package/docs/api/interfaces/agent.ToolRegistry.html +8 -0
- package/docs/api/interfaces/errors.SchemaIssue.html +4 -0
- package/docs/api/interfaces/events.RunTelemetry.html +10 -0
- package/docs/api/interfaces/events.SpanHandle.html +6 -0
- package/docs/api/interfaces/index.AgentDefinition.html +16 -0
- package/docs/api/interfaces/index.AgentHooks.html +21 -0
- package/docs/api/interfaces/index.AgentRegistry.html +16 -0
- package/docs/api/interfaces/index.AnthropicOptions.html +13 -0
- package/docs/api/interfaces/index.AsToolOptions.html +13 -0
- package/docs/api/interfaces/index.CompletionRequest.html +30 -0
- package/docs/api/interfaces/index.CompletionResult.html +14 -0
- package/docs/api/interfaces/index.ContextItem.html +6 -0
- package/docs/api/interfaces/index.ContextProvider.html +5 -0
- package/docs/api/interfaces/index.ContextStrategy.html +8 -0
- package/docs/api/interfaces/index.ContextStrategyContext.html +8 -0
- package/docs/api/interfaces/index.ContextWindowOptions.html +8 -0
- package/docs/api/interfaces/index.CreateSessionOptions.html +10 -0
- package/docs/api/interfaces/index.EngineContext.html +10 -0
- package/docs/api/interfaces/index.EventHub.html +4 -0
- package/docs/api/interfaces/index.EventHubOptions.html +8 -0
- package/docs/api/interfaces/index.GenerationSettings.html +12 -0
- package/docs/api/interfaces/index.GoogleOptions.html +11 -0
- package/docs/api/interfaces/index.ImagePart.html +5 -0
- package/docs/api/interfaces/index.InstructionContext.html +9 -0
- package/docs/api/interfaces/index.JsonSchema.html +18 -0
- package/docs/api/interfaces/index.LoggingSubscriberOptions.html +4 -0
- package/docs/api/interfaces/index.Message.html +8 -0
- package/docs/api/interfaces/index.MessageBusSubscriberOptions.html +4 -0
- package/docs/api/interfaces/index.OpenAICompatibleOptions.html +16 -0
- package/docs/api/interfaces/index.OpenAIOptions.html +16 -0
- package/docs/api/interfaces/index.Provider.html +11 -0
- package/docs/api/interfaces/index.ProviderCapabilities.html +9 -0
- package/docs/api/interfaces/index.ProviderTool.html +6 -0
- package/docs/api/interfaces/index.ResponseSchema.html +6 -0
- package/docs/api/interfaces/index.RunBridge.html +10 -0
- package/docs/api/interfaces/index.RunOptions.html +39 -0
- package/docs/api/interfaces/index.RunResult.html +20 -0
- package/docs/api/interfaces/index.Schema.html +13 -0
- package/docs/api/interfaces/index.Session.html +13 -0
- package/docs/api/interfaces/index.SessionState.html +5 -0
- package/docs/api/interfaces/index.SessionStore.html +15 -0
- package/docs/api/interfaces/index.TextPart.html +4 -0
- package/docs/api/interfaces/index.ToolCall.html +9 -0
- package/docs/api/interfaces/index.ToolCallPart.html +6 -0
- package/docs/api/interfaces/index.ToolContext.html +21 -0
- package/docs/api/interfaces/index.ToolDefinition.html +14 -0
- package/docs/api/interfaces/index.ToolFailure.html +9 -0
- package/docs/api/interfaces/index.ToolResultPart.html +8 -0
- package/docs/api/interfaces/index.ToolSpec.html +6 -0
- package/docs/api/interfaces/index.ToolSuccess.html +6 -0
- package/docs/api/interfaces/index.Usage.html +9 -0
- package/docs/api/interfaces/lifecycle.AgentRuntimeOptions.html +17 -0
- package/docs/api/interfaces/providers.AdapterSpec.html +17 -0
- package/docs/api/interfaces/providers.ProviderHttpOptions.html +14 -0
- package/docs/api/interfaces/providers.SseMessage.html +6 -0
- package/docs/api/interfaces/providers.SseRequest.html +8 -0
- package/docs/api/interfaces/runtime.Logger.html +22 -0
- package/docs/api/interfaces/runtime.Telemetry.html +9 -0
- package/docs/api/interfaces/schema.OptionalSchema.html +13 -0
- package/docs/api/interfaces/session-stores.CloseableSessionStore.html +11 -0
- package/docs/api/interfaces/session-stores.CreatePostgresSessionStoreOptions.html +6 -0
- package/docs/api/interfaces/session-stores.CreateSqliteSessionStoreOptions.html +7 -0
- package/docs/api/interfaces/session-stores.FilesystemJsonlSessionStoreOptions.html +3 -0
- package/docs/api/interfaces/session-stores.ForgeDataSessionStoreOptions.html +5 -0
- package/docs/api/interfaces/session-stores.RedisSessionStoreClient.html +9 -0
- package/docs/api/interfaces/session-stores.RedisSessionStoreOptions.html +4 -0
- package/docs/api/interfaces/session-stores.RedisSessionStoreTransaction.html +6 -0
- package/docs/api/interfaces/session-stores.SessionArchiveRecord.html +7 -0
- package/docs/api/interfaces/session-stores.SessionArchiver.html +2 -0
- package/docs/api/interfaces/session-stores.SessionCompactionResult.html +3 -0
- package/docs/api/interfaces/session-stores.SessionCompactor.html +3 -0
- package/docs/api/interfaces/session-stores.SessionStoreCodec.html +6 -0
- package/docs/api/interfaces/session-stores.SessionStoreHookContext.html +3 -0
- package/docs/api/interfaces/session-stores.SessionStoreHooks.html +3 -0
- package/docs/api/interfaces/session-stores.VersionedSessionStore.html +11 -0
- package/docs/api/interfaces/testing.MockProviderOptions.html +12 -0
- package/docs/api/interfaces/testing.RecordedCall.html +4 -0
- package/docs/api/interfaces/testing.RecordingFetch.html +4 -0
- package/docs/api/interfaces/testing_conformance.ConformanceFixtures.html +12 -0
- package/docs/api/interfaces/testing_conformance.ConformanceOptions.html +10 -0
- package/docs/api/interfaces/testing_conformance.ConformanceTestApi.html +4 -0
- package/docs/api/interfaces/testing_conformance.ProviderIO.html +7 -0
- package/docs/api/interfaces/tools-fs.ApplyPatchArgs.html +6 -0
- package/docs/api/interfaces/tools-fs.DiffStatusArgs.html +5 -0
- package/docs/api/interfaces/tools-fs.EditRangeArgs.html +7 -0
- package/docs/api/interfaces/tools-fs.EditReplaceArgs.html +7 -0
- package/docs/api/interfaces/tools-fs.FilesystemTools.html +13 -0
- package/docs/api/interfaces/tools-fs.FilesystemToolsConfig.html +12 -0
- package/docs/api/interfaces/tools-fs.FindFilesArgs.html +7 -0
- package/docs/api/interfaces/tools-fs.OpenWindowArgs.html +5 -0
- package/docs/api/interfaces/tools-fs.ReadArgs.html +9 -0
- package/docs/api/interfaces/tools-fs.RepoMapArgs.html +7 -0
- package/docs/api/interfaces/tools-fs.SearchSemanticArgs.html +8 -0
- package/docs/api/interfaces/tools-fs.SearchTextArgs.html +11 -0
- package/docs/api/interfaces/tools-fs.SymbolInfo.html +7 -0
- package/docs/api/interfaces/tools-fs.SymbolsArgs.html +5 -0
- package/docs/api/interfaces/tools-fs.ValidationCommandRequest.html +4 -0
- package/docs/api/interfaces/tools-fs.ValidationCommandResult.html +6 -0
- package/docs/api/interfaces/tools-fs.ValidationOptions.html +4 -0
- package/docs/api/interfaces/tools-fs.WriteFileArgs.html +6 -0
- package/docs/api/interfaces/tools-http.HeaderEntry.html +4 -0
- package/docs/api/interfaces/tools-http.HttpClientRequest.html +11 -0
- package/docs/api/interfaces/tools-http.HttpPolicy.html +13 -0
- package/docs/api/interfaces/tools-http.HttpRequestResult.html +17 -0
- package/docs/api/interfaces/tools-http.HttpRetryOptions.html +10 -0
- package/docs/api/interfaces/tools-http.HttpToolClient.html +5 -0
- package/docs/api/interfaces/tools-http.HttpTools.html +4 -0
- package/docs/api/interfaces/tools-http.HttpToolsConfig.html +38 -0
- package/docs/api/interfaces/tools-shell.ApprovalDecision.html +5 -0
- package/docs/api/interfaces/tools-shell.CommandRequest.html +14 -0
- package/docs/api/interfaces/tools-shell.CommandResult.html +21 -0
- package/docs/api/interfaces/tools-shell.EnvPolicy.html +9 -0
- package/docs/api/interfaces/tools-shell.ShellPolicy.html +7 -0
- package/docs/api/interfaces/tools-shell.ShellTools.html +6 -0
- package/docs/api/interfaces/tools-shell.ShellToolsConfig.html +26 -0
- package/docs/api/interfaces/tools-web.Citation.html +6 -0
- package/docs/api/interfaces/tools-web.SearchProvider.html +3 -0
- package/docs/api/interfaces/tools-web.SearchRequest.html +4 -0
- package/docs/api/interfaces/tools-web.SearchResult.html +7 -0
- package/docs/api/interfaces/tools-web.SourceMetadata.html +10 -0
- package/docs/api/interfaces/tools-web.WebTools.html +6 -0
- package/docs/api/interfaces/tools-web.WebToolsConfig.html +56 -0
- package/docs/api/modules/agent.html +5 -0
- package/docs/api/modules/context.html +5 -0
- package/docs/api/modules/errors.html +8 -0
- package/docs/api/modules/events.html +16 -0
- package/docs/api/modules/execution.html +4 -0
- package/docs/api/modules/index.html +16 -0
- package/docs/api/modules/lifecycle.html +6 -0
- package/docs/api/modules/messages.html +3 -0
- package/docs/api/modules/providers.html +10 -0
- package/docs/api/modules/runtime.html +4 -0
- package/docs/api/modules/schema.html +5 -0
- package/docs/api/modules/session-stores.html +6 -0
- package/docs/api/modules/session.html +4 -0
- package/docs/api/modules/testing.html +4 -0
- package/docs/api/modules/testing_conformance.html +21 -0
- package/docs/api/modules/tools-fs.html +9 -0
- package/docs/api/modules/tools-http.html +8 -0
- package/docs/api/modules/tools-shell.html +12 -0
- package/docs/api/modules/tools-web.html +7 -0
- package/docs/api/modules/tools.html +4 -0
- package/docs/api/types/events.Attrs.html +2 -0
- package/docs/api/types/index.AnyRunOptions.html +3 -0
- package/docs/api/types/index.BufferedRunOptions.html +2 -0
- package/docs/api/types/index.ContentPart.html +2 -0
- package/docs/api/types/index.FinishReason.html +2 -0
- package/docs/api/types/index.Infer.html +2 -0
- package/docs/api/types/index.Instructions.html +5 -0
- package/docs/api/types/index.LogLevel.html +2 -0
- package/docs/api/types/index.Role.html +2 -0
- package/docs/api/types/index.RunEvent.html +30 -0
- package/docs/api/types/index.RunHandle.html +7 -0
- package/docs/api/types/index.RunInput.html +2 -0
- package/docs/api/types/index.RunSubscriber.html +4 -0
- package/docs/api/types/index.SafeParseResult.html +2 -0
- package/docs/api/types/index.StreamEvent.html +8 -0
- package/docs/api/types/index.StreamingRunOptions.html +2 -0
- package/docs/api/types/index.TelemetryHandle.html +2 -0
- package/docs/api/types/index.TokenCounter.html +2 -0
- package/docs/api/types/index.ToolChoice.html +2 -0
- package/docs/api/types/index.ToolResult.html +2 -0
- package/docs/api/types/lifecycle.ProviderProbe.html +2 -0
- package/docs/api/types/session-stores.SessionStoreHookOperation.html +1 -0
- package/docs/api/types/testing_conformance.MakeProvider.html +2 -0
- package/docs/api/types/tools-fs.SymbolKind.html +1 -0
- package/docs/api/types/tools-http.HostPattern.html +2 -0
- package/docs/api/types/tools-shell.CommandPattern.html +2 -0
- package/docs/api/types/tools-web.RobotsPolicy.html +2 -0
- package/docs/api/variables/events.SPAN_PROVIDER.html +2 -0
- package/docs/api/variables/events.SPAN_RUN.html +2 -0
- package/docs/api/variables/events.SPAN_TOOL.html +2 -0
- package/docs/api/variables/execution.DEFAULT_MAX_HANDOFFS.html +2 -0
- package/docs/api/variables/execution.DEFAULT_MAX_STEPS.html +2 -0
- package/docs/api/variables/index.s.html +11 -0
- package/docs/api/variables/providers.DEFAULT_TIMEOUT_MS.html +2 -0
- package/docs/api/variables/session-stores.SESSION_STORE_SCHEMA_VERSION.html +1 -0
- package/docs/api/variables/session-stores.jsonSessionStoreCodec.html +2 -0
- package/docs/api/variables/tools-http.HTTP_EVENT.html +2 -0
- package/docs/api/variables/tools-shell.SHELL_EVENT.html +2 -0
- package/docs/events-and-telemetry.md +68 -0
- package/docs/execution.md +63 -0
- package/docs/getting-started.md +73 -0
- package/docs/multi-agent.md +52 -0
- package/docs/optional-tool-packs.md +93 -0
- package/docs/providers.md +68 -0
- package/docs/retrieval.md +83 -0
- package/docs/sessions-and-context.md +82 -0
- package/docs/testing-and-lifecycle.md +60 -0
- package/docs/tools.md +69 -0
- package/examples/01-minimal-agent.ts +19 -0
- package/examples/02-custom-tool.ts +32 -0
- package/examples/03-streaming.ts +46 -0
- package/examples/04-sessions-context.ts +40 -0
- package/examples/05-events-subscribers.ts +35 -0
- package/examples/06-multi-agent-handoff.ts +27 -0
- package/examples/07-sub-agent-tool.ts +26 -0
- package/examples/08-testing-agent.ts +31 -0
- package/examples/09-tools-filesystem.ts +49 -0
- package/examples/10-tools-http-web.ts +74 -0
- package/examples/11-shell-tools.ts +39 -0
- package/examples/12-provider-openai.ts +24 -0
- package/examples/13-lifecycle.ts +37 -0
- package/examples/14-retrieval.ts +65 -0
- package/examples/incident-analysis.ts +39 -87
- package/package.json +78 -4
- package/dist/index-02s1fjxr.js +0 -226
- package/examples/README.md +0 -24
- package/examples/lifecycle.ts +0 -53
- package/examples/multi-agent.ts +0 -93
- package/examples/terminal-coder.ts +0 -80
|
@@ -0,0 +1,1695 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FilesystemPolicyError,
|
|
3
|
+
SchemaValidationError
|
|
4
|
+
} from "../index-ajr3nk10.js";
|
|
5
|
+
import {
|
|
6
|
+
defineTool
|
|
7
|
+
} from "../index-a67ej96j.js";
|
|
8
|
+
import {
|
|
9
|
+
__require
|
|
10
|
+
} from "../index-37x76zdn.js";
|
|
11
|
+
|
|
12
|
+
// src/tools-fs/define.ts
|
|
13
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
14
|
+
import { lstat as lstat3, mkdir as mkdir2, readFile as readFile3, stat } from "node:fs/promises";
|
|
15
|
+
import { dirname as dirname4, extname as extname3 } from "node:path";
|
|
16
|
+
import { applyPatch as applyUnifiedPatch, parsePatch } from "diff";
|
|
17
|
+
import Fuse from "fuse.js";
|
|
18
|
+
|
|
19
|
+
// src/tools-fs/files.ts
|
|
20
|
+
import { createHash } from "node:crypto";
|
|
21
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
22
|
+
import { lstat, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
23
|
+
import { basename, dirname as dirname2, extname, join, relative as relative2 } from "node:path";
|
|
24
|
+
import { glob } from "glob";
|
|
25
|
+
import ignore from "ignore";
|
|
26
|
+
|
|
27
|
+
// src/tools-fs/policy.ts
|
|
28
|
+
import { existsSync, lstatSync, realpathSync } from "node:fs";
|
|
29
|
+
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
30
|
+
class FilesystemAccessError extends Error {
|
|
31
|
+
constructor(message) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.name = "FilesystemAccessError";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
var DEFAULT_MAX_READ_BYTES = 20000;
|
|
37
|
+
var DEFAULT_MAX_WRITE_BYTES = 1e6;
|
|
38
|
+
var DEFAULT_MAX_ENTRIES = 300;
|
|
39
|
+
var DEFAULT_MAX_RESULTS = 50;
|
|
40
|
+
function normalizePathForOutput(path) {
|
|
41
|
+
return path.replaceAll("\\", "/");
|
|
42
|
+
}
|
|
43
|
+
function isInside(root, candidate) {
|
|
44
|
+
const rel = relative(root, candidate);
|
|
45
|
+
return rel === "" || !rel.startsWith("..") && !isAbsolute(rel);
|
|
46
|
+
}
|
|
47
|
+
function displayPath(root, path) {
|
|
48
|
+
const rel = relative(root, path);
|
|
49
|
+
return rel === "" ? "." : normalizePathForOutput(rel);
|
|
50
|
+
}
|
|
51
|
+
function realpathIfPossible(path) {
|
|
52
|
+
return existsSync(path) ? realpathSync.native(path) : resolve(path);
|
|
53
|
+
}
|
|
54
|
+
function assertPositiveInt(name, value, fallback) {
|
|
55
|
+
if (value === undefined)
|
|
56
|
+
return fallback;
|
|
57
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
58
|
+
throw new FilesystemPolicyError(`filesystemTools: \`${name}\` must be a positive integer`);
|
|
59
|
+
}
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
function normalizeFilesystemPolicy(config) {
|
|
63
|
+
if (!Array.isArray(config.allowedRoots) || config.allowedRoots.length === 0) {
|
|
64
|
+
throw new FilesystemPolicyError("filesystemTools: `allowedRoots` must be a non-empty array of absolute paths");
|
|
65
|
+
}
|
|
66
|
+
const allowedRoots = config.allowedRoots.map((entry) => {
|
|
67
|
+
if (typeof entry !== "string" || !isAbsolute(entry)) {
|
|
68
|
+
throw new FilesystemPolicyError(`filesystemTools: allowedRoots entry must be an absolute path, got ${JSON.stringify(entry)}`);
|
|
69
|
+
}
|
|
70
|
+
const logical = resolve(entry);
|
|
71
|
+
return { logical, real: realpathIfPossible(logical) };
|
|
72
|
+
});
|
|
73
|
+
const defaultRoot = config.defaultRoot === undefined ? allowedRoots[0].logical : resolve(allowedRoots[0].logical, config.defaultRoot);
|
|
74
|
+
const defaultRootReal = realpathIfPossible(defaultRoot);
|
|
75
|
+
const defaultAllowed = allowedRoots.some((root) => isInside(root.logical, defaultRoot) && isInside(root.real, defaultRootReal));
|
|
76
|
+
if (!defaultAllowed) {
|
|
77
|
+
throw new FilesystemPolicyError("filesystemTools: `defaultRoot` must resolve inside allowedRoots");
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
allowedRoots,
|
|
81
|
+
defaultRoot,
|
|
82
|
+
defaultRootReal,
|
|
83
|
+
maxReadBytes: assertPositiveInt("maxReadBytes", config.maxReadBytes, DEFAULT_MAX_READ_BYTES),
|
|
84
|
+
maxWriteBytes: assertPositiveInt("maxWriteBytes", config.maxWriteBytes, DEFAULT_MAX_WRITE_BYTES),
|
|
85
|
+
maxEntries: assertPositiveInt("maxEntries", config.maxEntries, DEFAULT_MAX_ENTRIES),
|
|
86
|
+
maxResults: assertPositiveInt("maxResults", config.maxResults, DEFAULT_MAX_RESULTS)
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function allowedRootFor(policy, logicalPath) {
|
|
90
|
+
return policy.allowedRoots.find((root) => isInside(root.logical, logicalPath)) ?? null;
|
|
91
|
+
}
|
|
92
|
+
function nearestExistingParent(path) {
|
|
93
|
+
let current = path;
|
|
94
|
+
while (!existsSync(current)) {
|
|
95
|
+
const parent = dirname(current);
|
|
96
|
+
if (parent === current)
|
|
97
|
+
return current;
|
|
98
|
+
current = parent;
|
|
99
|
+
}
|
|
100
|
+
const stat = lstatSync(current);
|
|
101
|
+
return stat.isDirectory() ? current : dirname(current);
|
|
102
|
+
}
|
|
103
|
+
async function resolvePath(policy, input, options = {}) {
|
|
104
|
+
const raw = input === undefined || input === "" ? "." : input;
|
|
105
|
+
const base = options.base ?? policy.defaultRoot;
|
|
106
|
+
const logicalPath = isAbsolute(raw) ? resolve(raw) : resolve(base, raw);
|
|
107
|
+
const root = allowedRootFor(policy, logicalPath);
|
|
108
|
+
if (root === null) {
|
|
109
|
+
throw new FilesystemAccessError(`path ${JSON.stringify(raw)} is outside the allowed roots`);
|
|
110
|
+
}
|
|
111
|
+
if (options.mustExist === true && !existsSync(logicalPath)) {
|
|
112
|
+
throw new FilesystemAccessError(`path ${JSON.stringify(raw)} does not exist`);
|
|
113
|
+
}
|
|
114
|
+
if (existsSync(logicalPath)) {
|
|
115
|
+
const realPath = realpathSync.native(logicalPath);
|
|
116
|
+
if (!isInside(root.real, realPath)) {
|
|
117
|
+
throw new FilesystemAccessError(`path ${JSON.stringify(raw)} resolves outside the allowed roots`);
|
|
118
|
+
}
|
|
119
|
+
return { path: logicalPath, root: root.logical, realPath };
|
|
120
|
+
}
|
|
121
|
+
if (options.forCreate === true || options.mustExist !== true) {
|
|
122
|
+
const parent = nearestExistingParent(logicalPath);
|
|
123
|
+
const parentReal = realpathSync.native(parent);
|
|
124
|
+
if (!isInside(root.real, parentReal)) {
|
|
125
|
+
throw new FilesystemAccessError(`path ${JSON.stringify(raw)} parent resolves outside the allowed roots`);
|
|
126
|
+
}
|
|
127
|
+
return { path: logicalPath, root: root.logical };
|
|
128
|
+
}
|
|
129
|
+
throw new FilesystemAccessError(`path ${JSON.stringify(raw)} does not exist`);
|
|
130
|
+
}
|
|
131
|
+
async function resolveRoot(policy, input) {
|
|
132
|
+
const resolved = await resolvePath(policy, input, { mustExist: true });
|
|
133
|
+
if (!lstatSync(resolved.path).isDirectory()) {
|
|
134
|
+
throw new FilesystemAccessError(`root ${JSON.stringify(input ?? ".")} is not a directory`);
|
|
135
|
+
}
|
|
136
|
+
return resolved;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// src/tools-fs/files.ts
|
|
140
|
+
var DEFAULT_IGNORE = [
|
|
141
|
+
".git",
|
|
142
|
+
"node_modules",
|
|
143
|
+
"dist",
|
|
144
|
+
"coverage",
|
|
145
|
+
".cache",
|
|
146
|
+
"*.tsbuildinfo"
|
|
147
|
+
];
|
|
148
|
+
var TEXT_EXTENSIONS = new Set([
|
|
149
|
+
".c",
|
|
150
|
+
".cc",
|
|
151
|
+
".config",
|
|
152
|
+
".cpp",
|
|
153
|
+
".cs",
|
|
154
|
+
".css",
|
|
155
|
+
".csv",
|
|
156
|
+
".go",
|
|
157
|
+
".h",
|
|
158
|
+
".hpp",
|
|
159
|
+
".html",
|
|
160
|
+
".java",
|
|
161
|
+
".js",
|
|
162
|
+
".json",
|
|
163
|
+
".jsx",
|
|
164
|
+
".md",
|
|
165
|
+
".mjs",
|
|
166
|
+
".php",
|
|
167
|
+
".py",
|
|
168
|
+
".rb",
|
|
169
|
+
".rs",
|
|
170
|
+
".sh",
|
|
171
|
+
".sql",
|
|
172
|
+
".svg",
|
|
173
|
+
".toml",
|
|
174
|
+
".ts",
|
|
175
|
+
".tsx",
|
|
176
|
+
".txt",
|
|
177
|
+
".xml",
|
|
178
|
+
".yaml",
|
|
179
|
+
".yml"
|
|
180
|
+
]);
|
|
181
|
+
function fileVersion(bytes) {
|
|
182
|
+
return `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
|
|
183
|
+
}
|
|
184
|
+
function languageForPath(path) {
|
|
185
|
+
const ext = extname(path).toLowerCase();
|
|
186
|
+
switch (ext) {
|
|
187
|
+
case ".ts":
|
|
188
|
+
return "TypeScript";
|
|
189
|
+
case ".tsx":
|
|
190
|
+
return "TSX";
|
|
191
|
+
case ".js":
|
|
192
|
+
case ".mjs":
|
|
193
|
+
case ".cjs":
|
|
194
|
+
return "JavaScript";
|
|
195
|
+
case ".jsx":
|
|
196
|
+
return "JSX";
|
|
197
|
+
case ".json":
|
|
198
|
+
return "JSON";
|
|
199
|
+
case ".md":
|
|
200
|
+
return "Markdown";
|
|
201
|
+
case ".css":
|
|
202
|
+
return "CSS";
|
|
203
|
+
case ".html":
|
|
204
|
+
return "HTML";
|
|
205
|
+
case ".py":
|
|
206
|
+
return "Python";
|
|
207
|
+
case ".rs":
|
|
208
|
+
return "Rust";
|
|
209
|
+
case ".go":
|
|
210
|
+
return "Go";
|
|
211
|
+
case ".java":
|
|
212
|
+
return "Java";
|
|
213
|
+
case ".sh":
|
|
214
|
+
return "Shell";
|
|
215
|
+
case ".yml":
|
|
216
|
+
case ".yaml":
|
|
217
|
+
return "YAML";
|
|
218
|
+
default:
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
function isProbablyTextPath(path) {
|
|
223
|
+
const ext = extname(path).toLowerCase();
|
|
224
|
+
return TEXT_EXTENSIONS.has(ext) || basename(path).startsWith(".");
|
|
225
|
+
}
|
|
226
|
+
function isHiddenRelativePath(path) {
|
|
227
|
+
return normalizePathForOutput(path).split("/").some((part) => part.startsWith(".") && part !== "." && part !== "..");
|
|
228
|
+
}
|
|
229
|
+
async function buildIgnore(root, respectGitignore) {
|
|
230
|
+
const ig = ignore().add(DEFAULT_IGNORE);
|
|
231
|
+
if (!respectGitignore)
|
|
232
|
+
return ig;
|
|
233
|
+
const gitignorePath = join(root, ".gitignore");
|
|
234
|
+
if (existsSync2(gitignorePath)) {
|
|
235
|
+
const content = await readFile(gitignorePath, "utf8");
|
|
236
|
+
ig.add(content);
|
|
237
|
+
}
|
|
238
|
+
return ig;
|
|
239
|
+
}
|
|
240
|
+
function hasGlobMatch(path, globs) {
|
|
241
|
+
if (globs === undefined || globs.length === 0)
|
|
242
|
+
return true;
|
|
243
|
+
const matcher = ignore().add(globs.map((g) => g.replaceAll("\\", "/")));
|
|
244
|
+
return matcher.ignores(path);
|
|
245
|
+
}
|
|
246
|
+
function isExcluded(path, globs) {
|
|
247
|
+
if (globs === undefined || globs.length === 0)
|
|
248
|
+
return false;
|
|
249
|
+
const matcher = ignore().add(globs.map((g) => g.replaceAll("\\", "/")));
|
|
250
|
+
return matcher.ignores(path);
|
|
251
|
+
}
|
|
252
|
+
async function listEntries(root, options = {}) {
|
|
253
|
+
const includeHidden = options.includeHidden ?? false;
|
|
254
|
+
const respectGitignore = options.respectGitignore ?? true;
|
|
255
|
+
const maxEntries = options.maxEntries ?? Number.POSITIVE_INFINITY;
|
|
256
|
+
const ig = await buildIgnore(root, respectGitignore);
|
|
257
|
+
const patterns = options.includeGlobs !== undefined && options.includeGlobs.length > 0 ? [...options.includeGlobs] : ["**/*"];
|
|
258
|
+
const matches = await glob(patterns, {
|
|
259
|
+
cwd: root,
|
|
260
|
+
dot: includeHidden,
|
|
261
|
+
nodir: false,
|
|
262
|
+
absolute: true,
|
|
263
|
+
follow: false,
|
|
264
|
+
windowsPathsNoEscape: true
|
|
265
|
+
});
|
|
266
|
+
const entries = [];
|
|
267
|
+
let total = 0;
|
|
268
|
+
for (const abs of matches.sort()) {
|
|
269
|
+
const rel = normalizePathForOutput(relative2(root, abs));
|
|
270
|
+
if (rel === "")
|
|
271
|
+
continue;
|
|
272
|
+
if (!includeHidden && isHiddenRelativePath(rel))
|
|
273
|
+
continue;
|
|
274
|
+
if (ig.ignores(rel))
|
|
275
|
+
continue;
|
|
276
|
+
if (!hasGlobMatch(rel, options.includeGlobs))
|
|
277
|
+
continue;
|
|
278
|
+
if (isExcluded(rel, options.excludeGlobs))
|
|
279
|
+
continue;
|
|
280
|
+
const info = await lstat(abs);
|
|
281
|
+
const type = info.isDirectory() ? "directory" : info.isFile() ? "file" : info.isSymbolicLink() ? "symlink" : "other";
|
|
282
|
+
if (options.onlyFiles === true && type !== "file")
|
|
283
|
+
continue;
|
|
284
|
+
if (options.includeFiles === false && type === "file")
|
|
285
|
+
continue;
|
|
286
|
+
total += 1;
|
|
287
|
+
if (entries.length >= maxEntries)
|
|
288
|
+
continue;
|
|
289
|
+
entries.push({ path: abs, relativePath: rel, type, size: info.size });
|
|
290
|
+
}
|
|
291
|
+
return { entries, truncated: total > entries.length, total };
|
|
292
|
+
}
|
|
293
|
+
async function readTextFile(path, maxBytes) {
|
|
294
|
+
const bytes = await readFile(path);
|
|
295
|
+
const sliced = bytes.byteLength > maxBytes ? bytes.subarray(0, maxBytes) : bytes;
|
|
296
|
+
return {
|
|
297
|
+
text: sliced.toString("utf8"),
|
|
298
|
+
version: fileVersion(bytes),
|
|
299
|
+
truncated: bytes.byteLength > maxBytes
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
async function atomicWrite(path, content) {
|
|
303
|
+
await mkdir(dirname2(path), { recursive: true });
|
|
304
|
+
const tmp = join(dirname2(path), `.engine-lib-${process.pid}-${Date.now()}.tmp`);
|
|
305
|
+
await writeFile(tmp, content, "utf8");
|
|
306
|
+
await rename(tmp, path);
|
|
307
|
+
return fileVersion(content);
|
|
308
|
+
}
|
|
309
|
+
async function removeFile(path) {
|
|
310
|
+
await rm(path, { force: true });
|
|
311
|
+
}
|
|
312
|
+
function lineOffsets(text) {
|
|
313
|
+
return text.replace(/\r\n/g, `
|
|
314
|
+
`).split(`
|
|
315
|
+
`);
|
|
316
|
+
}
|
|
317
|
+
function renderLineRange(lines, startLine, endLine, includeLineNumbers) {
|
|
318
|
+
const selected = lines.slice(startLine - 1, endLine);
|
|
319
|
+
if (!includeLineNumbers)
|
|
320
|
+
return selected.join(`
|
|
321
|
+
`);
|
|
322
|
+
return selected.map((line, index) => `${startLine + index}: ${line}`).join(`
|
|
323
|
+
`);
|
|
324
|
+
}
|
|
325
|
+
function clampLineRange(totalLines, startLine, endLine) {
|
|
326
|
+
const start = Math.max(1, Math.min(startLine ?? 1, Math.max(totalLines, 1)));
|
|
327
|
+
const end = Math.max(start, Math.min(endLine ?? totalLines, Math.max(totalLines, 1)));
|
|
328
|
+
return { startLine: start, endLine: end };
|
|
329
|
+
}
|
|
330
|
+
function detectFrameworks(entries) {
|
|
331
|
+
const files = new Set(entries.map((entry) => entry.relativePath));
|
|
332
|
+
const frameworks = new Set;
|
|
333
|
+
if (files.has("package.json"))
|
|
334
|
+
frameworks.add("Node.js");
|
|
335
|
+
if (files.has("bun.lock") || files.has("bun.lockb"))
|
|
336
|
+
frameworks.add("Bun");
|
|
337
|
+
if ([...files].some((file) => file.includes("vite.config.")))
|
|
338
|
+
frameworks.add("Vite");
|
|
339
|
+
if ([...files].some((file) => file.includes("next.config.")))
|
|
340
|
+
frameworks.add("Next.js");
|
|
341
|
+
if ([...files].some((file) => file.includes("tsconfig.json")))
|
|
342
|
+
frameworks.add("TypeScript");
|
|
343
|
+
if (files.has("Cargo.toml"))
|
|
344
|
+
frameworks.add("Rust/Cargo");
|
|
345
|
+
if (files.has("go.mod"))
|
|
346
|
+
frameworks.add("Go modules");
|
|
347
|
+
return [...frameworks].sort();
|
|
348
|
+
}
|
|
349
|
+
function importantFiles(entries) {
|
|
350
|
+
const importantNames = new Set([
|
|
351
|
+
"README.md",
|
|
352
|
+
"package.json",
|
|
353
|
+
"tsconfig.json",
|
|
354
|
+
"bun.lock",
|
|
355
|
+
"Cargo.toml",
|
|
356
|
+
"go.mod",
|
|
357
|
+
"pyproject.toml",
|
|
358
|
+
"Dockerfile",
|
|
359
|
+
".gitignore"
|
|
360
|
+
]);
|
|
361
|
+
return entries.filter((entry) => entry.type === "file" && importantNames.has(entry.relativePath)).map((entry) => entry.relativePath);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// src/tools-fs/git.ts
|
|
365
|
+
import { execFile } from "node:child_process";
|
|
366
|
+
import { promisify } from "node:util";
|
|
367
|
+
var execFileAsync = promisify(execFile);
|
|
368
|
+
async function git(root, args) {
|
|
369
|
+
try {
|
|
370
|
+
const { stdout } = await execFileAsync("git", ["-C", root, ...args], {
|
|
371
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
372
|
+
windowsHide: true
|
|
373
|
+
});
|
|
374
|
+
return stdout;
|
|
375
|
+
} catch {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function truncate(text, maxChars) {
|
|
380
|
+
if (text.length <= maxChars)
|
|
381
|
+
return { text, truncated: false };
|
|
382
|
+
return { text: `${text.slice(0, Math.max(0, maxChars - 13))}
|
|
383
|
+
[truncated]`, truncated: true };
|
|
384
|
+
}
|
|
385
|
+
function splitGitDiffByFile(diff) {
|
|
386
|
+
const sections = diff.match(/^diff --git .*?(?=^diff --git |\s*$)/gms) ?? [];
|
|
387
|
+
const byPath = new Map;
|
|
388
|
+
for (const section of sections) {
|
|
389
|
+
const header = /^diff --git a\/(.+?) b\/(.+)$/m.exec(section);
|
|
390
|
+
if (header === null)
|
|
391
|
+
continue;
|
|
392
|
+
const oldPath = header[1]?.replaceAll("\\", "/");
|
|
393
|
+
const newPath = header[2]?.replaceAll("\\", "/");
|
|
394
|
+
if (oldPath !== undefined)
|
|
395
|
+
byPath.set(oldPath, section);
|
|
396
|
+
if (newPath !== undefined)
|
|
397
|
+
byPath.set(newPath, section);
|
|
398
|
+
}
|
|
399
|
+
return byPath;
|
|
400
|
+
}
|
|
401
|
+
async function diffStatus(root, options) {
|
|
402
|
+
const top = await git(root, ["rev-parse", "--show-toplevel"]);
|
|
403
|
+
if (top === null)
|
|
404
|
+
return { isGitRepo: false, files: [], truncated: false };
|
|
405
|
+
const pathArgs = options.paths !== undefined && options.paths.length > 0 ? ["--", ...options.paths] : [];
|
|
406
|
+
const status = await git(root, ["status", "--porcelain=v1", ...pathArgs]);
|
|
407
|
+
if (status === null)
|
|
408
|
+
return { isGitRepo: false, files: [], truncated: false };
|
|
409
|
+
const files = status.split(/\r?\n/).filter((line) => line.trim() !== "").map((line) => {
|
|
410
|
+
const statusCode = line.slice(0, 2).trim() || "modified";
|
|
411
|
+
const path = line.slice(3).replace(/^.* -> /, "").replaceAll("\\", "/");
|
|
412
|
+
return { path, status: statusCode };
|
|
413
|
+
});
|
|
414
|
+
if (!options.includeDiff || files.length === 0) {
|
|
415
|
+
return { isGitRepo: true, files, truncated: false };
|
|
416
|
+
}
|
|
417
|
+
const diff = await git(root, ["diff", `--unified=${options.contextLines}`, ...pathArgs]);
|
|
418
|
+
const diffByFile = splitGitDiffByFile(diff ?? "");
|
|
419
|
+
let truncated = false;
|
|
420
|
+
const withDiff = files.map((file) => {
|
|
421
|
+
const clipped = truncate(diffByFile.get(file.path) ?? "", options.maxDiffChars);
|
|
422
|
+
truncated = truncated || clipped.truncated;
|
|
423
|
+
return { ...file, diff: clipped.text };
|
|
424
|
+
});
|
|
425
|
+
return {
|
|
426
|
+
isGitRepo: true,
|
|
427
|
+
files: withDiff,
|
|
428
|
+
truncated
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// src/tools-fs/schemas.ts
|
|
433
|
+
function issue(path, message) {
|
|
434
|
+
return { path: [...path], message };
|
|
435
|
+
}
|
|
436
|
+
function isPlainObject(value) {
|
|
437
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
438
|
+
}
|
|
439
|
+
function validate(node, input, path = []) {
|
|
440
|
+
const issues = [];
|
|
441
|
+
if (node.oneOf !== undefined) {
|
|
442
|
+
const matches = node.oneOf.filter((schema) => validate(schema, input, path).length === 0);
|
|
443
|
+
if (matches.length !== 1)
|
|
444
|
+
issues.push(issue(path, "expected exactly one matching schema"));
|
|
445
|
+
return issues;
|
|
446
|
+
}
|
|
447
|
+
if (node.enum !== undefined) {
|
|
448
|
+
if (!node.enum.includes(input)) {
|
|
449
|
+
issues.push(issue(path, `expected one of ${JSON.stringify(node.enum)}`));
|
|
450
|
+
}
|
|
451
|
+
return issues;
|
|
452
|
+
}
|
|
453
|
+
switch (node.type) {
|
|
454
|
+
case "string":
|
|
455
|
+
if (typeof input !== "string")
|
|
456
|
+
issues.push(issue(path, "expected string"));
|
|
457
|
+
break;
|
|
458
|
+
case "boolean":
|
|
459
|
+
if (typeof input !== "boolean")
|
|
460
|
+
issues.push(issue(path, "expected boolean"));
|
|
461
|
+
break;
|
|
462
|
+
case "integer":
|
|
463
|
+
if (typeof input !== "number" || !Number.isInteger(input)) {
|
|
464
|
+
issues.push(issue(path, "expected integer"));
|
|
465
|
+
}
|
|
466
|
+
break;
|
|
467
|
+
case "number":
|
|
468
|
+
if (typeof input !== "number" || Number.isNaN(input))
|
|
469
|
+
issues.push(issue(path, "expected number"));
|
|
470
|
+
break;
|
|
471
|
+
case "array":
|
|
472
|
+
if (!Array.isArray(input)) {
|
|
473
|
+
issues.push(issue(path, "expected array"));
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
if (node.items !== undefined) {
|
|
477
|
+
input.forEach((value, index) => {
|
|
478
|
+
issues.push(...validate(node.items, value, [...path, index]));
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
break;
|
|
482
|
+
case "object": {
|
|
483
|
+
if (!isPlainObject(input)) {
|
|
484
|
+
issues.push(issue(path, "expected object"));
|
|
485
|
+
break;
|
|
486
|
+
}
|
|
487
|
+
const properties = node.properties ?? {};
|
|
488
|
+
for (const key of node.required ?? []) {
|
|
489
|
+
if (input[key] === undefined)
|
|
490
|
+
issues.push(issue([...path, key], "required"));
|
|
491
|
+
}
|
|
492
|
+
for (const [key, prop] of Object.entries(properties)) {
|
|
493
|
+
if (input[key] !== undefined)
|
|
494
|
+
issues.push(...validate(prop, input[key], [...path, key]));
|
|
495
|
+
}
|
|
496
|
+
if (node.additionalProperties === false) {
|
|
497
|
+
for (const key of Object.keys(input)) {
|
|
498
|
+
if (!(key in properties))
|
|
499
|
+
issues.push(issue([...path, key], "unexpected property"));
|
|
500
|
+
}
|
|
501
|
+
} else if (typeof node.additionalProperties === "object") {
|
|
502
|
+
for (const [key, value] of Object.entries(input)) {
|
|
503
|
+
if (!(key in properties)) {
|
|
504
|
+
issues.push(...validate(node.additionalProperties, value, [...path, key]));
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
default:
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
if ((node.type === "integer" || node.type === "number") && typeof input === "number") {
|
|
514
|
+
if (node.minimum !== undefined && input < node.minimum) {
|
|
515
|
+
issues.push(issue(path, `expected >= ${node.minimum}`));
|
|
516
|
+
}
|
|
517
|
+
if (node.maximum !== undefined && input > node.maximum) {
|
|
518
|
+
issues.push(issue(path, `expected <= ${node.maximum}`));
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return issues;
|
|
522
|
+
}
|
|
523
|
+
function toolSchema(jsonSchema) {
|
|
524
|
+
return {
|
|
525
|
+
jsonSchema,
|
|
526
|
+
safeParse(input) {
|
|
527
|
+
const issues = validate(jsonSchema, input);
|
|
528
|
+
if (issues.length === 0)
|
|
529
|
+
return { success: true, data: input };
|
|
530
|
+
return {
|
|
531
|
+
success: false,
|
|
532
|
+
error: new SchemaValidationError("schema validation failed", { issues })
|
|
533
|
+
};
|
|
534
|
+
},
|
|
535
|
+
parse(input) {
|
|
536
|
+
const result = this.safeParse(input);
|
|
537
|
+
if (!result.success)
|
|
538
|
+
throw result.error;
|
|
539
|
+
return result.data;
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
var stringSchema = { type: "string" };
|
|
544
|
+
var boolSchema = { type: "boolean" };
|
|
545
|
+
var intSchema = (extra = {}) => ({ type: "integer", ...extra });
|
|
546
|
+
var stringArray = { type: "array", items: stringSchema };
|
|
547
|
+
var SCHEMAS = {
|
|
548
|
+
repoMap: toolSchema({
|
|
549
|
+
type: "object",
|
|
550
|
+
properties: {
|
|
551
|
+
root: { ...stringSchema, default: "." },
|
|
552
|
+
depth: intSchema({ minimum: 1, maximum: 6, default: 3 }),
|
|
553
|
+
include_files: { ...boolSchema, default: true },
|
|
554
|
+
include_symbols: { ...boolSchema, default: false },
|
|
555
|
+
max_entries: intSchema({ default: 300 }),
|
|
556
|
+
respect_gitignore: { ...boolSchema, default: true }
|
|
557
|
+
},
|
|
558
|
+
additionalProperties: false
|
|
559
|
+
}),
|
|
560
|
+
findFiles: toolSchema({
|
|
561
|
+
type: "object",
|
|
562
|
+
properties: {
|
|
563
|
+
query: stringSchema,
|
|
564
|
+
mode: { type: "string", enum: ["auto", "exact", "glob", "fuzzy", "extension", "regex"], default: "auto" },
|
|
565
|
+
root: { ...stringSchema, default: "." },
|
|
566
|
+
include_hidden: { ...boolSchema, default: false },
|
|
567
|
+
respect_gitignore: { ...boolSchema, default: true },
|
|
568
|
+
max_results: intSchema({ default: 50 })
|
|
569
|
+
},
|
|
570
|
+
required: ["query"],
|
|
571
|
+
additionalProperties: false
|
|
572
|
+
}),
|
|
573
|
+
searchText: toolSchema({
|
|
574
|
+
type: "object",
|
|
575
|
+
properties: {
|
|
576
|
+
pattern: stringSchema,
|
|
577
|
+
mode: { type: "string", enum: ["literal", "regex"], default: "literal" },
|
|
578
|
+
root: { ...stringSchema, default: "." },
|
|
579
|
+
include_globs: stringArray,
|
|
580
|
+
exclude_globs: stringArray,
|
|
581
|
+
case_sensitive: { ...boolSchema, default: false },
|
|
582
|
+
context_lines: intSchema({ minimum: 0, maximum: 10, default: 2 }),
|
|
583
|
+
max_results: intSchema({ default: 50 }),
|
|
584
|
+
max_preview_chars: intSchema({ default: 12000 }),
|
|
585
|
+
use_index: { ...boolSchema, default: true }
|
|
586
|
+
},
|
|
587
|
+
required: ["pattern"],
|
|
588
|
+
additionalProperties: false
|
|
589
|
+
}),
|
|
590
|
+
searchSemantic: toolSchema({
|
|
591
|
+
type: "object",
|
|
592
|
+
properties: {
|
|
593
|
+
query: stringSchema,
|
|
594
|
+
root: { ...stringSchema, default: "." },
|
|
595
|
+
include_globs: stringArray,
|
|
596
|
+
exclude_globs: stringArray,
|
|
597
|
+
granularity: { type: "string", enum: ["file", "chunk", "symbol"], default: "chunk" },
|
|
598
|
+
max_results: intSchema({ default: 20 }),
|
|
599
|
+
max_preview_chars: intSchema({ default: 12000 })
|
|
600
|
+
},
|
|
601
|
+
required: ["query"],
|
|
602
|
+
additionalProperties: false
|
|
603
|
+
}),
|
|
604
|
+
symbols: toolSchema({
|
|
605
|
+
type: "object",
|
|
606
|
+
properties: {
|
|
607
|
+
path: stringSchema,
|
|
608
|
+
recursive: { ...boolSchema, default: false },
|
|
609
|
+
symbol_kinds: {
|
|
610
|
+
type: "array",
|
|
611
|
+
items: {
|
|
612
|
+
type: "string",
|
|
613
|
+
enum: ["class", "function", "method", "interface", "type", "variable", "import", "export"]
|
|
614
|
+
}
|
|
615
|
+
},
|
|
616
|
+
max_results: intSchema({ default: 200 })
|
|
617
|
+
},
|
|
618
|
+
required: ["path"],
|
|
619
|
+
additionalProperties: false
|
|
620
|
+
}),
|
|
621
|
+
read: toolSchema({
|
|
622
|
+
type: "object",
|
|
623
|
+
properties: {
|
|
624
|
+
path: stringSchema,
|
|
625
|
+
start_line: intSchema({ minimum: 1 }),
|
|
626
|
+
end_line: intSchema({ minimum: 1 }),
|
|
627
|
+
symbol: stringSchema,
|
|
628
|
+
max_bytes: intSchema({ default: 20000 }),
|
|
629
|
+
include_line_numbers: { ...boolSchema, default: true },
|
|
630
|
+
collapse_imports: { ...boolSchema, default: false },
|
|
631
|
+
collapse_comments: { ...boolSchema, default: false }
|
|
632
|
+
},
|
|
633
|
+
required: ["path"],
|
|
634
|
+
additionalProperties: false
|
|
635
|
+
}),
|
|
636
|
+
openWindow: toolSchema({
|
|
637
|
+
type: "object",
|
|
638
|
+
properties: {
|
|
639
|
+
path: stringSchema,
|
|
640
|
+
anchor: { oneOf: [intSchema(), stringSchema] },
|
|
641
|
+
window_lines: intSchema({ minimum: 20, maximum: 200, default: 100 }),
|
|
642
|
+
direction: { type: "string", enum: ["center", "next", "prev"], default: "center" }
|
|
643
|
+
},
|
|
644
|
+
required: ["path"],
|
|
645
|
+
additionalProperties: false
|
|
646
|
+
}),
|
|
647
|
+
editReplace: toolSchema({
|
|
648
|
+
type: "object",
|
|
649
|
+
properties: {
|
|
650
|
+
path: stringSchema,
|
|
651
|
+
old_text: stringSchema,
|
|
652
|
+
new_text: stringSchema,
|
|
653
|
+
occurrence: intSchema({ minimum: 1, default: 1 }),
|
|
654
|
+
expected_file_version: stringSchema,
|
|
655
|
+
validate: validationSchema()
|
|
656
|
+
},
|
|
657
|
+
required: ["path", "old_text", "new_text"],
|
|
658
|
+
additionalProperties: false
|
|
659
|
+
}),
|
|
660
|
+
editRange: toolSchema({
|
|
661
|
+
type: "object",
|
|
662
|
+
properties: {
|
|
663
|
+
path: stringSchema,
|
|
664
|
+
start_line: intSchema({ minimum: 1 }),
|
|
665
|
+
end_line: intSchema({ minimum: 1 }),
|
|
666
|
+
new_text: stringSchema,
|
|
667
|
+
expected_file_version: stringSchema,
|
|
668
|
+
validate: validationSchema()
|
|
669
|
+
},
|
|
670
|
+
required: ["path", "start_line", "end_line", "new_text", "expected_file_version"],
|
|
671
|
+
additionalProperties: false
|
|
672
|
+
}),
|
|
673
|
+
applyPatch: toolSchema({
|
|
674
|
+
type: "object",
|
|
675
|
+
properties: {
|
|
676
|
+
patch: stringSchema,
|
|
677
|
+
root: { ...stringSchema, default: "." },
|
|
678
|
+
dry_run: { ...boolSchema, default: false },
|
|
679
|
+
expected_versions: { type: "object", additionalProperties: stringSchema },
|
|
680
|
+
validate: validationSchema()
|
|
681
|
+
},
|
|
682
|
+
required: ["patch"],
|
|
683
|
+
additionalProperties: false
|
|
684
|
+
}),
|
|
685
|
+
writeFile: toolSchema({
|
|
686
|
+
type: "object",
|
|
687
|
+
properties: {
|
|
688
|
+
path: stringSchema,
|
|
689
|
+
content: stringSchema,
|
|
690
|
+
mode: { type: "string", enum: ["create_only", "overwrite", "append"], default: "create_only" },
|
|
691
|
+
expected_file_version: stringSchema,
|
|
692
|
+
create_dirs: { ...boolSchema, default: true }
|
|
693
|
+
},
|
|
694
|
+
required: ["path", "content"],
|
|
695
|
+
additionalProperties: false
|
|
696
|
+
}),
|
|
697
|
+
diffStatus: toolSchema({
|
|
698
|
+
type: "object",
|
|
699
|
+
properties: {
|
|
700
|
+
paths: stringArray,
|
|
701
|
+
include_diff: { ...boolSchema, default: true },
|
|
702
|
+
max_diff_chars: intSchema({ default: 20000 }),
|
|
703
|
+
context_lines: intSchema({ default: 3 })
|
|
704
|
+
},
|
|
705
|
+
additionalProperties: false
|
|
706
|
+
})
|
|
707
|
+
};
|
|
708
|
+
function validationSchema() {
|
|
709
|
+
return {
|
|
710
|
+
type: "object",
|
|
711
|
+
properties: {
|
|
712
|
+
syntax: { ...boolSchema, default: true },
|
|
713
|
+
format: { ...boolSchema, default: false },
|
|
714
|
+
tests: stringArray
|
|
715
|
+
},
|
|
716
|
+
additionalProperties: false
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
// src/tools-fs/search.ts
|
|
721
|
+
import { spawn } from "node:child_process";
|
|
722
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
723
|
+
import { dirname as dirname3, isAbsolute as isAbsolute2, relative as relative3 } from "node:path";
|
|
724
|
+
import MiniSearch from "minisearch";
|
|
725
|
+
function truncate2(text, maxChars) {
|
|
726
|
+
return text.length > maxChars ? `${text.slice(0, Math.max(0, maxChars - 13))}
|
|
727
|
+
[truncated]` : text;
|
|
728
|
+
}
|
|
729
|
+
function buildRegex(pattern, mode, caseSensitive) {
|
|
730
|
+
const source = mode === "literal" ? pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&") : pattern;
|
|
731
|
+
return new RegExp(source, caseSensitive ? "g" : "gi");
|
|
732
|
+
}
|
|
733
|
+
function searchOneFile(text, relPath, regex, contextLines, maxPreviewChars, maxResults) {
|
|
734
|
+
const lines = lineOffsets(text);
|
|
735
|
+
const out = [];
|
|
736
|
+
for (let index = 0;index < lines.length; index += 1) {
|
|
737
|
+
const line = lines[index] ?? "";
|
|
738
|
+
regex.lastIndex = 0;
|
|
739
|
+
const match = regex.exec(line);
|
|
740
|
+
if (match === null)
|
|
741
|
+
continue;
|
|
742
|
+
const start = Math.max(0, index - contextLines);
|
|
743
|
+
const end = Math.min(lines.length - 1, index + contextLines);
|
|
744
|
+
const context = [];
|
|
745
|
+
for (let lineIndex = start;lineIndex <= end; lineIndex += 1) {
|
|
746
|
+
context.push({
|
|
747
|
+
line: lineIndex + 1,
|
|
748
|
+
text: lines[lineIndex] ?? "",
|
|
749
|
+
match: lineIndex === index
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
out.push({
|
|
753
|
+
path: relPath,
|
|
754
|
+
line: index + 1,
|
|
755
|
+
column: match.index + 1,
|
|
756
|
+
preview: truncate2(line, maxPreviewChars),
|
|
757
|
+
context
|
|
758
|
+
});
|
|
759
|
+
if (out.length >= maxResults)
|
|
760
|
+
break;
|
|
761
|
+
}
|
|
762
|
+
return out;
|
|
763
|
+
}
|
|
764
|
+
async function tryRipgrepSearch(options) {
|
|
765
|
+
try {
|
|
766
|
+
const mod = await import("@vscode/ripgrep");
|
|
767
|
+
const rgPath = mod.rgPath;
|
|
768
|
+
if (typeof rgPath !== "string")
|
|
769
|
+
return null;
|
|
770
|
+
const args = [
|
|
771
|
+
"--json",
|
|
772
|
+
"--color=never",
|
|
773
|
+
"--line-number",
|
|
774
|
+
"--column",
|
|
775
|
+
"--context",
|
|
776
|
+
String(options.contextLines),
|
|
777
|
+
...options.caseSensitive ? [] : ["--ignore-case"],
|
|
778
|
+
...options.mode === "literal" ? ["--fixed-strings"] : [],
|
|
779
|
+
...(options.includeGlobs ?? []).flatMap((glob2) => ["--glob", glob2]),
|
|
780
|
+
...(options.excludeGlobs ?? []).flatMap((glob2) => ["--glob", `!${glob2}`]),
|
|
781
|
+
"--",
|
|
782
|
+
options.pattern,
|
|
783
|
+
options.root
|
|
784
|
+
];
|
|
785
|
+
const stdout = await new Promise((resolve2, reject) => {
|
|
786
|
+
const child = spawn(rgPath, args, { cwd: options.root, stdio: ["ignore", "pipe", "pipe"] });
|
|
787
|
+
let out = "";
|
|
788
|
+
let err = "";
|
|
789
|
+
child.stdout.setEncoding("utf8");
|
|
790
|
+
child.stderr.setEncoding("utf8");
|
|
791
|
+
child.stdout.on("data", (chunk) => {
|
|
792
|
+
out += chunk;
|
|
793
|
+
});
|
|
794
|
+
child.stderr.on("data", (chunk) => {
|
|
795
|
+
err += chunk;
|
|
796
|
+
});
|
|
797
|
+
child.on("error", reject);
|
|
798
|
+
child.on("close", (code) => {
|
|
799
|
+
if (code === 0 || code === 1)
|
|
800
|
+
resolve2(out);
|
|
801
|
+
else
|
|
802
|
+
reject(new Error(err || `ripgrep exited ${code}`));
|
|
803
|
+
});
|
|
804
|
+
});
|
|
805
|
+
const results = [];
|
|
806
|
+
for (const raw of stdout.split(/\r?\n/)) {
|
|
807
|
+
if (raw.trim() === "")
|
|
808
|
+
continue;
|
|
809
|
+
const event = JSON.parse(raw);
|
|
810
|
+
if (event.type !== "match" || event.data === undefined)
|
|
811
|
+
continue;
|
|
812
|
+
const rawPath = event.data.path?.text ?? "";
|
|
813
|
+
const path = isAbsolute2(rawPath) ? relative3(options.root, rawPath) : rawPath;
|
|
814
|
+
const line = event.data.line_number ?? 1;
|
|
815
|
+
const text = event.data.lines?.text?.replace(/\r?\n$/, "") ?? "";
|
|
816
|
+
const column = (event.data.submatches?.[0]?.start ?? 0) + 1;
|
|
817
|
+
results.push({
|
|
818
|
+
path: path.replaceAll("\\", "/"),
|
|
819
|
+
line,
|
|
820
|
+
column,
|
|
821
|
+
preview: truncate2(text, options.maxPreviewChars),
|
|
822
|
+
context: [{ line, text, match: true }]
|
|
823
|
+
});
|
|
824
|
+
if (results.length >= options.maxResults)
|
|
825
|
+
break;
|
|
826
|
+
}
|
|
827
|
+
return results;
|
|
828
|
+
} catch {
|
|
829
|
+
return null;
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
async function searchText(root, options) {
|
|
833
|
+
const rg = await tryRipgrepSearch({ root, ...options });
|
|
834
|
+
if (rg !== null) {
|
|
835
|
+
return { results: rg, backend: "ripgrep", truncated: rg.length >= options.maxResults };
|
|
836
|
+
}
|
|
837
|
+
const files = await listEntries(root, {
|
|
838
|
+
onlyFiles: true,
|
|
839
|
+
respectGitignore: true,
|
|
840
|
+
includeHidden: false,
|
|
841
|
+
includeGlobs: options.includeGlobs,
|
|
842
|
+
excludeGlobs: options.excludeGlobs,
|
|
843
|
+
maxEntries: Number.POSITIVE_INFINITY
|
|
844
|
+
});
|
|
845
|
+
const regex = buildRegex(options.pattern, options.mode, options.caseSensitive);
|
|
846
|
+
const out = [];
|
|
847
|
+
for (const entry of files.entries) {
|
|
848
|
+
if (!isProbablyTextPath(entry.path))
|
|
849
|
+
continue;
|
|
850
|
+
const text = await readFile2(entry.path, "utf8").catch(() => "");
|
|
851
|
+
out.push(...searchOneFile(text, entry.relativePath, regex, options.contextLines, options.maxPreviewChars, options.maxResults - out.length));
|
|
852
|
+
if (out.length >= options.maxResults)
|
|
853
|
+
break;
|
|
854
|
+
}
|
|
855
|
+
return { results: out, backend: "node", truncated: files.truncated || out.length >= options.maxResults };
|
|
856
|
+
}
|
|
857
|
+
function chunksForText(path, text, maxPreviewChars) {
|
|
858
|
+
const lines = lineOffsets(text);
|
|
859
|
+
const chunks = [];
|
|
860
|
+
const chunkSize = 40;
|
|
861
|
+
for (let start = 0;start < lines.length; start += chunkSize) {
|
|
862
|
+
const end = Math.min(lines.length, start + chunkSize);
|
|
863
|
+
const preview = truncate2(lines.slice(start, end).join(`
|
|
864
|
+
`), maxPreviewChars);
|
|
865
|
+
chunks.push({ path, score: 0, startLine: start + 1, endLine: end, preview });
|
|
866
|
+
}
|
|
867
|
+
return chunks;
|
|
868
|
+
}
|
|
869
|
+
function symbolDocsForText(path, text, maxPreviewChars) {
|
|
870
|
+
const lines = lineOffsets(text);
|
|
871
|
+
const out = [];
|
|
872
|
+
const regex = /^\s*(?:export\s+)?(?:class|function|interface|type|const|let|var)\s+([A-Za-z_$][\w$]*)/;
|
|
873
|
+
lines.forEach((line, index) => {
|
|
874
|
+
const match = regex.exec(line);
|
|
875
|
+
if (match === null)
|
|
876
|
+
return;
|
|
877
|
+
out.push({
|
|
878
|
+
path,
|
|
879
|
+
score: 0,
|
|
880
|
+
startLine: index + 1,
|
|
881
|
+
endLine: index + 1,
|
|
882
|
+
preview: truncate2(line, maxPreviewChars),
|
|
883
|
+
symbol: match[1]
|
|
884
|
+
});
|
|
885
|
+
});
|
|
886
|
+
return out;
|
|
887
|
+
}
|
|
888
|
+
async function searchSemantic(root, options) {
|
|
889
|
+
const files = await listEntries(root, {
|
|
890
|
+
onlyFiles: true,
|
|
891
|
+
respectGitignore: true,
|
|
892
|
+
includeGlobs: options.includeGlobs,
|
|
893
|
+
excludeGlobs: options.excludeGlobs,
|
|
894
|
+
maxEntries: Number.POSITIVE_INFINITY
|
|
895
|
+
});
|
|
896
|
+
const docs = [];
|
|
897
|
+
for (const entry of files.entries) {
|
|
898
|
+
if (!isProbablyTextPath(entry.path))
|
|
899
|
+
continue;
|
|
900
|
+
const text = (await readTextFile(entry.path, 200000).catch(() => null))?.text;
|
|
901
|
+
if (text === undefined)
|
|
902
|
+
continue;
|
|
903
|
+
if (options.granularity === "file") {
|
|
904
|
+
docs.push({
|
|
905
|
+
id: entry.relativePath,
|
|
906
|
+
path: entry.relativePath,
|
|
907
|
+
score: 0,
|
|
908
|
+
startLine: 1,
|
|
909
|
+
endLine: lineOffsets(text).length,
|
|
910
|
+
preview: truncate2(text, options.maxPreviewChars),
|
|
911
|
+
text: `${entry.relativePath}
|
|
912
|
+
${text}`
|
|
913
|
+
});
|
|
914
|
+
} else {
|
|
915
|
+
const units = options.granularity === "symbol" ? symbolDocsForText(entry.relativePath, text, options.maxPreviewChars) : chunksForText(entry.relativePath, text, options.maxPreviewChars);
|
|
916
|
+
for (const chunk of units.length > 0 ? units : chunksForText(entry.relativePath, text, options.maxPreviewChars)) {
|
|
917
|
+
docs.push({
|
|
918
|
+
id: `${entry.relativePath}:${chunk.startLine}:${chunk.symbol ?? ""}`,
|
|
919
|
+
...chunk,
|
|
920
|
+
text: `${entry.relativePath}
|
|
921
|
+
${chunk.symbol ?? ""}
|
|
922
|
+
${chunk.preview}`
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
if (docs.length === 0)
|
|
928
|
+
return { results: [], truncated: false };
|
|
929
|
+
const index = new MiniSearch({
|
|
930
|
+
fields: ["path", "text", "symbol"],
|
|
931
|
+
storeFields: ["path", "startLine", "endLine", "preview", "symbol"],
|
|
932
|
+
searchOptions: { prefix: true, fuzzy: 0.2 }
|
|
933
|
+
});
|
|
934
|
+
index.addAll(docs);
|
|
935
|
+
const matches = index.search(options.query).slice(0, options.maxResults);
|
|
936
|
+
return {
|
|
937
|
+
results: matches.map((match) => ({
|
|
938
|
+
path: String(match.path),
|
|
939
|
+
score: match.score,
|
|
940
|
+
startLine: Number(match.startLine),
|
|
941
|
+
endLine: Number(match.endLine),
|
|
942
|
+
preview: String(match.preview),
|
|
943
|
+
...match.symbol !== undefined ? { symbol: String(match.symbol) } : {}
|
|
944
|
+
})),
|
|
945
|
+
truncated: matches.length >= options.maxResults && docs.length > matches.length
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// src/tools-fs/symbols.ts
|
|
950
|
+
import { lstat as lstat2 } from "node:fs/promises";
|
|
951
|
+
import { extname as extname2 } from "node:path";
|
|
952
|
+
var TS_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
953
|
+
function wanted(kind, kinds) {
|
|
954
|
+
return kinds === undefined || kinds.length === 0 || kinds.includes(kind);
|
|
955
|
+
}
|
|
956
|
+
function lineRangeFromText(text, start, end) {
|
|
957
|
+
const beforeStart = text.slice(0, start);
|
|
958
|
+
const beforeEnd = text.slice(0, end);
|
|
959
|
+
return {
|
|
960
|
+
startLine: beforeStart.split(/\r\n|\r|\n/).length,
|
|
961
|
+
endLine: beforeEnd.split(/\r\n|\r|\n/).length
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
function regexSymbols(text, path, kinds) {
|
|
965
|
+
const results = [];
|
|
966
|
+
const lines = lineOffsets(text);
|
|
967
|
+
const patterns = [
|
|
968
|
+
{ kind: "import", regex: /^\s*import\s+(?:type\s+)?(?:.+?\s+from\s+)?["']([^"']+)["']/gm, nameIndex: 1 },
|
|
969
|
+
{ kind: "export", regex: /^\s*export\s+(?:default\s+)?(?:\{[^}]+\}|(?:class|function|interface|type|const|let|var)\s+([A-Za-z_$][\w$]*)?)/gm, nameIndex: 1 },
|
|
970
|
+
{ kind: "class", regex: /^\s*(?:export\s+)?class\s+([A-Za-z_$][\w$]*)/gm, nameIndex: 1 },
|
|
971
|
+
{ kind: "function", regex: /^\s*(?:export\s+)?(?:async\s+)?function\s+([A-Za-z_$][\w$]*)/gm, nameIndex: 1 },
|
|
972
|
+
{ kind: "interface", regex: /^\s*(?:export\s+)?interface\s+([A-Za-z_$][\w$]*)/gm, nameIndex: 1 },
|
|
973
|
+
{ kind: "type", regex: /^\s*(?:export\s+)?type\s+([A-Za-z_$][\w$]*)/gm, nameIndex: 1 },
|
|
974
|
+
{ kind: "variable", regex: /^\s*(?:export\s+)?(?:const|let|var)\s+([A-Za-z_$][\w$]*)/gm, nameIndex: 1 }
|
|
975
|
+
];
|
|
976
|
+
for (const { kind, regex, nameIndex } of patterns) {
|
|
977
|
+
if (!wanted(kind, kinds))
|
|
978
|
+
continue;
|
|
979
|
+
for (const match of text.matchAll(regex)) {
|
|
980
|
+
const name = match[nameIndex] ?? match[0].trim();
|
|
981
|
+
const range = lineRangeFromText(text, match.index ?? 0, (match.index ?? 0) + match[0].length);
|
|
982
|
+
results.push({
|
|
983
|
+
path,
|
|
984
|
+
name,
|
|
985
|
+
kind,
|
|
986
|
+
startLine: range.startLine,
|
|
987
|
+
endLine: range.endLine,
|
|
988
|
+
signature: lines[range.startLine - 1]?.trim()
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
return results.sort((a, b) => a.startLine - b.startLine || a.name.localeCompare(b.name));
|
|
993
|
+
}
|
|
994
|
+
async function typescriptSymbols(text, path, kinds) {
|
|
995
|
+
if (!TS_EXTENSIONS.has(extname2(path).toLowerCase()))
|
|
996
|
+
return regexSymbols(text, path, kinds);
|
|
997
|
+
try {
|
|
998
|
+
const ts = await import("typescript");
|
|
999
|
+
const scriptKind = extname2(path).toLowerCase().includes("x") ? ts.ScriptKind.TSX : ts.ScriptKind.TS;
|
|
1000
|
+
const source = ts.createSourceFile(path, text, ts.ScriptTarget.Latest, true, scriptKind);
|
|
1001
|
+
const lines = lineOffsets(text);
|
|
1002
|
+
const out = [];
|
|
1003
|
+
const emit = (node, kind, name) => {
|
|
1004
|
+
if (!wanted(kind, kinds))
|
|
1005
|
+
return;
|
|
1006
|
+
const start = source.getLineAndCharacterOfPosition(node.getStart(source)).line + 1;
|
|
1007
|
+
const end = source.getLineAndCharacterOfPosition(node.getEnd()).line + 1;
|
|
1008
|
+
out.push({
|
|
1009
|
+
path,
|
|
1010
|
+
name,
|
|
1011
|
+
kind,
|
|
1012
|
+
startLine: start,
|
|
1013
|
+
endLine: end,
|
|
1014
|
+
signature: lines[start - 1]?.trim()
|
|
1015
|
+
});
|
|
1016
|
+
};
|
|
1017
|
+
const exported = (node) => {
|
|
1018
|
+
return ts.canHaveModifiers(node) && ts.getModifiers(node)?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) === true;
|
|
1019
|
+
};
|
|
1020
|
+
const visit = (node) => {
|
|
1021
|
+
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
|
|
1022
|
+
emit(node, "import", node.moduleSpecifier.text);
|
|
1023
|
+
} else if (ts.isClassDeclaration(node) && node.name !== undefined) {
|
|
1024
|
+
emit(node, "class", node.name.text);
|
|
1025
|
+
if (exported(node))
|
|
1026
|
+
emit(node, "export", node.name.text);
|
|
1027
|
+
} else if (ts.isFunctionDeclaration(node) && node.name !== undefined) {
|
|
1028
|
+
emit(node, "function", node.name.text);
|
|
1029
|
+
if (exported(node))
|
|
1030
|
+
emit(node, "export", node.name.text);
|
|
1031
|
+
} else if (ts.isMethodDeclaration(node) && ts.isIdentifier(node.name)) {
|
|
1032
|
+
emit(node, "method", node.name.text);
|
|
1033
|
+
} else if (ts.isInterfaceDeclaration(node)) {
|
|
1034
|
+
emit(node, "interface", node.name.text);
|
|
1035
|
+
if (exported(node))
|
|
1036
|
+
emit(node, "export", node.name.text);
|
|
1037
|
+
} else if (ts.isTypeAliasDeclaration(node)) {
|
|
1038
|
+
emit(node, "type", node.name.text);
|
|
1039
|
+
if (exported(node))
|
|
1040
|
+
emit(node, "export", node.name.text);
|
|
1041
|
+
} else if (ts.isVariableStatement(node)) {
|
|
1042
|
+
for (const declaration of node.declarationList.declarations) {
|
|
1043
|
+
if (ts.isIdentifier(declaration.name)) {
|
|
1044
|
+
emit(declaration, "variable", declaration.name.text);
|
|
1045
|
+
if (exported(node))
|
|
1046
|
+
emit(declaration, "export", declaration.name.text);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
} else if (ts.isExportDeclaration(node)) {
|
|
1050
|
+
const moduleName = node.moduleSpecifier !== undefined && ts.isStringLiteral(node.moduleSpecifier) ? node.moduleSpecifier.text : "export";
|
|
1051
|
+
emit(node, "export", moduleName);
|
|
1052
|
+
}
|
|
1053
|
+
ts.forEachChild(node, visit);
|
|
1054
|
+
};
|
|
1055
|
+
visit(source);
|
|
1056
|
+
return out.sort((a, b) => a.startLine - b.startLine || a.name.localeCompare(b.name));
|
|
1057
|
+
} catch {
|
|
1058
|
+
return regexSymbols(text, path, kinds);
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
async function symbolsForFile(absPath, root, maxBytes, kinds) {
|
|
1062
|
+
const text = (await readTextFile(absPath, maxBytes)).text;
|
|
1063
|
+
return typescriptSymbols(text, displayPath(root, absPath), kinds);
|
|
1064
|
+
}
|
|
1065
|
+
async function symbolsForPath(policy, input, options = {}) {
|
|
1066
|
+
const resolved = await resolvePath(policy, input, { mustExist: true });
|
|
1067
|
+
const info = await lstat2(resolved.path);
|
|
1068
|
+
const maxResults = options.maxResults ?? 200;
|
|
1069
|
+
if (info.isDirectory()) {
|
|
1070
|
+
if (options.recursive !== true) {
|
|
1071
|
+
return { results: [], truncated: false, root: resolved.path };
|
|
1072
|
+
}
|
|
1073
|
+
const root = await resolveRoot(policy, input);
|
|
1074
|
+
const listed = await listEntries(root.path, {
|
|
1075
|
+
onlyFiles: true,
|
|
1076
|
+
respectGitignore: true,
|
|
1077
|
+
maxEntries: maxResults * 4
|
|
1078
|
+
});
|
|
1079
|
+
const out = [];
|
|
1080
|
+
for (const entry of listed.entries) {
|
|
1081
|
+
if (!TS_EXTENSIONS.has(extname2(entry.path).toLowerCase()))
|
|
1082
|
+
continue;
|
|
1083
|
+
out.push(...await symbolsForFile(entry.path, root.path, policy.maxReadBytes, options.kinds));
|
|
1084
|
+
if (out.length >= maxResults)
|
|
1085
|
+
break;
|
|
1086
|
+
}
|
|
1087
|
+
return { results: out.slice(0, maxResults), truncated: listed.truncated || out.length > maxResults, root: root.path };
|
|
1088
|
+
}
|
|
1089
|
+
const results = await symbolsForFile(resolved.path, resolved.root, policy.maxReadBytes, options.kinds);
|
|
1090
|
+
return { results: results.slice(0, maxResults), truncated: results.length > maxResults, root: resolved.root };
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
// src/tools-fs/define.ts
|
|
1094
|
+
function fail(error) {
|
|
1095
|
+
if (error instanceof FilesystemAccessError)
|
|
1096
|
+
return { ok: false, error: error.message };
|
|
1097
|
+
if (error instanceof Error)
|
|
1098
|
+
return { ok: false, error: error.message };
|
|
1099
|
+
return { ok: false, error: String(error) };
|
|
1100
|
+
}
|
|
1101
|
+
function bounded(value, fallback, min, max) {
|
|
1102
|
+
const n = value ?? fallback;
|
|
1103
|
+
return Math.max(min, Math.min(max, n));
|
|
1104
|
+
}
|
|
1105
|
+
function countLanguages(entries) {
|
|
1106
|
+
const counts = new Map;
|
|
1107
|
+
for (const entry of entries) {
|
|
1108
|
+
if (entry.type !== "file")
|
|
1109
|
+
continue;
|
|
1110
|
+
const language = languageForPath(entry.relativePath);
|
|
1111
|
+
if (language !== undefined)
|
|
1112
|
+
counts.set(language, (counts.get(language) ?? 0) + 1);
|
|
1113
|
+
}
|
|
1114
|
+
return [...counts.entries()].map(([language, files]) => ({ language, files })).sort((a, b) => b.files - a.files || a.language.localeCompare(b.language));
|
|
1115
|
+
}
|
|
1116
|
+
function depthOf(path) {
|
|
1117
|
+
return path === "." ? 0 : path.split("/").filter(Boolean).length;
|
|
1118
|
+
}
|
|
1119
|
+
function modeFor(query, mode) {
|
|
1120
|
+
if (mode !== undefined && mode !== "auto")
|
|
1121
|
+
return mode;
|
|
1122
|
+
if (/[\\/[*?\]]/.test(query))
|
|
1123
|
+
return query.includes("*") || query.includes("?") ? "glob" : "exact";
|
|
1124
|
+
if (query.startsWith(".") || /^[A-Za-z0-9]+$/.test(query))
|
|
1125
|
+
return "fuzzy";
|
|
1126
|
+
return "fuzzy";
|
|
1127
|
+
}
|
|
1128
|
+
function resultEntry(root, path, type, size, score) {
|
|
1129
|
+
return {
|
|
1130
|
+
path: displayPath(root, path),
|
|
1131
|
+
absolutePath: path,
|
|
1132
|
+
type,
|
|
1133
|
+
size,
|
|
1134
|
+
...score !== undefined ? { score } : {}
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
function collapseLines(lines, options) {
|
|
1138
|
+
const out = [];
|
|
1139
|
+
for (let i = 0;i < lines.length; i += 1) {
|
|
1140
|
+
const text = lines[i] ?? "";
|
|
1141
|
+
if (options.collapseImports && /^\s*import\b/.test(text)) {
|
|
1142
|
+
const start = i;
|
|
1143
|
+
while (i + 1 < lines.length && /^\s*import\b/.test(lines[i + 1] ?? ""))
|
|
1144
|
+
i += 1;
|
|
1145
|
+
out.push({ line: start + 1, text: `[imports collapsed: ${i - start + 1} lines]` });
|
|
1146
|
+
continue;
|
|
1147
|
+
}
|
|
1148
|
+
if (options.collapseComments && /^\s*(\/\/|\/\*|\*|#)/.test(text)) {
|
|
1149
|
+
const start = i;
|
|
1150
|
+
while (i + 1 < lines.length && /^\s*(\/\/|\/\*|\*|#)/.test(lines[i + 1] ?? ""))
|
|
1151
|
+
i += 1;
|
|
1152
|
+
out.push({ line: start + 1, text: `[comments collapsed: ${i - start + 1} lines]` });
|
|
1153
|
+
continue;
|
|
1154
|
+
}
|
|
1155
|
+
out.push({ line: i + 1, text });
|
|
1156
|
+
}
|
|
1157
|
+
return out;
|
|
1158
|
+
}
|
|
1159
|
+
function renderCollapsedLineRange(lines, startLine, endLine, includeLineNumbers, options) {
|
|
1160
|
+
const collapsed = collapseLines(lines.slice(startLine - 1, endLine), options);
|
|
1161
|
+
if (!includeLineNumbers)
|
|
1162
|
+
return collapsed.map((line) => line.text).join(`
|
|
1163
|
+
`);
|
|
1164
|
+
return collapsed.map((line) => `${startLine + line.line - 1}: ${line.text}`).join(`
|
|
1165
|
+
`);
|
|
1166
|
+
}
|
|
1167
|
+
async function syntaxCheck(path, content) {
|
|
1168
|
+
const ext = extname3(path).toLowerCase();
|
|
1169
|
+
try {
|
|
1170
|
+
if (ext === ".json")
|
|
1171
|
+
JSON.parse(content);
|
|
1172
|
+
if ([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"].includes(ext)) {
|
|
1173
|
+
const ts = await import("typescript");
|
|
1174
|
+
const diagnostics = ts.transpileModule(content, {
|
|
1175
|
+
compilerOptions: { noEmit: true },
|
|
1176
|
+
reportDiagnostics: true
|
|
1177
|
+
}).diagnostics ?? [];
|
|
1178
|
+
const first = diagnostics.find((diagnostic) => diagnostic.category === ts.DiagnosticCategory.Error);
|
|
1179
|
+
if (first !== undefined) {
|
|
1180
|
+
return { ok: false, error: ts.flattenDiagnosticMessageText(first.messageText, `
|
|
1181
|
+
`) };
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
return { ok: true };
|
|
1185
|
+
} catch (err) {
|
|
1186
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
async function runValidation(config, validate2, changedFiles, cwd, ctx) {
|
|
1190
|
+
const options = validate2 ?? {};
|
|
1191
|
+
const syntax = options.syntax ?? true;
|
|
1192
|
+
const syntaxResults = [];
|
|
1193
|
+
if (syntax) {
|
|
1194
|
+
for (const file of changedFiles) {
|
|
1195
|
+
syntaxResults.push({ path: file.path, ...await syntaxCheck(file.path, file.content) });
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
const tests = [];
|
|
1199
|
+
for (const command of options.tests ?? []) {
|
|
1200
|
+
if (config.runValidationCommand === undefined) {
|
|
1201
|
+
tests.push({ command, ok: false, skipped: true, reason: "no runValidationCommand hook configured" });
|
|
1202
|
+
continue;
|
|
1203
|
+
}
|
|
1204
|
+
tests.push(await config.runValidationCommand(command, {
|
|
1205
|
+
cwd,
|
|
1206
|
+
changedFiles: changedFiles.map((file) => file.path),
|
|
1207
|
+
ctx
|
|
1208
|
+
}));
|
|
1209
|
+
}
|
|
1210
|
+
return {
|
|
1211
|
+
syntax: syntaxResults,
|
|
1212
|
+
...options.format === true ? { format: { ok: false, skipped: true, reason: "no formatter hook configured" } } : {},
|
|
1213
|
+
tests
|
|
1214
|
+
};
|
|
1215
|
+
}
|
|
1216
|
+
async function readWholeFile(path, maxBytes) {
|
|
1217
|
+
const info = await stat(path);
|
|
1218
|
+
if (info.size > maxBytes) {
|
|
1219
|
+
throw new FilesystemAccessError(`file ${JSON.stringify(path)} exceeds max write/read size`);
|
|
1220
|
+
}
|
|
1221
|
+
const bytes = await readFile3(path);
|
|
1222
|
+
return { text: bytes.toString("utf8"), version: fileVersion(bytes) };
|
|
1223
|
+
}
|
|
1224
|
+
function checkExpectedVersion(current, expected) {
|
|
1225
|
+
if (expected !== undefined && expected !== current) {
|
|
1226
|
+
return { ok: false, error: `file version mismatch: expected ${expected}, got ${current}` };
|
|
1227
|
+
}
|
|
1228
|
+
return null;
|
|
1229
|
+
}
|
|
1230
|
+
function stripPatchPath(path) {
|
|
1231
|
+
if (path === undefined || path === "/dev/null")
|
|
1232
|
+
return null;
|
|
1233
|
+
return normalizePathForOutput(path).replace(/^a\//, "").replace(/^b\//, "");
|
|
1234
|
+
}
|
|
1235
|
+
function filesystemTools(config) {
|
|
1236
|
+
const policy = normalizeFilesystemPolicy(config);
|
|
1237
|
+
const repoMap = defineTool({
|
|
1238
|
+
name: "repo_map",
|
|
1239
|
+
description: "Compact repository overview with directory tree, important files, languages, frameworks, and optional symbols.",
|
|
1240
|
+
parameters: SCHEMAS.repoMap,
|
|
1241
|
+
async execute(args) {
|
|
1242
|
+
try {
|
|
1243
|
+
const root = await resolveRoot(policy, args.root);
|
|
1244
|
+
const depth = bounded(args.depth, 3, 1, 6);
|
|
1245
|
+
const maxEntries = bounded(args.max_entries, policy.maxEntries, 1, 1e4);
|
|
1246
|
+
const listed = await listEntries(root.path, {
|
|
1247
|
+
includeFiles: args.include_files ?? true,
|
|
1248
|
+
respectGitignore: args.respect_gitignore ?? true,
|
|
1249
|
+
maxEntries
|
|
1250
|
+
});
|
|
1251
|
+
const visible = listed.entries.filter((entry) => depthOf(entry.relativePath) <= depth);
|
|
1252
|
+
const symbols2 = args.include_symbols === true ? (await symbolsForPath(policy, root.path, { recursive: true, maxResults: 100 })).results : undefined;
|
|
1253
|
+
return {
|
|
1254
|
+
ok: true,
|
|
1255
|
+
content: {
|
|
1256
|
+
root: root.path,
|
|
1257
|
+
tree: visible.map((entry) => ({
|
|
1258
|
+
path: entry.relativePath,
|
|
1259
|
+
type: entry.type,
|
|
1260
|
+
size: entry.type === "file" ? entry.size : undefined,
|
|
1261
|
+
language: entry.type === "file" ? languageForPath(entry.relativePath) : undefined
|
|
1262
|
+
})),
|
|
1263
|
+
importantFiles: importantFiles(listed.entries),
|
|
1264
|
+
languages: countLanguages(listed.entries),
|
|
1265
|
+
frameworks: detectFrameworks(listed.entries),
|
|
1266
|
+
truncated: listed.truncated || visible.length < listed.entries.length,
|
|
1267
|
+
totalEntries: listed.total,
|
|
1268
|
+
...symbols2 !== undefined ? { symbols: symbols2 } : {}
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
} catch (err) {
|
|
1272
|
+
return fail(err);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
});
|
|
1276
|
+
const findFiles = defineTool({
|
|
1277
|
+
name: "find_files",
|
|
1278
|
+
description: "Find files by exact path, glob, fuzzy name, extension, or regex path pattern.",
|
|
1279
|
+
parameters: SCHEMAS.findFiles,
|
|
1280
|
+
async execute(args) {
|
|
1281
|
+
try {
|
|
1282
|
+
const root = await resolveRoot(policy, args.root);
|
|
1283
|
+
const maxResults = bounded(args.max_results, policy.maxResults, 1, 1000);
|
|
1284
|
+
const mode = modeFor(args.query, args.mode);
|
|
1285
|
+
if (mode === "exact") {
|
|
1286
|
+
const resolved = await resolvePath(policy, args.query, { base: root.path, mustExist: true });
|
|
1287
|
+
const info = await lstat3(resolved.path);
|
|
1288
|
+
return {
|
|
1289
|
+
ok: true,
|
|
1290
|
+
content: {
|
|
1291
|
+
root: root.path,
|
|
1292
|
+
mode,
|
|
1293
|
+
results: [resultEntry(root.path, resolved.path, info.isDirectory() ? "directory" : "file", info.size)],
|
|
1294
|
+
truncated: false
|
|
1295
|
+
}
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
const listed = await listEntries(root.path, {
|
|
1299
|
+
includeHidden: args.include_hidden ?? false,
|
|
1300
|
+
respectGitignore: args.respect_gitignore ?? true,
|
|
1301
|
+
maxEntries: Number.POSITIVE_INFINITY,
|
|
1302
|
+
includeGlobs: mode === "glob" ? [args.query] : undefined
|
|
1303
|
+
});
|
|
1304
|
+
let matches = listed.entries;
|
|
1305
|
+
if (mode === "extension") {
|
|
1306
|
+
const ext = args.query.startsWith(".") ? args.query : `.${args.query}`;
|
|
1307
|
+
matches = matches.filter((entry) => entry.type === "file" && extname3(entry.relativePath) === ext);
|
|
1308
|
+
} else if (mode === "regex") {
|
|
1309
|
+
const regex = new RegExp(args.query);
|
|
1310
|
+
matches = matches.filter((entry) => regex.test(entry.relativePath));
|
|
1311
|
+
} else if (mode === "fuzzy") {
|
|
1312
|
+
const fuse = new Fuse(matches, { keys: ["relativePath"], includeScore: true, threshold: 0.45 });
|
|
1313
|
+
const ranked = fuse.search(args.query).slice(0, maxResults);
|
|
1314
|
+
return {
|
|
1315
|
+
ok: true,
|
|
1316
|
+
content: {
|
|
1317
|
+
root: root.path,
|
|
1318
|
+
mode,
|
|
1319
|
+
results: ranked.map((item) => resultEntry(root.path, item.item.path, item.item.type, item.item.size, item.score)),
|
|
1320
|
+
truncated: ranked.length < matches.length && ranked.length >= maxResults
|
|
1321
|
+
}
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
return {
|
|
1325
|
+
ok: true,
|
|
1326
|
+
content: {
|
|
1327
|
+
root: root.path,
|
|
1328
|
+
mode,
|
|
1329
|
+
results: matches.slice(0, maxResults).map((entry) => resultEntry(root.path, entry.path, entry.type, entry.size)),
|
|
1330
|
+
truncated: matches.length > maxResults
|
|
1331
|
+
}
|
|
1332
|
+
};
|
|
1333
|
+
} catch (err) {
|
|
1334
|
+
return fail(err);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
});
|
|
1338
|
+
const searchText2 = defineTool({
|
|
1339
|
+
name: "search_text",
|
|
1340
|
+
description: "Search file contents using literal text or regex and return compact previews.",
|
|
1341
|
+
parameters: SCHEMAS.searchText,
|
|
1342
|
+
async execute(args) {
|
|
1343
|
+
try {
|
|
1344
|
+
const root = await resolveRoot(policy, args.root);
|
|
1345
|
+
const result = await searchText(root.path, {
|
|
1346
|
+
pattern: args.pattern,
|
|
1347
|
+
mode: args.mode ?? "literal",
|
|
1348
|
+
includeGlobs: args.include_globs,
|
|
1349
|
+
excludeGlobs: args.exclude_globs,
|
|
1350
|
+
caseSensitive: args.case_sensitive ?? false,
|
|
1351
|
+
contextLines: bounded(args.context_lines, 2, 0, 10),
|
|
1352
|
+
maxResults: bounded(args.max_results, policy.maxResults, 1, 1000),
|
|
1353
|
+
maxPreviewChars: bounded(args.max_preview_chars, 12000, 100, 1e5)
|
|
1354
|
+
});
|
|
1355
|
+
return { ok: true, content: { root: root.path, ...result } };
|
|
1356
|
+
} catch (err) {
|
|
1357
|
+
return fail(err);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
});
|
|
1361
|
+
const searchSemantic2 = defineTool({
|
|
1362
|
+
name: "search_semantic",
|
|
1363
|
+
description: "Find relevant files, chunks, or symbols from a natural-language code query.",
|
|
1364
|
+
parameters: SCHEMAS.searchSemantic,
|
|
1365
|
+
async execute(args) {
|
|
1366
|
+
try {
|
|
1367
|
+
const root = await resolveRoot(policy, args.root);
|
|
1368
|
+
const result = await searchSemantic(root.path, {
|
|
1369
|
+
query: args.query,
|
|
1370
|
+
includeGlobs: args.include_globs,
|
|
1371
|
+
excludeGlobs: args.exclude_globs,
|
|
1372
|
+
granularity: args.granularity ?? "chunk",
|
|
1373
|
+
maxResults: bounded(args.max_results, 20, 1, 1000),
|
|
1374
|
+
maxPreviewChars: bounded(args.max_preview_chars, 12000, 100, 1e5)
|
|
1375
|
+
});
|
|
1376
|
+
return { ok: true, content: { root: root.path, backend: "minisearch", ...result } };
|
|
1377
|
+
} catch (err) {
|
|
1378
|
+
return fail(err);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
});
|
|
1382
|
+
const symbols = defineTool({
|
|
1383
|
+
name: "symbols",
|
|
1384
|
+
description: "Return compact symbols, imports, exports, and line ranges for a file or directory.",
|
|
1385
|
+
parameters: SCHEMAS.symbols,
|
|
1386
|
+
async execute(args) {
|
|
1387
|
+
try {
|
|
1388
|
+
const result = await symbolsForPath(policy, args.path, {
|
|
1389
|
+
recursive: args.recursive ?? false,
|
|
1390
|
+
kinds: args.symbol_kinds,
|
|
1391
|
+
maxResults: bounded(args.max_results, 200, 1, 5000)
|
|
1392
|
+
});
|
|
1393
|
+
return { ok: true, content: result };
|
|
1394
|
+
} catch (err) {
|
|
1395
|
+
return fail(err);
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
});
|
|
1399
|
+
const read = defineTool({
|
|
1400
|
+
name: "read",
|
|
1401
|
+
description: "Read exact line ranges or symbol ranges with line numbers and token-aware limits.",
|
|
1402
|
+
parameters: SCHEMAS.read,
|
|
1403
|
+
async execute(args) {
|
|
1404
|
+
try {
|
|
1405
|
+
const resolved = await resolvePath(policy, args.path, { mustExist: true });
|
|
1406
|
+
const maxBytes = bounded(args.max_bytes, policy.maxReadBytes, 1, policy.maxWriteBytes);
|
|
1407
|
+
const file = await readTextFile(resolved.path, maxBytes);
|
|
1408
|
+
const lines = lineOffsets(file.text);
|
|
1409
|
+
let range = clampLineRange(lines.length, args.start_line, args.end_line);
|
|
1410
|
+
if (args.symbol !== undefined) {
|
|
1411
|
+
const found = (await symbolsForFile(resolved.path, resolved.root, maxBytes)).find((symbol) => symbol.name === args.symbol);
|
|
1412
|
+
if (found === undefined)
|
|
1413
|
+
return { ok: false, error: `symbol ${JSON.stringify(args.symbol)} not found` };
|
|
1414
|
+
range = { startLine: found.startLine, endLine: found.endLine };
|
|
1415
|
+
}
|
|
1416
|
+
return {
|
|
1417
|
+
ok: true,
|
|
1418
|
+
content: {
|
|
1419
|
+
path: displayPath(resolved.root, resolved.path),
|
|
1420
|
+
absolutePath: resolved.path,
|
|
1421
|
+
fileVersion: file.version,
|
|
1422
|
+
startLine: range.startLine,
|
|
1423
|
+
endLine: range.endLine,
|
|
1424
|
+
totalLines: lines.length,
|
|
1425
|
+
truncated: file.truncated,
|
|
1426
|
+
content: renderCollapsedLineRange(lines, range.startLine, range.endLine, args.include_line_numbers ?? true, {
|
|
1427
|
+
collapseImports: args.collapse_imports ?? false,
|
|
1428
|
+
collapseComments: args.collapse_comments ?? false
|
|
1429
|
+
})
|
|
1430
|
+
}
|
|
1431
|
+
};
|
|
1432
|
+
} catch (err) {
|
|
1433
|
+
return fail(err);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
});
|
|
1437
|
+
const openWindow = defineTool({
|
|
1438
|
+
name: "open_window",
|
|
1439
|
+
description: "Open or scroll a token-limited viewing window over a file.",
|
|
1440
|
+
parameters: SCHEMAS.openWindow,
|
|
1441
|
+
async execute(args) {
|
|
1442
|
+
try {
|
|
1443
|
+
const resolved = await resolvePath(policy, args.path, { mustExist: true });
|
|
1444
|
+
const file = await readTextFile(resolved.path, policy.maxReadBytes);
|
|
1445
|
+
const lines = lineOffsets(file.text);
|
|
1446
|
+
const windowLines = bounded(args.window_lines, 100, 20, 200);
|
|
1447
|
+
let anchorLine = 1;
|
|
1448
|
+
if (typeof args.anchor === "number") {
|
|
1449
|
+
anchorLine = args.anchor;
|
|
1450
|
+
} else if (typeof args.anchor === "string") {
|
|
1451
|
+
const symbol = (await symbolsForFile(resolved.path, resolved.root, policy.maxReadBytes)).find((item) => item.name === args.anchor);
|
|
1452
|
+
anchorLine = symbol?.startLine ?? Math.max(1, lines.findIndex((line) => line.includes(args.anchor)) + 1);
|
|
1453
|
+
}
|
|
1454
|
+
const direction = args.direction ?? "center";
|
|
1455
|
+
const start = direction === "next" ? anchorLine + 1 : direction === "prev" ? anchorLine - windowLines : anchorLine - Math.floor(windowLines / 2);
|
|
1456
|
+
const range = clampLineRange(lines.length, start, start + windowLines - 1);
|
|
1457
|
+
return {
|
|
1458
|
+
ok: true,
|
|
1459
|
+
content: {
|
|
1460
|
+
path: displayPath(resolved.root, resolved.path),
|
|
1461
|
+
absolutePath: resolved.path,
|
|
1462
|
+
fileVersion: file.version,
|
|
1463
|
+
anchorLine,
|
|
1464
|
+
startLine: range.startLine,
|
|
1465
|
+
endLine: range.endLine,
|
|
1466
|
+
totalLines: lines.length,
|
|
1467
|
+
content: renderLineRange(lines, range.startLine, range.endLine, true)
|
|
1468
|
+
}
|
|
1469
|
+
};
|
|
1470
|
+
} catch (err) {
|
|
1471
|
+
return fail(err);
|
|
1472
|
+
}
|
|
1473
|
+
}
|
|
1474
|
+
});
|
|
1475
|
+
const editReplace = defineTool({
|
|
1476
|
+
name: "edit_replace",
|
|
1477
|
+
description: "Replace exact text in a file with concurrency checks and mismatch diagnostics.",
|
|
1478
|
+
parameters: SCHEMAS.editReplace,
|
|
1479
|
+
async execute(args, ctx) {
|
|
1480
|
+
try {
|
|
1481
|
+
const resolved = await resolvePath(policy, args.path, { mustExist: true });
|
|
1482
|
+
const current = await readWholeFile(resolved.path, policy.maxWriteBytes);
|
|
1483
|
+
const versionError = checkExpectedVersion(current.version, args.expected_file_version);
|
|
1484
|
+
if (versionError !== null)
|
|
1485
|
+
return versionError;
|
|
1486
|
+
const occurrence = args.occurrence ?? 1;
|
|
1487
|
+
let index = -1;
|
|
1488
|
+
let from = 0;
|
|
1489
|
+
for (let i = 0;i < occurrence; i += 1) {
|
|
1490
|
+
index = current.text.indexOf(args.old_text, from);
|
|
1491
|
+
if (index === -1)
|
|
1492
|
+
return { ok: false, error: `old_text occurrence ${occurrence} not found` };
|
|
1493
|
+
from = index + args.old_text.length;
|
|
1494
|
+
}
|
|
1495
|
+
const next = `${current.text.slice(0, index)}${args.new_text}${current.text.slice(index + args.old_text.length)}`;
|
|
1496
|
+
const nextVersion = await atomicWrite(resolved.path, next);
|
|
1497
|
+
const validation = await runValidation(config, args.validate, [{ path: resolved.path, content: next }], dirname4(resolved.path), ctx);
|
|
1498
|
+
return {
|
|
1499
|
+
ok: true,
|
|
1500
|
+
content: {
|
|
1501
|
+
path: displayPath(resolved.root, resolved.path),
|
|
1502
|
+
previousFileVersion: current.version,
|
|
1503
|
+
fileVersion: nextVersion,
|
|
1504
|
+
changed: true,
|
|
1505
|
+
validation
|
|
1506
|
+
}
|
|
1507
|
+
};
|
|
1508
|
+
} catch (err) {
|
|
1509
|
+
return fail(err);
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
});
|
|
1513
|
+
const editRange = defineTool({
|
|
1514
|
+
name: "edit_range",
|
|
1515
|
+
description: "Replace a precise line range in a file.",
|
|
1516
|
+
parameters: SCHEMAS.editRange,
|
|
1517
|
+
async execute(args, ctx) {
|
|
1518
|
+
try {
|
|
1519
|
+
const resolved = await resolvePath(policy, args.path, { mustExist: true });
|
|
1520
|
+
const current = await readWholeFile(resolved.path, policy.maxWriteBytes);
|
|
1521
|
+
const versionError = checkExpectedVersion(current.version, args.expected_file_version);
|
|
1522
|
+
if (versionError !== null)
|
|
1523
|
+
return versionError;
|
|
1524
|
+
const lines = lineOffsets(current.text);
|
|
1525
|
+
if (args.end_line < args.start_line)
|
|
1526
|
+
return { ok: false, error: "end_line must be >= start_line" };
|
|
1527
|
+
if (args.start_line > lines.length)
|
|
1528
|
+
return { ok: false, error: "start_line is beyond end of file" };
|
|
1529
|
+
const replacement = args.new_text.replace(/\r\n/g, `
|
|
1530
|
+
`).split(`
|
|
1531
|
+
`);
|
|
1532
|
+
const nextLines = [
|
|
1533
|
+
...lines.slice(0, args.start_line - 1),
|
|
1534
|
+
...replacement,
|
|
1535
|
+
...lines.slice(args.end_line)
|
|
1536
|
+
];
|
|
1537
|
+
const next = nextLines.join(`
|
|
1538
|
+
`);
|
|
1539
|
+
const nextVersion = await atomicWrite(resolved.path, next);
|
|
1540
|
+
const validation = await runValidation(config, args.validate, [{ path: resolved.path, content: next }], dirname4(resolved.path), ctx);
|
|
1541
|
+
return {
|
|
1542
|
+
ok: true,
|
|
1543
|
+
content: {
|
|
1544
|
+
path: displayPath(resolved.root, resolved.path),
|
|
1545
|
+
previousFileVersion: current.version,
|
|
1546
|
+
fileVersion: nextVersion,
|
|
1547
|
+
changed: true,
|
|
1548
|
+
validation
|
|
1549
|
+
}
|
|
1550
|
+
};
|
|
1551
|
+
} catch (err) {
|
|
1552
|
+
return fail(err);
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
});
|
|
1556
|
+
const applyPatch = defineTool({
|
|
1557
|
+
name: "apply_patch",
|
|
1558
|
+
description: "Apply a unified diff patch across one or more files with dry-run and validation support.",
|
|
1559
|
+
parameters: SCHEMAS.applyPatch,
|
|
1560
|
+
async execute(args, ctx) {
|
|
1561
|
+
try {
|
|
1562
|
+
const root = await resolveRoot(policy, args.root);
|
|
1563
|
+
const parsed = parsePatch(args.patch);
|
|
1564
|
+
if (parsed.length === 0)
|
|
1565
|
+
return { ok: false, error: "patch did not contain any file changes" };
|
|
1566
|
+
const dryRun = args.dry_run ?? false;
|
|
1567
|
+
const files = [];
|
|
1568
|
+
const changedFiles = [];
|
|
1569
|
+
for (const patch of parsed) {
|
|
1570
|
+
const relPath = stripPatchPath(patch.newFileName) ?? stripPatchPath(patch.oldFileName);
|
|
1571
|
+
if (relPath === null)
|
|
1572
|
+
return { ok: false, error: "patch file is missing a path" };
|
|
1573
|
+
const resolved = await resolvePath(policy, relPath, { base: root.path, forCreate: true });
|
|
1574
|
+
const current = existsSync3(resolved.path) ? await readWholeFile(resolved.path, policy.maxWriteBytes) : { text: "", version: fileVersion("") };
|
|
1575
|
+
const expected = args.expected_versions?.[relPath] ?? args.expected_versions?.[displayPath(root.path, resolved.path)];
|
|
1576
|
+
const versionError = checkExpectedVersion(current.version, expected);
|
|
1577
|
+
if (versionError !== null)
|
|
1578
|
+
return versionError;
|
|
1579
|
+
const next = applyUnifiedPatch(current.text, patch, { autoConvertLineEndings: true });
|
|
1580
|
+
if (next === false)
|
|
1581
|
+
return { ok: false, error: `patch failed to apply to ${relPath}` };
|
|
1582
|
+
const deleting = stripPatchPath(patch.newFileName) === null;
|
|
1583
|
+
if (!dryRun) {
|
|
1584
|
+
if (deleting)
|
|
1585
|
+
await removeFile(resolved.path);
|
|
1586
|
+
else
|
|
1587
|
+
await atomicWrite(resolved.path, next);
|
|
1588
|
+
}
|
|
1589
|
+
if (!deleting)
|
|
1590
|
+
changedFiles.push({ path: resolved.path, content: next });
|
|
1591
|
+
files.push({
|
|
1592
|
+
path: relPath,
|
|
1593
|
+
changed: true,
|
|
1594
|
+
deleted: deleting,
|
|
1595
|
+
previousFileVersion: current.version,
|
|
1596
|
+
fileVersion: deleting ? undefined : fileVersion(next)
|
|
1597
|
+
});
|
|
1598
|
+
}
|
|
1599
|
+
const validation = dryRun ? undefined : await runValidation(config, args.validate, changedFiles, root.path, ctx);
|
|
1600
|
+
return { ok: true, content: { root: root.path, dryRun, files, validation } };
|
|
1601
|
+
} catch (err) {
|
|
1602
|
+
return fail(err);
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
});
|
|
1606
|
+
const writeFileTool = defineTool({
|
|
1607
|
+
name: "write_file",
|
|
1608
|
+
description: "Create, append, or overwrite a file. Prefer patch/range/replace edits for existing files.",
|
|
1609
|
+
parameters: SCHEMAS.writeFile,
|
|
1610
|
+
async execute(args) {
|
|
1611
|
+
try {
|
|
1612
|
+
if (Buffer.byteLength(args.content, "utf8") > policy.maxWriteBytes) {
|
|
1613
|
+
return { ok: false, error: "content exceeds maxWriteBytes" };
|
|
1614
|
+
}
|
|
1615
|
+
const resolved = await resolvePath(policy, args.path, { forCreate: true });
|
|
1616
|
+
const mode = args.mode ?? "create_only";
|
|
1617
|
+
const exists = existsSync3(resolved.path);
|
|
1618
|
+
if (mode === "create_only" && exists)
|
|
1619
|
+
return { ok: false, error: "file already exists" };
|
|
1620
|
+
if ((args.create_dirs ?? true) === false && !existsSync3(dirname4(resolved.path))) {
|
|
1621
|
+
return { ok: false, error: "parent directory does not exist" };
|
|
1622
|
+
}
|
|
1623
|
+
let previousFileVersion;
|
|
1624
|
+
let next = args.content;
|
|
1625
|
+
if (exists) {
|
|
1626
|
+
const current = await readWholeFile(resolved.path, policy.maxWriteBytes);
|
|
1627
|
+
previousFileVersion = current.version;
|
|
1628
|
+
const versionError = checkExpectedVersion(current.version, args.expected_file_version);
|
|
1629
|
+
if (versionError !== null)
|
|
1630
|
+
return versionError;
|
|
1631
|
+
if (mode === "append")
|
|
1632
|
+
next = current.text + args.content;
|
|
1633
|
+
}
|
|
1634
|
+
if (Buffer.byteLength(next, "utf8") > policy.maxWriteBytes) {
|
|
1635
|
+
return { ok: false, error: "resulting file exceeds maxWriteBytes" };
|
|
1636
|
+
}
|
|
1637
|
+
if ((args.create_dirs ?? true) === true)
|
|
1638
|
+
await mkdir2(dirname4(resolved.path), { recursive: true });
|
|
1639
|
+
const nextVersion = await atomicWrite(resolved.path, next);
|
|
1640
|
+
return {
|
|
1641
|
+
ok: true,
|
|
1642
|
+
content: {
|
|
1643
|
+
path: displayPath(resolved.root, resolved.path),
|
|
1644
|
+
previousFileVersion,
|
|
1645
|
+
fileVersion: nextVersion,
|
|
1646
|
+
mode,
|
|
1647
|
+
changed: true
|
|
1648
|
+
}
|
|
1649
|
+
};
|
|
1650
|
+
} catch (err) {
|
|
1651
|
+
return fail(err);
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
});
|
|
1655
|
+
const diffStatus2 = defineTool({
|
|
1656
|
+
name: "diff_status",
|
|
1657
|
+
description: "Return compact Git-style status and diffs for changed files.",
|
|
1658
|
+
parameters: SCHEMAS.diffStatus,
|
|
1659
|
+
async execute(args) {
|
|
1660
|
+
try {
|
|
1661
|
+
const paths = [];
|
|
1662
|
+
for (const path of args.paths ?? []) {
|
|
1663
|
+
const resolved = await resolvePath(policy, path, { mustExist: false });
|
|
1664
|
+
paths.push(displayPath(policy.defaultRoot, resolved.path));
|
|
1665
|
+
}
|
|
1666
|
+
const result = await diffStatus(policy.defaultRoot, {
|
|
1667
|
+
paths,
|
|
1668
|
+
includeDiff: args.include_diff ?? true,
|
|
1669
|
+
maxDiffChars: bounded(args.max_diff_chars, 20000, 100, 1e6),
|
|
1670
|
+
contextLines: bounded(args.context_lines, 3, 0, 20)
|
|
1671
|
+
});
|
|
1672
|
+
return { ok: true, content: { root: policy.defaultRoot, ...result } };
|
|
1673
|
+
} catch (err) {
|
|
1674
|
+
return fail(err);
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
});
|
|
1678
|
+
return {
|
|
1679
|
+
repoMap,
|
|
1680
|
+
findFiles,
|
|
1681
|
+
searchText: searchText2,
|
|
1682
|
+
searchSemantic: searchSemantic2,
|
|
1683
|
+
symbols,
|
|
1684
|
+
read,
|
|
1685
|
+
openWindow,
|
|
1686
|
+
editReplace,
|
|
1687
|
+
editRange,
|
|
1688
|
+
applyPatch,
|
|
1689
|
+
writeFile: writeFileTool,
|
|
1690
|
+
diffStatus: diffStatus2
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1693
|
+
export {
|
|
1694
|
+
filesystemTools
|
|
1695
|
+
};
|