hammoc 1.4.0 → 1.5.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 +422 -405
- package/bin/hammoc.js +0 -6
- package/package.json +100 -94
- package/packages/client/dist/assets/agentExampleHighlight-BgwTm15v.js +1 -0
- package/packages/client/dist/assets/commandTokenHighlight-BljHwnrK.js +1 -0
- package/packages/client/dist/assets/index-CjyjnXB8.css +32 -0
- package/packages/client/dist/assets/index-D3LxqW3f.js +2 -0
- package/packages/client/dist/assets/index-NqJdhlek.js +1498 -0
- package/packages/client/dist/assets/snippetTokenHighlight-DWsaQXX0.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/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/harnessAgentController.d.ts +28 -0
- package/packages/server/dist/controllers/harnessAgentController.d.ts.map +1 -0
- package/packages/server/dist/controllers/harnessAgentController.js +339 -0
- package/packages/server/dist/controllers/harnessAgentController.js.map +1 -0
- package/packages/server/dist/controllers/harnessCommandController.d.ts +28 -0
- package/packages/server/dist/controllers/harnessCommandController.d.ts.map +1 -0
- package/packages/server/dist/controllers/harnessCommandController.js +382 -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 +363 -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 +310 -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 +15 -0
- package/packages/server/dist/controllers/harnessShareScopeController.d.ts.map +1 -0
- package/packages/server/dist/controllers/harnessShareScopeController.js +73 -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/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/websocket.d.ts +15 -0
- package/packages/server/dist/handlers/websocket.d.ts.map +1 -1
- package/packages/server/dist/handlers/websocket.js +79 -0
- 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 +92 -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/bmadStatusService.d.ts +6 -2
- package/packages/server/dist/services/bmadStatusService.d.ts.map +1 -1
- package/packages/server/dist/services/bmadStatusService.js +88 -32
- 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 +27 -6
- 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/fileWatcherService.d.ts +24 -0
- package/packages/server/dist/services/fileWatcherService.d.ts.map +1 -1
- package/packages/server/dist/services/fileWatcherService.js +192 -1
- package/packages/server/dist/services/fileWatcherService.js.map +1 -1
- package/packages/server/dist/services/harnessAgentService.d.ts +79 -0
- package/packages/server/dist/services/harnessAgentService.d.ts.map +1 -0
- package/packages/server/dist/services/harnessAgentService.js +933 -0
- package/packages/server/dist/services/harnessAgentService.js.map +1 -0
- package/packages/server/dist/services/harnessCommandService.d.ts +60 -0
- package/packages/server/dist/services/harnessCommandService.d.ts.map +1 -0
- package/packages/server/dist/services/harnessCommandService.js +853 -0
- package/packages/server/dist/services/harnessCommandService.js.map +1 -0
- package/packages/server/dist/services/harnessHookService.d.ts +55 -0
- package/packages/server/dist/services/harnessHookService.d.ts.map +1 -0
- package/packages/server/dist/services/harnessHookService.js +1060 -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 +77 -0
- package/packages/server/dist/services/harnessMcpService.d.ts.map +1 -0
- package/packages/server/dist/services/harnessMcpService.js +814 -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 +31 -0
- package/packages/server/dist/services/harnessShareScopeService.d.ts.map +1 -0
- package/packages/server/dist/services/harnessShareScopeService.js +93 -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 +2 -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/queueService.d.ts.map +1 -1
- package/packages/server/dist/services/queueService.js +45 -2
- package/packages/server/dist/services/queueService.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/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/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/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 +105 -0
- package/packages/server/dist/utils/harnessBundleSchema.d.ts.map +1 -0
- package/packages/server/dist/utils/harnessBundleSchema.js +79 -0
- package/packages/server/dist/utils/harnessBundleSchema.js.map +1 -0
- package/packages/server/dist/utils/harnessPaths.d.ts +34 -0
- package/packages/server/dist/utils/harnessPaths.d.ts.map +1 -0
- package/packages/server/dist/utils/harnessPaths.js +124 -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 +4 -1
- package/packages/server/resources/internals/INDEX.md +23 -0
- package/packages/server/resources/internals/harness-files.md +63 -0
- package/packages/server/resources/internals/image-storage.md +43 -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 +74 -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 +193 -0
- package/packages/server/resources/manual/11-bmad-method-integration.md +128 -0
- package/packages/server/resources/manual/12-harness-workbench.md +175 -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 +3 -0
- package/packages/shared/dist/index.d.ts.map +1 -1
- package/packages/shared/dist/index.js +6 -0
- package/packages/shared/dist/index.js.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/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 +170 -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/preferences.d.ts +2 -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/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,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 30.3 (Task 2.2): apply the three `secretsPolicy` modes to a payload
|
|
3
|
+
* before it is written into the bundle.
|
|
4
|
+
*
|
|
5
|
+
* Two operating shapes, mirroring `detectSecretsInValue` / `detectSecretsInText`:
|
|
6
|
+
*
|
|
7
|
+
* 1. `applyPolicyToValue` walks a JSON-like value (mcp config, settings.json
|
|
8
|
+
* hooks block). For `excluded` the matched leaf is deleted from its
|
|
9
|
+
* parent; for `placeholder` it is rewritten as `${ENV_REF_NAME}` using
|
|
10
|
+
* `secretPlaceholderNamer`; for `included-explicit` the value is left
|
|
11
|
+
* untouched.
|
|
12
|
+
*
|
|
13
|
+
* 2. `applyPolicyToText` walks line-oriented text (CLAUDE.md, agent body,
|
|
14
|
+
* command body). For `excluded` the offending line is replaced with the
|
|
15
|
+
* sentinel `<< SECRET REMOVED >>` so line numbers stay stable; for
|
|
16
|
+
* `placeholder` every secret substring on that line is rewritten as
|
|
17
|
+
* `${ENV_REF_NAME}`; for `included-explicit` the text is unchanged.
|
|
18
|
+
*
|
|
19
|
+
* Both return per-call counters that the caller aggregates into the
|
|
20
|
+
* `ImportApplySummary` (well, the export equivalent — same idea: surface to
|
|
21
|
+
* the user how many secrets were affected).
|
|
22
|
+
*/
|
|
23
|
+
import type { BundleItemDomain, SecretsPolicy } from '@hammoc/shared';
|
|
24
|
+
export declare const SECRET_REMOVED_TEXT_PLACEHOLDER = "<< SECRET REMOVED >>";
|
|
25
|
+
export interface ApplyPolicyValueInput {
|
|
26
|
+
policy: SecretsPolicy;
|
|
27
|
+
domain: BundleItemDomain;
|
|
28
|
+
cardName: string;
|
|
29
|
+
hookEvent?: string;
|
|
30
|
+
/** Root-relative path prefix for matched dot-paths (e.g. `mcpServers.context7`). */
|
|
31
|
+
pathPrefix?: string[];
|
|
32
|
+
value: unknown;
|
|
33
|
+
}
|
|
34
|
+
export interface ApplyPolicyValueResult {
|
|
35
|
+
value: unknown;
|
|
36
|
+
removedCount: number;
|
|
37
|
+
replacedCount: number;
|
|
38
|
+
}
|
|
39
|
+
export interface ApplyPolicyTextInput {
|
|
40
|
+
policy: SecretsPolicy;
|
|
41
|
+
domain: BundleItemDomain;
|
|
42
|
+
cardName: string;
|
|
43
|
+
hookEvent?: string;
|
|
44
|
+
text: string;
|
|
45
|
+
}
|
|
46
|
+
export interface ApplyPolicyTextResult {
|
|
47
|
+
text: string;
|
|
48
|
+
removedCount: number;
|
|
49
|
+
replacedCount: number;
|
|
50
|
+
}
|
|
51
|
+
export declare function applyPolicyToText(input: ApplyPolicyTextInput): ApplyPolicyTextResult;
|
|
52
|
+
export declare function applyPolicyToValue(input: ApplyPolicyValueInput): ApplyPolicyValueResult;
|
|
53
|
+
//# sourceMappingURL=applySecretsPolicy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"applySecretsPolicy.d.ts","sourceRoot":"","sources":["../../src/utils/applySecretsPolicy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AASH,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEtE,eAAO,MAAM,+BAA+B,yBAAyB,CAAC;AAEtE,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,gBAAgB,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oFAAoF;IACpF,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,aAAa,CAAC;IACtB,MAAM,EAAE,gBAAgB,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACvB;AAwFD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,oBAAoB,GAAG,qBAAqB,CAsCpF;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,qBAAqB,GAAG,sBAAsB,CA4DvF"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 30.3 (Task 2.2): apply the three `secretsPolicy` modes to a payload
|
|
3
|
+
* before it is written into the bundle.
|
|
4
|
+
*
|
|
5
|
+
* Two operating shapes, mirroring `detectSecretsInValue` / `detectSecretsInText`:
|
|
6
|
+
*
|
|
7
|
+
* 1. `applyPolicyToValue` walks a JSON-like value (mcp config, settings.json
|
|
8
|
+
* hooks block). For `excluded` the matched leaf is deleted from its
|
|
9
|
+
* parent; for `placeholder` it is rewritten as `${ENV_REF_NAME}` using
|
|
10
|
+
* `secretPlaceholderNamer`; for `included-explicit` the value is left
|
|
11
|
+
* untouched.
|
|
12
|
+
*
|
|
13
|
+
* 2. `applyPolicyToText` walks line-oriented text (CLAUDE.md, agent body,
|
|
14
|
+
* command body). For `excluded` the offending line is replaced with the
|
|
15
|
+
* sentinel `<< SECRET REMOVED >>` so line numbers stay stable; for
|
|
16
|
+
* `placeholder` every secret substring on that line is rewritten as
|
|
17
|
+
* `${ENV_REF_NAME}`; for `included-explicit` the text is unchanged.
|
|
18
|
+
*
|
|
19
|
+
* Both return per-call counters that the caller aggregates into the
|
|
20
|
+
* `ImportApplySummary` (well, the export equivalent — same idea: surface to
|
|
21
|
+
* the user how many secrets were affected).
|
|
22
|
+
*/
|
|
23
|
+
import { ENV_REF_RE, SECRET_PATTERNS, shannonEntropy, } from './secretHeuristic.js';
|
|
24
|
+
import { namePlaceholder } from './secretPlaceholderNamer.js';
|
|
25
|
+
export const SECRET_REMOVED_TEXT_PLACEHOLDER = '<< SECRET REMOVED >>';
|
|
26
|
+
const BASE64_NON_ALPHA_RE = /[0-9+/=]/;
|
|
27
|
+
function matchPasses(s, pat) {
|
|
28
|
+
if (pat.minEntropy === undefined)
|
|
29
|
+
return true;
|
|
30
|
+
return shannonEntropy(s) >= pat.minEntropy && BASE64_NON_ALPHA_RE.test(s);
|
|
31
|
+
}
|
|
32
|
+
/** Iterate every secret substring inside `text`. */
|
|
33
|
+
function* iterateSecretMatches(text) {
|
|
34
|
+
for (const pat of SECRET_PATTERNS) {
|
|
35
|
+
const re = pat.re.global ? pat.re : new RegExp(pat.re.source, pat.re.flags + 'g');
|
|
36
|
+
re.lastIndex = 0;
|
|
37
|
+
let m;
|
|
38
|
+
while ((m = re.exec(text)) !== null) {
|
|
39
|
+
const raw = m[0];
|
|
40
|
+
if (matchPasses(raw, pat)) {
|
|
41
|
+
yield { start: m.index, end: m.index + raw.length, raw, pat };
|
|
42
|
+
}
|
|
43
|
+
if (m.index === re.lastIndex)
|
|
44
|
+
re.lastIndex += 1;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function stripEnvRefs(text) {
|
|
49
|
+
return text.replace(ENV_REF_RE, '');
|
|
50
|
+
}
|
|
51
|
+
function containsSecret(text) {
|
|
52
|
+
const stripped = stripEnvRefs(text);
|
|
53
|
+
for (const _ of iterateSecretMatches(stripped)) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Replace every secret substring inside `text` with the ENV-ref produced by
|
|
60
|
+
* the namer. Matches on the env-stripped clone first so existing `${FOO}`
|
|
61
|
+
* indirection isn't double-replaced.
|
|
62
|
+
*/
|
|
63
|
+
function replaceSecretsInText(text, makeName) {
|
|
64
|
+
// We need the original text positions but the secret-detection regex must
|
|
65
|
+
// not double-count existing ENV refs. Strategy: collect matches against the
|
|
66
|
+
// env-stripped clone, then translate offsets back to the original by
|
|
67
|
+
// walking through both strings in parallel.
|
|
68
|
+
const stripped = stripEnvRefs(text);
|
|
69
|
+
if (stripped === text) {
|
|
70
|
+
return rebuildText(text, makeName);
|
|
71
|
+
}
|
|
72
|
+
// When there were ENV refs, the safest fallback is to scan the original text
|
|
73
|
+
// directly. Existing `${FOO}` snippets won't match `SECRET_PATTERNS` (none of
|
|
74
|
+
// the regexes accept `{` or `}` in the middle), so direct scanning is fine.
|
|
75
|
+
return rebuildText(text, makeName);
|
|
76
|
+
}
|
|
77
|
+
function rebuildText(text, makeName) {
|
|
78
|
+
const matches = [];
|
|
79
|
+
for (const m of iterateSecretMatches(text)) {
|
|
80
|
+
matches.push({ start: m.start, end: m.end, raw: m.raw });
|
|
81
|
+
}
|
|
82
|
+
if (matches.length === 0)
|
|
83
|
+
return { text, replacedCount: 0 };
|
|
84
|
+
// Sort by start ascending, drop overlaps (keep first).
|
|
85
|
+
matches.sort((a, b) => a.start - b.start);
|
|
86
|
+
const nonOverlapping = [];
|
|
87
|
+
let lastEnd = -1;
|
|
88
|
+
for (const m of matches) {
|
|
89
|
+
if (m.start >= lastEnd) {
|
|
90
|
+
nonOverlapping.push(m);
|
|
91
|
+
lastEnd = m.end;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
let out = '';
|
|
95
|
+
let cursor = 0;
|
|
96
|
+
let replaced = 0;
|
|
97
|
+
for (const m of nonOverlapping) {
|
|
98
|
+
out += text.slice(cursor, m.start);
|
|
99
|
+
out += '${' + makeName(m.raw, replaced) + '}';
|
|
100
|
+
cursor = m.end;
|
|
101
|
+
replaced += 1;
|
|
102
|
+
}
|
|
103
|
+
out += text.slice(cursor);
|
|
104
|
+
return { text: out, replacedCount: replaced };
|
|
105
|
+
}
|
|
106
|
+
export function applyPolicyToText(input) {
|
|
107
|
+
const { policy, domain, cardName, hookEvent, text } = input;
|
|
108
|
+
if (policy === 'included-explicit') {
|
|
109
|
+
return { text, removedCount: 0, replacedCount: 0 };
|
|
110
|
+
}
|
|
111
|
+
if (!text) {
|
|
112
|
+
return { text, removedCount: 0, replacedCount: 0 };
|
|
113
|
+
}
|
|
114
|
+
if (policy === 'excluded') {
|
|
115
|
+
// Replace each offending line with the sentinel so line numbers stay
|
|
116
|
+
// stable for downstream tools that index by line.
|
|
117
|
+
const lines = text.split(/\r?\n/);
|
|
118
|
+
let removed = 0;
|
|
119
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
120
|
+
if (containsSecret(lines[i])) {
|
|
121
|
+
lines[i] = SECRET_REMOVED_TEXT_PLACEHOLDER;
|
|
122
|
+
removed += 1;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return { text: lines.join('\n'), removedCount: removed, replacedCount: 0 };
|
|
126
|
+
}
|
|
127
|
+
// placeholder
|
|
128
|
+
const seenCounts = new Map();
|
|
129
|
+
const result = replaceSecretsInText(text, (_raw, idx) => {
|
|
130
|
+
const base = {
|
|
131
|
+
domain,
|
|
132
|
+
fieldPath: `body:${idx}`,
|
|
133
|
+
cardName,
|
|
134
|
+
hookEvent,
|
|
135
|
+
};
|
|
136
|
+
const baseName = namePlaceholder(base);
|
|
137
|
+
const count = seenCounts.get(baseName) ?? 0;
|
|
138
|
+
seenCounts.set(baseName, count + 1);
|
|
139
|
+
return count === 0 ? baseName : `${baseName}_${count + 1}`;
|
|
140
|
+
});
|
|
141
|
+
return { text: result.text, removedCount: 0, replacedCount: result.replacedCount };
|
|
142
|
+
}
|
|
143
|
+
export function applyPolicyToValue(input) {
|
|
144
|
+
const { policy, domain, cardName, hookEvent, value } = input;
|
|
145
|
+
if (policy === 'included-explicit') {
|
|
146
|
+
return { value, removedCount: 0, replacedCount: 0 };
|
|
147
|
+
}
|
|
148
|
+
const pathPrefix = input.pathPrefix ?? [];
|
|
149
|
+
let removedCount = 0;
|
|
150
|
+
let replacedCount = 0;
|
|
151
|
+
const seenCounts = new Map();
|
|
152
|
+
const transform = (v, p) => {
|
|
153
|
+
if (typeof v === 'string') {
|
|
154
|
+
const stripped = stripEnvRefs(v);
|
|
155
|
+
if (!stripped || !containsSecret(stripped)) {
|
|
156
|
+
return v;
|
|
157
|
+
}
|
|
158
|
+
if (policy === 'excluded') {
|
|
159
|
+
removedCount += 1;
|
|
160
|
+
// Return a sentinel that the caller (e.g. the mcp/hook config walker)
|
|
161
|
+
// will interpret as "delete this leaf" — strings, by themselves, can't
|
|
162
|
+
// disappear from their parent. We use the literal undefined sentinel
|
|
163
|
+
// and let the array/object handlers filter.
|
|
164
|
+
return SECRET_REMOVED_SENTINEL;
|
|
165
|
+
}
|
|
166
|
+
// placeholder
|
|
167
|
+
const baseName = namePlaceholder({
|
|
168
|
+
domain,
|
|
169
|
+
cardName,
|
|
170
|
+
hookEvent,
|
|
171
|
+
fieldPath: p.join('.'),
|
|
172
|
+
});
|
|
173
|
+
const count = seenCounts.get(baseName) ?? 0;
|
|
174
|
+
seenCounts.set(baseName, count + 1);
|
|
175
|
+
const name = count === 0 ? baseName : `${baseName}_${count + 1}`;
|
|
176
|
+
replacedCount += 1;
|
|
177
|
+
return '${' + name + '}';
|
|
178
|
+
}
|
|
179
|
+
if (Array.isArray(v)) {
|
|
180
|
+
return v
|
|
181
|
+
.map((item, i) => transform(item, [...p, String(i)]))
|
|
182
|
+
.filter((item) => item !== SECRET_REMOVED_SENTINEL);
|
|
183
|
+
}
|
|
184
|
+
if (v && typeof v === 'object') {
|
|
185
|
+
const out = {};
|
|
186
|
+
for (const [k, child] of Object.entries(v)) {
|
|
187
|
+
const next = transform(child, [...p, k]);
|
|
188
|
+
if (next !== SECRET_REMOVED_SENTINEL)
|
|
189
|
+
out[k] = next;
|
|
190
|
+
}
|
|
191
|
+
return out;
|
|
192
|
+
}
|
|
193
|
+
return v;
|
|
194
|
+
};
|
|
195
|
+
const result = transform(value, pathPrefix);
|
|
196
|
+
// Top-level scalar might be the sentinel — flatten to undefined for callers.
|
|
197
|
+
return {
|
|
198
|
+
value: result === SECRET_REMOVED_SENTINEL ? undefined : result,
|
|
199
|
+
removedCount,
|
|
200
|
+
replacedCount,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
const SECRET_REMOVED_SENTINEL = Symbol('secret-removed');
|
|
204
|
+
//# sourceMappingURL=applySecretsPolicy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"applySecretsPolicy.js","sourceRoot":"","sources":["../../src/utils/applySecretsPolicy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 30.1 (Task 6.2): server-side guard that escalates a secret detection
|
|
3
|
+
* to a hard `HARNESS_SECRET_ON_SHARED` block when the target file is tracked
|
|
4
|
+
* by git (`shared` verdict from `harnessShareScopeService`).
|
|
5
|
+
*
|
|
6
|
+
* Sub-spike 6.2.0 decision: option (b) — distributed at the four domain
|
|
7
|
+
* services (agent / command / hook / mcp). Rationale:
|
|
8
|
+
* - `harnessService.write` IS a single common call site, so option (a)
|
|
9
|
+
* would also work for the raw write path. But AC4.d carves out
|
|
10
|
+
* domain-specific scopes (settings.json values, hook command/prompt
|
|
11
|
+
* fields, agent frontmatter) that the single point cannot meaningfully
|
|
12
|
+
* filter without re-parsing the file. Distributing the helper keeps each
|
|
13
|
+
* domain's existing per-field detection in place and just adds the
|
|
14
|
+
* "shared → block" escalation on top.
|
|
15
|
+
*
|
|
16
|
+
* The helper performs the cheap path-classification round trip
|
|
17
|
+
* (`harnessShareScopeService.evaluate` is a single `.gitignore` load + one
|
|
18
|
+
* `isIgnored()` call). User-scope writes are no-ops (no `.gitignore`
|
|
19
|
+
* involved).
|
|
20
|
+
*/
|
|
21
|
+
import { type HarnessScope } from '@hammoc/shared';
|
|
22
|
+
export interface AssertNoSecretOnSharedInput {
|
|
23
|
+
scope: HarnessScope;
|
|
24
|
+
projectSlug?: string;
|
|
25
|
+
/** Project-relative POSIX path of the file being written (e.g. `.claude/agents/dev.md`). */
|
|
26
|
+
relativePath: string;
|
|
27
|
+
/**
|
|
28
|
+
* Pre-computed detection result from the calling service. The helper does
|
|
29
|
+
* not re-run secret detection because each service knows which fields are
|
|
30
|
+
* in-scope per AC4.d.
|
|
31
|
+
*/
|
|
32
|
+
secretDetected: boolean;
|
|
33
|
+
/** Optional secret locations (line numbers / dot-paths) to surface in the error. */
|
|
34
|
+
detectedAt?: {
|
|
35
|
+
lines?: number[];
|
|
36
|
+
paths?: string[];
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export declare function assertNoSecretOnShared(input: AssertNoSecretOnSharedInput): Promise<void>;
|
|
40
|
+
//# sourceMappingURL=assertNoSecretOnShared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assertNoSecretOnShared.d.ts","sourceRoot":"","sources":["../../src/utils/assertNoSecretOnShared.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAGnE,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,YAAY,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4FAA4F;IAC5F,YAAY,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,cAAc,EAAE,OAAO,CAAC;IACxB,oFAAoF;IACpF,UAAU,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CACrD;AAED,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,2BAA2B,GACjC,OAAO,CAAC,IAAI,CAAC,CAsBf"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 30.1 (Task 6.2): server-side guard that escalates a secret detection
|
|
3
|
+
* to a hard `HARNESS_SECRET_ON_SHARED` block when the target file is tracked
|
|
4
|
+
* by git (`shared` verdict from `harnessShareScopeService`).
|
|
5
|
+
*
|
|
6
|
+
* Sub-spike 6.2.0 decision: option (b) — distributed at the four domain
|
|
7
|
+
* services (agent / command / hook / mcp). Rationale:
|
|
8
|
+
* - `harnessService.write` IS a single common call site, so option (a)
|
|
9
|
+
* would also work for the raw write path. But AC4.d carves out
|
|
10
|
+
* domain-specific scopes (settings.json values, hook command/prompt
|
|
11
|
+
* fields, agent frontmatter) that the single point cannot meaningfully
|
|
12
|
+
* filter without re-parsing the file. Distributing the helper keeps each
|
|
13
|
+
* domain's existing per-field detection in place and just adds the
|
|
14
|
+
* "shared → block" escalation on top.
|
|
15
|
+
*
|
|
16
|
+
* The helper performs the cheap path-classification round trip
|
|
17
|
+
* (`harnessShareScopeService.evaluate` is a single `.gitignore` load + one
|
|
18
|
+
* `isIgnored()` call). User-scope writes are no-ops (no `.gitignore`
|
|
19
|
+
* involved).
|
|
20
|
+
*/
|
|
21
|
+
import { HARNESS_ERRORS } from '@hammoc/shared';
|
|
22
|
+
import { harnessShareScopeService } from '../services/harnessShareScopeService.js';
|
|
23
|
+
export async function assertNoSecretOnShared(input) {
|
|
24
|
+
if (!input.secretDetected)
|
|
25
|
+
return;
|
|
26
|
+
// User scope (`~/.claude`) is not git-tracked — `.gitignore` does not apply.
|
|
27
|
+
if (input.scope !== 'project' || !input.projectSlug)
|
|
28
|
+
return;
|
|
29
|
+
const result = await harnessShareScopeService.evaluate({
|
|
30
|
+
projectSlug: input.projectSlug,
|
|
31
|
+
relativePaths: [input.relativePath],
|
|
32
|
+
});
|
|
33
|
+
const verdict = result.cards[input.relativePath];
|
|
34
|
+
// Only `shared` triggers the hard block. `local` / `fullyIgnored` keep
|
|
35
|
+
// the existing per-service acknowledgement flow.
|
|
36
|
+
if (verdict !== 'shared')
|
|
37
|
+
return;
|
|
38
|
+
const err = new Error('plaintext secret detected on a git-tracked file');
|
|
39
|
+
err.code = HARNESS_ERRORS.HARNESS_SECRET_ON_SHARED.code;
|
|
40
|
+
err.relativePath = input.relativePath;
|
|
41
|
+
if (input.detectedAt?.lines)
|
|
42
|
+
err.lines = input.detectedAt.lines;
|
|
43
|
+
if (input.detectedAt?.paths)
|
|
44
|
+
err.paths = input.detectedAt.paths;
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=assertNoSecretOnShared.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assertNoSecretOnShared.js","sourceRoot":"","sources":["../../src/utils/assertNoSecretOnShared.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,cAAc,EAAqB,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAC;AAiBnF,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,KAAkC;IAElC,IAAI,CAAC,KAAK,CAAC,cAAc;QAAE,OAAO;IAClC,6EAA6E;IAC7E,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,WAAW;QAAE,OAAO;IAE5D,MAAM,MAAM,GAAG,MAAM,wBAAwB,CAAC,QAAQ,CAAC;QACrD,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,aAAa,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC;KACpC,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IAEjD,uEAAuE;IACvE,iDAAiD;IACjD,IAAI,OAAO,KAAK,QAAQ;QAAE,OAAO;IAEjC,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,iDAAiD,CACN,CAAC;IAClE,GAAG,CAAC,IAAI,GAAG,cAAc,CAAC,wBAAwB,CAAC,IAAI,CAAC;IACxD,GAAG,CAAC,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC;IACtC,IAAI,KAAK,CAAC,UAAU,EAAE,KAAK;QAAE,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC;IAChE,IAAI,KAAK,CAAC,UAAU,EAAE,KAAK;QAAE,GAAG,CAAC,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC;IAChE,MAAM,GAAG,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 28.0.5 (Task 5): thin wrapper around `ignore` (kaelzhang/node-ignore).
|
|
3
|
+
*
|
|
4
|
+
* Originally introduced as preemptive infrastructure under the placeholder
|
|
5
|
+
* label "Epic 30 Story 10" (the pre-BMad-renumbering forward reference);
|
|
6
|
+
* Story 30.1 fills the call-sites (`harnessShareScopeService` + the
|
|
7
|
+
* client-side `ShareBadge` / `ModeBanner`) by consuming `loadGitignore()` +
|
|
8
|
+
* `isIgnored()` to drive the "shared vs. local vs. fully-ignored" badges on
|
|
9
|
+
* the harness workbench tree.
|
|
10
|
+
*/
|
|
11
|
+
import { type Ignore } from 'ignore';
|
|
12
|
+
/**
|
|
13
|
+
* Load `<rootPath>/.gitignore` into an `Ignore` matcher. Missing files yield
|
|
14
|
+
* a matcher that never excludes anything (so callers can treat "no
|
|
15
|
+
* .gitignore" as "nothing is ignored" without a separate branch).
|
|
16
|
+
*/
|
|
17
|
+
export declare function loadGitignore(rootPath: string): Promise<Ignore>;
|
|
18
|
+
/**
|
|
19
|
+
* Test a relative path against the matcher. Input paths are normalized to
|
|
20
|
+
* POSIX separators as required by `ignore`'s contract.
|
|
21
|
+
*/
|
|
22
|
+
export declare function isIgnored(matcher: Ignore, relativePath: string): boolean;
|
|
23
|
+
//# sourceMappingURL=gitignoreFilter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitignoreFilter.d.ts","sourceRoot":"","sources":["../../src/utils/gitignoreFilter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAsB,EAAE,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEpD;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAarE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAGxE"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 28.0.5 (Task 5): thin wrapper around `ignore` (kaelzhang/node-ignore).
|
|
3
|
+
*
|
|
4
|
+
* Originally introduced as preemptive infrastructure under the placeholder
|
|
5
|
+
* label "Epic 30 Story 10" (the pre-BMad-renumbering forward reference);
|
|
6
|
+
* Story 30.1 fills the call-sites (`harnessShareScopeService` + the
|
|
7
|
+
* client-side `ShareBadge` / `ModeBanner`) by consuming `loadGitignore()` +
|
|
8
|
+
* `isIgnored()` to drive the "shared vs. local vs. fully-ignored" badges on
|
|
9
|
+
* the harness workbench tree.
|
|
10
|
+
*/
|
|
11
|
+
import fs from 'fs/promises';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import ignoreFactory from 'ignore';
|
|
14
|
+
/**
|
|
15
|
+
* Load `<rootPath>/.gitignore` into an `Ignore` matcher. Missing files yield
|
|
16
|
+
* a matcher that never excludes anything (so callers can treat "no
|
|
17
|
+
* .gitignore" as "nothing is ignored" without a separate branch).
|
|
18
|
+
*/
|
|
19
|
+
export async function loadGitignore(rootPath) {
|
|
20
|
+
const matcher = ignoreFactory();
|
|
21
|
+
const gitignorePath = path.join(rootPath, '.gitignore');
|
|
22
|
+
try {
|
|
23
|
+
const content = await fs.readFile(gitignorePath, 'utf-8');
|
|
24
|
+
matcher.add(content);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
if (error.code !== 'ENOENT') {
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
// No .gitignore — return an empty matcher.
|
|
31
|
+
}
|
|
32
|
+
return matcher;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Test a relative path against the matcher. Input paths are normalized to
|
|
36
|
+
* POSIX separators as required by `ignore`'s contract.
|
|
37
|
+
*/
|
|
38
|
+
export function isIgnored(matcher, relativePath) {
|
|
39
|
+
const posix = relativePath.replace(/\\/g, '/');
|
|
40
|
+
return matcher.ignores(posix);
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=gitignoreFilter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitignoreFilter.js","sourceRoot":"","sources":["../../src/utils/gitignoreFilter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,aAAa,CAAC;AAC7B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,aAA8B,MAAM,QAAQ,CAAC;AAEpD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAgB;IAClD,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;IAChC,MAAM,aAAa,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IACxD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QAC1D,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvD,MAAM,KAAK,CAAC;QACd,CAAC;QACD,2CAA2C;IAC7C,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,OAAe,EAAE,YAAoB;IAC7D,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC/C,OAAO,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 30.3 (Task 4.2): Zod runtime validation of `manifest.json` inside the
|
|
3
|
+
* harness export/import bundle. Lives on the server because zod is a
|
|
4
|
+
* server-only dep — the shared package exposes only the TypeScript surface
|
|
5
|
+
* (`packages/shared/src/types/harnessBundle.ts`).
|
|
6
|
+
*
|
|
7
|
+
* Two boundaries call this schema:
|
|
8
|
+
* - the bundle service when unpacking an import ZIP (AC5.c "malformed
|
|
9
|
+
* manifest" branch)
|
|
10
|
+
* - the controller when accepting an export request body (AC1, AC2, AC5)
|
|
11
|
+
*
|
|
12
|
+
* Both reuse the discriminator/value sets exported from `@hammoc/shared` so
|
|
13
|
+
* the schema and types stay in lockstep.
|
|
14
|
+
*/
|
|
15
|
+
import { z } from 'zod';
|
|
16
|
+
/**
|
|
17
|
+
* Strict manifest schema — `bundleVersion` is locked to the literal 1 so
|
|
18
|
+
* future-version bundles fall through to the AC5.a "futureBundle" branch
|
|
19
|
+
* before this schema is even consulted (the import service inspects the raw
|
|
20
|
+
* JSON's version field first, then runs full Zod validation).
|
|
21
|
+
*/
|
|
22
|
+
export declare const bundleManifestSchema: z.ZodObject<{
|
|
23
|
+
bundleVersion: z.ZodLiteral<1>;
|
|
24
|
+
hammocVersion: z.ZodString;
|
|
25
|
+
claudeCodeSpecVersion: z.ZodUnion<readonly [z.ZodString, z.ZodNull]>;
|
|
26
|
+
createdAt: z.ZodString;
|
|
27
|
+
sourceProjectSlug: z.ZodString;
|
|
28
|
+
includes: z.ZodArray<z.ZodEnum<{
|
|
29
|
+
agents: "agents";
|
|
30
|
+
hooks: "hooks";
|
|
31
|
+
commands: "commands";
|
|
32
|
+
skills: "skills";
|
|
33
|
+
mcp: "mcp";
|
|
34
|
+
"claude-md": "claude-md";
|
|
35
|
+
bmad: "bmad";
|
|
36
|
+
}>>;
|
|
37
|
+
secretsPolicy: z.ZodEnum<{
|
|
38
|
+
excluded: "excluded";
|
|
39
|
+
placeholder: "placeholder";
|
|
40
|
+
"included-explicit": "included-explicit";
|
|
41
|
+
}>;
|
|
42
|
+
pluginDependencies: z.ZodArray<z.ZodObject<{
|
|
43
|
+
name: z.ZodString;
|
|
44
|
+
marketplace: z.ZodString;
|
|
45
|
+
version: z.ZodOptional<z.ZodString>;
|
|
46
|
+
}, z.core.$strip>>;
|
|
47
|
+
items: z.ZodArray<z.ZodObject<{
|
|
48
|
+
domain: z.ZodEnum<{
|
|
49
|
+
agent: "agent";
|
|
50
|
+
command: "command";
|
|
51
|
+
skill: "skill";
|
|
52
|
+
mcp: "mcp";
|
|
53
|
+
hook: "hook";
|
|
54
|
+
"claude-md": "claude-md";
|
|
55
|
+
bmad: "bmad";
|
|
56
|
+
}>;
|
|
57
|
+
identity: z.ZodString;
|
|
58
|
+
relativePath: z.ZodString;
|
|
59
|
+
sourceShareScope: z.ZodEnum<{
|
|
60
|
+
local: "local";
|
|
61
|
+
shared: "shared";
|
|
62
|
+
fullyIgnored: "fullyIgnored";
|
|
63
|
+
}>;
|
|
64
|
+
}, z.core.$strip>>;
|
|
65
|
+
}, z.core.$strip>;
|
|
66
|
+
export type BundleManifestParsed = z.infer<typeof bundleManifestSchema>;
|
|
67
|
+
/**
|
|
68
|
+
* Loose schema used for "is this even a manifest?" detection before the
|
|
69
|
+
* strict schema runs. Lets the import service distinguish "totally garbage
|
|
70
|
+
* JSON" (no bundleVersion field at all) from "future bundle" (bundleVersion
|
|
71
|
+
* is a number outside our supported range).
|
|
72
|
+
*/
|
|
73
|
+
export declare const loosenedManifestSchema: z.ZodObject<{
|
|
74
|
+
bundleVersion: z.ZodOptional<z.ZodNumber>;
|
|
75
|
+
}, z.core.$strip>;
|
|
76
|
+
export declare const exportBundleRequestSchema: z.ZodObject<{
|
|
77
|
+
projectSlug: z.ZodString;
|
|
78
|
+
includes: z.ZodArray<z.ZodEnum<{
|
|
79
|
+
agents: "agents";
|
|
80
|
+
hooks: "hooks";
|
|
81
|
+
commands: "commands";
|
|
82
|
+
skills: "skills";
|
|
83
|
+
mcp: "mcp";
|
|
84
|
+
"claude-md": "claude-md";
|
|
85
|
+
bmad: "bmad";
|
|
86
|
+
}>>;
|
|
87
|
+
secretsPolicy: z.ZodEnum<{
|
|
88
|
+
excluded: "excluded";
|
|
89
|
+
placeholder: "placeholder";
|
|
90
|
+
"included-explicit": "included-explicit";
|
|
91
|
+
}>;
|
|
92
|
+
}, z.core.$strip>;
|
|
93
|
+
export declare const importApplyRequestSchema: z.ZodObject<{
|
|
94
|
+
bundleToken: z.ZodString;
|
|
95
|
+
itemActions: z.ZodRecord<z.ZodString, z.ZodEnum<{
|
|
96
|
+
rename: "rename";
|
|
97
|
+
overwrite: "overwrite";
|
|
98
|
+
skip: "skip";
|
|
99
|
+
appendSection: "appendSection";
|
|
100
|
+
}>>;
|
|
101
|
+
}, z.core.$strip>;
|
|
102
|
+
export declare const pluginDepsQuerySchema: z.ZodObject<{
|
|
103
|
+
projectSlug: z.ZodString;
|
|
104
|
+
}, z.core.$strip>;
|
|
105
|
+
//# sourceMappingURL=harnessBundleSchema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harnessBundleSchema.d.ts","sourceRoot":"","sources":["../../src/utils/harnessBundleSchema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA0CxB;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAU/B,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAExE;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB;;iBAEjC,CAAC;AAEH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;iBAIpC,CAAC;AAEH,eAAO,MAAM,wBAAwB;;;;;;;;iBAGnC,CAAC;AAEH,eAAO,MAAM,qBAAqB;;iBAEhC,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Story 30.3 (Task 4.2): Zod runtime validation of `manifest.json` inside the
|
|
3
|
+
* harness export/import bundle. Lives on the server because zod is a
|
|
4
|
+
* server-only dep — the shared package exposes only the TypeScript surface
|
|
5
|
+
* (`packages/shared/src/types/harnessBundle.ts`).
|
|
6
|
+
*
|
|
7
|
+
* Two boundaries call this schema:
|
|
8
|
+
* - the bundle service when unpacking an import ZIP (AC5.c "malformed
|
|
9
|
+
* manifest" branch)
|
|
10
|
+
* - the controller when accepting an export request body (AC1, AC2, AC5)
|
|
11
|
+
*
|
|
12
|
+
* Both reuse the discriminator/value sets exported from `@hammoc/shared` so
|
|
13
|
+
* the schema and types stay in lockstep.
|
|
14
|
+
*/
|
|
15
|
+
import { z } from 'zod';
|
|
16
|
+
import { BUNDLE_SECTIONS, HARNESS_BUNDLE_VERSION, } from '@hammoc/shared';
|
|
17
|
+
const sectionSchema = z.enum(BUNDLE_SECTIONS);
|
|
18
|
+
const secretsPolicySchema = z.enum(['excluded', 'placeholder', 'included-explicit']);
|
|
19
|
+
const itemDomainSchema = z.enum([
|
|
20
|
+
'claude-md',
|
|
21
|
+
'skill',
|
|
22
|
+
'mcp',
|
|
23
|
+
'hook',
|
|
24
|
+
'command',
|
|
25
|
+
'agent',
|
|
26
|
+
'bmad',
|
|
27
|
+
]);
|
|
28
|
+
const sourceShareScopeSchema = z.enum(['shared', 'local', 'fullyIgnored']);
|
|
29
|
+
const itemActionSchema = z.enum(['overwrite', 'skip', 'rename', 'appendSection']);
|
|
30
|
+
const pluginRefSchema = z.object({
|
|
31
|
+
name: z.string().min(1),
|
|
32
|
+
marketplace: z.string().min(1),
|
|
33
|
+
version: z.string().optional(),
|
|
34
|
+
});
|
|
35
|
+
const bundleItemSchema = z.object({
|
|
36
|
+
domain: itemDomainSchema,
|
|
37
|
+
identity: z.string().min(1),
|
|
38
|
+
relativePath: z.string().min(1),
|
|
39
|
+
sourceShareScope: sourceShareScopeSchema,
|
|
40
|
+
});
|
|
41
|
+
/**
|
|
42
|
+
* Strict manifest schema — `bundleVersion` is locked to the literal 1 so
|
|
43
|
+
* future-version bundles fall through to the AC5.a "futureBundle" branch
|
|
44
|
+
* before this schema is even consulted (the import service inspects the raw
|
|
45
|
+
* JSON's version field first, then runs full Zod validation).
|
|
46
|
+
*/
|
|
47
|
+
export const bundleManifestSchema = z.object({
|
|
48
|
+
bundleVersion: z.literal(HARNESS_BUNDLE_VERSION),
|
|
49
|
+
hammocVersion: z.string().min(1),
|
|
50
|
+
claudeCodeSpecVersion: z.union([z.string().min(1), z.null()]),
|
|
51
|
+
createdAt: z.string().min(1),
|
|
52
|
+
sourceProjectSlug: z.string().min(1),
|
|
53
|
+
includes: z.array(sectionSchema),
|
|
54
|
+
secretsPolicy: secretsPolicySchema,
|
|
55
|
+
pluginDependencies: z.array(pluginRefSchema),
|
|
56
|
+
items: z.array(bundleItemSchema),
|
|
57
|
+
});
|
|
58
|
+
/**
|
|
59
|
+
* Loose schema used for "is this even a manifest?" detection before the
|
|
60
|
+
* strict schema runs. Lets the import service distinguish "totally garbage
|
|
61
|
+
* JSON" (no bundleVersion field at all) from "future bundle" (bundleVersion
|
|
62
|
+
* is a number outside our supported range).
|
|
63
|
+
*/
|
|
64
|
+
export const loosenedManifestSchema = z.object({
|
|
65
|
+
bundleVersion: z.number().int().optional(),
|
|
66
|
+
});
|
|
67
|
+
export const exportBundleRequestSchema = z.object({
|
|
68
|
+
projectSlug: z.string().min(1),
|
|
69
|
+
includes: z.array(sectionSchema).min(1, 'at least one section is required'),
|
|
70
|
+
secretsPolicy: secretsPolicySchema,
|
|
71
|
+
});
|
|
72
|
+
export const importApplyRequestSchema = z.object({
|
|
73
|
+
bundleToken: z.string().min(1),
|
|
74
|
+
itemActions: z.record(z.string(), itemActionSchema),
|
|
75
|
+
});
|
|
76
|
+
export const pluginDepsQuerySchema = z.object({
|
|
77
|
+
projectSlug: z.string().min(1),
|
|
78
|
+
});
|
|
79
|
+
//# sourceMappingURL=harnessBundleSchema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harnessBundleSchema.js","sourceRoot":"","sources":["../../src/utils/harnessBundleSchema.ts"],"names":[],"mappings":"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"}
|