@nerviq/cli 1.11.0 → 1.12.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 (49) hide show
  1. package/README.md +97 -19
  2. package/bin/cli.js +618 -182
  3. package/package.json +2 -2
  4. package/src/activity.js +49 -9
  5. package/src/adoption-advisor.js +299 -0
  6. package/src/aider/techniques.js +16 -11
  7. package/src/analyze.js +128 -0
  8. package/src/anti-patterns.js +13 -0
  9. package/src/audit.js +97 -22
  10. package/src/behavioral-drift.js +801 -0
  11. package/src/continuous-ops.js +681 -0
  12. package/src/cost-tracking.js +61 -0
  13. package/src/cursor/techniques.js +17 -12
  14. package/src/deep-review.js +83 -0
  15. package/src/diff-only.js +280 -0
  16. package/src/doctor.js +118 -55
  17. package/src/governance.js +59 -43
  18. package/src/hook-validation.js +342 -0
  19. package/src/index.js +5 -0
  20. package/src/integrations.js +42 -5
  21. package/src/mcp-validation.js +337 -0
  22. package/src/opencode/techniques.js +12 -7
  23. package/src/operating-profile.js +574 -0
  24. package/src/org.js +97 -13
  25. package/src/plans.js +192 -8
  26. package/src/platform-change-manifest.js +86 -0
  27. package/src/policy-layers.js +210 -0
  28. package/src/profiles.js +4 -1
  29. package/src/prompt-injection.js +74 -0
  30. package/src/repo-archetype.js +386 -0
  31. package/src/setup.js +34 -0
  32. package/src/source-urls.js +132 -132
  33. package/src/supplemental-checks.js +13 -12
  34. package/src/techniques/api.js +407 -0
  35. package/src/techniques/automation.js +316 -0
  36. package/src/techniques/compliance.js +257 -0
  37. package/src/techniques/hygiene.js +294 -0
  38. package/src/techniques/instructions.js +243 -0
  39. package/src/techniques/observability.js +226 -0
  40. package/src/techniques/optimization.js +142 -0
  41. package/src/techniques/quality.js +317 -0
  42. package/src/techniques/security.js +237 -0
  43. package/src/techniques/shared.js +443 -0
  44. package/src/techniques/stacks.js +2294 -0
  45. package/src/techniques/tools.js +106 -0
  46. package/src/techniques/workflow.js +413 -0
  47. package/src/techniques.js +78 -5607
  48. package/src/watch.js +18 -0
  49. package/src/windsurf/techniques.js +17 -12
@@ -0,0 +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
+ };