@nerviq/cli 1.20.1 → 1.22.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.
Files changed (186) hide show
  1. package/LICENSE +23 -23
  2. package/README.md +20 -2
  3. package/bin/cli.js +3 -3
  4. package/package.json +1 -1
  5. package/src/activity.js +1039 -1039
  6. package/src/adoption-advisor.js +299 -299
  7. package/src/aider/config-parser.js +166 -166
  8. package/src/aider/context.js +4 -1
  9. package/src/aider/deep-review.js +316 -316
  10. package/src/aider/domain-packs.js +303 -303
  11. package/src/aider/freshness.js +93 -93
  12. package/src/aider/governance.js +253 -253
  13. package/src/aider/interactive.js +334 -334
  14. package/src/aider/mcp-packs.js +329 -329
  15. package/src/aider/patch.js +214 -214
  16. package/src/aider/plans.js +186 -186
  17. package/src/aider/premium.js +360 -360
  18. package/src/aider/setup.js +404 -404
  19. package/src/aider/techniques.js +312 -67
  20. package/src/analyze.js +951 -951
  21. package/src/anti-patterns.js +485 -485
  22. package/src/audit/instruction-files.js +180 -180
  23. package/src/audit/recommendations.js +577 -577
  24. package/src/audit.js +20 -0
  25. package/src/auto-suggest.js +154 -154
  26. package/src/badge.js +13 -13
  27. package/src/behavioral-drift.js +801 -801
  28. package/src/benchmark.js +67 -67
  29. package/src/catalog.js +103 -103
  30. package/src/certification.js +128 -128
  31. package/src/codex/config-parser.js +183 -183
  32. package/src/codex/context.js +223 -223
  33. package/src/codex/deep-review.js +493 -493
  34. package/src/codex/domain-packs.js +394 -394
  35. package/src/codex/freshness.js +84 -84
  36. package/src/codex/governance.js +192 -192
  37. package/src/codex/interactive.js +618 -618
  38. package/src/codex/mcp-packs.js +914 -914
  39. package/src/codex/patch.js +209 -209
  40. package/src/codex/plans.js +251 -251
  41. package/src/codex/premium.js +614 -614
  42. package/src/codex/setup.js +591 -591
  43. package/src/continuous-ops.js +681 -681
  44. package/src/copilot/activity.js +309 -309
  45. package/src/copilot/deep-review.js +346 -346
  46. package/src/copilot/domain-packs.js +372 -372
  47. package/src/copilot/freshness.js +57 -57
  48. package/src/copilot/governance.js +222 -222
  49. package/src/copilot/interactive.js +406 -406
  50. package/src/copilot/mcp-packs.js +826 -826
  51. package/src/copilot/plans.js +253 -253
  52. package/src/copilot/premium.js +451 -451
  53. package/src/copilot/setup.js +488 -488
  54. package/src/cost-tracking.js +61 -61
  55. package/src/cursor/activity.js +301 -301
  56. package/src/cursor/config-parser.js +265 -265
  57. package/src/cursor/context.js +256 -256
  58. package/src/cursor/deep-review.js +334 -334
  59. package/src/cursor/domain-packs.js +368 -368
  60. package/src/cursor/freshness.js +65 -65
  61. package/src/cursor/governance.js +229 -229
  62. package/src/cursor/interactive.js +391 -391
  63. package/src/cursor/mcp-packs.js +828 -828
  64. package/src/cursor/plans.js +254 -254
  65. package/src/cursor/premium.js +469 -469
  66. package/src/cursor/setup.js +488 -488
  67. package/src/dashboard.js +493 -493
  68. package/src/deep-review.js +428 -428
  69. package/src/deprecation.js +98 -98
  70. package/src/diff-only.js +280 -280
  71. package/src/doctor.js +119 -119
  72. package/src/domain-pack-expansion.js +1033 -1033
  73. package/src/domain-packs.js +387 -387
  74. package/src/feedback.js +178 -178
  75. package/src/fix-engine.js +783 -783
  76. package/src/fix-prompts.js +122 -122
  77. package/src/formatters/csv.js +69 -0
  78. package/src/formatters/junit.js +99 -0
  79. package/src/formatters/markdown.js +118 -0
  80. package/src/formatters/sarif.js +115 -115
  81. package/src/freshness.js +74 -74
  82. package/src/gemini/config-parser.js +275 -275
  83. package/src/gemini/deep-review.js +559 -559
  84. package/src/gemini/domain-packs.js +393 -393
  85. package/src/gemini/freshness.js +66 -66
  86. package/src/gemini/governance.js +201 -201
  87. package/src/gemini/interactive.js +860 -860
  88. package/src/gemini/mcp-packs.js +915 -915
  89. package/src/gemini/plans.js +269 -269
  90. package/src/gemini/premium.js +760 -760
  91. package/src/gemini/setup.js +692 -692
  92. package/src/governance.js +72 -72
  93. package/src/harmony/add.js +68 -68
  94. package/src/harmony/advisor.js +333 -333
  95. package/src/harmony/canon.js +565 -565
  96. package/src/harmony/cli.js +591 -591
  97. package/src/harmony/drift.js +401 -401
  98. package/src/harmony/governance.js +313 -313
  99. package/src/harmony/memory.js +239 -239
  100. package/src/harmony/sync.js +475 -475
  101. package/src/harmony/watch.js +370 -370
  102. package/src/hook-validation.js +342 -342
  103. package/src/index.js +271 -271
  104. package/src/init.js +184 -184
  105. package/src/instruction-surfaces.js +185 -185
  106. package/src/integrations.js +144 -144
  107. package/src/interactive.js +118 -118
  108. package/src/locales/en.json +1 -1
  109. package/src/locales/es.json +1 -1
  110. package/src/mcp-packs.js +830 -830
  111. package/src/mcp-server.js +726 -726
  112. package/src/mcp-validation.js +337 -337
  113. package/src/nerviq-sync.json +7 -7
  114. package/src/opencode/config-parser.js +109 -109
  115. package/src/opencode/context.js +247 -247
  116. package/src/opencode/deep-review.js +313 -313
  117. package/src/opencode/domain-packs.js +262 -262
  118. package/src/opencode/freshness.js +66 -66
  119. package/src/opencode/governance.js +159 -159
  120. package/src/opencode/interactive.js +392 -392
  121. package/src/opencode/mcp-packs.js +705 -705
  122. package/src/opencode/patch.js +184 -184
  123. package/src/opencode/plans.js +231 -231
  124. package/src/opencode/premium.js +413 -413
  125. package/src/opencode/setup.js +449 -449
  126. package/src/opencode/techniques.js +27 -27
  127. package/src/operating-profile.js +574 -574
  128. package/src/org.js +152 -152
  129. package/src/permission-rules.js +218 -218
  130. package/src/plans.js +839 -839
  131. package/src/platform-change-manifest.js +86 -86
  132. package/src/plugins.js +110 -110
  133. package/src/policy-layers.js +210 -210
  134. package/src/profiles.js +124 -124
  135. package/src/prompt-injection.js +74 -74
  136. package/src/public-api.js +173 -173
  137. package/src/recommendation-rules.js +84 -84
  138. package/src/repo-archetype.js +386 -386
  139. package/src/secret-patterns.js +39 -39
  140. package/src/server.js +527 -527
  141. package/src/setup/analysis.js +607 -607
  142. package/src/setup/runtime.js +172 -172
  143. package/src/setup.js +677 -677
  144. package/src/shared/capabilities.js +194 -194
  145. package/src/source-urls.js +132 -132
  146. package/src/stack-checks.js +565 -565
  147. package/src/supplemental-checks.js +13 -13
  148. package/src/synergy/adaptive.js +261 -261
  149. package/src/synergy/compensation.js +137 -137
  150. package/src/synergy/evidence.js +193 -193
  151. package/src/synergy/learning.js +199 -199
  152. package/src/synergy/patterns.js +227 -227
  153. package/src/synergy/ranking.js +83 -83
  154. package/src/synergy/report.js +165 -165
  155. package/src/synergy/routing.js +146 -146
  156. package/src/techniques/api.js +407 -407
  157. package/src/techniques/automation.js +316 -316
  158. package/src/techniques/compliance.js +257 -257
  159. package/src/techniques/hygiene.js +294 -294
  160. package/src/techniques/instructions.js +243 -243
  161. package/src/techniques/observability.js +226 -226
  162. package/src/techniques/optimization.js +142 -142
  163. package/src/techniques/quality.js +318 -318
  164. package/src/techniques/security.js +237 -237
  165. package/src/techniques/shared.js +443 -443
  166. package/src/techniques/stacks.js +2294 -2294
  167. package/src/techniques/tools.js +106 -106
  168. package/src/techniques/workflow.js +413 -413
  169. package/src/techniques.js +81 -81
  170. package/src/terminology.js +73 -73
  171. package/src/token-estimate.js +35 -35
  172. package/src/usage-patterns.js +99 -99
  173. package/src/verification-metadata.js +145 -145
  174. package/src/watch.js +247 -247
  175. package/src/windsurf/activity.js +302 -302
  176. package/src/windsurf/config-parser.js +267 -267
  177. package/src/windsurf/deep-review.js +337 -337
  178. package/src/windsurf/domain-packs.js +370 -370
  179. package/src/windsurf/freshness.js +36 -36
  180. package/src/windsurf/governance.js +231 -231
  181. package/src/windsurf/interactive.js +388 -388
  182. package/src/windsurf/mcp-packs.js +792 -792
  183. package/src/windsurf/plans.js +247 -247
  184. package/src/windsurf/premium.js +468 -468
  185. package/src/windsurf/setup.js +471 -471
  186. package/src/workspace.js +375 -375
