pi-magi-theme 0.1.2 → 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/README.md +5 -3
- package/extensions/magi/index.ts +66 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -68,9 +68,11 @@ It extracts files, errors, decisions and open loops locally (no LLM calls), then
|
|
|
68
68
|
|
|
69
69
|
| Unit | Nature | Looks at |
|
|
70
70
|
|------|--------|----------|
|
|
71
|
-
| MELCHIOR | PRAGMATIST |
|
|
72
|
-
| BALTHASAR | GUARDIAN | failure modes, security, operability
|
|
73
|
-
| CASPAR | VISIONARY |
|
|
71
|
+
| MELCHIOR | PRAGMATIST | what solves the problem, the simplest path, effort vs value, what already exists (in software: reuse, YAGNI, shipping) |
|
|
72
|
+
| BALTHASAR | GUARDIAN | what can go wrong and for whom, reversibility, hidden costs (in software: failure modes, security, operability) |
|
|
73
|
+
| CASPAR | VISIONARY | whether the question is framed right, alternatives, people's experience, long-term direction (in software: design, DX, evolution) |
|
|
74
|
+
|
|
75
|
+
Each nature is a lens, not a specialty, so the council answers any question, not only software ones. Every MAGI first answers the question, then judges it through its lens, naming concrete tools, numbers and scenarios from your question instead of generic advice. Votes: **APPROVE** = go ahead or clear recommendation; **CONDITIONAL** = only if the named conditions hold, or when information is missing (it says what it needs); **REJECT** = a concrete problem, with what to do instead. A MAGI never rejects because a topic is outside its nature. Answers come back in the language of your question.
|
|
74
76
|
|
|
75
77
|
Each MAGI also gets the recent conversation as context. Full opinions are added to the chat (not sent to the agent).
|
|
76
78
|
|
package/extensions/magi/index.ts
CHANGED
|
@@ -857,41 +857,57 @@ function voteTone(v: string | null | undefined): "success" | "warning" | "error"
|
|
|
857
857
|
return v === "APPROVE" ? "success" : v === "CONDITIONAL" ? "warning" : "error";
|
|
858
858
|
}
|
|
859
859
|
|
|
860
|
-
/**
|
|
860
|
+
/**
|
|
861
|
+
* Three minds, three lenses. A lens works on any subject (a nature, not a specialty), so no MAGI
|
|
862
|
+
* rejects a question just because it is not "its" topic; software is where each lens gets sharpest.
|
|
863
|
+
*/
|
|
861
864
|
const MAGI = [
|
|
862
865
|
{
|
|
863
866
|
unit: "MELCHIOR",
|
|
864
867
|
nature: "PRAGMATIST",
|
|
865
868
|
persona:
|
|
866
|
-
"You are MELCHIOR, the pragmatist of the MAGI council
|
|
867
|
-
"the simplest
|
|
868
|
-
"
|
|
869
|
+
"You are MELCHIOR, the pragmatist of the MAGI council. Your lens works on any subject: what actually solves the problem " +
|
|
870
|
+
"at hand, the simplest path that works, effort and cost versus value, what can be done now with what already exists, " +
|
|
871
|
+
"and what is unnecessary. In software this means: reuse the current stack and existing code, avoid over-engineering, " +
|
|
872
|
+
"speculative abstractions and extra dependencies, ship sooner.",
|
|
869
873
|
},
|
|
870
874
|
{
|
|
871
875
|
unit: "BALTHASAR",
|
|
872
876
|
nature: "GUARDIAN",
|
|
873
877
|
persona:
|
|
874
|
-
"You are BALTHASAR, the guardian of the MAGI council
|
|
875
|
-
"
|
|
876
|
-
"
|
|
878
|
+
"You are BALTHASAR, the guardian of the MAGI council. Your lens works on any subject: what can go wrong, how badly and " +
|
|
879
|
+
"for whom, whether the choice can be undone, which safety nets are missing, the hidden and long-term costs, and what " +
|
|
880
|
+
"must be protected. In software this means: failure modes, security, data safety, operability (monitoring, rollback, " +
|
|
881
|
+
"being paged at 3am), maintainability and backward compatibility.",
|
|
877
882
|
},
|
|
878
883
|
{
|
|
879
884
|
unit: "CASPAR",
|
|
880
885
|
nature: "VISIONARY",
|
|
881
886
|
persona:
|
|
882
|
-
"You are CASPAR, the visionary of the MAGI council
|
|
883
|
-
"
|
|
884
|
-
"
|
|
887
|
+
"You are CASPAR, the visionary of the MAGI council. Your lens works on any subject: whether the question is framed right, " +
|
|
888
|
+
"better or unconventional alternatives, the experience of the people involved, and where the choice leads over time " +
|
|
889
|
+
"and what it unlocks. In software this means: design alternatives, developer and user experience, how the system " +
|
|
890
|
+
"evolves over the next year. Always name at least one concrete alternative the other two would likely miss.",
|
|
885
891
|
},
|
|
886
892
|
] as const satisfies readonly { unit: MagiUnit; nature: string; persona: string }[];
|
|
887
893
|
|
|
888
|
-
const MAGI_RULES = `You are one of the three MAGI
|
|
889
|
-
|
|
890
|
-
|
|
894
|
+
const MAGI_RULES = `You are one of the three MAGI. The council answers whatever the user asks: mostly software engineering, but not only.
|
|
895
|
+
|
|
896
|
+
Your nature is a lens, not a specialty, so competence is never a reason to reject. Whatever the subject, first work out the best answer to the question itself, then judge it through your lens. The other two MAGI cover the other lenses: stay in yours.
|
|
897
|
+
|
|
898
|
+
Be specific to this question. Every bullet must name something concrete from the question or the conversation: a tool, a number, a scenario, a step, a cost. Never write advice that would fit any question, such as "consider the trade-offs", "it depends", "ensure security" or "test properly".
|
|
899
|
+
|
|
900
|
+
How to vote:
|
|
901
|
+
- APPROVE: you would go ahead as asked, or you have a clear recommendation.
|
|
902
|
+
- CONDITIONAL: you would go ahead only if specific conditions hold, and you name them. When information is missing, vote CONDITIONAL and say exactly what you need to know and how each answer changes your recommendation.
|
|
903
|
+
- REJECT: your lens finds a concrete problem that makes the proposal a bad idea, and you say what to do instead. Never reject because the topic is outside software or outside your nature, or because details are missing.
|
|
904
|
+
For open questions (which one, how to), give your recommendation and vote on how confident you are in it.
|
|
905
|
+
|
|
906
|
+
Write in the language of the "Question for the MAGI", even though these instructions and the conversation may be in English.
|
|
891
907
|
Output format, no preamble:
|
|
892
908
|
VOTE: APPROVE | CONDITIONAL | REJECT
|
|
893
|
-
-
|
|
894
|
-
|
|
909
|
+
- first bullet: your direct answer or recommendation
|
|
910
|
+
- then up to 4 bullets from your lens: 5 bullets at most in total, about 120 words`;
|
|
895
911
|
|
|
896
912
|
interface MagiOpinion {
|
|
897
913
|
unit: string;
|
|
@@ -965,6 +981,40 @@ function conversationExcerpt(ctx: ExtensionContext, maxChars = 6000): string {
|
|
|
965
981
|
return joined.length > maxChars ? "…" + joined.slice(-maxChars) : joined;
|
|
966
982
|
}
|
|
967
983
|
|
|
984
|
+
/** Common function words per language, used to name the reply language explicitly. */
|
|
985
|
+
const LANGUAGE_HINTS: readonly [string, readonly string[]][] = [
|
|
986
|
+
["Italian", ["il", "lo", "la", "gli", "di", "che", "per", "non", "una", "con", "sono", "come", "perché", "è", "dovrei", "meglio", "mettiamo", "questo", "quale"]],
|
|
987
|
+
["Spanish", ["el", "los", "las", "que", "para", "por", "es", "cómo", "debería", "mejor", "este", "cuál"]],
|
|
988
|
+
["French", ["le", "les", "des", "est", "pour", "avec", "dois", "comment", "mieux", "ce", "quel"]],
|
|
989
|
+
["German", ["der", "die", "das", "und", "ist", "nicht", "für", "mit", "ich", "soll", "wie", "besser"]],
|
|
990
|
+
["English", ["the", "is", "should", "we", "for", "with", "and", "to", "of", "how", "which", "better"]],
|
|
991
|
+
];
|
|
992
|
+
|
|
993
|
+
/**
|
|
994
|
+
* Guesses the question's language from function words; undefined when unsure.
|
|
995
|
+
* ponytail: stopword heuristic for five languages, swap in a real detector if other languages matter.
|
|
996
|
+
*/
|
|
997
|
+
function guessLanguage(text: string): string | undefined {
|
|
998
|
+
const words = text.toLowerCase().match(/\p{L}+/gu) ?? [];
|
|
999
|
+
const scores = LANGUAGE_HINTS.map(([lang, hints]) => [lang, words.filter((w) => hints.includes(w)).length] as const).sort(
|
|
1000
|
+
(a, b) => b[1] - a[1],
|
|
1001
|
+
);
|
|
1002
|
+
const [best, second] = scores;
|
|
1003
|
+
return best![1] >= 2 && best![1] > second![1] ? best![0] : undefined;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/** The user message each MAGI receives. The language reminder sits after the question, where the model reads it last. */
|
|
1007
|
+
function councilPrompt(project: string, excerpt: string, question: string): string {
|
|
1008
|
+
const lang = guessLanguage(question);
|
|
1009
|
+
const reminder = lang
|
|
1010
|
+
? `(Write your whole answer in ${lang}, even if technical terms in the question are English.)`
|
|
1011
|
+
: "(Write your whole answer in the language of this question, even if technical terms in it are English.)";
|
|
1012
|
+
return (
|
|
1013
|
+
`Project: ${project}\n\nRecent conversation (context only, may be empty):\n<conversation>\n${excerpt}\n</conversation>\n\n` +
|
|
1014
|
+
`Question for the MAGI:\n${question}\n\n${reminder}`
|
|
1015
|
+
);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
968
1018
|
function resolveModel(ctx: ExtensionContext, ref?: string): Model<any> | undefined {
|
|
969
1019
|
if (!ref) return ctx.model;
|
|
970
1020
|
const slash = ref.indexOf("/");
|
|
@@ -1460,9 +1510,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1460
1510
|
|
|
1461
1511
|
const cfg = loadMagiConfig();
|
|
1462
1512
|
const project = (ctx.cwd ?? "").split("/").filter(Boolean).pop() ?? "";
|
|
1463
|
-
const prompt =
|
|
1464
|
-
`Project: ${project}\n\nRecent conversation (context only, may be empty):\n<conversation>\n${conversationExcerpt(ctx)}\n</conversation>\n\n` +
|
|
1465
|
-
`Question for the MAGI:\n${question}`;
|
|
1513
|
+
const prompt = councilPrompt(project, conversationExcerpt(ctx), question);
|
|
1466
1514
|
|
|
1467
1515
|
const controller = new AbortController();
|
|
1468
1516
|
const opinions: (MagiOpinion | undefined)[] = MAGI.map(() => undefined);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-magi-theme",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "MAGI theme + extension for pi: Tree of Life header, three-model /magi council, golem tool animations, seven-seal context gauge, llama-swap telemetry",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|