@magclaw/cli-core 0.1.39 → 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/package.json +1 -1
- package/src/cli-core/args.js +50 -0
- package/src/cli-core/team-sharing-delegate.js +19 -0
- package/src/cli.js +10 -646
- package/src/team-memory-hooks.js +0 -319
- package/src/team-sharing.js +0 -664
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
|
-
|
|
12
|
-
|
|
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
|
-
'
|
|
747
|
-
'
|
|
748
|
-
'
|
|
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
|
|
7123
|
+
await runExternalTeamSharingCommand(argv.slice(3), env);
|
|
7760
7124
|
break;
|
|
7761
7125
|
case 'skills':
|
|
7762
|
-
await
|
|
7126
|
+
await runExternalTeamSharingCommand(argv.slice(2), env);
|
|
7763
7127
|
break;
|
|
7764
7128
|
case 'hooks':
|
|
7765
|
-
await
|
|
7129
|
+
await runExternalTeamSharingCommand(argv.slice(2), env);
|
|
7766
7130
|
break;
|
|
7767
7131
|
case 'start': {
|
|
7768
7132
|
printJson(await startSavedBackground(flags, env));
|