codehere 1.0.1 → 1.0.2

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 (163) hide show
  1. package/README.md +36 -192
  2. package/dist/application/use-cases/check-claimed-tests.js +81 -0
  3. package/dist/application/use-cases/detect-test-weakening.js +237 -0
  4. package/dist/application/use-cases/rerun-tests.js +110 -7
  5. package/dist/index.js +38 -1212
  6. package/dist/infrastructure/orchestration/artifacts-extractor.js +2 -323
  7. package/dist/presentation/cli/commands/check-command.js +178 -23
  8. package/dist/presentation/cli/commands/help-command.js +21 -285
  9. package/dist/ui.js +5 -236
  10. package/dist/utils/version.js +0 -6
  11. package/package.json +9 -38
  12. package/dist/application/services/verdict-service.js +0 -154
  13. package/dist/application/use-cases/assess-cross-vendor.js +0 -126
  14. package/dist/application/use-cases/build-cross-vendor-verdict.js +0 -118
  15. package/dist/application/use-cases/domain-trust.js +0 -110
  16. package/dist/application/use-cases/export-aat.js +0 -107
  17. package/dist/application/use-cases/generate-acceptance-criteria.js +0 -223
  18. package/dist/application/use-cases/generate-final-report.js +0 -259
  19. package/dist/application/use-cases/generate-narration.js +0 -130
  20. package/dist/application/use-cases/generate-thread-title.js +0 -181
  21. package/dist/application/use-cases/narrator-prompt-template.js +0 -158
  22. package/dist/application/use-cases/plan-auto-review.js +0 -57
  23. package/dist/application/use-cases/reconcile-audit.js +0 -100
  24. package/dist/application/use-cases/search-sessions.js +0 -70
  25. package/dist/application/use-cases/should-auto-review.js +0 -71
  26. package/dist/application/use-cases/thread-export.js +0 -245
  27. package/dist/application/use-cases/verify-claim.js +0 -456
  28. package/dist/application/use-cases/write-pass.js +0 -277
  29. package/dist/domain/audit.js +0 -11
  30. package/dist/domain/services/codebase-detector.js +0 -85
  31. package/dist/domain/task.js +0 -91
  32. package/dist/error-handler.js +0 -139
  33. package/dist/infrastructure/completion/bash-completion.js +0 -29
  34. package/dist/infrastructure/completion/fish-completion.js +0 -17
  35. package/dist/infrastructure/completion/zsh-completion.js +0 -25
  36. package/dist/infrastructure/config/config-manager.js +0 -174
  37. package/dist/infrastructure/config/product-context.js +0 -49
  38. package/dist/infrastructure/context/adapters/drawio-source.js +0 -157
  39. package/dist/infrastructure/context/adapters/figma-source.js +0 -198
  40. package/dist/infrastructure/context/adapters/github-issue-source.js +0 -160
  41. package/dist/infrastructure/context/adapters/linear-source.js +0 -190
  42. package/dist/infrastructure/context/context-resolver.js +0 -144
  43. package/dist/infrastructure/context/context-source.js +0 -114
  44. package/dist/infrastructure/context/github-api.js +0 -110
  45. package/dist/infrastructure/context/github-device-flow.js +0 -65
  46. package/dist/infrastructure/context/oauth-flow.js +0 -207
  47. package/dist/infrastructure/context/oauth-store.js +0 -122
  48. package/dist/infrastructure/context/registry-bootstrap.js +0 -95
  49. package/dist/infrastructure/context/write-pass-resolver.js +0 -106
  50. package/dist/infrastructure/cost/budget-config.js +0 -114
  51. package/dist/infrastructure/cost/cost-router.js +0 -97
  52. package/dist/infrastructure/cost/spend-aggregator.js +0 -222
  53. package/dist/infrastructure/cost/team-ledger.js +0 -355
  54. package/dist/infrastructure/discovery/discovery-log.js +0 -85
  55. package/dist/infrastructure/hooks/audit-tip-signature.js +0 -336
  56. package/dist/infrastructure/hooks/claude-sdk-hook.js +0 -1018
  57. package/dist/infrastructure/hooks/hook-mode.js +0 -87
  58. package/dist/infrastructure/hooks/mirror-tool-call-to-audit.js +0 -49
  59. package/dist/infrastructure/hooks/pre-prompt-scan.js +0 -122
  60. package/dist/infrastructure/hooks/session-analyzer.js +0 -177
  61. package/dist/infrastructure/observability/execution-tracer.js +0 -227
  62. package/dist/infrastructure/orchestration/agent-adapter.js +0 -13
  63. package/dist/infrastructure/orchestration/agent-installer.js +0 -101
  64. package/dist/infrastructure/orchestration/agents-config-seed.js +0 -68
  65. package/dist/infrastructure/orchestration/aider-adapter.js +0 -611
  66. package/dist/infrastructure/orchestration/cassette-recorder.js +0 -120
  67. package/dist/infrastructure/orchestration/claude-code-adapter.js +0 -592
  68. package/dist/infrastructure/orchestration/codex-adapter.js +0 -574
  69. package/dist/infrastructure/orchestration/conductor-adapter.js +0 -416
  70. package/dist/infrastructure/orchestration/context-handoff.js +0 -162
  71. package/dist/infrastructure/orchestration/control/codex-control-surface.js +0 -99
  72. package/dist/infrastructure/orchestration/control/control-surface.js +0 -15
  73. package/dist/infrastructure/orchestration/detect-ollama.js +0 -75
  74. package/dist/infrastructure/orchestration/failover-classifier.js +0 -151
  75. package/dist/infrastructure/orchestration/failover-config.js +0 -67
  76. package/dist/infrastructure/orchestration/failover-policy.js +0 -106
  77. package/dist/infrastructure/orchestration/gemini-adapter.js +0 -663
  78. package/dist/infrastructure/orchestration/generic-cli-adapter.js +0 -689
  79. package/dist/infrastructure/orchestration/graphify-context.js +0 -209
  80. package/dist/infrastructure/orchestration/memory-injector.js +0 -380
  81. package/dist/infrastructure/orchestration/mocks/mock-agent-repl.js +0 -388
  82. package/dist/infrastructure/orchestration/mocks/mock-codex-app-server.js +0 -73
  83. package/dist/infrastructure/orchestration/mocks/mock-synthesis.js +0 -83
  84. package/dist/infrastructure/orchestration/mocks/resolve-agent-spawn.js +0 -37
  85. package/dist/infrastructure/orchestration/opencode-adapter.js +0 -580
  86. package/dist/infrastructure/orchestration/progress-aggregator.js +0 -906
  87. package/dist/infrastructure/orchestration/readiness.js +0 -103
  88. package/dist/infrastructure/orchestration/session-manager.js +0 -1590
  89. package/dist/infrastructure/orchestration/session-persistence.js +0 -394
  90. package/dist/infrastructure/orchestration/thread-key-store.js +0 -127
  91. package/dist/infrastructure/orchestration/write-pass-store.js +0 -38
  92. package/dist/infrastructure/protocols/cloud-sync.js +0 -207
  93. package/dist/infrastructure/protocols/mcp-helpers.js +0 -25
  94. package/dist/infrastructure/protocols/mcp-server-impl.js +0 -3935
  95. package/dist/infrastructure/protocols/mcp-tools.js +0 -401
  96. package/dist/infrastructure/protocols/probe-port.js +0 -36
  97. package/dist/infrastructure/protocols/thread-phase.js +0 -105
  98. package/dist/infrastructure/protocols/ui/app-script-artifacts.js +0 -121
  99. package/dist/infrastructure/protocols/ui/app-script-audit-footer.js +0 -44
  100. package/dist/infrastructure/protocols/ui/app-script-audit-verify.js +0 -56
  101. package/dist/infrastructure/protocols/ui/app-script-composer-cost.js +0 -588
  102. package/dist/infrastructure/protocols/ui/app-script-context-sources.js +0 -168
  103. package/dist/infrastructure/protocols/ui/app-script-conversation.js +0 -67
  104. package/dist/infrastructure/protocols/ui/app-script-drawer.js +0 -115
  105. package/dist/infrastructure/protocols/ui/app-script-failover.js +0 -133
  106. package/dist/infrastructure/protocols/ui/app-script-github.js +0 -113
  107. package/dist/infrastructure/protocols/ui/app-script-observer.js +0 -104
  108. package/dist/infrastructure/protocols/ui/app-script-onboarding.js +0 -80
  109. package/dist/infrastructure/protocols/ui/app-script-overlay.js +0 -203
  110. package/dist/infrastructure/protocols/ui/app-script-recovery.js +0 -136
  111. package/dist/infrastructure/protocols/ui/app-script-render.js +0 -166
  112. package/dist/infrastructure/protocols/ui/app-script-share.js +0 -78
  113. package/dist/infrastructure/protocols/ui/app-script-sidebar.js +0 -194
  114. package/dist/infrastructure/protocols/ui/app-script-tour.js +0 -78
  115. package/dist/infrastructure/protocols/ui/app-script-wpass.js +0 -312
  116. package/dist/infrastructure/protocols/ui/app-script.js +0 -2998
  117. package/dist/infrastructure/protocols/ui/font-faces.js +0 -7
  118. package/dist/infrastructure/protocols/ui/styles.js +0 -2147
  119. package/dist/infrastructure/protocols/ui/tokens.js +0 -96
  120. package/dist/infrastructure/protocols/ui-page-v2.js +0 -320
  121. package/dist/infrastructure/protocols/verify-page.js +0 -1677
  122. package/dist/infrastructure/security/file-collection.js +0 -118
  123. package/dist/infrastructure/security/fix-code.js +0 -144
  124. package/dist/infrastructure/security/fix-secret.js +0 -89
  125. package/dist/infrastructure/security/permissions-config.js +0 -235
  126. package/dist/infrastructure/security/scan-types.js +0 -10
  127. package/dist/infrastructure/security/ship-scan.js +0 -195
  128. package/dist/infrastructure/security/trust-scorer.js +0 -724
  129. package/dist/infrastructure/security/vibe-checks.js +0 -99
  130. package/dist/infrastructure/storage/atomic-write.js +0 -86
  131. package/dist/infrastructure/storage/cross-tool-agents.js +0 -268
  132. package/dist/infrastructure/storage/project-thread-migration.js +0 -237
  133. package/dist/infrastructure/storage/project-thread-mirror.js +0 -671
  134. package/dist/infrastructure/storage/project-thread-reader.js +0 -409
  135. package/dist/infrastructure/storage/project-thread-title-backfill.js +0 -214
  136. package/dist/monitoring.js +0 -212
  137. package/dist/presentation/cli/commands/audit-command.js +0 -201
  138. package/dist/presentation/cli/commands/budget-command.js +0 -79
  139. package/dist/presentation/cli/commands/config-command.js +0 -125
  140. package/dist/presentation/cli/commands/context-source-command.js +0 -217
  141. package/dist/presentation/cli/commands/hook-command.js +0 -265
  142. package/dist/presentation/cli/commands/init-command.js +0 -590
  143. package/dist/presentation/cli/commands/memory-command.js +0 -1031
  144. package/dist/presentation/cli/commands/narrator-command.js +0 -101
  145. package/dist/presentation/cli/commands/permission-command.js +0 -105
  146. package/dist/presentation/cli/commands/protocols-builder.js +0 -254
  147. package/dist/presentation/cli/commands/protocols-command.js +0 -84
  148. package/dist/presentation/cli/commands/repo-map-builder.js +0 -247
  149. package/dist/presentation/cli/commands/report-command.js +0 -403
  150. package/dist/presentation/cli/commands/scan-command.js +0 -52
  151. package/dist/presentation/cli/commands/scan-tool-command.js +0 -110
  152. package/dist/presentation/cli/commands/search-command.js +0 -51
  153. package/dist/presentation/cli/commands/setup-command.js +0 -330
  154. package/dist/presentation/cli/commands/status-command.js +0 -614
  155. package/dist/presentation/cli/commands/team-command.js +0 -256
  156. package/dist/presentation/cli/commands/thread-command.js +0 -370
  157. package/dist/presentation/cli/commands/trust-command.js +0 -328
  158. package/dist/presentation/cli/commands/verify-command.js +0 -298
  159. package/dist/presentation/cli/error-display.js +0 -328
  160. package/dist/presentation/cli/repl.js +0 -805
  161. package/dist/presentation/cli/task-deliverable.js +0 -153
  162. package/dist/testing/cassette.js +0 -125
  163. package/dist/testing/replay-adapter.js +0 -147
