audit-tools 0.32.44 → 0.32.46

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.
Files changed (204) hide show
  1. package/dist/audit/cli/args.d.ts.map +1 -1
  2. package/dist/audit/cli/args.js +2 -4
  3. package/dist/audit/cli/args.js.map +1 -1
  4. package/dist/audit/cli/auditStep.d.ts +1 -1
  5. package/dist/audit/cli/dispatch.d.ts +1 -0
  6. package/dist/audit/cli/dispatch.d.ts.map +1 -1
  7. package/dist/audit/cli/dispatch.js +39 -18
  8. package/dist/audit/cli/dispatch.js.map +1 -1
  9. package/dist/audit/cli/mergeAndIngestCommand.d.ts +41 -0
  10. package/dist/audit/cli/mergeAndIngestCommand.d.ts.map +1 -1
  11. package/dist/audit/cli/mergeAndIngestCommand.js +105 -18
  12. package/dist/audit/cli/mergeAndIngestCommand.js.map +1 -1
  13. package/dist/audit/cli/nextStepCommand.d.ts.map +1 -1
  14. package/dist/audit/cli/nextStepCommand.js +3 -3
  15. package/dist/audit/cli/nextStepCommand.js.map +1 -1
  16. package/dist/audit/cli/nextStepHelpers.d.ts +23 -38
  17. package/dist/audit/cli/nextStepHelpers.d.ts.map +1 -1
  18. package/dist/audit/cli/nextStepHelpers.js +130 -89
  19. package/dist/audit/cli/nextStepHelpers.js.map +1 -1
  20. package/dist/audit/cli/ownerTokens.d.ts +21 -0
  21. package/dist/audit/cli/ownerTokens.d.ts.map +1 -0
  22. package/dist/audit/cli/ownerTokens.js +86 -0
  23. package/dist/audit/cli/ownerTokens.js.map +1 -0
  24. package/dist/audit/cli/resynthesizeCommand.d.ts.map +1 -1
  25. package/dist/audit/cli/resynthesizeCommand.js +8 -10
  26. package/dist/audit/cli/resynthesizeCommand.js.map +1 -1
  27. package/dist/audit/cli/rollingAuditDispatch.d.ts.map +1 -1
  28. package/dist/audit/cli/rollingAuditDispatch.js +9 -22
  29. package/dist/audit/cli/rollingAuditDispatch.js.map +1 -1
  30. package/dist/audit/contracts/wrapperResponse.d.ts +6 -6
  31. package/dist/audit/extractors/gitHistory.d.ts.map +1 -1
  32. package/dist/audit/extractors/gitHistory.js +3 -0
  33. package/dist/audit/extractors/gitHistory.js.map +1 -1
  34. package/dist/audit/orchestrator/advance.d.ts +4 -1
  35. package/dist/audit/orchestrator/advance.d.ts.map +1 -1
  36. package/dist/audit/orchestrator/advance.js +185 -36
  37. package/dist/audit/orchestrator/advance.js.map +1 -1
  38. package/dist/audit/orchestrator/fileIntegrity.d.ts.map +1 -1
  39. package/dist/audit/orchestrator/fileIntegrity.js +21 -32
  40. package/dist/audit/orchestrator/fileIntegrity.js.map +1 -1
  41. package/dist/audit/orchestrator/reviewPackets.d.ts.map +1 -1
  42. package/dist/audit/orchestrator/reviewPackets.js +31 -36
  43. package/dist/audit/orchestrator/reviewPackets.js.map +1 -1
  44. package/dist/audit/orchestrator/taskBuilder.d.ts.map +1 -1
  45. package/dist/audit/orchestrator/taskBuilder.js +12 -24
  46. package/dist/audit/orchestrator/taskBuilder.js.map +1 -1
  47. package/dist/audit/providers/opencodeProvider.d.ts +1 -4
  48. package/dist/audit/providers/opencodeProvider.d.ts.map +1 -1
  49. package/dist/audit/providers/opencodeProvider.js +6 -9
  50. package/dist/audit/providers/opencodeProvider.js.map +1 -1
  51. package/dist/audit/reporting/mergeFindings.d.ts.map +1 -1
  52. package/dist/audit/reporting/mergeFindings.js +1 -32
  53. package/dist/audit/reporting/mergeFindings.js.map +1 -1
  54. package/dist/audit/reporting/synthesis.d.ts.map +1 -1
  55. package/dist/audit/reporting/synthesis.js +1 -12
  56. package/dist/audit/reporting/synthesis.js.map +1 -1
  57. package/dist/audit/supervisor/operatorHandoff.d.ts.map +1 -1
  58. package/dist/audit/supervisor/operatorHandoff.js +9 -12
  59. package/dist/audit/supervisor/operatorHandoff.js.map +1 -1
  60. package/dist/audit/supervisor/sessionConfig.d.ts.map +1 -1
  61. package/dist/audit/supervisor/sessionConfig.js +25 -35
  62. package/dist/audit/supervisor/sessionConfig.js.map +1 -1
  63. package/dist/audit/types/auditState.d.ts +6 -6
  64. package/dist/remediate/dedup/crossLensDedup.d.ts +2 -1
  65. package/dist/remediate/dedup/crossLensDedup.d.ts.map +1 -1
  66. package/dist/remediate/dedup/crossLensDedup.js +4 -33
  67. package/dist/remediate/dedup/crossLensDedup.js.map +1 -1
  68. package/dist/remediate/index.d.ts +2 -2
  69. package/dist/remediate/index.d.ts.map +1 -1
  70. package/dist/remediate/index.js +5 -8
  71. package/dist/remediate/index.js.map +1 -1
  72. package/dist/remediate/intake.d.ts.map +1 -1
  73. package/dist/remediate/intake.js +9 -5
  74. package/dist/remediate/intake.js.map +1 -1
  75. package/dist/remediate/phases/close.d.ts +59 -1
  76. package/dist/remediate/phases/close.d.ts.map +1 -1
  77. package/dist/remediate/phases/close.js +294 -29
  78. package/dist/remediate/phases/close.js.map +1 -1
  79. package/dist/remediate/phases/plan.d.ts +2 -2
  80. package/dist/remediate/phases/plan.d.ts.map +1 -1
  81. package/dist/remediate/phases/plan.js +31 -23
  82. package/dist/remediate/phases/plan.js.map +1 -1
  83. package/dist/remediate/providers/opencodeProvider.d.ts +1 -4
  84. package/dist/remediate/providers/opencodeProvider.d.ts.map +1 -1
  85. package/dist/remediate/providers/opencodeProvider.js +6 -9
  86. package/dist/remediate/providers/opencodeProvider.js.map +1 -1
  87. package/dist/remediate/state/store.d.ts +54 -11
  88. package/dist/remediate/state/store.d.ts.map +1 -1
  89. package/dist/remediate/state/store.js +37 -64
  90. package/dist/remediate/state/store.js.map +1 -1
  91. package/dist/remediate/state/types.d.ts +22 -0
  92. package/dist/remediate/state/types.d.ts.map +1 -1
  93. package/dist/remediate/state/types.js +8 -0
  94. package/dist/remediate/state/types.js.map +1 -1
  95. package/dist/remediate/steps/dispatch/acceptNode.d.ts +38 -0
  96. package/dist/remediate/steps/dispatch/acceptNode.d.ts.map +1 -1
  97. package/dist/remediate/steps/dispatch/acceptNode.js +70 -24
  98. package/dist/remediate/steps/dispatch/acceptNode.js.map +1 -1
  99. package/dist/remediate/steps/dispatch/marshal.d.ts.map +1 -1
  100. package/dist/remediate/steps/dispatch/marshal.js +30 -0
  101. package/dist/remediate/steps/dispatch/marshal.js.map +1 -1
  102. package/dist/remediate/steps/dispatch/worktreeLifecycle.d.ts.map +1 -1
  103. package/dist/remediate/steps/dispatch/worktreeLifecycle.js +42 -8
  104. package/dist/remediate/steps/dispatch/worktreeLifecycle.js.map +1 -1
  105. package/dist/remediate/steps/finalGate.d.ts +2 -2
  106. package/dist/remediate/steps/finalGate.d.ts.map +1 -1
  107. package/dist/remediate/steps/finalGate.js +8 -9
  108. package/dist/remediate/steps/finalGate.js.map +1 -1
  109. package/dist/remediate/steps/nextStep.d.ts +12 -0
  110. package/dist/remediate/steps/nextStep.d.ts.map +1 -1
  111. package/dist/remediate/steps/nextStep.js +57 -35
  112. package/dist/remediate/steps/nextStep.js.map +1 -1
  113. package/dist/remediate/steps/rollingSession.d.ts.map +1 -1
  114. package/dist/remediate/steps/rollingSession.js +27 -1
  115. package/dist/remediate/steps/rollingSession.js.map +1 -1
  116. package/dist/remediate/utils/fileIntegrity.d.ts.map +1 -1
  117. package/dist/remediate/utils/fileIntegrity.js +29 -33
  118. package/dist/remediate/utils/fileIntegrity.js.map +1 -1
  119. package/dist/remediate/utils/hostAssets.d.ts +4 -1
  120. package/dist/remediate/utils/hostAssets.d.ts.map +1 -1
  121. package/dist/remediate/utils/hostAssets.js +4 -1
  122. package/dist/remediate/utils/hostAssets.js.map +1 -1
  123. package/dist/remediate/validation/artifacts.d.ts.map +1 -1
  124. package/dist/remediate/validation/artifacts.js +3 -2
  125. package/dist/remediate/validation/artifacts.js.map +1 -1
  126. package/dist/remediate/validation/contractPipeline.d.ts.map +1 -1
  127. package/dist/remediate/validation/contractPipeline.js +3 -1
  128. package/dist/remediate/validation/contractPipeline.js.map +1 -1
  129. package/dist/shared/chunkByBudget.d.ts +58 -0
  130. package/dist/shared/chunkByBudget.d.ts.map +1 -0
  131. package/dist/shared/chunkByBudget.js +31 -0
  132. package/dist/shared/chunkByBudget.js.map +1 -0
  133. package/dist/shared/countBy.d.ts +19 -0
  134. package/dist/shared/countBy.d.ts.map +1 -0
  135. package/dist/shared/countBy.js +29 -0
  136. package/dist/shared/countBy.js.map +1 -0
  137. package/dist/shared/engine/obligationEngine.d.ts +2 -2
  138. package/dist/shared/fileIntegrity.d.ts +55 -0
  139. package/dist/shared/fileIntegrity.d.ts.map +1 -0
  140. package/dist/shared/fileIntegrity.js +50 -0
  141. package/dist/shared/fileIntegrity.js.map +1 -0
  142. package/dist/shared/findingSimilarity.d.ts +26 -0
  143. package/dist/shared/findingSimilarity.d.ts.map +1 -0
  144. package/dist/shared/findingSimilarity.js +54 -0
  145. package/dist/shared/findingSimilarity.js.map +1 -0
  146. package/dist/shared/friction/stepBoundaryCapture.d.ts +17 -0
  147. package/dist/shared/friction/stepBoundaryCapture.d.ts.map +1 -1
  148. package/dist/shared/friction/stepBoundaryCapture.js +21 -0
  149. package/dist/shared/friction/stepBoundaryCapture.js.map +1 -1
  150. package/dist/shared/git.d.ts +23 -0
  151. package/dist/shared/git.d.ts.map +1 -1
  152. package/dist/shared/git.js +26 -0
  153. package/dist/shared/git.js.map +1 -1
  154. package/dist/shared/index.d.ts +11 -4
  155. package/dist/shared/index.d.ts.map +1 -1
  156. package/dist/shared/index.js +22 -3
  157. package/dist/shared/index.js.map +1 -1
  158. package/dist/shared/io/auditToolsPaths.d.ts +7 -0
  159. package/dist/shared/io/auditToolsPaths.d.ts.map +1 -1
  160. package/dist/shared/io/auditToolsPaths.js +10 -0
  161. package/dist/shared/io/auditToolsPaths.js.map +1 -1
  162. package/dist/shared/io/lockedJsonStore.d.ts +66 -0
  163. package/dist/shared/io/lockedJsonStore.d.ts.map +1 -0
  164. package/dist/shared/io/lockedJsonStore.js +59 -0
  165. package/dist/shared/io/lockedJsonStore.js.map +1 -0
  166. package/dist/shared/loopCorePaths.d.ts.map +1 -1
  167. package/dist/shared/loopCorePaths.js +2 -0
  168. package/dist/shared/loopCorePaths.js.map +1 -1
  169. package/dist/shared/providers/openAiCompatibleProvider.d.ts.map +1 -1
  170. package/dist/shared/providers/openAiCompatibleProvider.js +2 -1
  171. package/dist/shared/providers/openAiCompatibleProvider.js.map +1 -1
  172. package/dist/shared/providers/opencodeProvider.d.ts +8 -0
  173. package/dist/shared/providers/opencodeProvider.d.ts.map +1 -1
  174. package/dist/shared/providers/opencodeProvider.js +10 -0
  175. package/dist/shared/providers/opencodeProvider.js.map +1 -1
  176. package/dist/shared/quota/claimRegistry.d.ts +9 -0
  177. package/dist/shared/quota/claimRegistry.d.ts.map +1 -1
  178. package/dist/shared/quota/claimRegistry.js +20 -0
  179. package/dist/shared/quota/claimRegistry.js.map +1 -1
  180. package/dist/shared/reporting/auditDeliverable.d.ts.map +1 -1
  181. package/dist/shared/reporting/auditDeliverable.js +14 -9
  182. package/dist/shared/reporting/auditDeliverable.js.map +1 -1
  183. package/dist/shared/tooling/exec.d.ts +42 -4
  184. package/dist/shared/tooling/exec.d.ts.map +1 -1
  185. package/dist/shared/tooling/exec.js +90 -12
  186. package/dist/shared/tooling/exec.js.map +1 -1
  187. package/dist/shared/types/contractPipeline/verification.d.ts +17 -3
  188. package/dist/shared/types/contractPipeline/verification.d.ts.map +1 -1
  189. package/dist/shared/types/sessionConfig.d.ts +28 -0
  190. package/dist/shared/types/sessionConfig.d.ts.map +1 -1
  191. package/dist/shared/types/sessionConfig.js +35 -0
  192. package/dist/shared/types/sessionConfig.js.map +1 -1
  193. package/opencode.json +2 -11
  194. package/package.json +2 -1
  195. package/scripts/audit/postinstall.mjs +57 -137
  196. package/scripts/remediate/postinstall.mjs +41 -169
  197. package/scripts/shared/install-host-assets.mjs +188 -0
  198. package/wrapper/audit-code-wrapper-lib.mjs +32 -4
  199. package/wrapper/audit-code-wrapper-opencode.mjs +41 -25
  200. package/wrapper/remediate-code-wrapper-opencode.mjs +43 -21
  201. package/dist/remediate/utils/commands.d.ts +0 -17
  202. package/dist/remediate/utils/commands.d.ts.map +0 -1
  203. package/dist/remediate/utils/commands.js +0 -29
  204. package/dist/remediate/utils/commands.js.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "audit-tools",
