@mnemonik/shared 7.97.2 → 7.98.1

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 (130) hide show
  1. package/dist/ambientPointer.d.ts.map +1 -1
  2. package/dist/ambientPointer.js +2 -1
  3. package/dist/ambientPointer.js.map +1 -1
  4. package/dist/apiOrigin.d.ts +3 -0
  5. package/dist/apiOrigin.d.ts.map +1 -0
  6. package/dist/apiOrigin.js +5 -0
  7. package/dist/apiOrigin.js.map +1 -0
  8. package/dist/ast/astChunker.d.ts +12 -12
  9. package/dist/ast/astChunker.js +32 -32
  10. package/dist/ast/grammars.d.ts +9 -9
  11. package/dist/ast/grammars.js +15 -15
  12. package/dist/codeScanner.d.ts +56 -43
  13. package/dist/codeScanner.d.ts.map +1 -1
  14. package/dist/codeScanner.js +262 -143
  15. package/dist/codeScanner.js.map +1 -1
  16. package/dist/contextBudget.js +2 -2
  17. package/dist/contextBudget.js.map +1 -1
  18. package/dist/gitEditDetector.d.ts +6 -6
  19. package/dist/gitEditDetector.js +7 -7
  20. package/dist/hookRuntime.d.ts +35 -2
  21. package/dist/hookRuntime.d.ts.map +1 -1
  22. package/dist/hookRuntime.js +127 -33
  23. package/dist/hookRuntime.js.map +1 -1
  24. package/dist/hookTimeouts.d.ts +6 -6
  25. package/dist/hookTimeouts.js +6 -6
  26. package/dist/hostAdapter.d.ts +135 -0
  27. package/dist/hostAdapter.d.ts.map +1 -0
  28. package/dist/hostAdapter.js +323 -0
  29. package/dist/hostAdapter.js.map +1 -0
  30. package/dist/hostBinary.d.ts +29 -0
  31. package/dist/hostBinary.d.ts.map +1 -0
  32. package/dist/hostBinary.js +179 -0
  33. package/dist/hostBinary.js.map +1 -0
  34. package/dist/index.d.ts +11 -1
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +10 -1
  37. package/dist/index.js.map +1 -1
  38. package/dist/instructions.d.ts +26 -26
  39. package/dist/instructions.js +26 -26
  40. package/dist/jitDeliveryAck.d.ts.map +1 -1
  41. package/dist/jitDeliveryAck.js +2 -0
  42. package/dist/jitDeliveryAck.js.map +1 -1
  43. package/dist/projectIdentityFile.d.ts +25 -0
  44. package/dist/projectIdentityFile.d.ts.map +1 -0
  45. package/dist/projectIdentityFile.js +87 -0
  46. package/dist/projectIdentityFile.js.map +1 -0
  47. package/dist/projectSetupHandoff.d.ts +24 -0
  48. package/dist/projectSetupHandoff.d.ts.map +1 -0
  49. package/dist/projectSetupHandoff.js +249 -0
  50. package/dist/projectSetupHandoff.js.map +1 -0
  51. package/dist/protectedPaths.d.ts +9 -0
  52. package/dist/protectedPaths.d.ts.map +1 -0
  53. package/dist/protectedPaths.js +66 -0
  54. package/dist/protectedPaths.js.map +1 -0
  55. package/dist/readiness.d.ts +143 -0
  56. package/dist/readiness.d.ts.map +1 -0
  57. package/dist/readiness.js +304 -0
  58. package/dist/readiness.js.map +1 -0
  59. package/dist/repositoryFingerprint.d.ts +50 -0
  60. package/dist/repositoryFingerprint.d.ts.map +1 -0
  61. package/dist/repositoryFingerprint.js +160 -0
  62. package/dist/repositoryFingerprint.js.map +1 -0
  63. package/dist/repositoryRoot.d.ts +56 -0
  64. package/dist/repositoryRoot.d.ts.map +1 -0
  65. package/dist/repositoryRoot.js +226 -0
  66. package/dist/repositoryRoot.js.map +1 -0
  67. package/dist/runtimeReader.d.ts +102 -0
  68. package/dist/runtimeReader.d.ts.map +1 -0
  69. package/dist/runtimeReader.js +408 -0
  70. package/dist/runtimeReader.js.map +1 -0
  71. package/dist/runtimeSigners.d.ts +37 -0
  72. package/dist/runtimeSigners.d.ts.map +1 -0
  73. package/dist/runtimeSigners.js +507 -0
  74. package/dist/runtimeSigners.js.map +1 -0
  75. package/dist/scannerSupervisor.d.ts +35 -0
  76. package/dist/scannerSupervisor.d.ts.map +1 -0
  77. package/dist/scannerSupervisor.js +2 -0
  78. package/dist/scannerSupervisor.js.map +1 -0
  79. package/dist/secretPatterns.d.ts +9 -9
  80. package/dist/secretPatterns.d.ts.map +1 -1
  81. package/dist/secretPatterns.js +27 -25
  82. package/dist/secretPatterns.js.map +1 -1
  83. package/dist/testing/hostAdapterConformance.d.ts +17 -0
  84. package/dist/testing/hostAdapterConformance.d.ts.map +1 -0
  85. package/dist/testing/hostAdapterConformance.js +477 -0
  86. package/dist/testing/hostAdapterConformance.js.map +1 -0
  87. package/dist/usageGuide.d.ts +1 -1
  88. package/dist/usageGuide.d.ts.map +1 -1
  89. package/dist/usageGuide.js +4 -4
  90. package/package.json +7 -2
  91. package/queries/bash.tags.scm +1 -1
  92. package/queries/groovy.tags.scm +1 -1
  93. package/scripts/vendor-grammars.ts +13 -13
  94. package/src/ambientPointer.ts +2 -1
  95. package/src/apiOrigin.ts +4 -0
  96. package/src/ast/astChunker.ts +39 -39
  97. package/src/ast/grammars.ts +16 -16
  98. package/src/codeScanner.ts +274 -149
  99. package/src/contextBudget.ts +2 -2
  100. package/src/gitEditDetector.ts +7 -7
  101. package/src/hookRuntime.ts +162 -34
  102. package/src/hookTimeouts.ts +6 -6
  103. package/src/hostAdapter.ts +468 -0
  104. package/src/hostBinary.ts +205 -0
  105. package/src/index.ts +20 -0
  106. package/src/instructions.ts +26 -26
  107. package/src/jitDeliveryAck.ts +2 -0
  108. package/src/projectIdentityFile.ts +100 -0
  109. package/src/projectSetupHandoff.ts +275 -0
  110. package/src/protectedPaths.ts +88 -0
  111. package/src/readiness.ts +516 -0
  112. package/src/repositoryFingerprint.ts +199 -0
  113. package/src/repositoryRoot.ts +261 -0
  114. package/src/runtimeReader.ts +495 -0
  115. package/src/runtimeSigners.ts +577 -0
  116. package/src/scannerSupervisor.ts +26 -0
  117. package/src/secretPatterns.ts +27 -25
  118. package/src/testing/hostAdapterConformance.ts +538 -0
  119. package/src/usageGuide.ts +4 -4
  120. package/test-fixtures/identity/runner.d.mts +12 -0
  121. package/test-fixtures/identity/runner.mjs +282 -0
  122. package/tests/fixtures/repository-fingerprint-v1.golden.json +834 -0
  123. package/tests/projectIdentity.test.ts +56 -0
  124. package/tests/readiness.test.ts +158 -0
  125. package/tests/repositoryFingerprint.test.ts +26 -0
  126. package/tsconfig.test.json +9 -0
  127. package/wasm/bash.tags.scm +1 -1
  128. package/wasm/groovy.tags.scm +1 -1
  129. package/wasm/javascript.tags.scm +2 -2
  130. package/wasm/solidity.tags.scm +1 -1
