engineering-memory 1.11.7 → 1.11.9

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.
@@ -9,7 +9,7 @@ Call \`session.entry\` before you answer anything in a repository, whatever the
9
9
  - Switched off: say nothing about Engineering Memory, offer nothing, hint at nothing, however useful it would be for what was asked. They turned it off; raising it again is what this prevents. Record it back on only when they ask.
10
10
  - Bound: continue into the lifecycle.
11
11
 
12
- \`session.entry\` also reports this client's version. When it says an update is available, offer once to run \`npx engineering-memory@latest install\` and tell the user the client restarts afterwards. If they decline, record it with \`session.decline_update\` and never raise that version again. When it says an update is required, there is no choice to offer: say that this client is older than the backend supports, give the command, and do not carry on as though the lifecycle works.
12
+ \`session.entry\` also reports this client's version. When it says an update is available, offer once to run \`npx engineering-memory@latest install\` and tell the user that the running bridge switches to the new release by itself on its next call, so nothing needs restarting; only a bridge older than 1.11.9 needs the client restarted once after that install. If they decline, record it with \`session.decline_update\` and never raise that version again. When it says an update is required, there is no choice to offer: say that this client is older than the backend supports, give the command, and do not carry on as though the lifecycle works.
13
13
 
14
14
  The user may say at any point that they want a different organization, a different project, Engineering Memory switched off, or switched back on. Recognise what they mean rather than matching words — the request arrives in whatever language and phrasing they happen to use — and act on it. Changing the organization always asks for the project again.
15
15
 
package/install/files.mjs CHANGED
@@ -11,7 +11,13 @@ import {
11
11
  rm,
12
12
  writeFile,
13
13
  } from 'node:fs/promises';
14
- import { basename, dirname, join } from 'node:path';
14
+ import { basename, dirname, join, resolve } from 'node:path';
15
+ import {
16
+ launcherPackageJson,
17
+ launcherSource,
18
+ normalizeRelativeEntry,
19
+ releasePointerName,
20
+ } from './launcher.mjs';
15
21
 
16
22
  export class InstallTransaction {
17
23
  constructor() {
@@ -110,6 +116,7 @@ export async function installManagedDirectory(
110
116
  ) {
111
117
  const artifact = options.artifact ?? 'skill';
112
118
  const artifactLabel = artifact === 'skill' ? 'skill' : 'bridge runtime';
119
+ const accepted = new Set([artifact, ...(options.replaces ?? [])]);
113
120
  const sourceHash = await hashDirectory(source);
114
121
  const manifestPath = join(destination, '.engineering-memory-install.json');
115
122
  if (await exists(destination)) {
@@ -117,7 +124,7 @@ export async function installManagedDirectory(
117
124
  if (
118
125
  manifest?.product !== 'engineering-memory' ||
119
126
  manifest?.schemaVersion !== 1 ||
120
- (manifest.artifact ?? 'skill') !== artifact
127
+ !accepted.has(manifest.artifact ?? 'skill')
121
128
  ) {
122
129
  throw new Error(
123
130
  `Refusing to overwrite an unmanaged ${artifactLabel} directory: ${destination}`,
@@ -151,6 +158,7 @@ export async function installManagedDirectory(
151
158
  schemaVersion: 1,
152
159
  artifact,
153
160
  sourceHash,
161
+ ...(options.manifest ?? {}),
154
162
  },
155
163
  null,
156
164
  2,
@@ -181,9 +189,22 @@ export async function installManagedDirectory(
181
189
 
182
190
  export async function installManagedBridgeRuntime(
183
191
  source,
184
- destination,
192
+ runtimeRoot,
185
193
  transaction,
194
+ options = {},
186
195
  ) {
196
+ const bridgeEntry = normalizeRelativeEntry(
197
+ options.bridgeEntry ?? 'dist/src/index.js',
198
+ 'Bridge entry',
199
+ );
200
+ const gateEntry = normalizeRelativeEntry(
201
+ options.gateEntry ?? 'dist/src/git/pre-commit.js',
202
+ 'Git gate entry',
203
+ );
204
+ const clientVersion =
205
+ typeof options.clientVersion === 'string' && options.clientVersion
206
+ ? options.clientVersion
207
+ : null;
187
208
  const packageJsonPath = join(source, 'package.json');
188
209
  const packageLockPath = join(source, 'package-lock.json');
189
210
  const nodeModulesPath = join(source, 'node_modules');
@@ -209,10 +230,8 @@ export async function installManagedBridgeRuntime(
209
230
  packageJson,
210
231
  packageLock,
211
232
  );
212
- await mkdir(dirname(destination), { recursive: true });
213
- const bundle = await mkdtemp(
214
- join(dirname(destination), '.bridge-runtime-source-'),
215
- );
233
+ await mkdir(runtimeRoot, { recursive: true });
234
+ const bundle = await mkdtemp(join(runtimeRoot, '.bridge-runtime-source-'));
216
235
  try {
217
236
  await copyDirectory(join(source, 'dist'), join(bundle, 'dist'));
218
237
  await copyFile(packageJsonPath, join(bundle, 'package.json'));
@@ -230,14 +249,101 @@ export async function installManagedBridgeRuntime(
230
249
  join(bundle, ...packageEntry.path.split('/')),
231
250
  );
232
251
  }
233
- return await installManagedDirectory(bundle, destination, transaction, {
252
+ for (const [entry, label] of [
253
+ [bridgeEntry, 'Bridge entry'],
254
+ [gateEntry, 'Git gate entry'],
255
+ ]) {
256
+ if (!(await exists(join(bundle, ...entry.split('/'))))) {
257
+ throw new Error(`${label} is missing from the built bridge: ${entry}`);
258
+ }
259
+ }
260
+ const sourceHash = await hashDirectory(bundle);
261
+ const releasePath = join(runtimeRoot, 'releases', sourceHash.slice(0, 16));
262
+ const installedAt = new Date().toISOString();
263
+ await installManagedDirectory(bundle, releasePath, transaction, {
234
264
  artifact: 'bridge-runtime',
265
+ manifest: { clientVersion, installedAt },
235
266
  });
267
+ const launcherPath = join(runtimeRoot, 'bridge');
268
+ const launcherStage = await mkdtemp(join(runtimeRoot, '.bridge-launcher-'));
269
+ try {
270
+ await writeAtomic(
271
+ join(launcherStage, 'package.json'),
272
+ launcherPackageJson,
273
+ );
274
+ await writeAtomic(
275
+ join(launcherStage, ...bridgeEntry.split('/')),
276
+ launcherSource('bridgeEntry', bridgeEntry),
277
+ );
278
+ await writeAtomic(
279
+ join(launcherStage, ...gateEntry.split('/')),
280
+ launcherSource('gateEntry', gateEntry),
281
+ );
282
+ await installManagedDirectory(launcherStage, launcherPath, transaction, {
283
+ artifact: 'bridge-launcher',
284
+ replaces: ['bridge-runtime'],
285
+ });
286
+ } finally {
287
+ await rm(launcherStage, { recursive: true, force: true });
288
+ }
289
+ await writeJson(
290
+ join(runtimeRoot, releasePointerName),
291
+ {
292
+ product: 'engineering-memory',
293
+ schemaVersion: 1,
294
+ version: clientVersion,
295
+ sourceHash,
296
+ path: releasePath,
297
+ bridgeEntry,
298
+ gateEntry,
299
+ installedAt,
300
+ },
301
+ transaction,
302
+ );
303
+ return { sourceHash, releasePath, launcherPath, bridgeEntry, gateEntry };
236
304
  } finally {
237
305
  await rm(bundle, { recursive: true, force: true });
238
306
  }
239
307
  }
240
308
 
309
+ export async function pruneReleases(runtimeRoot, options = {}) {
310
+ const keep = Math.max(1, options.keep ?? 3);
311
+ const releasesPath = join(runtimeRoot, 'releases');
312
+ if (!(await exists(releasesPath))) return [];
313
+ const pointer = await readJson(join(runtimeRoot, releasePointerName));
314
+ const currentPath =
315
+ pointer && typeof pointer.path === 'string' ? resolve(pointer.path) : null;
316
+ const candidates = [];
317
+ for (const entry of await readdir(releasesPath, { withFileTypes: true })) {
318
+ if (!entry.isDirectory()) continue;
319
+ const path = join(releasesPath, entry.name);
320
+ if (currentPath && resolve(path) === currentPath) continue;
321
+ const manifest = await readJson(
322
+ join(path, '.engineering-memory-install.json'),
323
+ );
324
+ if (
325
+ manifest?.product !== 'engineering-memory' ||
326
+ manifest.artifact !== 'bridge-runtime'
327
+ ) {
328
+ continue;
329
+ }
330
+ const installedAt = Date.parse(String(manifest.installedAt ?? ''));
331
+ candidates.push({
332
+ path,
333
+ installedAt: Number.isNaN(installedAt)
334
+ ? (await lstat(path)).mtimeMs
335
+ : installedAt,
336
+ });
337
+ }
338
+ candidates.sort((left, right) => right.installedAt - left.installedAt);
339
+ const removed = [];
340
+ for (const candidate of candidates.slice(keep - 1)) {
341
+ await rm(candidate.path, { recursive: true, force: true });
342
+ removed.push(candidate.path);
343
+ }
344
+ return removed;
345
+ }
346
+
241
347
  export async function hashDirectory(root, options = {}) {
242
348
  const hash = createHash('sha256');
243
349
  const excluded = new Set(options.exclude ?? []);
@@ -10,6 +10,7 @@ import {
10
10
  installManagedBridgeRuntime,
11
11
  installManagedDirectory,
12
12
  InstallTransaction,
13
+ pruneReleases,
13
14
  readJson,
14
15
  readText,
15
16
  replaceFile,
@@ -54,7 +55,8 @@ export async function installEngineeringMemory(options = {}) {
54
55
  options.gateEntry ??
55
56
  join(bridgeRuntimeSource, 'dist', 'src', 'git', 'pre-commit.js'),
56
57
  );
57
- const runtimePath = join(homeDir, '.engineering-memory', 'runtime', 'bridge');
58
+ const runtimeRoot = join(homeDir, '.engineering-memory', 'runtime');
59
+ const runtimePath = join(runtimeRoot, 'bridge');
58
60
  const bridgeEntry = stableRuntimeEntry(
59
61
  bridgeRuntimeSource,
60
62
  runtimePath,
@@ -153,10 +155,15 @@ export async function installEngineeringMemory(options = {}) {
153
155
  const transaction = new InstallTransaction();
154
156
  let result;
155
157
  try {
156
- const runtimeHash = await installManagedBridgeRuntime(
158
+ const runtime = await installManagedBridgeRuntime(
157
159
  bridgeRuntimeSource,
158
- runtimePath,
160
+ runtimeRoot,
159
161
  transaction,
162
+ {
163
+ clientVersion,
164
+ bridgeEntry: relative(bridgeRuntimeSource, sourceBridgeEntry),
165
+ gateEntry: relative(bridgeRuntimeSource, sourceGateEntry),
166
+ },
160
167
  );
161
168
  const mcp = {
162
169
  ...(existingState?.mcp ?? {}),
@@ -183,7 +190,8 @@ export async function installEngineeringMemory(options = {}) {
183
190
  skillHash,
184
191
  runtime: {
185
192
  path: runtimePath,
186
- sourceHash: runtimeHash,
193
+ sourceHash: runtime.sourceHash,
194
+ release: runtime.releasePath,
187
195
  bridgeEntry,
188
196
  gateEntry,
189
197
  },
@@ -206,6 +214,7 @@ export async function installEngineeringMemory(options = {}) {
206
214
  apiUrl,
207
215
  clientVersion,
208
216
  runtimePath,
217
+ release: runtime.releasePath,
209
218
  bridgeEntry,
210
219
  gateEntry,
211
220
  hook: hookResult,
@@ -222,6 +231,13 @@ export async function installEngineeringMemory(options = {}) {
222
231
  throw error;
223
232
  }
224
233
  await transaction.commit();
234
+ try {
235
+ result.prunedReleases = await pruneReleases(runtimeRoot);
236
+ } catch (error) {
237
+ result.prunedReleases = [];
238
+ result.releasePruneProblem =
239
+ error instanceof Error ? error.message : String(error);
240
+ }
225
241
  return result;
226
242
  }
227
243
 
@@ -0,0 +1,95 @@
1
+ export const launcherPackageJson = `${JSON.stringify(
2
+ {
3
+ name: 'engineering-memory-bridge-launcher',
4
+ private: true,
5
+ type: 'module',
6
+ },
7
+ null,
8
+ 2,
9
+ )}\n`;
10
+
11
+ export const releasePointerName = 'current.json';
12
+
13
+ export function normalizeRelativeEntry(entry, label) {
14
+ const normalized = String(entry).replaceAll('\\', '/').replace(/^\.\//, '');
15
+ const segments = normalized.split('/');
16
+ if (
17
+ normalized === '' ||
18
+ normalized.startsWith('/') ||
19
+ /^[A-Za-z]:/.test(normalized) ||
20
+ segments.some(
21
+ (segment) => segment === '' || segment === '..' || segment === '.',
22
+ )
23
+ ) {
24
+ throw new Error(
25
+ `${label} must be a relative path inside the release: ${entry}`,
26
+ );
27
+ }
28
+ if (segments[0] !== 'dist') {
29
+ throw new Error(
30
+ `${label} must be inside the built dist directory: ${entry}`,
31
+ );
32
+ }
33
+ return normalized;
34
+ }
35
+
36
+ export function launcherSource(kind, relativeEntry) {
37
+ if (kind !== 'bridgeEntry' && kind !== 'gateEntry') {
38
+ throw new Error(`Unknown launcher kind: ${kind}`);
39
+ }
40
+ const depth = normalizeRelativeEntry(relativeEntry, 'Launcher entry').split(
41
+ '/',
42
+ ).length;
43
+ const ascend = Array.from({ length: depth }, () => "'..'").join(', ');
44
+ const label = kind === 'bridgeEntry' ? 'bridge' : 'Git gate';
45
+ return [
46
+ '#!/usr/bin/env node',
47
+ "import { readFile } from 'node:fs/promises';",
48
+ "import { dirname, isAbsolute, join, resolve } from 'node:path';",
49
+ "import { fileURLToPath, pathToFileURL } from 'node:url';",
50
+ '',
51
+ `const runtimeRoot = resolve(dirname(fileURLToPath(import.meta.url)), ${ascend});`,
52
+ `const pointerPath = join(runtimeRoot, ${JSON.stringify(releasePointerName)});`,
53
+ `const installCommand = 'npx engineering-memory@latest install';`,
54
+ '',
55
+ 'function stop(message) {',
56
+ " process.stderr.write(message + '\\n');",
57
+ ' process.exit(1);',
58
+ '}',
59
+ '',
60
+ 'let pointer;',
61
+ 'try {',
62
+ " pointer = JSON.parse(await readFile(pointerPath, 'utf8'));",
63
+ '} catch (error) {',
64
+ ' stop(',
65
+ ` 'The Engineering Memory ${label} cannot start: ' +`,
66
+ ' pointerPath +',
67
+ " ' is unreadable (' +",
68
+ ' (error instanceof Error ? error.message : String(error)) +',
69
+ " '). Run ' +",
70
+ ' installCommand +',
71
+ " '.',",
72
+ ' );',
73
+ '}',
74
+ 'if (',
75
+ ' !pointer ||',
76
+ " pointer.product !== 'engineering-memory' ||",
77
+ " typeof pointer.path !== 'string' ||",
78
+ ` typeof pointer.${kind} !== 'string'`,
79
+ ') {',
80
+ ' stop(',
81
+ ` 'The Engineering Memory ${label} cannot start: ' +`,
82
+ ' pointerPath +',
83
+ " ' does not name a release. Run ' +",
84
+ ' installCommand +',
85
+ " '.',",
86
+ ' );',
87
+ '}',
88
+ 'const releasePath = isAbsolute(pointer.path)',
89
+ ' ? pointer.path',
90
+ ' : resolve(runtimeRoot, pointer.path);',
91
+ 'process.env.ENGINEERING_MEMORY_RUNTIME_CURRENT = pointerPath;',
92
+ `await import(pathToFileURL(join(releasePath, ...pointer.${kind}.split('/'))).href);`,
93
+ '',
94
+ ].join('\n');
95
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "engineering-memory",
3
- "version": "1.11.7",
3
+ "version": "1.11.9",
4
4
  "description": "Installs the Engineering Memory skill and its local MCP bridge. Sign in after installing; your organization and project are resolved from your account.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",
@@ -0,0 +1,3 @@
1
+ {
2
+ "gitHead": "3b5175e578b3d631932b079b5678afe13dc4f00d"
3
+ }
@@ -10,6 +10,9 @@ export function loadBridgeConfig(env = process.env) {
10
10
  apiBaseUrl: (env.ENGINEERING_MEMORY_API_URL ?? 'http://127.0.0.1:3000').replace(/\/$/, ''),
11
11
  stateRoot: resolve(env.ENGINEERING_MEMORY_STATE_DIR ?? join(homedir(), '.engineering-memory')),
12
12
  clientVersion: env.ENGINEERING_MEMORY_CLIENT_VERSION ?? null,
13
+ runtimeCurrentPath: env.ENGINEERING_MEMORY_RUNTIME_CURRENT
14
+ ? resolve(env.ENGINEERING_MEMORY_RUNTIME_CURRENT)
15
+ : null,
13
16
  requestTimeoutMs: positiveInteger(env.ENGINEERING_MEMORY_TIMEOUT_MS, 15_000),
14
17
  cacheMaxEntries: positiveInteger(env.ENGINEERING_MEMORY_CACHE_MAX_ENTRIES, 64),
15
18
  cacheMaxBytes: positiveInteger(env.ENGINEERING_MEMORY_CACHE_MAX_BYTES, 5 * 1024 * 1024),
@@ -1,88 +1,32 @@
1
1
  #!/usr/bin/env node
2
- import { WorktreePool } from './runtime/worktree-pool.js';
3
- import { WorktreePolicyCache } from './runtime/worktree-policy.js';
2
+ import { dirname, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
4
  import { serveStdio } from '@modelcontextprotocol/server/stdio';
5
- import { BrowserAuthCoordinator } from './auth/browser-auth.js';
6
- import { createCredentialStore } from './auth/credential-store.js';
7
- import { EtagCache } from './cache/etag-cache.js';
8
- import { apiNamespaceKey, apiStateRoot, endpoints, loadBridgeConfig } from './config.js';
9
- import { GitInspector } from './git/git-inspector.js';
10
- import { VerificationGate } from './git/verification-gate.js';
11
- import { JournalStore } from './journal/journal-store.js';
12
- import { createEngineeringMemoryServer } from './mcp/server.js';
13
- import { RepositoryResolver } from './project/repository.js';
14
- import { ApiClient } from './runtime/api-client.js';
15
- import { BridgeService } from './runtime/bridge-service.js';
16
- import { OfflineOutbox } from './runtime/offline-outbox.js';
17
- import { ActiveContextStore } from './runtime/active-context-store.js';
18
- import { RepositoryDecisionStore } from './runtime/repository-decision-store.js';
19
- import { ShadowNoticeStore } from './runtime/shadow-notice-store.js';
20
- import { UpdateChoiceStore } from './runtime/update-choice-store.js';
21
- import { PrincipalStateGuard } from './runtime/principal-state.js';
22
- import { OnboardingStore } from './runtime/onboarding-store.js';
23
- import { QuestionnaireStore } from './runtime/questionnaire-store.js';
24
- export function createBridgeService() {
5
+ import { loadBridgeConfig } from './config.js';
6
+ import { createMcpServer } from './mcp/server.js';
7
+ import { createRuntime } from './runtime/runtime-entry.js';
8
+ import { RuntimeHost, readCurrentRelease } from './runtime/runtime-host.js';
9
+ export { createBridgeService } from './runtime/create-bridge-service.js';
10
+ const releaseRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..', '..');
11
+ export async function createServer() {
25
12
  const config = loadBridgeConfig();
26
- const apiNamespace = apiNamespaceKey(config.apiBaseUrl);
27
- const stateRoot = apiStateRoot(config);
28
- const credentials = createCredentialStore({
29
- service: config.credentialService,
30
- account: `${config.credentialAccount}:${apiNamespace}`,
31
- stateRoot,
13
+ const server = createMcpServer();
14
+ let version = config.clientVersion;
15
+ const currentPath = config.runtimeCurrentPath;
16
+ if (currentPath) {
17
+ const current = await readCurrentRelease(currentPath);
18
+ if ('release' in current && current.path === releaseRoot)
19
+ version = current.release.version ?? version;
20
+ }
21
+ const host = new RuntimeHost({
22
+ server,
23
+ release: { path: releaseRoot, version, module: { createRuntime } },
24
+ ...(currentPath ? { currentPath } : {}),
25
+ log: (message) => process.stderr.write(`${message}\n`),
32
26
  });
33
- const cache = new EtagCache({
34
- stateRoot,
35
- maxEntries: config.cacheMaxEntries,
36
- maxBytes: config.cacheMaxBytes,
37
- maxAgeMs: config.cacheMaxAgeMs,
38
- });
39
- const client = new ApiClient({
40
- baseUrl: config.apiBaseUrl,
41
- timeoutMs: config.requestTimeoutMs,
42
- credentials,
43
- cache,
44
- refreshPath: endpoints.authRefresh,
45
- });
46
- const git = new GitInspector();
47
- const outbox = new OfflineOutbox(stateRoot);
48
- const activeContexts = new ActiveContextStore(stateRoot);
49
- const repositoryDecisions = new RepositoryDecisionStore(stateRoot);
50
- const updateChoices = new UpdateChoiceStore(stateRoot);
51
- const shadowNotices = new ShadowNoticeStore(stateRoot);
52
- const worktreePool = new WorktreePool(stateRoot, {
53
- pendingWork: async (entry) => {
54
- if (!entry.taskId)
55
- return false;
56
- const pointer = await activeContexts.loadForTask(entry.repoFingerprint, entry.taskId);
57
- return Boolean(pointer?.verificationIntent ||
58
- pointer?.closeIntent ||
59
- (await outbox.listForTask(entry.taskId)).length);
60
- },
61
- });
62
- const gate = new VerificationGate(stateRoot, git, outbox, undefined, worktreePool);
63
- const principalState = new PrincipalStateGuard(stateRoot, credentials, cache, outbox, activeContexts, gate);
64
- return new BridgeService({
65
- worktreePool,
66
- worktreePolicy: new WorktreePolicyCache(stateRoot),
67
- client,
68
- credentials,
69
- browserAuth: new BrowserAuthCoordinator(client, credentials),
70
- repositories: new RepositoryResolver(git, config.markerSchemaVersion, stateRoot),
71
- journal: new JournalStore(stateRoot),
72
- outbox,
73
- gate,
74
- activeContexts,
75
- repositoryDecisions,
76
- updateChoices,
77
- shadowNotices,
78
- questionnaires: new QuestionnaireStore(stateRoot),
79
- onboarding: new OnboardingStore(stateRoot),
80
- clientVersion: config.clientVersion,
81
- principalState,
82
- });
83
- }
84
- export function createServer() {
85
- return createEngineeringMemoryServer(createBridgeService());
27
+ host.start();
28
+ host.watch(15_000);
29
+ return server;
86
30
  }
87
31
  void serveStdio(createServer);
88
32
  //# sourceMappingURL=index.js.map
@@ -2,17 +2,24 @@ import { registerWorktreeTools } from './worktree-tools.js';
2
2
  import { McpServer } from '@modelcontextprotocol/server';
3
3
  import { registerOnboardingTools } from './onboarding-tools.js';
4
4
  import { registerEngineeringMemoryTools, registerQuestionnaireTools } from './tool-definitions.js';
5
- export function createEngineeringMemoryServer(service) {
6
- const server = new McpServer({
5
+ export const serverInstructions = 'Start with session.entry and follow the returned lifecycle. Do not block independent task work on memory.propose_revision drafting, submission or approval. If the host permits background agents or concurrent calls, dispatch independent proposals and continue useful work; otherwise checkpoint the draft and defer submission until needed. Checkpoint, correction, reconciliation, self-review and scaffold receipts use the durable local outbox; after deliveryStatus pending, continue independent work without polling or resending. Inventory upload, claim-bound evidence submission and test-result reporting may overlap independent analysis using host-permitted concurrent calls. Wait only when the next operation needs the result; required task deliveries must settle before verification. Preserve task/source identity and native user approval; never treat pending work as verified.';
6
+ export function createMcpServer() {
7
+ return new McpServer({
7
8
  name: 'engineering-memory',
8
9
  version: '0.1.0',
9
10
  }, {
10
- instructions: 'Start with session.entry and follow the returned lifecycle. Do not block independent task work on memory.propose_revision drafting, submission or approval. If the host permits background agents or concurrent calls, dispatch independent proposals and continue useful work; otherwise checkpoint the draft and defer submission until needed. Checkpoint, correction, reconciliation, self-review and scaffold receipts use the durable local outbox; after deliveryStatus pending, continue independent work without polling or resending. Inventory upload, claim-bound evidence submission and test-result reporting may overlap independent analysis using host-permitted concurrent calls. Wait only when the next operation needs the result; required task deliveries must settle before verification. Preserve task/source identity and native user approval; never treat pending work as verified.',
11
+ instructions: serverInstructions,
11
12
  });
12
- registerEngineeringMemoryTools(server, service);
13
- registerQuestionnaireTools(server, service);
14
- registerOnboardingTools(server, service);
15
- registerWorktreeTools(server, service);
13
+ }
14
+ export function registerAllTools(host, service) {
15
+ registerEngineeringMemoryTools(host, service);
16
+ registerQuestionnaireTools(host, service);
17
+ registerOnboardingTools(host, service);
18
+ registerWorktreeTools(host, service);
19
+ }
20
+ export function createEngineeringMemoryServer(service) {
21
+ const server = createMcpServer();
22
+ registerAllTools(server, service);
16
23
  return server;
17
24
  }
18
25
  //# sourceMappingURL=server.js.map
@@ -0,0 +1,12 @@
1
+ export function recordingHost(server) {
2
+ const tools = [];
3
+ const registerTool = (name, config, callback) => {
4
+ tools.push({ name, config, callback });
5
+ return undefined;
6
+ };
7
+ return {
8
+ host: { registerTool: registerTool, server },
9
+ tools,
10
+ };
11
+ }
12
+ //# sourceMappingURL=tool-host.js.map