engineering-memory 1.11.20 → 1.11.21
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.
- package/bin/engineering-memory.mjs +14 -24
- package/install/localization.generated.mjs +18 -0
- package/package.json +1 -1
- package/runtime/build.json +1 -1
- package/runtime/dist/src/auth/browser-auth.js +15 -4
- package/runtime/dist/src/config.js +1 -0
- package/runtime/dist/src/git/git-inspector.js +10 -0
- package/runtime/dist/src/localization/catalogue.generated.js +786 -0
- package/runtime/dist/src/mcp/delivery-tools.js +15 -48
- package/runtime/dist/src/mcp/onboarding-tools.js +289 -618
- package/runtime/dist/src/mcp/questionnaire-tools.js +23 -26
- package/runtime/dist/src/mcp/status-meaning-tools.js +12 -86
- package/runtime/dist/src/mcp/status-remap-tools.js +252 -0
- package/runtime/dist/src/mcp/tool-annotations.js +1 -0
- package/runtime/dist/src/mcp/tool-definitions.js +33 -55
- package/runtime/dist/src/mcp/workflow-tools.js +17 -108
- package/runtime/dist/src/mcp/worktree-tools.js +168 -250
- package/runtime/dist/src/runtime/bridge-service.js +100 -94
- package/runtime/dist/src/runtime/create-bridge-service.js +4 -2
- package/runtime/dist/src/runtime/language-store.js +6 -0
- package/runtime/dist/src/runtime/release-notes.js +6 -6
- package/runtime/dist/src/runtime/release-report.js +30 -30
- package/runtime/dist/src/runtime/task-start.js +75 -73
- package/runtime/dist/src/runtime/texts.js +135 -0
- package/runtime/dist/src/runtime/worktree-pool.js +30 -5
- package/runtime/dist/src/runtime/worktree-preparation.js +3 -2
- package/runtime/dist/src/utilities/process.js +7 -0
- package/skill/references/lifecycle.md +21 -3
- package/skill/references/project-onboarding.md +5 -2
- package/skill/references/questionnaires.md +3 -1
|
@@ -6,36 +6,22 @@ import { fileURLToPath } from 'node:url';
|
|
|
6
6
|
import { exists, readJson } from '../install/files.mjs';
|
|
7
7
|
import { stageBridgeRuntime } from '../lib/stage-runtime.mjs';
|
|
8
8
|
import { runInstaller } from '../install/cli.mjs';
|
|
9
|
+
import { installerTexts } from '../install/localization.generated.mjs';
|
|
9
10
|
|
|
10
11
|
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
11
12
|
const require = createRequire(import.meta.url);
|
|
12
13
|
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
in the first time an agent uses it, and your organization and project come from
|
|
19
|
-
your account.
|
|
20
|
-
|
|
21
|
-
Options:
|
|
22
|
-
--clients codex,claude which agent CLIs to install for
|
|
23
|
-
--api-url <https-url> a different backend, for a self-hosted install
|
|
24
|
-
--repo <repository-root> install the Git verification gate in this repository
|
|
25
|
-
--hook-mode chain|verify-only|cancel
|
|
26
|
-
--help
|
|
27
|
-
|
|
28
|
-
Choose the hook mode through the Codex or Claude native questionnaire before
|
|
29
|
-
passing a repository. The installer never opens a questionnaire or an
|
|
30
|
-
authentication web page.`;
|
|
14
|
+
const locale = Intl.DateTimeFormat().resolvedOptions().locale;
|
|
15
|
+
const texts =
|
|
16
|
+
installerTexts[locale] ??
|
|
17
|
+
installerTexts[locale.split('-')[0]] ??
|
|
18
|
+
installerTexts.en;
|
|
31
19
|
|
|
32
20
|
async function publishedApiUrl() {
|
|
33
21
|
const manifest = await readJson(join(packageRoot, 'package.json'));
|
|
34
22
|
const apiUrl = manifest?.engineeringMemory?.apiUrl;
|
|
35
23
|
if (typeof apiUrl !== 'string' || apiUrl.length === 0) {
|
|
36
|
-
throw new Error(
|
|
37
|
-
'This package was built without a backend address. Pass --api-url.',
|
|
38
|
-
);
|
|
24
|
+
throw new Error(texts['installer.noBackend']);
|
|
39
25
|
}
|
|
40
26
|
return apiUrl;
|
|
41
27
|
}
|
|
@@ -48,15 +34,19 @@ async function publishedVersion() {
|
|
|
48
34
|
async function main(argumentList) {
|
|
49
35
|
const [command, ...rest] = argumentList;
|
|
50
36
|
if (!command || command === '--help' || command === 'help') {
|
|
51
|
-
process.stdout.write(`${usage}\n`);
|
|
37
|
+
process.stdout.write(`${texts['installer.usage']}\n`);
|
|
52
38
|
return;
|
|
53
39
|
}
|
|
54
40
|
if (command !== 'install') {
|
|
55
|
-
throw new Error(
|
|
41
|
+
throw new Error(
|
|
42
|
+
texts['installer.unknownCommand'].replace('{command}', command),
|
|
43
|
+
);
|
|
56
44
|
}
|
|
57
45
|
const skillSource = join(packageRoot, 'skill');
|
|
58
46
|
if (!(await exists(join(skillSource, 'SKILL.md')))) {
|
|
59
|
-
throw new Error(
|
|
47
|
+
throw new Error(
|
|
48
|
+
texts['installer.missingSkill'].replace('{path}', skillSource),
|
|
49
|
+
);
|
|
60
50
|
}
|
|
61
51
|
const staged = await stageBridgeRuntime(packageRoot, require);
|
|
62
52
|
try {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export const installerTexts = {
|
|
2
|
+
en: {
|
|
3
|
+
'installer.missingSkill': 'Skill source is missing in {path}',
|
|
4
|
+
'installer.noBackend':
|
|
5
|
+
'This package was built without a backend address. Pass --api-url.',
|
|
6
|
+
'installer.unknownCommand': 'Unknown command: {command}',
|
|
7
|
+
'installer.usage':
|
|
8
|
+
'Usage:\n engineering-memory install\n\nInstalls the skill and the local bridge, and points them at the Engineering\nMemory backend this package was published for. Nothing else is needed: you sign\nin the first time an agent uses it, and your organization and project come from\nyour account.\n\nOptions:\n --clients codex,claude which agent CLIs to install for\n --api-url <https-url> a different backend, for a self-hosted install\n --repo <repository-root> install the Git verification gate in this repository\n --hook-mode chain|verify-only|cancel\n --help\n\nChoose the hook mode through the Codex or Claude native questionnaire before\npassing a repository. The installer never opens a questionnaire or an\nauthentication web page.',
|
|
9
|
+
},
|
|
10
|
+
tr: {
|
|
11
|
+
'installer.missingSkill': 'Skill kaynağı {path} içinde yok',
|
|
12
|
+
'installer.noBackend':
|
|
13
|
+
'Bu paket bir backend adresi olmadan derlenmiş. --api-url ver.',
|
|
14
|
+
'installer.unknownCommand': 'Bilinmeyen komut: {command}',
|
|
15
|
+
'installer.usage':
|
|
16
|
+
"Kullanım:\n engineering-memory install\n\nSkill'i ve yerel bridge'i kurar, ikisini de bu paketin yayınlandığı Engineering\nMemory backend'ine bağlar. Başka bir şey gerekmez: bir ajan ilk kez kullandığında\ngiriş yaparsın; organizasyonun ve projen hesabından gelir.\n\nSeçenekler:\n --clients codex,claude hangi ajan CLI'ları için kurulacağı\n --api-url <https-url> kendi sunucunda çalışan başka bir backend\n --repo <repository-root> commit öncesi Git doğrulamasını bu depoya kurar\n --hook-mode chain|verify-only|cancel\n --help\n\nDepo vermeden önce hook modunu Codex veya Claude'un yerel soru formuyla seç.\nKurulum hiçbir zaman soru formu ya da giriş sayfası açmaz.",
|
|
17
|
+
},
|
|
18
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "engineering-memory",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.21",
|
|
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",
|
package/runtime/build.json
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import { createHash, randomBytes } from 'node:crypto';
|
|
2
2
|
import { createServer } from 'node:http';
|
|
3
|
+
import * as z from 'zod/v4';
|
|
4
|
+
import { copies, escapeHtml, textDirection } from '../runtime/texts.js';
|
|
3
5
|
import { endpoints } from '../config.js';
|
|
6
|
+
const text = z.string().min(1);
|
|
7
|
+
const signInWording = z.strictObject({ invalid: text, completed: text, failed: text });
|
|
4
8
|
export class BrowserAuthCoordinator {
|
|
5
9
|
client;
|
|
6
10
|
credentials;
|
|
11
|
+
language;
|
|
7
12
|
server = null;
|
|
8
13
|
pending = null;
|
|
9
|
-
constructor(client, credentials) {
|
|
14
|
+
constructor(client, credentials, language = async () => undefined) {
|
|
10
15
|
this.client = client;
|
|
11
16
|
this.credentials = credentials;
|
|
17
|
+
this.language = language;
|
|
12
18
|
}
|
|
13
19
|
async ensureAuthenticated(options = {}) {
|
|
14
20
|
if (await this.credentials.get('access-token')) {
|
|
@@ -100,7 +106,7 @@ export class BrowserAuthCoordinator {
|
|
|
100
106
|
!authorizationCode ||
|
|
101
107
|
Date.parse(this.pending.expiresAt) <= Date.now()) {
|
|
102
108
|
response.writeHead(400, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
103
|
-
response.end('
|
|
109
|
+
response.end(await this.page('invalid'));
|
|
104
110
|
return;
|
|
105
111
|
}
|
|
106
112
|
const envelope = await this.client.request(endpoints.authBrowserExchange, {
|
|
@@ -118,18 +124,23 @@ export class BrowserAuthCoordinator {
|
|
|
118
124
|
await this.persistTokenPair(envelope.data.accessToken, envelope.data.refreshToken);
|
|
119
125
|
this.pending = null;
|
|
120
126
|
response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
121
|
-
response.end(
|
|
127
|
+
response.end(await this.page('completed'));
|
|
122
128
|
await this.stopServer();
|
|
123
129
|
}
|
|
124
130
|
catch {
|
|
125
131
|
await this.clearCredentialsBestEffort();
|
|
126
132
|
this.pending = null;
|
|
127
133
|
response.writeHead(500, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
128
|
-
response.end(
|
|
134
|
+
response.end(await this.page('failed'));
|
|
129
135
|
await this.stopServer();
|
|
130
136
|
process.stderr.write('Browser authentication failed\n');
|
|
131
137
|
}
|
|
132
138
|
}
|
|
139
|
+
async page(outcome) {
|
|
140
|
+
const told = await this.language().catch(() => undefined);
|
|
141
|
+
const { language, copy } = copies(signInWording, 'signIn', told)[0];
|
|
142
|
+
return `<!doctype html><html lang="${escapeHtml(language)}" dir="${textDirection(language)}"><body>${escapeHtml(copy[outcome])}</body></html>`;
|
|
143
|
+
}
|
|
133
144
|
async persistTokenPair(accessToken, refreshToken) {
|
|
134
145
|
try {
|
|
135
146
|
await this.credentials.set('refresh-token', refreshToken);
|
|
@@ -30,6 +30,7 @@ export function apiStateRoot(config) {
|
|
|
30
30
|
}
|
|
31
31
|
export const endpoints = {
|
|
32
32
|
releases: '/runtime/releases',
|
|
33
|
+
runtimeTexts: '/runtime/texts',
|
|
33
34
|
projectGitPreferences: (projectId) => '/projects/' + projectId + '/git-preferences',
|
|
34
35
|
worktreePolicy: '/runtime/worktree-policy',
|
|
35
36
|
auditList: '/audit',
|
|
@@ -391,6 +391,16 @@ export class GitInspector {
|
|
|
391
391
|
const result = await this.runner.run('git', ['merge-base', '--is-ancestor', ancestor, descendant], { cwd: repoRoot });
|
|
392
392
|
return result.exitCode === 0;
|
|
393
393
|
}
|
|
394
|
+
async remoteRefContaining(repoRoot, commit) {
|
|
395
|
+
return await this.gitValue(repoRoot, [
|
|
396
|
+
'for-each-ref',
|
|
397
|
+
'--count=1',
|
|
398
|
+
'--format=%(refname)',
|
|
399
|
+
'--contains',
|
|
400
|
+
commit,
|
|
401
|
+
'refs/remotes/',
|
|
402
|
+
]);
|
|
403
|
+
}
|
|
394
404
|
async committedPaths(repoRoot, baseCommit) {
|
|
395
405
|
const result = await this.runner.run('git', ['diff', '--name-only', '-z', '--find-renames', baseCommit, 'HEAD', '--'], { cwd: repoRoot });
|
|
396
406
|
if (result.exitCode !== 0)
|