@superpms/memory-cli 0.1.1

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/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # @superpms/memory-cli
2
+
3
+ Memory system CLI — manage global layer, will sync, vendor distribution, and Cloud Brain integration.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g @superpms/memory-cli
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```bash
14
+ # Initialize global layer
15
+ memory init --global
16
+
17
+ # Check status
18
+ memory status
19
+
20
+ # Sync will
21
+ memory will push # project → global
22
+ memory will pull # global → project
23
+
24
+ # Update vendor
25
+ memory update # global → current project
26
+ memory update --cloud # Cloud Brain → global → project
27
+ memory update --cloud --all # → all registered projects
28
+
29
+ # Cloud Brain
30
+ memory login
31
+ memory vendor publish --version=1.0.0
32
+ memory vendor download
33
+ ```
34
+
35
+ ## Commands
36
+
37
+ | Command | Description |
38
+ |---------|-------------|
39
+ | `init --global` | Initialize global memory layer |
40
+ | `login` | Authenticate with Cloud Brain |
41
+ | `status` | Show current project memory status |
42
+ | `will <push\|pull\|sync\|broadcast>` | Will synchronization |
43
+ | `will <cloud-push\|cloud-pull>` | Cloud will sync |
44
+ | `update [--cloud] [--all]` | Vendor distribution |
45
+ | `vendor <publish\|latest\|changelog\|download>` | Cloud vendor management |
46
+ | `skill <list\|promote\|demote>` | Skill layer management |
47
+ | `cache <status\|clean>` | Cache management |
48
+ | `projects` | List registered projects |
49
+ | `version` | Show version info |
50
+
51
+ ## Requirements
52
+
53
+ - Node.js >= 18
54
+ - No external dependencies
package/bin/memory.mjs ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from 'node:util';
3
+ import { resolve, dirname } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const __dirname = dirname(fileURLToPath(import.meta.url));
7
+
8
+ const COMMANDS = {
9
+ init: () => import('../commands/init.mjs'),
10
+ login: () => import('../commands/login.mjs'),
11
+ update: () => import('../commands/update.mjs'),
12
+ will: () => import('../commands/will.mjs'),
13
+ vendor: () => import('../commands/vendor.mjs'),
14
+ skill: () => import('../commands/skill.mjs'),
15
+ cache: () => import('../commands/cache.mjs'),
16
+ status: () => import('../commands/status.mjs'),
17
+ version: () => import('../commands/version.mjs'),
18
+ };
19
+
20
+ const command = process.argv[2];
21
+ const args = process.argv.slice(3);
22
+
23
+ if (!command || command === '--help' || command === '-h') {
24
+ console.log(`memory — CLI for memory system
25
+
26
+ Commands:
27
+ init Initialize memory project (downloads vendor + will from Cloud)
28
+ login Login to Cloud Brain
29
+ login register Register a new account
30
+ login status Check login status
31
+ login logout Remove saved credentials
32
+ update Update vendor from Cloud Brain
33
+ will publish Publish will to Cloud Brain
34
+ will pull Pull will from Cloud Brain
35
+ will status Show will status
36
+ will versions List cloud will versions
37
+ will visibility <v> Set will visibility (private/public/unlisted)
38
+ will subscribe <id> Subscribe to a will
39
+ will unsubscribe <id> Unsubscribe from a will
40
+ will subscriptions List my subscriptions
41
+ will subscribers List who subscribes to my will
42
+ will sub-pull <id> Pull a subscribed will
43
+ vendor publish Publish vendor to Cloud Brain (admin only)
44
+ vendor latest Check latest cloud vendor version
45
+ vendor changelog Show vendor changelog
46
+ vendor download Download specific vendor version
47
+ skill list List all skills
48
+ skill uninstall <name> Remove skill from project
49
+ cache status Show cache size
50
+ cache clean Remove old cache entries
51
+ status Current project status
52
+ version Show CLI version
53
+
54
+ Options:
55
+ --help, -h Show this help message
56
+ `);
57
+ process.exit(0);
58
+ }
59
+
60
+ if (!COMMANDS[command]) {
61
+ console.error(`Unknown command: ${command}`);
62
+ console.error('Run "memory --help" for available commands.');
63
+ process.exit(1);
64
+ }
65
+
66
+ try {
67
+ const mod = await COMMANDS[command]();
68
+ await mod.default(args);
69
+ } catch (err) {
70
+ console.error(`Error: ${err.message}`);
71
+ process.exit(1);
72
+ }
@@ -0,0 +1,99 @@
1
+ import { existsSync, readdirSync, statSync, rmSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { findProjectRoot } from '../lib/project-store.mjs';
4
+
5
+ const CACHE_DIR = '.memory/.project/cache';
6
+
7
+ export default async function cache(args) {
8
+ const subcommand = args[0];
9
+
10
+ if (!subcommand || subcommand === '--help') {
11
+ console.log(`memory cache — Manage project cache
12
+
13
+ Subcommands:
14
+ status Show cache size and contents
15
+ clean [--older-than=30d] Remove old cache entries
16
+ `);
17
+ return;
18
+ }
19
+
20
+ const handlers = { status: handleStatus, clean: handleClean };
21
+ const handler = handlers[subcommand];
22
+ if (!handler) {
23
+ console.error('Unknown cache subcommand: ' + subcommand);
24
+ process.exit(1);
25
+ }
26
+ await handler(args.slice(1));
27
+ }
28
+
29
+ function getCacheDir() {
30
+ const root = findProjectRoot();
31
+ if (!root) { console.error('Not in a memory project.'); process.exit(1); }
32
+ return join(root, CACHE_DIR);
33
+ }
34
+
35
+ function dirSize(dir) {
36
+ let total = 0;
37
+ let count = 0;
38
+ if (!existsSync(dir)) return { total, count };
39
+ const walk = (d) => {
40
+ for (const entry of readdirSync(d, { withFileTypes: true })) {
41
+ const full = join(d, entry.name);
42
+ if (entry.isDirectory()) walk(full);
43
+ else { total += statSync(full).size; count++; }
44
+ }
45
+ };
46
+ walk(dir);
47
+ return { total, count };
48
+ }
49
+
50
+ function formatSize(bytes) {
51
+ if (bytes < 1024) return bytes + ' B';
52
+ if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
53
+ return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
54
+ }
55
+
56
+ async function handleStatus() {
57
+ const cacheDir = getCacheDir();
58
+ if (!existsSync(cacheDir)) { console.log('Cache directory does not exist.'); return; }
59
+
60
+ const subdirs = readdirSync(cacheDir, { withFileTypes: true }).filter((e) => e.isDirectory());
61
+ let totalSize = 0;
62
+ let totalFiles = 0;
63
+
64
+ console.log('=== Cache Status ===\n');
65
+ for (const sub of subdirs) {
66
+ const { total, count } = dirSize(join(cacheDir, sub.name));
67
+ totalSize += total;
68
+ totalFiles += count;
69
+ console.log(' ' + sub.name + ': ' + count + ' files, ' + formatSize(total));
70
+ }
71
+ console.log('\n Total: ' + totalFiles + ' files, ' + formatSize(totalSize));
72
+ }
73
+
74
+ async function handleClean(args) {
75
+ const olderThanFlag = args.find((a) => a.startsWith('--older-than='))?.split('=')[1];
76
+ const days = olderThanFlag ? parseInt(olderThanFlag) : 30;
77
+ const cutoff = Date.now() - days * 24 * 60 * 60 * 1000;
78
+
79
+ const cacheDir = getCacheDir();
80
+ if (!existsSync(cacheDir)) { console.log('Cache directory does not exist.'); return; }
81
+
82
+ const sessionsDir = join(cacheDir, 'workflow-sessions');
83
+ const evidenceDir = join(cacheDir, 'skill-evidence');
84
+
85
+ let removed = 0;
86
+ for (const dir of [sessionsDir, evidenceDir]) {
87
+ if (!existsSync(dir)) continue;
88
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
89
+ const full = join(dir, entry.name);
90
+ const stat = statSync(full);
91
+ if (stat.mtimeMs < cutoff) {
92
+ rmSync(full, { recursive: true, force: true });
93
+ removed++;
94
+ }
95
+ }
96
+ }
97
+
98
+ console.log('Cleaned ' + removed + ' entries older than ' + days + ' days.');
99
+ }
@@ -0,0 +1,152 @@
1
+ import { existsSync, cpSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'node:fs';
2
+ import { join, resolve, basename, dirname } from 'node:path';
3
+ import { isConfigured } from '../lib/api-client.mjs';
4
+ import { findProjectRoot, getProjectWillCurrent, getProjectVendor, readProjectName } from '../lib/project-store.mjs';
5
+
6
+ export default async function init(args) {
7
+ await initProject();
8
+ }
9
+
10
+ async function initProject() {
11
+ if (!isConfigured()) {
12
+ console.error('Not logged in. Run "memory login" first.');
13
+ process.exit(1);
14
+ }
15
+
16
+ const projectRoot = findProjectRoot();
17
+ if (projectRoot) {
18
+ console.log('Memory project already exists at: ' + projectRoot);
19
+ console.log('Use "memory status" to check current state.');
20
+ return;
21
+ }
22
+
23
+ const root = process.cwd();
24
+ console.log('Initializing memory project in: ' + root);
25
+
26
+ // 1. Download vendor from Cloud Brain
27
+ console.log('Downloading vendor from Cloud Brain...');
28
+ const { cloudVendorDownloadLatest } = await import('../lib/api-client.mjs');
29
+ const { writeVendorFiles } = await import('./vendor.mjs');
30
+
31
+ let vendorDir = join(root, 'memory', 'vendor');
32
+ try {
33
+ const result = await cloudVendorDownloadLatest();
34
+ mkdirSync(vendorDir, { recursive: true });
35
+ writeVendorFiles(vendorDir, result.files, result.version);
36
+ console.log(' Vendor v' + result.version + ' (' + Object.keys(result.files).length + ' files)');
37
+ } catch (err) {
38
+ if (err.status === 404) {
39
+ console.log(' No vendor releases on Cloud Brain. Creating empty vendor.');
40
+ mkdirSync(vendorDir, { recursive: true });
41
+ } else {
42
+ console.error('Failed to download vendor: ' + err.message);
43
+ process.exit(1);
44
+ }
45
+ }
46
+
47
+ // 2. Bootstrap project from templates
48
+ const templatesDir = join(vendorDir, 'install', 'templates');
49
+ if (existsSync(templatesDir)) {
50
+ bootstrapFromTemplates(root, templatesDir);
51
+ } else {
52
+ console.log(' No templates in vendor, creating minimal structure.');
53
+ bootstrapMinimal(root);
54
+ }
55
+
56
+ // 3. Pull will from Cloud Brain
57
+ console.log('Pulling will from Cloud Brain...');
58
+ const { cloudWillPull } = await import('../lib/api-client.mjs');
59
+ const willDir = getProjectWillCurrent(root);
60
+ try {
61
+ const result = await cloudWillPull();
62
+ mkdirSync(willDir, { recursive: true });
63
+ for (const [name, content] of Object.entries(result.files)) {
64
+ writeFileSync(join(willDir, name), content, 'utf8');
65
+ }
66
+ console.log(' Will v' + result.version + ' (' + Object.keys(result.files).length + ' files)');
67
+ } catch (err) {
68
+ console.log(' No will on Cloud Brain yet. Will directory created empty.');
69
+ mkdirSync(willDir, { recursive: true });
70
+ }
71
+
72
+ console.log('');
73
+ console.log('Project initialized. Files created:');
74
+ console.log(' memory/start.md — agent entry point');
75
+ console.log(' memory/vendor/ — system specs (read-only)');
76
+ console.log(' memory/will/current/ — your will');
77
+ console.log(' .memory/ — project content');
78
+ }
79
+
80
+ function readTemplate(templatesDir, relativePath) {
81
+ const filePath = join(templatesDir, relativePath);
82
+ if (!existsSync(filePath)) return null;
83
+ const today = new Date().toISOString().slice(0, 10);
84
+ return readFileSync(filePath, 'utf8').replace(/\{\{today\}\}/g, today);
85
+ }
86
+
87
+ function writeIfMissing(filePath, content) {
88
+ if (!content || existsSync(filePath)) return false;
89
+ mkdirSync(dirname(filePath), { recursive: true });
90
+ writeFileSync(filePath, content, 'utf8');
91
+ return true;
92
+ }
93
+
94
+ function bootstrapFromTemplates(root, templatesDir) {
95
+ const MEMORY = join(root, 'memory');
96
+ const DOT_MEMORY = join(root, '.memory');
97
+ const DOT_PROJECT = join(DOT_MEMORY, '.project');
98
+
99
+ // Skeleton directories
100
+ for (const sub of ['config', 'cache', 'global', 'skills']) {
101
+ mkdirSync(join(DOT_PROJECT, sub), { recursive: true });
102
+ }
103
+
104
+ // memory/start.md, memory/setup.md
105
+ writeIfMissing(join(MEMORY, 'start.md'), readTemplate(templatesDir, 'start.md'));
106
+ writeIfMissing(join(MEMORY, 'setup.md'), readTemplate(templatesDir, 'setup.md'));
107
+
108
+ // .memory/ template files
109
+ const dotMemoryMap = {
110
+ 'README.md': join(DOT_MEMORY, 'README.md'),
111
+ '00-索引.md': join(DOT_MEMORY, '00-索引.md'),
112
+ '.gitignore': join(DOT_MEMORY, '.gitignore'),
113
+ 'project/README.md': join(DOT_PROJECT, 'README.md'),
114
+ 'config/README.md': join(DOT_PROJECT, 'config', 'README.md'),
115
+ 'config/runtime.md': join(DOT_PROJECT, 'config', 'runtime.md'),
116
+ 'config/install-lock.md': join(DOT_PROJECT, 'config', 'install-lock.md'),
117
+ 'cache/README.md': join(DOT_PROJECT, 'cache', 'README.md'),
118
+ 'global/00-压缩背景.md': join(DOT_PROJECT, 'global', '00-压缩背景.md'),
119
+ 'global/01-全局记忆.md': join(DOT_PROJECT, 'global', '01-全局记忆.md'),
120
+ 'global/03-全局开发规范.md': join(DOT_PROJECT, 'global', '03-全局开发规范.md'),
121
+ 'skills/README.md': join(DOT_PROJECT, 'skills', 'README.md'),
122
+ };
123
+ for (const [tplPath, destPath] of Object.entries(dotMemoryMap)) {
124
+ writeIfMissing(destPath, readTemplate(templatesDir, join('dot-memory', tplPath)));
125
+ }
126
+
127
+ // Agent entry files
128
+ const agentEntryMap = {
129
+ 'CLAUDE.md': join(root, 'CLAUDE.md'),
130
+ 'AGENTS.md': join(root, 'AGENTS.md'),
131
+ 'Agent.md': join(root, 'Agent.md'),
132
+ '.cursorrules': join(root, '.cursorrules'),
133
+ };
134
+ for (const [tplName, destPath] of Object.entries(agentEntryMap)) {
135
+ writeIfMissing(destPath, readTemplate(templatesDir, join('agent-entries', tplName)));
136
+ }
137
+ writeIfMissing(
138
+ join(root, '.github', 'copilot-instructions.md'),
139
+ readTemplate(templatesDir, join('agent-entries', 'copilot-instructions.md'))
140
+ );
141
+ }
142
+
143
+ function bootstrapMinimal(root) {
144
+ const MEMORY = join(root, 'memory');
145
+ const DOT_MEMORY = join(root, '.memory');
146
+ const DOT_PROJECT = join(DOT_MEMORY, '.project');
147
+
148
+ for (const sub of ['config', 'cache', 'global', 'skills']) {
149
+ mkdirSync(join(DOT_PROJECT, sub), { recursive: true });
150
+ }
151
+ writeIfMissing(join(MEMORY, 'start.md'), '# Start\n\nRead this file first.\n');
152
+ }
@@ -0,0 +1,84 @@
1
+ import { readCredentials } from '../lib/credentials.mjs';
2
+ import { createInterface } from 'node:readline';
3
+ import { cloudRegister, cloudLogin, cloudMe, isConfigured } from '../lib/api-client.mjs';
4
+ import { saveCredentials } from '../lib/credentials.mjs';
5
+
6
+ function prompt(question) {
7
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
8
+ return new Promise((resolve) => {
9
+ rl.question(question, (answer) => {
10
+ rl.close();
11
+ resolve(answer.trim());
12
+ });
13
+ });
14
+ }
15
+
16
+ export default async function login(args) {
17
+ const subcommand = args[0];
18
+
19
+ if (subcommand === 'register') return handleRegister(args.slice(1));
20
+ if (subcommand === 'status') return handleStatus();
21
+ if (subcommand === 'logout') return handleLogout();
22
+
23
+ await handleLogin(args);
24
+ }
25
+
26
+ async function handleLogin(args) {
27
+ const endpoint = args.find((a) => a.startsWith('--endpoint='))?.split('=')[1];
28
+
29
+ const username = await prompt('Username: ');
30
+ const password = await prompt('Password: ');
31
+
32
+ try {
33
+ const result = await cloudLogin(username, password);
34
+ saveCredentials(result.api_key, result.user_id, result.will_id, endpoint);
35
+ console.log('Logged in as: ' + result.display_name + ' (' + result.username + ')');
36
+ console.log('Will ID: ' + result.will_id);
37
+ } catch (err) {
38
+ console.error('Login failed: ' + err.message);
39
+ process.exit(1);
40
+ }
41
+ }
42
+
43
+ async function handleRegister(args) {
44
+ const endpoint = args.find((a) => a.startsWith('--endpoint='))?.split('=')[1];
45
+
46
+ const username = await prompt('Username: ');
47
+ const password = await prompt('Password: ');
48
+ const displayName = await prompt('Display name (optional): ');
49
+
50
+ try {
51
+ const result = await cloudRegister(username, password, displayName || undefined);
52
+ saveCredentials(result.api_key, result.user_id, result.will_id, endpoint);
53
+ console.log('Registered as: ' + result.display_name + ' (' + result.username + ')');
54
+ console.log('Will ID: ' + result.will_id);
55
+ } catch (err) {
56
+ console.error('Registration failed: ' + err.message);
57
+ process.exit(1);
58
+ }
59
+ }
60
+
61
+ async function handleStatus() {
62
+ if (!isConfigured()) {
63
+ console.log('Not logged in. Run "memory login" to authenticate.');
64
+ return;
65
+ }
66
+
67
+ try {
68
+ const me = await cloudMe();
69
+ console.log('Logged in as: ' + me.display_name + ' (' + me.username + ')');
70
+ console.log('Will ID: ' + me.will_id);
71
+ console.log('Subscribers: ' + me.subscribers);
72
+ const creds = readCredentials();
73
+ console.log('Endpoint: ' + creds.api_endpoint);
74
+ } catch (err) {
75
+ console.error('Failed to fetch profile: ' + err.message);
76
+ console.log('Your API key may be invalid. Run "memory login" to re-authenticate.');
77
+ }
78
+ }
79
+
80
+ async function handleLogout() {
81
+ const { writeCredentials } = await import('../lib/credentials.mjs');
82
+ writeCredentials({ api_endpoint: readCredentials().api_endpoint, api_key: null, user_id: null, will_id: null });
83
+ console.log('Logged out.');
84
+ }
@@ -0,0 +1,112 @@
1
+ import { existsSync, readdirSync, readFileSync, rmSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { findProjectRoot } from '../lib/project-store.mjs';
4
+
5
+ const PROJECT_SKILLS_DIR = '.memory/.project/skills';
6
+
7
+ export default async function skill(args) {
8
+ const subcommand = args[0];
9
+
10
+ if (!subcommand || subcommand === '--help') {
11
+ console.log(`memory skill — Manage project skills
12
+
13
+ Subcommands:
14
+ list List all skills (vendor + project)
15
+ uninstall <name> Remove skill from current project
16
+ `);
17
+ return;
18
+ }
19
+
20
+ const handlers = {
21
+ list: handleList,
22
+ uninstall: handleUninstall,
23
+ };
24
+ const handler = handlers[subcommand];
25
+ if (!handler) {
26
+ console.error('Unknown skill subcommand: ' + subcommand);
27
+ process.exit(1);
28
+ }
29
+ await handler(args.slice(1));
30
+ }
31
+
32
+ function getProjectSkillsDir() {
33
+ const root = findProjectRoot();
34
+ return root ? join(root, PROJECT_SKILLS_DIR) : null;
35
+ }
36
+
37
+ function listSkillsIn(dir) {
38
+ if (!existsSync(dir)) return [];
39
+ return readdirSync(dir, { withFileTypes: true })
40
+ .filter((e) => e.isDirectory())
41
+ .map((e) => e.name);
42
+ }
43
+
44
+ function getSkillMeta(dir, name) {
45
+ const skillDir = join(dir, name);
46
+ const skillFile = join(skillDir, 'SKILL.md');
47
+ let description = '';
48
+ if (existsSync(skillFile)) {
49
+ const content = readFileSync(skillFile, 'utf8');
50
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
51
+ if (fmMatch) {
52
+ const descMatch = fmMatch[1].match(/description:\s*(.+)/);
53
+ if (descMatch) description = descMatch[1].trim();
54
+ }
55
+ }
56
+ return { name, path: skillDir, description };
57
+ }
58
+
59
+ async function handleList() {
60
+ const vendorRoot = findProjectRoot();
61
+ const vendorSkillsDir = vendorRoot ? join(vendorRoot, 'memory/vendor/skills') : null;
62
+ const projectDir = getProjectSkillsDir();
63
+
64
+ console.log('=== Skills ===\n');
65
+
66
+ if (vendorSkillsDir && existsSync(vendorSkillsDir)) {
67
+ const vendorSkills = listSkillsIn(vendorSkillsDir);
68
+ if (vendorSkills.length > 0) {
69
+ console.log('System (vendor):');
70
+ for (const s of vendorSkills) {
71
+ const meta = getSkillMeta(vendorSkillsDir, s);
72
+ console.log(' ' + s + (meta.description ? ' — ' + meta.description : ''));
73
+ }
74
+ console.log('');
75
+ }
76
+ }
77
+
78
+ if (projectDir && existsSync(projectDir)) {
79
+ const projectSkills = listSkillsIn(projectDir);
80
+ if (projectSkills.length > 0) {
81
+ console.log('Project:');
82
+ for (const s of projectSkills) {
83
+ const meta = getSkillMeta(projectDir, s);
84
+ console.log(' ' + s + (meta.description ? ' — ' + meta.description : ''));
85
+ }
86
+ console.log('');
87
+ }
88
+ }
89
+ }
90
+
91
+ async function handleUninstall(args) {
92
+ const name = args[0];
93
+ if (!name) {
94
+ console.error('Usage: memory skill uninstall <skill-name>');
95
+ process.exit(1);
96
+ }
97
+
98
+ const projectDir = getProjectSkillsDir();
99
+ if (!projectDir) {
100
+ console.error('Not in a memory project.');
101
+ process.exit(1);
102
+ }
103
+
104
+ const targetDir = join(projectDir, name);
105
+ if (!existsSync(targetDir)) {
106
+ console.error('Project skill not found: ' + name);
107
+ process.exit(1);
108
+ }
109
+
110
+ rmSync(targetDir, { recursive: true, force: true });
111
+ console.log('Removed skill "' + name + '" from project.');
112
+ }
@@ -0,0 +1,28 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { isConfigured } from '../lib/api-client.mjs';
3
+ import { readCredentials } from '../lib/credentials.mjs';
4
+ import { findProjectRoot, getProjectWillCurrent, getProjectVendor } from '../lib/project-store.mjs';
5
+
6
+ export default async function status() {
7
+ console.log('=== Memory Status ===\n');
8
+
9
+ // Credentials
10
+ const creds = readCredentials();
11
+ console.log('Cloud Brain: ' + (isConfigured() ? creds.api_endpoint : 'not configured'));
12
+ if (isConfigured()) {
13
+ console.log(' User: ' + (creds.user_id || 'unknown'));
14
+ }
15
+
16
+ console.log('');
17
+
18
+ // Project
19
+ const projectRoot = findProjectRoot();
20
+ if (projectRoot) {
21
+ console.log('Project: ' + projectRoot);
22
+ console.log(' Vendor: ' + (existsSync(getProjectVendor(projectRoot)) ? 'exists' : 'missing'));
23
+ console.log(' Will: ' + (existsSync(getProjectWillCurrent(projectRoot)) ? 'exists' : 'missing'));
24
+ } else {
25
+ console.log('Project: not in a memory project');
26
+ console.log(' Run "memory init" to create one.');
27
+ }
28
+ }
@@ -0,0 +1,32 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { isConfigured } from '../lib/api-client.mjs';
4
+ import { findProjectRoot, getProjectVendor } from '../lib/project-store.mjs';
5
+
6
+ export default async function update(args) {
7
+ if (!isConfigured()) {
8
+ console.error('Not logged in. Run "memory login" first.');
9
+ process.exit(1);
10
+ }
11
+
12
+ const projectRoot = findProjectRoot();
13
+ if (!projectRoot) {
14
+ console.error('Not in a memory project. Run "memory init" first.');
15
+ process.exit(1);
16
+ }
17
+
18
+ const { cloudVendorDownloadLatest } = await import('../lib/api-client.mjs');
19
+ const { writeVendorFiles } = await import('./vendor.mjs');
20
+
21
+ const vendorDir = getProjectVendor(projectRoot);
22
+
23
+ try {
24
+ const result = await cloudVendorDownloadLatest();
25
+ writeVendorFiles(vendorDir, result.files, result.version);
26
+ console.log('Vendor updated to v' + result.version + ' (' + Object.keys(result.files).length + ' files).');
27
+ } catch (err) {
28
+ if (err.status === 404) { console.log('No vendor releases on Cloud Brain.'); return; }
29
+ console.error('Download failed: ' + err.message);
30
+ process.exit(1);
31
+ }
32
+ }