@@ -1,101 +0,0 @@
1
- /**
2
- * `codehere narrator` — read or customize the PM narrator prompt template.
3
- *
4
- * Subcommands:
5
- * show — print active template + source (user / default)
6
- * and the path codehere reads
7
- * edit — write the default template to ~/.codehere/narrator-prompt.md
8
- * if it doesn't already exist, then open in $EDITOR
9
- * (or print the path if no $EDITOR set)
10
- * reset — delete the user-edited file, fall back to default
11
- *
12
- * The template uses {{placeholder}} substitution. Required placeholders:
13
- * {{prompt}}, {{agent}}, {{elapsed_seconds}}, {{steps}}
14
- * Missing any → silent fallback to baked-in default at render time, so a
15
- * typo never breaks narration.
16
- *
17
- * Per claim_user_persona HIGH: the narrator IS the agent's voice for
18
- * non-technical users. Letting them control it is a v1 promise.
19
- */
20
- import { spawnSync } from 'child_process';
21
- import { existsSync, readFileSync } from 'fs';
22
- import { loadTemplate, resetTemplate, templatePath, writeDefaultTemplate, DEFAULT_TEMPLATE, } from '../../../application/use-cases/narrator-prompt-template.js';
23
- import { error, info, newline, success } from '../../../ui.js';
24
- export async function executeNarratorCommand(subcommand) {
25
- const sub = subcommand ?? 'show';
26
- switch (sub) {
27
- case 'show':
28
- return showNarrator();
29
- case 'edit':
30
- return editNarrator();
31
- case 'reset':
32
- return resetNarrator();
33
- default:
34
- console.error(error('Unknown subcommand:'), sub);
35
- console.error(info('Try:'), 'codehere narrator [show | edit | reset]');
36
- process.exit(1);
37
- }
38
- }
39
- function showNarrator() {
40
- const { source, template } = loadTemplate();
41
- const path = templatePath();
42
- newline();
43
- console.log(info('Active template source:'), source === 'user' ? 'user-edited file' : 'baked-in default');
44
- console.log(info('Path:'), path);
45
- if (source === 'user') {
46
- const sizeBytes = existsSync(path) ? readFileSync(path, 'utf-8').length : 0;
47
- console.log(info('User file size:'), `${sizeBytes} bytes`);
48
- }
49
- else {
50
- console.log(info('Customize with:'), 'codehere narrator edit');
51
- }
52
- newline();
53
- console.log(info('Template (first 500 chars):'));
54
- console.log(template.slice(0, 500) + (template.length > 500 ? '\n…(truncated)' : ''));
55
- newline();
56
- }
57
- function editNarrator() {
58
- const { wrote, path } = writeDefaultTemplate(false);
59
- if (wrote) {
60
- console.log(success('Wrote default template to:'), path);
61
- }
62
- else {
63
- console.log(info('Existing template at:'), path);
64
- }
65
- const editor = process.env.EDITOR || process.env.VISUAL;
66
- if (!editor) {
67
- console.log(info('No $EDITOR set. Open this file in your editor of choice:'));
68
- console.log(' ', path);
69
- console.log(info('Then verify with:'), 'codehere narrator show');
70
- return;
71
- }
72
- console.log(info('Opening in $EDITOR:'), editor);
73
- // Fire the editor synchronously, inheriting tty so the user can edit.
74
- const result = spawnSync(editor, [path], { stdio: 'inherit' });
75
- if (result.status !== 0) {
76
- console.error(error('Editor exited non-zero.'), 'Edits (if any) still saved.');
77
- }
78
- // Reload + report — gives the user immediate confirmation their edits parsed.
79
- const after = loadTemplate();
80
- if (after.source === 'user') {
81
- console.log(success('Active template:'), 'user-edited file (parses, all placeholders present).');
82
- }
83
- else {
84
- console.log(error('Template missing required placeholders.'), 'Falling back to default until fixed.');
85
- console.log(info('Required:'), '{{prompt}}, {{agent}}, {{elapsed_seconds}}, {{steps}}');
86
- }
87
- }
88
- function resetNarrator() {
89
- const { removed, path } = resetTemplate();
90
- if (removed) {
91
- console.log(success('Removed user-edited template.'), 'Narrator now uses the baked-in default.');
92
- console.log(info('File was at:'), path);
93
- }
94
- else {
95
- console.log(info('No user-edited template to remove.'), 'Already on the baked-in default.');
96
- }
97
- // Don't load DEFAULT_TEMPLATE for any side effect — just acknowledge it's the
98
- // active source now. (Imported above so future test shape stays available.)
99
- void DEFAULT_TEMPLATE;
100
- }
101
- //# sourceMappingURL=narrator-command.js.map
@@ -1,105 +0,0 @@
1
- /**
2
- * `codehere permission` — manage per-cwd .codehere/permissions.yaml.
3
- *
4
- * Subcommands:
5
- * list — show current allow/deny entries
6
- * allow <pattern> [--reason X] — append an allow entry
7
- * deny <pattern> [--reason X] — append a deny entry (reserved; no-op today)
8
- * remove <pattern> — drop the matching entry from both buckets
9
- *
10
- * Always operates on the CURRENT working directory's
11
- * .codehere/permissions.yaml. Creates the file if missing. Writes are
12
- * idempotent — re-running `allow X` won't add a duplicate.
13
- *
14
- * Per claim_user_persona HIGH: the CLI is the developer escape hatch;
15
- * the slice-2 web-UI panel (forthcoming) wraps the same primitive for
16
- * non-technical users. The YAML format is the source of truth either way.
17
- */
18
- import { loadPermissions, addPermissionEntry, removePermissionEntry, } from '../../../infrastructure/security/permissions-config.js';
19
- import { colors, success, error, warning, info, newline } from '../../../ui.js';
20
- export async function executePermissionCommand(subcommand, pattern, options = {}) {
21
- const sub = subcommand ?? 'list';
22
- switch (sub) {
23
- case 'list':
24
- return listPermissions();
25
- case 'allow':
26
- if (!pattern) {
27
- console.error(error('Usage:'), 'codehere permission allow <pattern> [--reason "..."]');
28
- process.exit(1);
29
- }
30
- return addEntry('allow', pattern, options.reason);
31
- case 'deny':
32
- if (!pattern) {
33
- console.error(error('Usage:'), 'codehere permission deny <pattern> [--reason "..."]');
34
- process.exit(1);
35
- }
36
- return addEntry('deny', pattern, options.reason);
37
- case 'remove':
38
- if (!pattern) {
39
- console.error(error('Usage:'), 'codehere permission remove <pattern>');
40
- process.exit(1);
41
- }
42
- return removeEntry(pattern);
43
- default:
44
- console.error(error('Unknown subcommand:'), sub);
45
- console.error(info('Available:'), 'list · allow · deny · remove');
46
- process.exit(1);
47
- }
48
- }
49
- function listPermissions() {
50
- const cfg = loadPermissions(process.cwd());
51
- if (cfg.allow.length === 0 && cfg.deny.length === 0) {
52
- console.log(colors.dim(' (no permissions.yaml in this folder yet)'));
53
- console.log(colors.dim(' Run `codehere permission allow <pattern> --reason "..."` to add an entry.'));
54
- newline();
55
- return;
56
- }
57
- if (cfg.allow.length > 0) {
58
- console.log(success('allow:'));
59
- for (const e of cfg.allow) {
60
- console.log(` • ${e.pattern}`);
61
- if (e.reason)
62
- console.log(colors.dim(` reason: ${e.reason}`));
63
- if (e.addedAt)
64
- console.log(colors.dim(` added: ${e.addedAt}`));
65
- }
66
- }
67
- if (cfg.deny.length > 0) {
68
- console.log(warning('deny (reserved · currently no-op):'));
69
- for (const e of cfg.deny) {
70
- console.log(` • ${e.pattern}`);
71
- if (e.reason)
72
- console.log(colors.dim(` reason: ${e.reason}`));
73
- }
74
- }
75
- newline();
76
- }
77
- function addEntry(bucket, pattern, reason) {
78
- const result = addPermissionEntry({ cwd: process.cwd(), pattern, bucket, reason });
79
- if (result.status === 'already_present') {
80
- console.log(warning(`Pattern already in ${bucket}-list:`), pattern);
81
- if (result.entry.reason)
82
- console.log(colors.dim(` reason: ${result.entry.reason}`));
83
- if (reason && reason !== result.entry.reason) {
84
- console.log(colors.dim(' (use `codehere permission remove <pattern>` first to change the reason)'));
85
- }
86
- newline();
87
- return;
88
- }
89
- console.log(success(`Added to ${bucket}-list:`), pattern);
90
- if (reason)
91
- console.log(colors.dim(` reason: ${reason}`));
92
- console.log(colors.dim(` file: ${result.filePath}`));
93
- newline();
94
- }
95
- function removeEntry(pattern) {
96
- const { removed } = removePermissionEntry(process.cwd(), pattern);
97
- if (removed === 0) {
98
- console.log(warning('No entry found for pattern:'), pattern);
99
- newline();
100
- return;
101
- }
102
- console.log(success(`Removed ${removed} entr${removed === 1 ? 'y' : 'ies'} for:`), pattern);
103
- newline();
104
- }
105
- //# sourceMappingURL=permission-command.js.map
@@ -1,254 +0,0 @@
1
- /**
2
- * F3 Task 7 — protocols/hook_patterns.json exporter.
3
- *
4
- * Reads the trust-scorer's CODE_PATTERNS bank (the in-code TypeScript
5
- * source of truth for the safety hook's pattern matchers) and exports it
6
- * to .codehere/protocols/hook_patterns.json so the patterns are:
7
- *
8
- * - inspectable (auditors, security reviewers can read the JSON)
9
- * - version-controllable (the file lives in the repo)
10
- * - shareable (cross-tool: e.g. PR our patterns into the
11
- * agentic-stack ecosystem per ADR-006)
12
- * - extensible (project-specific patterns under `patterns_project[]`
13
- * survive re-exports)
14
- *
15
- * Schema split (regenerable / preserved):
16
- * patterns_builtin[] — regenerated from CODE_PATTERNS on each export
17
- * patterns_project[] — user-edited; preserved across re-exports
18
- *
19
- * Each pattern is identified by a stable `id` slugified from category +
20
- * description. Same code → same id, so users can reference patterns
21
- * deterministically (e.g. "ignore the `secrets-hardcoded-credential`
22
- * pattern in this folder").
23
- *
24
- * Forward-compatibility: regex is serialized as a JSON-portable string
25
- * (no special characters that would break JSON parsing); the flags are
26
- * a separate field. This avoids the JSON-vs-RegExp impedance mismatch.
27
- */
28
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
29
- import { join, dirname } from 'path';
30
- import { CODE_PATTERNS } from '../../../infrastructure/security/trust-scorer.js';
31
- /**
32
- * Slugify category + description into a stable id.
33
- *
34
- * "Secrets" + "Hardcoded credential" → "secrets-hardcoded-credential"
35
- * "SQL Injection" + "String interpolation in SQL query" → "sql-injection-string-interpolation-in-sql-query"
36
- *
37
- * Stability matters: re-exports must produce the same id from the same
38
- * source. Cite this slug in test fixtures, downstream tools, project
39
- * overrides etc.
40
- */
41
- function slugifyPatternId(category, description) {
42
- return (category + ' ' + description)
43
- .toLowerCase()
44
- .replace(/[^a-z0-9]+/g, '-')
45
- .replace(/^-+|-+$/g, '');
46
- }
47
- /**
48
- * Serialize a JS RegExp to a JSON-portable {regex, flags} pair.
49
- * `regex` is the source string (without surrounding `/`); `flags` is the
50
- * flags string ('gi' etc.).
51
- */
52
- function serializeRegex(re) {
53
- return { regex: re.source, flags: re.flags };
54
- }
55
- /**
56
- * Assign stable, source-order-independent ids to a list of patterns.
57
- *
58
- * The sort key is (category, description, regex.source) — a 3-tier
59
- * comparator. The first two tiers come from K2 Round 1 (sort by
60
- * category + description so duplicates get deterministic suffix
61
- * assignment). The 3rd tier (regex source) is the codex Round 2 fix:
62
- * (category, description) alone leaves stable-sort fall-through to
63
- * source order when two patterns share both fields, which means
64
- * reordering CODE_PATTERNS would still flip which one gets the base id
65
- * vs `-2`. Adding regex.source disambiguates definitively — two patterns
66
- * with identical category + description + regex are the SAME pattern;
67
- * they can't both exist.
68
- *
69
- * Returns a new sorted array with `id` attached. Pure; safe to test
70
- * with arbitrary fixtures.
71
- */
72
- export function assignStableIds(patterns) {
73
- const sorted = [...patterns].sort((a, b) => {
74
- const c = a.category.localeCompare(b.category);
75
- if (c !== 0)
76
- return c;
77
- const d = a.description.localeCompare(b.description);
78
- if (d !== 0)
79
- return d;
80
- return a.regex.source.localeCompare(b.regex.source);
81
- });
82
- const seenIds = new Map();
83
- return sorted.map((p) => {
84
- const baseId = slugifyPatternId(p.category, p.description);
85
- const seen = seenIds.get(baseId) ?? 0;
86
- seenIds.set(baseId, seen + 1);
87
- const id = seen === 0 ? baseId : `${baseId}-${seen + 1}`;
88
- return { ...p, id };
89
- });
90
- }
91
- /**
92
- * Build the export object from CODE_PATTERNS. Pure — does not touch disk.
93
- *
94
- * Delegates id assignment to `assignStableIds` so the source-order-
95
- * independence property has a direct unit test (see protocols-export.test.ts).
96
- */
97
- export function buildHookPatternsExport() {
98
- const withIds = assignStableIds(CODE_PATTERNS);
99
- const builtin = withIds.map((p) => {
100
- const { regex, flags } = serializeRegex(p.regex);
101
- const out = {
102
- id: p.id,
103
- regex,
104
- regex_flags: flags,
105
- severity: p.severity,
106
- category: p.category,
107
- description: p.description,
108
- };
109
- if (p.cwe)
110
- out.cwe = p.cwe;
111
- if (p.scanRaw)
112
- out.scanRaw = p.scanRaw;
113
- if (p.requiresContext) {
114
- const ctx = serializeRegex(p.requiresContext);
115
- out.requiresContext = { regex: ctx.regex, flags: ctx.flags };
116
- }
117
- return out;
118
- });
119
- return {
120
- version: 'v0.5',
121
- generatedAt: new Date().toISOString(),
122
- source: 'agent/src/infrastructure/security/trust-scorer.ts',
123
- _note: "Exported from codehere's trust-scorer pattern bank via 'codehere protocols export'. " +
124
- "DO NOT hand-edit `patterns_builtin[]` · it's regenerated on every export. " +
125
- 'Add project-specific patterns to `patterns_project[]` · those are preserved across re-exports. ' +
126
- 'Pattern ids are stable slugs of category + description; reference them deterministically.',
127
- patterns_builtin: builtin,
128
- patterns_project: [],
129
- };
130
- }
131
- /**
132
- * Detect whether the on-disk file is the init-time placeholder.
133
- *
134
- * Two placeholder shapes are recognized:
135
- *
136
- * 1. POST-2026-04-28 seed (current init) — split schema, empty arrays:
137
- * { version, patterns_builtin: [], patterns_project: [], _note }
138
- *
139
- * 2. PRE-2026-04-28 seed (legacy init) — single empty `patterns` array:
140
- * { version, patterns: [], _note }
141
- *
142
- * Either one is safe to overwrite without --force. A NON-empty legacy
143
- * `patterns` array is NOT a placeholder — see `extractLegacyProjectPatterns`
144
- * below which migrates user content into `patterns_project[]` rather than
145
- * silently discarding it (codex Round 1 fix — the legacy seed's _note
146
- * explicitly told users to add patterns to the legacy field, so users may
147
- * have followed that guidance).
148
- */
149
- function isInitPlaceholder(parsed) {
150
- if (typeof parsed !== 'object' || parsed === null)
151
- return false;
152
- const obj = parsed;
153
- // Current shape: split schema, both arrays empty.
154
- if (Array.isArray(obj.patterns_builtin) &&
155
- obj.patterns_builtin.length === 0 &&
156
- Array.isArray(obj.patterns_project) &&
157
- obj.patterns_project.length === 0) {
158
- return true;
159
- }
160
- // Legacy shape: single empty `patterns` array, no _builtin/_project.
161
- if (Array.isArray(obj.patterns) &&
162
- obj.patterns.length === 0 &&
163
- obj.patterns_builtin === undefined &&
164
- obj.patterns_project === undefined) {
165
- return true;
166
- }
167
- return false;
168
- }
169
- /**
170
- * Extract user-added patterns from the LEGACY `patterns[]` field, if any.
171
- * Used to migrate non-empty legacy files into `patterns_project[]` so users
172
- * who followed the legacy seed's _note advice ("add patterns to `patterns`")
173
- * don't lose their work on first export (codex Round 1 fix).
174
- *
175
- * Returns `null` if the file isn't a legacy-shape file with non-empty
176
- * patterns; callers fall through to the normal --rebuild path which reads
177
- * `patterns_project[]` directly.
178
- */
179
- function extractLegacyProjectPatterns(parsed) {
180
- if (typeof parsed !== 'object' || parsed === null)
181
- return null;
182
- const obj = parsed;
183
- if (Array.isArray(obj.patterns) &&
184
- obj.patterns.length > 0 &&
185
- obj.patterns_builtin === undefined &&
186
- obj.patterns_project === undefined) {
187
- return obj.patterns;
188
- }
189
- return null;
190
- }
191
- /**
192
- * Disk-side wrapper: build + write to .codehere/protocols/hook_patterns.json.
193
- *
194
- * If the existing file is the init-time placeholder, overwrite without
195
- * requiring --rebuild. Otherwise:
196
- * - existing file present + no --rebuild → 'exists' (refuse)
197
- * - existing file present + --rebuild → preserve patterns_project[],
198
- * regenerate patterns_builtin[]
199
- * - no existing file → write fresh
200
- */
201
- export function writeHookPatternsExport(codehereDir, options = {}) {
202
- const targetPath = join(codehereDir, 'protocols/hook_patterns.json');
203
- let preservedProject = [];
204
- if (existsSync(targetPath)) {
205
- let parsed;
206
- try {
207
- parsed = JSON.parse(readFileSync(targetPath, 'utf8'));
208
- }
209
- catch {
210
- return { result: 'failed', path: targetPath, error: 'existing file is not valid JSON' };
211
- }
212
- // Codex Round 1 fix: migrate non-empty legacy `patterns[]` content into
213
- // `patterns_project[]` BEFORE the placeholder/rebuild branches. The
214
- // legacy init seed's _note told users to add patterns to the legacy
215
- // field; a fresh export must preserve any content they added rather
216
- // than silently discard it.
217
- const legacy = extractLegacyProjectPatterns(parsed);
218
- if (legacy !== null) {
219
- preservedProject = legacy;
220
- // Fall through to writing — we treat a non-empty-legacy file as
221
- // overwriteable (the legacy patterns now live in preservedProject).
222
- }
223
- else if (isInitPlaceholder(parsed)) {
224
- // Empty placeholder (either schema shape) — overwrite without --rebuild.
225
- }
226
- else if (!options.rebuild) {
227
- return { result: 'exists', path: targetPath };
228
- }
229
- else {
230
- // --rebuild + non-placeholder, non-legacy existing file → preserve
231
- // patterns_project[].
232
- const obj = parsed;
233
- if (Array.isArray(obj.patterns_project)) {
234
- preservedProject = obj.patterns_project;
235
- }
236
- }
237
- }
238
- const built = buildHookPatternsExport();
239
- built.patterns_project = preservedProject;
240
- try {
241
- mkdirSync(dirname(targetPath), { recursive: true });
242
- writeFileSync(targetPath, JSON.stringify(built, null, 2) + '\n');
243
- }
244
- catch (e) {
245
- return { result: 'failed', path: targetPath, error: e instanceof Error ? e.message : String(e) };
246
- }
247
- return {
248
- result: 'exported',
249
- path: targetPath,
250
- builtinCount: built.patterns_builtin.length,
251
- projectCount: built.patterns_project.length,
252
- };
253
- }
254
- //# sourceMappingURL=protocols-builder.js.map
@@ -1,84 +0,0 @@
1
- /**
2
- * Protocols Command — F3 v0.5 protocols layer operations.
3
- *
4
- * Today: `codehere protocols export` writes the trust-scorer's pattern
5
- * bank to .codehere/protocols/hook_patterns.json. Future: `protocols
6
- * import`, `protocols verify`, `protocols diff` etc.
7
- *
8
- * The dispatch shape mirrors `memory-command.ts` so future actions slot
9
- * in without touching index.ts beyond a single .option() addition.
10
- */
11
- import { existsSync } from 'fs';
12
- import { join } from 'path';
13
- import { colors, success, info, error, newline } from '../../../ui.js';
14
- import { findCodebaseRoot } from '../../../domain/services/codebase-detector.js';
15
- import { writeHookPatternsExport } from './protocols-builder.js';
16
- function executeProtocolsExport(options) {
17
- const codeInfo = findCodebaseRoot();
18
- const root = codeInfo?.rootPath ?? process.cwd();
19
- const codehereDir = join(root, '.codehere');
20
- newline();
21
- console.log(colors.bold('codehere protocols export'));
22
- console.log(colors.dim(` exporting trust-scorer pattern bank › ${codehereDir}/protocols/hook_patterns.json`));
23
- newline();
24
- if (!existsSync(codehereDir)) {
25
- console.log(` ${error('refused')} .codehere/ not found at this root.`);
26
- console.log(` ${colors.dim('Run')} ${colors.cyan('codehere init')} ${colors.dim('first.')}`);
27
- newline();
28
- process.exitCode = 1;
29
- return;
30
- }
31
- const result = writeHookPatternsExport(codehereDir, { rebuild: !!options.rebuild });
32
- switch (result.result) {
33
- case 'exported': {
34
- const projectNote = result.projectCount && result.projectCount > 0
35
- ? ` · ${result.projectCount} project pattern${result.projectCount === 1 ? '' : 's'} preserved`
36
- : '';
37
- console.log(` ${success('exported')} ${result.path} ${colors.dim(`(${result.builtinCount} built-in patterns${projectNote})`)}`);
38
- break;
39
- }
40
- case 'exists':
41
- console.log(` ${info('exists ')} ${result.path} ${colors.dim('(use --rebuild to refresh built-in patterns; project patterns are preserved)')}`);
42
- break;
43
- case 'failed':
44
- console.log(` ${error('failed ')} ${result.path} ${colors.dim(`(${result.error})`)}`);
45
- process.exitCode = 1;
46
- break;
47
- }
48
- newline();
49
- console.log(colors.bold('Next:'));
50
- console.log(` - Inspect ${colors.cyan('.codehere/protocols/hook_patterns.json')} to see what the safety hook matches`);
51
- console.log(` - Add project-specific patterns under ${colors.cyan('patterns_project[]')} · they survive re-exports`);
52
- console.log(` ${colors.dim('(Edit patterns_builtin[] is a no-op · re-exports overwrite it from trust-scorer.ts.)')}`);
53
- newline();
54
- }
55
- export function executeProtocolsCommand(action, options = {}) {
56
- try {
57
- switch (action) {
58
- case 'export':
59
- executeProtocolsExport(options);
60
- return;
61
- case undefined:
62
- case 'help':
63
- console.log(`
64
- codehere protocols <action> — F3 protocols layer operations
65
-
66
- export Export trust-scorer pattern bank to .codehere/protocols/hook_patterns.json
67
-
68
- Coming later:
69
- import Apply patterns from a file (cross-tool ingestion)
70
- verify Check that the on-disk file matches the in-code pattern bank
71
- `);
72
- return;
73
- default:
74
- console.error(`\n ${error('failed')} unknown protocols action: ${action}\n Try: codehere protocols help\n`);
75
- process.exitCode = 1;
76
- }
77
- }
78
- catch (e) {
79
- const msg = e instanceof Error ? e.message : String(e);
80
- console.error(`\n ${error('failed')} codehere protocols ${action ?? ''} · ${msg}\n`);
81
- process.exitCode = 1;
82
- }
83
- }
84
- //# sourceMappingURL=protocols-command.js.map