@ghl-ai/aw 0.1.37-beta.34 → 0.1.37-beta.36

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/commands/init.mjs CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  import {
8
8
  existsSync,
9
+ mkdirSync,
9
10
  writeFileSync,
10
11
  symlinkSync,
11
12
  lstatSync,
@@ -246,6 +247,7 @@ export async function initCommand(args) {
246
247
  }
247
248
 
248
249
  ensureAwGitignore(AW_HOME);
250
+ mkdirSync(join(GLOBAL_AW_DIR, 'memory'), { recursive: true });
249
251
  const freshCfg = config.load(GLOBAL_AW_DIR);
250
252
  if (existsSync(GLOBAL_AW_DIR)) {
251
253
  syncFileTree(join(AW_HOME, RULES_SOURCE_DIR), join(GLOBAL_AW_DIR, RULES_SOURCE_DIR));
@@ -351,6 +353,7 @@ export async function initCommand(args) {
351
353
  try {
352
354
  await initPersistentClone(repoUrl, AW_HOME, sparsePaths);
353
355
  ensureAwGitignore(AW_HOME);
356
+ mkdirSync(join(GLOBAL_AW_DIR, 'memory'), { recursive: true });
354
357
  s.stop('Registry cloned');
355
358
  } catch (e) {
356
359
  s.stop(chalk.red('Clone failed'));
@@ -1,17 +1,24 @@
1
- // commands/memory.mjs — `aw memory [store|search|pack|stats]`
1
+ // commands/memory.mjs — `aw memory [store|search|pack|stats|validate|invalidate|sync]`
2
2
 
3
+ import { join } from 'node:path';
4
+ import { homedir } from 'node:os';
3
5
  import * as fmt from '../fmt.mjs';
4
6
  import { chalk } from '../fmt.mjs';
5
7
  import { callMemoryTool } from '../memory-bridge.mjs';
8
+ import { syncMemories } from '../memory-sync.mjs';
9
+ import { REGISTRY_DIR } from '../constants.mjs';
6
10
 
7
11
  export async function memoryCommand(args) {
8
12
  const sub = args._positional?.[0];
9
13
  switch (sub) {
10
- case 'store': return memoryStore(args);
11
- case 'search': return memorySearch(args);
12
- case 'pack': return memoryPack(args);
13
- case 'stats': return memoryStats(args);
14
- default: return memoryHelp();
14
+ case 'store': return memoryStore(args);
15
+ case 'search': return memorySearch(args);
16
+ case 'pack': return memoryPack(args);
17
+ case 'stats': return memoryStats(args);
18
+ case 'validate': return memoryValidate(args);
19
+ case 'invalidate': return memoryInvalidate(args);
20
+ case 'sync': return memorySync(args);
21
+ default: return memoryHelp();
15
22
  }
16
23
  }
17
24
 
@@ -132,8 +139,8 @@ async function memoryPack(args) {
132
139
 
133
140
  const params = { query };
134
141
  if (args['--namespace']) params.namespace = args['--namespace'];
135
- if (args['--budget']) params.budget = parseInt(args['--budget'], 10);
136
- else params.budget = 3500;
142
+ if (args['--budget']) params.token_budget = parseInt(args['--budget'], 10);
143
+ else params.token_budget = 3500;
137
144
  if (args['--layer']) params.layer = args['--layer'];
138
145
  if (args['--overlay']) params.overlay = args['--overlay'];
139
146
  if (args['--angle']) params.angle = args['--angle'];
@@ -223,6 +230,121 @@ async function memoryStats(args) {
223
230
  }
224
231
  }
225
232
 
233
+ // ── validate ──────────────────────────────────────────────────────────
234
+
235
+ async function memoryValidate(args) {
236
+ const memoryId = args._positional?.[1];
237
+ if (!memoryId) {
238
+ fmt.cancel('Usage: aw memory validate <memory-id> [--reason "text"]');
239
+ return;
240
+ }
241
+
242
+ fmt.intro('aw memory validate');
243
+
244
+ const params = {
245
+ memory_id: memoryId,
246
+ feedback_type: 'validate',
247
+ actor_type: 'user',
248
+ };
249
+ if (args['--reason']) params.reason = args['--reason'];
250
+
251
+ const s = fmt.spinner();
252
+ s.start('Validating memory...');
253
+ try {
254
+ const result = await callMemoryTool('memory_feedback', params);
255
+ s.stop('Memory validated');
256
+
257
+ const data = result?.result ?? result;
258
+ const lines = [
259
+ `${chalk.dim('id:')} ${memoryId}`,
260
+ `${chalk.dim('action:')} ${chalk.green('validated')}`,
261
+ data.reason ? `${chalk.dim('reason:')} ${data.reason}` : null,
262
+ ].filter(Boolean).join('\n');
263
+
264
+ fmt.note(lines, 'Result');
265
+ fmt.outro('Memory validated');
266
+ } catch (err) {
267
+ s.stop(chalk.red('Failed'));
268
+ fmt.cancel(`Validate failed: ${err.message}`);
269
+ }
270
+ }
271
+
272
+ // ── invalidate ────────────────────────────────────────────────────────
273
+
274
+ async function memoryInvalidate(args) {
275
+ const memoryId = args._positional?.[1];
276
+ if (!memoryId) {
277
+ fmt.cancel('Usage: aw memory invalidate <memory-id> [--reason "text"]');
278
+ return;
279
+ }
280
+
281
+ fmt.intro('aw memory invalidate');
282
+
283
+ const params = {
284
+ memory_id: memoryId,
285
+ feedback_type: 'invalidate',
286
+ actor_type: 'user',
287
+ };
288
+ if (args['--reason']) params.reason = args['--reason'];
289
+
290
+ const s = fmt.spinner();
291
+ s.start('Invalidating memory...');
292
+ try {
293
+ const result = await callMemoryTool('memory_feedback', params);
294
+ s.stop('Memory invalidated');
295
+
296
+ const data = result?.result ?? result;
297
+ const lines = [
298
+ `${chalk.dim('id:')} ${memoryId}`,
299
+ `${chalk.dim('action:')} ${chalk.red('invalidated')}`,
300
+ data.reason ? `${chalk.dim('reason:')} ${data.reason}` : null,
301
+ ].filter(Boolean).join('\n');
302
+
303
+ fmt.note(lines, 'Result');
304
+ fmt.outro('Memory invalidated');
305
+ } catch (err) {
306
+ s.stop(chalk.red('Failed'));
307
+ fmt.cancel(`Invalidate failed: ${err.message}`);
308
+ }
309
+ }
310
+
311
+ // ── sync ──────────────────────────────────────────────────────────────
312
+
313
+ async function memorySync(args) {
314
+ fmt.intro('aw memory sync');
315
+
316
+ const registryDir = join(homedir(), REGISTRY_DIR, 'memory');
317
+ const force = args['--force'] === true;
318
+
319
+ const s = fmt.spinner();
320
+ s.start('Syncing memories...');
321
+ try {
322
+ const result = await syncMemories(registryDir, { force });
323
+ if (result.skipped) {
324
+ s.stop('Cache is fresh');
325
+ fmt.logInfo('Local memory cache is less than 24h old. Use --force to re-sync.');
326
+ fmt.outro('Sync skipped');
327
+ return;
328
+ }
329
+
330
+ s.stop('Memories synced');
331
+
332
+ const fileLines = Object.entries(result.files)
333
+ .map(([file, count]) => ` ${chalk.cyan(file.padEnd(20))} ${count} memories`)
334
+ .join('\n');
335
+
336
+ if (fileLines) {
337
+ fmt.note(fileLines, 'Files written');
338
+ }
339
+
340
+ fmt.logStep(`Total: ${chalk.bold(result.total)} memories synced to ${chalk.dim(registryDir)}`);
341
+ fmt.outro('Sync complete');
342
+ } catch (err) {
343
+ s.stop(chalk.red('Failed'));
344
+ fmt.cancel(`Sync failed: ${err.message}`);
345
+ }
346
+ }
347
+
226
348
  // ── help ─────────────────────────────────────────────────────────────
227
349
 
228
350
  function memoryHelp() {
@@ -257,6 +379,15 @@ function memoryHelp() {
257
379
  cmd('aw memory stats', 'Show memory statistics'),
258
380
  cmd(' --namespace <ns>', 'Namespace scope'),
259
381
  '',
382
+ cmd('aw memory validate <memory-id>', 'Mark a memory as validated'),
383
+ cmd(' --reason "text"', 'Reason for validation'),
384
+ '',
385
+ cmd('aw memory invalidate <memory-id>', 'Mark a memory as invalidated'),
386
+ cmd(' --reason "text"', 'Reason for invalidation'),
387
+ '',
388
+ cmd('aw memory sync', 'Sync memories to local files'),
389
+ cmd(' --force', 'Force re-sync even if cache is fresh'),
390
+ '',
260
391
  ].join('\n');
261
392
 
262
393
  console.log(help);
package/ecc.mjs CHANGED
@@ -9,7 +9,7 @@ import * as fmt from "./fmt.mjs";
9
9
 
10
10
  const AW_ECC_REPO_SSH = "git@github.com:shreyansh-ghl/aw-ecc.git";
11
11
  const AW_ECC_REPO_HTTPS = "https://github.com/shreyansh-ghl/aw-ecc.git";
12
- const AW_ECC_TAG = "v1.2.9";
12
+ const AW_ECC_TAG = "v1.4.0";
13
13
 
14
14
  const MARKETPLACE_NAME = "aw-marketplace";
15
15
  const PLUGIN_KEY = `aw@${MARKETPLACE_NAME}`;
package/memory-bridge.mjs CHANGED
@@ -1,9 +1,60 @@
1
1
  // memory-bridge.mjs — Bridge between CLI and MCP memory backend (JSON-RPC)
2
2
 
3
- import { MCP_BASE_URL } from './constants.mjs';
3
+ import { execSync } from 'node:child_process';
4
+ import { join } from 'node:path';
5
+ import { MCP_BASE_URL, AW_HOME, REGISTRY_DIR } from './constants.mjs';
6
+ import * as config from './config.mjs';
4
7
 
5
8
  let _rpcId = 1;
6
9
 
10
+ /** Cached headers — resolved once per process. */
11
+ let _cachedHeaders = null;
12
+
13
+ /**
14
+ * Resolve auth + namespace headers for MCP requests.
15
+ * - X-Namespace: from .sync-config.json namespace (team slug → server resolves to team_id)
16
+ * - Authorization: GitHub PAT from GITHUB_TOKEN env or `gh auth token`
17
+ */
18
+ function resolveHeaders() {
19
+ if (_cachedHeaders) return _cachedHeaders;
20
+
21
+ const headers = {
22
+ 'Content-Type': 'application/json',
23
+ 'Accept': 'application/json, text/event-stream',
24
+ };
25
+
26
+ // Resolve namespace from .sync-config.json → X-Namespace header
27
+ const registryDir = join(AW_HOME, REGISTRY_DIR);
28
+ const cfg = config.load(registryDir);
29
+ if (cfg?.namespace) {
30
+ headers['X-Namespace'] = cfg.namespace;
31
+ }
32
+
33
+ // Resolve GitHub token for auth
34
+ const token = process.env.GITHUB_TOKEN || resolveGhToken();
35
+ if (token) {
36
+ headers['Authorization'] = `Bearer ${token}`;
37
+ }
38
+
39
+ _cachedHeaders = headers;
40
+ return headers;
41
+ }
42
+
43
+ /** Try `gh auth token` silently — returns null on failure. */
44
+ function resolveGhToken() {
45
+ try {
46
+ const token = execSync('gh auth token', {
47
+ encoding: 'utf8',
48
+ stdio: ['pipe', 'pipe', 'pipe'],
49
+ timeout: 3000,
50
+ }).trim();
51
+ if (token && (token.startsWith('ghp_') || token.startsWith('gho_') || token.startsWith('github_pat_'))) {
52
+ return token;
53
+ }
54
+ } catch { /* gh not available or not authenticated */ }
55
+ return null;
56
+ }
57
+
7
58
  /**
8
59
  * Call an MCP memory tool by name via JSON-RPC 2.0 (Streamable HTTP).
9
60
  * @param {string} toolName — MCP tool name (e.g. 'memory_curated_store', 'memory_search')
@@ -13,10 +64,7 @@ let _rpcId = 1;
13
64
  export async function callMemoryTool(toolName, params) {
14
65
  const response = await fetch(MCP_BASE_URL, {
15
66
  method: 'POST',
16
- headers: {
17
- 'Content-Type': 'application/json',
18
- 'Accept': 'application/json, text/event-stream',
19
- },
67
+ headers: resolveHeaders(),
20
68
  body: JSON.stringify({
21
69
  jsonrpc: '2.0',
22
70
  id: _rpcId++,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghl-ai/aw",
3
- "version": "0.1.37-beta.34",
3
+ "version": "0.1.37-beta.36",
4
4
  "description": "Agentic Workspace CLI — pull, push & manage agents, skills and commands from the registry",
5
5
  "type": "module",
6
6
  "bin": "bin.js",