@nosserb/prmt 0.0.0-stage → 2.0.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.
Files changed (166) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +191 -2
  3. package/bin/prmt.mjs +174 -0
  4. package/dist/assets/AuditModule-BOO_iRy8.js +1 -0
  5. package/dist/assets/Audits-yUBm0EpW.js +1 -0
  6. package/dist/assets/BrainPage-FIWO_9Nc.js +3 -0
  7. package/dist/assets/BrainPage-XQCEGRc5.css +1 -0
  8. package/dist/assets/Carte-CPuiR1Ha.css +1 -0
  9. package/dist/assets/Carte-xycu1mQn.js +1 -0
  10. package/dist/assets/Dashboard-DKvCQcX8.js +2 -0
  11. package/dist/assets/Dashboard-XdhC6_hM.css +1 -0
  12. package/dist/assets/Field-C7c2mCbb.js +2 -0
  13. package/dist/assets/Journal-BLIIlh80.css +1 -0
  14. package/dist/assets/Journal-BaelFjwV.js +1 -0
  15. package/dist/assets/Landing-B2jxV3zs.css +1 -0
  16. package/dist/assets/Landing-Dr612Zdu.js +26 -0
  17. package/dist/assets/Register-DA_Aq5f8.js +1 -0
  18. package/dist/assets/Reports-8wfDNSxH.js +1 -0
  19. package/dist/assets/Reports-B8GXN95N.css +1 -0
  20. package/dist/assets/SavoirPage-BTxbH3LY.css +1 -0
  21. package/dist/assets/SavoirPage-Dp3eUCHp.js +1 -0
  22. package/dist/assets/SettingsPage-BElIP46c.css +1 -0
  23. package/dist/assets/SettingsPage-BiQbnw3o.js +1 -0
  24. package/dist/assets/Shell-BAyHVljp.css +1 -0
  25. package/dist/assets/Shell-CQRlx9-V.js +2 -0
  26. package/dist/assets/Table-DUmGel5e.js +1 -0
  27. package/dist/assets/Tabs-CfhbELZk.js +1 -0
  28. package/dist/assets/Terminal-Bv1IJfi9.js +3 -0
  29. package/dist/assets/Terminal-CEfj38NV.css +1 -0
  30. package/dist/assets/arrow-right-Cx3pAEhl.js +1 -0
  31. package/dist/assets/auditViews-BMza2Sdz.js +1 -0
  32. package/dist/assets/auditViews-BePWj26h.css +1 -0
  33. package/dist/assets/bricolage-grotesque-latin-ext-opsz-normal-IcJDqblK.woff2 +0 -0
  34. package/dist/assets/bricolage-grotesque-latin-opsz-normal-Cre6nC2_.woff2 +0 -0
  35. package/dist/assets/bricolage-grotesque-vietnamese-opsz-normal-D9N6E8K1.woff2 +0 -0
  36. package/dist/assets/charts-BcijSJ4J.js +1 -0
  37. package/dist/assets/circle-stop-C33BAcKW.js +1 -0
  38. package/dist/assets/command-ChA49Xk7.js +1 -0
  39. package/dist/assets/command-Ck4pdQCv.css +1 -0
  40. package/dist/assets/download-CeuKOenD.js +1 -0
  41. package/dist/assets/eye-CXXHBBpr.js +1 -0
  42. package/dist/assets/geist-cyrillic-ext-wght-normal-DjL33-gN.woff2 +0 -0
  43. package/dist/assets/geist-cyrillic-wght-normal-BEAKL7Jp.woff2 +0 -0
  44. package/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
  45. package/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
  46. package/dist/assets/geist-vietnamese-wght-normal-6IgcOCM7.woff2 +0 -0
  47. package/dist/assets/habillage-B5mMBadn.js +1 -0
  48. package/dist/assets/index-BH1etemR.css +1 -0
  49. package/dist/assets/index-COSiM-R9.js +14 -0
  50. package/dist/assets/jetbrains-mono-cyrillic-ext-wght-normal-EocZY2iu.woff2 +0 -0
  51. package/dist/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  52. package/dist/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  53. package/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  54. package/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  55. package/dist/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  56. package/dist/assets/layers-2x-Bpkbi35X.png +0 -0
  57. package/dist/assets/layers-BWBAp2CZ.png +0 -0
  58. package/dist/assets/markdown-DF87LQh1.css +1 -0
  59. package/dist/assets/markdown-o5sMhPHN.js +4 -0
  60. package/dist/assets/marker-icon-hN30_KVU.png +0 -0
  61. package/dist/assets/play-Daw4K_73.js +1 -0
  62. package/dist/assets/plus-D_QHm8ud.js +1 -0
  63. package/dist/assets/trash-DLpHM-s8.js +1 -0
  64. package/dist/assets/web-6j1-aY67.js +1 -0
  65. package/dist/favicon.svg +1 -0
  66. package/dist/index.html +18 -0
  67. package/docs/ANALYSE.md +87 -0
  68. package/docs/SECURITE.md +188 -0
  69. package/lib/scripts/lier.js +15 -0
  70. package/lib/scripts/searxng.js +91 -0
  71. package/lib/server/app.js +444 -0
  72. package/lib/server/audits/modules/local.js +429 -0
  73. package/lib/server/audits/modules/network.js +331 -0
  74. package/lib/server/audits/modules/web.js +302 -0
  75. package/lib/server/audits/net.js +141 -0
  76. package/lib/server/audits/registry.js +202 -0
  77. package/lib/server/audits/types.js +58 -0
  78. package/lib/server/brain/brain.js +624 -0
  79. package/lib/server/brain/conversations.js +82 -0
  80. package/lib/server/brain/ollama.js +197 -0
  81. package/lib/server/brain/prompt.js +188 -0
  82. package/lib/server/brain/provider.js +1 -0
  83. package/lib/server/cli/bridge.js +292 -0
  84. package/lib/server/cli/pont.js +62 -0
  85. package/lib/server/config.js +68 -0
  86. package/lib/server/engine/analyzer.js +205 -0
  87. package/lib/server/engine/catalog.js +229 -0
  88. package/lib/server/engine/decoder.js +91 -0
  89. package/lib/server/engine/engine.js +194 -0
  90. package/lib/server/engine/paths.js +129 -0
  91. package/lib/server/engine/policy.js +397 -0
  92. package/lib/server/engine/relax.js +10 -0
  93. package/lib/server/engine/runner.js +193 -0
  94. package/lib/server/errors.js +23 -0
  95. package/lib/server/http/guard.js +75 -0
  96. package/lib/server/http/router.js +154 -0
  97. package/lib/server/http/static.js +76 -0
  98. package/lib/server/index.js +40 -0
  99. package/lib/server/journal.js +139 -0
  100. package/lib/server/lien.js +33 -0
  101. package/lib/server/savoir/base.js +284 -0
  102. package/lib/server/savoir/lecture.js +406 -0
  103. package/lib/server/savoir/memoire.js +115 -0
  104. package/lib/server/search/categories.js +179 -0
  105. package/lib/server/search/fiche.js +326 -0
  106. package/lib/server/search/lieux.js +134 -0
  107. package/lib/server/search/overpass.js +216 -0
  108. package/lib/server/search/searxng.js +223 -0
  109. package/lib/server/search/tiles.js +103 -0
  110. package/lib/server/status.js +157 -0
  111. package/lib/server/stores.js +141 -0
  112. package/lib/server/telephone/acces.js +457 -0
  113. package/lib/server/telephone/certificat.js +79 -0
  114. package/lib/server/telephone/qr.js +308 -0
  115. package/lib/shared/analysis.js +40 -0
  116. package/lib/shared/schemas.js +105 -0
  117. package/lib/shared/types.js +10 -0
  118. package/package.json +81 -4
  119. package/searxng/settings.yml +35 -0
  120. package/tui/Modelfile +23 -0
  121. package/tui/README.md +103 -0
  122. package/tui/prmt_tui.py +65 -0
  123. package/tui/promethee/__init__.py +1 -0
  124. package/tui/promethee/__main__.py +4 -0
  125. package/tui/promethee/accueil.py +66 -0
  126. package/tui/promethee/agents.py +220 -0
  127. package/tui/promethee/app/__init__.py +4 -0
  128. package/tui/promethee/app/application.py +299 -0
  129. package/tui/promethee/app/clavier.py +99 -0
  130. package/tui/promethee/app/commandes_slash.py +164 -0
  131. package/tui/promethee/app/contexte.py +188 -0
  132. package/tui/promethee/app/definitions.py +518 -0
  133. package/tui/promethee/app/ecran.py +177 -0
  134. package/tui/promethee/app/envoi.py +141 -0
  135. package/tui/promethee/app/historique.py +373 -0
  136. package/tui/promethee/app/modele.py +108 -0
  137. package/tui/promethee/app/notes.py +63 -0
  138. package/tui/promethee/app/outils.py +589 -0
  139. package/tui/promethee/app/plateforme.py +409 -0
  140. package/tui/promethee/app/saisie.py +100 -0
  141. package/tui/promethee/app/taches.py +92 -0
  142. package/tui/promethee/app/tour.py +539 -0
  143. package/tui/promethee/app/validation.py +210 -0
  144. package/tui/promethee/appels.py +35 -0
  145. package/tui/promethee/commandes.py +154 -0
  146. package/tui/promethee/completion.py +140 -0
  147. package/tui/promethee/config.py +12 -0
  148. package/tui/promethee/consignes.py +278 -0
  149. package/tui/promethee/conversation.py +94 -0
  150. package/tui/promethee/etat.py +253 -0
  151. package/tui/promethee/fichiers.py +354 -0
  152. package/tui/promethee/journal.py +116 -0
  153. package/tui/promethee/logo.py +132 -0
  154. package/tui/promethee/memoire.py +50 -0
  155. package/tui/promethee/ollama.py +52 -0
  156. package/tui/promethee/plan.py +91 -0
  157. package/tui/promethee/politique.py +429 -0
  158. package/tui/promethee/preferences.py +65 -0
  159. package/tui/promethee/presse_papiers.py +44 -0
  160. package/tui/promethee/recherche.py +581 -0
  161. package/tui/promethee/securite.py +204 -0
  162. package/tui/promethee/shell.py +190 -0
  163. package/tui/promethee/textes.py +190 -0
  164. package/tui/promethee/theme.py +92 -0
  165. package/tui/promethee/widgets.py +493 -0
  166. package/tui/requirements.txt +1 -0