@@ -0,0 +1,56 @@
1
+ import assert from 'node:assert/strict';
2
+ import { test } from 'node:test';
3
+ import { parseIdentityFile, readIdentityFile } from '../src/projectIdentityFile.js';
4
+ import { resolveProjectIdentity, resolveRepositoryRoot } from '../src/repositoryRoot.js';
5
+ import { runIdentityFixtureSuite } from '../test-fixtures/identity/runner.mjs';
6
+
7
+ runIdentityFixtureSuite(test, {
8
+ parse: parseIdentityFile,
9
+ read: readIdentityFile,
10
+ resolveRoot: resolveRepositoryRoot,
11
+ resolve: resolveProjectIdentity,
12
+ });
13
+
14
+ test('schema-v1 field type errors are malformed', () => {
15
+ const projectId = '11111111-1111-4111-8111-111111111111';
16
+ for (const value of [
17
+ { schemaVersion: 1, projectId: 42 },
18
+ { schemaVersion: 1, projectId, projectName: null },
19
+ { schemaVersion: 1, projectId, repositoryFingerprint: [] },
20
+ { schemaVersion: 1, projectId, repositoryFingerprint: { algorithmVersion: 2, hash: 'x' } },
21
+ ]) {
22
+ assert.equal(parseIdentityFile(JSON.stringify(value)).kind, 'malformed');
23
+ }
24
+ });
25
+
26
+ test('schema-v1 rejects unknown keys and names them', () => {
27
+ for (const value of [
28
+ {
29
+ schemaVersion: 1,
30
+ projectId: '11111111-1111-4111-8111-111111111111',
31
+ accessToken: 'Bearer secret',
32
+ },
33
+ {
34
+ schemaVersion: 1,
35
+ projectId: '11111111-1111-4111-8111-111111111111',
36
+ repositoryFingerprint: { algorithmVersion: 1, hash: 'x', secret: 'value' },
37
+ },
38
+ ]) {
39
+ const result = parseIdentityFile(JSON.stringify(value));
40
+ assert.equal(result.kind, 'malformed');
41
+ assert.match(result.detail, /accessToken|secret/);
42
+ }
43
+ });
44
+
45
+ test('schema-v1 requires canonical RFC 4122 UUID spelling', () => {
46
+ for (const projectId of [
47
+ '11111111-1111-1111-1111-111111111111',
48
+ '11111111-1111-4111-7111-111111111111',
49
+ 'aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa'.toUpperCase(),
50
+ ]) {
51
+ assert.equal(
52
+ parseIdentityFile(JSON.stringify({ schemaVersion: 1, projectId })).kind,
53
+ 'malformed'
54
+ );
55
+ }
56
+ });
@@ -0,0 +1,158 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+ import {
4
+ describeReadiness,
5
+ reduceReadiness,
6
+ serializeReadiness,
7
+ type ReadinessCondition,
8
+ type ReadinessState,
9
+ } from '../src/readiness.js';
10
+
11
+ const cases: Array<[ReadinessCondition, ReadinessState]> = [
12
+ [{ kind: 'selected_component_failed', reason: 'Scanner failed.' }, 'FAILED'],
13
+ [{ kind: 'host_trust_pending', reason: 'Approve Cursor.' }, 'ACTION_REQUIRED'],
14
+ [{ kind: 'vendor_policy_pending', reason: 'Allow the extension.' }, 'ACTION_REQUIRED'],
15
+ [{ kind: 'hooks_missing', reason: 'Hook declaration is missing.' }, 'ACTION_REQUIRED'],
16
+ [{ kind: 'host_not_connected', reason: 'Codex has not connected.' }, 'LIMITED'],
17
+ [{ kind: 'scanner_omitted', reason: 'Scanner was omitted.' }, 'LIMITED'],
18
+ [{ kind: 'project_uncovered', reason: 'Project is outside approved roots.' }, 'LIMITED'],
19
+ [{ kind: 'post_commit_upload_failed', reason: 'Result upload failed.' }, 'FAILED'],
20
+ ];
21
+
22
+ test('fixed precedence chooses the worse state for every state pair', () => {
23
+ const rank: Record<ReadinessState, number> = {
24
+ READY: 0,
25
+ LIMITED: 1,
26
+ ACTION_REQUIRED: 2,
27
+ FAILED: 3,
28
+ };
29
+ assert.equal(reduceReadiness([]).state, 'READY');
30
+ for (const [left, leftState] of cases) {
31
+ assert.equal(reduceReadiness([left]).state, leftState);
32
+ for (const [right, rightState] of cases) {
33
+ const expected = rank[leftState] >= rank[rightState] ? leftState : rightState;
34
+ assert.equal(reduceReadiness([left, right]).state, expected);
35
+ }
36
+ }
37
+ });
38
+
39
+ test('special cases and indexing semantics stay fixed', () => {
40
+ assert.equal(
41
+ reduceReadiness([
42
+ { kind: 'scanner_omitted', reason: 'Limited Mode was acknowledged.' },
43
+ { kind: 'selected_component_failed', reason: 'Codex failed.' },
44
+ ]).state,
45
+ 'FAILED'
46
+ );
47
+ assert.equal(
48
+ reduceReadiness([
49
+ { kind: 'scanner_omitted', reason: 'Limited Mode was acknowledged.' },
50
+ { kind: 'login_pending', reason: 'Sign in.' },
51
+ ]).state,
52
+ 'ACTION_REQUIRED'
53
+ );
54
+ assert.deepEqual(
55
+ reduceReadiness([{ kind: 'windows_task_creation_failed', reason: 'Access denied.' }]),
56
+ {
57
+ state: 'LIMITED',
58
+ reasons: ['Access denied.'],
59
+ actions: ['Run mnemonik scanner enable to try again.'],
60
+ }
61
+ );
62
+ assert.equal(
63
+ reduceReadiness([{ kind: 'indexing_stalled', reason: 'No heartbeat in two minutes.' }]).state,
64
+ 'FAILED'
65
+ );
66
+ });
67
+
68
+ test('serialization keeps progress separate and omits an unattempted project', () => {
69
+ const withProject = serializeReadiness({
70
+ installation: { conditions: [] },
71
+ projects: [
72
+ {
73
+ projectId: 'project-1',
74
+ summary: { conditions: [] },
75
+ indexing: { total: 3100, completed: 1240 },
76
+ },
77
+ ],
78
+ generatedAt: '2026-09-11T00:00:00.000Z',
79
+ });
80
+ assert.equal(withProject.projects?.[0]?.summary.state, 'READY');
81
+ assert.deepEqual(withProject.projects?.[0]?.indexing, { total: 3100, completed: 1240 });
82
+
83
+ const omitted = serializeReadiness({
84
+ installation: { conditions: [] },
85
+ generatedAt: '2026-09-11T00:00:00.000Z',
86
+ });
87
+ assert.equal(Object.hasOwn(omitted, 'projects'), false);
88
+ assert.equal(omitted.scanner, null);
89
+ assert.equal(omitted.devicesAndGrants, null);
90
+ });
91
+
92
+ test('the CLI sentence names the concrete reason and action', () => {
93
+ assert.equal(
94
+ describeReadiness(
95
+ reduceReadiness([
96
+ {
97
+ kind: 'host_skipped',
98
+ component: 'cursor',
99
+ reason: 'Cursor was skipped.',
100
+ },
101
+ ])
102
+ ),
103
+ 'Done, with one thing left. Cursor was skipped. Connect it later: mnemonik connect cursor'
104
+ );
105
+ });
106
+
107
+ test('completion counts reasons requiring attention and preserves the development note', () => {
108
+ assert.equal(
109
+ describeReadiness({ state: 'LIMITED', reasons: ['dev_release_source'], actions: [] }),
110
+ 'Done. dev_release_source.'
111
+ );
112
+ const reasons = Array.from({ length: 7 }, (_, index) => `problem_${index}`);
113
+ assert.equal(
114
+ describeReadiness({
115
+ state: 'LIMITED',
116
+ reasons: [...reasons, 'dev_release_source'],
117
+ actions: ['mnemonik repair'],
118
+ }),
119
+ `Done, with 7 things left. ${reasons.map((reason) => `${reason}.`).join(' ')} dev_release_source. mnemonik repair`
120
+ );
121
+ assert.equal(
122
+ describeReadiness({
123
+ state: 'LIMITED',
124
+ reasons: ['Scanner needs attention.'],
125
+ actions: ['First action.', 'Second action.'],
126
+ }),
127
+ 'Done, with one thing left. Scanner needs attention. First action. Second action.'
128
+ );
129
+ });
130
+
131
+ test('optional version receipts round-trip while old schema-1 documents remain valid', async () => {
132
+ const { isReadinessDocument } = await import('../src/readiness.js');
133
+ const old = serializeReadiness({ installation: { conditions: [] } });
134
+ assert.equal(isReadinessDocument(old), true);
135
+ assert.equal(Object.hasOwn(old, 'versions'), false);
136
+ const versions = {
137
+ cli: '0.1.0',
138
+ scanner: '7.95.2',
139
+ hosts: [
140
+ { host: 'claude_code', editor: '2.1.0', hooks: '0.10.0' },
141
+ { host: 'codex', hooks: '0.9.0' },
142
+ ],
143
+ };
144
+ const current = serializeReadiness({ installation: { conditions: [] }, versions });
145
+ assert.deepEqual(current.versions, versions);
146
+ assert.equal(isReadinessDocument(current), true);
147
+ for (const invalid of [
148
+ null,
149
+ { cli: 12 },
150
+ { scanner: '' },
151
+ { cli: 'bad\nversion' },
152
+ { cli: 'x'.repeat(129) },
153
+ { hosts: [{ host: 'cursor', editor: true }] },
154
+ { hosts: [{ host: 'cursor', token: 'secret' }] },
155
+ { secret: 'no' },
156
+ ])
157
+ assert.equal(isReadinessDocument({ ...old, versions: invalid }), false);
158
+ });
@@ -0,0 +1,26 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { createHash } from 'node:crypto';
3
+ import { test } from 'node:test';
4
+ import assert from 'node:assert/strict';
5
+ import { canonicalizeRemote, selectRemote } from '../src/repositoryFingerprint.js';
6
+
7
+ const golden = JSON.parse(
8
+ readFileSync(new URL('./fixtures/repository-fingerprint-v1.golden.json', import.meta.url), 'utf8')
9
+ );
10
+ for (const row of golden.remotes) {
11
+ test(`${row.group}/${row.name}`, () =>
12
+ assert.deepEqual(canonicalizeRemote(row.url), row.expected));
13
+ }
14
+ for (const row of golden.selections) {
15
+ test(`selection/${row.name}`, () => assert.deepEqual(selectRemote(row.remotes), row.expected));
16
+ }
17
+ test('all golden hashes are SHA-256 of the exact canonical UTF-8', () => {
18
+ for (const row of [...golden.remotes, ...golden.selections]) {
19
+ const expected = row.expected.fingerprint ?? row.expected;
20
+ if (expected.canonical)
21
+ assert.equal(
22
+ expected.hash,
23
+ createHash('sha256').update(expected.canonical, 'utf8').digest('hex')
24
+ );
25
+ }
26
+ });
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": ".",
5
+ "noEmit": true
6
+ },
7
+ "include": ["src/**/*", "tests/**/*"],
8
+ "exclude": ["node_modules", "dist"]
9
+ }
@@ -1,7 +1,7 @@
1
1
  ; Hand-written: tree-sitter-bash ships no queries/tags.scm.