3
- "version": "0.32.44",
3
+ "version": "0.32.46",
4
4
  "private": false,
5
5
  "license": "ISC",
6
6
  "description": "Portable hybrid code auditing + remediation orchestrators for arbitrary repositories.",
@@ -31,6 +31,7 @@
31
31
  "scripts/postinstall.mjs",
32
32
  "scripts/audit/postinstall.mjs",
33
33
  "scripts/remediate/postinstall.mjs",
34
+ "scripts/shared/install-host-assets.mjs",
34
35
  "opencode.json",
35
36
  "README.md",
36
37
  "docs/audit-pkg/**"
@@ -1,43 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
  import { homedir } from 'os';
3
3
  import { join, dirname } from 'path';
4
- import { mkdirSync, existsSync, readFileSync, writeFileSync } from 'fs';
4
+ import { readFileSync } from 'fs';
5
5
  import { fileURLToPath } from 'url';
6
-
6
+ import {
7
+ readRequiredSource,
8
+ readOptionalSource,
9
+ writeGeneratedFile,
10
+ objectValue,
11
+ resolveSharedOpenCodePermissions,
12
+ runInstalls,
13
+ installOpenCodeGlobalConfig,
14
+ installAntigravityPlugin,
15
+ finishPostinstall,
16
+ } from '../shared/install-host-assets.mjs';
17
+
18
+ const TOOL = 'audit-code';
7
19
  const pkgRoot = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