@@ -1,574 +1,574 @@
1
- 'use strict';
2
-
3
- const { detectPlatforms } = require('./public-api');
4
- const { PERMISSION_PROFILES, HOOK_REGISTRY, POLICY_PACKS } = require('./governance');
5
-
6
- function unique(values) {
7
- return [...new Set((values || []).filter(Boolean))];
8
- }
9
-
10
- function getProfile(key) {
11
- return PERMISSION_PROFILES.find((profile) => profile.key === key) || PERMISSION_PROFILES[0];
12
- }
13
-
14
- function getPolicyPack(key) {
15
- return POLICY_PACKS.find((pack) => pack.key === key) || POLICY_PACKS[0];
16
- }
17
-
18
- function getHook(key) {
19
- return HOOK_REGISTRY.find((hook) => hook.key === key);
20
- }
21
-
22
- function buildEvidence(repoArchetype, recommendedDomainPacks = [], extras = []) {
23
- const packLabels = (recommendedDomainPacks || []).map((pack) => pack.label).slice(0, 2);
24
- return unique([
25
- `Archetype: ${repoArchetype.label}`,
26
- `Workflow: ${repoArchetype.primaryWorkflow.label}`,
27
- `Risk: ${repoArchetype.riskProfile.label}`,
28
- packLabels.length > 0 ? `Domain packs: ${packLabels.join(', ')}` : null,
29
- ...(repoArchetype.signals || []).slice(0, 2),
30
- ...extras,
31
- ]).slice(0, 5);
32
- }
33
-
34
- function withExplainability(base, repoArchetype, recommendedDomainPacks, options = {}) {
35
- const {
36
- evidence = [],
37
- prerequisites = [],
38
- expectedBenefit = '',
39
- rollbackSafety = '',
40
- } = options;
41
-
42
- return {
43
- ...base,
44
- why: base.rationale,
45
- evidence: buildEvidence(repoArchetype, recommendedDomainPacks, evidence),
46
- prerequisites: unique(prerequisites),
47
- expectedBenefit,
48
- rollbackSafety,
49
- };
50
- }
51
-
52
- function buildDomainInfluence(repoArchetype, recommendedDomainPacks = [], recommendedMcpPacks = []) {
53
- const keys = new Set((recommendedDomainPacks || []).map((pack) => pack.key));
54
- const reasons = [];
55
- const extraHooks = [];
56
- let permissionOverride = null;
57
- let governancePackOverride = null;
58
- let verificationBias = null;
59
- let ciShapeOverride = null;
60
-
61
- if (keys.has('security-focused') || keys.has('regulated-lite')) {
62
- permissionOverride = 'suggest-only';
63
- governancePackOverride = 'regulated-lite';
64
- reasons.push('Security-focused or regulated domain signals push the repo toward review-first posture.');
65
- }
66
-
67
- if (keys.has('enterprise-governed')) {
68
- permissionOverride = 'suggest-only';
69
- governancePackOverride = governancePackOverride || 'security-sensitive';
70
- ciShapeOverride = 'governed-pr-gate';
71
- extraHooks.push('trust-drift-check');
72
- reasons.push('Enterprise-governed domain signals favor approvals, traceability, and trust-drift checks.');
73
- }
74
-
75
- if (keys.has('infra-platform') || keys.has('devops-cicd')) {
76
- governancePackOverride = governancePackOverride || 'security-sensitive';
77
- verificationBias = 'infra';
78
- ciShapeOverride = ciShapeOverride || 'governed-pr-gate';
79
- extraHooks.push('trust-drift-check');
80
- reasons.push('Infra / CI domain signals increase the value of plan-first rollout and trust-drift validation.');
81
- }
82
-
83
- if (keys.has('ai-ml') || keys.has('data-pipeline')) {
84
- verificationBias = 'pipeline';
85
- reasons.push('Pipeline-oriented domains need repeatable verification and state-aware review loops.');
86
- }
87
-
88
- if (keys.has('mobile')) {
89
- verificationBias = 'mobile';
90
- reasons.push('Mobile domain signals shift the recommended loop toward analyze + build verification.');
91
- }
92
-
93
- if (keys.has('oss-library') || keys.has('docs-content')) {
94
- permissionOverride = permissionOverride || 'suggest-only';
95
- governancePackOverride = governancePackOverride || 'oss-friendly';
96
- reasons.push('Contributor-sensitive or docs-heavy repos benefit from lighter review-first governance.');
97
- }
98
-
99
- if (keys.has('monorepo')) {
100
- ciShapeOverride = 'workspace-pr-gate';
101
- extraHooks.push('trust-drift-check');
102
- reasons.push('Monorepo domain signals reinforce workspace-aware CI and drift checks.');
103
- }
104
-
105
- if (keys.has('ecommerce')) {
106
- extraHooks.push('protect-secrets');
107
- reasons.push('Commerce and payment surfaces justify extra secret-handling discipline.');
108
- }
109
-
110
- if ((recommendedMcpPacks || []).length >= 3) {
111
- extraHooks.push('injection-defense');
112
- reasons.push('Multi-MCP posture increases the value of trust-boundary hooks.');
113
- }
114
-
115
- if (repoArchetype.repoClass.key === 'mobile-app' && !verificationBias) {
116
- verificationBias = 'mobile';
117
- }
118
-
119
- return {
120
- keys,
121
- reasons,
122
- extraHooks: unique(extraHooks),
123
- permissionOverride,
124
- governancePackOverride,
125
- verificationBias,
126
- ciShapeOverride,
127
- };
128
- }
129
-
130
- function recommendPlatformSupport(dir, platform, repoArchetype, recommendedDomainPacks = []) {
131
- const current = detectPlatforms(dir);
132
- const primary = current.length > 0 ? current[0] : platform;
133
- const recommended = current.length > 0 ? current : [platform];
134
- let strategy = 'single-platform-baseline';
135
- let rationale = 'Start from one governed primary platform before widening the surface area.';
136
- let optionalExpansion = null;
137
-
138
- if (recommended.length >= 2) {
139
- strategy = 'harmonize-current-platforms';
140
- rationale = 'Multiple AI platforms are already active, so the main priority is keeping them aligned instead of adding more.';
141
- } else if (repoArchetype.topology.key === 'monorepo' || repoArchetype.primaryWorkflow.key === 'governed-rollout' || repoArchetype.riskProfile.key === 'regulated') {
142
- strategy = 'primary-plus-review-surface';
143
- rationale = 'This repo benefits from one primary surface plus one secondary advisory/review surface once the baseline is stable.';
144
- optionalExpansion = primary === 'codex' ? 'claude' : 'codex';
145
- }
146
-
147
- const base = {
148
- current,
149
- primary,
150
- recommended,
151
- strategy,
152
- rationale,
153
- optionalExpansion,
154
- };
155
-
156
- return withExplainability(base, repoArchetype, recommendedDomainPacks, {
157
- evidence: [
158
- current.length > 0 ? `Detected platforms: ${current.join(', ')}` : `No existing platform config found; defaulting to ${platform}`,
159
- ],
160
- prerequisites: current.length >= 2
161
- ? ['Keep Harmony aligned before adding another platform surface.']
162
- : ['Stabilize the primary platform baseline before widening the tool surface.'],
163
- expectedBenefit: current.length >= 2
164
- ? 'Keeps active AI surfaces aligned so governance and review flows do not drift apart.'
165
- : 'Reduces setup sprawl by making one platform posture explicit before adding more.',
166
- rollbackSafety: 'Platform strategy is advisory only. You can stay on the current primary surface or revert secondary additions without touching source code.',
167
- });
168
- }
169
-
170
- function recommendPermissionProfile(repoArchetype, recommendedDomainPacks, domainInfluence) {
171
- if (repoArchetype.riskProfile.key === 'elevated') {
172
- return withExplainability({
173
- profile: getProfile('read-only'),
174
- rationale: 'Permissive runtime posture should be brought back under review before Nerviq writes into the repo.',
175
- }, repoArchetype, recommendedDomainPacks, {
176
- evidence: ['Elevated-risk posture detected from repo signals.'].concat(domainInfluence.reasons),
177
- prerequisites: ['Confirm discovery-only mode is acceptable for the next improvement pass.'],
178
- expectedBenefit: 'Prevents Nerviq from modifying the repo while the trust boundary is still unclear.',
179
- rollbackSafety: 'Switching back to a writable profile is a config-only change once the repo posture is explicit.',
180
- });
181
- }
182
-
183
- if (domainInfluence.permissionOverride) {
184
- const profile = getProfile(domainInfluence.permissionOverride);
185
- return withExplainability({
186
- profile,
187
- rationale: `Domain signals make ${profile.label.toLowerCase()} the safest recommended baseline for this repo.`,
188
- }, repoArchetype, recommendedDomainPacks, {
189
- evidence: domainInfluence.reasons,
190
- prerequisites: ['Keep proposal/export flows reviewable while adopting the recommended posture.'],
191
- expectedBenefit: 'Aligns repo writes and review expectations to the domain risk and contributor model.',
192
- rollbackSafety: 'Permission profiles are declarative. Move back to another profile once the repo proves it can support faster writes safely.',
193
- });
194
- }
195
-
196
- if (repoArchetype.riskProfile.key === 'regulated' || repoArchetype.primaryWorkflow.key === 'governed-rollout') {
197
- return withExplainability({
198
- profile: getProfile('suggest-only'),
199
- rationale: 'Governed or security-sensitive repos should begin from proposal/export flows instead of direct writes.',
200
- }, repoArchetype, recommendedDomainPacks, {
201
- evidence: ['Governed rollout or regulated posture detected.'],
202
- prerequisites: ['Use plan/export artifacts for any write-bearing rollout.'],
203
- expectedBenefit: 'Creates a reviewable path to adoption without blocking analysis and planning.',
204
- rollbackSafety: 'Profiles can be relaxed later without changing repo files.',
205
- });
206
- }
207
-
208
- if (repoArchetype.maturity.key === 'none' || repoArchetype.maturity.key === 'starter') {
209
- return withExplainability({
210
- profile: getProfile('safe-write'),
211
- rationale: 'Bootstrap repos need a writable baseline, but still with visible rollback and no overwrites.',
212
- }, repoArchetype, recommendedDomainPacks, {
213
- evidence: ['Repo maturity is starter-level or missing governed assets.'],
214
- prerequisites: ['Keep rollback artifacts enabled before broader apply flows.'],
215
- expectedBenefit: 'Lets Nerviq establish a baseline quickly without flattening existing work.',
216
- rollbackSafety: 'Safe-write preserves existing files and keeps rollback artifacts for removals.',
217
- });
218
- }
219
-
220
- if (repoArchetype.repoClass.key === 'library-sdk' || repoArchetype.stackFamily.key === 'docs') {
221
- return withExplainability({
222
- profile: getProfile('suggest-only'),
223
- rationale: 'Contributor-sensitive or docs-heavy repos benefit from review-first proposal flows.',
224
- }, repoArchetype, recommendedDomainPacks, {
225
- evidence: ['Library or docs-oriented repo class detected.'],
226
- prerequisites: ['Use human review as the merge gate for generated changes.'],
227
- expectedBenefit: 'Protects external contributors from unexpected automation while keeping Nerviq useful.',
228
- rollbackSafety: 'No direct writes are implied, so there is nothing operational to roll back.',
229
- });
230
- }
231
-
232
- if (repoArchetype.repoClass.key === 'developer-tool' && repoArchetype.maturity.key === 'mature') {
233
- return withExplainability({
234
- profile: getProfile('power-user'),
235
- rationale: 'Mature developer tooling repos can tolerate faster iteration once the baseline is already explicit.',
236
- }, repoArchetype, recommendedDomainPacks, {
237
- evidence: ['Mature developer-tool repo detected.'],
238
- prerequisites: ['Keep trust settings, hooks, and rollback flows explicit before widening autonomy.'],
239
- expectedBenefit: 'Speeds iteration for maintainers who already have a stable governed baseline.',
240
- rollbackSafety: 'Move back to safe-write or suggest-only if the broader autonomy level proves noisy.',
241
- });
242
- }
243
-
244
- return withExplainability({
245
- profile: getProfile('safe-write'),
246
- rationale: 'A safe writable baseline is the default operating posture for product repos after first contact.',
247
- }, repoArchetype, recommendedDomainPacks, {
248
- evidence: domainInfluence.reasons,
249
- prerequisites: ['Keep visible rollback and plan export available for non-trivial changes.'],
250
- expectedBenefit: 'Lets the repo move from advisory mode into real guided setup work without bypassing guardrails.',
251
- rollbackSafety: 'Safe-write avoids overwriting existing assets and keeps rollback visible.',
252
- });
253
- }
254
-
255
- function recommendGovernancePack(repoArchetype, recommendedDomainPacks, domainInfluence) {
256
- if (domainInfluence.governancePackOverride) {
257
- const pack = getPolicyPack(domainInfluence.governancePackOverride);
258
- return withExplainability({
259
- pack,
260
- rationale: `Domain signals make ${pack.label.toLowerCase()} the best governance baseline for this repo.`,
261
- }, repoArchetype, recommendedDomainPacks, {
262
- evidence: domainInfluence.reasons,
263
- prerequisites: ['Apply the pack after confirming the repo owner agrees with the rollout style.'],
264
- expectedBenefit: 'Narrows Nerviq to the modules and rollout posture that fit this repo instead of generic defaults.',
265
- rollbackSafety: 'Policy packs are compositional. You can step back to a lighter pack without rewriting application code.',
266
- });
267
- }
268
-
269
- if (repoArchetype.riskProfile.key === 'regulated') {
270
- return withExplainability({
271
- pack: getPolicyPack('regulated-lite'),
272
- rationale: 'This repo needs auditable rollout defaults more than raw automation breadth.',
273
- }, repoArchetype, recommendedDomainPacks, {
274
- evidence: ['Regulated risk posture detected.'],
275
- prerequisites: ['Keep activity artifacts and rollback manifests in the rollout path.'],
276
- expectedBenefit: 'Adds auditability without requiring a full enterprise control plane from day one.',
277
- rollbackSafety: 'The pack changes the recommended rollout modules, not the application architecture.',
278
- });
279
- }
280
-
281
- if (repoArchetype.repoClass.key === 'library-sdk' || repoArchetype.stackFamily.key === 'docs') {
282
- return withExplainability({
283
- pack: getPolicyPack('oss-friendly'),
284
- rationale: 'Lower-footprint governance keeps contributor workflows reviewable without overfitting the repo.',
285
- }, repoArchetype, recommendedDomainPacks, {
286
- evidence: ['Contributor-sensitive or docs-oriented repo detected.'],
287
- prerequisites: ['Keep merge review human-owned for external-facing changes.'],
288
- expectedBenefit: 'Preserves contributor friendliness while still making AI posture explicit.',
289
- rollbackSafety: 'Reverting to baseline-engineering is a pack-level config change.',
290
- });
291
- }
292
-
293
- if (repoArchetype.primaryWorkflow.key === 'governed-rollout') {
294
- return withExplainability({
295
- pack: getPolicyPack('security-sensitive'),
296
- rationale: 'The workflow already points toward approvals, hooks, and reviewable rollout discipline.',
297
- }, repoArchetype, recommendedDomainPacks, {
298
- evidence: ['Governed rollout workflow detected.'],
299
- prerequisites: ['Confirm approvals and pre-merge review are real team behaviors, not just intent.'],
300
- expectedBenefit: 'Matches governance modules to the way the repo is already being operated.',
301
- rollbackSafety: 'You can drop back to baseline-engineering if the workflow proves lighter in practice.',
302
- });
303
- }
304
-
305
- return withExplainability({
306
- pack: getPolicyPack('baseline-engineering'),
307
- rationale: 'A pragmatic engineering baseline is the best starting point for this repo shape.',
308
- }, repoArchetype, recommendedDomainPacks, {
309
- evidence: domainInfluence.reasons,
310
- prerequisites: ['Confirm the repo wants a practical default before enabling domain-specific packs.'],
311
- expectedBenefit: 'Provides a stable baseline without forcing heavier governance where it is not justified.',
312
- rollbackSafety: 'Baseline packs are additive and can be swapped without touching product code.',
313
- });
314
- }
315
-
316
- function recommendHooks(repoArchetype, recommendedDomainPacks, domainInfluence) {
317
- const keys = ['protect-secrets', 'log-changes', 'session-init'];
318
-
319
- if (repoArchetype.stackFamily.key !== 'docs') {
320
- keys.push('on-edit-lint');
321
- }
322
- if (repoArchetype.workflowTraits.some((trait) => trait.key === 'tool-enriched')) {
323
- keys.push('injection-defense');
324
- }
325
- if (repoArchetype.primaryWorkflow.key === 'governed-rollout' || repoArchetype.topology.key === 'monorepo') {
326
- keys.push('trust-drift-check');
327
- }
328
- keys.push(...domainInfluence.extraHooks);
329
-
330
- return unique(keys)
331
- .map((key) => getHook(key))
332
- .filter(Boolean)
333
- .map((hook) => ({
334
- key: hook.key,
335
- label: hook.file,
336
- triggerPoint: hook.triggerPoint,
337
- matcher: hook.matcher || null,
338
- risk: hook.risk,
339
- rationale: hook.purpose,
340
- why: hook.purpose,
341
- evidence: buildEvidence(repoArchetype, recommendedDomainPacks, [
342
- hook.matcher ? `Trigger matcher: ${hook.matcher}` : `Trigger point: ${hook.triggerPoint}`,
343
- ].concat(domainInfluence.reasons)),
344
- prerequisites: unique([
345
- hook.triggerPoint === 'PostToolUse' ? 'Confirm the repo has the runtime/tooling needed for the hook script.' : null,
346
- hook.triggerPoint === 'PreToolUse' ? 'Review the block rules before enabling the hook in a shared repo.' : null,
347
- 'Validate hook runtime health with `nerviq doctor` after registration.',
348
- ]),
349
- expectedBenefit: hook.purpose,
350
- rollbackSafety: hook.rollbackPath,
351
- }));
352
- }
353
-
354
- function recommendVerificationProfile(repoArchetype, recommendedDomainPacks, domainInfluence) {
355
- const bias = domainInfluence.verificationBias || repoArchetype.stackFamily.key;
356
-
357
- if (bias === 'mobile') {
358
- return withExplainability({
359
- key: 'mobile-release-loop',
360
- label: 'Mobile release loop',
361
- required: ['test', 'lint/analyze', 'build'],
362
- optional: ['security-review'],
363
- rationale: 'Mobile repos need correctness, platform analysis, and build verification before rollout.',
364
- }, repoArchetype, recommendedDomainPacks, {
365
- evidence: domainInfluence.reasons,
366
- prerequisites: ['Document the concrete mobile test/analyze/build commands in repo instructions.'],
367
- expectedBenefit: 'Catches platform-specific regressions before they turn into emulator or release surprises.',
368
- rollbackSafety: 'Verification guidance is advisory and can be narrowed if the repo later proves lighter-weight.',
369
- });
370
- }
371
-
372
- if (bias === 'infra') {
373
- return withExplainability({
374
- key: 'infra-change-loop',
375
- label: 'Infra change loop',
376
- required: ['lint', 'validate/plan', 'build'],
377
- optional: ['security-review'],
378
- rationale: 'Infrastructure repos need validation and dry-run style checks before operational changes land.',
379
- }, repoArchetype, recommendedDomainPacks, {
380
- evidence: domainInfluence.reasons,
381
- prerequisites: ['Document validate/plan commands before enabling wider apply flows.'],
382
- expectedBenefit: 'Shifts repo changes toward plan-first rollout and catches operational blast radius earlier.',
383
- rollbackSafety: 'Verification loops can be narrowed later without undoing generated config.',
384
- });
385
- }
386
-
387
- if (repoArchetype.stackFamily.key === 'docs') {
388
- return withExplainability({
389
- key: 'content-publish-loop',
390
- label: 'Content publish loop',
391
- required: ['build', 'link/content checks'],
392
- optional: ['lint'],
393
- rationale: 'Docs/content repos need publish safety more than heavy runtime verification.',
394
- }, repoArchetype, recommendedDomainPacks, {
395
- evidence: ['Docs / content stack family detected.'],
396
- prerequisites: ['Make the publish/build command explicit in repo instructions.'],
397
- expectedBenefit: 'Keeps content repos fast while still protecting against broken docs deploys.',
398
- rollbackSafety: 'This is guidance only; adding or removing checks does not mutate the repo automatically.',
399
- });
400
- }
401
-
402
- if (bias === 'pipeline' || repoArchetype.stackFamily.key === 'data-ml') {
403
- return withExplainability({
404
- key: 'pipeline-verification-loop',
405
- label: 'Pipeline verification loop',
406
- required: ['test', 'lint', 'build'],
407
- optional: ['security-review', 'data/pipeline smoke check'],
408
- rationale: 'Data and ML repos still need code verification, but often also benefit from pipeline sanity checks.',
409
- }, repoArchetype, recommendedDomainPacks, {
410
- evidence: domainInfluence.reasons,
411
- prerequisites: ['Define at least one lightweight pipeline smoke check if the repo owns jobs or model flows.'],
412
- expectedBenefit: 'Protects repo correctness and catches broken pipeline assumptions before rollout.',
413
- rollbackSafety: 'Pipeline smoke checks are additive and can be disabled without impacting baseline code generation.',
414
- });
415
- }
416
-
417
- return withExplainability({
418
- key: 'application-verification-loop',
419
- label: 'Application verification loop',
420
- required: ['test', 'lint', 'build'],
421
- optional: repoArchetype.riskProfile.key === 'regulated' ? ['security-review'] : [],
422
- rationale: 'Product repos should default to explicit test, lint, and build loops before completion.',
423
- }, repoArchetype, recommendedDomainPacks, {
424
- evidence: domainInfluence.reasons,
425
- prerequisites: ['Document repo-specific test/lint/build commands if they are not already explicit.'],
426
- expectedBenefit: 'Creates a predictable verification floor for day-to-day AI-assisted edits.',
427
- rollbackSafety: 'Verification recommendations can be tuned over time without reworking repo structure.',
428
- });
429
- }
430
-
431
- function recommendCiShape(repoArchetype, recommendedDomainPacks, domainInfluence) {
432
- const ciBias = domainInfluence.ciShapeOverride;
433
-
434
- if (repoArchetype.topology.key === 'monorepo' || ciBias === 'workspace-pr-gate') {
435
- return withExplainability({
436
- key: 'workspace-pr-gate',
437
- label: 'Workspace-aware PR gate',
438
- steps: [
439
- 'Run `nerviq audit --diff-only` in PRs for changed-file governance feedback',
440
- 'Run workspace-aware audits for touched packages before merge',
441
- 'Save tagged full snapshots on baseline and release checkpoints',
442
- ],
443
- rationale: 'Monorepos need scoped PR checks plus periodic full-root evidence.',
444
- }, repoArchetype, recommendedDomainPacks, {
445
- evidence: ['Monorepo or workspace-oriented domain signals detected.'].concat(domainInfluence.reasons),
446
- prerequisites: ['Adopt workspace-aware audit coverage before relying on per-package score semantics.'],
447
- expectedBenefit: 'Keeps package-local drift visible without losing root governance posture.',
448
- rollbackSafety: 'CI shape is an operating recommendation; you can revert to a simpler gate without touching product code.',
449
- });
450
- }
451
-
452
- if (repoArchetype.primaryWorkflow.key === 'governed-rollout' || repoArchetype.riskProfile.key === 'regulated' || ciBias === 'governed-pr-gate') {
453
- return withExplainability({
454
- key: 'governed-pr-gate',
455
- label: 'Governed PR gate',
456
- steps: [
457
- 'Run `nerviq audit --diff-only` on PRs',
458
- 'Run a full `nerviq audit --threshold` check before merge or release',
459
- 'Use tagged snapshots (`baseline`, `post-fix`, `pre-release`) for traceable history',
460
- ],
461
- rationale: 'Governed repos need PR feedback plus full-posture evidence before risky changes are accepted.',
462
- }, repoArchetype, recommendedDomainPacks, {
463
- evidence: domainInfluence.reasons,
464
- prerequisites: ['Make baseline and release snapshot milestones part of the merge process.'],
465
- expectedBenefit: 'Turns Nerviq into a repeatable part of repo governance instead of a one-off setup step.',
466
- rollbackSafety: 'CI shape is policy-level guidance and can be dialed back if the repo does not need full governed gates.',
467
- });
468
- }
469
-
470
- if (repoArchetype.maturity.key === 'none' || repoArchetype.maturity.key === 'starter') {
471
- return withExplainability({
472
- key: 'bootstrap-ci',
473
- label: 'Bootstrap CI baseline',
474
- steps: [
475
- 'Start with a full `nerviq audit` on the default branch',
476
- 'Introduce `--diff-only` PR checks once the repo has a stable baseline',
477
- 'Capture a named baseline snapshot before broad apply flows',
478
- ],
479
- rationale: 'Early-stage repos need one stable baseline before diff-aware automation becomes meaningful.',
480
- }, repoArchetype, recommendedDomainPacks, {
481
- evidence: ['Starter or missing managed baseline detected.'],
482
- prerequisites: ['Capture the first clean baseline snapshot before diff-only enforcement.'],
483
- expectedBenefit: 'Avoids noisy CI by introducing governance incrementally instead of all at once.',
484
- rollbackSafety: 'Bootstrap CI is intentionally minimal and can graduate into stronger gates later.',
485
- });
486
- }
487
-
488
- return withExplainability({
489
- key: 'standard-pr-gate',
490
- label: 'Standard PR gate',
491
- steps: [
492
- 'Use `nerviq audit --diff-only` for PR-level feedback',
493
- 'Run periodic full audits to keep score semantics grounded in live repo state',
494
- 'Capture tagged snapshots around major fixes or releases',
495
- ],
496
- rationale: 'The repo is ready for regular diff-aware checks plus scheduled full-posture verification.',
497
- }, repoArchetype, recommendedDomainPacks, {
498
- evidence: domainInfluence.reasons,
499
- prerequisites: ['Keep periodic full audits scheduled so diff-only checks stay grounded.'],
500
- expectedBenefit: 'Makes Nerviq part of the normal PR loop without requiring heavy governed rollout overhead.',
501
- rollbackSafety: 'This operating mode can be tightened or relaxed as team habits change.',
502
- });
503
- }
504
-
505
- function buildOperatingProfile(options) {
506
- const {
507
- dir,
508
- platform,
509
- repoArchetype,
510
- recommendedDomainPacks = [],
511
- recommendedMcpPacks = [],
512
- } = options || {};
513
-
514
- const domainInfluence = buildDomainInfluence(repoArchetype, recommendedDomainPacks, recommendedMcpPacks);
515
- const platformSupport = recommendPlatformSupport(dir, platform, repoArchetype, recommendedDomainPacks);
516
- const permission = recommendPermissionProfile(repoArchetype, recommendedDomainPacks, domainInfluence);
517
- const governancePack = recommendGovernancePack(repoArchetype, recommendedDomainPacks, domainInfluence);
518
- const hooks = recommendHooks(repoArchetype, recommendedDomainPacks, domainInfluence);
519
- const verification = recommendVerificationProfile(repoArchetype, recommendedDomainPacks, domainInfluence);
520
- const ciShape = recommendCiShape(repoArchetype, recommendedDomainPacks, domainInfluence);
521
-
522
- const snapshotTags = repoArchetype.primaryWorkflow.key === 'governed-rollout'
523
- ? ['baseline', 'post-fix', 'pre-release']
524
- : ['baseline', 'post-fix'];
525
-
526
- return {
527
- key: `${repoArchetype.key}:${permission.profile.key}`,
528
- label: `${repoArchetype.label} operating profile`,
529
- summary: `Recommended posture: ${permission.profile.label}, ${ciShape.label.toLowerCase()}, and ${hooks.length} starter hooks for a ${repoArchetype.label.toLowerCase()}.`,
530
- platformSupport,
531
- permissionProfile: {
532
- key: permission.profile.key,
533
- label: permission.profile.label,
534
- risk: permission.profile.risk,
535
- rationale: permission.rationale,
536
- why: permission.why,
537
- evidence: permission.evidence,
538
- prerequisites: permission.prerequisites,
539
- expectedBenefit: permission.expectedBenefit,
540
- rollbackSafety: permission.rollbackSafety,
541
- },
542
- governancePack: {
543
- key: governancePack.pack.key,
544
- label: governancePack.pack.label,
545
- modules: governancePack.pack.modules,
546
- rationale: governancePack.rationale,
547
- why: governancePack.why,
548
- evidence: governancePack.evidence,
549
- prerequisites: governancePack.prerequisites,
550
- expectedBenefit: governancePack.expectedBenefit,
551
- rollbackSafety: governancePack.rollbackSafety,
552
- },
553
- hooks,
554
- verification,
555
- ciShape,
556
- governanceDefaults: {
557
- benchmarkBeforeApply: repoArchetype.riskProfile.key === 'regulated' || repoArchetype.maturity.key === 'mature',
558
- exportPlanBeforeWrites: permission.profile.key !== 'safe-write' || repoArchetype.maturity.key !== 'none',
559
- useDiffOnlyInPrs: ciShape.key !== 'bootstrap-ci',
560
- harmonizePlatforms: platformSupport.current.length >= 2,
561
- snapshotTags,
562
- recommendedDomainPacks: recommendedDomainPacks.map((pack) => pack.key),
563
- recommendedMcpPacks: recommendedMcpPacks.map((pack) => pack.key),
564
- },
565
- domainInfluence: {
566
- keys: [...domainInfluence.keys],
567
- reasons: domainInfluence.reasons,
568
- },
569
- };
570
- }
571
-
572
- module.exports = {
573
- buildOperatingProfile,
574
- };
1
+ 'use strict';
2
+
3
+ const { detectPlatforms } = require('./public-api');
4
+ const { PERMISSION_PROFILES, HOOK_REGISTRY, POLICY_PACKS } = require('./governance');
5
+
6
+ function unique(values) {
7
+ return [...new Set((values || []).filter(Boolean))];
8
+ }
9
+
10
+ function getProfile(key) {
11
+ return PERMISSION_PROFILES.find((profile) => profile.key === key) || PERMISSION_PROFILES[0];
12
+ }
13
+
14
+ function getPolicyPack(key) {
15
+ return POLICY_PACKS.find((pack) => pack.key === key) || POLICY_PACKS[0];
16
+ }
17
+
18
+ function getHook(key) {
19
+ return HOOK_REGISTRY.find((hook) => hook.key === key);
20
+ }
21
+
22
+ function buildEvidence(repoArchetype, recommendedDomainPacks = [], extras = []) {
23
+ const packLabels = (recommendedDomainPacks || []).map((pack) => pack.label).slice(0, 2);
24
+ return unique([
25
+ `Archetype: ${repoArchetype.label}`,
26
+ `Workflow: ${repoArchetype.primaryWorkflow.label}`,
27
+ `Risk: ${repoArchetype.riskProfile.label}`,
28
+ packLabels.length > 0 ? `Domain packs: ${packLabels.join(', ')}` : null,
29
+ ...(repoArchetype.signals || []).slice(0, 2),
30
+ ...extras,
31
+ ]).slice(0, 5);
32
+ }
33
+
34
+ function withExplainability(base, repoArchetype, recommendedDomainPacks, options = {}) {
35
+ const {
36
+ evidence = [],
37
+ prerequisites = [],
38
+ expectedBenefit = '',
39
+ rollbackSafety = '',
40
+ } = options;
41
+
42
+ return {
43
+ ...base,
44
+ why: base.rationale,
45
+ evidence: buildEvidence(repoArchetype, recommendedDomainPacks, evidence),
46
+ prerequisites: unique(prerequisites),
47
+ expectedBenefit,
48
+ rollbackSafety,
49
+ };
50
+ }
51
+
52
+ function buildDomainInfluence(repoArchetype, recommendedDomainPacks = [], recommendedMcpPacks = []) {
53
+ const keys = new Set((recommendedDomainPacks || []).map((pack) => pack.key));
54
+ const reasons = [];
55
+ const extraHooks = [];
56
+ let permissionOverride = null;
57
+ let governancePackOverride = null;
58
+ let verificationBias = null;
59
+ let ciShapeOverride = null;
60
+
61
+ if (keys.has('security-focused') || keys.has('regulated-lite')) {
62
+ permissionOverride = 'suggest-only';
63
+ governancePackOverride = 'regulated-lite';
64
+ reasons.push('Security-focused or regulated domain signals push the repo toward review-first posture.');
65
+ }
66
+
67
+ if (keys.has('enterprise-governed')) {
68
+ permissionOverride = 'suggest-only';
69
+ governancePackOverride = governancePackOverride || 'security-sensitive';
70
+ ciShapeOverride = 'governed-pr-gate';
71
+ extraHooks.push('trust-drift-check');
72
+ reasons.push('Enterprise-governed domain signals favor approvals, traceability, and trust-drift checks.');
73
+ }
74
+
75
+ if (keys.has('infra-platform') || keys.has('devops-cicd')) {
76
+ governancePackOverride = governancePackOverride || 'security-sensitive';
77
+ verificationBias = 'infra';
78
+ ciShapeOverride = ciShapeOverride || 'governed-pr-gate';
79
+ extraHooks.push('trust-drift-check');
80
+ reasons.push('Infra / CI domain signals increase the value of plan-first rollout and trust-drift validation.');
81
+ }
82
+
83
+ if (keys.has('ai-ml') || keys.has('data-pipeline')) {
84
+ verificationBias = 'pipeline';
85
+ reasons.push('Pipeline-oriented domains need repeatable verification and state-aware review loops.');
86
+ }
87
+
88
+ if (keys.has('mobile')) {
89
+ verificationBias = 'mobile';
90
+ reasons.push('Mobile domain signals shift the recommended loop toward analyze + build verification.');
91
+ }
92
+
93
+ if (keys.has('oss-library') || keys.has('docs-content')) {
94
+ permissionOverride = permissionOverride || 'suggest-only';
95
+ governancePackOverride = governancePackOverride || 'oss-friendly';
96
+ reasons.push('Contributor-sensitive or docs-heavy repos benefit from lighter review-first governance.');
97
+ }
98
+
99
+ if (keys.has('monorepo')) {
100
+ ciShapeOverride = 'workspace-pr-gate';
101
+ extraHooks.push('trust-drift-check');
102
+ reasons.push('Monorepo domain signals reinforce workspace-aware CI and drift checks.');
103
+ }
104
+
105
+ if (keys.has('ecommerce')) {
106
+ extraHooks.push('protect-secrets');
107
+ reasons.push('Commerce and payment surfaces justify extra secret-handling discipline.');
108
+ }
109
+
110
+ if ((recommendedMcpPacks || []).length >= 3) {
111
+ extraHooks.push('injection-defense');
112
+ reasons.push('Multi-MCP posture increases the value of trust-boundary hooks.');
113
+ }
114
+
115
+ if (repoArchetype.repoClass.key === 'mobile-app' && !verificationBias) {
116
+ verificationBias = 'mobile';
117
+ }
118
+
119
+ return {
120
+ keys,
121
+ reasons,
122
+ extraHooks: unique(extraHooks),
123
+ permissionOverride,
124
+ governancePackOverride,
125
+ verificationBias,
126
+ ciShapeOverride,
127
+ };
128
+ }
129
+
130
+ function recommendPlatformSupport(dir, platform, repoArchetype, recommendedDomainPacks = []) {
131
+ const current = detectPlatforms(dir);
132
+ const primary = current.length > 0 ? current[0] : platform;
133
+ const recommended = current.length > 0 ? current : [platform];
134
+ let strategy = 'single-platform-baseline';
135
+ let rationale = 'Start from one governed primary platform before widening the surface area.';
136
+ let optionalExpansion = null;
137
+
138
+ if (recommended.length >= 2) {
139
+ strategy = 'harmonize-current-platforms';
140
+ rationale = 'Multiple AI platforms are already active, so the main priority is keeping them aligned instead of adding more.';
141
+ } else if (repoArchetype.topology.key === 'monorepo' || repoArchetype.primaryWorkflow.key === 'governed-rollout' || repoArchetype.riskProfile.key === 'regulated') {
142
+ strategy = 'primary-plus-review-surface';
143
+ rationale = 'This repo benefits from one primary surface plus one secondary advisory/review surface once the baseline is stable.';
144
+ optionalExpansion = primary === 'codex' ? 'claude' : 'codex';
145
+ }
146
+
147
+ const base = {
148
+ current,
149
+ primary,
150
+ recommended,
151
+ strategy,
152
+ rationale,
153
+ optionalExpansion,
154
+ };
155
+
156
+ return withExplainability(base, repoArchetype, recommendedDomainPacks, {
157
+ evidence: [
158
+ current.length > 0 ? `Detected platforms: ${current.join(', ')}` : `No existing platform config found; defaulting to ${platform}`,
159
+ ],
160
+ prerequisites: current.length >= 2
161
+ ? ['Keep Harmony aligned before adding another platform surface.']
162
+ : ['Stabilize the primary platform baseline before widening the tool surface.'],
163
+ expectedBenefit: current.length >= 2
164
+ ? 'Keeps active AI surfaces aligned so governance and review flows do not drift apart.'
165
+ : 'Reduces setup sprawl by making one platform posture explicit before adding more.',
166
+ rollbackSafety: 'Platform strategy is advisory only. You can stay on the current primary surface or revert secondary additions without touching source code.',
167
+ });
168
+ }
169
+
170
+ function recommendPermissionProfile(repoArchetype, recommendedDomainPacks, domainInfluence) {
171
+ if (repoArchetype.riskProfile.key === 'elevated') {
172
+ return withExplainability({
173
+ profile: getProfile('read-only'),
174
+ rationale: 'Permissive runtime posture should be brought back under review before Nerviq writes into the repo.',
175
+ }, repoArchetype, recommendedDomainPacks, {
176
+ evidence: ['Elevated-risk posture detected from repo signals.'].concat(domainInfluence.reasons),
177
+ prerequisites: ['Confirm discovery-only mode is acceptable for the next improvement pass.'],
178
+ expectedBenefit: 'Prevents Nerviq from modifying the repo while the trust boundary is still unclear.',
179
+ rollbackSafety: 'Switching back to a writable profile is a config-only change once the repo posture is explicit.',
180
+ });
181
+ }
182
+
183
+ if (domainInfluence.permissionOverride) {
184
+ const profile = getProfile(domainInfluence.permissionOverride);
185
+ return withExplainability({
186
+ profile,
187
+ rationale: `Domain signals make ${profile.label.toLowerCase()} the safest recommended baseline for this repo.`,
188
+ }, repoArchetype, recommendedDomainPacks, {
189
+ evidence: domainInfluence.reasons,
190
+ prerequisites: ['Keep proposal/export flows reviewable while adopting the recommended posture.'],
191
+ expectedBenefit: 'Aligns repo writes and review expectations to the domain risk and contributor model.',
192
+ rollbackSafety: 'Permission profiles are declarative. Move back to another profile once the repo proves it can support faster writes safely.',
193
+ });
194
+ }
195
+
196
+ if (repoArchetype.riskProfile.key === 'regulated' || repoArchetype.primaryWorkflow.key === 'governed-rollout') {
197
+ return withExplainability({
198
+ profile: getProfile('suggest-only'),
199
+ rationale: 'Governed or security-sensitive repos should begin from proposal/export flows instead of direct writes.',
200
+ }, repoArchetype, recommendedDomainPacks, {
201
+ evidence: ['Governed rollout or regulated posture detected.'],
202
+ prerequisites: ['Use plan/export artifacts for any write-bearing rollout.'],
203
+ expectedBenefit: 'Creates a reviewable path to adoption without blocking analysis and planning.',
204
+ rollbackSafety: 'Profiles can be relaxed later without changing repo files.',
205
+ });
206
+ }
207
+
208
+ if (repoArchetype.maturity.key === 'none' || repoArchetype.maturity.key === 'starter') {
209
+ return withExplainability({
210
+ profile: getProfile('safe-write'),
211
+ rationale: 'Bootstrap repos need a writable baseline, but still with visible rollback and no overwrites.',
212
+ }, repoArchetype, recommendedDomainPacks, {
213
+ evidence: ['Repo maturity is starter-level or missing governed assets.'],
214
+ prerequisites: ['Keep rollback artifacts enabled before broader apply flows.'],
215
+ expectedBenefit: 'Lets Nerviq establish a baseline quickly without flattening existing work.',
216
+ rollbackSafety: 'Safe-write preserves existing files and keeps rollback artifacts for removals.',
217
+ });
218
+ }
219
+
220
+ if (repoArchetype.repoClass.key === 'library-sdk' || repoArchetype.stackFamily.key === 'docs') {
221
+ return withExplainability({
222
+ profile: getProfile('suggest-only'),
223
+ rationale: 'Contributor-sensitive or docs-heavy repos benefit from review-first proposal flows.',
224
+ }, repoArchetype, recommendedDomainPacks, {
225
+ evidence: ['Library or docs-oriented repo class detected.'],
226
+ prerequisites: ['Use human review as the merge gate for generated changes.'],
227
+ expectedBenefit: 'Protects external contributors from unexpected automation while keeping Nerviq useful.',
228
+ rollbackSafety: 'No direct writes are implied, so there is nothing operational to roll back.',
229
+ });
230
+ }
231
+
232
+ if (repoArchetype.repoClass.key === 'developer-tool' && repoArchetype.maturity.key === 'mature') {
233
+ return withExplainability({
234
+ profile: getProfile('power-user'),
235
+ rationale: 'Mature developer tooling repos can tolerate faster iteration once the baseline is already explicit.',
236
+ }, repoArchetype, recommendedDomainPacks, {
237
+ evidence: ['Mature developer-tool repo detected.'],
238
+ prerequisites: ['Keep trust settings, hooks, and rollback flows explicit before widening autonomy.'],
239
+ expectedBenefit: 'Speeds iteration for maintainers who already have a stable governed baseline.',
240
+ rollbackSafety: 'Move back to safe-write or suggest-only if the broader autonomy level proves noisy.',
241
+ });
242
+ }
243
+
244
+ return withExplainability({
245
+ profile: getProfile('safe-write'),
246
+ rationale: 'A safe writable baseline is the default operating posture for product repos after first contact.',
247
+ }, repoArchetype, recommendedDomainPacks, {
248
+ evidence: domainInfluence.reasons,
249
+ prerequisites: ['Keep visible rollback and plan export available for non-trivial changes.'],
250
+ expectedBenefit: 'Lets the repo move from advisory mode into real guided setup work without bypassing guardrails.',
251
+ rollbackSafety: 'Safe-write avoids overwriting existing assets and keeps rollback visible.',
252
+ });
253
+ }
254
+
255
+ function recommendGovernancePack(repoArchetype, recommendedDomainPacks, domainInfluence) {
256
+ if (domainInfluence.governancePackOverride) {
257
+ const pack = getPolicyPack(domainInfluence.governancePackOverride);
258
+ return withExplainability({
259
+ pack,
260
+ rationale: `Domain signals make ${pack.label.toLowerCase()} the best governance baseline for this repo.`,
261
+ }, repoArchetype, recommendedDomainPacks, {
262
+ evidence: domainInfluence.reasons,
263
+ prerequisites: ['Apply the pack after confirming the repo owner agrees with the rollout style.'],
264
+ expectedBenefit: 'Narrows Nerviq to the modules and rollout posture that fit this repo instead of generic defaults.',
265
+ rollbackSafety: 'Policy packs are compositional. You can step back to a lighter pack without rewriting application code.',
266
+ });
267
+ }
268
+
269
+ if (repoArchetype.riskProfile.key === 'regulated') {
270
+ return withExplainability({
271
+ pack: getPolicyPack('regulated-lite'),
272
+ rationale: 'This repo needs auditable rollout defaults more than raw automation breadth.',
273
+ }, repoArchetype, recommendedDomainPacks, {
274
+ evidence: ['Regulated risk posture detected.'],
275
+ prerequisites: ['Keep activity artifacts and rollback manifests in the rollout path.'],
276
+ expectedBenefit: 'Adds auditability without requiring a full enterprise control plane from day one.',
277
+ rollbackSafety: 'The pack changes the recommended rollout modules, not the application architecture.',
278
+ });
279
+ }
280
+
281
+ if (repoArchetype.repoClass.key === 'library-sdk' || repoArchetype.stackFamily.key === 'docs') {
282
+ return withExplainability({
283
+ pack: getPolicyPack('oss-friendly'),
284
+ rationale: 'Lower-footprint governance keeps contributor workflows reviewable without overfitting the repo.',
285
+ }, repoArchetype, recommendedDomainPacks, {
286
+ evidence: ['Contributor-sensitive or docs-oriented repo detected.'],
287
+ prerequisites: ['Keep merge review human-owned for external-facing changes.'],
288
+ expectedBenefit: 'Preserves contributor friendliness while still making AI posture explicit.',
289
+ rollbackSafety: 'Reverting to baseline-engineering is a pack-level config change.',
290
+ });
291
+ }
292
+
293
+ if (repoArchetype.primaryWorkflow.key === 'governed-rollout') {
294
+ return withExplainability({
295
+ pack: getPolicyPack('security-sensitive'),
296
+ rationale: 'The workflow already points toward approvals, hooks, and reviewable rollout discipline.',
297
+ }, repoArchetype, recommendedDomainPacks, {
298
+ evidence: ['Governed rollout workflow detected.'],
299
+ prerequisites: ['Confirm approvals and pre-merge review are real team behaviors, not just intent.'],
300
+ expectedBenefit: 'Matches governance modules to the way the repo is already being operated.',
301
+ rollbackSafety: 'You can drop back to baseline-engineering if the workflow proves lighter in practice.',
302
+ });
303
+ }
304
+
305
+ return withExplainability({
306
+ pack: getPolicyPack('baseline-engineering'),
307
+ rationale: 'A pragmatic engineering baseline is the best starting point for this repo shape.',
308
+ }, repoArchetype, recommendedDomainPacks, {
309
+ evidence: domainInfluence.reasons,
310
+ prerequisites: ['Confirm the repo wants a practical default before enabling domain-specific packs.'],
311
+ expectedBenefit: 'Provides a stable baseline without forcing heavier governance where it is not justified.',
312
+ rollbackSafety: 'Baseline packs are additive and can be swapped without touching product code.',
313
+ });
314
+ }
315
+
316
+ function recommendHooks(repoArchetype, recommendedDomainPacks, domainInfluence) {
317
+ const keys = ['protect-secrets', 'log-changes', 'session-init'];
318
+
319
+ if (repoArchetype.stackFamily.key !== 'docs') {
320
+ keys.push('on-edit-lint');
321
+ }
322
+ if (repoArchetype.workflowTraits.some((trait) => trait.key === 'tool-enriched')) {
323
+ keys.push('injection-defense');
324
+ }
325
+ if (repoArchetype.primaryWorkflow.key === 'governed-rollout' || repoArchetype.topology.key === 'monorepo') {
326
+ keys.push('trust-drift-check');
327
+ }
328
+ keys.push(...domainInfluence.extraHooks);
329
+
330
+ return unique(keys)
331
+ .map((key) => getHook(key))
332
+ .filter(Boolean)
333
+ .map((hook) => ({
334
+ key: hook.key,
335
+ label: hook.file,
336
+ triggerPoint: hook.triggerPoint,
337
+ matcher: hook.matcher || null,
338
+ risk: hook.risk,
339
+ rationale: hook.purpose,
340
+ why: hook.purpose,
341
+ evidence: buildEvidence(repoArchetype, recommendedDomainPacks, [
342
+ hook.matcher ? `Trigger matcher: ${hook.matcher}` : `Trigger point: ${hook.triggerPoint}`,
343
+ ].concat(domainInfluence.reasons)),
344
+ prerequisites: unique([
345
+ hook.triggerPoint === 'PostToolUse' ? 'Confirm the repo has the runtime/tooling needed for the hook script.' : null,
346
+ hook.triggerPoint === 'PreToolUse' ? 'Review the block rules before enabling the hook in a shared repo.' : null,
347
+ 'Validate hook runtime health with `nerviq doctor` after registration.',
348
+ ]),
349
+ expectedBenefit: hook.purpose,
350
+ rollbackSafety: hook.rollbackPath,
351
+ }));
352
+ }
353
+
354
+ function recommendVerificationProfile(repoArchetype, recommendedDomainPacks, domainInfluence) {
355
+ const bias = domainInfluence.verificationBias || repoArchetype.stackFamily.key;
356
+
357
+ if (bias === 'mobile') {
358
+ return withExplainability({
359
+ key: 'mobile-release-loop',
360
+ label: 'Mobile release loop',
361
+ required: ['test', 'lint/analyze', 'build'],
362
+ optional: ['security-review'],
363
+ rationale: 'Mobile repos need correctness, platform analysis, and build verification before rollout.',
364
+ }, repoArchetype, recommendedDomainPacks, {
365
+ evidence: domainInfluence.reasons,
366
+ prerequisites: ['Document the concrete mobile test/analyze/build commands in repo instructions.'],
367
+ expectedBenefit: 'Catches platform-specific regressions before they turn into emulator or release surprises.',
368
+ rollbackSafety: 'Verification guidance is advisory and can be narrowed if the repo later proves lighter-weight.',
369
+ });
370
+ }
371
+
372
+ if (bias === 'infra') {
373
+ return withExplainability({
374
+ key: 'infra-change-loop',
375
+ label: 'Infra change loop',
376
+ required: ['lint', 'validate/plan', 'build'],
377
+ optional: ['security-review'],
378
+ rationale: 'Infrastructure repos need validation and dry-run style checks before operational changes land.',
379
+ }, repoArchetype, recommendedDomainPacks, {
380
+ evidence: domainInfluence.reasons,
381
+ prerequisites: ['Document validate/plan commands before enabling wider apply flows.'],
382
+ expectedBenefit: 'Shifts repo changes toward plan-first rollout and catches operational blast radius earlier.',
383
+ rollbackSafety: 'Verification loops can be narrowed later without undoing generated config.',
384
+ });
385
+ }
386
+
387
+ if (repoArchetype.stackFamily.key === 'docs') {
388
+ return withExplainability({
389
+ key: 'content-publish-loop',
390
+ label: 'Content publish loop',
391
+ required: ['build', 'link/content checks'],
392
+ optional: ['lint'],
393
+ rationale: 'Docs/content repos need publish safety more than heavy runtime verification.',
394
+ }, repoArchetype, recommendedDomainPacks, {
395
+ evidence: ['Docs / content stack family detected.'],
396
+ prerequisites: ['Make the publish/build command explicit in repo instructions.'],
397
+ expectedBenefit: 'Keeps content repos fast while still protecting against broken docs deploys.',
398
+ rollbackSafety: 'This is guidance only; adding or removing checks does not mutate the repo automatically.',
399
+ });
400
+ }
401
+
402
+ if (bias === 'pipeline' || repoArchetype.stackFamily.key === 'data-ml') {
403
+ return withExplainability({
404
+ key: 'pipeline-verification-loop',
405
+ label: 'Pipeline verification loop',
406
+ required: ['test', 'lint', 'build'],
407
+ optional: ['security-review', 'data/pipeline smoke check'],
408
+ rationale: 'Data and ML repos still need code verification, but often also benefit from pipeline sanity checks.',
409
+ }, repoArchetype, recommendedDomainPacks, {
410
+ evidence: domainInfluence.reasons,
411
+ prerequisites: ['Define at least one lightweight pipeline smoke check if the repo owns jobs or model flows.'],
412
+ expectedBenefit: 'Protects repo correctness and catches broken pipeline assumptions before rollout.',
413
+ rollbackSafety: 'Pipeline smoke checks are additive and can be disabled without impacting baseline code generation.',
414
+ });
415
+ }
416
+
417
+ return withExplainability({
418
+ key: 'application-verification-loop',
419
+ label: 'Application verification loop',
420
+ required: ['test', 'lint', 'build'],
421
+ optional: repoArchetype.riskProfile.key === 'regulated' ? ['security-review'] : [],
422
+ rationale: 'Product repos should default to explicit test, lint, and build loops before completion.',
423
+ }, repoArchetype, recommendedDomainPacks, {
424
+ evidence: domainInfluence.reasons,
425
+ prerequisites: ['Document repo-specific test/lint/build commands if they are not already explicit.'],
426
+ expectedBenefit: 'Creates a predictable verification floor for day-to-day AI-assisted edits.',
427
+ rollbackSafety: 'Verification recommendations can be tuned over time without reworking repo structure.',
428
+ });
429
+ }
430
+
431
+ function recommendCiShape(repoArchetype, recommendedDomainPacks, domainInfluence) {
432
+ const ciBias = domainInfluence.ciShapeOverride;
433
+
434
+ if (repoArchetype.topology.key === 'monorepo' || ciBias === 'workspace-pr-gate') {
435
+ return withExplainability({
436
+ key: 'workspace-pr-gate',
437
+ label: 'Workspace-aware PR gate',
438
+ steps: [
439
+ 'Run `nerviq audit --diff-only` in PRs for changed-file governance feedback',
440
+ 'Run workspace-aware audits for touched packages before merge',
441
+ 'Save tagged full snapshots on baseline and release checkpoints',
442
+ ],
443
+ rationale: 'Monorepos need scoped PR checks plus periodic full-root evidence.',
444
+ }, repoArchetype, recommendedDomainPacks, {
445
+ evidence: ['Monorepo or workspace-oriented domain signals detected.'].concat(domainInfluence.reasons),
446
+ prerequisites: ['Adopt workspace-aware audit coverage before relying on per-package score semantics.'],
447
+ expectedBenefit: 'Keeps package-local drift visible without losing root governance posture.',
448
+ rollbackSafety: 'CI shape is an operating recommendation; you can revert to a simpler gate without touching product code.',
449
+ });
450
+ }
451
+
452
+ if (repoArchetype.primaryWorkflow.key === 'governed-rollout' || repoArchetype.riskProfile.key === 'regulated' || ciBias === 'governed-pr-gate') {
453
+ return withExplainability({
454
+ key: 'governed-pr-gate',
455
+ label: 'Governed PR gate',
456
+ steps: [
457
+ 'Run `nerviq audit --diff-only` on PRs',
458
+ 'Run a full `nerviq audit --threshold` check before merge or release',
459
+ 'Use tagged snapshots (`baseline`, `post-fix`, `pre-release`) for traceable history',
460
+ ],
461
+ rationale: 'Governed repos need PR feedback plus full-posture evidence before risky changes are accepted.',
462
+ }, repoArchetype, recommendedDomainPacks, {
463
+ evidence: domainInfluence.reasons,
464
+ prerequisites: ['Make baseline and release snapshot milestones part of the merge process.'],
465
+ expectedBenefit: 'Turns Nerviq into a repeatable part of repo governance instead of a one-off setup step.',
466
+ rollbackSafety: 'CI shape is policy-level guidance and can be dialed back if the repo does not need full governed gates.',
467
+ });
468
+ }
469
+
470
+ if (repoArchetype.maturity.key === 'none' || repoArchetype.maturity.key === 'starter') {
471
+ return withExplainability({
472
+ key: 'bootstrap-ci',
473
+ label: 'Bootstrap CI baseline',
474
+ steps: [
475
+ 'Start with a full `nerviq audit` on the default branch',
476
+ 'Introduce `--diff-only` PR checks once the repo has a stable baseline',
477
+ 'Capture a named baseline snapshot before broad apply flows',
478
+ ],
479
+ rationale: 'Early-stage repos need one stable baseline before diff-aware automation becomes meaningful.',
480
+ }, repoArchetype, recommendedDomainPacks, {
481
+ evidence: ['Starter or missing managed baseline detected.'],
482
+ prerequisites: ['Capture the first clean baseline snapshot before diff-only enforcement.'],
483
+ expectedBenefit: 'Avoids noisy CI by introducing governance incrementally instead of all at once.',
484
+ rollbackSafety: 'Bootstrap CI is intentionally minimal and can graduate into stronger gates later.',
485
+ });
486
+ }
487
+
488
+ return withExplainability({
489
+ key: 'standard-pr-gate',
490
+ label: 'Standard PR gate',
491
+ steps: [
492
+ 'Use `nerviq audit --diff-only` for PR-level feedback',
493
+ 'Run periodic full audits to keep score semantics grounded in live repo state',
494
+ 'Capture tagged snapshots around major fixes or releases',
495
+ ],
496
+ rationale: 'The repo is ready for regular diff-aware checks plus scheduled full-posture verification.',
497
+ }, repoArchetype, recommendedDomainPacks, {
498
+ evidence: domainInfluence.reasons,
499
+ prerequisites: ['Keep periodic full audits scheduled so diff-only checks stay grounded.'],
500
+ expectedBenefit: 'Makes Nerviq part of the normal PR loop without requiring heavy governed rollout overhead.',
501
+ rollbackSafety: 'This operating mode can be tightened or relaxed as team habits change.',
502
+ });
503
+ }
504
+
505
+ function buildOperatingProfile(options) {
506
+ const {
507
+ dir,
508
+ platform,
509
+ repoArchetype,
510
+ recommendedDomainPacks = [],
511
+ recommendedMcpPacks = [],
512
+ } = options || {};
513
+
514
+ const domainInfluence = buildDomainInfluence(repoArchetype, recommendedDomainPacks, recommendedMcpPacks);
515
+ const platformSupport = recommendPlatformSupport(dir, platform, repoArchetype, recommendedDomainPacks);
516
+ const permission = recommendPermissionProfile(repoArchetype, recommendedDomainPacks, domainInfluence);
517
+ const governancePack = recommendGovernancePack(repoArchetype, recommendedDomainPacks, domainInfluence);
518
+ const hooks = recommendHooks(repoArchetype, recommendedDomainPacks, domainInfluence);
519
+ const verification = recommendVerificationProfile(repoArchetype, recommendedDomainPacks, domainInfluence);
520
+ const ciShape = recommendCiShape(repoArchetype, recommendedDomainPacks, domainInfluence);
521
+
522
+ const snapshotTags = repoArchetype.primaryWorkflow.key === 'governed-rollout'
523
+ ? ['baseline', 'post-fix', 'pre-release']
524
+ : ['baseline', 'post-fix'];
525
+
526
+ return {
527
+ key: `${repoArchetype.key}:${permission.profile.key}`,
528
+ label: `${repoArchetype.label} operating profile`,
529
+ summary: `Recommended posture: ${permission.profile.label}, ${ciShape.label.toLowerCase()}, and ${hooks.length} starter hooks for a ${repoArchetype.label.toLowerCase()}.`,
530
+ platformSupport,
531
+ permissionProfile: {
532
+ key: permission.profile.key,
533
+ label: permission.profile.label,
534
+ risk: permission.profile.risk,
535
+ rationale: permission.rationale,
536
+ why: permission.why,
537
+ evidence: permission.evidence,
538
+ prerequisites: permission.prerequisites,
539
+ expectedBenefit: permission.expectedBenefit,
540
+ rollbackSafety: permission.rollbackSafety,
541
+ },
542
+ governancePack: {
543
+ key: governancePack.pack.key,
544
+ label: governancePack.pack.label,
545
+ modules: governancePack.pack.modules,
546
+ rationale: governancePack.rationale,
547
+ why: governancePack.why,
548
+ evidence: governancePack.evidence,
549
+ prerequisites: governancePack.prerequisites,
550
+ expectedBenefit: governancePack.expectedBenefit,
551
+ rollbackSafety: governancePack.rollbackSafety,
552
+ },
553
+ hooks,
554
+ verification,
555
+ ciShape,
556
+ governanceDefaults: {
557
+ benchmarkBeforeApply: repoArchetype.riskProfile.key === 'regulated' || repoArchetype.maturity.key === 'mature',
558
+ exportPlanBeforeWrites: permission.profile.key !== 'safe-write' || repoArchetype.maturity.key !== 'none',
559
+ useDiffOnlyInPrs: ciShape.key !== 'bootstrap-ci',
560
+ harmonizePlatforms: platformSupport.current.length >= 2,
561
+ snapshotTags,
562
+ recommendedDomainPacks: recommendedDomainPacks.map((pack) => pack.key),
563
+ recommendedMcpPacks: recommendedMcpPacks.map((pack) => pack.key),
564
+ },
565
+ domainInfluence: {
566
+ keys: [...domainInfluence.keys],
567
+ reasons: domainInfluence.reasons,
568
+ },
569
+ };
570
+ }
571
+
572
+ module.exports = {
573
+ buildOperatingProfile,
574
+ };