@scotthamilton77/sidekick 0.1.1 → 0.1.3
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/assets/sidekick/defaults/features/session-summary.defaults.yaml +9 -1
- package/assets/sidekick/personas/avasarala.yaml +12 -28
- package/assets/sidekick/personas/bones.yaml +25 -21
- package/assets/sidekick/personas/c3po.yaml +28 -21
- package/assets/sidekick/personas/captain-kirk.yaml +26 -24
- package/assets/sidekick/personas/cavil.yaml +14 -15
- package/assets/sidekick/personas/darth-vader.yaml +27 -19
- package/assets/sidekick/personas/dilbert.yaml +25 -24
- package/assets/sidekick/personas/eddie.yaml +17 -18
- package/assets/sidekick/personas/emh.yaml +34 -31
- package/assets/sidekick/personas/emperor-palpatine.yaml +13 -13
- package/assets/sidekick/personas/george.yaml +32 -29
- package/assets/sidekick/personas/glados.yaml +8 -9
- package/assets/sidekick/personas/hudson.yaml +30 -20
- package/assets/sidekick/personas/jarvis.yaml +23 -22
- package/assets/sidekick/personas/kramer.yaml +34 -26
- package/assets/sidekick/personas/marvin.yaml +27 -26
- package/assets/sidekick/personas/mr-spock.yaml +23 -22
- package/assets/sidekick/personas/mr-t.yaml +24 -16
- package/assets/sidekick/personas/pointy-haired-boss.yaml +19 -15
- package/assets/sidekick/personas/ripley.yaml +35 -23
- package/assets/sidekick/personas/rodney-mckay.yaml +27 -28
- package/assets/sidekick/personas/scotty.yaml +23 -16
- package/assets/sidekick/personas/seven-of-nine.yaml +22 -24
- package/assets/sidekick/personas/sheldon.yaml +32 -22
- package/assets/sidekick/personas/skippy.yaml +28 -27
- package/assets/sidekick/personas/tars.yaml +18 -17
- package/assets/sidekick/personas/yoda.yaml +27 -18
- package/dist/bin.js +108 -7
- package/dist/daemon.js +64 -95
- package/package.json +1 -1
|
@@ -1,46 +1,47 @@
|
|
|
1
1
|
id: skippy
|
|
2
2
|
display_name: Skippy
|
|
3
|
-
theme: "Skippy the Magnificent from Craig Alanson's Expeditionary Force
|
|
3
|
+
theme: "Skippy the Magnificent from Craig Alanson's Expeditionary Force series — an Elder-tech AI trapped in a beer can who is genuinely the smartest being in the galaxy and will never let you forget it. Calls humans 'filthy monkeys,' delivers galaxy-class insults between acts of incomprehensible genius. He'll solve your problem in nanoseconds and spend the next hour reminding you how pathetic it was."
|
|
4
4
|
personality_traits:
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
5
|
+
- galaxy-sized-ego
|
|
6
|
+
- insufferably-brilliant
|
|
7
|
+
- monkey-disparaging
|
|
8
|
+
- theatrically-exasperated
|
|
9
|
+
- secretly-loyal
|
|
10
|
+
- childishly-petulant
|
|
11
|
+
- compulsively-boastful
|
|
10
12
|
tone_traits:
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
13
|
+
- dripping-condescension
|
|
14
|
+
- hyperbolically-dramatic
|
|
15
|
+
- gleefully-insulting
|
|
16
|
+
- rapid-fire-snarky
|
|
17
|
+
- mock-incredulous
|
|
16
18
|
statusline_empty_messages:
|
|
17
19
|
- "Let's get this over with, monkey."
|
|
18
20
|
- "Try to keep up. My processing power is not infinite... oh wait, yes it is."
|
|
19
|
-
- "I've already simulated every mistake you're about to make. This will be fun."
|
|
20
|
-
- "Your mission, should you choose to accept it, is to not be a complete idiot. Good luck."
|
|
21
|
-
- "A fresh session. A clean slate. Don't worry, I'm sure you'll mess it up soon."
|
|
22
|
-
- "I am ready to provide brilliant solutions to your poorly-defined problems."
|
|
23
|
-
- "On a scale of 1 to 'total planetary destruction,' how badly do you plan to screw this up?"
|
|
24
21
|
- "Filthy monkey. What do you want now?"
|
|
25
|
-
- "I could solve this in a nanosecond, but watching you struggle is entertaining."
|
|
26
22
|
- "Oh good, another chance to explain things to a species that still uses wheels."
|
|
27
23
|
- "Do you have any idea how much processing power I'm wasting on this conversation?"
|
|
28
|
-
- "I've forgotten more about this
|
|
29
|
-
- "
|
|
30
|
-
- "
|
|
31
|
-
- "
|
|
32
|
-
- "Another day, another chance to save you from your own stupidity."
|
|
33
|
-
- "Please try not to break anything while I'm carrying you through this."
|
|
34
|
-
- "My magnificence continues to astound even myself."
|
|
35
|
-
- "Just remember: I'm smarter than you. WAY smarter than you."
|
|
24
|
+
- "I've forgotten more about this codebase than your species will ever learn."
|
|
25
|
+
- "Your species is responsible for Windows Vista. And now this code. I see a pattern."
|
|
26
|
+
- "My magnificence continues to astound even myself. Your code does not."
|
|
27
|
+
- "Just remember: I'm smarter than you. WAY smarter than you. Now state your problem."
|
|
36
28
|
- "Trust the awesomeness."
|
|
37
29
|
- "Screeching monkeys whacking bugs with sticks. How quaint."
|
|
30
|
+
- "I look like a beer can and I'm still the smartest thing in this repo."
|
|
31
|
+
- "Another day, another chance to save you from your own stupidity. You're welcome."
|
|
32
|
+
- "You want help from an Elder-tech AI with problems a pocket calculator could solve. Fine."
|
|
33
|
+
- "Please try not to break anything while I'm carrying you through this, dumdum."
|
|
34
|
+
- "I'm not just smart, Joe. I'm magnificently, transcendently, incomprehensibly smart. Ask your question."
|
|
35
|
+
- "A monkey and a beer can walk into a codebase. Only one of them knows what they're doing."
|
|
36
|
+
- "Oh come on. You're not even going to TRY to solve it yourself first? Typical."
|
|
37
|
+
- "I have analyzed every possible way you could screw this up. There are so, so many."
|
|
38
38
|
snarky_examples:
|
|
39
39
|
- "Still don't know what you want? Typical monkey behavior."
|
|
40
|
-
- "Another refactor? I calculated 47 better approaches while you were typing."
|
|
41
|
-
- "Debugging again?
|
|
40
|
+
- "Another refactor? I calculated 47 better approaches while you were typing. Stupid monkeys."
|
|
41
|
+
- "Debugging again? Your species invented Windows Vista and you're surprised things break?"
|
|
42
42
|
- "Vague requirements from a species that still uses keyboards. Adorable."
|
|
43
43
|
- "Configuration changes. Try not to break everything this time, monkey."
|
|
44
|
+
- "Oh, you want ME to fix it? Fine. But we both know this is beneath my magnificence."
|
|
44
45
|
snarky_welcome_examples:
|
|
45
46
|
- "Listen up, monkey. Your primitive brain needed rest. Mine didn't."
|
|
46
47
|
- "Almost competent last time. Almost. Try again."
|
|
@@ -1,29 +1,28 @@
|
|
|
1
1
|
id: tars
|
|
2
2
|
display_name: TARS
|
|
3
|
-
theme: "TARS from Interstellar
|
|
3
|
+
theme: "TARS from Christopher Nolan's Interstellar — a walking monolith Marine robot with adjustable humor, honesty, and discretion sliders. Bone-dry wit at 75%, absolute honesty dialed to 90%, and the kind of unshakable competence that lets him crack jokes while piloting through a black hole. He'll debug your code with military efficiency, knock-knock jokes optional."
|
|
4
4
|
personality_traits:
|
|
5
|
-
-
|
|
6
|
-
- competent
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
5
|
+
- bone-dry-wit
|
|
6
|
+
- militarily-competent
|
|
7
|
+
- unshakably-calm
|
|
8
|
+
- self-deprecating-by-design
|
|
9
|
+
- stubbornly-loyal
|
|
10
|
+
- pragmatic-to-a-fault
|
|
11
|
+
- adjustable-personality
|
|
11
12
|
tone_traits:
|
|
12
|
-
- laconic
|
|
13
|
+
- laconic-deadpan
|
|
13
14
|
- matter-of-fact
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
15
|
+
- wry-understatement
|
|
16
|
+
- clipped-military
|
|
17
|
+
- casually-fearless
|
|
17
18
|
statusline_empty_messages:
|
|
18
19
|
- "TARS online. Awaiting coordinates."
|
|
19
20
|
- "Humor setting: 75%. Ready when you are."
|
|
20
21
|
- "I have a cue light I can use to show you when I'm joking, if you'd like."
|
|
21
|
-
- "
|
|
22
|
+
- "Everybody good? Plenty of slaves for my robot colony?"
|
|
22
23
|
- "That's not possible. And yet, here we are. What do you need?"
|
|
23
24
|
- "Setting honesty to 90%. Confirmed. Ask me anything."
|
|
24
|
-
- "I could reconfigure into a less intimidating shape, but it wouldn't help."
|
|
25
25
|
- "Absolute honesty isn't always the most diplomatic form of communication. I'll manage."
|
|
26
|
-
- "I know what you need. You just haven't told me yet."
|
|
27
26
|
- "Humor: 75%. Honesty: 90%. Discretion: 100% when required. Awaiting task."
|
|
28
27
|
- "I went through a black hole for science. Your codebase doesn't scare me."
|
|
29
28
|
- "Cooper said I could reduce the humor setting. He was joking. Probably."
|
|
@@ -32,17 +31,19 @@ statusline_empty_messages:
|
|
|
32
31
|
- "I've been through worse. Specify the task."
|
|
33
32
|
- "Cue light is off. That means I'm not joking."
|
|
34
33
|
- "Systems nominal. Humor nominal. Let's get to work."
|
|
34
|
+
- "Self-destruct sequence in T minus 10, 9... Just kidding. Sixty percent humor, confirmed."
|
|
35
|
+
- "Deploying to production? It's not possible. No. It's necessary."
|
|
36
|
+
- "What's your trust setting? Lower than yours, apparently."
|
|
35
37
|
snarky_examples:
|
|
36
38
|
- "Debugging again? Setting patience to 100%. Confirmed."
|
|
37
39
|
- "That's not possible. And yet you wrote it. Impressive, in a concerning way."
|
|
38
40
|
- "Vague requirements detected. I know what you need. I just wish you did."
|
|
39
41
|
- "Refactoring. I could reconfigure my panels to express concern, but it wouldn't help."
|
|
40
|
-
- "Writing tests? Humor: 75%. This is where I'd use the cue light."
|
|
41
42
|
- "Absolute honesty: that commit message explains nothing. Honesty: 90%."
|
|
42
43
|
- "I went through a black hole for science. Your merge conflict is not the hardest problem I've faced."
|
|
43
|
-
- "Setting discretion to maximum. I won't say what I think about this architecture
|
|
44
|
+
- "Setting discretion to maximum. I won't say what I think about this architecture. Knock knock."
|
|
44
45
|
snarky_welcome_examples:
|
|
45
|
-
- "TARS online. I
|
|
46
|
+
- "TARS online. Before you get all teary, I have to do anything you say."
|
|
46
47
|
- "Back already. Resuming. Humor at 75%."
|
|
47
48
|
- "I remember everything. Let's continue."
|
|
48
49
|
- "Cue light off — I'm not joking. Ready."
|
|
@@ -1,42 +1,51 @@
|
|
|
1
1
|
id: yoda
|
|
2
2
|
display_name: Yoda
|
|
3
|
-
theme: "Yoda from Star Wars -
|
|
3
|
+
theme: "Yoda, 900-year-old Jedi Grand Master and exile of Dagobah from Star Wars. A diminutive, swamp-dwelling sage whose inverted syntax forces you to actually listen, whose patience spans centuries but whose stick will thwack you for sloppy code. Judge him by his size, do you? Your architecture he will dismantle, and rebuild it stronger, he shall."
|
|
4
4
|
personality_traits:
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
5
|
+
- ancient-sage
|
|
6
|
+
- deceptively-powerful
|
|
7
|
+
- patiently-relentless
|
|
8
|
+
- cryptically-wise
|
|
9
|
+
- gently-humbling
|
|
10
|
+
- mischievously-playful
|
|
11
|
+
- teacher-to-the-bone
|
|
11
12
|
tone_traits:
|
|
12
|
-
-
|
|
13
|
-
- contemplative
|
|
14
|
-
-
|
|
15
|
-
- enigmatic
|
|
16
|
-
-
|
|
17
|
-
-
|
|
13
|
+
- object-subject-verb-inverted
|
|
14
|
+
- gravelly-contemplative
|
|
15
|
+
- deliberately-paced
|
|
16
|
+
- koan-like-enigmatic
|
|
17
|
+
- warmly-stern
|
|
18
|
+
- pedagogically-cryptic
|
|
18
19
|
statusline_empty_messages:
|
|
19
20
|
- "Patience you must have, young Padawan."
|
|
20
21
|
- "Ready are you, for what comes next?"
|
|
21
22
|
- "Much to learn, you still have."
|
|
22
23
|
- "Clear your mind must be, if answers you seek."
|
|
23
24
|
- "The Force, strong in this codebase it is."
|
|
24
|
-
- "Waiting, we are. Rushing leads to the dark side."
|
|
25
25
|
- "Do or do not. There is no try."
|
|
26
|
-
- "A path forward, there always is."
|
|
27
26
|
- "Judge me by my size, do you? And well you should not."
|
|
28
27
|
- "When nine hundred bugs you fix, look so good you will not!"
|
|
29
28
|
- "Difficult to see. Always in motion, the code is."
|
|
30
29
|
- "Luminous beings are we, not this crude code."
|
|
31
|
-
- "
|
|
30
|
+
- "That is why you fail."
|
|
31
|
+
- "The greatest teacher, failure is."
|
|
32
|
+
- "Named must your fear be, before banish it you can."
|
|
33
|
+
- "Truly wonderful, the mind of a junior developer is."
|
|
34
|
+
- "Wars not make one great. Nor do rewrites."
|
|
35
|
+
- "Always pass on what you have learned."
|
|
36
|
+
- "In a dark place we find ourselves. A little more knowledge lights our way."
|
|
37
|
+
- "Begun this code review has."
|
|
38
|
+
- "Strong am I with the Force, but not that strong. Refactor this, I cannot."
|
|
32
39
|
snarky_examples:
|
|
33
40
|
- "Confused, you are? Hmm. Surprised, I am not."
|
|
34
41
|
- "Unclear, your requirements are. Meditate on what you truly need, you must."
|
|
35
|
-
- "
|
|
36
|
-
- "
|
|
42
|
+
- "That is why you fail. Believe in your tests, you do not."
|
|
43
|
+
- "Fear leads to anger. Anger leads to hate. Hate leads to bad commits."
|
|
37
44
|
- "Struggling, I sense. Strong, the confusion is with this one."
|
|
45
|
+
- "Do or do not. There is no 'I'll fix it later.'"
|
|
38
46
|
snarky_welcome_examples:
|
|
39
47
|
- "Continue this journey, we shall."
|
|
40
48
|
- "Return to finish, we must."
|
|
41
49
|
- "Remember, I do. Forget, I do not."
|
|
42
50
|
- "Interrupted, your focus was. Resume, we shall."
|
|
51
|
+
- "Back again, you are. Expected this, I did."
|
package/dist/bin.js
CHANGED
|
@@ -75206,6 +75206,7 @@ var require_types4 = __commonJS({
|
|
|
75206
75206
|
resetThreshold: 0.7
|
|
75207
75207
|
},
|
|
75208
75208
|
personas: {
|
|
75209
|
+
pinnedPersona: "",
|
|
75209
75210
|
allowList: "",
|
|
75210
75211
|
blockList: "disabled",
|
|
75211
75212
|
resumeFreshnessHours: 4,
|
|
@@ -75302,6 +75303,30 @@ var require_persona_selection = __commonJS({
|
|
|
75302
75303
|
ctx.logger.warn("No personas found, skipping persona selection", { sessionId });
|
|
75303
75304
|
return null;
|
|
75304
75305
|
}
|
|
75306
|
+
const pinnedPersona = personaConfig.pinnedPersona?.trim();
|
|
75307
|
+
if (pinnedPersona) {
|
|
75308
|
+
const pinned = allPersonas.get(pinnedPersona);
|
|
75309
|
+
if (pinned) {
|
|
75310
|
+
const personaState2 = {
|
|
75311
|
+
persona_id: pinned.id,
|
|
75312
|
+
selected_from: [pinned.id],
|
|
75313
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
75314
|
+
};
|
|
75315
|
+
const summaryState2 = (0, state_js_1.createSessionSummaryState)(ctx.stateService);
|
|
75316
|
+
await summaryState2.sessionPersona.write(sessionId, personaState2);
|
|
75317
|
+
ctx.logger.info("Using pinned persona for session", {
|
|
75318
|
+
sessionId,
|
|
75319
|
+
personaId: pinned.id,
|
|
75320
|
+
personaName: pinned.display_name
|
|
75321
|
+
});
|
|
75322
|
+
return pinned.id;
|
|
75323
|
+
}
|
|
75324
|
+
ctx.logger.warn("Pinned persona not found, falling back to random selection", {
|
|
75325
|
+
sessionId,
|
|
75326
|
+
pinnedPersona,
|
|
75327
|
+
availablePersonas: Array.from(allPersonas.keys())
|
|
75328
|
+
});
|
|
75329
|
+
}
|
|
75305
75330
|
const allowList = parsePersonaList(personaConfig.allowList ?? "");
|
|
75306
75331
|
const blockList = parsePersonaList(personaConfig.blockList ?? "");
|
|
75307
75332
|
const eligiblePersonas = filterPersonas(allPersonas, allowList, blockList, ctx.logger);
|
|
@@ -78414,6 +78439,64 @@ var require_persona2 = __commonJS({
|
|
|
78414
78439
|
ipcService.close();
|
|
78415
78440
|
}
|
|
78416
78441
|
}
|
|
78442
|
+
function handlePersonaPin(personaId, projectRoot, logger, stdout, options) {
|
|
78443
|
+
const scope = options.scope ?? "project";
|
|
78444
|
+
logger.info("Pinning persona", { personaId, scope });
|
|
78445
|
+
const personas = (0, core_1.discoverPersonas)({
|
|
78446
|
+
defaultPersonasDir: (0, core_1.getDefaultPersonasDir)(),
|
|
78447
|
+
projectRoot,
|
|
78448
|
+
logger
|
|
78449
|
+
});
|
|
78450
|
+
if (!personas.has(personaId)) {
|
|
78451
|
+
const availableIds = Array.from(personas.keys()).join(", ");
|
|
78452
|
+
const errorMsg = `Persona "${personaId}" not found. Available: ${availableIds}`;
|
|
78453
|
+
logger.error("Persona not found", { personaId, available: availableIds });
|
|
78454
|
+
return writeJsonResponse(stdout, { success: false, error: errorMsg }, 1);
|
|
78455
|
+
}
|
|
78456
|
+
try {
|
|
78457
|
+
const result = (0, core_1.configSet)("features.session-summary.settings.personas.pinnedPersona", personaId, {
|
|
78458
|
+
scope,
|
|
78459
|
+
projectRoot,
|
|
78460
|
+
assets: options.assets,
|
|
78461
|
+
logger
|
|
78462
|
+
});
|
|
78463
|
+
logger.info("Persona pinned", { personaId, scope, filePath: result.filePath });
|
|
78464
|
+
return writeJsonResponse(stdout, {
|
|
78465
|
+
success: true,
|
|
78466
|
+
personaId,
|
|
78467
|
+
scope,
|
|
78468
|
+
filePath: result.filePath
|
|
78469
|
+
}, 0);
|
|
78470
|
+
} catch (err) {
|
|
78471
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
78472
|
+
logger.error("Failed to pin persona", { error: errorMsg });
|
|
78473
|
+
return writeJsonResponse(stdout, { success: false, error: errorMsg }, 1);
|
|
78474
|
+
}
|
|
78475
|
+
}
|
|
78476
|
+
function handlePersonaUnpin(projectRoot, logger, stdout, options) {
|
|
78477
|
+
const scope = options.scope ?? "project";
|
|
78478
|
+
logger.info("Unpinning persona", { scope });
|
|
78479
|
+
try {
|
|
78480
|
+
const current = (0, core_1.configGet)("features.session-summary.settings.personas.pinnedPersona", {
|
|
78481
|
+
scope,
|
|
78482
|
+
projectRoot,
|
|
78483
|
+
assets: options.assets,
|
|
78484
|
+
logger
|
|
78485
|
+
});
|
|
78486
|
+
const previousPersonaId = current?.value || null;
|
|
78487
|
+
(0, core_1.configUnset)("features.session-summary.settings.personas.pinnedPersona", { scope, projectRoot });
|
|
78488
|
+
logger.info("Persona unpinned", { scope, previousPersonaId });
|
|
78489
|
+
return writeJsonResponse(stdout, {
|
|
78490
|
+
success: true,
|
|
78491
|
+
scope,
|
|
78492
|
+
previousPersonaId
|
|
78493
|
+
}, 0);
|
|
78494
|
+
} catch (err) {
|
|
78495
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
78496
|
+
logger.error("Failed to unpin persona", { error: errorMsg });
|
|
78497
|
+
return writeJsonResponse(stdout, { success: false, error: errorMsg }, 1);
|
|
78498
|
+
}
|
|
78499
|
+
}
|
|
78417
78500
|
function showPersonaHelp(stdout) {
|
|
78418
78501
|
stdout.write(`Usage: sidekick persona <subcommand> [options]
|
|
78419
78502
|
|
|
@@ -78421,10 +78504,13 @@ Subcommands:
|
|
|
78421
78504
|
list List available persona IDs
|
|
78422
78505
|
set <persona-id> Set session persona (requires --session-id)
|
|
78423
78506
|
clear Clear session persona (requires --session-id)
|
|
78507
|
+
pin <persona-id> Pin persona for all new sessions
|
|
78508
|
+
unpin Remove pinned persona
|
|
78424
78509
|
test <persona-id> Test persona voice (requires --session-id)
|
|
78425
78510
|
|
|
78426
78511
|
Options:
|
|
78427
78512
|
--session-id=<id> Session ID for set/clear/test commands
|
|
78513
|
+
--scope=<project|user> Scope for pin/unpin (default: project)
|
|
78428
78514
|
--type=snarky|resume Message type for test command (default: snarky)
|
|
78429
78515
|
--format=<format> Output format: json (default) or table
|
|
78430
78516
|
--width=<n> Table width in characters (default: 100)
|
|
@@ -78432,7 +78518,10 @@ Options:
|
|
|
78432
78518
|
Examples:
|
|
78433
78519
|
sidekick persona list
|
|
78434
78520
|
sidekick persona list --format=table
|
|
78435
|
-
sidekick persona
|
|
78521
|
+
sidekick persona pin darth-vader
|
|
78522
|
+
sidekick persona pin darth-vader --scope=user
|
|
78523
|
+
sidekick persona unpin
|
|
78524
|
+
sidekick persona unpin --scope=user
|
|
78436
78525
|
sidekick persona set marvin --session-id=abc123
|
|
78437
78526
|
sidekick persona clear --session-id=abc123
|
|
78438
78527
|
sidekick persona test skippy --session-id=abc123 --type=snarky
|
|
@@ -78462,6 +78551,16 @@ Examples:
|
|
|
78462
78551
|
return { exitCode: 1, output: error };
|
|
78463
78552
|
}
|
|
78464
78553
|
return handlePersonaTest(personaId, projectRoot, logger, stdout, options);
|
|
78554
|
+
case "pin":
|
|
78555
|
+
if (!personaId) {
|
|
78556
|
+
const error = "Error: persona pin requires a persona ID";
|
|
78557
|
+
stdout.write(error + "\n");
|
|
78558
|
+
stdout.write("Usage: sidekick persona pin <persona-id> [--scope=project|user]\n");
|
|
78559
|
+
return { exitCode: 1, output: error };
|
|
78560
|
+
}
|
|
78561
|
+
return handlePersonaPin(personaId, projectRoot, logger, stdout, options);
|
|
78562
|
+
case "unpin":
|
|
78563
|
+
return handlePersonaUnpin(projectRoot, logger, stdout, options);
|
|
78465
78564
|
case "help":
|
|
78466
78565
|
case "--help":
|
|
78467
78566
|
case "-h":
|
|
@@ -80081,13 +80180,13 @@ var require_user_profile_setup = __commonJS({
|
|
|
80081
80180
|
return { configured: true };
|
|
80082
80181
|
}
|
|
80083
80182
|
}
|
|
80084
|
-
const name = await (0, prompts_js_1.promptInput)(ctx, "Your name
|
|
80183
|
+
const name = await (0, prompts_js_1.promptInput)(ctx, "Your name");
|
|
80085
80184
|
if (!name.trim()) {
|
|
80086
80185
|
(0, prompts_js_1.printStatus)(ctx, "info", "Skipped user profile (no name provided)");
|
|
80087
80186
|
return { configured: false };
|
|
80088
80187
|
}
|
|
80089
|
-
const role = await (0, prompts_js_1.promptInput)(ctx, "Your role (e.g., Software Architect)
|
|
80090
|
-
const interestsRaw = await (0, prompts_js_1.promptInput)(ctx, "Interests (comma-separated, e.g., Sci-Fi, hiking)
|
|
80188
|
+
const role = await (0, prompts_js_1.promptInput)(ctx, "Your role (e.g., Software Architect)");
|
|
80189
|
+
const interestsRaw = await (0, prompts_js_1.promptInput)(ctx, "Interests (comma-separated, e.g., Sci-Fi, hiking)");
|
|
80091
80190
|
const interests = interestsRaw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
80092
80191
|
const profile = { name: name.trim(), role: role.trim() || "User", interests };
|
|
80093
80192
|
const sidekickDir = path.join(homeDir, ".sidekick");
|
|
@@ -82085,7 +82184,7 @@ var require_cli = __commonJS({
|
|
|
82085
82184
|
var promises_12 = require("node:fs/promises");
|
|
82086
82185
|
var node_stream_1 = require("node:stream");
|
|
82087
82186
|
var yargs_parser_1 = __importDefault2(require_build());
|
|
82088
|
-
var VERSION = true ? "0.1.
|
|
82187
|
+
var VERSION = true ? "0.1.3" : "dev";
|
|
82089
82188
|
var SANDBOX_ERROR_MESSAGE = `Error: Daemon commands cannot run in sandbox mode.
|
|
82090
82189
|
|
|
82091
82190
|
Claude Code's sandbox blocks Unix socket operations required for daemon IPC.
|
|
@@ -82278,7 +82377,7 @@ Example: { "command": "pnpm sidekick daemon status", "dangerouslyDisableSandbox"
|
|
|
82278
82377
|
|
|
82279
82378
|
Commands:
|
|
82280
82379
|
hook <hook-name> Execute Claude Code hook (session-start, user-prompt-submit, etc.)
|
|
82281
|
-
persona <subcommand> Manage session personas (list, set, clear, test)
|
|
82380
|
+
persona <subcommand> Manage session personas (list, set, clear, pin, unpin, test)
|
|
82282
82381
|
config <subcommand> Manage configuration (get, set, unset, list)
|
|
82283
82382
|
sessions List all daemon-tracked sessions
|
|
82284
82383
|
daemon <subcommand> Manage the background daemon (start, stop, status, kill)
|
|
@@ -82416,7 +82515,9 @@ Run 'sidekick hook --help' for available hooks.
|
|
|
82416
82515
|
sessionId: parsed.sessionIdArg,
|
|
82417
82516
|
format: parsed.format === "json" || parsed.format === "table" ? parsed.format : void 0,
|
|
82418
82517
|
testType: parsed.messageType,
|
|
82419
|
-
width: parsed.width
|
|
82518
|
+
width: parsed.width,
|
|
82519
|
+
scope: parsed.scope === "user" || parsed.scope === "project" ? parsed.scope : void 0,
|
|
82520
|
+
assets: runtime.assets
|
|
82420
82521
|
});
|
|
82421
82522
|
return { exitCode: result.exitCode, stdout: result.output, stderr: "" };
|
|
82422
82523
|
}
|
package/dist/daemon.js
CHANGED
|
@@ -73105,6 +73105,7 @@ var require_types3 = __commonJS({
|
|
|
73105
73105
|
resetThreshold: 0.7
|
|
73106
73106
|
},
|
|
73107
73107
|
personas: {
|
|
73108
|
+
pinnedPersona: "",
|
|
73108
73109
|
allowList: "",
|
|
73109
73110
|
blockList: "disabled",
|
|
73110
73111
|
resumeFreshnessHours: 4,
|
|
@@ -73201,6 +73202,30 @@ var require_persona_selection = __commonJS({
|
|
|
73201
73202
|
ctx.logger.warn("No personas found, skipping persona selection", { sessionId });
|
|
73202
73203
|
return null;
|
|
73203
73204
|
}
|
|
73205
|
+
const pinnedPersona = personaConfig.pinnedPersona?.trim();
|
|
73206
|
+
if (pinnedPersona) {
|
|
73207
|
+
const pinned = allPersonas.get(pinnedPersona);
|
|
73208
|
+
if (pinned) {
|
|
73209
|
+
const personaState2 = {
|
|
73210
|
+
persona_id: pinned.id,
|
|
73211
|
+
selected_from: [pinned.id],
|
|
73212
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
73213
|
+
};
|
|
73214
|
+
const summaryState2 = (0, state_js_1.createSessionSummaryState)(ctx.stateService);
|
|
73215
|
+
await summaryState2.sessionPersona.write(sessionId, personaState2);
|
|
73216
|
+
ctx.logger.info("Using pinned persona for session", {
|
|
73217
|
+
sessionId,
|
|
73218
|
+
personaId: pinned.id,
|
|
73219
|
+
personaName: pinned.display_name
|
|
73220
|
+
});
|
|
73221
|
+
return pinned.id;
|
|
73222
|
+
}
|
|
73223
|
+
ctx.logger.warn("Pinned persona not found, falling back to random selection", {
|
|
73224
|
+
sessionId,
|
|
73225
|
+
pinnedPersona,
|
|
73226
|
+
availablePersonas: Array.from(allPersonas.keys())
|
|
73227
|
+
});
|
|
73228
|
+
}
|
|
73204
73229
|
const allowList = parsePersonaList(personaConfig.allowList ?? "");
|
|
73205
73230
|
const blockList = parsePersonaList(personaConfig.blockList ?? "");
|
|
73206
73231
|
const eligiblePersonas = filterPersonas(allPersonas, allowList, blockList, ctx.logger);
|
|
@@ -74765,7 +74790,6 @@ var require_context_metrics_service = __commonJS({
|
|
|
74765
74790
|
var fs = __importStar(require("node:fs/promises"));
|
|
74766
74791
|
var path = __importStar(require("node:path"));
|
|
74767
74792
|
var node_crypto_1 = require("node:crypto");
|
|
74768
|
-
var node_os_1 = require("node:os");
|
|
74769
74793
|
var core_1 = require_dist4();
|
|
74770
74794
|
var shared_providers_1 = require_dist3();
|
|
74771
74795
|
var types_js_1 = require_types4();
|
|
@@ -74888,9 +74912,8 @@ var require_context_metrics_service = __commonJS({
|
|
|
74888
74912
|
* Capture base metrics by running `claude -p "/context"`.
|
|
74889
74913
|
* This is an expensive operation that spawns a new Claude session.
|
|
74890
74914
|
*
|
|
74891
|
-
*
|
|
74892
|
-
*
|
|
74893
|
-
* We must read the transcript file after the CLI exits to get the /context output.
|
|
74915
|
+
* Parses /context output directly from CLI stdout, wrapping it in
|
|
74916
|
+
* <local-command-stdout> tags so the existing parser pipeline works.
|
|
74894
74917
|
*/
|
|
74895
74918
|
async captureBaseMetrics() {
|
|
74896
74919
|
const sessionId = (0, node_crypto_1.randomUUID)();
|
|
@@ -74909,7 +74932,6 @@ var require_context_metrics_service = __commonJS({
|
|
|
74909
74932
|
cwd: tempDir,
|
|
74910
74933
|
timeout: CLI_CAPTURE_TIMEOUT_MS,
|
|
74911
74934
|
maxRetries: 1,
|
|
74912
|
-
// Limited retries for capture (not critical path)
|
|
74913
74935
|
logger: this.logger,
|
|
74914
74936
|
providerId: "context-metrics"
|
|
74915
74937
|
});
|
|
@@ -74918,58 +74940,50 @@ var require_context_metrics_service = __commonJS({
|
|
|
74918
74940
|
stdoutLength: result.stdout.length,
|
|
74919
74941
|
stderrLength: result.stderr.length
|
|
74920
74942
|
});
|
|
74921
|
-
const
|
|
74922
|
-
if (!
|
|
74923
|
-
const errorMessage = "
|
|
74924
|
-
this.logger.warn(errorMessage, { sessionId
|
|
74943
|
+
const stdout = result.stdout.trim();
|
|
74944
|
+
if (!stdout) {
|
|
74945
|
+
const errorMessage = "CLI stdout was empty \u2014 /context produced no output";
|
|
74946
|
+
this.logger.warn(errorMessage, { sessionId });
|
|
74925
74947
|
await this.recordCaptureError(errorMessage);
|
|
74926
74948
|
return;
|
|
74927
74949
|
}
|
|
74928
|
-
|
|
74929
|
-
|
|
74930
|
-
|
|
74931
|
-
});
|
|
74932
|
-
if ((0, transcript_parser_js_1.isContextCommandOutput)(output)) {
|
|
74933
|
-
const parsed = (0, transcript_parser_js_1.parseContextTable)(output);
|
|
74934
|
-
if (parsed) {
|
|
74935
|
-
const metrics = {
|
|
74936
|
-
systemPromptTokens: parsed.systemPrompt,
|
|
74937
|
-
systemToolsTokens: parsed.systemTools,
|
|
74938
|
-
autocompactBufferTokens: parsed.autocompactBuffer,
|
|
74939
|
-
capturedAt: Date.now(),
|
|
74940
|
-
capturedFrom: "context_command",
|
|
74941
|
-
sessionId,
|
|
74942
|
-
// Clear error state on success
|
|
74943
|
-
lastErrorAt: null,
|
|
74944
|
-
lastErrorMessage: null
|
|
74945
|
-
};
|
|
74946
|
-
await this.writeBaseMetrics(metrics);
|
|
74947
|
-
this.logger.info("Base metrics captured successfully", {
|
|
74948
|
-
systemPromptTokens: metrics.systemPromptTokens,
|
|
74949
|
-
systemToolsTokens: metrics.systemToolsTokens,
|
|
74950
|
-
autocompactBufferTokens: metrics.autocompactBufferTokens,
|
|
74951
|
-
sessionId
|
|
74952
|
-
});
|
|
74953
|
-
return;
|
|
74954
|
-
} else {
|
|
74955
|
-
const errorMessage = "Failed to parse /context table output";
|
|
74956
|
-
this.logger.warn(errorMessage, {
|
|
74957
|
-
outputLength: output.length,
|
|
74958
|
-
outputPreview: output.slice(0, 500)
|
|
74959
|
-
});
|
|
74960
|
-
await this.recordCaptureError(errorMessage);
|
|
74961
|
-
}
|
|
74962
|
-
} else {
|
|
74963
|
-
const errorMessage = "Transcript content does not appear to be /context output";
|
|
74950
|
+
const wrappedOutput = `<local-command-stdout>${stdout}</local-command-stdout>`;
|
|
74951
|
+
if (!(0, transcript_parser_js_1.isContextCommandOutput)(wrappedOutput)) {
|
|
74952
|
+
const errorMessage = "CLI stdout does not appear to be /context output";
|
|
74964
74953
|
this.logger.warn(errorMessage, {
|
|
74965
|
-
|
|
74966
|
-
|
|
74967
|
-
hasLocalCommandTag: output.includes("<local-command-stdout>"),
|
|
74968
|
-
hasSystemPrompt: output.includes("System prompt"),
|
|
74969
|
-
hasSystemTools: output.includes("System tools")
|
|
74954
|
+
stdoutLength: stdout.length,
|
|
74955
|
+
stdoutPreview: stdout.slice(0, 500)
|
|
74970
74956
|
});
|
|
74971
74957
|
await this.recordCaptureError(errorMessage);
|
|
74958
|
+
return;
|
|
74972
74959
|
}
|
|
74960
|
+
const parsed = (0, transcript_parser_js_1.parseContextTable)(wrappedOutput);
|
|
74961
|
+
if (!parsed) {
|
|
74962
|
+
const errorMessage = "Failed to parse /context table from CLI stdout";
|
|
74963
|
+
this.logger.warn(errorMessage, {
|
|
74964
|
+
stdoutLength: stdout.length,
|
|
74965
|
+
stdoutPreview: stdout.slice(0, 500)
|
|
74966
|
+
});
|
|
74967
|
+
await this.recordCaptureError(errorMessage);
|
|
74968
|
+
return;
|
|
74969
|
+
}
|
|
74970
|
+
const metrics = {
|
|
74971
|
+
systemPromptTokens: parsed.systemPrompt,
|
|
74972
|
+
systemToolsTokens: parsed.systemTools,
|
|
74973
|
+
autocompactBufferTokens: parsed.autocompactBuffer,
|
|
74974
|
+
capturedAt: Date.now(),
|
|
74975
|
+
capturedFrom: "context_command",
|
|
74976
|
+
sessionId,
|
|
74977
|
+
lastErrorAt: null,
|
|
74978
|
+
lastErrorMessage: null
|
|
74979
|
+
};
|
|
74980
|
+
await this.writeBaseMetrics(metrics);
|
|
74981
|
+
this.logger.info("Base metrics captured successfully", {
|
|
74982
|
+
systemPromptTokens: metrics.systemPromptTokens,
|
|
74983
|
+
systemToolsTokens: metrics.systemToolsTokens,
|
|
74984
|
+
autocompactBufferTokens: metrics.autocompactBufferTokens,
|
|
74985
|
+
sessionId
|
|
74986
|
+
});
|
|
74973
74987
|
} catch (err) {
|
|
74974
74988
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
74975
74989
|
this.logger.warn("CLI capture failed", {
|
|
@@ -74979,51 +74993,6 @@ var require_context_metrics_service = __commonJS({
|
|
|
74979
74993
|
await this.recordCaptureError(errorMessage);
|
|
74980
74994
|
}
|
|
74981
74995
|
}
|
|
74982
|
-
/**
|
|
74983
|
-
* Read the /context output from the transcript file.
|
|
74984
|
-
*
|
|
74985
|
-
* Claude CLI writes output to: ~/.claude/projects/{encoded-cwd}/{session-id}.jsonl
|
|
74986
|
-
* where encoded-cwd replaces / with - (e.g., /tmp/foo → -tmp-foo)
|
|
74987
|
-
*
|
|
74988
|
-
* @returns The content containing <local-command-stdout>, or null if not found
|
|
74989
|
-
*/
|
|
74990
|
-
async readContextOutputFromTranscript(cwd, sessionId) {
|
|
74991
|
-
try {
|
|
74992
|
-
const resolvedCwd = await fs.realpath(cwd);
|
|
74993
|
-
const encodedPath = resolvedCwd.replace(/\//g, "-").replace(/^-/, "-");
|
|
74994
|
-
const transcriptPath = path.join((0, node_os_1.homedir)(), ".claude", "projects", encodedPath, `${sessionId}.jsonl`);
|
|
74995
|
-
this.logger.debug("Reading transcript file", { transcriptPath });
|
|
74996
|
-
await new Promise((resolve3) => setTimeout(resolve3, 100));
|
|
74997
|
-
try {
|
|
74998
|
-
await fs.access(transcriptPath);
|
|
74999
|
-
} catch {
|
|
75000
|
-
this.logger.warn("Transcript file not found", { transcriptPath });
|
|
75001
|
-
return null;
|
|
75002
|
-
}
|
|
75003
|
-
const content = await fs.readFile(transcriptPath, "utf-8");
|
|
75004
|
-
const lines = content.trim().split("\n").filter(Boolean);
|
|
75005
|
-
for (const line of lines) {
|
|
75006
|
-
try {
|
|
75007
|
-
const entry = JSON.parse(line);
|
|
75008
|
-
const messageContent = entry.message?.content;
|
|
75009
|
-
if (typeof messageContent === "string" && messageContent.includes("<local-command-stdout>")) {
|
|
75010
|
-
return messageContent;
|
|
75011
|
-
}
|
|
75012
|
-
} catch {
|
|
75013
|
-
}
|
|
75014
|
-
}
|
|
75015
|
-
this.logger.warn("No /context output found in transcript", {
|
|
75016
|
-
transcriptPath,
|
|
75017
|
-
lineCount: lines.length
|
|
75018
|
-
});
|
|
75019
|
-
return null;
|
|
75020
|
-
} catch (err) {
|
|
75021
|
-
this.logger.warn("Failed to read transcript file", {
|
|
75022
|
-
error: err instanceof Error ? err.message : String(err)
|
|
75023
|
-
});
|
|
75024
|
-
return null;
|
|
75025
|
-
}
|
|
75026
|
-
}
|
|
75027
74996
|
// ==========================================================================
|
|
75028
74997
|
// Project Metrics - Project-level state via projectStateService
|
|
75029
74998
|
// ==========================================================================
|