8
20
  const packageVersion = JSON.parse(readFileSync(join(pkgRoot, 'package.json'), 'utf8')).version ?? '0.0.0';
9
21
  const promptSourceFile = join(pkgRoot, 'skills', 'audit-code', 'audit-code.prompt.md');
10
22
  const skillSourceFile = join(pkgRoot, 'skills', 'audit-code', 'SKILL.md');
11
23
  const codexOpenAiAgentSourceFile = join(pkgRoot, 'skills', 'audit-code', 'agents', 'openai.yaml');
12
24
 
13
- function readRequiredSource(path, label) {
14
- if (!existsSync(path)) {
15
- console.warn(`audit-code: ${label} source not found at ${path} - skipping global command install`);
16
- process.exitCode = 0;
17
- return null;
18
- }
19
-
20
- return readFileSync(path);
21
- }
22
-
23
- function readOptionalSource(path, label) {
24
- if (!existsSync(path)) {
25
- console.warn(`audit-code: ${label} source not found at ${path} - skipping optional install`);
26
- return null;
27
- }
28
-
29
- return readFileSync(path);
30
- }
31
-
32
- function writeGeneratedFile(path, content) {
33
- const action = existsSync(path) ? 'updated' : 'installed';
34
- mkdirSync(dirname(path), { recursive: true });
35
- writeFileSync(path, content);
36
- return action;
37
- }
38
-
39
- const OPENCODE_AUDIT_EXTERNAL_DIRECTORY_PERMISSION = { '*': 'allow' };
40
-
41
25
  const OPENCODE_AUDIT_EDIT_PERMISSION = {
42
26
  '*': 'ask',
43
27
  '.audit-code/**': 'allow',
@@ -45,7 +29,7 @@ const OPENCODE_AUDIT_EDIT_PERMISSION = {
45
29
  };
46
30
 
47
31
  const OPENCODE_AUDIT_BASH_PERMISSION = {
48
- '*': 'allow',
32
+ '*': 'ask',
49
33
  'audit-code synthesize*': 'deny',
50
34
  'audit-code cleanup*': 'deny',
51
35
  'audit-code requeue*': 'deny',
@@ -82,35 +66,20 @@ const OPENCODE_AUDIT_BASH_PERMISSION = {
82
66
  'rm *': 'deny',
83
67
  };
84
68
 
85
- function objectValue(value) {
86
- return value && typeof value === 'object' && !Array.isArray(value)
87
- ? value
88
- : {};
89
- }
90
-
91
69
  // The scoped OpenCode permission merge helpers are single-sourced in
92
70
  // audit-tools/shared (global top-level scope vs. auditor agent scope).
93
- // Resolve them best-effort: on a fresh workspace checkout the shared dist may
94
- // not be built yet, in which case the OpenCode config deployment below is
71
+ // Resolved best-effort: on a fresh workspace checkout the shared dist may not
72
+ // be built yet, in which case the OpenCode config deployment below is
95
73
  // skipped with a warning instead of failing the whole install.
96
- let sharedOpenCodePermissions = null;
97
- try {
98
- const shared = await import('audit-tools/shared');
99
- if (
100
- typeof shared.mergeOpenCodeAgentPermissionRule === 'function' &&
101
- typeof shared.mergeOpenCodeGlobalPermissionRule === 'function' &&
102
- typeof shared.migrateOpenCodeGlobalExternalDirectory === 'function'
103
- ) {
104
- sharedOpenCodePermissions = shared;
105
- }
106
- } catch {
107
- // Leave null; the OpenCode deployment step reports the skip.
108
- }
74
+ const sharedOpenCodePermissions = await resolveSharedOpenCodePermissions();
109
75
 
110
- // Auditor agent scope: broad-allow-with-denylist, unchanged. Managed rules
111
- // (including the wildcard) always win at this scope.
76
+ // Auditor agent scope (read-only agent, parity with the remediator hardening):
77
+ // enumerated audit-code commands stay managed allows/denies, but the bash
78
+ // wildcard defaults to "ask" (an existing user wildcard survives — the
79
+ // managed set is passed without "*") and no external_directory allow-all is
80
+ // seeded.
112
81
  function mergeOpenCodeAgentPermissionConfig(existingPermission, generatedPermission) {
113
- const { mergeOpenCodeAgentPermissionRule } = sharedOpenCodePermissions;
82
+ const { mergeOpenCodeAgentPermissionRule, withoutOpenCodeWildcard } = sharedOpenCodePermissions;
114
83
  if (!existingPermission || typeof existingPermission !== 'object' || Array.isArray(existingPermission)) {
115
84
  return generatedPermission;
116
85
  }
@@ -121,11 +90,6 @@ function mergeOpenCodeAgentPermissionConfig(existingPermission, generatedPermiss
121
90
  read: generatedPermission.read,
122
91
  glob: generatedPermission.glob,
123
92
  grep: generatedPermission.grep,
124
- external_directory: mergeOpenCodeAgentPermissionRule(
125
- existingPermission.external_directory,
126
- generatedPermission.external_directory,
127
- OPENCODE_AUDIT_EXTERNAL_DIRECTORY_PERMISSION,
128
- ),
129
93
  edit: mergeOpenCodeAgentPermissionRule(
130
94
  existingPermission.edit,
131
95
  generatedPermission.edit,
@@ -134,7 +98,7 @@ function mergeOpenCodeAgentPermissionConfig(existingPermission, generatedPermiss
134
98
  bash: mergeOpenCodeAgentPermissionRule(
135
99
  existingPermission.bash,
136
100
  generatedPermission.bash,
137
- OPENCODE_AUDIT_BASH_PERMISSION,
101
+ withoutOpenCodeWildcard(OPENCODE_AUDIT_BASH_PERMISSION),
138
102
  ),
139
103
  };
140
104
  }
@@ -184,7 +148,6 @@ function renderOpenCodePermissionConfig() {
184
148
  read: 'allow',
185
149
  glob: 'allow',
186
150
  grep: 'allow',
187
- external_directory: { ...OPENCODE_AUDIT_EXTERNAL_DIRECTORY_PERMISSION },
188
151
  edit: { ...OPENCODE_AUDIT_EDIT_PERMISSION },
189
152
  bash: { ...OPENCODE_AUDIT_BASH_PERMISSION },
190
153
  };
@@ -220,7 +183,6 @@ function mergeOpenCodeGlobalConfig(existing) {
220
183
  description: 'Read-heavy audit orchestration agent for the /audit-code workflow.',
221
184
  permission: {
222
185
  ...mergeOpenCodeAgentPermissionConfig(existingAuditor.permission, auditPermission),
223
- external_directory: { '*': 'allow' },
224
186
  'auditor_*': 'allow',
225
187
  question: 'allow',
226
188
  task: 'allow',
@@ -234,27 +196,17 @@ function claudePluginExternalDir() {
234
196
  return join(homedir(), '.claude', 'plugins', 'marketplaces', 'claude-plugins-official', 'external_plugins', 'audit-code');
235
197
  }
236
198
 
237
- function installMergedJson(path, buildMerged) {
238
- const existing = existsSync(path) ? readFileSync(path, 'utf8') : null;
239
- const merged = buildMerged(existing);
240
- const action = existing ? 'updated' : 'installed';
241
- mkdirSync(dirname(path), { recursive: true });
242
- writeFileSync(path, JSON.stringify(merged, null, 2) + '\n', 'utf8');
243
- return action;
244
- }
245
-
246
- const promptSource = readRequiredSource(promptSourceFile, 'prompt');
247
- const skillSource = readRequiredSource(skillSourceFile, 'skill');
199
+ const promptSource = readRequiredSource(promptSourceFile, 'prompt', TOOL);
200
+ const skillSource = readRequiredSource(skillSourceFile, 'skill', TOOL);
248
201
 
249
202
  if (!promptSource || !skillSource) {
250
203
  process.exit(0);
251
204
  }
252
205
 
253
- const codexOpenAiAgentSource = readOptionalSource(codexOpenAiAgentSourceFile, 'Codex skill UI metadata');
206
+ const codexOpenAiAgentSource = readOptionalSource(codexOpenAiAgentSourceFile, 'Codex skill UI metadata', TOOL);
254
207
 
255
208
  const postinstallStart = Date.now();
256
- let succeeded = 0;
257
- let failed = 0;
209
+ const counts = { succeeded: 0, failed: 0 };
258
210
 
259
211
  const installs = [
260
212
  {
@@ -287,65 +239,36 @@ const installs = [
287
239
  : []),
288
240
  ];
289
241
 
290
- for (const install of installs) {
291
- try {
292
- const action = writeGeneratedFile(install.path, install.content);
293
- console.log(`audit-code: ${action} global ${install.label} at ${install.path}`);
294
- succeeded++;
295
- } catch (err) {
296
- console.warn(`audit-code: could not install global ${install.label} (${err.message})`);
297
- console.warn(` To install manually, copy from:`);
298
- console.warn(` ${install.sourcePath}`);
299
- console.warn(` to:`);
300
- console.warn(` ${install.path}`);
301
- failed++;
302
- }
303
- }
242
+ runInstalls(TOOL, installs, counts);
304
243
 
305
244
  // Install OpenCode global command and MCP via merged config
306
245
  const opencodeGlobalConfig = join(homedir(), '.config', 'opencode', 'opencode.json');
307
- if (!sharedOpenCodePermissions) {
308
- // Expected when audit-tools/shared isn't built yet — notably during `npm ci`
309
- // in CI, where postinstall runs before the shared build step. This is a SKIP,
310
- // not a failure: counting it would trip the `failed > 0` exit-1 guard below and
311
- // break `npm ci`. Genuine OpenCode write errors are still counted as failures.
312
- console.warn(
313
- 'audit-code: audit-tools/shared is unavailable (build the shared workspace first); skipping OpenCode config deployment',
314
- );
315
- } else {
316
- try {
317
- const action = installMergedJson(opencodeGlobalConfig, (existing) =>
318
- mergeOpenCodeGlobalConfig(existing),
319
- );
320
- console.log(`audit-code: ${action} global OpenCode config in ${opencodeGlobalConfig}`);
321
- succeeded++;
322
- } catch (err) {
323
- console.warn(`audit-code: could not install global OpenCode config (${err.message})`);
324
- console.warn(` To install manually, add the mcp.auditor and command["audit-code"] entries to:`);
325
- console.warn(` ${opencodeGlobalConfig}`);
326
- failed++;
327
- }
328
- }
246
+ installOpenCodeGlobalConfig(
247
+ {
248
+ toolName: TOOL,
249
+ path: opencodeGlobalConfig,
250
+ sharedOpenCodePermissions,
251
+ buildMerged: (existing) => mergeOpenCodeGlobalConfig(existing),
252
+ label: 'OpenCode config',
253
+ manualInstructions: [
254
+ ` To install manually, add the mcp.auditor and command["audit-code"] entries to:`,
255
+ ` ${opencodeGlobalConfig}`,
256
+ ],
257
+ },
258
+ counts,
259
+ );
329
260
 
330
261
  // Install Antigravity plugin (global skill for Gemini IDE / Antigravity Hub)
331
- const antigravityPluginDir = join(homedir(), '.gemini', 'config', 'plugins', 'audit-code');
332
- const antigravityPluginJsonPath = join(antigravityPluginDir, 'plugin.json');
333
- const antigravityPluginSkillPath = join(antigravityPluginDir, 'skills', 'SKILL.md');
334
-
335
- try {
336
- const pluginJsonAction = writeGeneratedFile(
337
- antigravityPluginJsonPath,
338
- Buffer.from(JSON.stringify({ name: 'audit-code', version: '1.0.0' }, null, 2) + '\n'),
339
- );
340
- console.log(`audit-code: ${pluginJsonAction} Antigravity plugin manifest at ${antigravityPluginJsonPath}`);
341
-
342
- const skillAction = writeGeneratedFile(antigravityPluginSkillPath, skillSource);
343
- console.log(`audit-code: ${skillAction} Antigravity plugin skill at ${antigravityPluginSkillPath}`);
344
- succeeded++;
345
- } catch (err) {
346
- console.warn(`audit-code: could not install Antigravity plugin (${err.message})`);
347
- failed++;
348
- }
262
+ installAntigravityPlugin(
263
+ {
264
+ toolName: TOOL,
265
+ homeDir: homedir(),
266
+ pluginName: 'audit-code',
267
+ pluginVersion: '1.0.0',
268
+ skillSource,
269
+ },
270
+ counts,
271
+ );
349
272
 
350
273
  // Install Claude Desktop plugin so /audit-code appears in the slash-command menu
351
274
  // Claude Desktop reads external plugins from ~/.claude/plugins/marketplaces/claude-plugins-official/external_plugins/
@@ -380,14 +303,11 @@ try {
380
303
  console.log(`audit-code: ${skillAction} Claude Desktop plugin skill at ${claudePluginSkillPath}`);
381
304
 
382
305
  console.log(`audit-code: restart Claude Desktop for /audit-code to appear in the slash-command menu`);
383
- succeeded++;
306
+ counts.succeeded++;
384
307
  } catch (err) {
385
308
  console.warn(`audit-code: could not install Claude Desktop plugin (${err.message})`);
386
309
  console.warn(` Plugin directory: ${claudePluginDir}`);
387
- failed++;
310
+ counts.failed++;
388
311
  }
389
312
 
390
- console.log(`audit-code: postinstall complete — ${succeeded} succeeded, ${failed} failed (${Date.now() - postinstallStart}ms)`);
391
- if (failed > 0) {
392
- process.exitCode = 1;
393
- }
313
+ finishPostinstall(TOOL, counts, postinstallStart);
@@ -1,9 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import { homedir } from "os";
3
3
  import { join, dirname } from "path";
4
- import { mkdirSync, existsSync, readFileSync, writeFileSync } from "fs";
4
+ import { readFileSync } from "fs";
5
5
  import { fileURLToPath } from "url";
6
+ import {
7
+ readRequiredSource,
8
+ readOptionalSource,
9
+ objectValue,
10
+ splitFrontmatter,
11
+ resolveSharedOpenCodePermissions,
12
+ runInstalls,
13
+ installOpenCodeGlobalConfig,
14
+ installAntigravityPlugin,
15
+ finishPostinstall,
16
+ } from "../shared/install-host-assets.mjs";
6
17
 
18
+ const TOOL = "remediate-code";
7
19
  const pkgRoot = dirname(dirname(dirname(fileURLToPath(import.meta.url))));
8
20
  const packageVersion = JSON.parse(readFileSync(join(pkgRoot, 'package.json'), 'utf8')).version ?? '0.0.0';
9
21
  const promptSourceFile = join(
@@ -21,53 +33,6 @@ const codexOpenAiAgentSourceFile = join(
21
33
  "openai.yaml",
22
34
  );
23
35
 
24
- function readRequiredSource(path, label) {
25
- if (!existsSync(path)) {
26
- console.warn(
27
- `remediate-code: ${label} source not found at ${path} - skipping global command install`,
28
- );
29
- process.exitCode = 0;
30
- return null;
31
- }
32
- return readFileSync(path);
33
- }
34
-
35
- function readOptionalSource(path, label) {
36
- if (!existsSync(path)) {
37
- console.warn(
38
- `remediate-code: ${label} source not found at ${path} - skipping optional install`,
39
- );
40
- return null;
41
- }
42
- return readFileSync(path);
43
- }
44
-
45
- // splitFrontmatter, writeGeneratedFile, and objectValue are single-sourced in
46
- // src/utils/hostAssets.ts. Import them from the compiled output best-effort;
47
- // fall back to inline definitions so postinstall works even on a fresh checkout
48
- // where dist/ may not be built yet.
49
- let _hostAssets = null;
50
- try {
51
- _hostAssets = await import(join(pkgRoot, "dist", "remediate", "utils", "hostAssets.js"));
52
- } catch {
53
- // dist not built yet — inline fallbacks below will be used.
54
- }
55
-
56
- function writeGeneratedFile(path, content) {
57
- if (_hostAssets) return _hostAssets.writeGeneratedFile(path, content);
58
- const action = existsSync(path) ? "updated" : "installed";
59
- mkdirSync(dirname(path), { recursive: true });
60
- writeFileSync(path, content);
61
- return action;
62
- }
63
-
64
- function splitFrontmatter(text) {
65
- if (_hostAssets) return _hostAssets.splitFrontmatter(text);
66
- const normalized = text.replace(/\r\n/g, "\n");
67
- const match = normalized.match(/^---\n[\s\S]*?\n---\n?/u);
68
- return { body: match ? normalized.slice(match[0].length) : normalized };
69
- }
70
-
71
36
  const OPENCODE_REMEDIATE_EDIT_PERMISSION = {
72
37
  "*": "ask",
73
38
  ".remediation-artifacts/**": "allow",
@@ -101,32 +66,12 @@ const OPENCODE_REMEDIATE_BASH_PERMISSION = {
101
66
  "rm *": "deny",
102
67
  };
103
68
 
104
- function objectValue(value) {
105
- if (_hostAssets) return _hostAssets.objectValue(value);
106
- return value && typeof value === "object" && !Array.isArray(value)
107
- ? value
108
- : {};
109
- }
110
-
111
69
  // The scoped OpenCode permission merge helpers are single-sourced in
112
70
  // audit-tools/shared (global top-level scope vs. remediator agent scope).
113
- // Resolve them best-effort: on a fresh workspace checkout the shared dist may
114
- // not be built yet, in which case the OpenCode config deployment below is
71
+ // Resolved best-effort: on a fresh workspace checkout the shared dist may not
72
+ // be built yet, in which case the OpenCode config deployment below is
115
73
  // skipped with a warning instead of failing the whole install.
116
- let sharedOpenCodePermissions = null;
117
- try {
118
- const shared = await import("audit-tools/shared");
119
- if (
120
- typeof shared.mergeOpenCodeAgentPermissionRule === "function" &&
121
- typeof shared.mergeOpenCodeGlobalPermissionRule === "function" &&
122
- typeof shared.migrateOpenCodeGlobalExternalDirectory === "function" &&
123
- typeof shared.withoutOpenCodeWildcard === "function"
124
- ) {
125
- sharedOpenCodePermissions = shared;
126
- }
127
- } catch {
128
- // Leave null; the OpenCode deployment step reports the skip.
129
- }
74
+ const sharedOpenCodePermissions = await resolveSharedOpenCodePermissions();
130
75
 
131
76
  // Remediator agent scope: managed rules win for specific patterns; an
132
77
  // existing user wildcard survives (the managed set is passed without "*").
@@ -219,30 +164,21 @@ function mergeOpenCodeGlobalConfig(existing, promptBody) {
219
164
  };
220
165
  }
221
166
 
222
- function installMergedJson(path, buildMerged) {
223
- const existing = existsSync(path) ? readFileSync(path, "utf8") : null;
224
- const merged = buildMerged(existing);
225
- const action = existing ? "updated" : "installed";
226
- mkdirSync(dirname(path), { recursive: true });
227
- writeFileSync(path, JSON.stringify(merged, null, 2) + "\n", "utf8");
228
- return action;
229
- }
230
-
231
- const promptSource = readRequiredSource(promptSourceFile, "prompt");
232
- const skillSource = readRequiredSource(skillSourceFile, "skill");
167
+ const promptSource = readRequiredSource(promptSourceFile, "prompt", TOOL);
168
+ const skillSource = readRequiredSource(skillSourceFile, "skill", TOOL);
233
169
 
234
170
  if (!promptSource || !skillSource) {
235
171
  process.exit(0);
236
172
  }
237
173
 
238
174
  const postinstallStart = Date.now();
239
- let succeeded = 0;
240
- let failed = 0;
175
+ const counts = { succeeded: 0, failed: 0 };
241
176
 
242
177
  const promptBody = splitFrontmatter(promptSource.toString("utf8")).body;
243
178
  const codexOpenAiAgentSource = readOptionalSource(
244
179
  codexOpenAiAgentSourceFile,
245
180
  "Codex skill UI metadata",
181
+ TOOL,
246
182
  );
247
183
 
248
184
  const installs = [
@@ -289,24 +225,7 @@ const installs = [
289
225
  : []),
290
226
  ];
291
227
 
292
- for (const install of installs) {
293
- try {
294
- const action = writeGeneratedFile(install.path, install.content);
295
- console.log(
296
- `remediate-code: ${action} global ${install.label} at ${install.path}`,
297
- );
298
- succeeded++;
299
- } catch (err) {
300
- console.warn(
301
- `remediate-code: could not install global ${install.label} (${err.message})`,
302
- );
303
- console.warn(" To install manually, copy from:");
304
- console.warn(` ${install.sourcePath}`);
305
- console.warn(" to:");
306
- console.warn(` ${install.path}`);
307
- failed++;
308
- }
309
- }
228
+ runInstalls(TOOL, installs, counts);
310
229
 
311
230
  const opencodeGlobalConfig = join(
312
231
  homedir(),
@@ -314,74 +233,27 @@ const opencodeGlobalConfig = join(
314
233
  "opencode",
315
234
  "opencode.json",
316
235
  );
317
- if (!sharedOpenCodePermissions) {
318
- // Expected when audit-tools/shared isn't built yet — notably during `npm ci`
319
- // in CI, where postinstall runs before the shared build step. This is a SKIP,
320
- // not a failure: counting it would trip the `failed > 0` exit-1 guard below and
321
- // break `npm ci`. Genuine OpenCode write errors are still counted as failures.
322
- console.warn(
323
- "remediate-code: audit-tools/shared is unavailable (build the shared workspace first); skipping OpenCode config deployment",
324
- );
325
- } else {
326
- try {
327
- const action = installMergedJson(opencodeGlobalConfig, (existing) =>
328
- mergeOpenCodeGlobalConfig(existing, promptBody),
329
- );
330
- console.log(
331
- `remediate-code: ${action} global OpenCode command in ${opencodeGlobalConfig}`,
332
- );
333
- succeeded++;
334
- } catch (err) {
335
- console.warn(
336
- `remediate-code: could not install global OpenCode command (${err.message})`,
337
- );
338
- failed++;
339
- }
340
- }
236
+ installOpenCodeGlobalConfig(
237
+ {
238
+ toolName: TOOL,
239
+ path: opencodeGlobalConfig,
240
+ sharedOpenCodePermissions,
241
+ buildMerged: (existing) => mergeOpenCodeGlobalConfig(existing, promptBody),
242
+ label: "OpenCode command",
243
+ },
244
+ counts,
245
+ );
341
246
 
342
247
  // Install Antigravity plugin (global skill for Gemini IDE / Antigravity Hub)
343
- const antigravityPluginDir = join(
344
- homedir(),
345
- ".gemini",
346
- "config",
347
- "plugins",
348
- "remediate-code",
349
- );
350
- const antigravityPluginJsonPath = join(antigravityPluginDir, "plugin.json");
351
- const antigravityPluginSkillPath = join(
352
- antigravityPluginDir,
353
- "skills",
354
- "SKILL.md",
248
+ installAntigravityPlugin(
249
+ {
250
+ toolName: TOOL,
251
+ homeDir: homedir(),
252
+ pluginName: "remediate-code",
253
+ pluginVersion: packageVersion,
254
+ skillSource,
255
+ },
256
+ counts,
355
257
  );
356
258
 
357
- try {
358
- const pluginJsonAction = writeGeneratedFile(
359
- antigravityPluginJsonPath,
360
- Buffer.from(
361
- JSON.stringify(
362
- { name: "remediate-code", version: packageVersion },
363
- null,
364
- 2,
365
- ) + "\n",
366
- ),
367
- );
368
- console.log(
369
- `remediate-code: ${pluginJsonAction} Antigravity plugin manifest at ${antigravityPluginJsonPath}`,
370
- );
371
-
372
- const skillAction = writeGeneratedFile(antigravityPluginSkillPath, skillSource);
373
- console.log(
374
- `remediate-code: ${skillAction} Antigravity plugin skill at ${antigravityPluginSkillPath}`,
375
- );
376
- succeeded++;
377
- } catch (err) {
378
- console.warn(
379
- `remediate-code: could not install Antigravity plugin (${err.message})`,
380
- );
381
- failed++;
382
- }
383
-
384
- console.log(`remediate-code: postinstall complete — ${succeeded} succeeded, ${failed} failed (${Date.now() - postinstallStart}ms)`);
385
- if (failed > 0) {
386
- process.exitCode = 1;
387
- }
259
+ finishPostinstall(TOOL, counts, postinstallStart);