@magic-ingredients/tiny-brain-local 0.8.0 → 0.10.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/dist/agents/formatters/claude-code-formatter.d.ts +37 -0
- package/dist/agents/formatters/claude-code-formatter.d.ts.map +1 -0
- package/dist/agents/formatters/claude-code-formatter.js +217 -0
- package/dist/agents/formatters/claude-code-formatter.js.map +1 -0
- package/dist/agents/formatters/formatter-factory.d.ts +25 -0
- package/dist/agents/formatters/formatter-factory.d.ts.map +1 -0
- package/dist/agents/formatters/formatter-factory.js +61 -0
- package/dist/agents/formatters/formatter-factory.js.map +1 -0
- package/dist/agents/types.d.ts +68 -0
- package/dist/agents/types.d.ts.map +1 -0
- package/dist/agents/types.js +12 -0
- package/dist/agents/types.js.map +1 -0
- package/dist/analyser/analyzers/script-analyzer.d.ts +10 -0
- package/dist/analyser/analyzers/script-analyzer.d.ts.map +1 -0
- package/dist/analyser/analyzers/script-analyzer.js +205 -0
- package/dist/analyser/analyzers/script-analyzer.js.map +1 -0
- package/dist/analyser/detectors/base-detector.d.ts +12 -0
- package/dist/analyser/detectors/base-detector.d.ts.map +1 -0
- package/dist/analyser/detectors/base-detector.js +50 -0
- package/dist/analyser/detectors/base-detector.js.map +1 -0
- package/dist/analyser/detectors/javascript-detector.d.ts +19 -0
- package/dist/analyser/detectors/javascript-detector.d.ts.map +1 -0
- package/dist/analyser/detectors/javascript-detector.js +347 -0
- package/dist/analyser/detectors/javascript-detector.js.map +1 -0
- package/dist/analyser/index.d.ts +5 -0
- package/dist/analyser/index.d.ts.map +1 -0
- package/dist/analyser/index.js +315 -0
- package/dist/analyser/index.js.map +1 -0
- package/dist/analyser/types.d.ts +2 -0
- package/dist/analyser/types.d.ts.map +1 -0
- package/dist/analyser/types.js +2 -0
- package/dist/analyser/types.js.map +1 -0
- package/dist/analyser/utils.d.ts +5 -0
- package/dist/analyser/utils.d.ts.map +1 -0
- package/dist/analyser/utils.js +24 -0
- package/dist/analyser/utils.js.map +1 -0
- package/dist/cli/cli-factory.d.ts.map +1 -1
- package/dist/cli/cli-factory.js +17 -0
- package/dist/cli/cli-factory.js.map +1 -1
- package/dist/cli/commands/analyse.command.d.ts +7 -0
- package/dist/cli/commands/analyse.command.d.ts.map +1 -0
- package/dist/cli/commands/analyse.command.js +130 -0
- package/dist/cli/commands/analyse.command.js.map +1 -0
- package/dist/cli/commands/status.command.d.ts.map +1 -1
- package/dist/cli/commands/status.command.js +3 -1
- package/dist/cli/commands/status.command.js.map +1 -1
- package/dist/core/mcp-server.d.ts +10 -8
- package/dist/core/mcp-server.d.ts.map +1 -1
- package/dist/core/mcp-server.js +93 -85
- package/dist/core/mcp-server.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -8
- package/dist/index.js.map +1 -1
- package/dist/prompts/persona/persona.prompt.js +8 -8
- package/dist/prompts/persona/persona.prompt.js.map +1 -1
- package/dist/prompts/planning/planning.prompt.d.ts +0 -8
- package/dist/prompts/planning/planning.prompt.d.ts.map +1 -1
- package/dist/prompts/planning/planning.prompt.js +0 -175
- package/dist/prompts/planning/planning.prompt.js.map +1 -1
- package/dist/services/agent-installation-service.d.ts +101 -0
- package/dist/services/agent-installation-service.d.ts.map +1 -0
- package/dist/services/agent-installation-service.js +328 -0
- package/dist/services/agent-installation-service.js.map +1 -0
- package/dist/services/agent-manager.d.ts +45 -0
- package/dist/services/agent-manager.d.ts.map +1 -0
- package/dist/services/agent-manager.js +154 -0
- package/dist/services/agent-manager.js.map +1 -0
- package/dist/services/agent-service.d.ts +70 -0
- package/dist/services/agent-service.d.ts.map +1 -0
- package/dist/services/agent-service.js +273 -0
- package/dist/services/agent-service.js.map +1 -0
- package/dist/services/analyse-service.d.ts +97 -0
- package/dist/services/analyse-service.d.ts.map +1 -0
- package/dist/services/analyse-service.js +370 -0
- package/dist/services/analyse-service.js.map +1 -0
- package/dist/services/dashboard-launcher.service.d.ts +20 -0
- package/dist/services/dashboard-launcher.service.d.ts.map +1 -0
- package/dist/services/dashboard-launcher.service.js +30 -0
- package/dist/services/dashboard-launcher.service.js.map +1 -0
- package/dist/services/persona-enhancer.d.ts +52 -0
- package/dist/services/persona-enhancer.d.ts.map +1 -0
- package/dist/services/persona-enhancer.js +252 -0
- package/dist/services/persona-enhancer.js.map +1 -0
- package/dist/services/persona-grouper.d.ts +29 -0
- package/dist/services/persona-grouper.d.ts.map +1 -0
- package/dist/services/persona-grouper.js +111 -0
- package/dist/services/persona-grouper.js.map +1 -0
- package/dist/services/persona-service.d.ts +52 -0
- package/dist/services/persona-service.d.ts.map +1 -0
- package/dist/services/{enhanced-persona-service.js → persona-service.js} +125 -7
- package/dist/services/persona-service.js.map +1 -0
- package/dist/services/remote/auth-token-service.d.ts.map +1 -1
- package/dist/services/remote/auth-token-service.js +10 -3
- package/dist/services/remote/auth-token-service.js.map +1 -1
- package/dist/services/remote/system-persona-service.d.ts.map +1 -1
- package/dist/services/remote/system-persona-service.js +41 -10
- package/dist/services/remote/system-persona-service.js.map +1 -1
- package/dist/services/repo-service.d.ts +195 -0
- package/dist/services/repo-service.d.ts.map +1 -0
- package/dist/services/repo-service.js +1023 -0
- package/dist/services/repo-service.js.map +1 -0
- package/dist/services/types/persona-types.d.ts +84 -0
- package/dist/services/types/persona-types.d.ts.map +1 -0
- package/dist/services/types/persona-types.js +5 -0
- package/dist/services/types/persona-types.js.map +1 -0
- package/dist/services/versioning-service.d.ts +79 -0
- package/dist/services/versioning-service.d.ts.map +1 -0
- package/dist/services/versioning-service.js +191 -0
- package/dist/services/versioning-service.js.map +1 -0
- package/dist/storage/local-filesystem-adapter.d.ts +1 -0
- package/dist/storage/local-filesystem-adapter.d.ts.map +1 -1
- package/dist/storage/local-filesystem-adapter.js +47 -3
- package/dist/storage/local-filesystem-adapter.js.map +1 -1
- package/dist/storage/platform-config-adapter.d.ts +9 -0
- package/dist/storage/platform-config-adapter.d.ts.map +1 -1
- package/dist/storage/platform-config-adapter.js +55 -1
- package/dist/storage/platform-config-adapter.js.map +1 -1
- package/dist/tools/analyse.tool.d.ts +17 -0
- package/dist/tools/analyse.tool.d.ts.map +1 -0
- package/dist/tools/analyse.tool.js +124 -0
- package/dist/tools/analyse.tool.js.map +1 -0
- package/dist/tools/persona/as.tool.d.ts +32 -11
- package/dist/tools/persona/as.tool.d.ts.map +1 -1
- package/dist/tools/persona/as.tool.js +452 -317
- package/dist/tools/persona/as.tool.js.map +1 -1
- package/dist/tools/persona/persona.tool.js +2 -2
- package/dist/tools/persona/persona.tool.js.map +1 -1
- package/dist/tools/plan/plan.tool.d.ts +3 -3
- package/dist/tools/plan/plan.tool.d.ts.map +1 -1
- package/dist/tools/plan/plan.tool.js +78 -55
- package/dist/tools/plan/plan.tool.js.map +1 -1
- package/dist/tools/tool-registry.d.ts.map +1 -1
- package/dist/tools/tool-registry.js +4 -0
- package/dist/tools/tool-registry.js.map +1 -1
- package/dist/utils/repo-utils.d.ts +10 -0
- package/dist/utils/repo-utils.d.ts.map +1 -0
- package/dist/utils/repo-utils.js +55 -0
- package/dist/utils/repo-utils.js.map +1 -0
- package/package.json +6 -2
- package/dist/services/enhanced-persona-service.d.ts +0 -22
- package/dist/services/enhanced-persona-service.d.ts.map +0 -1
- package/dist/services/enhanced-persona-service.js.map +0 -1
- package/dist/services/plan-watcher.service.d.ts +0 -141
- package/dist/services/plan-watcher.service.d.ts.map +0 -1
- package/dist/services/plan-watcher.service.js +0 -1010
- package/dist/services/plan-watcher.service.js.map +0 -1
|
@@ -1,34 +1,66 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { createErrorResult } from '../index.js';
|
|
3
|
-
import { createDefaultProfile, PlanningService, RulesService,
|
|
4
|
-
import {
|
|
5
|
-
|
|
3
|
+
import { createDefaultProfile, PlanningService, RulesService, LibraryClient, parsePersonaMarkdown } from '@magic-ingredients/tiny-brain-core';
|
|
4
|
+
import { PersonaService } from '../../services/persona-service.js';
|
|
5
|
+
import { RepoService } from '../../services/repo-service.js';
|
|
6
|
+
import { AgentService } from '../../services/agent-service.js';
|
|
6
7
|
const AsArgsSchema = z.object({
|
|
7
|
-
personaName: z.string().optional(),
|
|
8
|
-
createIfMissing: z.boolean().optional(),
|
|
8
|
+
personaName: z.string().optional(),
|
|
9
9
|
confirmCreate: z.boolean().optional(),
|
|
10
10
|
});
|
|
11
|
+
// Removed conversions - TBR accepts TBL's RepoAnalysis format directly
|
|
11
12
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* AS Tool - Switch personas with repository-specific agent enhancement
|
|
14
|
+
*
|
|
15
|
+
* Flow:
|
|
16
|
+
* 1. Show current persona (if no name provided)
|
|
17
|
+
* 2. Load local persona OR import from library
|
|
18
|
+
* 3. Enhance imported personas with agents
|
|
19
|
+
* 4. Activate persona in context
|
|
20
|
+
* 5. Return formatted response
|
|
15
21
|
*/
|
|
16
22
|
export class AsTool {
|
|
17
23
|
static getToolDefinition() {
|
|
18
24
|
return {
|
|
19
25
|
name: 'as',
|
|
20
|
-
description:
|
|
26
|
+
description: `🎭 PERSONA SWITCHER 🎭
|
|
27
|
+
|
|
28
|
+
⚡ INSTANT CONTEXT: Switch to or create a persona for context-aware conversations.
|
|
29
|
+
|
|
30
|
+
✅ FEATURES:
|
|
31
|
+
• Activate existing personas instantly
|
|
32
|
+
• Create new personas with confirmation prompt
|
|
33
|
+
• Rename existing personas
|
|
34
|
+
• Import from library with repository analysis
|
|
35
|
+
• Show current persona when called without parameters
|
|
36
|
+
|
|
37
|
+
💡 USAGE:
|
|
38
|
+
• as - Show current persona (whoami)
|
|
39
|
+
• as <persona_name> - Switch to persona
|
|
40
|
+
• as <persona_name> <new_name> - Rename persona
|
|
41
|
+
• as library:<name> - Import from library
|
|
42
|
+
• as library:<name> <local_name> - Import and rename
|
|
43
|
+
|
|
44
|
+
📝 EXAMPLES:
|
|
45
|
+
• as - Show current active persona
|
|
46
|
+
• as developer - Switch to 'developer' persona
|
|
47
|
+
• as developer fred - Rename 'developer' to 'fred'
|
|
48
|
+
• as library:developer - Import 'developer' from library
|
|
49
|
+
• as library:developer fred - Import as 'fred' locally
|
|
50
|
+
|
|
51
|
+
🛡️ SAFETY: New personas require confirmation to prevent accidental creation
|
|
52
|
+
|
|
53
|
+
🎯 USE CASES:
|
|
54
|
+
• Project-specific contexts
|
|
55
|
+
• Different communication styles
|
|
56
|
+
• Specialized knowledge domains
|
|
57
|
+
• Repository-aware agent installation`,
|
|
21
58
|
inputSchema: {
|
|
22
59
|
type: 'object',
|
|
23
60
|
properties: {
|
|
24
61
|
personaName: {
|
|
25
62
|
type: 'string',
|
|
26
|
-
description: '
|
|
27
|
-
},
|
|
28
|
-
createIfMissing: {
|
|
29
|
-
type: 'boolean',
|
|
30
|
-
description: "Create the persona if it doesn't exist",
|
|
31
|
-
default: false,
|
|
63
|
+
description: 'Persona command: name to switch to, "name newName" to rename, or "library:name [localName]" to import',
|
|
32
64
|
},
|
|
33
65
|
confirmCreate: {
|
|
34
66
|
type: 'boolean',
|
|
@@ -40,150 +72,24 @@ export class AsTool {
|
|
|
40
72
|
},
|
|
41
73
|
};
|
|
42
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Main execution entry point
|
|
77
|
+
*/
|
|
43
78
|
static async execute(args, context) {
|
|
79
|
+
context.logger.debug('[As tool] [execute]');
|
|
80
|
+
// Cast to LocalRequestContext to access auth token
|
|
81
|
+
const localContext = context;
|
|
44
82
|
try {
|
|
45
83
|
const validatedArgs = AsArgsSchema.parse(args);
|
|
46
|
-
//
|
|
84
|
+
// Handle "whoami" functionality
|
|
47
85
|
if (!validatedArgs.personaName) {
|
|
48
|
-
|
|
49
|
-
if (!context.activePersona || !context.activePersona.id) {
|
|
50
|
-
return createErrorResult('No active persona. Use "as <persona_name>" to switch to a persona.');
|
|
51
|
-
}
|
|
52
|
-
const currentPersonaName = context.activePersona.id;
|
|
53
|
-
// Load optimized context for the current persona
|
|
54
|
-
// Note: No need to switch persona here since we're just showing current state
|
|
55
|
-
const optimizedResponse = await AsTool.loadOptimizedContext(currentPersonaName, context);
|
|
56
|
-
return {
|
|
57
|
-
content: [
|
|
58
|
-
{
|
|
59
|
-
type: 'text',
|
|
60
|
-
text: JSON.stringify(optimizedResponse, null, 2),
|
|
61
|
-
},
|
|
62
|
-
],
|
|
63
|
-
isError: false,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
// Create enhanced service with RequestContext
|
|
67
|
-
const service = new EnhancedPersonaService(context);
|
|
68
|
-
// Load persona using new pattern with explicit args
|
|
69
|
-
let personaProfile = await service.loadPersona({
|
|
70
|
-
personaName: validatedArgs.personaName
|
|
71
|
-
});
|
|
72
|
-
if (!personaProfile) {
|
|
73
|
-
// Try to import from library if not found locally
|
|
74
|
-
try {
|
|
75
|
-
const importedName = await service.importLibraryPersonaByName(validatedArgs.personaName);
|
|
76
|
-
if (importedName) {
|
|
77
|
-
// Successfully imported from library - message can be shown later if needed
|
|
78
|
-
// const message = importedName !== validatedArgs.personaName
|
|
79
|
-
// ? `Imported library persona as '${importedName}' (name conflict with existing local persona)`
|
|
80
|
-
// : `Imported library persona '${importedName}'`;
|
|
81
|
-
// Load the newly imported persona
|
|
82
|
-
personaProfile = await service.loadPersona({
|
|
83
|
-
personaName: importedName
|
|
84
|
-
});
|
|
85
|
-
// Update the args to use the actual imported name
|
|
86
|
-
validatedArgs.personaName = importedName;
|
|
87
|
-
}
|
|
88
|
-
else {
|
|
89
|
-
// Not found in library either (or not authenticated), check if we should create
|
|
90
|
-
const availablePersonas = await service.listPersonas({});
|
|
91
|
-
const availableList = availablePersonas.length > 0
|
|
92
|
-
? `Available personas: ${availablePersonas.join(', ')}`
|
|
93
|
-
: 'No personas available.';
|
|
94
|
-
if (!validatedArgs.createIfMissing) {
|
|
95
|
-
return createErrorResult(`Persona '${validatedArgs.personaName}' not found. ${availableList} Use createIfMissing: true to create it.`);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
100
|
-
// If library import fails for any reason, fall back to local-only behavior
|
|
101
|
-
const availablePersonas = await service.listPersonas({});
|
|
102
|
-
const availableList = availablePersonas.length > 0
|
|
103
|
-
? `Available personas: ${availablePersonas.join(', ')}`
|
|
104
|
-
: 'No personas available.';
|
|
105
|
-
if (!validatedArgs.createIfMissing) {
|
|
106
|
-
return createErrorResult(`Persona '${validatedArgs.personaName}' not found. ${availableList} Use createIfMissing: true to create it.`);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
// Continue with existing logic if persona still doesn't exist and createIfMissing is true
|
|
111
|
-
if (!personaProfile) {
|
|
112
|
-
// Get available personas for display
|
|
113
|
-
const availablePersonas = await service.listPersonas({});
|
|
114
|
-
// If createIfMissing is true but no explicit confirmation, prompt for it
|
|
115
|
-
if (validatedArgs.createIfMissing && !validatedArgs.confirmCreate) {
|
|
116
|
-
return {
|
|
117
|
-
content: [
|
|
118
|
-
{
|
|
119
|
-
type: 'text',
|
|
120
|
-
text: JSON.stringify({
|
|
121
|
-
requiresConfirmation: true,
|
|
122
|
-
action: 'createPersona',
|
|
123
|
-
personaName: validatedArgs.personaName,
|
|
124
|
-
message: `Persona "${validatedArgs.personaName}" doesn't exist. Would you like to create it?`,
|
|
125
|
-
availablePersonas: availablePersonas.length > 0 ? availablePersonas : undefined,
|
|
126
|
-
hint: 'To confirm creation, call the tool again with confirmCreate: true',
|
|
127
|
-
}, null, 2),
|
|
128
|
-
},
|
|
129
|
-
],
|
|
130
|
-
isError: false,
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
if (!personaProfile && validatedArgs.createIfMissing && validatedArgs.confirmCreate) {
|
|
135
|
-
// Create new persona with confirmation
|
|
136
|
-
const profileString = createDefaultProfile(validatedArgs.personaName, new Date().toISOString());
|
|
137
|
-
// Create persona using new pattern with explicit args
|
|
138
|
-
await service.createPersona({
|
|
139
|
-
personaName: validatedArgs.personaName,
|
|
140
|
-
profile: profileString
|
|
141
|
-
});
|
|
142
|
-
// Load the created persona to get the full PersonaProfile
|
|
143
|
-
personaProfile = await service.loadPersona({
|
|
144
|
-
personaName: validatedArgs.personaName
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
// Set the active persona using the callback for subsequent tools
|
|
148
|
-
if (personaProfile && context.updateActivePersona) {
|
|
149
|
-
// Create a minimal PersonaProfile object for the context
|
|
150
|
-
const activePersona = {
|
|
151
|
-
id: validatedArgs.personaName,
|
|
152
|
-
userContent: {
|
|
153
|
-
profile: typeof personaProfile.profile === 'string' ? personaProfile.profile : undefined,
|
|
154
|
-
},
|
|
155
|
-
};
|
|
156
|
-
context.logger.info(`AS tool calling updateActivePersona with persona: ${validatedArgs.personaName}`);
|
|
157
|
-
context.updateActivePersona(activePersona);
|
|
158
|
-
context.logger.info(`AS tool completed updateActivePersona call`);
|
|
159
|
-
// Load optimized persona context for AI AFTER persona is switched
|
|
160
|
-
const optimizedResponse = await AsTool.loadOptimizedContext(validatedArgs.personaName, context);
|
|
161
|
-
// Return clean context for AI (no operational metadata)
|
|
162
|
-
return {
|
|
163
|
-
content: [
|
|
164
|
-
{
|
|
165
|
-
type: 'text',
|
|
166
|
-
text: JSON.stringify(optimizedResponse, null, 2),
|
|
167
|
-
},
|
|
168
|
-
],
|
|
169
|
-
isError: false,
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
else {
|
|
173
|
-
context.logger.warn(`AS tool: Cannot update active persona - ${!personaProfile ? 'no profile' : 'no callback'}`);
|
|
174
|
-
// Load optimized persona context for AI (fallback without persona switch)
|
|
175
|
-
const optimizedResponse = await AsTool.loadOptimizedContext(validatedArgs.personaName, context);
|
|
176
|
-
// Return clean context for AI (no operational metadata)
|
|
177
|
-
return {
|
|
178
|
-
content: [
|
|
179
|
-
{
|
|
180
|
-
type: 'text',
|
|
181
|
-
text: JSON.stringify(optimizedResponse, null, 2),
|
|
182
|
-
},
|
|
183
|
-
],
|
|
184
|
-
isError: false,
|
|
185
|
-
};
|
|
86
|
+
return AsTool.handleShowCurrentPersona(context);
|
|
186
87
|
}
|
|
88
|
+
// Switch to specified persona (pass localContext)
|
|
89
|
+
return AsTool.handleSwitchPersona({
|
|
90
|
+
personaName: validatedArgs.personaName,
|
|
91
|
+
confirmCreate: validatedArgs.confirmCreate
|
|
92
|
+
}, localContext);
|
|
187
93
|
}
|
|
188
94
|
catch (error) {
|
|
189
95
|
if (error instanceof z.ZodError) {
|
|
@@ -193,194 +99,423 @@ export class AsTool {
|
|
|
193
99
|
}
|
|
194
100
|
}
|
|
195
101
|
/**
|
|
196
|
-
*
|
|
102
|
+
* Handle showing the current active persona
|
|
197
103
|
*/
|
|
198
|
-
static async
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
104
|
+
static async handleShowCurrentPersona(context) {
|
|
105
|
+
context.logger.debug('No persona specified: showing current persona');
|
|
106
|
+
if (!context.activePersona?.id) {
|
|
107
|
+
// Not an error - just helpful guidance
|
|
108
|
+
const service = new PersonaService(context);
|
|
109
|
+
const availablePersonas = await service.listPersonas({});
|
|
110
|
+
let helpMessage = '## No Active Persona\n\n';
|
|
111
|
+
helpMessage += 'No persona is currently selected.\n\n';
|
|
112
|
+
if (availablePersonas.length > 0) {
|
|
113
|
+
helpMessage += '### Available Personas:\n';
|
|
114
|
+
availablePersonas.forEach(persona => {
|
|
115
|
+
helpMessage += `- ${persona}\n`;
|
|
116
|
+
});
|
|
117
|
+
helpMessage += '\n';
|
|
207
118
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
119
|
+
helpMessage += '### How to use:\n';
|
|
120
|
+
helpMessage += '- `as <persona_name>` - Switch to a persona\n';
|
|
121
|
+
helpMessage += '- `as library:<name>` - Import from library\n';
|
|
122
|
+
helpMessage += '- `manage_personas list` - See all personas with details\n';
|
|
123
|
+
return {
|
|
124
|
+
content: [{
|
|
125
|
+
type: 'text',
|
|
126
|
+
text: helpMessage,
|
|
127
|
+
}],
|
|
128
|
+
isError: false,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
// Load and parse the current persona
|
|
132
|
+
const service = new PersonaService(context);
|
|
133
|
+
const persona = await service.loadPersona({ personaName: context.activePersona.id });
|
|
134
|
+
if (!persona?.profile) {
|
|
135
|
+
return createErrorResult(`Failed to load current persona '${context.activePersona.id}'`);
|
|
136
|
+
}
|
|
137
|
+
const parsedPersona = parsePersonaMarkdown(typeof persona.profile === 'string' ? persona.profile : JSON.stringify(persona.profile));
|
|
138
|
+
// Override with actual persona name from context (id is the persona name)
|
|
139
|
+
if (context.activePersona?.id) {
|
|
140
|
+
parsedPersona.name = context.activePersona.id;
|
|
141
|
+
}
|
|
142
|
+
// Get context file path for agent info
|
|
143
|
+
const agentService = new AgentService(context);
|
|
144
|
+
const formatter = agentService.getDefaultFormatter();
|
|
145
|
+
const contextFilePath = formatter.getRepoContextFilePath();
|
|
146
|
+
const response = await AsTool.buildResponse(context, parsedPersona, contextFilePath);
|
|
147
|
+
return {
|
|
148
|
+
content: [{
|
|
149
|
+
type: 'text',
|
|
150
|
+
text: AsTool.formatResponse(response, contextFilePath),
|
|
151
|
+
}],
|
|
152
|
+
isError: false,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Handle persona switch/creation with clear flow
|
|
157
|
+
*/
|
|
158
|
+
static async handleSwitchPersona(args, context) {
|
|
159
|
+
context.logger.debug(`[As tool] [handleSwitchPersona: ${args.personaName} ]`);
|
|
160
|
+
const fullPersonaName = args.personaName;
|
|
161
|
+
// Check if this is a library persona request
|
|
162
|
+
if (fullPersonaName.startsWith('library:')) {
|
|
163
|
+
return AsTool.handleLibraryPersona(fullPersonaName, context);
|
|
164
|
+
}
|
|
165
|
+
// Parse potential rename: "personaName newName"
|
|
166
|
+
const parts = fullPersonaName.split(/\s+/);
|
|
167
|
+
const personaName = parts[0];
|
|
168
|
+
const newName = parts.length > 1 ? parts.slice(1).join(' ') : null;
|
|
169
|
+
// Local persona flow
|
|
170
|
+
const personaService = new PersonaService(context);
|
|
171
|
+
const exists = await personaService.exists(personaName);
|
|
172
|
+
if (exists) {
|
|
173
|
+
context.logger.debug(`[As tool] [handleSwitchPersona] persona already exists`);
|
|
174
|
+
// Check if this is a rename operation
|
|
175
|
+
if (newName) {
|
|
176
|
+
context.logger.debug('[As tool] [handleSwitchPersona] found persona name and rename');
|
|
177
|
+
return AsTool.handlePersonaRename(personaName, newName, personaService, context);
|
|
213
178
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
179
|
+
// Switch to existing persona
|
|
180
|
+
context.logger.debug('[As tool] [handleSwitchPersona] switching to existing persona');
|
|
181
|
+
const profile = await personaService.loadPersona({ personaName });
|
|
182
|
+
if (!profile) {
|
|
183
|
+
return createErrorResult(`[As tool] [handleSwitchPersona] failed to load persona '${personaName}'`);
|
|
184
|
+
}
|
|
185
|
+
// Parse the persona
|
|
186
|
+
const parsedPersona = parsePersonaMarkdown(typeof profile.profile === 'string' ? profile.profile : JSON.stringify(profile.profile));
|
|
187
|
+
// Override the parsed name with the actual persona name from the folder
|
|
188
|
+
parsedPersona.name = personaName;
|
|
189
|
+
// Get context file path for agent info
|
|
190
|
+
const agentService = new AgentService(context);
|
|
191
|
+
const formatter = agentService.getDefaultFormatter();
|
|
192
|
+
const contextFilePath = formatter?.getRepoContextFilePath();
|
|
193
|
+
// Activate persona and return response with updated context
|
|
194
|
+
const updatedContext = await AsTool.activatePersona(personaName, profile, context);
|
|
195
|
+
const response = await AsTool.buildResponse(updatedContext, parsedPersona, contextFilePath);
|
|
196
|
+
return {
|
|
197
|
+
content: [{
|
|
198
|
+
type: 'text',
|
|
199
|
+
text: AsTool.formatResponse(response, contextFilePath),
|
|
200
|
+
}],
|
|
201
|
+
isError: false,
|
|
227
202
|
};
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
// Persona doesn't exist - confirm creation
|
|
206
|
+
return AsTool.handlePersonaNotFound({ personaName, confirmCreate: args.confirmCreate }, personaService, context);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Handle library persona import - simplified to only import persona
|
|
211
|
+
*/
|
|
212
|
+
static async handleLibraryPersona(personaName, context) {
|
|
213
|
+
// Parse library:sourceName targetName
|
|
214
|
+
// e.g., "library:developer fred" → fetch "developer", create as "fred"
|
|
215
|
+
const afterPrefix = personaName.substring('library:'.length).trim();
|
|
216
|
+
const parts = afterPrefix.split(/\s+/);
|
|
217
|
+
const libraryPersonaName = parts[0];
|
|
218
|
+
const localPersonaName = parts.slice(1).join(' ') || libraryPersonaName;
|
|
219
|
+
if (!libraryPersonaName) {
|
|
220
|
+
return createErrorResult('Invalid library persona format. Use: library:<persona_name> [local_name]');
|
|
221
|
+
}
|
|
222
|
+
// Check auth
|
|
223
|
+
if (!context.authToken?.token) {
|
|
224
|
+
return createErrorResult('authentication required to fetch library personas');
|
|
225
|
+
}
|
|
226
|
+
try {
|
|
227
|
+
// Step 1: Fetch library persona
|
|
228
|
+
const libraryClient = new LibraryClient();
|
|
229
|
+
const personaResponse = await libraryClient.getPersona(libraryPersonaName, context.authToken?.token || '', false // Without agents
|
|
230
|
+
);
|
|
231
|
+
// Step 2: Create local persona
|
|
232
|
+
const personaService = new PersonaService(context);
|
|
233
|
+
// Handle both response formats
|
|
234
|
+
const personaData = typeof personaResponse === 'string'
|
|
235
|
+
? { systemBlock: personaResponse }
|
|
236
|
+
: personaResponse.systemBlock
|
|
237
|
+
? personaResponse
|
|
238
|
+
: { systemBlock: personaResponse.persona?.systemBlock || JSON.stringify(personaResponse) };
|
|
239
|
+
const enhancedPersona = personaService.addUserBlock(personaData, localPersonaName);
|
|
240
|
+
// Step 3: Create metadata
|
|
241
|
+
const metadata = {
|
|
242
|
+
id: localPersonaName,
|
|
243
|
+
name: localPersonaName,
|
|
244
|
+
description: personaResponse.description || `${personaResponse.name} persona`,
|
|
245
|
+
created: new Date().toISOString(),
|
|
246
|
+
lastUpdated: new Date().toISOString(),
|
|
247
|
+
sourceLibrary: {
|
|
248
|
+
id: personaResponse.id,
|
|
249
|
+
name: libraryPersonaName,
|
|
250
|
+
version: personaResponse.version || '1.0.0',
|
|
251
|
+
importedAt: new Date().toISOString()
|
|
259
252
|
}
|
|
260
|
-
const currentPhase = activePlan.phases?.find(p => p.status === 'in_progress');
|
|
261
|
-
activePlanContext = {
|
|
262
|
-
id: activePlan.id,
|
|
263
|
-
title: activePlan.title || 'Untitled Plan',
|
|
264
|
-
goal: activePlan.overview || '',
|
|
265
|
-
current_phase: currentPhase?.title || 'Planning',
|
|
266
|
-
next_actions: nextActions.slice(0, 5),
|
|
267
|
-
recent_progress: recentProgress.slice(0, 5),
|
|
268
|
-
blockers
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
// 4. Extract persona-specific rules from profile
|
|
272
|
-
const personaRules = extractRulesFromProfile(profileContent);
|
|
273
|
-
// 5. Extract other profile sections
|
|
274
|
-
const background = AsTool.extractBackground(profileContent);
|
|
275
|
-
const communicationStyle = AsTool.extractCommunicationStyleText(profileContent);
|
|
276
|
-
const details = extractDetailsFromProfile(profileContent);
|
|
277
|
-
// 6. Build persona object with only non-empty fields
|
|
278
|
-
const persona = {
|
|
279
|
-
name: personaName,
|
|
280
|
-
rules: personaRules
|
|
281
253
|
};
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if (preferences.length > 0 || avoidPatterns.length > 0) {
|
|
296
|
-
persona.learned_patterns = {
|
|
297
|
-
preferences,
|
|
298
|
-
avoid: avoidPatterns
|
|
299
|
-
};
|
|
254
|
+
// Step 4: Create the persona and get parsed result
|
|
255
|
+
const parsedPersona = await personaService.create(localPersonaName, enhancedPersona, metadata);
|
|
256
|
+
context.logger.info(`Imported library persona '${libraryPersonaName}' as '${localPersonaName}'`);
|
|
257
|
+
// Step 5: Activate persona
|
|
258
|
+
const updatedContext = await AsTool.activatePersona(localPersonaName, { profile: enhancedPersona.systemBlock }, context);
|
|
259
|
+
// Step 6: Check if in repository and if it needs initialization
|
|
260
|
+
const repoService = new RepoService(context);
|
|
261
|
+
let needsInitMessage = '';
|
|
262
|
+
if (repoService.isInRepository()) {
|
|
263
|
+
const initialized = await repoService.isRepositoryInitialized();
|
|
264
|
+
if (!initialized) {
|
|
265
|
+
needsInitMessage = '\n\n📝 Note: Repository not initialized. Run `analyse` to fetch recommended agents for your tech stack.';
|
|
266
|
+
}
|
|
300
267
|
}
|
|
301
|
-
// 7
|
|
302
|
-
const
|
|
303
|
-
|
|
304
|
-
|
|
268
|
+
// Step 7: Get context file path for agent info (if available)
|
|
269
|
+
const agentService = new AgentService(context);
|
|
270
|
+
const formatter = agentService.getDefaultFormatter();
|
|
271
|
+
const contextFilePath = formatter?.getRepoContextFilePath();
|
|
272
|
+
// Step 8: Build and return response
|
|
273
|
+
const response = await AsTool.buildResponse(updatedContext, parsedPersona, contextFilePath);
|
|
274
|
+
return {
|
|
275
|
+
content: [{
|
|
276
|
+
type: 'text',
|
|
277
|
+
text: AsTool.formatResponse(response, contextFilePath) + needsInitMessage,
|
|
278
|
+
}],
|
|
279
|
+
isError: false,
|
|
305
280
|
};
|
|
306
|
-
// Only add active plan if it exists
|
|
307
|
-
if (activePlanContext) {
|
|
308
|
-
response.ACTIVE_PLAN = activePlanContext;
|
|
309
|
-
}
|
|
310
|
-
return response;
|
|
311
281
|
}
|
|
312
282
|
catch (error) {
|
|
313
|
-
context.logger.error(`Failed to
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
283
|
+
context.logger.error(`Failed to fetch library persona: ${error}`);
|
|
284
|
+
return createErrorResult(`Failed to fetch library persona: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Activate persona in context and return updated context
|
|
289
|
+
*/
|
|
290
|
+
static async activatePersona(personaName, profile, context) {
|
|
291
|
+
// Update the active persona in context and get the new context
|
|
292
|
+
if (context.updateActivePersona) {
|
|
293
|
+
const newContext = await context.updateActivePersona({
|
|
294
|
+
id: personaName,
|
|
295
|
+
userContent: {
|
|
296
|
+
profile: profile.profile || profile
|
|
320
297
|
}
|
|
321
|
-
};
|
|
298
|
+
});
|
|
299
|
+
return newContext || context; // Return new context or original if update doesn't return one
|
|
322
300
|
}
|
|
301
|
+
return context;
|
|
323
302
|
}
|
|
324
303
|
/**
|
|
325
|
-
*
|
|
304
|
+
* Build persona response data structure
|
|
326
305
|
*/
|
|
327
|
-
static
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
306
|
+
static async buildResponse(context, parsedPersona, _contextFilePath // Used in formatResponse, not here
|
|
307
|
+
) {
|
|
308
|
+
const rulesService = new RulesService(context);
|
|
309
|
+
const planningService = new PlanningService(context);
|
|
310
|
+
// Get additional rules from rules service
|
|
311
|
+
const allRules = await rulesService.getAllRules(parsedPersona.name);
|
|
312
|
+
const additionalUserRules = allRules?.rules || [];
|
|
313
|
+
// Get active plan if any
|
|
314
|
+
let activePlan = null;
|
|
315
|
+
try {
|
|
316
|
+
activePlan = await planningService.getActivePlan();
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
context.logger.debug('No active persona for plan lookup');
|
|
320
|
+
}
|
|
321
|
+
// Note: contextFilePath is passed but not used here since we removed AGENT USAGE INSTRUCTIONS
|
|
322
|
+
// It's used later in formatResponse to add the reminder to re-read CLAUDE.md
|
|
323
|
+
const response = {
|
|
324
|
+
PERSONA: {
|
|
325
|
+
name: parsedPersona.name,
|
|
326
|
+
"GOLDEN RULES": additionalUserRules,
|
|
327
|
+
"USER RULES": parsedPersona.user?.rules || [],
|
|
328
|
+
"USER DETAILS": parsedPersona.user?.details || {},
|
|
329
|
+
"SYSTEM RULES": parsedPersona.system?.rules || [],
|
|
330
|
+
"SYSTEM DETAILS": parsedPersona.system?.details || {}
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
if (activePlan) {
|
|
334
|
+
// Extract just the first line or a short summary of the plan
|
|
335
|
+
let goalSummary = '';
|
|
336
|
+
if (activePlan.overview) {
|
|
337
|
+
const lines = activePlan.overview.split('\n').filter(l => l.trim());
|
|
338
|
+
goalSummary = lines[0] || activePlan.title;
|
|
339
|
+
// Truncate if still too long
|
|
340
|
+
if (goalSummary.length > 200) {
|
|
341
|
+
goalSummary = goalSummary.substring(0, 197) + '...';
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
goalSummary = activePlan.title;
|
|
346
|
+
}
|
|
347
|
+
response.ACTIVE_PLAN = {
|
|
348
|
+
id: activePlan.id,
|
|
349
|
+
title: activePlan.title,
|
|
350
|
+
goal: goalSummary,
|
|
351
|
+
current_phase: activePlan.currentState?.phaseTitle || '',
|
|
352
|
+
next_actions: activePlan.currentState?.nextAction ? [activePlan.currentState.nextAction.description] : [],
|
|
353
|
+
blockers: activePlan.metadata?.blockers?.map((b) => b.description)
|
|
354
|
+
};
|
|
334
355
|
}
|
|
335
|
-
return
|
|
356
|
+
return response;
|
|
336
357
|
}
|
|
337
358
|
/**
|
|
338
|
-
*
|
|
359
|
+
* Format the persona response as human-readable markdown with enforced workflow
|
|
339
360
|
*/
|
|
340
|
-
static
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
361
|
+
static formatResponse(response, contextFilePath) {
|
|
362
|
+
let formatted = `# THIS IS YOUR CONTEXT FOR THIS SESSION - YOU MUST USE IT\n\n`;
|
|
363
|
+
formatted += `IMPORTANT: USER RULES and USER DETAILS ALWAYS take precedence over SYSTEM RULES and SYSTEM DETAILS. When there are conflicts, follow the user's preferences.\n\n`;
|
|
364
|
+
formatted += `## PERSONA: ${response.PERSONA.name}\n\n`;
|
|
365
|
+
// Golden Rules
|
|
366
|
+
if (response.PERSONA["GOLDEN RULES"] && response.PERSONA["GOLDEN RULES"].length > 0) {
|
|
367
|
+
formatted += `### GOLDEN RULES\n`;
|
|
368
|
+
response.PERSONA["GOLDEN RULES"].forEach(rule => {
|
|
369
|
+
formatted += `- ${rule}\n`;
|
|
370
|
+
});
|
|
371
|
+
formatted += '\n';
|
|
372
|
+
}
|
|
373
|
+
// User Rules
|
|
374
|
+
if (response.PERSONA["USER RULES"] && response.PERSONA["USER RULES"].length > 0) {
|
|
375
|
+
formatted += `### USER RULES\n`;
|
|
376
|
+
response.PERSONA["USER RULES"].forEach(rule => {
|
|
377
|
+
formatted += `- ${rule}\n`;
|
|
378
|
+
});
|
|
379
|
+
formatted += '\n';
|
|
380
|
+
}
|
|
381
|
+
// User Details
|
|
382
|
+
const userDetails = response.PERSONA["USER DETAILS"];
|
|
383
|
+
if (userDetails && Object.keys(userDetails).length > 0) {
|
|
384
|
+
formatted += `### USER DETAILS\n`;
|
|
385
|
+
Object.entries(userDetails).forEach(([key, value]) => {
|
|
386
|
+
formatted += `#### ${key}\n${value}\n\n`;
|
|
387
|
+
});
|
|
347
388
|
}
|
|
348
|
-
|
|
389
|
+
// System Rules
|
|
390
|
+
if (response.PERSONA["SYSTEM RULES"] && response.PERSONA["SYSTEM RULES"].length > 0) {
|
|
391
|
+
formatted += `### SYSTEM RULES\n`;
|
|
392
|
+
response.PERSONA["SYSTEM RULES"].forEach(rule => {
|
|
393
|
+
formatted += `- ${rule}\n`;
|
|
394
|
+
});
|
|
395
|
+
formatted += '\n';
|
|
396
|
+
}
|
|
397
|
+
// System Details
|
|
398
|
+
const systemDetails = response.PERSONA["SYSTEM DETAILS"];
|
|
399
|
+
if (systemDetails && Object.keys(systemDetails).length > 0) {
|
|
400
|
+
formatted += `### SYSTEM DETAILS\n`;
|
|
401
|
+
Object.entries(systemDetails).forEach(([key, value]) => {
|
|
402
|
+
formatted += `#### ${key}\n${value}\n\n`;
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
// Active Plan - Keep it concise
|
|
406
|
+
if (response.ACTIVE_PLAN) {
|
|
407
|
+
formatted += `### ACTIVE PLAN\n`;
|
|
408
|
+
formatted += `**${response.ACTIVE_PLAN.title}**\n`;
|
|
409
|
+
if (response.ACTIVE_PLAN.goal && response.ACTIVE_PLAN.goal !== response.ACTIVE_PLAN.title) {
|
|
410
|
+
formatted += `${response.ACTIVE_PLAN.goal}\n`;
|
|
411
|
+
}
|
|
412
|
+
formatted += `\n`;
|
|
413
|
+
formatted += `- Current Phase: ${response.ACTIVE_PLAN.current_phase || 'Not specified'}\n`;
|
|
414
|
+
if (response.ACTIVE_PLAN.next_actions && response.ACTIVE_PLAN.next_actions.length > 0) {
|
|
415
|
+
formatted += `- Next Action: ${response.ACTIVE_PLAN.next_actions[0]}\n`;
|
|
416
|
+
}
|
|
417
|
+
if (response.ACTIVE_PLAN.blockers && response.ACTIVE_PLAN.blockers.length > 0) {
|
|
418
|
+
formatted += `- Blockers: ${response.ACTIVE_PLAN.blockers.length} active\n`;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
// REFLECTION REQUIREMENT
|
|
422
|
+
formatted += `\n## 🔍 MANDATORY REFLECTION - AFTER EVERY TOOL USE\n\n`;
|
|
423
|
+
formatted += `After using ANY tool, you MUST ask yourself:\n`;
|
|
424
|
+
formatted += `- Was this the right phase for this action?\n`;
|
|
425
|
+
formatted += `- Should I have used a specialized agent instead?\n`;
|
|
426
|
+
formatted += `- Am I following the checklist workflow?\n`;
|
|
427
|
+
formatted += `- Does this align with the user's actual request?\n\n`;
|
|
428
|
+
// Add reminder to re-read context file if in a repo with agents installed
|
|
429
|
+
if (contextFilePath) {
|
|
430
|
+
formatted += `---\n\n`;
|
|
431
|
+
formatted += `**⚡ AGENT ORCHESTRATION REQUIRED**\n\n`;
|
|
432
|
+
formatted += `**RE-READ ${contextFilePath} to understand available agents for this repository.**\n`;
|
|
433
|
+
formatted += `**DEFAULT BEHAVIOR**: Use Task tool with specialized agents\n`;
|
|
434
|
+
formatted += `**EXCEPTION**: Only skip agents if you can explicitly justify why none apply\n\n`;
|
|
435
|
+
formatted += `**MANDATORY CONFIRMATION:** When ready to proceed, you MUST respond with exactly this format (copy it exactly, do not modify):\n\n`;
|
|
436
|
+
formatted += `\`\`\`\n`;
|
|
437
|
+
formatted += ` ✅ Loaded context\n`;
|
|
438
|
+
formatted += ` ✅ Re-read ${contextFilePath}\n`;
|
|
439
|
+
formatted += ` ✅ Completed pre-flight checklist\n`;
|
|
440
|
+
formatted += ` ✅ Ready for agent-first workflow\n`;
|
|
441
|
+
formatted += ` \n\n`;
|
|
442
|
+
formatted += ` I've switched to your **${response.PERSONA.name}** persona.\n`;
|
|
443
|
+
formatted += ` \n`;
|
|
444
|
+
formatted += ` 🧠 Dashboard available at: [http://localhost:8765](http://localhost:8765)\n`;
|
|
445
|
+
formatted += `\`\`\`\n\n`;
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
// Add persona switch confirmation message when no context file
|
|
449
|
+
formatted += `I've switched to your **${response.PERSONA.name}** persona.\n\n`;
|
|
450
|
+
formatted += `🧠 Dashboard available at: [http://localhost:8765](http://localhost:8765)\n`;
|
|
451
|
+
}
|
|
452
|
+
return formatted;
|
|
349
453
|
}
|
|
350
454
|
/**
|
|
351
|
-
*
|
|
455
|
+
* Handle renaming an existing persona
|
|
352
456
|
*/
|
|
353
|
-
static
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
lowerPattern.includes('never') ||
|
|
362
|
-
lowerPattern.includes('don\'t') ||
|
|
363
|
-
lowerPattern.includes('do not') ||
|
|
364
|
-
lowerPattern.includes('should not') ||
|
|
365
|
-
lowerPattern.includes('must not')) {
|
|
366
|
-
avoidPatterns.push(pattern);
|
|
457
|
+
static async handlePersonaRename(oldName, newName, service, context) {
|
|
458
|
+
try {
|
|
459
|
+
// Use storage renamePersona method
|
|
460
|
+
await context.storage.renamePersona(oldName, newName, context.userId);
|
|
461
|
+
// Load and activate the renamed persona
|
|
462
|
+
const profile = await service.loadPersona({ personaName: newName });
|
|
463
|
+
if (!profile) {
|
|
464
|
+
return createErrorResult(`Failed to load renamed persona '${newName}'`);
|
|
367
465
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
466
|
+
// Parse the persona
|
|
467
|
+
const parsedPersona = parsePersonaMarkdown(typeof profile.profile === 'string' ? profile.profile : JSON.stringify(profile.profile));
|
|
468
|
+
// Override with the new name being set
|
|
469
|
+
parsedPersona.name = newName;
|
|
470
|
+
// Get context file path for agent info
|
|
471
|
+
const agentService = new AgentService(context);
|
|
472
|
+
const formatter = agentService.getDefaultFormatter();
|
|
473
|
+
const contextFilePath = formatter?.getRepoContextFilePath();
|
|
474
|
+
const updatedContext = await AsTool.activatePersona(newName, profile, context);
|
|
475
|
+
context.logger.info(`Renamed persona '${oldName}' to '${newName}'`);
|
|
476
|
+
// Return response showing the renamed persona is now active with updated context
|
|
477
|
+
const response = await AsTool.buildResponse(updatedContext, parsedPersona, contextFilePath);
|
|
478
|
+
return {
|
|
479
|
+
content: [{
|
|
480
|
+
type: 'text',
|
|
481
|
+
text: AsTool.formatResponse(response, contextFilePath),
|
|
482
|
+
}],
|
|
483
|
+
isError: false,
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
catch (error) {
|
|
487
|
+
context.logger.error(`Failed to rename persona: ${error}`);
|
|
488
|
+
return createErrorResult(`Failed to rename persona '${oldName}' to '${newName}': ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
382
489
|
}
|
|
383
|
-
|
|
490
|
+
}
|
|
491
|
+
static async handlePersonaNotFound(args, service, context) {
|
|
492
|
+
const availablePersonas = await service.listPersonas({}) || [];
|
|
493
|
+
// Always prompt for confirmation when persona doesn't exist
|
|
494
|
+
if (!args.confirmCreate) {
|
|
495
|
+
return {
|
|
496
|
+
content: [{
|
|
497
|
+
type: 'text',
|
|
498
|
+
text: JSON.stringify({
|
|
499
|
+
requiresConfirmation: true,
|
|
500
|
+
action: 'createPersona',
|
|
501
|
+
personaName: args.personaName,
|
|
502
|
+
message: `Persona "${args.personaName}" doesn't exist. Would you like to create it?`,
|
|
503
|
+
availablePersonas: availablePersonas.length > 0 ? availablePersonas : undefined,
|
|
504
|
+
hint: 'To confirm creation, call the tool again with confirmCreate: true',
|
|
505
|
+
}, null, 2),
|
|
506
|
+
}],
|
|
507
|
+
isError: false,
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
// Create the persona
|
|
511
|
+
const profileString = createDefaultProfile(args.personaName, new Date().toISOString());
|
|
512
|
+
await service.createPersona({
|
|
513
|
+
personaName: args.personaName,
|
|
514
|
+
profile: profileString
|
|
515
|
+
});
|
|
516
|
+
// After creation, switch to the new persona
|
|
517
|
+
return AsTool.handleSwitchPersona({ ...args }, // confirmCreate is already true, no recursion issue
|
|
518
|
+
context);
|
|
384
519
|
}
|
|
385
520
|
}
|
|
386
521
|
//# sourceMappingURL=as.tool.js.map
|