@nerviq/cli 1.18.0 → 1.19.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 (187) hide show
  1. package/LICENSE +23 -23
  2. package/README.md +2 -2
  3. package/bin/cli.js +130 -130
  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 +158 -158
  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 +16 -16
  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/auto-suggest.js +154 -154
  25. package/src/badge.js +13 -13
  26. package/src/behavioral-drift.js +801 -801
  27. package/src/benchmark.js +67 -67
  28. package/src/catalog.js +103 -103
  29. package/src/certification.js +128 -128
  30. package/src/codex/config-parser.js +183 -183
  31. package/src/codex/context.js +223 -223
  32. package/src/codex/deep-review.js +493 -493
  33. package/src/codex/domain-packs.js +394 -394
  34. package/src/codex/freshness.js +84 -84
  35. package/src/codex/governance.js +192 -192
  36. package/src/codex/interactive.js +618 -618
  37. package/src/codex/mcp-packs.js +914 -914
  38. package/src/codex/patch.js +209 -209
  39. package/src/codex/plans.js +251 -251
  40. package/src/codex/premium.js +614 -614
  41. package/src/codex/setup.js +591 -591
  42. package/src/context.js +320 -320
  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/sarif.js +115 -115
  78. package/src/freshness.js +74 -74
  79. package/src/gemini/config-parser.js +275 -275
  80. package/src/gemini/context.js +221 -221
  81. package/src/gemini/deep-review.js +559 -559
  82. package/src/gemini/domain-packs.js +393 -393
  83. package/src/gemini/freshness.js +66 -66
  84. package/src/gemini/governance.js +201 -201
  85. package/src/gemini/interactive.js +860 -860
  86. package/src/gemini/mcp-packs.js +915 -915
  87. package/src/gemini/plans.js +269 -269
  88. package/src/gemini/premium.js +760 -760
  89. package/src/gemini/setup.js +692 -692
  90. package/src/gemini/techniques.js +14 -14
  91. package/src/governance.js +72 -72
  92. package/src/harmony/add.js +68 -68
  93. package/src/harmony/advisor.js +333 -333
  94. package/src/harmony/canon.js +565 -565
  95. package/src/harmony/cli.js +591 -591
  96. package/src/harmony/drift.js +401 -401
  97. package/src/harmony/governance.js +313 -313
  98. package/src/harmony/memory.js +239 -239
  99. package/src/harmony/sync.js +475 -475
  100. package/src/harmony/watch.js +370 -370
  101. package/src/hook-validation.js +342 -342
  102. package/src/index.js +271 -271
  103. package/src/init.js +184 -184
  104. package/src/instruction-surfaces.js +185 -185
  105. package/src/integrations.js +144 -144
  106. package/src/interactive.js +118 -118
  107. package/src/locales/en.json +1 -1
  108. package/src/locales/es.json +1 -1
  109. package/src/mcp-packs.js +830 -830
  110. package/src/mcp-server.js +726 -726
  111. package/src/mcp-validation.js +337 -337
  112. package/src/nerviq-sync.json +7 -7
  113. package/src/opencode/config-parser.js +109 -109
  114. package/src/opencode/context.js +247 -247
  115. package/src/opencode/deep-review.js +313 -313
  116. package/src/opencode/domain-packs.js +262 -262
  117. package/src/opencode/freshness.js +66 -66
  118. package/src/opencode/governance.js +159 -159
  119. package/src/opencode/interactive.js +392 -392
  120. package/src/opencode/mcp-packs.js +705 -705
  121. package/src/opencode/patch.js +184 -184
  122. package/src/opencode/plans.js +231 -231
  123. package/src/opencode/premium.js +413 -413
  124. package/src/opencode/setup.js +449 -449
  125. package/src/opencode/techniques.js +27 -27
  126. package/src/operating-profile.js +574 -574
  127. package/src/org.js +152 -152
  128. package/src/permission-rules.js +218 -218
  129. package/src/plans.js +839 -839
  130. package/src/platform-change-manifest.js +86 -86
  131. package/src/plugins.js +110 -110
  132. package/src/policy-layers.js +210 -210
  133. package/src/profiles.js +124 -124
  134. package/src/prompt-injection.js +74 -74
  135. package/src/public-api.js +173 -173
  136. package/src/recommendation-rules.js +84 -84
  137. package/src/repo-archetype.js +386 -386
  138. package/src/secret-patterns.js +39 -39
  139. package/src/server.js +527 -527
  140. package/src/setup/analysis.js +607 -607
  141. package/src/setup/runtime.js +172 -172
  142. package/src/setup.js +677 -677
  143. package/src/shared/capabilities.js +194 -194
  144. package/src/source-urls.js +132 -132
  145. package/src/stack-checks.js +565 -565
  146. package/src/supplemental-checks.js +13 -13
  147. package/src/synergy/adaptive.js +261 -261
  148. package/src/synergy/compensation.js +137 -137
  149. package/src/synergy/evidence.js +193 -193
  150. package/src/synergy/learning.js +199 -199
  151. package/src/synergy/patterns.js +227 -227
  152. package/src/synergy/ranking.js +83 -83
  153. package/src/synergy/report.js +165 -165
  154. package/src/synergy/routing.js +146 -146
  155. package/src/techniques/api.js +407 -407
  156. package/src/techniques/automation.js +316 -316
  157. package/src/techniques/compliance.js +257 -257
  158. package/src/techniques/hygiene.js +294 -294
  159. package/src/techniques/instructions.js +243 -243
  160. package/src/techniques/observability.js +226 -226
  161. package/src/techniques/optimization.js +142 -142
  162. package/src/techniques/quality.js +318 -318
  163. package/src/techniques/security.js +237 -237
  164. package/src/techniques/shared.js +443 -443
  165. package/src/techniques/stacks.js +2294 -2294
  166. package/src/techniques/tools.js +106 -106
  167. package/src/techniques/workflow.js +413 -413
  168. package/src/techniques.js +81 -81
  169. package/src/terminology.js +73 -73
  170. package/src/token-estimate.js +35 -35
  171. package/src/usage-patterns.js +99 -99
  172. package/src/verification-metadata.js +145 -145
  173. package/src/watch.js +247 -247
  174. package/src/windsurf/activity.js +302 -302
  175. package/src/windsurf/config-parser.js +267 -267
  176. package/src/windsurf/context.js +249 -249
  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/windsurf/techniques.js +17 -17
  187. 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
+ };