@wangzhizhi/remi 0.0.1-alpha

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 (55) hide show
  1. package/README.md +9 -0
  2. package/dist/doctor.js +108 -0
  3. package/dist/git.js +41 -0
  4. package/dist/help.js +27 -0
  5. package/dist/i18n.js +422 -0
  6. package/dist/index.js +97 -0
  7. package/dist/initPrompt.js +17 -0
  8. package/dist/model.js +116 -0
  9. package/dist/modelSelection.js +34 -0
  10. package/dist/permissionDisplay.js +46 -0
  11. package/dist/permissions.js +206 -0
  12. package/dist/repl.js +346 -0
  13. package/dist/resume.js +3 -0
  14. package/dist/setup.js +62 -0
  15. package/dist/statusline.js +59 -0
  16. package/dist/style.js +48 -0
  17. package/dist/syntaxTheme.js +39 -0
  18. package/dist/tui/RemiApp.js +1756 -0
  19. package/dist/tui/commands.js +427 -0
  20. package/dist/tui/index.js +42 -0
  21. package/dist/tui/renderers/Header.js +28 -0
  22. package/dist/tui/renderers/MessageList.js +1176 -0
  23. package/dist/tui/renderers/PromptBox.js +118 -0
  24. package/dist/tui/renderers/StatusLine.js +124 -0
  25. package/dist/tui/renderers/WorkingIndicator.js +70 -0
  26. package/dist/tui/slashCommandHighlight.js +8 -0
  27. package/dist/tui/theme.js +13 -0
  28. package/dist/tui/types.js +1 -0
  29. package/dist/usage.js +66 -0
  30. package/dist/version.js +5 -0
  31. package/node_modules/@remi/compact/dist/index.js +389 -0
  32. package/node_modules/@remi/compact/package.json +8 -0
  33. package/node_modules/@remi/config/dist/index.js +426 -0
  34. package/node_modules/@remi/config/package.json +8 -0
  35. package/node_modules/@remi/core/dist/contextBuilder.js +344 -0
  36. package/node_modules/@remi/core/dist/directoryOverview.js +359 -0
  37. package/node_modules/@remi/core/dist/index.js +2843 -0
  38. package/node_modules/@remi/core/dist/projectInstructions.js +123 -0
  39. package/node_modules/@remi/core/dist/responseStyles.js +98 -0
  40. package/node_modules/@remi/core/package.json +8 -0
  41. package/node_modules/@remi/llm/dist/index.js +804 -0
  42. package/node_modules/@remi/llm/package.json +8 -0
  43. package/node_modules/@remi/memory/dist/index.js +312 -0
  44. package/node_modules/@remi/memory/package.json +8 -0
  45. package/node_modules/@remi/permissions/dist/index.js +90 -0
  46. package/node_modules/@remi/permissions/package.json +8 -0
  47. package/node_modules/@remi/sessions/dist/index.js +370 -0
  48. package/node_modules/@remi/sessions/package.json +8 -0
  49. package/node_modules/@remi/skills/dist/index.js +273 -0
  50. package/node_modules/@remi/skills/package.json +8 -0
  51. package/node_modules/@remi/terminal-markdown/dist/index.js +1412 -0
  52. package/node_modules/@remi/terminal-markdown/package.json +8 -0
  53. package/node_modules/@remi/tools/dist/index.js +3875 -0
  54. package/node_modules/@remi/tools/package.json +8 -0
  55. package/package.json +48 -0
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@remi/llm",
3
+ "version": "0.0.1-alpha",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": "./dist/index.js"
7
+ }
8
+ }
@@ -0,0 +1,312 @@
1
+ import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { randomUUID } from 'node:crypto';
4
+ export const memoryPackageName = '@remi/memory';
5
+ export const memoryTypes = ['user', 'project', 'feedback', 'reference'];
6
+ const defaultRecallLimit = 5;
7
+ const defaultMaxBodyChars = 6_000;
8
+ export function memoryDir(cwd = process.cwd()) {
9
+ return join(cwd, '.agent', 'memory');
10
+ }
11
+ export function memoriesDir(cwd = process.cwd()) {
12
+ return join(memoryDir(cwd), 'memories');
13
+ }
14
+ export function memoryIndexPath(cwd = process.cwd()) {
15
+ return join(memoryDir(cwd), 'index.json');
16
+ }
17
+ export function memoryFilePath(cwd, id) {
18
+ return join(memoriesDir(cwd), `${assertSafeMemoryId(id)}.md`);
19
+ }
20
+ export function createMemory(cwd, input) {
21
+ const id = assertSafeMemoryId(input.id ?? randomUUID());
22
+ const now = input.now ?? new Date().toISOString();
23
+ const description = input.description.replace(/\s+/g, ' ').trim();
24
+ const body = input.body.trim();
25
+ if (description.length === 0) {
26
+ throw new Error('Memory description is required');
27
+ }
28
+ if (body.length === 0) {
29
+ throw new Error('Memory body is required');
30
+ }
31
+ if (containsSensitiveText(`${description}\n${body}`)) {
32
+ throw new Error('Memory content appears to contain sensitive data');
33
+ }
34
+ const record = {
35
+ id,
36
+ type: input.type,
37
+ scope: input.scope ?? 'project',
38
+ description,
39
+ createdAt: now,
40
+ updatedAt: now,
41
+ useCount: 0,
42
+ body,
43
+ };
44
+ ensureMemoryDirs(cwd);
45
+ writeMemoryRecord(cwd, record);
46
+ refreshMemoryIndex(cwd, now);
47
+ return record;
48
+ }
49
+ export function readMemoryIndex(cwd) {
50
+ const path = memoryIndexPath(cwd);
51
+ if (!existsSync(path)) {
52
+ return { version: 1, updatedAt: '', memories: [] };
53
+ }
54
+ try {
55
+ const parsed = JSON.parse(readFileSync(path, 'utf8'));
56
+ if (!isMemoryIndex(parsed)) {
57
+ return { version: 1, updatedAt: '', memories: [] };
58
+ }
59
+ return parsed;
60
+ }
61
+ catch {
62
+ return { version: 1, updatedAt: '', memories: [] };
63
+ }
64
+ }
65
+ export function refreshMemoryIndex(cwd, now = new Date().toISOString()) {
66
+ ensureMemoryDirs(cwd);
67
+ const index = {
68
+ version: 1,
69
+ updatedAt: now,
70
+ memories: scanMemoryHeaders(cwd).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)),
71
+ };
72
+ writeFileSync(memoryIndexPath(cwd), `${JSON.stringify(index, null, 2)}\n`);
73
+ return index;
74
+ }
75
+ export function scanMemoryHeaders(cwd) {
76
+ const dir = memoriesDir(cwd);
77
+ if (!existsSync(dir)) {
78
+ return [];
79
+ }
80
+ return readdirSync(dir, { withFileTypes: true })
81
+ .filter(entry => entry.isFile() && entry.name.endsWith('.md'))
82
+ .flatMap(entry => {
83
+ const parsed = readMemoryHeader(join(dir, entry.name));
84
+ return parsed ? [parsed] : [];
85
+ });
86
+ }
87
+ export function readMemory(cwd, id) {
88
+ const path = memoryFilePath(cwd, id);
89
+ if (!existsSync(path)) {
90
+ return undefined;
91
+ }
92
+ const parsed = parseMemoryFile(readFileSync(path, 'utf8'));
93
+ if (!parsed || containsSensitiveText(`${parsed.description}\n${parsed.body}`)) {
94
+ return undefined;
95
+ }
96
+ return parsed;
97
+ }
98
+ export function recallRelevantMemories(cwd, query, options = {}) {
99
+ const limit = normalizeLimit(options.limit, defaultRecallLimit);
100
+ const maxBodyChars = normalizeLimit(options.maxBodyChars, defaultMaxBodyChars);
101
+ const index = refreshMemoryIndex(cwd);
102
+ if (limit === 0 || maxBodyChars === 0 || query.trim().length === 0) {
103
+ return emptyMemoryRecall(query, limit, index.memories.length);
104
+ }
105
+ const scored = index.memories
106
+ .map(memory => ({ memory, score: scoreMemory(query, memory) }))
107
+ .filter(item => item.score > 0)
108
+ .sort((left, right) => {
109
+ if (right.score !== left.score) {
110
+ return right.score - left.score;
111
+ }
112
+ return right.memory.updatedAt.localeCompare(left.memory.updatedAt);
113
+ });
114
+ const memories = [];
115
+ let remainingChars = maxBodyChars;
116
+ for (const item of scored) {
117
+ if (memories.length >= limit || remainingChars <= 0) {
118
+ break;
119
+ }
120
+ const record = readMemory(cwd, item.memory.id);
121
+ if (!record) {
122
+ continue;
123
+ }
124
+ const body = truncateMemoryBody(record.body, remainingChars);
125
+ remainingChars -= body.length;
126
+ memories.push({
127
+ ...record,
128
+ body,
129
+ reason: `matched ${item.score} query token${item.score === 1 ? '' : 's'} in memory description`,
130
+ });
131
+ }
132
+ return {
133
+ memories,
134
+ trace: {
135
+ query,
136
+ limit,
137
+ manifestCount: index.memories.length,
138
+ selected: memories.map(memory => ({
139
+ id: memory.id,
140
+ type: memory.type,
141
+ description: memory.description,
142
+ reason: memory.reason,
143
+ })),
144
+ },
145
+ };
146
+ }
147
+ export function emptyMemoryRecall(query, limit = defaultRecallLimit, manifestCount = 0) {
148
+ return {
149
+ memories: [],
150
+ trace: {
151
+ query,
152
+ limit,
153
+ manifestCount,
154
+ selected: [],
155
+ },
156
+ };
157
+ }
158
+ export function recordMemoryUse(cwd, ids, now = new Date().toISOString()) {
159
+ const uniqueIds = [...new Set(ids.map(id => assertSafeMemoryId(id)))];
160
+ for (const id of uniqueIds) {
161
+ const record = readMemory(cwd, id);
162
+ if (!record) {
163
+ continue;
164
+ }
165
+ writeMemoryRecord(cwd, {
166
+ ...record,
167
+ lastUsedAt: now,
168
+ useCount: record.useCount + 1,
169
+ });
170
+ }
171
+ if (uniqueIds.length > 0) {
172
+ refreshMemoryIndex(cwd, now);
173
+ }
174
+ }
175
+ function ensureMemoryDirs(cwd) {
176
+ mkdirSync(memoriesDir(cwd), { recursive: true });
177
+ }
178
+ function readMemoryHeader(path) {
179
+ const parsed = parseMemoryFile(readFileSync(path, 'utf8'));
180
+ if (!parsed) {
181
+ return undefined;
182
+ }
183
+ const { body: _body, ...metadata } = parsed;
184
+ return metadata;
185
+ }
186
+ function parseMemoryFile(content) {
187
+ const lines = content.split(/\r?\n/);
188
+ if (lines[0]?.trim() !== '---') {
189
+ return undefined;
190
+ }
191
+ const endIndex = lines.findIndex((line, index) => index > 0 && line.trim() === '---');
192
+ if (endIndex <= 0) {
193
+ return undefined;
194
+ }
195
+ const metadata = parseFrontmatter(lines.slice(1, endIndex));
196
+ if (!metadata) {
197
+ return undefined;
198
+ }
199
+ const body = lines.slice(endIndex + 1).join('\n').trim();
200
+ return { ...metadata, body };
201
+ }
202
+ function parseFrontmatter(lines) {
203
+ const fields = new Map();
204
+ for (const line of lines) {
205
+ const separatorIndex = line.indexOf(':');
206
+ if (separatorIndex <= 0) {
207
+ continue;
208
+ }
209
+ const key = line.slice(0, separatorIndex).trim();
210
+ const value = line.slice(separatorIndex + 1).trim();
211
+ fields.set(key, value);
212
+ }
213
+ const id = fields.get('id');
214
+ const type = fields.get('type');
215
+ const scope = fields.get('scope') || 'project';
216
+ const description = fields.get('description');
217
+ const createdAt = fields.get('createdAt');
218
+ const updatedAt = fields.get('updatedAt');
219
+ const useCount = Number.parseInt(fields.get('useCount') ?? '0', 10);
220
+ const lastUsedAt = fields.get('lastUsedAt');
221
+ if (!id ||
222
+ !isMemoryType(type) ||
223
+ !isMemoryScope(scope) ||
224
+ !description ||
225
+ !createdAt ||
226
+ !updatedAt ||
227
+ !Number.isFinite(useCount)) {
228
+ return undefined;
229
+ }
230
+ const entry = {
231
+ id: assertSafeMemoryId(id),
232
+ type,
233
+ scope,
234
+ description,
235
+ createdAt,
236
+ updatedAt,
237
+ useCount,
238
+ };
239
+ return lastUsedAt ? { ...entry, lastUsedAt } : entry;
240
+ }
241
+ function writeMemoryRecord(cwd, record) {
242
+ const lines = [
243
+ '---',
244
+ `id: ${record.id}`,
245
+ `type: ${record.type}`,
246
+ `scope: ${record.scope}`,
247
+ `description: ${record.description}`,
248
+ `createdAt: ${record.createdAt}`,
249
+ `updatedAt: ${record.updatedAt}`,
250
+ `lastUsedAt: ${record.lastUsedAt ?? ''}`,
251
+ `useCount: ${record.useCount}`,
252
+ '---',
253
+ '',
254
+ record.body.trim(),
255
+ '',
256
+ ];
257
+ writeFileSync(memoryFilePath(cwd, record.id), lines.join('\n'));
258
+ }
259
+ function isMemoryIndex(value) {
260
+ if (!value || typeof value !== 'object') {
261
+ return false;
262
+ }
263
+ const index = value;
264
+ return index.version === 1 && typeof index.updatedAt === 'string' && Array.isArray(index.memories);
265
+ }
266
+ function isMemoryType(value) {
267
+ return typeof value === 'string' && memoryTypes.includes(value);
268
+ }
269
+ function isMemoryScope(value) {
270
+ return value === 'project' || value === 'user';
271
+ }
272
+ function scoreMemory(query, memory) {
273
+ const queryTokens = new Set(tokenize(query));
274
+ if (queryTokens.size === 0) {
275
+ return 0;
276
+ }
277
+ const memoryTokens = new Set(tokenize(`${memory.type} ${memory.description} ${memory.id}`));
278
+ let score = 0;
279
+ for (const token of queryTokens) {
280
+ if (memoryTokens.has(token)) {
281
+ score += 1;
282
+ }
283
+ }
284
+ return score;
285
+ }
286
+ function tokenize(text) {
287
+ return [...text.toLowerCase().matchAll(/[\p{L}\p{N}_-]+/gu)].map(match => match[0] ?? '').filter(Boolean);
288
+ }
289
+ function normalizeLimit(value, fallback) {
290
+ if (value === undefined || !Number.isFinite(value)) {
291
+ return fallback;
292
+ }
293
+ return Math.max(0, Math.floor(value));
294
+ }
295
+ function truncateMemoryBody(body, maxChars) {
296
+ if (body.length <= maxChars) {
297
+ return body;
298
+ }
299
+ if (maxChars <= 48) {
300
+ return body.slice(0, maxChars);
301
+ }
302
+ return `${body.slice(0, maxChars - 31)}\n[Memory body truncated.]`;
303
+ }
304
+ function containsSensitiveText(text) {
305
+ return /(api[_-]?key|access[_-]?token|private[_-]?key|BEGIN [A-Z ]*PRIVATE KEY|sk-[A-Za-z0-9]{12,})/i.test(text);
306
+ }
307
+ function assertSafeMemoryId(id) {
308
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(id)) {
309
+ throw new Error(`Invalid memory id: ${id}`);
310
+ }
311
+ return id;
312
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@remi/memory",
3
+ "version": "0.0.1-alpha",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": "./dist/index.js"
7
+ }
8
+ }
@@ -0,0 +1,90 @@
1
+ export const permissionsPackageName = '@remi/permissions';
2
+ export function evaluateToolPermission(request) {
3
+ if (request.policy.requirements.length === 0) {
4
+ return {
5
+ status: 'deny',
6
+ reason: `Tool ${request.toolName} declares no permission requirements.`,
7
+ requirements: [],
8
+ };
9
+ }
10
+ if (request.policy.mode === 'deny') {
11
+ return {
12
+ status: 'deny',
13
+ reason: request.policy.reason,
14
+ requirements: request.policy.requirements,
15
+ };
16
+ }
17
+ if (request.policy.mode === 'bypass-required') {
18
+ return {
19
+ status: 'deny',
20
+ reason: `Tool ${request.toolName} requires explicit bypass mode.`,
21
+ requirements: request.policy.requirements,
22
+ };
23
+ }
24
+ if (request.policy.mode === 'allow') {
25
+ return {
26
+ status: 'allow',
27
+ reason: request.policy.reason,
28
+ requirements: request.policy.requirements,
29
+ };
30
+ }
31
+ return {
32
+ status: 'ask',
33
+ reason: request.policy.reason,
34
+ requirements: request.policy.requirements,
35
+ };
36
+ }
37
+ export function summarizeToolInput(input, maxLength = 240) {
38
+ const stable = JSON.stringify(sortRecord(input));
39
+ if (stable.length <= maxLength) {
40
+ return stable;
41
+ }
42
+ return `${stable.slice(0, Math.max(0, maxLength - 3))}...`;
43
+ }
44
+ export function autoReviewToolPermission(request) {
45
+ if (request.toolName !== 'execute_shell' || !request.command) {
46
+ return undefined;
47
+ }
48
+ if (request.command.segments.length === 0) {
49
+ return undefined;
50
+ }
51
+ const reviewable = request.command.segments.every(isAutoReviewAllowedShellSegment);
52
+ if (!reviewable) {
53
+ return undefined;
54
+ }
55
+ return {
56
+ status: 'allow',
57
+ reason: 'Auto-review approved a bounded local validation command inside the current sandbox.',
58
+ requirements: request.policy.requirements,
59
+ };
60
+ }
61
+ function isAutoReviewAllowedShellSegment(segment) {
62
+ const [firstArg, secondArg] = segment.args;
63
+ if (segment.command === 'pwd') {
64
+ return segment.args.length === 0;
65
+ }
66
+ if (segment.command === 'go') {
67
+ return firstArg === 'test' || firstArg === 'vet';
68
+ }
69
+ if (segment.command === 'pnpm' || segment.command === 'npm' || segment.command === 'yarn') {
70
+ if (firstArg === 'test' || firstArg === 'typecheck' || firstArg === 'build' || firstArg === 'lint') {
71
+ return true;
72
+ }
73
+ return firstArg === 'run' && ['test', 'typecheck', 'build', 'lint'].includes(secondArg ?? '');
74
+ }
75
+ if (segment.command === 'git') {
76
+ return ['status', 'diff', 'log'].includes(firstArg ?? '');
77
+ }
78
+ return false;
79
+ }
80
+ function sortRecord(value) {
81
+ if (Array.isArray(value)) {
82
+ return value.map(item => sortRecord(item));
83
+ }
84
+ if (!value || typeof value !== 'object') {
85
+ return value;
86
+ }
87
+ return Object.fromEntries(Object.entries(value)
88
+ .sort(([left], [right]) => left.localeCompare(right))
89
+ .map(([key, item]) => [key, sortRecord(item)]));
90
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "@remi/permissions",
3
+ "version": "0.0.1-alpha",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": "./dist/index.js"
7
+ }
8
+ }