@magclaw/cli-core 0.1.38 → 0.1.40

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/src/cli.js CHANGED
@@ -7,56 +7,11 @@ import { chmod, copyFile, cp, lstat, mkdir, open, readFile, readdir, readlink, r
7
7
  import os from 'node:os';
8
8
  import path from 'node:path';
9
9
  import { fileURLToPath } from 'node:url';
10
+ import { parseCli } from './cli-core/args.js';
11
+ import { runExternalTeamSharingCommand } from './cli-core/team-sharing-delegate.js';
10
12
  import { renderListProfiles, shouldUseColor } from './list-renderer.js';
11
- import {
12
- buildTeamMemorySyncPackageFromTranscript,
13
- installTeamMemoryHookConfig,
14
- parseTeamMemoryTranscript,
15
- } from './team-memory-hooks.js';
16
- import {
17
- checkTeamSharingUpgrade,
18
- convertTeamSharingProjectToMemoryConfig,
19
- disableTeamSharingSkill,
20
- initTeamSharingProject,
21
- installTeamSharingHooks,
22
- installTeamSharingSkill,
23
- listTeamSharingProjects,
24
- loginTeamSharingProfile,
25
- logoutTeamSharingProfile,
26
- readTeamSharingProfileConfig,
27
- readTeamSharingProjectConfig,
28
- removeTeamSharingHooks,
29
- removeTeamSharingSkill,
30
- setTeamSharingProjectEnabled,
31
- setupTeamSharing,
32
- statusTeamSharingProject,
33
- statusTeamSharingHooks,
34
- statusTeamSharingSkill,
35
- teamSharingPaths,
36
- unsetTeamSharingProject,
37
- whoamiTeamSharingProfile,
38
- } from './team-sharing.js';
39
-
40
- export {
41
- checkTeamSharingUpgrade,
42
- disableTeamSharingSkill,
43
- initTeamSharingProject,
44
- installTeamSharingHooks,
45
- installTeamSharingSkill,
46
- listTeamSharingProjects,
47
- loginTeamSharingProfile,
48
- logoutTeamSharingProfile,
49
- removeTeamSharingHooks,
50
- removeTeamSharingSkill,
51
- setTeamSharingProjectEnabled,
52
- setupTeamSharing,
53
- statusTeamSharingProject,
54
- statusTeamSharingHooks,
55
- statusTeamSharingSkill,
56
- teamSharingPaths,
57
- unsetTeamSharingProject,
58
- whoamiTeamSharingProfile,
59
- } from './team-sharing.js';
13
+
14
+ export { parseCli } from './cli-core/args.js';
60
15
 
61
16
  export const DEFAULT_PROFILE = 'default';
62
17
  export const DEFAULT_SERVER_URL = 'http://127.0.0.1:6543';
@@ -682,51 +637,6 @@ async function acquireDaemonLock(profile = DEFAULT_PROFILE, config = {}, env = p
682
637
  throw new Error(`MagClaw daemon profile "${paths.profile}" is already starting.`);
683
638
  }
684
639
 
685
- function parseFlagKey(item) {
686
- return item
687
- .replace(/^--/, '')
688
- .replace(/-([a-z])/g, (_match, char) => char.toUpperCase());
689
- }
690
-
691
- export function parseCli(argv = process.argv) {
692
- const args = argv.slice(2);
693
- const command = args[0] && !args[0].startsWith('-') ? args.shift() : 'connect';
694
- const flags = {};
695
- const positionals = [];
696
- for (let index = 0; index < args.length; index += 1) {
697
- const item = args[index];
698
- if (item === '-h') {
699
- flags.help = true;
700
- continue;
701
- }
702
- if (item === '-V') {
703
- flags.version = true;
704
- continue;
705
- }
706
- if (!item.startsWith('--')) {
707
- positionals.push(item);
708
- continue;
709
- }
710
- const equalsIndex = item.indexOf('=');
711
- if (equalsIndex > 2) {
712
- flags[parseFlagKey(item.slice(0, equalsIndex))] = item.slice(equalsIndex + 1);
713
- continue;
714
- }
715
- const key = parseFlagKey(item);
716
- const next = args[index + 1];
717
- if (!next || next.startsWith('--')) {
718
- flags[key] = true;
719
- } else {
720
- flags[key] = next;
721
- index += 1;
722
- }
723
- }
724
- flags._ = positionals;
725
- flags.profileExplicit = Boolean(flags.profile);
726
- flags.profile = safeProfileName(flags.profile || process.env.MAGCLAW_DAEMON_PROFILE || DEFAULT_PROFILE);
727
- return { command, flags };
728
- }
729
-
730
640
  function renderHelp() {
731
641
  return [
732
642
  `MagClaw daemon CLI ${DAEMON_VERSION}`,
@@ -743,10 +653,9 @@ function renderHelp() {
743
653
  ' list List local daemon profiles and connected Computers',
744
654
  ' logs Print recent daemon logs for one profile',
745
655
  ' install-cli Install or repair durable magclaw command shims',
746
- ' memory Configure and use MagClaw team-memory sync',
747
- ' team-sharing Configure MagClaw Team Sharing setup, login, hooks, and skill',
748
- ' skills Install or manage MagClaw feature skills',
749
- ' hooks Install or manage MagClaw feature hooks',
656
+ ' team-sharing Delegate to the standalone @magclaw/team-sharing CLI',
657
+ ' skills Delegate Team Sharing skill management to @magclaw/team-sharing',
658
+ ' hooks Delegate Team Sharing hook management to @magclaw/team-sharing',
750
659
  ' upgrade Upgrade the background daemon package',
751
660
  ' doctor Show runtime and environment diagnostics',
752
661
  ' uninstall Stop and remove the background daemon service',
@@ -936,85 +845,6 @@ async function writeJsonFile(file, value) {
936
845
  await writeFile(file, `${JSON.stringify(value, null, 2)}\n`);
937
846
  }
938
847
 
939
- export function teamMemoryPaths({ profile = DEFAULT_PROFILE, cwd = process.cwd(), env = process.env } = {}) {
940
- const home = homeDirForEnv(env) || os.homedir();
941
- const memoryHome = path.resolve(env.MAGCLAW_MEMORY_HOME || path.join(home, '.magclaw', 'memory'));
942
- const cleanProfile = safeProfileName(profile || env.MAGCLAW_MEMORY_PROFILE || DEFAULT_PROFILE);
943
- const projectDir = path.resolve(cwd || process.cwd());
944
- return {
945
- profile: cleanProfile,
946
- memoryHome,
947
- profilesDir: path.join(memoryHome, 'profiles'),
948
- profileConfig: path.join(memoryHome, 'profiles', cleanProfile, 'config.json'),
949
- projectConfig: path.join(projectDir, '.magclaw', 'team-memory.json'),
950
- projectCursor: path.join(projectDir, '.magclaw', 'team-memory-cursor.json'),
951
- };
952
- }
953
-
954
- function normalizeMemoryServerUrl(value = '') {
955
- return String(value || DEFAULT_SERVER_URL).replace(/\/+$/, '');
956
- }
957
-
958
- export async function loginTeamMemoryProfile(flags = {}, env = process.env) {
959
- const paths = teamMemoryPaths({ profile: flags.profile || env.MAGCLAW_MEMORY_PROFILE || DEFAULT_PROFILE, env });
960
- const existing = await readJsonFile(paths.profileConfig, {});
961
- const token = String(flags.token || flags.apiKey || flags.memoryToken || env.MAGCLAW_MEMORY_TOKEN || existing.token || '').trim();
962
- const serverUrl = normalizeMemoryServerUrl(flags.serverUrl || existing.serverUrl || env.MAGCLAW_PUBLIC_URL || DEFAULT_SERVER_URL);
963
- const profile = {
964
- version: 1,
965
- profile: paths.profile,
966
- serverUrl,
967
- workspaceId: String(flags.workspaceId || flags.workspace || existing.workspaceId || env.MAGCLAW_WORKSPACE_ID || 'local').trim(),
968
- token,
969
- tokenScope: ['team_memory:sync', 'team_memory:search', 'team_memory:context', 'team_memory:feedback'],
970
- updatedAt: now(),
971
- createdAt: existing.createdAt || now(),
972
- };
973
- await writeJsonFile(paths.profileConfig, profile);
974
- return {
975
- ok: true,
976
- profile: paths.profile,
977
- serverUrl,
978
- workspaceId: profile.workspaceId,
979
- hasToken: Boolean(token),
980
- profileConfig: paths.profileConfig,
981
- };
982
- }
983
-
984
- export async function initTeamMemoryProject(flags = {}, env = process.env) {
985
- const cwd = path.resolve(flags.cwd || process.cwd());
986
- const paths = teamMemoryPaths({ profile: flags.profile || env.MAGCLAW_MEMORY_PROFILE || DEFAULT_PROFILE, cwd, env });
987
- const channel = String(flags.channel || flags.channelId || flags.channelPath || flags._?.[1] || '').trim();
988
- if (!channel) throw new Error('Usage: magclaw memory init --channel <channelPathOrId>');
989
- const existing = await readJsonFile(paths.projectConfig, {});
990
- const projectKey = String(flags.projectKey || existing.projectKey || path.basename(cwd)).trim();
991
- const config = {
992
- version: 1,
993
- enabled: flags.enabled === undefined ? true : !['0', 'false', 'no'].includes(String(flags.enabled).toLowerCase()),
994
- profile: paths.profile,
995
- serverUrl: normalizeMemoryServerUrl(flags.serverUrl || existing.serverUrl || env.MAGCLAW_PUBLIC_URL || DEFAULT_SERVER_URL),
996
- workspaceId: String(flags.workspaceId || flags.workspace || existing.workspaceId || env.MAGCLAW_WORKSPACE_ID || 'local').trim(),
997
- channelId: String(flags.channelId || (!/^(https?|feishu|lark|mc):/i.test(channel) ? channel : existing.channelId || '')).trim(),
998
- channelPath: String(flags.channelPath || (/^(https?|feishu|lark|mc):/i.test(channel) ? channel : existing.channelPath || '')).trim(),
999
- routingMode: 'fixed_single_channel',
1000
- projectKey,
1001
- enabledRuntimes: ['codex', 'claude_code'],
1002
- updatedAt: now(),
1003
- createdAt: existing.createdAt || now(),
1004
- };
1005
- await writeJsonFile(paths.projectConfig, config);
1006
- return {
1007
- ok: true,
1008
- projectConfig: paths.projectConfig,
1009
- profile: paths.profile,
1010
- serverUrl: config.serverUrl,
1011
- workspaceId: config.workspaceId,
1012
- channelId: config.channelId,
1013
- channelPath: config.channelPath,
1014
- projectKey,
1015
- };
1016
- }
1017
-
1018
848
  async function readProfile(profile, env = process.env) {
1019
849
  const paths = profilePaths(profile, env);
1020
850
  const config = await readJsonFile(paths.config, {});
@@ -6699,469 +6529,6 @@ async function postSetupJson(serverUrl, pathname, body = {}) {
6699
6529
  return data;
6700
6530
  }
6701
6531
 
6702
- async function teamMemoryRequestJson({ serverUrl, token = '', method = 'GET', pathname = '/api/team-memory/doctor', body = null } = {}) {
6703
- const response = await fetch(`${normalizeMemoryServerUrl(serverUrl)}${pathname}`, {
6704
- method,
6705
- headers: {
6706
- ...(body ? { 'content-type': 'application/json' } : {}),
6707
- ...(token ? { authorization: `Bearer ${token}` } : {}),
6708
- },
6709
- ...(body ? { body: JSON.stringify(body) } : {}),
6710
- });
6711
- const data = await response.json().catch(() => ({}));
6712
- if (!response.ok) {
6713
- throw new Error(data.error || data.message || `${response.status} ${response.statusText}`);
6714
- }
6715
- return data;
6716
- }
6717
-
6718
- async function readTeamMemoryProjectConfig(flags = {}, env = process.env) {
6719
- const paths = teamMemoryPaths({ profile: flags.profile || env.MAGCLAW_MEMORY_PROFILE || DEFAULT_PROFILE, cwd: flags.cwd || process.cwd(), env });
6720
- const teamSharing = await readTeamSharingProjectConfig({ profile: flags.profile || env.MAGCLAW_TEAM_SHARING_PROFILE || env.MAGCLAW_MEMORY_PROFILE || DEFAULT_PROFILE, cwd: flags.cwd || process.cwd(), env });
6721
- if (teamSharing.config) {
6722
- return {
6723
- paths: {
6724
- ...paths,
6725
- teamSharingProjectConfig: teamSharing.paths.projectConfig,
6726
- },
6727
- config: convertTeamSharingProjectToMemoryConfig(teamSharing.config),
6728
- };
6729
- }
6730
- return {
6731
- paths,
6732
- config: await readJsonFile(paths.projectConfig, null),
6733
- };
6734
- }
6735
-
6736
- async function readTeamMemoryProfile(profile, env = process.env) {
6737
- const paths = teamMemoryPaths({ profile, env });
6738
- const sharing = await readTeamSharingProfileConfig(profile, env);
6739
- if (sharing.config?.token || sharing.config?.server_url) {
6740
- return {
6741
- paths: {
6742
- ...paths,
6743
- teamSharingProfileConfig: sharing.paths.profileConfig,
6744
- },
6745
- config: {
6746
- version: sharing.config.version || 1,
6747
- profile: sharing.config.profile || profile,
6748
- serverUrl: sharing.config.server_url || sharing.config.serverUrl,
6749
- workspaceId: sharing.config.workspace_id || sharing.config.workspaceId,
6750
- token: sharing.config.token,
6751
- },
6752
- };
6753
- }
6754
- return {
6755
- paths,
6756
- config: await readJsonFile(paths.profileConfig, {}),
6757
- };
6758
- }
6759
-
6760
- async function resolveTeamMemoryClient(flags = {}, env = process.env) {
6761
- const project = await readTeamMemoryProjectConfig(flags, env);
6762
- if (!project.config) throw new Error('Run `magclaw memory init --channel <channel>` in this project first.');
6763
- const profile = await readTeamMemoryProfile(flags.profile || project.config.profile || DEFAULT_PROFILE, env);
6764
- return {
6765
- project,
6766
- profile,
6767
- serverUrl: flags.serverUrl || project.config.serverUrl || profile.config.serverUrl || DEFAULT_SERVER_URL,
6768
- token: String(profile.config.token || env.MAGCLAW_MEMORY_TOKEN || '').trim(),
6769
- };
6770
- }
6771
-
6772
- async function doctorTeamMemory(flags = {}, env = process.env) {
6773
- const project = await readTeamMemoryProjectConfig(flags, env);
6774
- const profileName = flags.profile || project.config?.profile || env.MAGCLAW_MEMORY_PROFILE || DEFAULT_PROFILE;
6775
- const profile = await readTeamMemoryProfile(profileName, env);
6776
- const serverUrl = normalizeMemoryServerUrl(flags.serverUrl || project.config?.serverUrl || profile.config.serverUrl || DEFAULT_SERVER_URL);
6777
- const token = String(profile.config.token || env.MAGCLAW_MEMORY_TOKEN || '').trim();
6778
- const local = {
6779
- projectConfig: { exists: Boolean(project.config), path: project.paths.projectConfig },
6780
- profileConfig: { exists: Boolean(profile.config?.profile), path: profile.paths.profileConfig },
6781
- hasToken: Boolean(token),
6782
- channelConfigured: Boolean(project.config?.channelId || project.config?.channelPath),
6783
- };
6784
- if (flags.offline) {
6785
- return { ok: Object.values(local).every((item) => typeof item === 'boolean' ? item : item.exists !== false), local, remote: null };
6786
- }
6787
- try {
6788
- const remote = await teamMemoryRequestJson({ serverUrl, token, pathname: '/api/team-memory/doctor' });
6789
- return {
6790
- ok: Boolean(local.projectConfig.exists && local.profileConfig.exists && local.channelConfigured && remote.ok),
6791
- serverUrl,
6792
- local,
6793
- remote,
6794
- };
6795
- } catch (error) {
6796
- return {
6797
- ok: false,
6798
- serverUrl,
6799
- local,
6800
- remote: { ok: false, error: error?.message || String(error) },
6801
- };
6802
- }
6803
- }
6804
-
6805
- function cursorLastOrdinal(cursor = {}, runtime = 'codex', sessionId = '') {
6806
- return Number(cursor?.sessions?.[runtime]?.[sessionId]?.lastOrdinal || 0);
6807
- }
6808
-
6809
- async function writeTeamMemoryCursor(file, runtime, cursor) {
6810
- const existing = await readJsonFile(file, {});
6811
- const sessions = existing.sessions && typeof existing.sessions === 'object' ? existing.sessions : {};
6812
- sessions[runtime] = sessions[runtime] && typeof sessions[runtime] === 'object' ? sessions[runtime] : {};
6813
- sessions[runtime][cursor.sessionId] = {
6814
- ...(sessions[runtime][cursor.sessionId] || {}),
6815
- ...cursor,
6816
- };
6817
- await writeJsonFile(file, {
6818
- version: 1,
6819
- sessions,
6820
- updatedAt: now(),
6821
- });
6822
- }
6823
-
6824
- export async function syncTeamMemoryTranscript(flags = {}, env = process.env) {
6825
- const transcriptPath = String(flags.transcript || flags.file || flags._?.[1] || '').trim();
6826
- if (!transcriptPath) {
6827
- if (flags.hookEvent) return { ok: true, empty: true, reason: 'missing_transcript_path' };
6828
- throw new Error('Usage: magclaw memory sync --transcript <path>');
6829
- }
6830
- const project = await readTeamMemoryProjectConfig(flags, env);
6831
- if (!project.config) throw new Error('Run `magclaw memory init --channel <channel>` in this project first.');
6832
- const runtime = String(flags.runtime || 'codex').trim().toLowerCase() === 'claude' || String(flags.runtime || '').trim().toLowerCase() === 'claude-code'
6833
- ? 'claude_code'
6834
- : String(flags.runtime || 'codex').trim().toLowerCase();
6835
- if (project.config.enabled === false) {
6836
- if (flags.hookEvent) return { ok: true, empty: true, reason: 'project_disabled' };
6837
- throw new Error('Team Sharing is disabled for this project.');
6838
- }
6839
- const runtimeConfig = project.config.runtimes?.[runtime];
6840
- if (flags.hookEvent && runtimeConfig && runtimeConfig.hooksEnabled === false) {
6841
- return { ok: true, empty: true, reason: 'runtime_hooks_disabled' };
6842
- }
6843
- const profile = await readTeamMemoryProfile(flags.profile || project.config.profile || DEFAULT_PROFILE, env);
6844
- const token = String(profile.config.token || env.MAGCLAW_MEMORY_TOKEN || '').trim();
6845
- const content = await readFile(path.resolve(transcriptPath), 'utf8');
6846
- const parsed = parseTeamMemoryTranscript(content, {
6847
- runtime,
6848
- sessionId: flags.sessionId || '',
6849
- title: flags.title || '',
6850
- projectDir: flags.cwd || process.cwd(),
6851
- });
6852
- const cursor = await readJsonFile(project.paths.projectCursor, {});
6853
- const lastOrdinal = Number(flags.full ? 0 : cursorLastOrdinal(cursor, parsed.runtime, parsed.sessionId));
6854
- const syncPackage = buildTeamMemorySyncPackageFromTranscript(content, {
6855
- runtime: parsed.runtime,
6856
- sessionId: parsed.sessionId,
6857
- title: flags.title || parsed.title || path.basename(transcriptPath),
6858
- projectKey: project.config.projectKey,
6859
- workspaceId: project.config.workspaceId,
6860
- channelId: project.config.channelId,
6861
- channelPath: project.config.channelPath,
6862
- projectDir: flags.cwd || process.cwd(),
6863
- lastOrdinal,
6864
- minCreatedAt: project.config.enabledSince || '',
6865
- });
6866
- if (syncPackage.empty || !syncPackage.body) return { ok: true, empty: true, cursor: syncPackage.cursor };
6867
- const result = await teamMemoryRequestJson({
6868
- serverUrl: flags.serverUrl || project.config.serverUrl || profile.config.serverUrl || DEFAULT_SERVER_URL,
6869
- token,
6870
- method: 'POST',
6871
- pathname: '/api/team-memory/sync',
6872
- body: syncPackage.body,
6873
- });
6874
- if (result?.ok !== false) {
6875
- await writeTeamMemoryCursor(project.paths.projectCursor, parsed.runtime, syncPackage.cursor);
6876
- }
6877
- return {
6878
- ...result,
6879
- cursor: syncPackage.cursor,
6880
- };
6881
- }
6882
-
6883
- export async function installTeamMemoryHooks(flags = {}, env = process.env) {
6884
- const home = homeDirForEnv(env) || os.homedir();
6885
- const cwd = path.resolve(flags.cwd || process.cwd());
6886
- const runtime = String(flags.runtime || 'all').trim().toLowerCase();
6887
- const output = { ok: true };
6888
- if (runtime === 'all' || runtime === 'codex') {
6889
- output.codex = await installTeamMemoryHookConfig({
6890
- runtime: 'codex',
6891
- configPath: flags.codexConfig || path.join(home, '.codex', 'hooks.json'),
6892
- projectDir: cwd,
6893
- });
6894
- }
6895
- if (runtime === 'all' || runtime === 'claude' || runtime === 'claude_code' || runtime === 'claude-code') {
6896
- output.claude = await installTeamMemoryHookConfig({
6897
- runtime: 'claude_code',
6898
- configPath: flags.claudeConfig || path.join(home, '.claude', 'settings.json'),
6899
- projectDir: cwd,
6900
- });
6901
- }
6902
- output.ok = Boolean((!output.codex || output.codex.ok) && (!output.claude || output.claude.ok));
6903
- return output;
6904
- }
6905
-
6906
- export async function searchTeamMemory(flags = {}, env = process.env) {
6907
- const query = String(flags.query || flags._?.slice(1).join(' ') || '').trim();
6908
- if (!query) throw new Error('Usage: magclaw memory search --query <text>');
6909
- const { project, serverUrl, token } = await resolveTeamMemoryClient(flags, env);
6910
- return teamMemoryRequestJson({
6911
- serverUrl,
6912
- token,
6913
- method: 'POST',
6914
- pathname: '/api/team-memory/search',
6915
- body: {
6916
- query,
6917
- channelId: flags.channelId || project.config.channelId || '',
6918
- projectKey: flags.projectKey || project.config.projectKey || '',
6919
- dateRange: flags.dateRange || null,
6920
- candidateK: flags.candidateK || undefined,
6921
- limit: flags.limit || 5,
6922
- },
6923
- });
6924
- }
6925
-
6926
- export async function readTeamMemoryContext(flags = {}, env = process.env) {
6927
- const sessionId = String(flags.sessionId || flags.session || flags._?.[1] || '').trim();
6928
- if (!sessionId) throw new Error('Usage: magclaw memory context --session-id <sessionId>');
6929
- const { serverUrl, token } = await resolveTeamMemoryClient(flags, env);
6930
- const params = new URLSearchParams();
6931
- if (flags.anchorEventId || flags.anchor) params.set('anchorEventId', String(flags.anchorEventId || flags.anchor));
6932
- if (flags.direction) params.set('direction', String(flags.direction));
6933
- if (flags.limit) params.set('limit', String(flags.limit));
6934
- const suffix = params.toString() ? `?${params.toString()}` : '';
6935
- return teamMemoryRequestJson({
6936
- serverUrl,
6937
- token,
6938
- pathname: `/api/team-memory/context/${encodeURIComponent(sessionId)}${suffix}`,
6939
- });
6940
- }
6941
-
6942
- function teamMemorySkillMarkdown() {
6943
- return [
6944
- '---',
6945
- 'name: magclaw-team-memory',
6946
- 'description: Search and read MagClaw team memory shared from Codex and Claude Code sessions.',
6947
- '---',
6948
- '',
6949
- '# MagClaw Team Memory',
6950
- '',
6951
- 'Use this skill when the user asks what the team discussed, wants to align with another session, or needs original AI conversation context.',
6952
- '',
6953
- '## Workflow',
6954
- '',
6955
- '1. Run `magclaw memory search --query "<question>" --limit 5` from the project directory.',
6956
- '2. Answer from the returned L0/L1 evidence when the user only needs a rough understanding.',
6957
- '3. For deep follow-up, run `magclaw memory context --session-id <sessionId> --anchor-event-id <eventId> --direction around --limit 20`.',
6958
- '4. Cite session titles, source refs, and context URLs from the command output.',
6959
- '',
6960
- '## Rules',
6961
- '',
6962
- '- Do not upload local secrets or raw tool output.',
6963
- '- Prefer concise synthesis first, then pull original context only when needed.',
6964
- '- If search returns low confidence or too few results, ask a narrower question or date range.',
6965
- '',
6966
- ].join('\n');
6967
- }
6968
-
6969
- async function writeTeamMemorySkill(rootDir) {
6970
- const skillDir = path.join(rootDir, 'skills', 'magclaw-team-memory');
6971
- await mkdir(skillDir, { recursive: true });
6972
- const skillPath = path.join(skillDir, 'SKILL.md');
6973
- await writeFile(skillPath, teamMemorySkillMarkdown());
6974
- return skillPath;
6975
- }
6976
-
6977
- export async function installTeamMemorySkill(flags = {}, env = process.env) {
6978
- const home = homeDirForEnv(env) || os.homedir();
6979
- const target = String(flags.target || 'codex').trim().toLowerCase();
6980
- const output = { ok: true, installed: [] };
6981
- if (target === 'codex' || target === 'all') {
6982
- const codexHome = path.resolve(env.CODEX_HOME || path.join(home, '.codex'));
6983
- output.installed.push({ target: 'codex', path: await writeTeamMemorySkill(codexHome) });
6984
- }
6985
- if (target === 'claude' || target === 'claude_code' || target === 'claude-code' || target === 'all') {
6986
- const claudeHome = path.resolve(env.CLAUDE_HOME || path.join(home, '.claude'));
6987
- output.installed.push({ target: 'claude_code', path: await writeTeamMemorySkill(claudeHome) });
6988
- }
6989
- output.ok = output.installed.length > 0;
6990
- return output;
6991
- }
6992
-
6993
- async function runTeamMemoryCommand(flags = {}, env = process.env) {
6994
- const subcommand = String(flags._?.[0] || 'help').trim();
6995
- if (subcommand === 'help' || flags.help) {
6996
- process.stdout.write([
6997
- 'Usage: magclaw memory <command> [options]',
6998
- '',
6999
- 'Commands:',
7000
- ' login Save a scoped team-memory token in the user profile',
7001
- ' init Write .magclaw/team-memory.json for the current project',
7002
- ' doctor Check local and server-side team-memory configuration',
7003
- ' install-hooks Install Codex/Claude hook commands for this project',
7004
- ' install-skill Install the MagClaw team-memory skill locally',
7005
- ' search Query shared team memory through /api/team-memory/search',
7006
- ' context Read original context around a session anchor',
7007
- ' sync Upload one transcript file through /api/team-memory/sync',
7008
- '',
7009
- ].join('\n'));
7010
- return;
7011
- }
7012
- switch (subcommand) {
7013
- case 'login':
7014
- printJson(await loginTeamMemoryProfile(flags, env));
7015
- break;
7016
- case 'init':
7017
- printJson(await initTeamMemoryProject(flags, env));
7018
- break;
7019
- case 'doctor':
7020
- printJson(await doctorTeamMemory(flags, env));
7021
- break;
7022
- case 'install-hooks':
7023
- case 'install':
7024
- printJson(await installTeamMemoryHooks(flags, env));
7025
- break;
7026
- case 'install-skill':
7027
- case 'skill':
7028
- printJson(await installTeamMemorySkill(flags, env));
7029
- break;
7030
- case 'search':
7031
- printJson(await searchTeamMemory(flags, env));
7032
- break;
7033
- case 'context':
7034
- printJson(await readTeamMemoryContext(flags, env));
7035
- break;
7036
- case 'sync':
7037
- printJson(await syncTeamMemoryTranscript(flags, env));
7038
- break;
7039
- default:
7040
- throw new Error(`Unknown memory command: ${subcommand}`);
7041
- }
7042
- }
7043
-
7044
- async function runTeamSharingCommand(flags = {}, env = process.env) {
7045
- const subcommand = String(flags._?.[0] || 'help').trim();
7046
- if (subcommand === 'help' || flags.help) {
7047
- process.stdout.write([
7048
- 'Usage: magclaw team-sharing <command> [options]',
7049
- '',
7050
- 'Commands:',
7051
- ' setup Configure login, project channel, hooks, and skill',
7052
- ' login Browser/device login for scoped team-memory sync token',
7053
- ' logout Revoke and remove the cached Team Sharing token',
7054
- ' relogin Force a fresh browser/device login',
7055
- ' whoami Show the current Team Sharing identity',
7056
- ' projects List configured project paths',
7057
- ' init Write .magclaw/team-sharing.yaml for this project',
7058
- ' unset Remove this project Team Sharing config',
7059
- ' enable Enable this project sync',
7060
- ' disable Disable this project sync',
7061
- ' status Show project/login/hook/skill status',
7062
- ' doctor Check local config, server auth, hooks, skill, and upgrade state',
7063
- ' upgrade Check npm latest version for team-sharing',
7064
- ' search Query shared team memory',
7065
- ' context Read original context around an anchor',
7066
- ' sync Upload one transcript file',
7067
- '',
7068
- ].join('\n'));
7069
- return;
7070
- }
7071
- switch (subcommand) {
7072
- case 'setup':
7073
- case 'install':
7074
- printJson({
7075
- ...(await setupTeamSharing(flags, env)),
7076
- cli: flags.noInstallCli ? { ok: true, skipped: true } : await installCliShim(flags, env),
7077
- });
7078
- break;
7079
- case 'login':
7080
- printJson(await loginTeamSharingProfile(flags, env));
7081
- break;
7082
- case 'relogin':
7083
- await logoutTeamSharingProfile(flags, env);
7084
- printJson(await loginTeamSharingProfile(flags, env));
7085
- break;
7086
- case 'logout':
7087
- printJson(await logoutTeamSharingProfile(flags, env));
7088
- break;
7089
- case 'whoami':
7090
- printJson(await whoamiTeamSharingProfile(flags, env));
7091
- break;
7092
- case 'projects':
7093
- printJson(await listTeamSharingProjects(flags, env));
7094
- break;
7095
- case 'init':
7096
- printJson(await initTeamSharingProject(flags, env));
7097
- break;
7098
- case 'unset':
7099
- printJson(await unsetTeamSharingProject(flags, env));
7100
- break;
7101
- case 'enable':
7102
- printJson(await setTeamSharingProjectEnabled(flags, env, true));
7103
- break;
7104
- case 'disable':
7105
- printJson(await setTeamSharingProjectEnabled(flags, env, false));
7106
- break;
7107
- case 'status':
7108
- printJson({
7109
- ok: true,
7110
- project: await statusTeamSharingProject(flags, env),
7111
- hooks: await statusTeamSharingHooks({ ...flags, target: flags.target || 'all' }, env),
7112
- skill: await statusTeamSharingSkill({ ...flags, target: flags.target || 'all' }, env),
7113
- });
7114
- break;
7115
- case 'doctor':
7116
- printJson({
7117
- ok: true,
7118
- project: await statusTeamSharingProject(flags, env),
7119
- hooks: await statusTeamSharingHooks({ ...flags, target: flags.target || 'all' }, env),
7120
- skill: await statusTeamSharingSkill({ ...flags, target: flags.target || 'all' }, env),
7121
- upgrade: await checkTeamSharingUpgrade({ force: Boolean(flags.force) }, env).catch((error) => ({ ok: false, error: error.message })),
7122
- });
7123
- break;
7124
- case 'upgrade':
7125
- printJson(await checkTeamSharingUpgrade({ force: true }, env));
7126
- break;
7127
- case 'search':
7128
- printJson(await searchTeamMemory(flags, env));
7129
- break;
7130
- case 'context':
7131
- printJson(await readTeamMemoryContext(flags, env));
7132
- break;
7133
- case 'sync':
7134
- printJson(await syncTeamMemoryTranscript({ ...flags, integration: flags.integration || 'team-sharing' }, env));
7135
- break;
7136
- default:
7137
- throw new Error(`Unknown team-sharing command: ${subcommand}`);
7138
- }
7139
- }
7140
-
7141
- async function runFeatureInstallCommand(kind, flags = {}, env = process.env) {
7142
- const subcommand = String(flags._?.[0] || 'help').trim();
7143
- const feature = String(flags.feature || flags.name || flags._?.[1] || 'team-sharing').trim();
7144
- if (feature !== 'team-sharing' && feature !== 'team-memory') {
7145
- throw new Error(`${kind} currently supports --feature team-sharing.`);
7146
- }
7147
- if (subcommand === 'help' || flags.help) {
7148
- process.stdout.write(`Usage: magclaw ${kind} <install|remove|enable|disable|status> --feature team-sharing\n`);
7149
- return;
7150
- }
7151
- if (kind === 'skills') {
7152
- if (subcommand === 'install' || subcommand === 'enable') printJson(await installTeamSharingSkill(flags, env));
7153
- else if (subcommand === 'remove') printJson(await removeTeamSharingSkill(flags, env));
7154
- else if (subcommand === 'disable') printJson(await disableTeamSharingSkill(flags, env));
7155
- else if (subcommand === 'status') printJson(await statusTeamSharingSkill(flags, env));
7156
- else throw new Error(`Unknown skills command: ${subcommand}`);
7157
- return;
7158
- }
7159
- if (subcommand === 'install' || subcommand === 'enable') printJson(await installTeamSharingHooks(flags, env));
7160
- else if (subcommand === 'remove' || subcommand === 'disable') printJson(await removeTeamSharingHooks(flags, env));
7161
- else if (subcommand === 'status') printJson(await statusTeamSharingHooks(flags, env));
7162
- else throw new Error(`Unknown hooks command: ${subcommand}`);
7163
- }
7164
-
7165
6532
  function hasComputerTarget(flags = {}) {
7166
6533
  return Boolean(flags.profileExplicit || flags.server || flags.serverSlug || flags.slug || flags._?.[1]);
7167
6534
  }
@@ -7752,17 +7119,14 @@ export async function main(argv = process.argv, env = process.env) {
7752
7119
  case 'computer':
7753
7120
  await runComputerCommand(flags, env);
7754
7121
  break;
7755
- case 'memory':
7756
- await runTeamMemoryCommand(flags, env);
7757
- break;
7758
7122
  case 'team-sharing':
7759
- await runTeamSharingCommand(flags, env);
7123
+ await runExternalTeamSharingCommand(argv.slice(3), env);
7760
7124
  break;
7761
7125
  case 'skills':
7762
- await runFeatureInstallCommand('skills', flags, env);
7126
+ await runExternalTeamSharingCommand(argv.slice(2), env);
7763
7127
  break;
7764
7128
  case 'hooks':
7765
- await runFeatureInstallCommand('hooks', flags, env);
7129
+ await runExternalTeamSharingCommand(argv.slice(2), env);
7766
7130
  break;
7767
7131
  case 'start': {
7768
7132
  printJson(await startSavedBackground(flags, env));