hammoc 1.4.0 → 1.6.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 +428 -405
- package/bin/hammoc.js +0 -6
- package/package.json +100 -94
- package/packages/client/dist/assets/agentExampleHighlight-ltj9ce0U.js +1 -0
- package/packages/client/dist/assets/commandTokenHighlight-ji_ViMb4.js +1 -0
- package/packages/client/dist/assets/index-B-DiRGuz.js +2 -0
- package/packages/client/dist/assets/index-B09doO8H.js +139 -0
- package/packages/client/dist/assets/index-BT4RIi0U.js +1523 -0
- package/packages/client/dist/assets/index-DyNJ5jEW.css +32 -0
- package/packages/client/dist/assets/snippetTokenHighlight-CP3v4o2g.js +1 -0
- package/packages/client/dist/index.html +2 -2
- package/packages/client/dist/sw.js +1 -1
- package/packages/server/dist/app.d.ts.map +1 -1
- package/packages/server/dist/app.js +13 -21
- package/packages/server/dist/app.js.map +1 -1
- package/packages/server/dist/controllers/bmadCoreConfigController.d.ts +41 -0
- package/packages/server/dist/controllers/bmadCoreConfigController.d.ts.map +1 -0
- package/packages/server/dist/controllers/bmadCoreConfigController.js +172 -0
- package/packages/server/dist/controllers/bmadCoreConfigController.js.map +1 -0
- package/packages/server/dist/controllers/claudeMdController.d.ts +26 -0
- package/packages/server/dist/controllers/claudeMdController.d.ts.map +1 -0
- package/packages/server/dist/controllers/claudeMdController.js +158 -0
- package/packages/server/dist/controllers/claudeMdController.js.map +1 -0
- package/packages/server/dist/controllers/contextBuilderController.d.ts +43 -0
- package/packages/server/dist/controllers/contextBuilderController.d.ts.map +1 -0
- package/packages/server/dist/controllers/contextBuilderController.js +159 -0
- package/packages/server/dist/controllers/contextBuilderController.js.map +1 -0
- package/packages/server/dist/controllers/harnessAgentController.d.ts +35 -0
- package/packages/server/dist/controllers/harnessAgentController.d.ts.map +1 -0
- package/packages/server/dist/controllers/harnessAgentController.js +372 -0
- package/packages/server/dist/controllers/harnessAgentController.js.map +1 -0
- package/packages/server/dist/controllers/harnessBundleController.d.ts +37 -0
- package/packages/server/dist/controllers/harnessBundleController.d.ts.map +1 -0
- package/packages/server/dist/controllers/harnessBundleController.js +312 -0
- package/packages/server/dist/controllers/harnessBundleController.js.map +1 -0
- package/packages/server/dist/controllers/harnessCommandController.d.ts +35 -0
- package/packages/server/dist/controllers/harnessCommandController.d.ts.map +1 -0
- package/packages/server/dist/controllers/harnessCommandController.js +415 -0
- package/packages/server/dist/controllers/harnessCommandController.js.map +1 -0
- package/packages/server/dist/controllers/harnessController.d.ts +21 -0
- package/packages/server/dist/controllers/harnessController.d.ts.map +1 -0
- package/packages/server/dist/controllers/harnessController.js +176 -0
- package/packages/server/dist/controllers/harnessController.js.map +1 -0
- package/packages/server/dist/controllers/harnessHookController.d.ts +32 -0
- package/packages/server/dist/controllers/harnessHookController.d.ts.map +1 -0
- package/packages/server/dist/controllers/harnessHookController.js +406 -0
- package/packages/server/dist/controllers/harnessHookController.js.map +1 -0
- package/packages/server/dist/controllers/harnessLintController.d.ts +18 -0
- package/packages/server/dist/controllers/harnessLintController.d.ts.map +1 -0
- package/packages/server/dist/controllers/harnessLintController.js +72 -0
- package/packages/server/dist/controllers/harnessLintController.js.map +1 -0
- package/packages/server/dist/controllers/harnessMcpController.d.ts +28 -0
- package/packages/server/dist/controllers/harnessMcpController.d.ts.map +1 -0
- package/packages/server/dist/controllers/harnessMcpController.js +371 -0
- package/packages/server/dist/controllers/harnessMcpController.js.map +1 -0
- package/packages/server/dist/controllers/harnessPluginController.d.ts +17 -0
- package/packages/server/dist/controllers/harnessPluginController.d.ts.map +1 -0
- package/packages/server/dist/controllers/harnessPluginController.js +115 -0
- package/packages/server/dist/controllers/harnessPluginController.js.map +1 -0
- package/packages/server/dist/controllers/harnessShareScopeController.d.ts +24 -0
- package/packages/server/dist/controllers/harnessShareScopeController.d.ts.map +1 -0
- package/packages/server/dist/controllers/harnessShareScopeController.js +120 -0
- package/packages/server/dist/controllers/harnessShareScopeController.js.map +1 -0
- package/packages/server/dist/controllers/harnessSkillController.d.ts +32 -0
- package/packages/server/dist/controllers/harnessSkillController.d.ts.map +1 -0
- package/packages/server/dist/controllers/harnessSkillController.js +453 -0
- package/packages/server/dist/controllers/harnessSkillController.js.map +1 -0
- package/packages/server/dist/controllers/marketplaceController.d.ts +19 -0
- package/packages/server/dist/controllers/marketplaceController.d.ts.map +1 -0
- package/packages/server/dist/controllers/marketplaceController.js +74 -0
- package/packages/server/dist/controllers/marketplaceController.js.map +1 -0
- package/packages/server/dist/controllers/observabilityController.d.ts +32 -0
- package/packages/server/dist/controllers/observabilityController.d.ts.map +1 -0
- package/packages/server/dist/controllers/observabilityController.js +148 -0
- package/packages/server/dist/controllers/observabilityController.js.map +1 -0
- package/packages/server/dist/controllers/projectController.d.ts.map +1 -1
- package/packages/server/dist/controllers/projectController.js +11 -0
- package/packages/server/dist/controllers/projectController.js.map +1 -1
- package/packages/server/dist/controllers/snippetController.d.ts +35 -0
- package/packages/server/dist/controllers/snippetController.d.ts.map +1 -0
- package/packages/server/dist/controllers/snippetController.js +294 -0
- package/packages/server/dist/controllers/snippetController.js.map +1 -0
- package/packages/server/dist/handlers/streamCallbacks.d.ts +8 -0
- package/packages/server/dist/handlers/streamCallbacks.d.ts.map +1 -1
- package/packages/server/dist/handlers/streamCallbacks.js +8 -0
- package/packages/server/dist/handlers/streamCallbacks.js.map +1 -1
- package/packages/server/dist/handlers/websocket.d.ts +15 -0
- package/packages/server/dist/handlers/websocket.d.ts.map +1 -1
- package/packages/server/dist/handlers/websocket.js +103 -2
- package/packages/server/dist/handlers/websocket.js.map +1 -1
- package/packages/server/dist/index.js +5 -0
- package/packages/server/dist/index.js.map +1 -1
- package/packages/server/dist/locales/en/server.json +37 -4
- package/packages/server/dist/locales/es/server.json +0 -4
- package/packages/server/dist/locales/ja/server.json +0 -4
- package/packages/server/dist/locales/ko/server.json +0 -4
- package/packages/server/dist/locales/pt/server.json +0 -4
- package/packages/server/dist/locales/zh-CN/server.json +0 -4
- package/packages/server/dist/routes/harness.d.ts +8 -0
- package/packages/server/dist/routes/harness.d.ts.map +1 -0
- package/packages/server/dist/routes/harness.js +150 -0
- package/packages/server/dist/routes/harness.js.map +1 -0
- package/packages/server/dist/routes/projects.d.ts.map +1 -1
- package/packages/server/dist/routes/projects.js +5 -60
- package/packages/server/dist/routes/projects.js.map +1 -1
- package/packages/server/dist/routes/snippets.d.ts +14 -0
- package/packages/server/dist/routes/snippets.d.ts.map +1 -0
- package/packages/server/dist/routes/snippets.js +27 -0
- package/packages/server/dist/routes/snippets.js.map +1 -0
- package/packages/server/dist/services/bmadCoreConfigService.d.ts +86 -0
- package/packages/server/dist/services/bmadCoreConfigService.d.ts.map +1 -0
- package/packages/server/dist/services/bmadCoreConfigService.js +175 -0
- package/packages/server/dist/services/bmadCoreConfigService.js.map +1 -0
- package/packages/server/dist/services/bmadStatusService.d.ts +15 -2
- package/packages/server/dist/services/bmadStatusService.d.ts.map +1 -1
- package/packages/server/dist/services/bmadStatusService.js +146 -37
- package/packages/server/dist/services/bmadStatusService.js.map +1 -1
- package/packages/server/dist/services/chatService.d.ts +3 -0
- package/packages/server/dist/services/chatService.d.ts.map +1 -1
- package/packages/server/dist/services/chatService.js +28 -7
- package/packages/server/dist/services/chatService.js.map +1 -1
- package/packages/server/dist/services/claudeMdService.d.ts +48 -0
- package/packages/server/dist/services/claudeMdService.d.ts.map +1 -0
- package/packages/server/dist/services/claudeMdService.js +240 -0
- package/packages/server/dist/services/claudeMdService.js.map +1 -0
- package/packages/server/dist/services/commandService.d.ts +10 -0
- package/packages/server/dist/services/commandService.d.ts.map +1 -1
- package/packages/server/dist/services/commandService.js +129 -4
- package/packages/server/dist/services/commandService.js.map +1 -1
- package/packages/server/dist/services/contextBuilderScriptTemplate.d.ts +24 -0
- package/packages/server/dist/services/contextBuilderScriptTemplate.d.ts.map +1 -0
- package/packages/server/dist/services/contextBuilderScriptTemplate.js +181 -0
- package/packages/server/dist/services/contextBuilderScriptTemplate.js.map +1 -0
- package/packages/server/dist/services/contextBuilderService.d.ts +68 -0
- package/packages/server/dist/services/contextBuilderService.d.ts.map +1 -0
- package/packages/server/dist/services/contextBuilderService.js +345 -0
- package/packages/server/dist/services/contextBuilderService.js.map +1 -0
- package/packages/server/dist/services/fileWatcherService.d.ts +24 -0
- package/packages/server/dist/services/fileWatcherService.d.ts.map +1 -1
- package/packages/server/dist/services/fileWatcherService.js +232 -1
- package/packages/server/dist/services/fileWatcherService.js.map +1 -1
- package/packages/server/dist/services/harnessAgentService.d.ts +97 -0
- package/packages/server/dist/services/harnessAgentService.d.ts.map +1 -0
- package/packages/server/dist/services/harnessAgentService.js +988 -0
- package/packages/server/dist/services/harnessAgentService.js.map +1 -0
- package/packages/server/dist/services/harnessBundleService.d.ts +145 -0
- package/packages/server/dist/services/harnessBundleService.d.ts.map +1 -0
- package/packages/server/dist/services/harnessBundleService.js +1318 -0
- package/packages/server/dist/services/harnessBundleService.js.map +1 -0
- package/packages/server/dist/services/harnessCommandService.d.ts +81 -0
- package/packages/server/dist/services/harnessCommandService.d.ts.map +1 -0
- package/packages/server/dist/services/harnessCommandService.js +917 -0
- package/packages/server/dist/services/harnessCommandService.js.map +1 -0
- package/packages/server/dist/services/harnessHookService.d.ts +82 -0
- package/packages/server/dist/services/harnessHookService.d.ts.map +1 -0
- package/packages/server/dist/services/harnessHookService.js +1112 -0
- package/packages/server/dist/services/harnessHookService.js.map +1 -0
- package/packages/server/dist/services/harnessLintService.d.ts +49 -0
- package/packages/server/dist/services/harnessLintService.d.ts.map +1 -0
- package/packages/server/dist/services/harnessLintService.js +628 -0
- package/packages/server/dist/services/harnessLintService.js.map +1 -0
- package/packages/server/dist/services/harnessMcpService.d.ts +100 -0
- package/packages/server/dist/services/harnessMcpService.d.ts.map +1 -0
- package/packages/server/dist/services/harnessMcpService.js +884 -0
- package/packages/server/dist/services/harnessMcpService.js.map +1 -0
- package/packages/server/dist/services/harnessPluginService.d.ts +66 -0
- package/packages/server/dist/services/harnessPluginService.d.ts.map +1 -0
- package/packages/server/dist/services/harnessPluginService.js +559 -0
- package/packages/server/dist/services/harnessPluginService.js.map +1 -0
- package/packages/server/dist/services/harnessService.d.ts +40 -0
- package/packages/server/dist/services/harnessService.d.ts.map +1 -0
- package/packages/server/dist/services/harnessService.js +222 -0
- package/packages/server/dist/services/harnessService.js.map +1 -0
- package/packages/server/dist/services/harnessShareScopeService.d.ts +50 -0
- package/packages/server/dist/services/harnessShareScopeService.d.ts.map +1 -0
- package/packages/server/dist/services/harnessShareScopeService.js +158 -0
- package/packages/server/dist/services/harnessShareScopeService.js.map +1 -0
- package/packages/server/dist/services/harnessSkillService.d.ts +70 -0
- package/packages/server/dist/services/harnessSkillService.d.ts.map +1 -0
- package/packages/server/dist/services/harnessSkillService.js +636 -0
- package/packages/server/dist/services/harnessSkillService.js.map +1 -0
- package/packages/server/dist/services/issueService.d.ts.map +1 -1
- package/packages/server/dist/services/issueService.js +3 -1
- package/packages/server/dist/services/issueService.js.map +1 -1
- package/packages/server/dist/services/manualSyncService.d.ts +19 -0
- package/packages/server/dist/services/manualSyncService.d.ts.map +1 -0
- package/packages/server/dist/services/manualSyncService.js +110 -0
- package/packages/server/dist/services/manualSyncService.js.map +1 -0
- package/packages/server/dist/services/marketplaceService.d.ts +50 -0
- package/packages/server/dist/services/marketplaceService.d.ts.map +1 -0
- package/packages/server/dist/services/marketplaceService.js +326 -0
- package/packages/server/dist/services/marketplaceService.js.map +1 -0
- package/packages/server/dist/services/observabilityService.d.ts +87 -0
- package/packages/server/dist/services/observabilityService.d.ts.map +1 -0
- package/packages/server/dist/services/observabilityService.js +0 -0
- package/packages/server/dist/services/observabilityService.js.map +1 -0
- package/packages/server/dist/services/queueService.d.ts.map +1 -1
- package/packages/server/dist/services/queueService.js +48 -2
- package/packages/server/dist/services/queueService.js.map +1 -1
- package/packages/server/dist/services/sessionService.d.ts +16 -0
- package/packages/server/dist/services/sessionService.d.ts.map +1 -1
- package/packages/server/dist/services/sessionService.js +125 -0
- package/packages/server/dist/services/sessionService.js.map +1 -1
- package/packages/server/dist/services/snippetService.d.ts +54 -0
- package/packages/server/dist/services/snippetService.d.ts.map +1 -0
- package/packages/server/dist/services/snippetService.js +371 -0
- package/packages/server/dist/services/snippetService.js.map +1 -0
- package/packages/server/dist/services/tokenCountService.d.ts +71 -0
- package/packages/server/dist/services/tokenCountService.d.ts.map +1 -0
- package/packages/server/dist/services/tokenCountService.js +313 -0
- package/packages/server/dist/services/tokenCountService.js.map +1 -0
- package/packages/server/dist/services/utils/applyYamlFrontmatterPatch.d.ts +46 -0
- package/packages/server/dist/services/utils/applyYamlFrontmatterPatch.d.ts.map +1 -0
- package/packages/server/dist/services/utils/applyYamlFrontmatterPatch.js +125 -0
- package/packages/server/dist/services/utils/applyYamlFrontmatterPatch.js.map +1 -0
- package/packages/server/dist/snippets/apply-qa-fixes +7 -5
- package/packages/server/dist/snippets/qa-review +5 -1
- package/packages/server/dist/snippets/split-commit +9 -0
- package/packages/server/dist/utils/applySecretsPolicy.d.ts +53 -0
- package/packages/server/dist/utils/applySecretsPolicy.d.ts.map +1 -0
- package/packages/server/dist/utils/applySecretsPolicy.js +204 -0
- package/packages/server/dist/utils/applySecretsPolicy.js.map +1 -0
- package/packages/server/dist/utils/assertNoSecretOnShared.d.ts +40 -0
- package/packages/server/dist/utils/assertNoSecretOnShared.d.ts.map +1 -0
- package/packages/server/dist/utils/assertNoSecretOnShared.js +47 -0
- package/packages/server/dist/utils/assertNoSecretOnShared.js.map +1 -0
- package/packages/server/dist/utils/assertSafeBundlePath.d.ts +29 -0
- package/packages/server/dist/utils/assertSafeBundlePath.d.ts.map +1 -0
- package/packages/server/dist/utils/assertSafeBundlePath.js +53 -0
- package/packages/server/dist/utils/assertSafeBundlePath.js.map +1 -0
- package/packages/server/dist/utils/bundledBinaryModelSupport.d.ts +7 -0
- package/packages/server/dist/utils/bundledBinaryModelSupport.d.ts.map +1 -0
- package/packages/server/dist/utils/bundledBinaryModelSupport.js +107 -0
- package/packages/server/dist/utils/bundledBinaryModelSupport.js.map +1 -0
- package/packages/server/dist/utils/effortUtils.d.ts +2 -2
- package/packages/server/dist/utils/effortUtils.js +5 -5
- package/packages/server/dist/utils/effortUtils.js.map +1 -1
- package/packages/server/dist/utils/errors.d.ts +1 -0
- package/packages/server/dist/utils/errors.d.ts.map +1 -1
- package/packages/server/dist/utils/errors.js +17 -0
- package/packages/server/dist/utils/errors.js.map +1 -1
- package/packages/server/dist/utils/gitignoreFilter.d.ts +23 -0
- package/packages/server/dist/utils/gitignoreFilter.d.ts.map +1 -0
- package/packages/server/dist/utils/gitignoreFilter.js +42 -0
- package/packages/server/dist/utils/gitignoreFilter.js.map +1 -0
- package/packages/server/dist/utils/harnessBundleSchema.d.ts +107 -0
- package/packages/server/dist/utils/harnessBundleSchema.d.ts.map +1 -0
- package/packages/server/dist/utils/harnessBundleSchema.js +89 -0
- package/packages/server/dist/utils/harnessBundleSchema.js.map +1 -0
- package/packages/server/dist/utils/harnessPaths.d.ts +74 -0
- package/packages/server/dist/utils/harnessPaths.d.ts.map +1 -0
- package/packages/server/dist/utils/harnessPaths.js +247 -0
- package/packages/server/dist/utils/harnessPaths.js.map +1 -0
- package/packages/server/dist/utils/secretHeuristic.d.ts +72 -0
- package/packages/server/dist/utils/secretHeuristic.d.ts.map +1 -0
- package/packages/server/dist/utils/secretHeuristic.js +163 -0
- package/packages/server/dist/utils/secretHeuristic.js.map +1 -0
- package/packages/server/dist/utils/secretPlaceholderNamer.d.ts +41 -0
- package/packages/server/dist/utils/secretPlaceholderNamer.d.ts.map +1 -0
- package/packages/server/dist/utils/secretPlaceholderNamer.js +81 -0
- package/packages/server/dist/utils/secretPlaceholderNamer.js.map +1 -0
- package/packages/server/dist/utils/serverPathResolver.d.ts +29 -0
- package/packages/server/dist/utils/serverPathResolver.d.ts.map +1 -0
- package/packages/server/dist/utils/serverPathResolver.js +59 -0
- package/packages/server/dist/utils/serverPathResolver.js.map +1 -0
- package/packages/server/dist/utils/snippetPaths.d.ts +61 -0
- package/packages/server/dist/utils/snippetPaths.d.ts.map +1 -0
- package/packages/server/dist/utils/snippetPaths.js +123 -0
- package/packages/server/dist/utils/snippetPaths.js.map +1 -0
- package/packages/server/dist/utils/structuredEditor.d.ts +34 -0
- package/packages/server/dist/utils/structuredEditor.d.ts.map +1 -0
- package/packages/server/dist/utils/structuredEditor.js +111 -0
- package/packages/server/dist/utils/structuredEditor.js.map +1 -0
- package/packages/server/package.json +6 -2
- package/packages/server/resources/internals/INDEX.md +25 -0
- package/packages/server/resources/internals/bmad-qa-fix-marker.md +32 -0
- package/packages/server/resources/internals/harness-files.md +85 -0
- package/packages/server/resources/internals/image-storage.md +43 -0
- package/packages/server/resources/internals/observability-storage.md +23 -0
- package/packages/server/resources/manual/01-getting-started.md +104 -0
- package/packages/server/resources/manual/02-chat.md +285 -0
- package/packages/server/resources/manual/03-sessions.md +48 -0
- package/packages/server/resources/manual/04-slash-commands-favorites.md +152 -0
- package/packages/server/resources/manual/05-projects.md +76 -0
- package/packages/server/resources/manual/06-file-explorer-editor.md +90 -0
- package/packages/server/resources/manual/07-git.md +94 -0
- package/packages/server/resources/manual/08-terminal.md +59 -0
- package/packages/server/resources/manual/09-queue-runner.md +262 -0
- package/packages/server/resources/manual/10-project-board.md +194 -0
- package/packages/server/resources/manual/11-bmad-method-integration.md +130 -0
- package/packages/server/resources/manual/12-harness-workbench.md +256 -0
- package/packages/server/resources/manual/13-settings.md +241 -0
- package/packages/server/resources/manual/14-keyboard-shortcuts.md +68 -0
- package/packages/server/resources/manual/15-environment-variables.md +28 -0
- package/packages/server/resources/manual/16-troubleshooting.md +110 -0
- package/packages/server/resources/manual/INDEX.md +60 -0
- package/packages/shared/dist/index.d.ts +7 -0
- package/packages/shared/dist/index.d.ts.map +1 -1
- package/packages/shared/dist/index.js +14 -0
- package/packages/shared/dist/index.js.map +1 -1
- package/packages/shared/dist/types/bmadCoreConfig.d.ts +71 -0
- package/packages/shared/dist/types/bmadCoreConfig.d.ts.map +1 -0
- package/packages/shared/dist/types/bmadCoreConfig.js +30 -0
- package/packages/shared/dist/types/bmadCoreConfig.js.map +1 -0
- package/packages/shared/dist/types/bmadStatus.d.ts +10 -0
- package/packages/shared/dist/types/bmadStatus.d.ts.map +1 -1
- package/packages/shared/dist/types/bmadStatus.js.map +1 -1
- package/packages/shared/dist/types/board.d.ts +6 -0
- package/packages/shared/dist/types/board.d.ts.map +1 -1
- package/packages/shared/dist/types/command.d.ts +3 -3
- package/packages/shared/dist/types/command.d.ts.map +1 -1
- package/packages/shared/dist/types/contextBuilder.d.ts +102 -0
- package/packages/shared/dist/types/contextBuilder.d.ts.map +1 -0
- package/packages/shared/dist/types/contextBuilder.js +55 -0
- package/packages/shared/dist/types/contextBuilder.js.map +1 -0
- package/packages/shared/dist/types/harness.d.ts +1211 -0
- package/packages/shared/dist/types/harness.d.ts.map +1 -0
- package/packages/shared/dist/types/harness.js +107 -0
- package/packages/shared/dist/types/harness.js.map +1 -0
- package/packages/shared/dist/types/harnessBundle.d.ts +205 -0
- package/packages/shared/dist/types/harnessBundle.d.ts.map +1 -0
- package/packages/shared/dist/types/harnessBundle.js +18 -0
- package/packages/shared/dist/types/harnessBundle.js.map +1 -0
- package/packages/shared/dist/types/marketplace.d.ts +83 -0
- package/packages/shared/dist/types/marketplace.d.ts.map +1 -0
- package/packages/shared/dist/types/marketplace.js +18 -0
- package/packages/shared/dist/types/marketplace.js.map +1 -0
- package/packages/shared/dist/types/observability.d.ts +148 -0
- package/packages/shared/dist/types/observability.d.ts.map +1 -0
- package/packages/shared/dist/types/observability.js +24 -0
- package/packages/shared/dist/types/observability.js.map +1 -0
- package/packages/shared/dist/types/preferences.d.ts +4 -0
- package/packages/shared/dist/types/preferences.d.ts.map +1 -1
- package/packages/shared/dist/types/preferences.js.map +1 -1
- package/packages/shared/dist/types/queue.d.ts +9 -0
- package/packages/shared/dist/types/queue.d.ts.map +1 -1
- package/packages/shared/dist/types/sdk.d.ts +1 -1
- package/packages/shared/dist/types/sdk.d.ts.map +1 -1
- package/packages/shared/dist/types/sdk.js +1 -1
- package/packages/shared/dist/types/sdk.js.map +1 -1
- package/packages/shared/dist/types/websocket.d.ts +10 -0
- package/packages/shared/dist/types/websocket.d.ts.map +1 -1
- package/packages/shared/dist/utils/markdownSections.d.ts +50 -0
- package/packages/shared/dist/utils/markdownSections.d.ts.map +1 -0
- package/packages/shared/dist/utils/markdownSections.js +111 -0
- package/packages/shared/dist/utils/markdownSections.js.map +1 -0
- package/packages/shared/dist/utils/queueParser.d.ts.map +1 -1
- package/packages/shared/dist/utils/queueParser.js +104 -0
- package/packages/shared/dist/utils/queueParser.js.map +1 -1
- package/scripts/build-manual-shards.mjs +100 -0
- package/packages/client/dist/assets/index-6jREnVYd.js +0 -2
- package/packages/client/dist/assets/index-BFF0iqyW.css +0 -32
- package/packages/client/dist/assets/index-BcI4y-fU.js +0 -1454
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 30.2 (Task 1.5): server-side PATH resolver for the
|
|
3
|
+
* `mcp/command-not-on-path` lint rule.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors the `which`/`where` + 5s timeout pattern used by
|
|
6
|
+
* serverController.ts:14-31 for npm path resolution. Absolute paths skip the
|
|
7
|
+
* shell call entirely and fall through to a simple `fs.existsSync` check —
|
|
8
|
+
* faster and avoids spurious `which: not found` lines on stderr for inputs
|
|
9
|
+
* that obviously don't need PATH resolution.
|
|
10
|
+
*
|
|
11
|
+
* The result is intentionally shaped as `{ resolved: string | null }` (not a
|
|
12
|
+
* boolean) so the caller can surface the resolved absolute path in tooltips
|
|
13
|
+
* without re-running the lookup.
|
|
14
|
+
*/
|
|
15
|
+
export interface ResolveCommandResult {
|
|
16
|
+
resolved: string | null;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Resolve `cmd` against the current process PATH.
|
|
20
|
+
*
|
|
21
|
+
* - Absolute path → `fs.existsSync` only (no shell).
|
|
22
|
+
* - Relative path containing a separator → resolved relative to `cwd`
|
|
23
|
+
* (uncommon for MCP `command` fields but covered for completeness).
|
|
24
|
+
* - Bare name → `where.exe` on Windows / `which` elsewhere.
|
|
25
|
+
*
|
|
26
|
+
* Never throws — failures collapse to `{ resolved: null }`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveCommandOnServerPath(cmd: string): ResolveCommandResult;
|
|
29
|
+
//# sourceMappingURL=serverPathResolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serverPathResolver.d.ts","sourceRoot":"","sources":["../../src/utils/serverPathResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAID;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CA8B5E"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 30.2 (Task 1.5): server-side PATH resolver for the
|
|
3
|
+
* `mcp/command-not-on-path` lint rule.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors the `which`/`where` + 5s timeout pattern used by
|
|
6
|
+
* serverController.ts:14-31 for npm path resolution. Absolute paths skip the
|
|
7
|
+
* shell call entirely and fall through to a simple `fs.existsSync` check —
|
|
8
|
+
* faster and avoids spurious `which: not found` lines on stderr for inputs
|
|
9
|
+
* that obviously don't need PATH resolution.
|
|
10
|
+
*
|
|
11
|
+
* The result is intentionally shaped as `{ resolved: string | null }` (not a
|
|
12
|
+
* boolean) so the caller can surface the resolved absolute path in tooltips
|
|
13
|
+
* without re-running the lookup.
|
|
14
|
+
*/
|
|
15
|
+
import { execFileSync } from 'child_process';
|
|
16
|
+
import fs from 'fs';
|
|
17
|
+
import path from 'path';
|
|
18
|
+
const RESOLVE_TIMEOUT_MS = 5000;
|
|
19
|
+
/**
|
|
20
|
+
* Resolve `cmd` against the current process PATH.
|
|
21
|
+
*
|
|
22
|
+
* - Absolute path → `fs.existsSync` only (no shell).
|
|
23
|
+
* - Relative path containing a separator → resolved relative to `cwd`
|
|
24
|
+
* (uncommon for MCP `command` fields but covered for completeness).
|
|
25
|
+
* - Bare name → `where.exe` on Windows / `which` elsewhere.
|
|
26
|
+
*
|
|
27
|
+
* Never throws — failures collapse to `{ resolved: null }`.
|
|
28
|
+
*/
|
|
29
|
+
export function resolveCommandOnServerPath(cmd) {
|
|
30
|
+
if (!cmd || typeof cmd !== 'string')
|
|
31
|
+
return { resolved: null };
|
|
32
|
+
const trimmed = cmd.trim();
|
|
33
|
+
if (!trimmed)
|
|
34
|
+
return { resolved: null };
|
|
35
|
+
if (path.isAbsolute(trimmed)) {
|
|
36
|
+
return { resolved: fs.existsSync(trimmed) ? trimmed : null };
|
|
37
|
+
}
|
|
38
|
+
// Path with a separator (e.g. "./bin/foo" or "scripts/run.sh") — resolve
|
|
39
|
+
// against cwd. Server PATH is irrelevant in this case.
|
|
40
|
+
if (trimmed.includes('/') || trimmed.includes('\\')) {
|
|
41
|
+
const abs = path.resolve(process.cwd(), trimmed);
|
|
42
|
+
return { resolved: fs.existsSync(abs) ? abs : null };
|
|
43
|
+
}
|
|
44
|
+
const finder = process.platform === 'win32' ? 'where.exe' : 'which';
|
|
45
|
+
try {
|
|
46
|
+
const stdout = execFileSync(finder, [trimmed], {
|
|
47
|
+
encoding: 'utf-8',
|
|
48
|
+
timeout: RESOLVE_TIMEOUT_MS,
|
|
49
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
50
|
+
});
|
|
51
|
+
const first = stdout.trim().split(/\r?\n/)[0]?.trim();
|
|
52
|
+
return { resolved: first ? first : null };
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// Non-zero exit (not found) or timeout — both surface as "unresolved".
|
|
56
|
+
return { resolved: null };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=serverPathResolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serverPathResolver.js","sourceRoot":"","sources":["../../src/utils/serverPathResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAMxB,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEhC;;;;;;;;;GASG;AACH,MAAM,UAAU,0BAA0B,CAAC,GAAW;IACpD,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAE/D,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAExC,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,QAAQ,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC/D,CAAC;IAED,yEAAyE;IACzE,uDAAuD;IACvD,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACpD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC;QACjD,OAAO,EAAE,QAAQ,EAAE,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACvD,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC;IACpE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE;YAC7C,QAAQ,EAAE,OAAO;YACjB,OAAO,EAAE,kBAAkB;YAC3B,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;SACpC,CAAC,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;QACtD,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,uEAAuE;QACvE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC5B,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 29.2: Snippet path resolver.
|
|
3
|
+
*
|
|
4
|
+
* The snippet system has three scopes that map to different on-disk roots:
|
|
5
|
+
*
|
|
6
|
+
* - project → `<projectRoot>/.hammoc/snippets/`
|
|
7
|
+
* - user → `~/.hammoc/snippets/`
|
|
8
|
+
* - bundled → server-bundled snippets (read-only, located alongside the
|
|
9
|
+
* server build at `<serverDist>/snippets/`)
|
|
10
|
+
*
|
|
11
|
+
* `resolveSnippetPath` is the only path-shaping entry point used by
|
|
12
|
+
* `snippetService` — it accepts `{ scope, projectSlug?, name }` and never lets
|
|
13
|
+
* a caller-supplied relative path through. NAME_RE rejects anything outside
|
|
14
|
+
* `[A-Za-z0-9._-]+` so directory traversal (`..`, `/`, `\`, `\0`, drive
|
|
15
|
+
* letters, UNC prefixes) is impossible by construction.
|
|
16
|
+
*
|
|
17
|
+
* On-disk filename normalization: writes always land at `<root>/<name>.md`
|
|
18
|
+
* regardless of input form. Reads accept both `<name>.md` and the legacy
|
|
19
|
+
* extension-less `<name>` (snippetResolver supports both for back-compat) so
|
|
20
|
+
* existing bundled and user files continue to load.
|
|
21
|
+
*/
|
|
22
|
+
/** Snippet name regex — same shape as snippetResolver's NAME_RE. */
|
|
23
|
+
export declare const SNIPPET_NAME_RE: RegExp;
|
|
24
|
+
/** Return the absolute path of `~/.hammoc/snippets/`. */
|
|
25
|
+
export declare function getUserSnippetsDir(): string;
|
|
26
|
+
/** Return the absolute path of the server-bundled snippets directory. */
|
|
27
|
+
export declare function getBundledSnippetsDir(): string;
|
|
28
|
+
/** Return the absolute path of `<projectRoot>/.hammoc/snippets/`. */
|
|
29
|
+
export declare function getProjectSnippetsDir(projectSlug: string): Promise<string>;
|
|
30
|
+
export interface ResolvedSnippetPath {
|
|
31
|
+
/** Root directory (the snippets dir itself, not the project root). */
|
|
32
|
+
resolvedRoot: string;
|
|
33
|
+
/** `<resolvedRoot>/<name>.md`. */
|
|
34
|
+
absolutePath: string;
|
|
35
|
+
/** Same path but without `.md` — legacy format used for back-compat reads. */
|
|
36
|
+
legacyAbsolutePath: string;
|
|
37
|
+
/** True when this path is read-only (bundled). */
|
|
38
|
+
readOnly: boolean;
|
|
39
|
+
}
|
|
40
|
+
export type SnippetScope = 'project' | 'user' | 'bundled';
|
|
41
|
+
export interface SnippetPathRef {
|
|
42
|
+
scope: SnippetScope;
|
|
43
|
+
/** Required when scope === 'project'. */
|
|
44
|
+
projectSlug?: string;
|
|
45
|
+
name: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolve a snippet path with NAME_RE validation and bundled read-only flagging.
|
|
49
|
+
* Throws an Error with `code: 'HARNESS_PATH_DENIED'` for invalid names.
|
|
50
|
+
*/
|
|
51
|
+
export declare function resolveSnippetPath(ref: SnippetPathRef): Promise<ResolvedSnippetPath>;
|
|
52
|
+
/**
|
|
53
|
+
* Throws an HARNESS_PATH_DENIED Error when the name is invalid.
|
|
54
|
+
*
|
|
55
|
+
* Note: SNIPPET_NAME_RE allows `.` so single-dot names (`a.b`) pass through.
|
|
56
|
+
* That also means literal `..` would pass the regex — we reject it
|
|
57
|
+
* separately below, mirroring snippetResolver.resolveSnippet's defensive
|
|
58
|
+
* filter.
|
|
59
|
+
*/
|
|
60
|
+
export declare function validateSnippetName(name: string): void;
|
|
61
|
+
//# sourceMappingURL=snippetPaths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snippetPaths.d.ts","sourceRoot":"","sources":["../../src/utils/snippetPaths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAaH,oEAAoE;AACpE,eAAO,MAAM,eAAe,QAAsB,CAAC;AAOnD,yDAAyD;AACzD,wBAAgB,kBAAkB,IAAI,MAAM,CAM3C;AAED,yEAAyE;AACzE,wBAAgB,qBAAqB,IAAI,MAAM,CAM9C;AAED,qEAAqE;AACrE,wBAAsB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAchF;AAED,MAAM,WAAW,mBAAmB;IAClC,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kDAAkD;IAClD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAE1D,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,YAAY,CAAC;IACpB,yCAAyC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA4B1F;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAatD"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 29.2: Snippet path resolver.
|
|
3
|
+
*
|
|
4
|
+
* The snippet system has three scopes that map to different on-disk roots:
|
|
5
|
+
*
|
|
6
|
+
* - project → `<projectRoot>/.hammoc/snippets/`
|
|
7
|
+
* - user → `~/.hammoc/snippets/`
|
|
8
|
+
* - bundled → server-bundled snippets (read-only, located alongside the
|
|
9
|
+
* server build at `<serverDist>/snippets/`)
|
|
10
|
+
*
|
|
11
|
+
* `resolveSnippetPath` is the only path-shaping entry point used by
|
|
12
|
+
* `snippetService` — it accepts `{ scope, projectSlug?, name }` and never lets
|
|
13
|
+
* a caller-supplied relative path through. NAME_RE rejects anything outside
|
|
14
|
+
* `[A-Za-z0-9._-]+` so directory traversal (`..`, `/`, `\`, `\0`, drive
|
|
15
|
+
* letters, UNC prefixes) is impossible by construction.
|
|
16
|
+
*
|
|
17
|
+
* On-disk filename normalization: writes always land at `<root>/<name>.md`
|
|
18
|
+
* regardless of input form. Reads accept both `<name>.md` and the legacy
|
|
19
|
+
* extension-less `<name>` (snippetResolver supports both for back-compat) so
|
|
20
|
+
* existing bundled and user files continue to load.
|
|
21
|
+
*/
|
|
22
|
+
import os from 'os';
|
|
23
|
+
import path from 'path';
|
|
24
|
+
import { fileURLToPath } from 'node:url';
|
|
25
|
+
import { projectService } from '../services/projectService.js';
|
|
26
|
+
/** Hammoc home root override for tests. Mirrors `HAMMOC_HARNESS_HOME_OVERRIDE`. */
|
|
27
|
+
const HOME_OVERRIDE_ENV = 'HAMMOC_HOME_OVERRIDE';
|
|
28
|
+
/** Bundled snippet directory override for tests (e.g. point at a tmp dir). */
|
|
29
|
+
const BUNDLED_OVERRIDE_ENV = 'HAMMOC_BUNDLED_SNIPPETS_DIR';
|
|
30
|
+
/** Snippet name regex — same shape as snippetResolver's NAME_RE. */
|
|
31
|
+
export const SNIPPET_NAME_RE = /^[a-zA-Z0-9._-]+$/;
|
|
32
|
+
const SNIPPETS_SUBDIR = '.hammoc/snippets';
|
|
33
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
34
|
+
const DEFAULT_BUNDLED_DIR = path.resolve(__dirname, '..', 'snippets');
|
|
35
|
+
/** Return the absolute path of `~/.hammoc/snippets/`. */
|
|
36
|
+
export function getUserSnippetsDir() {
|
|
37
|
+
const override = process.env[HOME_OVERRIDE_ENV];
|
|
38
|
+
if (override && override.length > 0) {
|
|
39
|
+
return path.join(override, '.hammoc', 'snippets');
|
|
40
|
+
}
|
|
41
|
+
return path.join(os.homedir(), '.hammoc', 'snippets');
|
|
42
|
+
}
|
|
43
|
+
/** Return the absolute path of the server-bundled snippets directory. */
|
|
44
|
+
export function getBundledSnippetsDir() {
|
|
45
|
+
const override = process.env[BUNDLED_OVERRIDE_ENV];
|
|
46
|
+
if (override && override.length > 0) {
|
|
47
|
+
return override;
|
|
48
|
+
}
|
|
49
|
+
return DEFAULT_BUNDLED_DIR;
|
|
50
|
+
}
|
|
51
|
+
/** Return the absolute path of `<projectRoot>/.hammoc/snippets/`. */
|
|
52
|
+
export async function getProjectSnippetsDir(projectSlug) {
|
|
53
|
+
if (!projectSlug) {
|
|
54
|
+
throw makePathDeniedError('projectSlug is required for project scope');
|
|
55
|
+
}
|
|
56
|
+
if (projectSlug.includes('\0') ||
|
|
57
|
+
projectSlug.includes('..') ||
|
|
58
|
+
projectSlug.includes('/') ||
|
|
59
|
+
projectSlug.includes('\\')) {
|
|
60
|
+
throw makePathDeniedError('projectSlug must not contain path separators');
|
|
61
|
+
}
|
|
62
|
+
const projectRoot = await projectService.resolveOriginalPath(projectSlug);
|
|
63
|
+
return path.join(projectRoot, SNIPPETS_SUBDIR);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Resolve a snippet path with NAME_RE validation and bundled read-only flagging.
|
|
67
|
+
* Throws an Error with `code: 'HARNESS_PATH_DENIED'` for invalid names.
|
|
68
|
+
*/
|
|
69
|
+
export async function resolveSnippetPath(ref) {
|
|
70
|
+
validateSnippetName(ref.name);
|
|
71
|
+
let resolvedRoot;
|
|
72
|
+
let readOnly = false;
|
|
73
|
+
if (ref.scope === 'project') {
|
|
74
|
+
if (!ref.projectSlug) {
|
|
75
|
+
throw makePathDeniedError('projectSlug is required for project scope');
|
|
76
|
+
}
|
|
77
|
+
resolvedRoot = path.resolve(await getProjectSnippetsDir(ref.projectSlug));
|
|
78
|
+
}
|
|
79
|
+
else if (ref.scope === 'user') {
|
|
80
|
+
resolvedRoot = path.resolve(getUserSnippetsDir());
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
resolvedRoot = path.resolve(getBundledSnippetsDir());
|
|
84
|
+
readOnly = true;
|
|
85
|
+
}
|
|
86
|
+
// Belt + suspenders: even though NAME_RE forbids separators, double-check
|
|
87
|
+
// the resolved file stays inside the root.
|
|
88
|
+
const absolutePath = path.resolve(resolvedRoot, `${ref.name}.md`);
|
|
89
|
+
if (absolutePath !== resolvedRoot &&
|
|
90
|
+
!absolutePath.startsWith(resolvedRoot + path.sep)) {
|
|
91
|
+
throw makePathDeniedError('snippet path escapes root');
|
|
92
|
+
}
|
|
93
|
+
const legacyAbsolutePath = path.resolve(resolvedRoot, ref.name);
|
|
94
|
+
return { resolvedRoot, absolutePath, legacyAbsolutePath, readOnly };
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Throws an HARNESS_PATH_DENIED Error when the name is invalid.
|
|
98
|
+
*
|
|
99
|
+
* Note: SNIPPET_NAME_RE allows `.` so single-dot names (`a.b`) pass through.
|
|
100
|
+
* That also means literal `..` would pass the regex — we reject it
|
|
101
|
+
* separately below, mirroring snippetResolver.resolveSnippet's defensive
|
|
102
|
+
* filter.
|
|
103
|
+
*/
|
|
104
|
+
export function validateSnippetName(name) {
|
|
105
|
+
if (!name || typeof name !== 'string') {
|
|
106
|
+
throw makePathDeniedError('snippet name is required');
|
|
107
|
+
}
|
|
108
|
+
if (name.includes('\0')) {
|
|
109
|
+
throw makePathDeniedError('null byte in snippet name');
|
|
110
|
+
}
|
|
111
|
+
if (name === '..' || name === '.' || name.includes('/') || name.includes('\\')) {
|
|
112
|
+
throw makePathDeniedError(`path traversal denied: ${name}`);
|
|
113
|
+
}
|
|
114
|
+
if (!SNIPPET_NAME_RE.test(name)) {
|
|
115
|
+
throw makePathDeniedError(`invalid snippet name: ${name}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
function makePathDeniedError(message) {
|
|
119
|
+
const err = new Error(message);
|
|
120
|
+
err.code = 'HARNESS_PATH_DENIED';
|
|
121
|
+
return err;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=snippetPaths.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snippetPaths.js","sourceRoot":"","sources":["../../src/utils/snippetPaths.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 28.0.5: YAML / JSONC round-trip editor.
|
|
3
|
+
*
|
|
4
|
+
* The goal is to mutate a single key inside a structured config file while
|
|
5
|
+
* preserving the author's comments, blank lines, key order, and quoting style.
|
|
6
|
+
*
|
|
7
|
+
* - YAML → `yaml` (eemeli) `parseDocument` + `doc.setIn/deleteIn` + `doc.toString()`.
|
|
8
|
+
* The Document AST keeps comment & blank-line metadata attached to
|
|
9
|
+
* each node, and `toString()` re-emits them.
|
|
10
|
+
* NOTE: `js-yaml@4` remains a coexisting dependency for session-meta parsing
|
|
11
|
+
* elsewhere in the codebase, but has no comment-preservation path and must
|
|
12
|
+
* not be used for harness edits.
|
|
13
|
+
*
|
|
14
|
+
* - JSONC → `jsonc-parser` `modify` + `applyEdits`. This is the same path VS
|
|
15
|
+
* Code uses when it edits user `settings.json` — comments and
|
|
16
|
+
* formatting are preserved.
|
|
17
|
+
*
|
|
18
|
+
* Both entry points throw `HARNESS_PARSE_ERROR` on unparseable input so the
|
|
19
|
+
* harness controller can surface the envelope and the client can fall back to
|
|
20
|
+
* raw editing.
|
|
21
|
+
*/
|
|
22
|
+
import { type HarnessStructuredPatchOp } from '@hammoc/shared';
|
|
23
|
+
/**
|
|
24
|
+
* Apply structured patches to a YAML source string while preserving comments,
|
|
25
|
+
* blank lines, and key order.
|
|
26
|
+
*/
|
|
27
|
+
export declare function applyYamlPatch(source: string, ops: HarnessStructuredPatchOp[]): string;
|
|
28
|
+
/**
|
|
29
|
+
* Apply structured patches to a JSONC source string while preserving comments
|
|
30
|
+
* and formatting. Inserts missing intermediate objects as needed (matches
|
|
31
|
+
* `jsonc-parser` `modify` default behavior).
|
|
32
|
+
*/
|
|
33
|
+
export declare function applyJsoncPatch(source: string, ops: HarnessStructuredPatchOp[]): string;
|
|
34
|
+
//# sourceMappingURL=structuredEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structuredEditor.d.ts","sourceRoot":"","sources":["../../src/utils/structuredEditor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAaH,OAAO,EAAkB,KAAK,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAQ/E;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,wBAAwB,EAAE,GAAG,MAAM,CAmCtF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,wBAAwB,EAAE,GAAG,MAAM,CAqCvF"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 28.0.5: YAML / JSONC round-trip editor.
|
|
3
|
+
*
|
|
4
|
+
* The goal is to mutate a single key inside a structured config file while
|
|
5
|
+
* preserving the author's comments, blank lines, key order, and quoting style.
|
|
6
|
+
*
|
|
7
|
+
* - YAML → `yaml` (eemeli) `parseDocument` + `doc.setIn/deleteIn` + `doc.toString()`.
|
|
8
|
+
* The Document AST keeps comment & blank-line metadata attached to
|
|
9
|
+
* each node, and `toString()` re-emits them.
|
|
10
|
+
* NOTE: `js-yaml@4` remains a coexisting dependency for session-meta parsing
|
|
11
|
+
* elsewhere in the codebase, but has no comment-preservation path and must
|
|
12
|
+
* not be used for harness edits.
|
|
13
|
+
*
|
|
14
|
+
* - JSONC → `jsonc-parser` `modify` + `applyEdits`. This is the same path VS
|
|
15
|
+
* Code uses when it edits user `settings.json` — comments and
|
|
16
|
+
* formatting are preserved.
|
|
17
|
+
*
|
|
18
|
+
* Both entry points throw `HARNESS_PARSE_ERROR` on unparseable input so the
|
|
19
|
+
* harness controller can surface the envelope and the client can fall back to
|
|
20
|
+
* raw editing.
|
|
21
|
+
*/
|
|
22
|
+
import { parseDocument } from 'yaml';
|
|
23
|
+
import { modify, applyEdits, parse, parseTree, findNodeAtLocation, printParseErrorCode, } from 'jsonc-parser';
|
|
24
|
+
import { HARNESS_ERRORS } from '@hammoc/shared';
|
|
25
|
+
function parseError(format, cause) {
|
|
26
|
+
const err = new Error(`failed to parse ${format}: ${cause?.message ?? String(cause)}`);
|
|
27
|
+
err.code = HARNESS_ERRORS.HARNESS_PARSE_ERROR.code;
|
|
28
|
+
return err;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Apply structured patches to a YAML source string while preserving comments,
|
|
32
|
+
* blank lines, and key order.
|
|
33
|
+
*/
|
|
34
|
+
export function applyYamlPatch(source, ops) {
|
|
35
|
+
let doc;
|
|
36
|
+
try {
|
|
37
|
+
doc = parseDocument(source, { keepSourceTokens: true });
|
|
38
|
+
if (doc.errors.length > 0) {
|
|
39
|
+
throw doc.errors[0];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (cause) {
|
|
43
|
+
throw parseError('yaml', cause);
|
|
44
|
+
}
|
|
45
|
+
// A freshly-created empty document has `contents: null`; we need a map so
|
|
46
|
+
// setIn can plant new keys at the top level.
|
|
47
|
+
if (doc.contents == null) {
|
|
48
|
+
doc.contents = doc.createNode({});
|
|
49
|
+
}
|
|
50
|
+
for (const op of ops) {
|
|
51
|
+
if (!op.path || op.path.length === 0) {
|
|
52
|
+
throw parseError('yaml', new Error('patch op requires a non-empty path'));
|
|
53
|
+
}
|
|
54
|
+
if (op.value === undefined) {
|
|
55
|
+
// Idempotent delete: if any segment (including intermediates) is already
|
|
56
|
+
// absent, the target is effectively deleted — skip. `doc.deleteIn`
|
|
57
|
+
// itself only no-ops on a missing leaf; a missing intermediate throws.
|
|
58
|
+
if (!doc.hasIn(op.path))
|
|
59
|
+
continue;
|
|
60
|
+
doc.deleteIn(op.path);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
doc.setIn(op.path, op.value);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// toString() preserves original comments/blank lines/quote style for any
|
|
67
|
+
// node that was not explicitly replaced.
|
|
68
|
+
return doc.toString();
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Apply structured patches to a JSONC source string while preserving comments
|
|
72
|
+
* and formatting. Inserts missing intermediate objects as needed (matches
|
|
73
|
+
* `jsonc-parser` `modify` default behavior).
|
|
74
|
+
*/
|
|
75
|
+
export function applyJsoncPatch(source, ops) {
|
|
76
|
+
// Validate the source up front — `modify` silently starts from `{}` on
|
|
77
|
+
// garbage input, which would quietly erase the user's file. Feed the
|
|
78
|
+
// official errors array to catch unterminated strings, missing values, etc.
|
|
79
|
+
if (source.trim().length > 0) {
|
|
80
|
+
const errors = [];
|
|
81
|
+
parse(source, errors, { allowTrailingComma: true, disallowComments: false });
|
|
82
|
+
if (errors.length > 0) {
|
|
83
|
+
const first = errors[0];
|
|
84
|
+
throw parseError('jsonc', new Error(`${printParseErrorCode(first.error)} at offset ${first.offset}`));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const formattingOptions = {
|
|
88
|
+
insertSpaces: true,
|
|
89
|
+
tabSize: 2,
|
|
90
|
+
eol: source.includes('\r\n') ? '\r\n' : '\n',
|
|
91
|
+
};
|
|
92
|
+
let current = source;
|
|
93
|
+
for (const op of ops) {
|
|
94
|
+
if (!op.path || op.path.length === 0) {
|
|
95
|
+
throw parseError('jsonc', new Error('patch op requires a non-empty path'));
|
|
96
|
+
}
|
|
97
|
+
// Idempotent delete: `modify(undefined)` throws "Can not delete…" when the
|
|
98
|
+
// target (or any intermediate) does not exist; treat that as a no-op so
|
|
99
|
+
// callers can apply delete patches without first probing state.
|
|
100
|
+
if (op.value === undefined) {
|
|
101
|
+
const tree = parseTree(current, [], { allowTrailingComma: true, disallowComments: false });
|
|
102
|
+
if (!tree || !findNodeAtLocation(tree, op.path))
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
// `modify` takes `undefined` to mean "remove", matching HarnessStructuredPatchOp.value semantics.
|
|
106
|
+
const edits = modify(current, op.path, op.value, { formattingOptions });
|
|
107
|
+
current = applyEdits(current, edits);
|
|
108
|
+
}
|
|
109
|
+
return current;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=structuredEditor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structuredEditor.js","sourceRoot":"","sources":["../../src/utils/structuredEditor.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,23 +15,27 @@
|
|
|
15
15
|
"test:all": "vitest run"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@anthropic-ai/claude-agent-sdk": "^0.
|
|
18
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.158",
|
|
19
|
+
"@anthropic-ai/sdk": "^0.100.1",
|
|
19
20
|
"@hammoc/shared": "*",
|
|
20
21
|
"bcrypt": "^6.0.0",
|
|
21
22
|
"chokidar": "^5.0.0",
|
|
22
23
|
"cookie-session": "^2.1.1",
|
|
23
24
|
"cors": "^2.8.5",
|
|
24
25
|
"express": "^4.21.0",
|
|
25
|
-
"express-rate-limit": "^8.3.1",
|
|
26
26
|
"helmet": "^8.1.0",
|
|
27
27
|
"i18next": "^24.2.3",
|
|
28
|
+
"ignore": "^5.3.2",
|
|
28
29
|
"js-yaml": "^4.1.1",
|
|
30
|
+
"jsonc-parser": "^3.3.1",
|
|
31
|
+
"jszip": "^3.10.1",
|
|
29
32
|
"multer": "^2.1.1",
|
|
30
33
|
"node-pty": "^1.0.0",
|
|
31
34
|
"sharp": "^0.34.5",
|
|
32
35
|
"simple-git": "^3.27.0",
|
|
33
36
|
"socket.io": "^4.8.0",
|
|
34
37
|
"web-push": "^3.6.7",
|
|
38
|
+
"yaml": "^2.8.3",
|
|
35
39
|
"zod": "^4.3.6"
|
|
36
40
|
},
|
|
37
41
|
"devDependencies": {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Hammoc Internals (Agent-Only Reference)
|
|
2
|
+
|
|
3
|
+
This folder documents Hammoc's internal mechanisms that an in-IDE agent may need to read or correlate, but that are deliberately omitted from the user-facing manual. The user does not need to know any of this; the agent does.
|
|
4
|
+
|
|
5
|
+
## How to use
|
|
6
|
+
|
|
7
|
+
Read individual entries **on demand** when the user's request involves the underlying mechanism (for example: correlating an attached image with a file on disk, understanding how a session ID maps to a JSONL file). Do not pre-load this folder.
|
|
8
|
+
|
|
9
|
+
## Entries
|
|
10
|
+
|
|
11
|
+
- [Image Storage](./image-storage.md) — On-disk path and filename scheme for chat-attached images
|
|
12
|
+
- [Harness File Layout](./harness-files.md) — Where `.claude/` items (skills, commands, agents, hooks, MCP servers, `CLAUDE.md`) live on disk, when changes take effect, the Context Builder's Hammoc-managed SessionStart files, plugin install-state paths, and how the Secret-on-Shared guard relates to direct file writes
|
|
13
|
+
- [Observability Storage](./observability-storage.md) — Where the MCP call log and exact-token-count cache live, their JSONL/JSON format, body-stripping, and 30-day retention
|
|
14
|
+
- [BMad QA-Fix Marker](./bmad-qa-fix-marker.md) — The `<!-- hammoc:qa-fix -->` story-file convention that drives QA re-review recommendations and the board's "QA Fixed" badge
|
|
15
|
+
|
|
16
|
+
## Maintenance
|
|
17
|
+
|
|
18
|
+
Add a new file here whenever there is internal behavior the agent needs to act on but the user does not need to see. Likely future entries:
|
|
19
|
+
|
|
20
|
+
- Session ID and project-path slug encoding under `~/.claude/projects/`
|
|
21
|
+
- JSONL message tree structure (parent/child UUIDs, branching)
|
|
22
|
+
- Permission-mode internal effects on the SDK call
|
|
23
|
+
- Snippet resolution order and substitution rules
|
|
24
|
+
|
|
25
|
+
Each new file should describe the on-disk reality (paths, formats, lifecycle), not the user-facing UI behavior — that belongs in the user manual.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# BMad QA-Fix Marker
|
|
2
|
+
|
|
3
|
+
A Hammoc-specific convention layered on top of standard BMad story files. It lets the project-overview Next Step Recommender (manual §11.5) and the board's "QA Fixed" badge (§10.7) know whether a developer has already addressed the **current** QA gate — without guessing from file modification times (the old, unreliable approach: `review-story` rewrites the story after the gate, which made a freshly-reviewed story look "stale").
|
|
4
|
+
|
|
5
|
+
## The marker
|
|
6
|
+
|
|
7
|
+
A single HTML comment inside the story markdown file:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
<!-- hammoc:qa-fix gate="<the gate's `updated` value>" applied="true|false" -->
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- It is an HTML comment, so standard BMad tooling ignores it and it never changes the story's Status or the gate file.
|
|
14
|
+
- `gate` is the QA gate's `updated:` value — it ties the marker to one specific gate. When QA later re-reviews, the gate's `updated` changes and any marker pointing at the old value becomes stale automatically.
|
|
15
|
+
- `applied="false"` → "QA flagged this gate; a fix is still needed." `applied="true"` → "Dev addressed this gate."
|
|
16
|
+
|
|
17
|
+
## Who writes it
|
|
18
|
+
|
|
19
|
+
- **QA review** (Hammoc's bundled `qa-review` snippet) appends `applied="false"` in the story's **QA Results** section when it issues a CONCERNS or FAIL gate.
|
|
20
|
+
- **Apply QA fixes** (the bundled `apply-qa-fixes` snippet) appends `applied="true"` in the story's **Completion Notes** after the fixes are made — without touching Status or the gate file.
|
|
21
|
+
|
|
22
|
+
If you perform a QA review or apply QA fixes **manually** (not through these snippets), append the matching marker yourself so the recommender and badge stay accurate.
|
|
23
|
+
|
|
24
|
+
## How it is read
|
|
25
|
+
|
|
26
|
+
The server (BMad status service) collects all markers in a story and matches them against the **current** gate's `updated` value to derive a per-story `gateFixState`:
|
|
27
|
+
|
|
28
|
+
- an `applied="true"` marker for the current gate → **`applied`** (Dev done; QA re-review is the next step)
|
|
29
|
+
- only an `applied="false"` marker for the current gate → **`needed`** (Dev must apply fixes)
|
|
30
|
+
- no marker for the current gate → **`undefined`** → the UI offers **both** actions (Apply QA fixes / Request QA review) and lets the user choose, because it can't tell whether fixes were applied (legacy story, external BMad project, or manually-edited gate)
|
|
31
|
+
|
|
32
|
+
An `applied` marker wins over a `needed` marker for the same gate. The board shows the sky-blue **QA Fixed** badge only when `gateFixState='applied'` for a FAIL/CONCERNS gate.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Harness File Layout
|
|
2
|
+
|
|
3
|
+
The Harness Workbench (user-facing, see manual §12) edits Claude Code's `.claude/` configuration trees in place. An agent can reach the same files directly with Read / Write / Edit and skip the UI when that's faster.
|
|
4
|
+
|
|
5
|
+
## On-disk roots
|
|
6
|
+
|
|
7
|
+
Two trees, walked in this priority order:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
<projectRoot>/.claude/ # project scope (highest)
|
|
11
|
+
<homeDir>/.claude/ # global scope (user scope)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
When the same name (skill, command, agent, hook, MCP server, snippet) exists in both, the project copy is **active** and the global copy is **shadowed** but kept on disk.
|
|
15
|
+
|
|
16
|
+
`<homeDir>` must be resolved before use — Read / Write / Edit do not expand `~`. On Windows that is `C:\Users\<user>\`.
|
|
17
|
+
|
|
18
|
+
## Per-item layout
|
|
19
|
+
|
|
20
|
+
| Item | Path (under either `.claude/` root) | Format |
|
|
21
|
+
|------|--------------------------------------|--------|
|
|
22
|
+
| Skill | `skills/<name>/SKILL.md` + bundle assets in the same directory | Markdown body + YAML frontmatter (`name`, `description`, `version`) |
|
|
23
|
+
| Slash command | `commands/<name>.md` | Markdown body + YAML frontmatter |
|
|
24
|
+
| Sub-agent | `agents/<name>.md` | Markdown body + YAML frontmatter (`name`, `description`, `tools`) |
|
|
25
|
+
| Hook | `settings.json` → `hooks.<EventName>[]` entries | JSON; one event name per array key (`PreToolUse`, `PostToolUse`, `Stop`, `SubagentStop`, `SessionStart`, `SessionEnd`, `UserPromptSubmit`, `PreCompact`, `Notification`) |
|
|
26
|
+
| MCP server | `.mcp.json` at the project root, or `<homeDir>/.claude/.mcp.json` | JSON; entries under `mcpServers.<name>` |
|
|
27
|
+
| `CLAUDE.md` | `<projectRoot>/.claude/CLAUDE.md`, `<homeDir>/.claude/CLAUDE.md` | Plain Markdown; both files load into every session, project wins on conflict |
|
|
28
|
+
| Plugin | `plugins/<vendor>__<name>/` | Plugin bundle directory; treated read-only by Hammoc — copy items out to project/global to customize |
|
|
29
|
+
|
|
30
|
+
Hammoc-native `%snippets` are a separate layer (see manual §4.6); they live under `<projectRoot>/.hammoc/snippets/` and `<homeDir>/.hammoc/snippets/`, **not** the `.claude/` tree.
|
|
31
|
+
|
|
32
|
+
## Context Builder generated files (Hammoc-managed)
|
|
33
|
+
|
|
34
|
+
The Context Builder (manual §12.17) writes two Hammoc-owned files plus one `settings.json` entry:
|
|
35
|
+
|
|
36
|
+
| File | Role |
|
|
37
|
+
|------|------|
|
|
38
|
+
| `<projectRoot>/.hammoc/context-builder.json` | Manifest — the single source of truth: `enabled` flag, reference-file list, dynamic-variable toggles, recent-commit count, custom-command list |
|
|
39
|
+
| `<projectRoot>/.hammoc/hooks/context-builder.mjs` | Generated Node.js SessionStart hook. Regenerated from the manifest on every change; reads the reference files fresh, recomputes the variables, runs acknowledged custom commands, and prints `{"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"…"}}` |
|
|
40
|
+
| `<projectRoot>/.claude/settings.json` → `hooks.SessionStart[]` | Auto-registered entry whose `command` is `node "<absPath>/.hammoc/hooks/context-builder.mjs"` (forward-slash normalized) |
|
|
41
|
+
|
|
42
|
+
Hammoc recognizes its own SessionStart entry by the `.hammoc/hooks/context-builder.` substring in the command — there is no metadata key. **Do not hand-edit that entry or the `.mjs` script directly**: the Context Builder panel owns them and regenerates the script (overwriting manual edits) on the next save. To change the injected context, edit `context-builder.json` (or use the panel). User-authored SessionStart entries that do not contain the marker substring are left untouched.
|
|
43
|
+
|
|
44
|
+
## Plugin install state (read-only)
|
|
45
|
+
|
|
46
|
+
The Marketplace panel (manual §12.19) and the Plugins panel read Claude Code's own plugin bookkeeping under `<homeDir>/.claude/plugins/`:
|
|
47
|
+
|
|
48
|
+
- `known_marketplaces.json` — registered marketplace repos
|
|
49
|
+
- `marketplaces/<name>/.claude-plugin/marketplace.json` — each marketplace's catalog manifest (`plugins[]`)
|
|
50
|
+
- `installed_plugins.json` — which plugins are installed (used to mark catalog cards "Installed")
|
|
51
|
+
|
|
52
|
+
Hammoc only **reads** these; installs/uninstalls happen through the interactive `/plugin …` slash commands in a Claude CLI session, after which a file watcher refreshes the cards.
|
|
53
|
+
|
|
54
|
+
## Sharing scope
|
|
55
|
+
|
|
56
|
+
Each file's "share" status is computed from the project's `.gitignore`:
|
|
57
|
+
|
|
58
|
+
- **Shared** — File path is tracked by git
|
|
59
|
+
- **Local** — File path is untracked but `.claude/` is not ignored
|
|
60
|
+
- **Ignored** — A `.gitignore` rule excludes `.claude/` (or an ancestor)
|
|
61
|
+
|
|
62
|
+
The workbench shows a badge for each file. When an agent writes a file under `.claude/`, the resulting share scope is whatever the `.gitignore` already says — Hammoc does not rewrite `.gitignore` on the agent's behalf.
|
|
63
|
+
|
|
64
|
+
## Secret-on-Shared guard
|
|
65
|
+
|
|
66
|
+
When the user saves a `Shared`-scope file through the UI, Hammoc scans for plaintext secrets (entropy + pattern heuristic) and blocks the save with a dialog. **An agent writing the file directly via Write / Edit bypasses that dialog.** If the agent is editing a `Shared` file under `.claude/`, it must avoid committing plaintext API keys, bearer tokens, etc. Use a sibling `*.local.<ext>` file (gitignored) and reference it from the shared file, or use `${ENV_VAR}` references that the hook / MCP runtime expands.
|
|
67
|
+
|
|
68
|
+
## When changes take effect
|
|
69
|
+
|
|
70
|
+
- **Skills, commands, agents, CLAUDE.md, snippets** — Picked up on the next message in a chat turn (the system prompt and tool list re-resolve on each turn).
|
|
71
|
+
- **Hooks** — Same: next message in a chat turn.
|
|
72
|
+
- **MCP servers** — Picked up only on a **fresh session spawn**, not mid-session. The workbench UI shows a "Takes effect on your next user message" banner with a "Start new session" button after the user edits an MCP entry; an agent making MCP edits should remind the user to start a new session, or do so on their behalf.
|
|
73
|
+
- **Plugin enable/disable** — Same as MCP: fresh spawn required.
|
|
74
|
+
|
|
75
|
+
## Static lint (informational)
|
|
76
|
+
|
|
77
|
+
The workbench runs seven static-lint rules over the trees (see manual §12.12). Agents writing harness files should keep these rules in mind even though writes are not blocked:
|
|
78
|
+
|
|
79
|
+
- duplicate names across scopes
|
|
80
|
+
- invalid hook matcher regex
|
|
81
|
+
- frontmatter / JSON parse errors
|
|
82
|
+
- MCP `stdio` command not on `PATH`
|
|
83
|
+
- malformed MCP URLs
|
|
84
|
+
- non-standard agent tool names
|
|
85
|
+
- hook bodies referencing undefined env vars
|