2
2
  ;
3
3
  ; Node names verified by parsing a sample with the vendored bash.wasm rather than
4
- ; taken from documentation — `function_definition` carries its name in a `name`
4
+ ; taken from documentation - `function_definition` carries its name in a `name`
5
5
  ; field typed `word`, covering both `foo() { }` and `function foo { }`.
6
6
  (function_definition
7
7
  name: (word) @name) @definition.function
@@ -2,7 +2,7 @@
2
2
  ;
3
3
  ; Node names verified against the vendored groovy.wasm. Groovy distinguishes a
4
4
  ; top-level `function_definition` from a `method_declaration` inside a
5
- ; `class_body`, and both name via an `identifier` field — so methods get
5
+ ; `class_body`, and both name via an `identifier` field - so methods get
6
6
  ; definition.method and keep their own name, which is the defect this plan
7
7
  ; exists to fix for every language.
8
8
  (class_declaration
@@ -68,8 +68,8 @@
68
68
  ; ANCHORED TO `program` ON PURPOSE, and the anchor is load-bearing. The chunker's
69
69
  ; leaf rule only suppresses a definition nested inside ANOTHER DEFINITION; a bare
70
70
  ; block, an `if` body and a `for` initialiser are not definitions, so an
71
- ; unanchored variable_declarator pattern writes locals — the `i` of every
72
- ; for-loop, every `const` in every block — straight into
71
+ ; unanchored variable_declarator pattern writes locals - the `i` of every
72
+ ; for-loop, every `const` in every block - straight into
73
73
  ; `memory_file_index.symbol_name`, where the `symbol:` leg prefix-matches them.
74
74
  ; Measured on this repo, unanchored inflates the chunk count 28.9% and makes
75
75
  ; `symbol: i` return loop bodies. python.tags.scm anchors its constant pattern to
@@ -38,6 +38,6 @@
38
38
  ancestor: (user_defined_type (_) @name . )) @reference.class
39
39
 
40
40
 
41
- ;; Imports ( note that unknown is not standardised )
41
+ ;; Imports (unknown is not standardised)
42
42
  (import_directive
43
43
  import_name: (_) @name ) @reference.unknown