minimal-agent 0.10.0 → 0.12.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 +11 -3
- package/package.json +52 -5
- package/plugins/computer_use/.claude-plugin/plugin.json +6 -0
- package/plugins/computer_use/README.md +94 -0
- package/plugins/computer_use/app/_template.md +142 -0
- package/plugins/computer_use/app/chrome.md +165 -0
- package/plugins/computer_use/app/finder.md +76 -0
- package/plugins/computer_use/app/macos-system-settings.md +81 -0
- package/plugins/computer_use/app/microsoft-excel.md +80 -0
- package/plugins/computer_use/app/microsoft-powerpoint.md +80 -0
- package/plugins/computer_use/app/microsoft-word.md +79 -0
- package/plugins/computer_use/app/wechat.md +413 -0
- package/plugins/computer_use/app/weread.md +319 -0
- package/plugins/computer_use/commands/computeruse.md +25 -0
- package/plugins/computer_use/commands/cu.md +9 -0
- package/plugins/computer_use/plugin.js +26 -0
- package/plugins/computer_use/plugin.ts +30 -0
- package/plugins/computer_use/src/capture.js +90 -0
- package/plugins/computer_use/src/capture.ts +104 -0
- package/plugins/computer_use/src/commands.js +91 -0
- package/plugins/computer_use/src/commands.ts +103 -0
- package/plugins/computer_use/src/daemon.js +134 -0
- package/plugins/computer_use/src/daemon.ts +161 -0
- package/plugins/computer_use/src/manuals.js +135 -0
- package/plugins/computer_use/src/manuals.ts +159 -0
- package/plugins/computer_use/src/verify.js +168 -0
- package/plugins/computer_use/src/verify.ts +219 -0
- package/plugins/computer_use/test/commands.test.ts +73 -0
- package/plugins/cron-runner/plugin.ts +38 -0
- package/plugins/cron-runner/src/commands.ts +373 -0
- package/plugins/cron-runner/src/cronTime.ts +155 -0
- package/plugins/cron-runner/src/lock.ts +79 -0
- package/plugins/cron-runner/src/runs.ts +99 -0
- package/plugins/cron-runner/src/scheduleEngine.ts +76 -0
- package/plugins/cron-runner/src/scheduler.ts +376 -0
- package/plugins/cron-runner/src/schema.ts +139 -0
- package/plugins/cron-runner/src/service.ts +193 -0
- package/plugins/cron-runner/src/store.ts +333 -0
- package/plugins/cron-runner/src/types.ts +141 -0
- package/plugins/cron-runner/test/cronTime.test.ts +41 -0
- package/plugins/cron-runner/test/plugin-integration.test.ts +47 -0
- package/plugins/cron-runner/test/scheduleEngine.test.ts +41 -0
- package/plugins/cron-runner/test/service.test.ts +76 -0
- package/plugins/cron-runner/test/store-and-scheduler.test.ts +501 -0
- package/plugins/workflow-runner/plugin.js +56 -2
- package/plugins/workflow-runner/plugin.ts +113 -0
- package/plugins/workflow-runner/src/agentCommon.js +43 -4
- package/plugins/workflow-runner/src/agentCommon.ts +435 -0
- package/plugins/workflow-runner/src/controlFlow/branch.js +40 -0
- package/plugins/workflow-runner/src/controlFlow/branch.ts +46 -0
- package/plugins/workflow-runner/src/controlFlow/loop.js +211 -0
- package/plugins/workflow-runner/src/controlFlow/loop.ts +248 -0
- package/plugins/workflow-runner/src/controlFlow/pause.js +129 -0
- package/plugins/workflow-runner/src/controlFlow/pause.ts +141 -0
- package/plugins/workflow-runner/src/controlFlow/signal.js +21 -0
- package/plugins/workflow-runner/src/controlFlow/signal.ts +29 -0
- package/plugins/workflow-runner/src/controlFlow/types.js +18 -0
- package/plugins/workflow-runner/src/controlFlow/types.ts +63 -0
- package/plugins/workflow-runner/src/dslManifest.ts +127 -0
- package/plugins/workflow-runner/src/expressions.ts +622 -0
- package/plugins/workflow-runner/src/index.js +13 -4
- package/plugins/workflow-runner/src/index.ts +338 -0
- package/plugins/workflow-runner/src/jsonPayload.ts +84 -0
- package/plugins/workflow-runner/src/loader.js +23 -0
- package/plugins/workflow-runner/src/loader.ts +721 -0
- package/plugins/workflow-runner/src/multimodal.js +2 -1
- package/plugins/workflow-runner/src/multimodal.ts +64 -0
- package/plugins/workflow-runner/src/recovery.ts +149 -0
- package/plugins/workflow-runner/src/runner.js +75 -659
- package/plugins/workflow-runner/src/runner.ts +604 -0
- package/plugins/workflow-runner/src/stepExecutors/agent.ts +234 -0
- package/plugins/workflow-runner/src/stepExecutors/assert.ts +50 -0
- package/plugins/workflow-runner/src/stepExecutors/echo.js +31 -0
- package/plugins/workflow-runner/src/stepExecutors/echo.ts +38 -0
- package/plugins/workflow-runner/src/stepExecutors/llm.ts +133 -0
- package/plugins/workflow-runner/src/stepExecutors/set.js +49 -0
- package/plugins/workflow-runner/src/stepExecutors/set.ts +56 -0
- package/plugins/workflow-runner/src/stepExecutors/skill.js +5 -6
- package/plugins/workflow-runner/src/stepExecutors/skill.ts +140 -0
- package/plugins/workflow-runner/src/stepExecutors/tool.js +80 -8
- package/plugins/workflow-runner/src/stepExecutors/tool.ts +256 -0
- package/plugins/workflow-runner/src/stepExecutors/verify.ts +68 -0
- package/plugins/workflow-runner/src/stepPipeline.js +277 -0
- package/plugins/workflow-runner/src/stepPipeline.ts +366 -0
- package/plugins/workflow-runner/src/taskIdentity.ts +405 -0
- package/plugins/workflow-runner/src/toolRuntime.js +48 -0
- package/plugins/workflow-runner/src/toolRuntime.ts +64 -0
- package/plugins/workflow-runner/src/types.js +37 -1
- package/plugins/workflow-runner/src/types.ts +602 -0
- package/plugins/workflow-runner/src/workflowState.ts +733 -0
- package/plugins/workflow-runner/test/agent.test.ts +587 -0
- package/plugins/workflow-runner/test/agentLlmError.test.ts +373 -0
- package/plugins/workflow-runner/test/bashBlacklist.sweep.test.ts +65 -0
- package/plugins/workflow-runner/test/captureMisbind.test.ts +99 -0
- package/plugins/workflow-runner/test/cli.e2e.test.ts +147 -0
- package/plugins/workflow-runner/test/context-files.test.ts +320 -0
- package/plugins/workflow-runner/test/e2e.test.ts +370 -0
- package/plugins/workflow-runner/test/engineRobustness.test.ts +226 -0
- package/plugins/workflow-runner/test/expressions.test.ts +397 -0
- package/plugins/workflow-runner/test/foundation.test.ts +126 -0
- package/plugins/workflow-runner/test/graceful.test.ts +139 -0
- package/plugins/workflow-runner/test/greedyInputs.load.test.ts +47 -0
- package/plugins/workflow-runner/test/hostCapability.test.ts +108 -0
- package/plugins/workflow-runner/test/illustrate.load.test.ts +183 -0
- package/plugins/workflow-runner/test/jsonPayload.test.ts +83 -0
- package/plugins/workflow-runner/test/loader.test.ts +617 -0
- package/plugins/workflow-runner/test/maxTurnsRetryDelay.test.ts +33 -0
- package/plugins/workflow-runner/test/multimodal.test.ts +342 -0
- package/plugins/workflow-runner/test/output-schema.test.ts +213 -0
- package/plugins/workflow-runner/test/parseWorkflowArgs.test.ts +87 -0
- package/plugins/workflow-runner/test/pluginRunner.isolation.test.ts +269 -0
- package/plugins/workflow-runner/test/ppt.load.test.ts +1220 -0
- package/plugins/workflow-runner/test/ralphLoop.e2e.test.ts +220 -0
- package/plugins/workflow-runner/test/recovery.test.ts +265 -0
- package/plugins/workflow-runner/test/runner.test.ts +911 -0
- package/plugins/workflow-runner/test/runnerPreflight.test.ts +379 -0
- package/plugins/workflow-runner/test/skillTurnDone.test.ts +71 -0
- package/plugins/workflow-runner/test/stateLifecycle.test.ts +150 -0
- package/plugins/workflow-runner/test/stepPipeline.test.ts +114 -0
- package/plugins/workflow-runner/test/taskArbitration.test.ts +338 -0
- package/plugins/workflow-runner/test/taskIdentity.test.ts +237 -0
- package/plugins/workflow-runner/test/taskLifecycle.test.ts +270 -0
- package/plugins/workflow-runner/test/toolArtifactOverwrite.test.ts +221 -0
- package/plugins/workflow-runner/test/toolCapabilityBoundary.test.ts +29 -0
- package/plugins/workflow-runner/test/toolRuntimeIntegration.test.ts +168 -0
- package/plugins/workflow-runner/test/webppt.load.test.ts +250 -0
- package/plugins/workflow-runner/test/word.load.test.ts +208 -0
- package/plugins/workflow-runner/test/workflows.smoke.test.ts +76 -0
- package/plugins/workflow-runner/test/yamlGuardConditions.test.ts +207 -0
- package/skills/browser-automation/SKILL.md +9 -19
- package/skills/web-crawler/SKILL.md +9 -11
- package/src/askBridge.ts +249 -0
- package/src/bootstrap/cwdArg.js +10 -3
- package/src/bootstrap/cwdArg.ts +31 -0
- package/src/bootstrap/workingDir.ts +48 -0
- package/src/cli/args.js +70 -7
- package/src/cli/args.ts +146 -0
- package/src/cli/computerUse.js +185 -0
- package/src/cli/computerUse.ts +221 -0
- package/src/cli/configMenu.js +274 -202
- package/src/cli/configMenu.tsx +521 -0
- package/src/cli/memoryAccess.js +45 -0
- package/src/cli/memoryAccess.ts +53 -0
- package/src/cli/print.js +119 -5
- package/src/cli/print.ts +597 -0
- package/src/cli/quickModelSetup.js +60 -0
- package/src/cli/quickModelSetup.tsx +96 -0
- package/src/cli/streamJson.js +80 -4
- package/src/cli/streamJson.ts +433 -0
- package/src/cli/wizards/advisorWizard.js +1 -0
- package/src/cli/wizards/advisorWizard.tsx +195 -0
- package/src/cli/wizards/agentRuntimeWizard.js +84 -0
- package/src/cli/wizards/agentRuntimeWizard.tsx +134 -0
- package/src/cli/wizards/browserWizard.js +55 -0
- package/src/cli/wizards/browserWizard.tsx +77 -0
- package/src/cli/wizards/contextAdvisorWizard.js +84 -0
- package/src/cli/wizards/contextAdvisorWizard.tsx +124 -0
- package/src/cli/wizards/fieldForm.js +7 -4
- package/src/cli/wizards/fieldForm.tsx +250 -0
- package/src/cli/wizards/imageWizard.js +4 -5
- package/src/cli/wizards/imageWizard.tsx +96 -0
- package/src/cli/wizards/openRouterWizard.tsx +48 -0
- package/src/cli/wizards/providerPresets.js +1 -8
- package/src/cli/wizards/providerPresets.ts +106 -0
- package/src/cli/wizards/providerWizard.js +152 -205
- package/src/cli/wizards/providerWizard.tsx +266 -0
- package/src/cli/wizards/searchWizard.tsx +47 -0
- package/src/cli/wizards/ttsWizard.js +4 -3
- package/src/cli/wizards/ttsWizard.tsx +61 -0
- package/src/config/configDefaults.js +25 -0
- package/src/config/configDefaults.ts +33 -0
- package/src/config/configDiagnostics.js +117 -0
- package/src/config/configDiagnostics.ts +166 -0
- package/src/config/configFile.js +52 -32
- package/src/config/configFile.ts +633 -0
- package/src/config/configManagement.js +126 -0
- package/src/config/configManagement.ts +159 -0
- package/src/config/configService.js +66 -11
- package/src/config/configService.ts +554 -0
- package/src/config/numericLimits.js +61 -0
- package/src/config/numericLimits.ts +80 -0
- package/src/config.ts +109 -0
- package/src/context/agentHome.ts +11 -0
- package/src/context/archive.ts +144 -0
- package/src/context/budgetPlan.js +54 -0
- package/src/context/budgetPlan.ts +88 -0
- package/src/context/compact.js +179 -106
- package/src/context/compact.ts +584 -0
- package/src/context/contextBundle.js +119 -0
- package/src/context/contextBundle.ts +176 -0
- package/src/context/contextLock.ts +84 -0
- package/src/context/microCompactLite.ts +218 -0
- package/src/context/persistContext.ts +177 -0
- package/src/context/reactiveCompact.js +19 -4
- package/src/context/reactiveCompact.ts +240 -0
- package/src/context/recentDirs.ts +42 -0
- package/src/context/sessionPath.ts +206 -0
- package/src/context/sessionRegistry.js +8 -2
- package/src/context/sessionRegistry.ts +414 -0
- package/src/context/snipCompact.ts +163 -0
- package/src/context/tokenCounter.ts +106 -0
- package/src/context/workspaceIndex.js +2 -2
- package/src/context/workspaceIndex.ts +751 -0
- package/src/entry.compiled.tsx +20 -0
- package/src/harness/claimValidation.js +9 -0
- package/src/harness/claimValidation.ts +22 -0
- package/src/harness/evidenceLedger.js +92 -0
- package/src/harness/evidenceLedger.ts +96 -0
- package/src/harness/stallGuard.js +60 -0
- package/src/harness/stallGuard.ts +81 -0
- package/src/humanIntervention.js +129 -0
- package/src/humanIntervention.ts +216 -0
- package/src/infra/verification.ts +295 -0
- package/src/irreversible/rules.js +216 -0
- package/src/irreversible/rules.ts +261 -0
- package/src/llm/client.js +40 -3
- package/src/llm/client.ts +621 -0
- package/src/llm/promptCache.js +27 -0
- package/src/llm/promptCache.ts +41 -0
- package/src/llm/providers/anthropicFormat.js +41 -5
- package/src/llm/providers/anthropicFormat.ts +290 -0
- package/src/llm/providers/openaiFormat.js +186 -8
- package/src/llm/providers/openaiFormat.ts +428 -0
- package/src/loop.js +614 -63
- package/src/loop.ts +1454 -0
- package/src/main.js +67 -10
- package/src/main.tsx +344 -0
- package/src/memory/codec.js +204 -0
- package/src/memory/codec.ts +249 -0
- package/src/memory/constants.js +34 -0
- package/src/memory/constants.ts +50 -0
- package/src/memory/episodicMemory.js +684 -0
- package/src/memory/episodicMemory.ts +815 -0
- package/src/memory/fileStore.js +148 -0
- package/src/memory/fileStore.ts +184 -0
- package/src/memory/memoryService.js +570 -0
- package/src/memory/memoryService.ts +859 -0
- package/src/memory/migration.js +128 -0
- package/src/memory/migration.ts +160 -0
- package/src/memory/repository.js +789 -0
- package/src/memory/repository.ts +893 -0
- package/src/memory/retrieval.js +108 -0
- package/src/memory/retrieval.ts +145 -0
- package/src/memory/searchIndex.js +107 -0
- package/src/memory/searchIndex.ts +153 -0
- package/src/memory/topicView.js +82 -0
- package/src/memory/topicView.ts +95 -0
- package/src/memory/turnJournal.js +95 -0
- package/src/memory/turnJournal.ts +120 -0
- package/src/memory/types.js +2 -0
- package/src/memory/types.ts +186 -0
- package/src/messageQueue.ts +123 -0
- package/src/multimodal.js +128 -46
- package/src/multimodal.ts +349 -0
- package/src/plugin-sdk.js +21 -1
- package/src/plugin-sdk.ts +134 -0
- package/src/plugins/builtinPluginIds.js +47 -0
- package/src/plugins/builtinPluginIds.ts +51 -0
- package/src/plugins/builtinRegistry.ts +43 -0
- package/src/plugins/commandRouter.js +40 -26
- package/src/plugins/commandRouter.ts +261 -0
- package/src/plugins/hookEngine.ts +341 -0
- package/src/plugins/pluginApi.ts +133 -0
- package/src/plugins/pluginLifecycle.ts +34 -0
- package/src/plugins/pluginLoader.ts +147 -0
- package/src/plugins/pluginManifest.js +127 -0
- package/src/plugins/pluginManifest.ts +152 -0
- package/src/plugins/pluginRunner.js +47 -2
- package/src/plugins/pluginRunner.ts +207 -0
- package/src/plugins/transcript.ts +208 -0
- package/src/prompts/lint.js +169 -0
- package/src/prompts/lint.ts +220 -0
- package/src/prompts/pluginList.js +32 -16
- package/src/prompts/pluginList.ts +86 -0
- package/src/prompts/policyRegistry.js +318 -0
- package/src/prompts/policyRegistry.ts +358 -0
- package/src/prompts/projectInstructions.js +24 -0
- package/src/prompts/projectInstructions.ts +81 -0
- package/src/prompts/promptAssembly.js +311 -202
- package/src/prompts/promptAssembly.ts +510 -0
- package/src/prompts/skillList.js +106 -45
- package/src/prompts/skillList.ts +198 -0
- package/src/prompts/system.js +31 -6
- package/src/prompts/system.ts +81 -0
- package/src/runtime/agentRuntimeContext.js +53 -0
- package/src/runtime/agentRuntimeContext.ts +112 -0
- package/src/session/runAgentTurn.js +71 -12
- package/src/session/runAgentTurn.ts +181 -0
- package/src/session/runTurn.js +29 -0
- package/src/session/runTurn.ts +160 -0
- package/src/session/runtimeStore.js +149 -0
- package/src/session/runtimeStore.ts +197 -0
- package/src/session/sessionState.js +79 -3
- package/src/session/sessionState.ts +134 -0
- package/src/toolGate.ts +50 -0
- package/src/tools/advisor/advisor.js +76 -15
- package/src/tools/advisor/advisor.ts +512 -0
- package/src/tools/agent/agent.js +245 -29
- package/src/tools/agent/agent.ts +837 -0
- package/src/tools/agent/agentTaskService.js +60 -0
- package/src/tools/agent/agentTaskService.ts +113 -0
- package/src/tools/agent/agentTypes.js +20 -8
- package/src/tools/agent/agentTypes.ts +94 -0
- package/src/tools/agent/coordinator.ts +111 -0
- package/src/tools/agent/worktree.ts +296 -0
- package/src/tools/ask/ask.js +17 -0
- package/src/tools/ask/ask.ts +122 -0
- package/src/tools/bash/attachImage.js +223 -0
- package/src/tools/bash/attachImage.ts +281 -0
- package/src/tools/bash/bash.js +132 -14
- package/src/tools/bash/bash.ts +642 -0
- package/src/tools/bash/semantics.ts +103 -0
- package/src/tools/bash/warnings.js +17 -11
- package/src/tools/bash/warnings.ts +117 -0
- package/src/tools/capabilities.js +42 -0
- package/src/tools/capabilities.ts +69 -0
- package/src/tools/computeruse/accessibilityPermission.js +261 -0
- package/src/tools/computeruse/accessibilityPermission.ts +294 -0
- package/src/tools/computeruse/axSearch.js +96 -0
- package/src/tools/computeruse/axSearch.ts +120 -0
- package/src/tools/computeruse/axSnap.js +272 -0
- package/src/tools/computeruse/axSnap.ts +349 -0
- package/src/tools/computeruse/capabilityLabels.js +95 -0
- package/src/tools/computeruse/capabilityLabels.ts +115 -0
- package/src/tools/computeruse/computerUseService.js +2235 -0
- package/src/tools/computeruse/computerUseService.ts +2686 -0
- package/src/tools/computeruse/computeruse.js +2742 -0
- package/src/tools/computeruse/computeruse.ts +3265 -0
- package/src/tools/computeruse/diagnosticTrace.js +154 -0
- package/src/tools/computeruse/diagnosticTrace.ts +195 -0
- package/src/tools/computeruse/diagnostics.js +369 -0
- package/src/tools/computeruse/diagnostics.ts +453 -0
- package/src/tools/computeruse/grants.js +53 -0
- package/src/tools/computeruse/grants.ts +70 -0
- package/src/tools/computeruse/humanBoundary.js +159 -0
- package/src/tools/computeruse/humanBoundary.ts +199 -0
- package/src/tools/computeruse/intervention.js +77 -0
- package/src/tools/computeruse/intervention.ts +110 -0
- package/src/tools/computeruse/manualSchema.js +709 -0
- package/src/tools/computeruse/manualSchema.ts +916 -0
- package/src/tools/computeruse/observationImages.js +705 -0
- package/src/tools/computeruse/observationImages.ts +889 -0
- package/src/tools/computeruse/protocol.js +60 -0
- package/src/tools/computeruse/protocol.ts +515 -0
- package/src/tools/computeruse/telemetryLog.js +57 -0
- package/src/tools/computeruse/telemetryLog.ts +67 -0
- package/src/tools/computeruse/vision/appGuide.js +325 -0
- package/src/tools/computeruse/vision/appGuide.ts +482 -0
- package/src/tools/computeruse/vision/focus.js +291 -0
- package/src/tools/computeruse/vision/focus.ts +382 -0
- package/src/tools/computeruse/vision/frames.js +248 -0
- package/src/tools/computeruse/vision/frames.ts +329 -0
- package/src/tools/computeruse/vision/geometry.js +131 -0
- package/src/tools/computeruse/vision/geometry.ts +163 -0
- package/src/tools/computeruse/vision/imaging.js +201 -0
- package/src/tools/computeruse/vision/imaging.ts +281 -0
- package/src/tools/computeruse/vision/png.js +274 -0
- package/src/tools/computeruse/vision/png.ts +317 -0
- package/src/tools/computeruse/vision/raster.js +320 -0
- package/src/tools/computeruse/vision/raster.ts +408 -0
- package/src/tools/computeruse/vision/settle.js +131 -0
- package/src/tools/computeruse/vision/settle.ts +220 -0
- package/src/tools/computeruse/vision/telemetry.js +196 -0
- package/src/tools/computeruse/vision/telemetry.ts +241 -0
- package/src/tools/computeruse/visionOcr.js +115 -0
- package/src/tools/computeruse/visionOcr.ts +128 -0
- package/src/tools/computeruse/waitFor.js +237 -0
- package/src/tools/computeruse/waitFor.ts +291 -0
- package/src/tools/download/download.js +10 -6
- package/src/tools/download/download.ts +187 -0
- package/src/tools/edit/edit.js +30 -18
- package/src/tools/edit/edit.ts +349 -0
- package/src/tools/edit/multi-edit.js +25 -14
- package/src/tools/edit/multi-edit.ts +223 -0
- package/src/tools/effectiveToolSet.js +73 -0
- package/src/tools/effectiveToolSet.ts +91 -0
- package/src/tools/genimage/genimage.ts +257 -0
- package/src/tools/genspeech/genspeech.ts +83 -0
- package/src/tools/glob/glob.js +176 -29
- package/src/tools/glob/glob.ts +283 -0
- package/src/tools/grep/grep.js +61 -19
- package/src/tools/grep/grep.ts +239 -0
- package/src/tools/grep/rgPath.ts +206 -0
- package/src/tools/index.js +99 -5
- package/src/tools/index.ts +293 -0
- package/src/tools/lsp/config.js +113 -0
- package/src/tools/lsp/config.ts +113 -0
- package/src/tools/lsp/lsp.js +256 -0
- package/src/tools/lsp/lsp.ts +274 -0
- package/src/tools/lsp/service.js +303 -0
- package/src/tools/lsp/service.ts +296 -0
- package/src/tools/memory/memory.js +520 -0
- package/src/tools/memory/memory.ts +625 -0
- package/src/tools/memory/policy.js +53 -0
- package/src/tools/memory/policy.ts +72 -0
- package/src/tools/notebook/notebookEdit.js +114 -0
- package/src/tools/notebook/notebookEdit.ts +126 -0
- package/src/tools/powershell/powershell.js +210 -0
- package/src/tools/powershell/powershell.ts +239 -0
- package/src/tools/presentresult/presentresult.js +145 -0
- package/src/tools/presentresult/presentresult.ts +156 -0
- package/src/tools/read/read.js +371 -163
- package/src/tools/read/read.ts +558 -0
- package/src/tools/shared/diffSummary.js +44 -0
- package/src/tools/shared/diffSummary.ts +63 -0
- package/src/tools/shared/fileActivity.js +51 -0
- package/src/tools/shared/fileActivity.ts +66 -0
- package/src/tools/shared/fileMutationBus.js +13 -0
- package/src/tools/shared/fileMutationBus.ts +14 -0
- package/src/tools/shared/fileState.js +73 -65
- package/src/tools/shared/fileState.ts +145 -0
- package/src/tools/shared/fileUtils.js +81 -4
- package/src/tools/shared/fileUtils.ts +504 -0
- package/src/tools/shared/killTree.ts +50 -0
- package/src/tools/shared/permanentDeleteGuard.js +26 -0
- package/src/tools/shared/permanentDeleteGuard.ts +34 -0
- package/src/tools/shared/schemas.ts +20 -0
- package/src/tools/shared/toolOutputStore.js +97 -0
- package/src/tools/shared/toolOutputStore.ts +110 -0
- package/src/tools/skill/skill.js +86 -0
- package/src/tools/skill/skill.ts +94 -0
- package/src/tools/snip/snip.js +67 -0
- package/src/tools/snip/snip.ts +80 -0
- package/src/tools/tasks/backgroundTaskService.js +275 -0
- package/src/tools/tasks/backgroundTaskService.ts +369 -0
- package/src/tools/tasks/processEvidence.js +24 -0
- package/src/tools/tasks/processEvidence.ts +34 -0
- package/src/tools/tasks/taskManagement.js +163 -0
- package/src/tools/tasks/taskManagement.ts +180 -0
- package/src/tools/tasks/taskOutput.js +113 -0
- package/src/tools/tasks/taskOutput.ts +145 -0
- package/src/tools/tasks/taskStop.js +74 -0
- package/src/tools/tasks/taskStop.ts +99 -0
- package/src/tools/tasks/taskStore.js +46 -0
- package/src/tools/tasks/taskStore.ts +77 -0
- package/src/tools/terminal/terminalCapture.js +167 -0
- package/src/tools/terminal/terminalCapture.ts +179 -0
- package/src/tools/trash/trash.js +233 -0
- package/src/tools/trash/trash.ts +283 -0
- package/src/tools/types.js +1 -1
- package/src/tools/types.ts +24 -0
- package/src/tools/webbrowser/agentBrowserPath.ts +286 -0
- package/src/tools/webbrowser/browser.js +214 -11
- package/src/tools/webbrowser/browser.ts +293 -0
- package/src/tools/webbrowser/browserControl.js +153 -48
- package/src/tools/webbrowser/browserControl.ts +262 -0
- package/src/tools/webbrowser/browserDaemon.js +147 -39
- package/src/tools/webbrowser/browserDaemon.ts +270 -0
- package/src/tools/webbrowser/browserOutcome.js +273 -0
- package/src/tools/webbrowser/browserOutcome.ts +306 -0
- package/src/tools/webbrowser/browserRunRegistry.js +150 -0
- package/src/tools/webbrowser/browserRunRegistry.ts +179 -0
- package/src/tools/webbrowser/browserService.js +20 -6
- package/src/tools/webbrowser/browserService.ts +56 -0
- package/src/tools/webbrowser/browserStream.js +30 -1
- package/src/tools/webbrowser/browserStream.ts +216 -0
- package/src/tools/webbrowser/browserTabs.js +143 -0
- package/src/tools/webbrowser/browserTabs.ts +210 -0
- package/src/tools/webbrowser/webbrowser.js +831 -150
- package/src/tools/webbrowser/webbrowser.ts +1537 -0
- package/src/tools/webfetch/preapproved.ts +274 -0
- package/src/tools/webfetch/webfetch.js +24 -23
- package/src/tools/webfetch/webfetch.ts +383 -0
- package/src/tools/websearch/websearch.js +12 -11
- package/src/tools/websearch/websearch.ts +292 -0
- package/src/tools/worktree/sessionWorktree.js +128 -0
- package/src/tools/worktree/sessionWorktree.ts +158 -0
- package/src/tools/write/write.js +27 -44
- package/src/tools/write/write.ts +136 -0
- package/src/types.ts +960 -0
- package/src/ui/App.js +216 -12
- package/src/ui/App.tsx +879 -0
- package/src/ui/AskQuestion.tsx +300 -0
- package/src/ui/InputBox.js +77 -1
- package/src/ui/InputBox.tsx +567 -0
- package/src/ui/LiveArea.tsx +344 -0
- package/src/ui/MessageList.js +27 -6
- package/src/ui/MessageList.tsx +559 -0
- package/src/ui/ModelPicker.tsx +107 -0
- package/src/ui/QueuedMessagesBanner.tsx +51 -0
- package/src/ui/Root.js +6 -8
- package/src/ui/Root.tsx +100 -0
- package/src/ui/SessionPicker.tsx +169 -0
- package/src/ui/StatusLine.js +22 -1
- package/src/ui/StatusLine.tsx +145 -0
- package/src/ui/WorkspacePicker.tsx +84 -0
- package/src/ui/advisorCommand.js +54 -0
- package/src/ui/advisorCommand.ts +63 -0
- package/src/ui/computerCommand.js +19 -0
- package/src/ui/computerCommand.ts +35 -0
- package/src/ui/figures.ts +45 -0
- package/src/ui/hooks/askSlot.ts +112 -0
- package/src/ui/hooks/useChat.js +271 -7
- package/src/ui/hooks/useChat.ts +1578 -0
- package/src/ui/hooks/useInputHistory.ts +235 -0
- package/src/ui/hooks/usePasteHandler.ts +220 -0
- package/src/ui/hooks/useTerminalSize.ts +41 -0
- package/src/ui/hooks/useTextBuffer.ts +108 -0
- package/src/ui/hooks/useTokenUsage.ts +44 -0
- package/src/ui/liveViewport.ts +189 -0
- package/src/ui/markdown.ts +230 -0
- package/src/ui/textBuffer.ts +239 -0
- package/src/ui/theme.ts +35 -0
- package/src/utils/clipboardImage.js +358 -0
- package/src/utils/clipboardImage.ts +420 -0
- package/src/utils/debugLog.ts +50 -0
- package/src/utils/greenRoot.ts +35 -0
- package/src/utils/packageRoot.ts +40 -0
- package/src/utils/pathContains.js +28 -0
- package/src/utils/pathContains.ts +31 -0
- package/src/utils/resourcePaths.ts +71 -0
- package/src/utils/stripThink.ts +49 -0
- package/src/utils/zodToJson.ts +73 -0
- package/src/web/browserBridge.js +365 -66
- package/src/web/browserBridge.ts +672 -0
- package/src/web/protocol.ts +1804 -0
- package/vendor/computer-use/arm64-darwin/cu-helperd +0 -0
- package/workflows/ppt.yaml +13 -2
- package/workflows/wechat-reading-shots.yaml +315 -0
- package/workflows/word.yaml +11 -1
- package/skills/docx/scripts/office/helpers/__pycache__/__init__.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/helpers/__pycache__/merge_runs.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/helpers/__pycache__/simplify_redlines.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/__init__.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/base.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/docx.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/pptx.cpython-314.pyc +0 -0
- package/skills/docx/scripts/office/validators/__pycache__/redlining.cpython-314.pyc +0 -0
- package/skills/image-stats/scripts/__pycache__/image_stats.cpython-314.pyc +0 -0
- package/skills/image-stats/scripts/__pycache__/image_stats_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/assemble_material.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/assemble_material_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/chart_sanity_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/check_language_policy.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/check_ui_artifact.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/check_ui_artifact_pptx_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/deck_pages_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/deck_pages_check_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/deck_visual_style_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/deck_visual_style_check_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/design_budget_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/design_budget_check_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/design_repair_hints.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/design_repair_hints_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/design_style_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/design_style_check_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/draft_coverage_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/extract_source_assets.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/extract_source_assets_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/generated_image_review_collect.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/generated_image_review_collect_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/image_plan_lint.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/image_plan_lint_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/image_prompt_rewrite_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/layout_balance.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/layout_balance_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/normalize_design_json.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/normalize_design_json_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/plan_source_layout.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/plan_source_layout_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/resolve_source_hint.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/resolve_source_hint_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/resolve_source_input.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/resolve_source_input_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/scaffold_ui_artifact.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/source_usage_check.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/source_usage_check_test.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/text_readability_pixel.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/visual_quality_gate.cpython-314.pyc +0 -0
- package/skills/ui-design/scripts/__pycache__/visual_quality_gate_test.cpython-314.pyc +0 -0
- /package/skills/ai-image/scripts/{generate_test.mjs → generate.selftest.mjs} +0 -0
- /package/skills/ai-image/scripts/{generate_from_plan_test.mjs → generate_from_plan.selftest.mjs} +0 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================
|
|
3
|
+
* src/plugins/transcript.ts —— Claude Code 风格的 JSONL transcript 写入
|
|
4
|
+
* ------------------------------------------------------------
|
|
5
|
+
* 插件 hook(如 stop-hook.sh)通过 stdin 收到 `transcript_path` 后
|
|
6
|
+
* 会 grep / jq 这份 JSONL 文件来还原对话。schema 必须与 Anthropic
|
|
7
|
+
* Claude Code 真实 transcript 对齐:每行一个 JSON 对象,形如
|
|
8
|
+
* { type, uuid, session_id, timestamp, message: { role, content: [...parts] } }
|
|
9
|
+
* content 为 typed parts 数组:text / tool_use / tool_result。
|
|
10
|
+
*
|
|
11
|
+
* 设计要点:
|
|
12
|
+
* - OpenAI 风格的 assistant.tool_calls 在这里被拍平成 content 数组
|
|
13
|
+
* 里的 tool_use parts(Claude Code 的真实格式)
|
|
14
|
+
* - tool role 消息被包装成 type:'user' + tool_result part(上游惯例)
|
|
15
|
+
* - 同步 appendFileSync —— spawn 的 hook 子进程要立刻看到最新内容
|
|
16
|
+
* - 任何写入异常都被吞掉只 warn —— transcript 失败绝不能拖垮 agent
|
|
17
|
+
* - 零依赖(只用 node 内置)
|
|
18
|
+
*
|
|
19
|
+
* 存储位置:~/.minimal-agent/transcripts/<sessionId>.jsonl
|
|
20
|
+
* ============================================================
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { appendFileSync, mkdirSync } from 'node:fs';
|
|
24
|
+
import { join } from 'node:path';
|
|
25
|
+
import { randomUUID } from 'node:crypto';
|
|
26
|
+
|
|
27
|
+
import type { Message } from '../types.js';
|
|
28
|
+
import { agentStateDir } from '../context/agentHome.js';
|
|
29
|
+
|
|
30
|
+
export interface TranscriptHandle {
|
|
31
|
+
sessionId: string;
|
|
32
|
+
path: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 模块级状态:当前会话的 handle(lazy 初始化)
|
|
36
|
+
let current: TranscriptHandle | null = null;
|
|
37
|
+
|
|
38
|
+
function transcriptDir(): string {
|
|
39
|
+
return join(agentStateDir(), 'transcripts');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function generateSessionId(): string {
|
|
43
|
+
return `${Date.now().toString(36)}-${randomUUID().slice(0, 8)}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function ensureHandle(sessionId?: string): TranscriptHandle {
|
|
47
|
+
// 三分支:指定 sessionId 且与当前相同 → 复用;未指定且已有当前 → 复用;
|
|
48
|
+
// 其余(指定了不同 sessionId / 尚无当前)→ 新建句柄(即"切到新文件")。
|
|
49
|
+
if (sessionId && current && current.sessionId === sessionId) {
|
|
50
|
+
return current;
|
|
51
|
+
}
|
|
52
|
+
if (!sessionId && current) {
|
|
53
|
+
return current;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const sid = sessionId ?? generateSessionId();
|
|
57
|
+
const dir = transcriptDir();
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
mkdirSync(dir, { recursive: true });
|
|
61
|
+
} catch (err) {
|
|
62
|
+
console.warn(
|
|
63
|
+
`[transcript] mkdir failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const handle: TranscriptHandle = {
|
|
68
|
+
sessionId: sid,
|
|
69
|
+
path: join(dir, `${sid}.jsonl`),
|
|
70
|
+
};
|
|
71
|
+
current = handle;
|
|
72
|
+
return handle;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function writeLine(line: object): void {
|
|
76
|
+
try {
|
|
77
|
+
const handle = ensureHandle();
|
|
78
|
+
appendFileSync(handle.path, `${JSON.stringify(line)}\n`, 'utf8');
|
|
79
|
+
} catch (err) {
|
|
80
|
+
console.warn(
|
|
81
|
+
`[transcript] write failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function nowIso(): string {
|
|
87
|
+
return new Date().toISOString();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* 初始化 transcript:在 ~/.minimal-agent/transcripts/ 下打开 <sessionId>.jsonl。
|
|
92
|
+
* 如果未提供 sessionId,用时间戳 + 随机后缀生成。
|
|
93
|
+
* 多次调用同一 sessionId 会复用同一文件(append)。
|
|
94
|
+
*/
|
|
95
|
+
export function initTranscript(sessionId?: string): TranscriptHandle {
|
|
96
|
+
// 显式 init:即便 current 已存在也允许切换到指定 sessionId
|
|
97
|
+
if (sessionId) {
|
|
98
|
+
const dir = transcriptDir();
|
|
99
|
+
try {
|
|
100
|
+
mkdirSync(dir, { recursive: true });
|
|
101
|
+
} catch (err) {
|
|
102
|
+
console.warn(
|
|
103
|
+
`[transcript] mkdir failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
current = {
|
|
107
|
+
sessionId,
|
|
108
|
+
path: join(dir, `${sessionId}.jsonl`),
|
|
109
|
+
};
|
|
110
|
+
return current;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return ensureHandle();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** 获取当前会话的 transcript 路径。未 init 时 lazy 初始化。 */
|
|
117
|
+
export function getCurrentTranscriptPath(): string {
|
|
118
|
+
return ensureHandle().path;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** 追加一条 user 消息。 */
|
|
122
|
+
export function appendUserMessage(content: string): void {
|
|
123
|
+
const handle = ensureHandle();
|
|
124
|
+
writeLine({
|
|
125
|
+
type: 'user',
|
|
126
|
+
uuid: randomUUID(),
|
|
127
|
+
session_id: handle.sessionId,
|
|
128
|
+
timestamp: nowIso(),
|
|
129
|
+
message: {
|
|
130
|
+
role: 'user',
|
|
131
|
+
content: [{ type: 'text', text: content }],
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/** 追加一条 assistant 消息。tool_calls 转成 content 数组里的 tool_use parts。 */
|
|
137
|
+
export function appendAssistantMessage(
|
|
138
|
+
message: Extract<Message, { role: 'assistant' }>,
|
|
139
|
+
): void {
|
|
140
|
+
const handle = ensureHandle();
|
|
141
|
+
const parts: Array<Record<string, unknown>> = [];
|
|
142
|
+
|
|
143
|
+
// 文本部分:null / 空字符串都跳过
|
|
144
|
+
if (typeof message.content === 'string' && message.content.length > 0) {
|
|
145
|
+
parts.push({ type: 'text', text: message.content });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// tool_calls 拍平成 tool_use parts
|
|
149
|
+
if (Array.isArray(message.tool_calls)) {
|
|
150
|
+
for (const tc of message.tool_calls) {
|
|
151
|
+
let input: unknown;
|
|
152
|
+
try {
|
|
153
|
+
input = JSON.parse(tc.function.arguments);
|
|
154
|
+
} catch {
|
|
155
|
+
input = { _raw: tc.function.arguments };
|
|
156
|
+
}
|
|
157
|
+
parts.push({
|
|
158
|
+
type: 'tool_use',
|
|
159
|
+
id: tc.id,
|
|
160
|
+
name: tc.function.name,
|
|
161
|
+
input,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
writeLine({
|
|
167
|
+
type: 'assistant',
|
|
168
|
+
uuid: randomUUID(),
|
|
169
|
+
session_id: handle.sessionId,
|
|
170
|
+
timestamp: nowIso(),
|
|
171
|
+
message: {
|
|
172
|
+
role: 'assistant',
|
|
173
|
+
content: parts,
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** 追加一条 tool 执行结果(来自 tool role 消息)。 */
|
|
179
|
+
export function appendToolResult(
|
|
180
|
+
toolCallId: string,
|
|
181
|
+
_toolName: string,
|
|
182
|
+
ok: boolean,
|
|
183
|
+
content: string,
|
|
184
|
+
): void {
|
|
185
|
+
const handle = ensureHandle();
|
|
186
|
+
writeLine({
|
|
187
|
+
type: 'user',
|
|
188
|
+
uuid: randomUUID(),
|
|
189
|
+
session_id: handle.sessionId,
|
|
190
|
+
timestamp: nowIso(),
|
|
191
|
+
message: {
|
|
192
|
+
role: 'user',
|
|
193
|
+
content: [
|
|
194
|
+
{
|
|
195
|
+
type: 'tool_result',
|
|
196
|
+
tool_use_id: toolCallId,
|
|
197
|
+
content,
|
|
198
|
+
is_error: !ok,
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** 重置当前 handle(用于测试 / /new 清屏后开新 session)。 */
|
|
206
|
+
export function resetTranscript(): void {
|
|
207
|
+
current = null;
|
|
208
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================
|
|
3
|
+
* src/prompts/lint.ts —— 系统提示词的跨层重复检查(确定性,无 LLM)
|
|
4
|
+
* ------------------------------------------------------------
|
|
5
|
+
* **为什么需要它**:`promptAssembly.ts` 把提示词分成若干层(身份/纪律层、工具编排层、
|
|
6
|
+
* Skills、Plugins、项目指令)。层与层之间很容易把同一条规则说两遍——写的时候各看各的段落,
|
|
7
|
+
* 读起来都通顺,拼起来就是重复烧 token、而且两处措辞一旦漂移就会互相矛盾。
|
|
8
|
+
* 人工清理一次能干净,但下一次加规则又会退化,所以要把「不重复」变成**回归护栏**。
|
|
9
|
+
*
|
|
10
|
+
* **分层判据(去重时按它归位,不是凭感觉)**:
|
|
11
|
+
* 一条规则若在「该工具不存在时依然成立」→ 属身份层;否则属工具层。
|
|
12
|
+
* 例:「改文件前先读最新内容」没有 Edit 工具时无意义 → 工具层;
|
|
13
|
+
* 「只在无法推断时才提问」没有 AskUserQuestion 时仍成立(约束的是判断而非调用)→ 身份层。
|
|
14
|
+
*
|
|
15
|
+
* **能力边界(必须如实说)**:这是**词面**近重复检测,不是语义检测。
|
|
16
|
+
* 「不用正则盲改源码」与「源码编辑优先专用工具」表达同一件事但几乎不共享字面,
|
|
17
|
+
* 本检查器**抓不到**——那类只能靠人读。它保证的是:已经清理干净的地方不会因为
|
|
18
|
+
* 有人复制粘贴一句旧措辞而悄悄退化。
|
|
19
|
+
*
|
|
20
|
+
* **为什么按「子句」而不是整条规则比**:整条规则通常长且各带自己的补充说明,
|
|
21
|
+
* 即使核心重复,整条的 Jaccard 也只有 0.1 量级(实测),根本触发不了。
|
|
22
|
+
* 拆成子句后重复的那一句会高度重合。
|
|
23
|
+
*
|
|
24
|
+
* **为什么用 containment 而不是 Jaccard**:我们要问的是「这句话是不是在别处已经说过了」,
|
|
25
|
+
* 即**包含**关系;短句被长句涵盖时 Jaccard 会被长句的其余部分稀释,containment 不会。
|
|
26
|
+
* ============================================================
|
|
27
|
+
*/
|
|
28
|
+
/** 数据型段落:内容来自用户目录/项目文件,不是我们写的策略,不参与去重。 */
|
|
29
|
+
const DEFAULT_IGNORED_SECTIONS = ['Skills', 'Plugins', '项目指令'];
|
|
30
|
+
/** 子句短于这个 token 数就不参与比较——短句碰巧重合太常见,会淹没真信号。 */
|
|
31
|
+
const MIN_TOKENS = 6;
|
|
32
|
+
/** containment 达到这个值即判为「已经在别处说过」。 */
|
|
33
|
+
const DEFAULT_THRESHOLD = 0.7;
|
|
34
|
+
/**
|
|
35
|
+
* 归一化:抹掉不影响语义的差异,让「先读取」和「读取」这类插字不至于把整句错开。
|
|
36
|
+
* 全角标点、markdown 强调、反引号代码标记一律去掉。
|
|
37
|
+
*/
|
|
38
|
+
function normalize(text) {
|
|
39
|
+
return text
|
|
40
|
+
.replace(/[`*_~]/g, '')
|
|
41
|
+
.replace(/[,。;、:?!()【】「」《》,.;:?!()[\]{}"'/\\-]/g, '')
|
|
42
|
+
.replace(/\s+/g, '')
|
|
43
|
+
.toLocaleLowerCase();
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 中文按 2-gram、拉丁词按整词切。中文没有空格分词,bigram 是这里最稳的近似
|
|
47
|
+
* (memoryService.ts::terms 出于同样理由也用 bigram,保持一致)。
|
|
48
|
+
*/
|
|
49
|
+
function tokenize(normalized) {
|
|
50
|
+
const tokens = new Set();
|
|
51
|
+
for (const word of normalized.match(/[a-z0-9]+/g) ?? []) {
|
|
52
|
+
if (word.length > 1)
|
|
53
|
+
tokens.add(word);
|
|
54
|
+
}
|
|
55
|
+
const cjk = normalized.replace(/[a-z0-9]+/g, '');
|
|
56
|
+
for (let index = 0; index + 1 < cjk.length; index += 1) {
|
|
57
|
+
tokens.add(cjk.slice(index, index + 2));
|
|
58
|
+
}
|
|
59
|
+
return tokens;
|
|
60
|
+
}
|
|
61
|
+
function containmentOf(a, b) {
|
|
62
|
+
const smaller = a.size <= b.size ? a : b;
|
|
63
|
+
const larger = smaller === a ? b : a;
|
|
64
|
+
let shared = 0;
|
|
65
|
+
for (const token of smaller)
|
|
66
|
+
if (larger.has(token))
|
|
67
|
+
shared += 1;
|
|
68
|
+
return smaller.size === 0 ? 0 : shared / smaller.size;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 把组装好的提示词拆成「段落 → 规则行 → 子句」。
|
|
72
|
+
* 只取以 `- ` 开头的规则行:段落里的说明性散文不是规则,重复也无害。
|
|
73
|
+
*/
|
|
74
|
+
export function extractPromptClauses(prompt, ignoredSections = DEFAULT_IGNORED_SECTIONS) {
|
|
75
|
+
const clauses = [];
|
|
76
|
+
let section = '(开头)';
|
|
77
|
+
for (const line of prompt.split(/\r?\n/)) {
|
|
78
|
+
const heading = line.match(/^#+\s+(.*)$/);
|
|
79
|
+
if (heading) {
|
|
80
|
+
section = heading[1].trim();
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
if (ignoredSections.some((ignored) => section.includes(ignored)))
|
|
84
|
+
continue;
|
|
85
|
+
const rule = line.match(/^\s*-\s+(.*)$/)?.[1];
|
|
86
|
+
if (!rule)
|
|
87
|
+
continue;
|
|
88
|
+
for (const piece of rule.split(/[;;。]/)) {
|
|
89
|
+
const text = piece.trim();
|
|
90
|
+
if (!text)
|
|
91
|
+
continue;
|
|
92
|
+
const tokens = tokenize(normalize(text));
|
|
93
|
+
if (tokens.size < MIN_TOKENS)
|
|
94
|
+
continue;
|
|
95
|
+
clauses.push({ section, rule, text, tokens });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return clauses;
|
|
99
|
+
}
|
|
100
|
+
function isExempt(a, b, exemptions) {
|
|
101
|
+
return exemptions.some((item) => (a.text.includes(item.a) && b.text.includes(item.b))
|
|
102
|
+
|| (a.text.includes(item.b) && b.text.includes(item.a)));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* 找出**跨段落**的近重复子句。同段落内的重复不报——同一段里连续两句共享措辞通常是
|
|
106
|
+
* 有意的并列表达,报出来只会逼人写豁免;真正烧 token 且会漂移的是跨层重复。
|
|
107
|
+
*/
|
|
108
|
+
export function findDuplicateClauses(prompt, options = {}) {
|
|
109
|
+
const threshold = options.threshold ?? DEFAULT_THRESHOLD;
|
|
110
|
+
const exemptions = options.exemptions ?? [];
|
|
111
|
+
const clauses = extractPromptClauses(prompt, options.ignoredSections);
|
|
112
|
+
const findings = [];
|
|
113
|
+
for (let i = 0; i < clauses.length; i += 1) {
|
|
114
|
+
for (let j = i + 1; j < clauses.length; j += 1) {
|
|
115
|
+
const a = clauses[i];
|
|
116
|
+
const b = clauses[j];
|
|
117
|
+
if (a.section === b.section)
|
|
118
|
+
continue;
|
|
119
|
+
const containment = containmentOf(a.tokens, b.tokens);
|
|
120
|
+
if (containment < threshold)
|
|
121
|
+
continue;
|
|
122
|
+
if (isExempt(a, b, exemptions))
|
|
123
|
+
continue;
|
|
124
|
+
findings.push({ a, b, containment });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return findings.sort((x, y) => y.containment - x.containment);
|
|
128
|
+
}
|
|
129
|
+
/** 供测试失败时打印——直接给出两处原文和所在段落,不用再去翻提示词。 */
|
|
130
|
+
export function formatDuplicateFindings(findings) {
|
|
131
|
+
return findings
|
|
132
|
+
.map((item) => `[${item.containment.toFixed(2)}] 「${item.a.section}」${item.a.text}\n`
|
|
133
|
+
+ ` ↔ 「${item.b.section}」${item.b.text}`)
|
|
134
|
+
.join('\n');
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Cross-surface routing guard. The three Web tools intentionally have disjoint
|
|
138
|
+
* jobs, so stale fallback wording is a capability bug rather than style drift.
|
|
139
|
+
*/
|
|
140
|
+
export function findToolRoutingConflicts(prompt, tools) {
|
|
141
|
+
const conflicts = [];
|
|
142
|
+
const descriptions = new Map(tools.map((tool) => [
|
|
143
|
+
tool.name,
|
|
144
|
+
typeof tool.description === 'function' ? tool.description() : tool.description,
|
|
145
|
+
]));
|
|
146
|
+
const browser = descriptions.get('WebBrowser') ?? '';
|
|
147
|
+
const combinedRouting = `${prompt}\n${browser}`;
|
|
148
|
+
if (/(?:Search\/Fetch|WebSearch\/WebFetch)[^\n]{0,100}(?:无法|失败|cannot obtain)[^\n]{0,100}(?:降级|fallback|WebBrowser)|supported fallback/i.test(combinedRouting)) {
|
|
149
|
+
conflicts.push({
|
|
150
|
+
toolName: 'WebBrowser',
|
|
151
|
+
reason: 'WebBrowser 被描述为 Search/Fetch 失败后的普通兜底',
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
const fetch = descriptions.get('WebFetch') ?? '';
|
|
155
|
+
if (/WebBrowser/i.test(fetch)) {
|
|
156
|
+
conflicts.push({
|
|
157
|
+
toolName: 'WebFetch',
|
|
158
|
+
reason: 'WebFetch description 不应把失败路由到 WebBrowser',
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
if (!/does not call another model/i.test(fetch)
|
|
162
|
+
&& /process(?:es)?[^\n]{0,40}using an AI model|takes?[^\n]{0,30}prompt as input|performs? hidden summarization/i.test(fetch)) {
|
|
163
|
+
conflicts.push({
|
|
164
|
+
toolName: 'WebFetch',
|
|
165
|
+
reason: 'WebFetch description 声称存在实现中没有的模型处理步骤',
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
return conflicts;
|
|
169
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ============================================================
|
|
3
|
+
* src/prompts/lint.ts —— 系统提示词的跨层重复检查(确定性,无 LLM)
|
|
4
|
+
* ------------------------------------------------------------
|
|
5
|
+
* **为什么需要它**:`promptAssembly.ts` 把提示词分成若干层(身份/纪律层、工具编排层、
|
|
6
|
+
* Skills、Plugins、项目指令)。层与层之间很容易把同一条规则说两遍——写的时候各看各的段落,
|
|
7
|
+
* 读起来都通顺,拼起来就是重复烧 token、而且两处措辞一旦漂移就会互相矛盾。
|
|
8
|
+
* 人工清理一次能干净,但下一次加规则又会退化,所以要把「不重复」变成**回归护栏**。
|
|
9
|
+
*
|
|
10
|
+
* **分层判据(去重时按它归位,不是凭感觉)**:
|
|
11
|
+
* 一条规则若在「该工具不存在时依然成立」→ 属身份层;否则属工具层。
|
|
12
|
+
* 例:「改文件前先读最新内容」没有 Edit 工具时无意义 → 工具层;
|
|
13
|
+
* 「只在无法推断时才提问」没有 AskUserQuestion 时仍成立(约束的是判断而非调用)→ 身份层。
|
|
14
|
+
*
|
|
15
|
+
* **能力边界(必须如实说)**:这是**词面**近重复检测,不是语义检测。
|
|
16
|
+
* 「不用正则盲改源码」与「源码编辑优先专用工具」表达同一件事但几乎不共享字面,
|
|
17
|
+
* 本检查器**抓不到**——那类只能靠人读。它保证的是:已经清理干净的地方不会因为
|
|
18
|
+
* 有人复制粘贴一句旧措辞而悄悄退化。
|
|
19
|
+
*
|
|
20
|
+
* **为什么按「子句」而不是整条规则比**:整条规则通常长且各带自己的补充说明,
|
|
21
|
+
* 即使核心重复,整条的 Jaccard 也只有 0.1 量级(实测),根本触发不了。
|
|
22
|
+
* 拆成子句后重复的那一句会高度重合。
|
|
23
|
+
*
|
|
24
|
+
* **为什么用 containment 而不是 Jaccard**:我们要问的是「这句话是不是在别处已经说过了」,
|
|
25
|
+
* 即**包含**关系;短句被长句涵盖时 Jaccard 会被长句的其余部分稀释,containment 不会。
|
|
26
|
+
* ============================================================
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import type { Tool } from '../types.js';
|
|
30
|
+
|
|
31
|
+
/** 数据型段落:内容来自用户目录/项目文件,不是我们写的策略,不参与去重。 */
|
|
32
|
+
const DEFAULT_IGNORED_SECTIONS = ['Skills', 'Plugins', '项目指令'];
|
|
33
|
+
|
|
34
|
+
/** 子句短于这个 token 数就不参与比较——短句碰巧重合太常见,会淹没真信号。 */
|
|
35
|
+
const MIN_TOKENS = 6;
|
|
36
|
+
|
|
37
|
+
/** containment 达到这个值即判为「已经在别处说过」。 */
|
|
38
|
+
const DEFAULT_THRESHOLD = 0.7;
|
|
39
|
+
|
|
40
|
+
export interface PromptClause {
|
|
41
|
+
/** 所属段落标题(`# xxx` 里的 xxx);顶部无标题内容归入 '(开头)'。 */
|
|
42
|
+
section: string;
|
|
43
|
+
/** 该子句所属的完整规则行,报错时给人看上下文。 */
|
|
44
|
+
rule: string;
|
|
45
|
+
/** 子句原文。 */
|
|
46
|
+
text: string;
|
|
47
|
+
tokens: Set<string>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface DuplicateFinding {
|
|
51
|
+
a: PromptClause;
|
|
52
|
+
b: PromptClause;
|
|
53
|
+
/** |A∩B| / min(|A|,|B|),1 表示一方被另一方完全涵盖。 */
|
|
54
|
+
containment: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** 显式豁免。`reason` 是必填的——没有理由的豁免等于把检查器关掉。 */
|
|
58
|
+
export interface DedupExemption {
|
|
59
|
+
a: string;
|
|
60
|
+
b: string;
|
|
61
|
+
reason: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface DedupOptions {
|
|
65
|
+
threshold?: number;
|
|
66
|
+
ignoredSections?: string[];
|
|
67
|
+
exemptions?: DedupExemption[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* 归一化:抹掉不影响语义的差异,让「先读取」和「读取」这类插字不至于把整句错开。
|
|
72
|
+
* 全角标点、markdown 强调、反引号代码标记一律去掉。
|
|
73
|
+
*/
|
|
74
|
+
function normalize(text: string): string {
|
|
75
|
+
return text
|
|
76
|
+
.replace(/[`*_~]/g, '')
|
|
77
|
+
.replace(/[,。;、:?!()【】「」《》,.;:?!()[\]{}"'/\\-]/g, '')
|
|
78
|
+
.replace(/\s+/g, '')
|
|
79
|
+
.toLocaleLowerCase();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 中文按 2-gram、拉丁词按整词切。中文没有空格分词,bigram 是这里最稳的近似
|
|
84
|
+
* (memoryService.ts::terms 出于同样理由也用 bigram,保持一致)。
|
|
85
|
+
*/
|
|
86
|
+
function tokenize(normalized: string): Set<string> {
|
|
87
|
+
const tokens = new Set<string>();
|
|
88
|
+
for (const word of normalized.match(/[a-z0-9]+/g) ?? []) {
|
|
89
|
+
if (word.length > 1) tokens.add(word);
|
|
90
|
+
}
|
|
91
|
+
const cjk = normalized.replace(/[a-z0-9]+/g, '');
|
|
92
|
+
for (let index = 0; index + 1 < cjk.length; index += 1) {
|
|
93
|
+
tokens.add(cjk.slice(index, index + 2));
|
|
94
|
+
}
|
|
95
|
+
return tokens;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function containmentOf(a: Set<string>, b: Set<string>): number {
|
|
99
|
+
const smaller = a.size <= b.size ? a : b;
|
|
100
|
+
const larger = smaller === a ? b : a;
|
|
101
|
+
let shared = 0;
|
|
102
|
+
for (const token of smaller) if (larger.has(token)) shared += 1;
|
|
103
|
+
return smaller.size === 0 ? 0 : shared / smaller.size;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 把组装好的提示词拆成「段落 → 规则行 → 子句」。
|
|
108
|
+
* 只取以 `- ` 开头的规则行:段落里的说明性散文不是规则,重复也无害。
|
|
109
|
+
*/
|
|
110
|
+
export function extractPromptClauses(
|
|
111
|
+
prompt: string,
|
|
112
|
+
ignoredSections: string[] = DEFAULT_IGNORED_SECTIONS,
|
|
113
|
+
): PromptClause[] {
|
|
114
|
+
const clauses: PromptClause[] = [];
|
|
115
|
+
let section = '(开头)';
|
|
116
|
+
for (const line of prompt.split(/\r?\n/)) {
|
|
117
|
+
const heading = line.match(/^#+\s+(.*)$/);
|
|
118
|
+
if (heading) {
|
|
119
|
+
section = heading[1]!.trim();
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (ignoredSections.some((ignored) => section.includes(ignored))) continue;
|
|
123
|
+
const rule = line.match(/^\s*-\s+(.*)$/)?.[1];
|
|
124
|
+
if (!rule) continue;
|
|
125
|
+
for (const piece of rule.split(/[;;。]/)) {
|
|
126
|
+
const text = piece.trim();
|
|
127
|
+
if (!text) continue;
|
|
128
|
+
const tokens = tokenize(normalize(text));
|
|
129
|
+
if (tokens.size < MIN_TOKENS) continue;
|
|
130
|
+
clauses.push({ section, rule, text, tokens });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return clauses;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function isExempt(a: PromptClause, b: PromptClause, exemptions: DedupExemption[]): boolean {
|
|
137
|
+
return exemptions.some((item) =>
|
|
138
|
+
(a.text.includes(item.a) && b.text.includes(item.b))
|
|
139
|
+
|| (a.text.includes(item.b) && b.text.includes(item.a)));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* 找出**跨段落**的近重复子句。同段落内的重复不报——同一段里连续两句共享措辞通常是
|
|
144
|
+
* 有意的并列表达,报出来只会逼人写豁免;真正烧 token 且会漂移的是跨层重复。
|
|
145
|
+
*/
|
|
146
|
+
export function findDuplicateClauses(
|
|
147
|
+
prompt: string,
|
|
148
|
+
options: DedupOptions = {},
|
|
149
|
+
): DuplicateFinding[] {
|
|
150
|
+
const threshold = options.threshold ?? DEFAULT_THRESHOLD;
|
|
151
|
+
const exemptions = options.exemptions ?? [];
|
|
152
|
+
const clauses = extractPromptClauses(prompt, options.ignoredSections);
|
|
153
|
+
const findings: DuplicateFinding[] = [];
|
|
154
|
+
for (let i = 0; i < clauses.length; i += 1) {
|
|
155
|
+
for (let j = i + 1; j < clauses.length; j += 1) {
|
|
156
|
+
const a = clauses[i]!;
|
|
157
|
+
const b = clauses[j]!;
|
|
158
|
+
if (a.section === b.section) continue;
|
|
159
|
+
const containment = containmentOf(a.tokens, b.tokens);
|
|
160
|
+
if (containment < threshold) continue;
|
|
161
|
+
if (isExempt(a, b, exemptions)) continue;
|
|
162
|
+
findings.push({ a, b, containment });
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return findings.sort((x, y) => y.containment - x.containment);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** 供测试失败时打印——直接给出两处原文和所在段落,不用再去翻提示词。 */
|
|
169
|
+
export function formatDuplicateFindings(findings: DuplicateFinding[]): string {
|
|
170
|
+
return findings
|
|
171
|
+
.map((item) =>
|
|
172
|
+
`[${item.containment.toFixed(2)}] 「${item.a.section}」${item.a.text}\n`
|
|
173
|
+
+ ` ↔ 「${item.b.section}」${item.b.text}`)
|
|
174
|
+
.join('\n');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export interface ToolRoutingConflict {
|
|
178
|
+
toolName: string;
|
|
179
|
+
reason: string;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Cross-surface routing guard. The three Web tools intentionally have disjoint
|
|
184
|
+
* jobs, so stale fallback wording is a capability bug rather than style drift.
|
|
185
|
+
*/
|
|
186
|
+
export function findToolRoutingConflicts(
|
|
187
|
+
prompt: string,
|
|
188
|
+
tools: readonly Tool[],
|
|
189
|
+
): ToolRoutingConflict[] {
|
|
190
|
+
const conflicts: ToolRoutingConflict[] = [];
|
|
191
|
+
const descriptions = new Map(tools.map((tool) => [
|
|
192
|
+
tool.name,
|
|
193
|
+
typeof tool.description === 'function' ? tool.description() : tool.description,
|
|
194
|
+
]));
|
|
195
|
+
const browser = descriptions.get('WebBrowser') ?? '';
|
|
196
|
+
const combinedRouting = `${prompt}\n${browser}`;
|
|
197
|
+
if (/(?:Search\/Fetch|WebSearch\/WebFetch)[^\n]{0,100}(?:无法|失败|cannot obtain)[^\n]{0,100}(?:降级|fallback|WebBrowser)|supported fallback/i.test(combinedRouting)) {
|
|
198
|
+
conflicts.push({
|
|
199
|
+
toolName: 'WebBrowser',
|
|
200
|
+
reason: 'WebBrowser 被描述为 Search/Fetch 失败后的普通兜底',
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
const fetch = descriptions.get('WebFetch') ?? '';
|
|
204
|
+
if (/WebBrowser/i.test(fetch)) {
|
|
205
|
+
conflicts.push({
|
|
206
|
+
toolName: 'WebFetch',
|
|
207
|
+
reason: 'WebFetch description 不应把失败路由到 WebBrowser',
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
if (
|
|
211
|
+
!/does not call another model/i.test(fetch)
|
|
212
|
+
&& /process(?:es)?[^\n]{0,40}using an AI model|takes?[^\n]{0,30}prompt as input|performs? hidden summarization/i.test(fetch)
|
|
213
|
+
) {
|
|
214
|
+
conflicts.push({
|
|
215
|
+
toolName: 'WebFetch',
|
|
216
|
+
reason: 'WebFetch description 声称存在实现中没有的模型处理步骤',
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
return conflicts;
|
|
220
|
+
}
|
|
@@ -12,8 +12,8 @@ import { discoverPlugins } from '../plugins/commandRouter.js';
|
|
|
12
12
|
* 扫描 plugins/ 目录,返回已安装插件的元数据摘要。
|
|
13
13
|
* 复用 discoverPlugins() 的缓存结果,只提取轻量信息。
|
|
14
14
|
*/
|
|
15
|
-
export async function getPluginMetaList() {
|
|
16
|
-
const manifests = await discoverPlugins();
|
|
15
|
+
export async function getPluginMetaList(workingDir) {
|
|
16
|
+
const manifests = await discoverPlugins(workingDir);
|
|
17
17
|
return manifests.map((m) => ({
|
|
18
18
|
name: m.name,
|
|
19
19
|
description: m.description ?? '(no description)',
|
|
@@ -31,24 +31,40 @@ export async function getPluginMetaList() {
|
|
|
31
31
|
* - 明确标注触发方式("由用户通过 /xxx 激活")
|
|
32
32
|
* - 无插件时返回空字符串(调用方自行决定是否追加)
|
|
33
33
|
*/
|
|
34
|
+
/** 与 skillList 的 MAX_CATALOG_CHARS 同构:插件同样来自用户目录,条数不可预知。 */
|
|
35
|
+
const MAX_CATALOG_CHARS = 4_000;
|
|
36
|
+
const MAX_DESCRIPTION = 160;
|
|
34
37
|
export function formatPluginHint(plugins) {
|
|
35
38
|
if (plugins.length === 0)
|
|
36
39
|
return '';
|
|
37
40
|
const lines = [
|
|
38
|
-
'#
|
|
39
|
-
'',
|
|
40
|
-
'以下是当前已安装的插件。插件只能由用户输入 /xxx 激活——**你不能代替用户执行插件命令,也绝不能模拟/伪造插件的输出**。',
|
|
41
|
-
'当用户的需求明显匹配某个插件时(如想定时执行任务而装有 cron 类插件),可以一句话告知对应的 /xxx 命令存在,由用户自己决定是否运行;其余时候不必主动提及插件。',
|
|
42
|
-
'',
|
|
41
|
+
'# Plugins',
|
|
42
|
+
'插件命令只能由用户输入 `/命令` 激活。你可以提示匹配的命令,但不能代替用户激活或伪造插件输出。',
|
|
43
43
|
];
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
// 预算与降级和 skillList 保持同一套语义:先丢描述、再丢条目,且被丢弃的条数如实报出。
|
|
45
|
+
// 插件列表来自 cwd + packageRoot 双源扫描,装得多时条数不可预知,此前完全没有上限。
|
|
46
|
+
const render = (withDescription) => plugins.map((plugin) => {
|
|
47
|
+
const commands = plugin.commands.map((command) => `/${command.name}`).join(', ');
|
|
48
|
+
const description = withDescription ? `: ${plugin.description.slice(0, MAX_DESCRIPTION)}` : '';
|
|
49
|
+
return `- ${plugin.name}${description}${commands ? ` — ${commands}` : ''}`;
|
|
50
|
+
});
|
|
51
|
+
const full = render(true);
|
|
52
|
+
if (full.join('\n').length <= MAX_CATALOG_CHARS)
|
|
53
|
+
return [...lines, ...full].join('\n');
|
|
54
|
+
const compact = render(false);
|
|
55
|
+
const kept = [];
|
|
56
|
+
let used = 0;
|
|
57
|
+
for (const line of compact) {
|
|
58
|
+
if (used + line.length > MAX_CATALOG_CHARS)
|
|
59
|
+
break;
|
|
60
|
+
used += line.length + 1;
|
|
61
|
+
kept.push(line);
|
|
52
62
|
}
|
|
53
|
-
|
|
63
|
+
const dropped = plugins.length - kept.length;
|
|
64
|
+
return [
|
|
65
|
+
...lines,
|
|
66
|
+
'- (插件较多,此处只列名称与命令入口。)',
|
|
67
|
+
...kept,
|
|
68
|
+
...(dropped > 0 ? [`- (另有 ${dropped} 个插件因篇幅未列出。)`] : []),
|
|
69
|
+
].join('\n');
|
|
54
70
|
}
|