@@ -0,0 +1,188 @@
1
+ """/contexte, /code, retour en arrière (Esc Esc) et /init."""
2
+ from __future__ import annotations
3
+
4
+ import json
5
+ import os
6
+
7
+ from rich.text import Text
8
+ from textual import work
9
+ from textual.widgets import Markdown, Static
10
+
11
+ from ..agents import agents_guide, list_agents
12
+ from ..commandes import INIT_EXISTING, INIT_PROMPT
13
+ from ..consignes import TOOL_GUIDE
14
+ from ..conversation import AUTO_COMPACT_PCT
15
+ from ..memoire import GLOBAL_MEMORY, MEMORY_NAME
16
+ from ..shell import SHELL_TAG
17
+ from ..ollama import OLLAMA
18
+ from ..theme import CODE_COLOR, DIM, GREEN, RED, YELLOW
19
+
20
+
21
+ class Contexte:
22
+ """Partie de PrometheeApp : /contexte, /code, retour en arrière (Esc Esc) et /init."""
23
+
24
+ # ------------------------------------------------------------------ /contexte
25
+ def context_breakdown(self) -> list[tuple[str, int]]:
26
+ """Ce qui occupe le contexte, en jetons estimés (≈ 3 caractères par jeton)."""
27
+ def tokens(chars: int) -> int:
28
+ return round(chars / 3.0)
29
+
30
+ parts: list[tuple[str, int]] = [("Consignes du modèle (Modelfile)", tokens(len(self.model_system or "")))]
31
+ if self.supports_tools:
32
+ parts.append(("Guide des outils", tokens(len(TOOL_GUIDE.format(cwd=self.cwd, date="00/00/0000")))))
33
+ agents = list_agents(self.cwd)
34
+ if agents:
35
+ parts.append((f"Agents ({len(agents)})", tokens(len(agents_guide(agents)))))
36
+ parts.append(("Définitions des outils", 1250))
37
+ memory = self.memory_text()
38
+ if memory:
39
+ parts.append(("Mémoire (PROMETHEE.md)", tokens(len(memory))))
40
+ if self.summary:
41
+ parts.append(("Résumé (compactage)", tokens(len(self.summary) + 60)))
42
+ asked = answers = outputs = attached = 0
43
+ for m in self.messages:
44
+ content = str(m.get("content") or "")
45
+ if m["role"] == "user":
46
+ if content.startswith(SHELL_TAG):
47
+ outputs += len(content)
48
+ else:
49
+ shown = len(m.get("display") or content)
50
+ asked += min(shown, len(content))
51
+ attached += max(0, len(content) - shown)
52
+ elif m["role"] == "assistant":
53
+ answers += len(content) + (len(json.dumps(m["tool_calls"], ensure_ascii=False)) if m.get("tool_calls") else 0)
54
+ else:
55
+ outputs += len(content)
56
+ parts += [("Tes messages", tokens(asked)), ("Fichiers joints (@)", tokens(attached)),
57
+ ("Réponses de Prométhée", tokens(answers)), ("Sorties des outils et rapports d'agents", tokens(outputs))]
58
+ return [(label, n) for label, n in parts if n]
59
+
60
+ def cmd_context(self, arg: str = "") -> None:
61
+ parts = self.context_breakdown()
62
+ total = sum(n for _, n in parts)
63
+ used = max(total, self.ctx_used)
64
+ limit = self.num_ctx or 0
65
+ rows = "\n".join(f"| {label} | {n:,} | {100 * n / limit:.0f} % |".replace(",", " ") if limit
66
+ else f"| {label} | {n:,} | |".replace(",", " ") for label, n in parts)
67
+ bar = ""
68
+ if limit:
69
+ pct = min(100, round(100 * used / limit))
70
+ filled = round(pct / 5)
71
+ bar = f"`{'█' * filled}{'░' * (20 - filled)}` **{pct} %** de {limit:,} jetons".replace(",", " ")
72
+ tips = ("\n\n*Le contexte est la mémoire de travail du modèle. Au-delà de "
73
+ f"{AUTO_COMPACT_PCT} %, la conversation est compactée automatiquement ; `/compact` pour le faire maintenant, "
74
+ "`/clear` pour repartir de zéro. Les agents travaillent dans leur propre contexte : leurs longues sorties "
75
+ "n'arrivent pas ici, seulement leur rapport.*")
76
+ self.add_notice_md(f"**Contexte** {bar}\n\n| Contenu | Jetons (≈) | Part |\n|---|---:|---:|\n{rows}"
77
+ f"\n| **Total estimé** | **{total:,}** | |".replace(",", " ") + tips)
78
+
79
+ # ------------------------------------------------------------------ /code : IA de code
80
+ def cmd_code(self, arg: str = "") -> None:
81
+ word = arg.strip()
82
+ if word.lower() in ("off", "non", "stop"):
83
+ self.code_auto = False
84
+ self.add_notice("Bascule automatique coupée : Prométhée répond à tout, code compris. /code on pour la "
85
+ "rétablir.")
86
+ elif word.lower() in ("on", "oui"):
87
+ self.code_auto = True
88
+ self.add_notice(f"Bascule automatique activée : les demandes de programmation vont à {self.code_model}.",
89
+ CODE_COLOR)
90
+ elif word:
91
+ self.set_code_model(word)
92
+ else:
93
+ state = "activée" if self.code_auto else "coupée (/code on pour l'activer)"
94
+ self.add_notice_md(
95
+ f"**IA de code** : `{self.code_model}` · bascule automatique **{state}**\n\n"
96
+ "Quand tu demandes d'écrire, corriger ou expliquer du code, l'interpréteur le détecte et confie la "
97
+ "réponse à l'IA de code (même conversation, mêmes outils, tu valides toujours chaque commande). "
98
+ "Prométhée reprend la main au message suivant. Un changement de modèle prend quelques secondes "
99
+ "(chargement dans la carte graphique).\n\n"
100
+ "`/code off` · `/code on` · `/code <modèle>` pour en choisir un autre (il doit savoir utiliser des outils).")
101
+
102
+ @work(thread=True, exclusive=True, group="model")
103
+ def set_code_model(self, name: str) -> None:
104
+ name = name[:-7] if name.endswith(":latest") else name
105
+ self._caps.pop(name, None)
106
+ caps = self.model_caps(name)
107
+ if caps is None and name not in self._caps:
108
+ self.call_from_thread(self.add_notice, f"Ollama ne répond pas sur {OLLAMA} : impossible de vérifier « {name} ». "
109
+ "Lance l'application Ollama puis réessaie.", f"bold {RED}")
110
+ elif caps is None:
111
+ self.call_from_thread(self.add_notice, f"Modèle « {name} » introuvable. /modele pour voir la liste.", f"bold {RED}")
112
+ elif "tools" not in caps:
113
+ self.call_from_thread(self.add_notice, f"« {name} » ne sait pas utiliser d'outils : il ne pourrait ni écrire "
114
+ "de fichier ni compiler. Choisis-en un autre.", YELLOW)
115
+ else:
116
+ self.code_model, self.code_auto, self._code_warned = name, True, False
117
+ self.call_from_thread(self.add_notice, f"IA de code : {name}.", CODE_COLOR)
118
+
119
+ # ------------------------------------------------------------------ Esc Esc : revenir en arrière
120
+ def open_rewind(self) -> None:
121
+ """Comme Claude Code : choisir un de ses messages ; la conversation repart juste avant, message à modifier."""
122
+ points = [(i, " ".join((m.get("display") or m["content"]).split())) for i, m in enumerate(self.messages)
123
+ if m["role"] == "user" and not str(m.get("content") or "").startswith(SHELL_TAG)]
124
+ if not points:
125
+ self.notify("Aucun message auquel revenir.", timeout=2)
126
+ return
127
+ points = points[-9:][::-1]
128
+ options = []
129
+ for k, (i, text) in enumerate(points):
130
+ later = sum(1 for m in self.messages[i:] if m["role"] == "user")
131
+ when = "ton dernier message" if k == 0 else f"il y a {later} messages"
132
+ options.append((str(i), f"{text[:70] + '…' if len(text) > 70 else text} ({when})"))
133
+ options.append(("no", "Annuler (Esc)"))
134
+ body = Text(" La conversation reprendra juste avant le message choisi, qui revient dans la saisie pour que tu "
135
+ "le modifies.\n Les commandes déjà exécutées et les fichiers déjà modifiés ne sont pas annulés.",
136
+ style=DIM)
137
+
138
+ def chosen(decision: str) -> None:
139
+ if decision.isdigit():
140
+ self.run_worker(self.rewind_to(int(decision)), exclusive=False)
141
+
142
+ self.ask("Revenir en arrière", body, "Reprendre avant quel message ?", options, chosen)
143
+
144
+ async def rewind_to(self, index: int) -> None:
145
+ if self.generating or index >= len(self.messages):
146
+ return
147
+ message = self.messages[index]
148
+ text = message.get("display") or message["content"]
149
+ self.messages = self.messages[:index]
150
+ self.tasks = []
151
+ self.render_tasks()
152
+ self.last_answer = next((m["content"] for m in reversed(self.messages)
153
+ if m["role"] == "assistant" and (m.get("content") or "").strip()), "")
154
+ await self.rebuild_chat()
155
+ self.add_notice("Conversation reprise avant ce message : modifie-le puis Entrée pour le renvoyer.", GREEN)
156
+ self.input.load_text(text)
157
+ self.input.move_cursor(self.input.document.end)
158
+ self.ctx_used = self.estimate_tokens()
159
+ self.update_status()
160
+ self.save_session()
161
+
162
+ async def rebuild_chat(self) -> None:
163
+ """Réaffiche toute la conversation à partir des messages (reprise, retour en arrière)."""
164
+ await self.chat.remove_children()
165
+ await self.chat.mount_all(list(self._welcome_widgets()))
166
+ if self.summary:
167
+ await self.chat.mount(Static(Text.assemble(("⎿ ", DIM), ("Début de la conversation compacté. Résumé :", DIM)),
168
+ classes="notice"))
169
+ await self.chat.mount(Markdown(self.summary, classes="notice-md"))
170
+ await self.chat.mount_all(self._replay_widgets())
171
+ self.chat.scroll_end(animate=False)
172
+ self.call_after_refresh(self._follow)
173
+
174
+ # ------------------------------------------------------------------ /init
175
+ def cmd_init(self, arg: str = "") -> None:
176
+ if self.generating:
177
+ self.add_notice("Attends la fin de la réponse (ou Esc pour l'interrompre).", YELLOW)
178
+ return
179
+ if not self.supports_tools:
180
+ self.add_notice("Ce modèle ne sait pas utiliser les outils : /init a besoin de lire le dossier.", YELLOW)
181
+ return
182
+ path = self.cwd / MEMORY_NAME
183
+ if os.path.normcase(str(path)) == os.path.normcase(str(GLOBAL_MEMORY)):
184
+ self.add_notice("Ici, PROMETHEE.md est la mémoire globale : lance /init dans le dossier d'un projet "
185
+ "(cd dans ce dossier avec ! cd chemin).", YELLOW)
186
+ return
187
+ prompt = INIT_PROMPT.format(cwd=self.cwd, path=path, existing=INIT_EXISTING if path.exists() else "")
188
+ self.send_message("/init", prompt, intent=prompt, mode="action")