engineering-memory 0.2.0 → 0.2.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.
@@ -29,8 +29,6 @@ Choose the hook mode through the Codex or Claude native questionnaire before
29
29
  passing a repository. The installer never opens a questionnaire or an
30
30
  authentication web page.`;
31
31
 
32
- // The backend this package was published for. An installation only names one
33
- // when it points at a different, self-hosted backend.
34
32
  async function publishedApiUrl() {
35
33
  const manifest = await readJson(join(packageRoot, 'package.json'));
36
34
  const apiUrl = manifest?.engineeringMemory?.apiUrl;
@@ -3,10 +3,6 @@ import { tmpdir } from 'node:os';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { readJson, resolveProductionPackagePaths } from '../install/files.mjs';
5
5
 
6
- // The published package cannot carry node_modules, because npm excludes it from
7
- // every tarball. The bridge runtime is assembled here from the dependencies npm
8
- // installed for this package, into the exact layout the installer verifies
9
- // against the bridge lockfile.
10
6
  export async function stageBridgeRuntime(packageRoot, resolver) {
11
7
  const source = join(packageRoot, 'runtime');
12
8
  const packageJson = await readJson(join(source, 'package.json'));
@@ -37,8 +33,6 @@ export async function stageBridgeRuntime(packageRoot, resolver) {
37
33
  return staged;
38
34
  }
39
35
 
40
- // A package is free to keep package.json out of its exports map, and several do,
41
- // so the directory is found on disk rather than through module resolution.
42
36
  async function locate(name, resolver) {
43
37
  for (const directory of resolver.resolve.paths(name) ?? []) {
44
38
  const candidate = join(directory, ...name.split('/'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "engineering-memory",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
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",
@@ -1160,9 +1160,6 @@ export class BridgeService {
1160
1160
  });
1161
1161
  });
1162
1162
  }
1163
- // What the backend expects of this client, and whether the user has already
1164
- // been asked about it. A required update leaves no choice; an optional one is
1165
- // offered once per version and never raised again after a refusal.
1166
1163
  async clientUpdate(authenticated) {
1167
1164
  const installed = this.dependencies.clientVersion;
1168
1165
  if (!authenticated) {
@@ -1799,8 +1796,8 @@ export class BridgeService {
1799
1796
  if (pointer.verificationIntent || pointer.closeIntent) {
1800
1797
  throw new Error('A clean active task snapshot is required for checkpointing');
1801
1798
  }
1802
- if (pointer.taskSlug !== input.taskSlug) {
1803
- throw new Error('Checkpoint task slug does not match the active task pointer');
1799
+ if (pointer.taskSlug.toLowerCase() !== input.taskSlug.toLowerCase()) {
1800
+ throw new Error(`Checkpoint task slug ${input.taskSlug} does not match the active task ${pointer.taskSlug}`);
1804
1801
  }
1805
1802
  return pointer;
1806
1803
  }
@@ -2296,7 +2293,19 @@ function assertNewResourceEvidence(candidates, evidence) {
2296
2293
  .map(({ path, kind }) => ({ path, kind }))
2297
2294
  .sort((left, right) => left.path.localeCompare(right.path));
2298
2295
  if (stableStringify(expected) !== stableStringify(actual)) {
2299
- throw new Error('New screen or shared component paths require exact memory reconciliation');
2296
+ const expectedPaths = new Set(expected.map((entry) => entry.path));
2297
+ const actualPaths = new Set(actual.map((entry) => entry.path));
2298
+ const missing = expected
2299
+ .filter((entry) => !actualPaths.has(entry.path))
2300
+ .map((entry) => `${entry.path} (${entry.kind})`);
2301
+ const extra = actual
2302
+ .filter((entry) => !expectedPaths.has(entry.path))
2303
+ .map((entry) => `${entry.path} (${entry.kind})`);
2304
+ const details = [
2305
+ missing.length ? `missing evidence for ${missing.join(', ')}` : '',
2306
+ extra.length ? `evidence for paths the policy does not classify: ${extra.join(', ')}` : '',
2307
+ ].filter(Boolean);
2308
+ throw new Error(`New screen or shared component paths require exact memory reconciliation. ${details.length ? details.join('; ') : 'The kinds do not match the project discovery policy.'}`);
2300
2309
  }
2301
2310
  }
2302
2311
  function assertValidationEvidence(validations) {
@@ -1,8 +1,5 @@
1
1
  import { join } from 'node:path';
2
2
  import { readJson, removeFile, writeJson } from '../utilities/files.js';
3
- // What a user decided about Engineering Memory in one repository, on one
4
- // machine. It never lives in the repository: a developer switching it off must
5
- // not switch it off for everyone who clones after them.
6
3
  export class RepositoryDecisionStore {
7
4
  root;
8
5
  queues = new Map();
@@ -1,7 +1,5 @@
1
1
  import { join } from 'node:path';
2
2
  import { readJson, writeJson } from '../utilities/files.js';
3
- // Which client version the user was offered and turned down. Asking again for
4
- // the same version is nagging; asking when a newer one arrives is news.
5
3
  export class UpdateChoiceStore {
6
4
  path;
7
5
  root;
@@ -68,6 +68,12 @@ Before proposing a flow record, ask the user what the design cannot answer: whic
68
68
 
69
69
  When the user names a different Figma file or link, treat it as a correction to the `figma_reference` record and propose the revision in the same reply, with the new file key and URL. Ask for approval there and then. Do not carry the new address only in the conversation: the next session reads the record, not the chat.
70
70
 
71
+ ## Running the Application
72
+
73
+ When the change is something a person sees or interacts with and this environment can run the application, ask before launching it rather than starting on your own. A run occupies the user's machine, can take minutes, and they may be using it. Say what you would exercise and what you are looking for, so the answer is about the work rather than about the tooling.
74
+
75
+ Ask again nothing when they decline. Verify what you can without it and state in the handoff that the flow was not exercised in a running application.
76
+
71
77
  ## Correction Scope
72
78
 
73
79
  At a natural checkpoint after recording and fixing a user correction, first state how the correction was classified and why, then offer the scopes that classification allows.
@@ -99,6 +105,8 @@ After `task.close`, and every time, ask the user what to do with the finished wo
99
105
 
100
106
  The two pull request options take the base branch the pull request targets, typed by the user; the head is the branch the task was done on. Never guess a base branch, and never open a pull request that was not asked for.
101
107
 
108
+ Check that a pull request can actually be opened before offering one. If the machine has no `gh` and no token to open it with, say so in the option itself and offer what is really on the table: commit and push, and the ready-to-open compare link afterwards. Offering something you cannot deliver wastes the user's answer. Never read a credential out of a keychain or a helper to open one yourself.
109
+
102
110
  Once a pull request exists the work is not finished and the turn does not end there. Check whether it merges cleanly. If it does, say so with the link. If it does not, name the conflicting files and ask whether to resolve them — then wait. Resolving a conflict is a change to someone else's work and needs its own yes.
103
111
 
104
112
  ## Git Hook