fabiana 1.2.0 → 1.3.0
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/cli.js +5 -1
- package/dist/daemon/index.js +49 -21
- package/dist/tools/index.js +0 -5
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import { readFileSync } from 'fs';
|
|
|
9
9
|
import { fileURLToPath } from 'url';
|
|
10
10
|
import { join, dirname } from 'path';
|
|
11
11
|
import { spawnSync } from 'child_process';
|
|
12
|
-
import { startDaemon, runInitiativeOnce, runConsolidateOnce, runSolitudeOnce } from './daemon/index.js';
|
|
12
|
+
import { startDaemon, runInitiativeOnce, runConsolidateOnce, runSolitudeOnce, runPromptPreview } from './daemon/index.js';
|
|
13
13
|
import { runMigration } from './db/migrate-from-files.js';
|
|
14
14
|
import { runDoctor } from './doctor.js';
|
|
15
15
|
import { runBackup, runRestore } from './backup.js';
|
|
@@ -71,6 +71,10 @@ program
|
|
|
71
71
|
.action((type, opts) => {
|
|
72
72
|
runSolitudeOnce(type, opts.dryRun ?? false);
|
|
73
73
|
});
|
|
74
|
+
program
|
|
75
|
+
.command('prompt-preview [mode]')
|
|
76
|
+
.description('Preview the combined system prompt before it reaches the AI — all modes or a specific one')
|
|
77
|
+
.action((mode) => runPromptPreview(mode));
|
|
74
78
|
program
|
|
75
79
|
.command('config')
|
|
76
80
|
.description('Tweak her settings (opens config.json in your editor)')
|
package/dist/daemon/index.js
CHANGED
|
@@ -24,6 +24,54 @@ async function loadConfig() {
|
|
|
24
24
|
throw new Error(`Config not found at ${paths.configJson}. Run 'fabiana init' to set up your companion.`);
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
+
export async function buildSystemPrompt(mode, conversationState) {
|
|
28
|
+
const baseSystemPrompt = await fs.readFile(paths.systemMd(), 'utf-8');
|
|
29
|
+
// Both external-outreach and external-reply share system-external.md
|
|
30
|
+
const modeKey = mode.startsWith('external-') ? 'external' : mode;
|
|
31
|
+
const modeSystemPrompt = await fs.readFile(paths.systemMd(modeKey), 'utf-8').catch(() => '');
|
|
32
|
+
let systemPromptContent = modeSystemPrompt
|
|
33
|
+
? `${baseSystemPrompt}\n\n---\n\n${modeSystemPrompt}`
|
|
34
|
+
: baseSystemPrompt;
|
|
35
|
+
// Resolve .fabiana/ references to the actual home path so Fabiana uses correct absolute paths
|
|
36
|
+
systemPromptContent = systemPromptContent.replaceAll('.fabiana/', `${FABIANA_HOME}/`);
|
|
37
|
+
// Always inject the absolute home path so Fabiana knows where her files live
|
|
38
|
+
systemPromptContent = `Your home directory is ${FABIANA_HOME}. All your memory, config, and data files live there.\n\n${systemPromptContent}`;
|
|
39
|
+
// Inject owner name and conversation purpose into external system prompt
|
|
40
|
+
if (mode.startsWith('external-')) {
|
|
41
|
+
const identity = await fs.readFile(paths.memory('identity.md'), 'utf-8').catch(() => '');
|
|
42
|
+
const ownerNameMatch = identity.match(/(?:my name is|I am|name:\s*)([A-Z][a-z]+)/i);
|
|
43
|
+
const ownerName = ownerNameMatch ? ownerNameMatch[1] : 'the owner';
|
|
44
|
+
systemPromptContent = systemPromptContent.replace('{owner_name}', ownerName);
|
|
45
|
+
const purpose = conversationState?.purpose ?? '[purpose — provided at runtime]';
|
|
46
|
+
systemPromptContent = systemPromptContent.replace('{purpose}', purpose);
|
|
47
|
+
}
|
|
48
|
+
// Append skills section — skills live at ~/.fabiana/skills/, scoped per user
|
|
49
|
+
const skills = await loadFabianaSkills();
|
|
50
|
+
if (skills.length > 0) {
|
|
51
|
+
systemPromptContent += formatSkillsForPrompt(skills);
|
|
52
|
+
}
|
|
53
|
+
return systemPromptContent;
|
|
54
|
+
}
|
|
55
|
+
export async function runPromptPreview(mode) {
|
|
56
|
+
const PREVIEW_MODES = ['chat', 'initiative', 'consolidate', 'solitude', 'external-outreach'];
|
|
57
|
+
const modesToShow = mode
|
|
58
|
+
? [mode]
|
|
59
|
+
: PREVIEW_MODES;
|
|
60
|
+
for (const m of modesToShow) {
|
|
61
|
+
const bar = '═'.repeat(60);
|
|
62
|
+
console.log(`\n${bar}`);
|
|
63
|
+
console.log(` SYSTEM PROMPT — ${m.toUpperCase()}`);
|
|
64
|
+
console.log(bar);
|
|
65
|
+
try {
|
|
66
|
+
const prompt = await buildSystemPrompt(m);
|
|
67
|
+
console.log(prompt);
|
|
68
|
+
console.log(`\n[${prompt.length.toLocaleString()} chars]`);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
console.error(`❌ Failed to build prompt for ${m}: ${err.message}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
27
75
|
export async function runPiSession(mode, incomingMessage, channel, incomingMsg, conversationState, allChannels, conversationManager, initiativeOptions, solitudeOptions) {
|
|
28
76
|
const logger = Logger.create();
|
|
29
77
|
const sessionStartTime = Date.now();
|
|
@@ -46,29 +94,9 @@ export async function runPiSession(mode, incomingMessage, channel, incomingMsg,
|
|
|
46
94
|
}
|
|
47
95
|
console.log(' ✓ Model loaded');
|
|
48
96
|
console.log('[5/8] Loading system prompt...');
|
|
49
|
-
const
|
|
50
|
-
// Both external-outreach and external-reply share system-external.md
|
|
51
|
-
const modeKey = mode.startsWith('external-') ? 'external' : mode;
|
|
52
|
-
const modeSystemPrompt = await fs.readFile(paths.systemMd(modeKey), 'utf-8').catch(() => '');
|
|
53
|
-
let systemPromptContent = modeSystemPrompt
|
|
54
|
-
? `${baseSystemPrompt}\n\n---\n\n${modeSystemPrompt}`
|
|
55
|
-
: baseSystemPrompt;
|
|
56
|
-
// Resolve .fabiana/ references to the actual home path so Fabiana uses correct absolute paths
|
|
57
|
-
systemPromptContent = systemPromptContent.replaceAll('.fabiana/', `${FABIANA_HOME}/`);
|
|
58
|
-
// Inject owner name and conversation purpose into external system prompt
|
|
59
|
-
if (mode.startsWith('external-')) {
|
|
60
|
-
const identity = await fs.readFile(paths.memory('identity.md'), 'utf-8').catch(() => '');
|
|
61
|
-
const ownerNameMatch = identity.match(/(?:my name is|I am|name:\s*)([A-Z][a-z]+)/i);
|
|
62
|
-
const ownerName = ownerNameMatch ? ownerNameMatch[1] : 'the owner';
|
|
63
|
-
systemPromptContent = systemPromptContent.replace('{owner_name}', ownerName);
|
|
64
|
-
if (conversationState) {
|
|
65
|
-
systemPromptContent = systemPromptContent.replace('{purpose}', conversationState.purpose);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
// Append skills section — skills live at ~/.fabiana/skills/, scoped per user
|
|
97
|
+
const systemPromptContent = await buildSystemPrompt(mode, conversationState);
|
|
69
98
|
const skills = await loadFabianaSkills();
|
|
70
99
|
if (skills.length > 0) {
|
|
71
|
-
systemPromptContent += formatSkillsForPrompt(skills);
|
|
72
100
|
console.log(` Skills: ${skills.map(s => s.name).join(', ')}`);
|
|
73
101
|
}
|
|
74
102
|
const loader = new DefaultResourceLoader({
|
package/dist/tools/index.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { createSafeReadTool } from './safe-read.js';
|
|
2
|
-
import { createSafeWriteTool } from './safe-write.js';
|
|
3
|
-
import { createSafeEditTool } from './safe-edit.js';
|
|
4
2
|
import { createSendMessageTool } from './send-message.js';
|
|
5
3
|
import { createManageTodoTool } from './manage-todo.js';
|
|
6
4
|
import { createFetchUrlTool } from './fetch-url.js';
|
|
@@ -17,9 +15,6 @@ export function createFabianaTools(validator, sendMessage, opts = { toolset: 'fu
|
|
|
17
15
|
}
|
|
18
16
|
// Full toolset for owner sessions
|
|
19
17
|
const tools = [
|
|
20
|
-
createSafeReadTool(),
|
|
21
|
-
createSafeWriteTool(validator),
|
|
22
|
-
createSafeEditTool(validator),
|
|
23
18
|
createSendMessageTool(sendMessage),
|
|
24
19
|
createManageTodoTool(validator),
|
|
25
20
|
createFetchUrlTool(),
|