minovative-mind-cli 2.3.1 → 2.3.2
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
CHANGED
|
@@ -121,11 +121,13 @@ Hot-swap during a session using `/models`:
|
|
|
121
121
|
|
|
122
122
|
---
|
|
123
123
|
|
|
124
|
-
## 🌐 Multi-Workspace & Cross-Repo Support
|
|
124
|
+
## 🌐 Multi-Workspace & Cross-Repo Support (Master & Sub-Workspaces)
|
|
125
125
|
|
|
126
|
-
Minovative Mind CLI doesn't restrict you to a single repository. You can
|
|
126
|
+
Minovative Mind CLI doesn't restrict you to a single repository. You can logically group multiple external repositories into **Master Workspaces** (Profiles) containing dedicated **Sub-Workspaces** (mapped to short aliases like `@backend` or `@frontend`).
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
The Context Agent and Thread Agents operate strictly within the boundaries of the active Master Workspace, ignoring other profiles to keep the context window highly targeted and memory-efficient.
|
|
129
|
+
|
|
130
|
+
By prefixing file paths with `@alias/` (e.g., `@backend/src/api.ts` and `@frontend/src/App.tsx`), the agents can investigate, refactor, and coordinate changes across your entire nested stack in a single prompt. The terminal highlights cross-workspace actions with a blue `[alias]` visual tag. Use the `/workspaces` command to create profiles, link sub-workspaces, or switch active environments.
|
|
129
131
|
|
|
130
132
|
---
|
|
131
133
|
|
|
@@ -11,7 +11,7 @@ import { chatHistoryService } from '../chatHistoryService.js';
|
|
|
11
11
|
import { printLogo, brandBg, brandFg } from '../../utils/logo.js';
|
|
12
12
|
import { readPaste } from '../../utils/paste.js';
|
|
13
13
|
import { setApprovalMode, getApprovalMode, isSubAgentsEnabled, setSubAgentsEnabled } from '../agent-tools.js';
|
|
14
|
-
import { ProxyChatSession, setGlobalActiveModel, getGlobalActiveModel
|
|
14
|
+
import { ProxyChatSession, setGlobalActiveModel, getGlobalActiveModel } from '../ai.js';
|
|
15
15
|
import { GEMINI_MODELS } from '../../utils/config.js';
|
|
16
16
|
const execAsync = promisify(exec);
|
|
17
17
|
/**
|
|
@@ -495,7 +495,9 @@ export async function handleSlashCommand(command, context) {
|
|
|
495
495
|
p.log.info(`${pc.dim('Model:')} ${pc.cyan(session.modelName)}`);
|
|
496
496
|
}
|
|
497
497
|
if (session.totalTokens !== undefined) {
|
|
498
|
-
const inputStr = session.totalInputTokens
|
|
498
|
+
const inputStr = session.totalInputTokens
|
|
499
|
+
? ` (Input: ${session.totalInputTokens.toLocaleString()}, Output: ${(session.totalOutputTokens || 0).toLocaleString()})`
|
|
500
|
+
: '';
|
|
499
501
|
p.log.info(`${pc.dim('Session Token Usage:')} ${pc.cyan(session.totalTokens.toLocaleString() + ' total tokens')}${pc.dim(inputStr)}`);
|
|
500
502
|
}
|
|
501
503
|
if (session.gitBranch) {
|
|
@@ -537,9 +539,9 @@ export async function handleSlashCommand(command, context) {
|
|
|
537
539
|
while (true) {
|
|
538
540
|
const allRoots = workspaceRegistry.getAllRoots(workspaceRoot);
|
|
539
541
|
const options = [];
|
|
542
|
+
const hasProfiles = workspaceRegistry.hasProfiles();
|
|
540
543
|
options.push({ value: 'add', label: 'Add Workspace' });
|
|
541
|
-
|
|
542
|
-
if (externalRoots.length > 0) {
|
|
544
|
+
if (hasProfiles) {
|
|
543
545
|
options.push({ value: 'edit', label: 'Edit Workspace' });
|
|
544
546
|
options.push({ value: 'remove', label: 'Remove Workspace' });
|
|
545
547
|
options.push({ value: 'list', label: 'List Workspaces' });
|
|
@@ -553,6 +555,40 @@ export async function handleSlashCommand(command, context) {
|
|
|
553
555
|
break;
|
|
554
556
|
}
|
|
555
557
|
if (action === 'add') {
|
|
558
|
+
const type = await p['select']({
|
|
559
|
+
message: 'What type of workspace would you like to add?',
|
|
560
|
+
options: [
|
|
561
|
+
{ value: 'master', label: 'Master Workspace (Create)' },
|
|
562
|
+
{ value: 'sub', label: 'Sub-workspace', hint: 'Requires an existing Master Workspace' },
|
|
563
|
+
],
|
|
564
|
+
});
|
|
565
|
+
if (p.isCancel(type))
|
|
566
|
+
continue;
|
|
567
|
+
let profileStr;
|
|
568
|
+
if (type === 'master') {
|
|
569
|
+
profileStr = (await p['text']({
|
|
570
|
+
message: 'Enter profile name (e.g. work, personal):',
|
|
571
|
+
validate: (val) => {
|
|
572
|
+
if (!val)
|
|
573
|
+
return 'Profile name is required';
|
|
574
|
+
},
|
|
575
|
+
}));
|
|
576
|
+
if (p.isCancel(profileStr))
|
|
577
|
+
continue;
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
const profiles = workspaceRegistry.listProfiles();
|
|
581
|
+
if (profiles.length === 0) {
|
|
582
|
+
p.log.error('No master workspaces (profiles) exist. Please create one first.');
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
profileStr = (await p['select']({
|
|
586
|
+
message: 'Select a Master Workspace (For Sub-Workspaces):',
|
|
587
|
+
options: profiles.map((p) => ({ value: p.name, label: p.name })),
|
|
588
|
+
}));
|
|
589
|
+
if (p.isCancel(profileStr))
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
556
592
|
const aliasStr = await p['text']({
|
|
557
593
|
message: 'Enter a short alias (e.g. backend, ui):',
|
|
558
594
|
validate: (val) => {
|
|
@@ -567,7 +603,7 @@ export async function handleSlashCommand(command, context) {
|
|
|
567
603
|
if (p.isCancel(aliasStr))
|
|
568
604
|
continue;
|
|
569
605
|
const rootPathStr = await p['text']({
|
|
570
|
-
message: 'Enter the absolute path to the workspace root
|
|
606
|
+
message: 'Enter the absolute path to the workspace root:',
|
|
571
607
|
validate: (val) => {
|
|
572
608
|
if (!val)
|
|
573
609
|
return 'Path is required';
|
|
@@ -586,17 +622,17 @@ export async function handleSlashCommand(command, context) {
|
|
|
586
622
|
p.log.error('Path is not a directory.');
|
|
587
623
|
continue;
|
|
588
624
|
}
|
|
589
|
-
workspaceRegistry.register(aliasStr, cleanRootPathStr);
|
|
590
|
-
p.log.success(`Added @${aliasStr} -> ${cleanRootPathStr}`);
|
|
625
|
+
workspaceRegistry.register(profileStr, aliasStr, cleanRootPathStr);
|
|
626
|
+
p.log.success(`Added @${aliasStr} -> ${cleanRootPathStr} (Profile: ${profileStr})`);
|
|
591
627
|
}
|
|
592
628
|
catch (e) {
|
|
593
|
-
p.log.error(`Invalid path or directory does not exist: ${cleanRootPathStr}`);
|
|
629
|
+
p.log.error(e.message || `Invalid path or directory does not exist: ${cleanRootPathStr}`);
|
|
594
630
|
}
|
|
595
631
|
}
|
|
596
632
|
else if (action === 'edit') {
|
|
597
|
-
const editOptions =
|
|
633
|
+
const editOptions = workspaceRegistry.list().map((r) => ({
|
|
598
634
|
value: r.alias,
|
|
599
|
-
label: `@${r.alias} -> ${r.
|
|
635
|
+
label: `@${r.alias} -> ${r.absolutePath}`,
|
|
600
636
|
}));
|
|
601
637
|
editOptions.push({ value: 'cancel', label: 'Cancel' });
|
|
602
638
|
const aliasToEdit = await p['select']({
|
|
@@ -608,6 +644,16 @@ export async function handleSlashCommand(command, context) {
|
|
|
608
644
|
const ws = workspaceRegistry.get(aliasToEdit);
|
|
609
645
|
if (!ws)
|
|
610
646
|
continue;
|
|
647
|
+
const newProfileStr = await p['text']({
|
|
648
|
+
message: `Enter new profile name (current: ${ws.profile}):`,
|
|
649
|
+
initialValue: ws.profile,
|
|
650
|
+
validate: (val) => {
|
|
651
|
+
if (!val)
|
|
652
|
+
return 'Profile name is required';
|
|
653
|
+
},
|
|
654
|
+
});
|
|
655
|
+
if (p.isCancel(newProfileStr))
|
|
656
|
+
continue;
|
|
611
657
|
const newAliasStr = await p['text']({
|
|
612
658
|
message: `Enter new alias (current: ${ws.alias}):`,
|
|
613
659
|
initialValue: ws.alias,
|
|
@@ -645,22 +691,22 @@ export async function handleSlashCommand(command, context) {
|
|
|
645
691
|
}
|
|
646
692
|
// Remove old alias first to avoid duplicate alias error, or to clean up
|
|
647
693
|
workspaceRegistry.unregister(ws.alias);
|
|
648
|
-
workspaceRegistry.register(newAliasStr, cleanNewRootPathStr);
|
|
649
|
-
p.log.success(`Updated @${newAliasStr} -> ${cleanNewRootPathStr}`);
|
|
694
|
+
workspaceRegistry.register(newProfileStr, newAliasStr, cleanNewRootPathStr);
|
|
695
|
+
p.log.success(`Updated @${newAliasStr} -> ${cleanNewRootPathStr} (Profile: ${newProfileStr})`);
|
|
650
696
|
}
|
|
651
697
|
catch (e) {
|
|
652
698
|
// If register failed, try to rollback
|
|
653
699
|
p.log.error(`Failed to update workspace: ${e instanceof Error ? e.message : String(e)}`);
|
|
654
700
|
try {
|
|
655
|
-
workspaceRegistry.register(ws.alias, ws.absolutePath);
|
|
701
|
+
workspaceRegistry.register(ws.profile, ws.alias, ws.absolutePath);
|
|
656
702
|
}
|
|
657
703
|
catch { }
|
|
658
704
|
}
|
|
659
705
|
}
|
|
660
706
|
else if (action === 'remove') {
|
|
661
|
-
const removeOptions =
|
|
707
|
+
const removeOptions = workspaceRegistry.list().map((r) => ({
|
|
662
708
|
value: r.alias,
|
|
663
|
-
label: `@${r.alias} -> ${r.
|
|
709
|
+
label: `@${r.alias} -> ${r.absolutePath}`,
|
|
664
710
|
}));
|
|
665
711
|
removeOptions.push({ value: 'cancel', label: 'Cancel' });
|
|
666
712
|
const aliasToRemove = await p['select']({
|
|
@@ -674,8 +720,9 @@ export async function handleSlashCommand(command, context) {
|
|
|
674
720
|
}
|
|
675
721
|
else if (action === 'list') {
|
|
676
722
|
for (const { alias, root } of allRoots) {
|
|
723
|
+
const ws = alias ? workspaceRegistry.get(alias) : null;
|
|
677
724
|
if (alias) {
|
|
678
|
-
p.log.step(`${pc.blue(`@${alias}`)} -> ${pc.dim(root)}`);
|
|
725
|
+
p.log.step(`${pc.blue(`@${alias}`)} [${ws?.profile || 'default'}] -> ${pc.dim(root)}`);
|
|
679
726
|
}
|
|
680
727
|
else {
|
|
681
728
|
p.log.step(`${pc.cyan('(primary)')} -> ${pc.dim(root)}`);
|
|
@@ -8,6 +8,17 @@ export interface RegisteredWorkspace {
|
|
|
8
8
|
absolutePath: string;
|
|
9
9
|
/** Unix epoch timestamp (ms) when this workspace was registered. */
|
|
10
10
|
registeredAt: number;
|
|
11
|
+
/** The profile (main workspace) this workspace belongs to. */
|
|
12
|
+
profile: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Represents a Profile (Main Workspace) that groups multiple registered workspaces.
|
|
16
|
+
*/
|
|
17
|
+
export interface Profile {
|
|
18
|
+
/** Unique name of the profile. */
|
|
19
|
+
name: string;
|
|
20
|
+
/** Unix epoch timestamp (ms) when this profile was created. */
|
|
21
|
+
createdAt: number;
|
|
11
22
|
}
|
|
12
23
|
/**
|
|
13
24
|
* Result of resolving an `@alias/relative/path` string against the workspace registry.
|
|
@@ -39,6 +50,8 @@ export interface ResolvedWorkspacePath {
|
|
|
39
50
|
declare class WorkspaceRegistry {
|
|
40
51
|
/** In-memory map of alias → registered workspace. */
|
|
41
52
|
private workspaces;
|
|
53
|
+
/** In-memory map of profile name → Profile. */
|
|
54
|
+
private profiles;
|
|
42
55
|
/** Whether the registry has been loaded from disk. */
|
|
43
56
|
private initialized;
|
|
44
57
|
/**
|
|
@@ -47,8 +60,9 @@ declare class WorkspaceRegistry {
|
|
|
47
60
|
*/
|
|
48
61
|
init(): void;
|
|
49
62
|
/**
|
|
50
|
-
* Registers a new external workspace root with the given alias.
|
|
63
|
+
* Registers a new external workspace root with the given alias under a profile.
|
|
51
64
|
*
|
|
65
|
+
* @param profile - The profile (main workspace) name.
|
|
52
66
|
* @param alias - Short identifier for the workspace (e.g., "backend").
|
|
53
67
|
* Must be lowercase alphanumeric with hyphens/underscores, 1–30 chars.
|
|
54
68
|
* @param absolutePath - Absolute path to the workspace root directory.
|
|
@@ -56,7 +70,7 @@ declare class WorkspaceRegistry {
|
|
|
56
70
|
* @returns The created `RegisteredWorkspace`, or throws on validation failure.
|
|
57
71
|
* @throws Error if the alias is invalid, the path doesn't exist, or the alias is already taken.
|
|
58
72
|
*/
|
|
59
|
-
register(alias: string, absolutePath: string): RegisteredWorkspace;
|
|
73
|
+
register(profile: string, alias: string, absolutePath: string): RegisteredWorkspace;
|
|
60
74
|
/**
|
|
61
75
|
* Removes a registered workspace by alias.
|
|
62
76
|
*
|
|
@@ -69,13 +83,21 @@ declare class WorkspaceRegistry {
|
|
|
69
83
|
*/
|
|
70
84
|
list(): RegisteredWorkspace[];
|
|
71
85
|
/**
|
|
72
|
-
* Returns
|
|
86
|
+
* Returns all profiles (main workspaces).
|
|
73
87
|
*/
|
|
74
|
-
|
|
88
|
+
listProfiles(): Profile[];
|
|
75
89
|
/**
|
|
76
|
-
* Checks whether any
|
|
90
|
+
* Checks whether any profiles (main workspaces) exist.
|
|
91
|
+
*/
|
|
92
|
+
hasProfiles(): boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Checks whether any workspaces exist.
|
|
77
95
|
*/
|
|
78
96
|
hasWorkspaces(): boolean;
|
|
97
|
+
/**
|
|
98
|
+
* Returns all registered workspaces for a given profile.
|
|
99
|
+
*/
|
|
100
|
+
getWorkspacesByProfile(profile: string): RegisteredWorkspace[];
|
|
79
101
|
/**
|
|
80
102
|
* Looks up a workspace by alias.
|
|
81
103
|
*
|
|
@@ -24,6 +24,8 @@ const REGISTRY_FILE = path.join(GLOBAL_CONFIG_DIR, 'workspaces.json');
|
|
|
24
24
|
class WorkspaceRegistry {
|
|
25
25
|
/** In-memory map of alias → registered workspace. */
|
|
26
26
|
workspaces = new Map();
|
|
27
|
+
/** In-memory map of profile name → Profile. */
|
|
28
|
+
profiles = new Map();
|
|
27
29
|
/** Whether the registry has been loaded from disk. */
|
|
28
30
|
initialized = false;
|
|
29
31
|
/**
|
|
@@ -37,8 +39,9 @@ class WorkspaceRegistry {
|
|
|
37
39
|
this.loadFromDisk();
|
|
38
40
|
}
|
|
39
41
|
/**
|
|
40
|
-
* Registers a new external workspace root with the given alias.
|
|
42
|
+
* Registers a new external workspace root with the given alias under a profile.
|
|
41
43
|
*
|
|
44
|
+
* @param profile - The profile (main workspace) name.
|
|
42
45
|
* @param alias - Short identifier for the workspace (e.g., "backend").
|
|
43
46
|
* Must be lowercase alphanumeric with hyphens/underscores, 1–30 chars.
|
|
44
47
|
* @param absolutePath - Absolute path to the workspace root directory.
|
|
@@ -46,8 +49,16 @@ class WorkspaceRegistry {
|
|
|
46
49
|
* @returns The created `RegisteredWorkspace`, or throws on validation failure.
|
|
47
50
|
* @throws Error if the alias is invalid, the path doesn't exist, or the alias is already taken.
|
|
48
51
|
*/
|
|
49
|
-
register(alias, absolutePath) {
|
|
52
|
+
register(profile, alias, absolutePath) {
|
|
53
|
+
const normalizedProfile = profile.toLowerCase().trim();
|
|
50
54
|
const normalizedAlias = alias.toLowerCase().trim();
|
|
55
|
+
// Ensure profile exists
|
|
56
|
+
if (!this.profiles.has(normalizedProfile)) {
|
|
57
|
+
this.profiles.set(normalizedProfile, {
|
|
58
|
+
name: normalizedProfile,
|
|
59
|
+
createdAt: Date.now(),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
51
62
|
// Validate alias format
|
|
52
63
|
if (!ALIAS_PATTERN.test(normalizedAlias)) {
|
|
53
64
|
throw new Error(`Invalid workspace alias "${normalizedAlias}". Must be 1-30 characters, ` +
|
|
@@ -85,10 +96,11 @@ class WorkspaceRegistry {
|
|
|
85
96
|
alias: normalizedAlias,
|
|
86
97
|
absolutePath: normalizedPath,
|
|
87
98
|
registeredAt: Date.now(),
|
|
99
|
+
profile: normalizedProfile,
|
|
88
100
|
};
|
|
89
101
|
this.workspaces.set(normalizedAlias, workspace);
|
|
90
102
|
this.saveToDisk();
|
|
91
|
-
debugLog(`Registered workspace: @${normalizedAlias} → ${normalizedPath}`);
|
|
103
|
+
debugLog(`Registered workspace: @${normalizedAlias} → ${normalizedPath} (Profile: ${normalizedProfile})`);
|
|
92
104
|
return workspace;
|
|
93
105
|
}
|
|
94
106
|
/**
|
|
@@ -113,17 +125,29 @@ class WorkspaceRegistry {
|
|
|
113
125
|
return Array.from(this.workspaces.values()).sort((a, b) => a.alias.localeCompare(b.alias));
|
|
114
126
|
}
|
|
115
127
|
/**
|
|
116
|
-
* Returns
|
|
128
|
+
* Returns all profiles (main workspaces).
|
|
117
129
|
*/
|
|
118
|
-
|
|
119
|
-
return this.
|
|
130
|
+
listProfiles() {
|
|
131
|
+
return Array.from(this.profiles.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
120
132
|
}
|
|
121
133
|
/**
|
|
122
|
-
* Checks whether any
|
|
134
|
+
* Checks whether any profiles (main workspaces) exist.
|
|
135
|
+
*/
|
|
136
|
+
hasProfiles() {
|
|
137
|
+
return this.profiles.size > 0;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Checks whether any workspaces exist.
|
|
123
141
|
*/
|
|
124
142
|
hasWorkspaces() {
|
|
125
143
|
return this.workspaces.size > 0;
|
|
126
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Returns all registered workspaces for a given profile.
|
|
147
|
+
*/
|
|
148
|
+
getWorkspacesByProfile(profile) {
|
|
149
|
+
return this.list().filter((ws) => ws.profile === profile.toLowerCase().trim());
|
|
150
|
+
}
|
|
127
151
|
/**
|
|
128
152
|
* Looks up a workspace by alias.
|
|
129
153
|
*
|
|
@@ -216,12 +240,31 @@ class WorkspaceRegistry {
|
|
|
216
240
|
}
|
|
217
241
|
const raw = fs.readFileSync(REGISTRY_FILE, 'utf-8');
|
|
218
242
|
const data = JSON.parse(raw);
|
|
219
|
-
if (!Array.isArray(data)) {
|
|
243
|
+
if (!data || !Array.isArray(data.workspaces)) {
|
|
244
|
+
// Fallback for old schema
|
|
245
|
+
const oldData = JSON.parse(raw);
|
|
246
|
+
if (Array.isArray(oldData)) {
|
|
247
|
+
for (const entry of oldData) {
|
|
248
|
+
if (typeof entry.alias === 'string' &&
|
|
249
|
+
typeof entry.absolutePath === 'string' &&
|
|
250
|
+
ALIAS_PATTERN.test(entry.alias)) {
|
|
251
|
+
if (fs.existsSync(entry.absolutePath)) {
|
|
252
|
+
const profile = 'default';
|
|
253
|
+
this.workspaces.set(entry.alias, { ...entry, profile });
|
|
254
|
+
if (!this.profiles.has(profile)) {
|
|
255
|
+
this.profiles.set(profile, { name: profile, createdAt: Date.now() });
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
debugLog(`Migrated ${this.workspaces.size} workspace(s) from old schema.`);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
220
263
|
debugLog('Workspace registry file has invalid format, starting fresh.');
|
|
221
264
|
return;
|
|
222
265
|
}
|
|
223
266
|
// Validate each entry and skip invalid ones
|
|
224
|
-
for (const entry of data) {
|
|
267
|
+
for (const entry of data.workspaces) {
|
|
225
268
|
if (typeof entry.alias === 'string' &&
|
|
226
269
|
typeof entry.absolutePath === 'string' &&
|
|
227
270
|
ALIAS_PATTERN.test(entry.alias)) {
|
|
@@ -234,7 +277,10 @@ class WorkspaceRegistry {
|
|
|
234
277
|
}
|
|
235
278
|
}
|
|
236
279
|
}
|
|
237
|
-
|
|
280
|
+
for (const profile of data.profiles) {
|
|
281
|
+
this.profiles.set(profile.name, profile);
|
|
282
|
+
}
|
|
283
|
+
debugLog(`Loaded ${this.workspaces.size} workspace(s) and ${this.profiles.size} profile(s) from global registry.`);
|
|
238
284
|
}
|
|
239
285
|
catch (err) {
|
|
240
286
|
debugLog(`Failed to load workspace registry: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -250,12 +296,15 @@ class WorkspaceRegistry {
|
|
|
250
296
|
if (!fs.existsSync(GLOBAL_CONFIG_DIR)) {
|
|
251
297
|
fs.mkdirSync(GLOBAL_CONFIG_DIR, { recursive: true });
|
|
252
298
|
}
|
|
253
|
-
const data =
|
|
299
|
+
const data = {
|
|
300
|
+
workspaces: Array.from(this.workspaces.values()),
|
|
301
|
+
profiles: Array.from(this.profiles.values()),
|
|
302
|
+
};
|
|
254
303
|
const tempPath = `${REGISTRY_FILE}.${Date.now()}.tmp`;
|
|
255
304
|
fs.writeFileSync(tempPath, JSON.stringify(data, null, 2), 'utf-8');
|
|
256
305
|
// Atomic rename to prevent corruption
|
|
257
306
|
fs.renameSync(tempPath, REGISTRY_FILE);
|
|
258
|
-
debugLog(`Saved ${data.length} workspace(s) to global registry.`);
|
|
307
|
+
debugLog(`Saved ${data.workspaces.length} workspace(s) and ${data.profiles.length} profile(s) to global registry.`);
|
|
259
308
|
}
|
|
260
309
|
catch (err) {
|
|
261
310
|
debugLog(`Failed to save workspace registry: ${err instanceof Error ? err.message : String(err)}`);
|
package/oclif.manifest.json
CHANGED
package/package.json
CHANGED