npcsh 1.1.21__py3-none-any.whl → 1.1.23__py3-none-any.whl

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 (188) hide show
  1. npcsh/_state.py +282 -125
  2. npcsh/benchmark/npcsh_agent.py +77 -232
  3. npcsh/benchmark/templates/install-npcsh.sh.j2 +12 -4
  4. npcsh/config.py +5 -2
  5. npcsh/mcp_server.py +9 -1
  6. npcsh/npc_team/alicanto.npc +8 -6
  7. npcsh/npc_team/corca.npc +5 -12
  8. npcsh/npc_team/frederic.npc +6 -9
  9. npcsh/npc_team/guac.npc +4 -4
  10. npcsh/npc_team/jinxs/lib/core/delegate.jinx +1 -1
  11. npcsh/npc_team/jinxs/lib/core/edit_file.jinx +84 -62
  12. npcsh/npc_team/jinxs/lib/core/sh.jinx +1 -1
  13. npcsh/npc_team/jinxs/lib/core/skill.jinx +59 -0
  14. npcsh/npc_team/jinxs/lib/utils/help.jinx +194 -10
  15. npcsh/npc_team/jinxs/lib/utils/init.jinx +528 -37
  16. npcsh/npc_team/jinxs/lib/utils/jinxs.jinx +0 -1
  17. npcsh/npc_team/jinxs/lib/utils/serve.jinx +938 -21
  18. npcsh/npc_team/jinxs/modes/alicanto.jinx +102 -41
  19. npcsh/npc_team/jinxs/modes/build.jinx +378 -0
  20. npcsh-1.1.21.data/data/npcsh/npc_team/config_tui.jinx → npcsh/npc_team/jinxs/modes/config.jinx +1 -1
  21. npcsh/npc_team/jinxs/modes/convene.jinx +670 -0
  22. npcsh/npc_team/jinxs/modes/corca.jinx +777 -387
  23. npcsh/npc_team/jinxs/modes/crond.jinx +818 -0
  24. npcsh/npc_team/jinxs/modes/kg.jinx +69 -2
  25. npcsh/npc_team/jinxs/modes/plonk.jinx +86 -15
  26. npcsh/npc_team/jinxs/modes/roll.jinx +368 -55
  27. npcsh/npc_team/jinxs/modes/skills.jinx +621 -0
  28. npcsh/npc_team/jinxs/modes/yap.jinx +1092 -177
  29. npcsh/npc_team/jinxs/skills/code-review/SKILL.md +45 -0
  30. npcsh/npc_team/jinxs/skills/debugging/SKILL.md +44 -0
  31. npcsh/npc_team/jinxs/skills/git-workflow.jinx +44 -0
  32. npcsh/npc_team/kadiefa.npc +6 -6
  33. npcsh/npc_team/npcsh.ctx +16 -0
  34. npcsh/npc_team/plonk.npc +5 -9
  35. npcsh/npc_team/sibiji.npc +15 -7
  36. npcsh/npcsh.py +1 -0
  37. npcsh/routes.py +0 -4
  38. npcsh/yap.py +22 -4
  39. npcsh-1.1.23.data/data/npcsh/npc_team/SKILL.md +44 -0
  40. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/alicanto.jinx +102 -41
  41. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/alicanto.npc +8 -6
  42. npcsh-1.1.23.data/data/npcsh/npc_team/build.jinx +378 -0
  43. npcsh/npc_team/jinxs/modes/config_tui.jinx → npcsh-1.1.23.data/data/npcsh/npc_team/config.jinx +1 -1
  44. npcsh-1.1.23.data/data/npcsh/npc_team/convene.jinx +670 -0
  45. npcsh-1.1.23.data/data/npcsh/npc_team/corca.jinx +820 -0
  46. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/corca.npc +5 -12
  47. npcsh-1.1.23.data/data/npcsh/npc_team/crond.jinx +818 -0
  48. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/delegate.jinx +1 -1
  49. npcsh-1.1.23.data/data/npcsh/npc_team/edit_file.jinx +119 -0
  50. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/frederic.npc +6 -9
  51. npcsh-1.1.23.data/data/npcsh/npc_team/git-workflow.jinx +44 -0
  52. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/guac.npc +4 -4
  53. npcsh-1.1.23.data/data/npcsh/npc_team/help.jinx +236 -0
  54. npcsh-1.1.23.data/data/npcsh/npc_team/init.jinx +532 -0
  55. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/jinxs.jinx +0 -1
  56. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/kadiefa.npc +6 -6
  57. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/kg.jinx +69 -2
  58. npcsh-1.1.23.data/data/npcsh/npc_team/npcsh.ctx +34 -0
  59. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonk.jinx +86 -15
  60. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonk.npc +5 -9
  61. npcsh-1.1.23.data/data/npcsh/npc_team/roll.jinx +378 -0
  62. npcsh-1.1.23.data/data/npcsh/npc_team/serve.jinx +943 -0
  63. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sh.jinx +1 -1
  64. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sibiji.npc +15 -7
  65. npcsh-1.1.23.data/data/npcsh/npc_team/skill.jinx +59 -0
  66. npcsh-1.1.23.data/data/npcsh/npc_team/skills.jinx +621 -0
  67. npcsh-1.1.23.data/data/npcsh/npc_team/yap.jinx +1190 -0
  68. {npcsh-1.1.21.dist-info → npcsh-1.1.23.dist-info}/METADATA +404 -278
  69. npcsh-1.1.23.dist-info/RECORD +216 -0
  70. npcsh/npc_team/jinxs/incognide/add_tab.jinx +0 -11
  71. npcsh/npc_team/jinxs/incognide/close_pane.jinx +0 -9
  72. npcsh/npc_team/jinxs/incognide/close_tab.jinx +0 -10
  73. npcsh/npc_team/jinxs/incognide/confirm.jinx +0 -10
  74. npcsh/npc_team/jinxs/incognide/focus_pane.jinx +0 -9
  75. npcsh/npc_team/jinxs/incognide/list_panes.jinx +0 -8
  76. npcsh/npc_team/jinxs/incognide/navigate.jinx +0 -10
  77. npcsh/npc_team/jinxs/incognide/notify.jinx +0 -10
  78. npcsh/npc_team/jinxs/incognide/open_pane.jinx +0 -13
  79. npcsh/npc_team/jinxs/incognide/read_pane.jinx +0 -9
  80. npcsh/npc_team/jinxs/incognide/run_terminal.jinx +0 -10
  81. npcsh/npc_team/jinxs/incognide/send_message.jinx +0 -10
  82. npcsh/npc_team/jinxs/incognide/split_pane.jinx +0 -12
  83. npcsh/npc_team/jinxs/incognide/switch_npc.jinx +0 -10
  84. npcsh/npc_team/jinxs/incognide/switch_tab.jinx +0 -10
  85. npcsh/npc_team/jinxs/incognide/write_file.jinx +0 -11
  86. npcsh/npc_team/jinxs/incognide/zen_mode.jinx +0 -9
  87. npcsh/npc_team/jinxs/lib/core/convene.jinx +0 -232
  88. npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +0 -429
  89. npcsh/npc_team/jinxs/lib/core/search.jinx +0 -54
  90. npcsh/npc_team/jinxs/lib/utils/build.jinx +0 -65
  91. npcsh-1.1.21.data/data/npcsh/npc_team/add_tab.jinx +0 -11
  92. npcsh-1.1.21.data/data/npcsh/npc_team/build.jinx +0 -65
  93. npcsh-1.1.21.data/data/npcsh/npc_team/close_pane.jinx +0 -9
  94. npcsh-1.1.21.data/data/npcsh/npc_team/close_tab.jinx +0 -10
  95. npcsh-1.1.21.data/data/npcsh/npc_team/confirm.jinx +0 -10
  96. npcsh-1.1.21.data/data/npcsh/npc_team/convene.jinx +0 -232
  97. npcsh-1.1.21.data/data/npcsh/npc_team/corca.jinx +0 -430
  98. npcsh-1.1.21.data/data/npcsh/npc_team/edit_file.jinx +0 -97
  99. npcsh-1.1.21.data/data/npcsh/npc_team/focus_pane.jinx +0 -9
  100. npcsh-1.1.21.data/data/npcsh/npc_team/help.jinx +0 -52
  101. npcsh-1.1.21.data/data/npcsh/npc_team/init.jinx +0 -41
  102. npcsh-1.1.21.data/data/npcsh/npc_team/kg_search.jinx +0 -429
  103. npcsh-1.1.21.data/data/npcsh/npc_team/list_panes.jinx +0 -8
  104. npcsh-1.1.21.data/data/npcsh/npc_team/navigate.jinx +0 -10
  105. npcsh-1.1.21.data/data/npcsh/npc_team/notify.jinx +0 -10
  106. npcsh-1.1.21.data/data/npcsh/npc_team/npcsh.ctx +0 -18
  107. npcsh-1.1.21.data/data/npcsh/npc_team/open_pane.jinx +0 -13
  108. npcsh-1.1.21.data/data/npcsh/npc_team/read_pane.jinx +0 -9
  109. npcsh-1.1.21.data/data/npcsh/npc_team/roll.jinx +0 -65
  110. npcsh-1.1.21.data/data/npcsh/npc_team/run_terminal.jinx +0 -10
  111. npcsh-1.1.21.data/data/npcsh/npc_team/search.jinx +0 -54
  112. npcsh-1.1.21.data/data/npcsh/npc_team/send_message.jinx +0 -10
  113. npcsh-1.1.21.data/data/npcsh/npc_team/serve.jinx +0 -26
  114. npcsh-1.1.21.data/data/npcsh/npc_team/split_pane.jinx +0 -12
  115. npcsh-1.1.21.data/data/npcsh/npc_team/switch_npc.jinx +0 -10
  116. npcsh-1.1.21.data/data/npcsh/npc_team/switch_tab.jinx +0 -10
  117. npcsh-1.1.21.data/data/npcsh/npc_team/write_file.jinx +0 -11
  118. npcsh-1.1.21.data/data/npcsh/npc_team/yap.jinx +0 -275
  119. npcsh-1.1.21.data/data/npcsh/npc_team/zen_mode.jinx +0 -9
  120. npcsh-1.1.21.dist-info/RECORD +0 -243
  121. /npcsh/npc_team/jinxs/lib/{core → utils}/chat.jinx +0 -0
  122. /npcsh/npc_team/jinxs/lib/{core → utils}/cmd.jinx +0 -0
  123. /npcsh/npc_team/jinxs/{incognide → lib/utils}/incognide.jinx +0 -0
  124. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/alicanto.png +0 -0
  125. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/arxiv.jinx +0 -0
  126. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/benchmark.jinx +0 -0
  127. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/browser_action.jinx +0 -0
  128. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/browser_screenshot.jinx +0 -0
  129. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/chat.jinx +0 -0
  130. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/click.jinx +0 -0
  131. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
  132. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/cmd.jinx +0 -0
  133. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/compile.jinx +0 -0
  134. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/compress.jinx +0 -0
  135. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/corca.png +0 -0
  136. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/corca_example.png +0 -0
  137. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/db_search.jinx +0 -0
  138. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/file_search.jinx +0 -0
  139. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/frederic4.png +0 -0
  140. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/git.jinx +0 -0
  141. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/guac.jinx +0 -0
  142. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/guac.png +0 -0
  143. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/incognide.jinx +0 -0
  144. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/kadiefa.png +0 -0
  145. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/key_press.jinx +0 -0
  146. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/launch_app.jinx +0 -0
  147. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/load_file.jinx +0 -0
  148. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/memories.jinx +0 -0
  149. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/models.jinx +0 -0
  150. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
  151. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/nql.jinx +0 -0
  152. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/open_browser.jinx +0 -0
  153. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/ots.jinx +0 -0
  154. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/papers.jinx +0 -0
  155. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/paste.jinx +0 -0
  156. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonk.png +0 -0
  157. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/plonkjr.png +0 -0
  158. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/pti.jinx +0 -0
  159. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/python.jinx +0 -0
  160. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/reattach.jinx +0 -0
  161. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sample.jinx +0 -0
  162. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/screenshot.jinx +0 -0
  163. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/set.jinx +0 -0
  164. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/setup.jinx +0 -0
  165. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/shh.jinx +0 -0
  166. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sibiji.png +0 -0
  167. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sleep.jinx +0 -0
  168. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/spool.jinx +0 -0
  169. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/spool.png +0 -0
  170. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sql.jinx +0 -0
  171. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/switch.jinx +0 -0
  172. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/switches.jinx +0 -0
  173. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/sync.jinx +0 -0
  174. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/team.jinx +0 -0
  175. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/teamviz.jinx +0 -0
  176. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/trigger.jinx +0 -0
  177. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/type_text.jinx +0 -0
  178. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/usage.jinx +0 -0
  179. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/verbose.jinx +0 -0
  180. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/vixynt.jinx +0 -0
  181. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/wait.jinx +0 -0
  182. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/wander.jinx +0 -0
  183. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/web_search.jinx +0 -0
  184. {npcsh-1.1.21.data → npcsh-1.1.23.data}/data/npcsh/npc_team/yap.png +0 -0
  185. {npcsh-1.1.21.dist-info → npcsh-1.1.23.dist-info}/WHEEL +0 -0
  186. {npcsh-1.1.21.dist-info → npcsh-1.1.23.dist-info}/entry_points.txt +0 -0
  187. {npcsh-1.1.21.dist-info → npcsh-1.1.23.dist-info}/licenses/LICENSE +0 -0
  188. {npcsh-1.1.21.dist-info → npcsh-1.1.23.dist-info}/top_level.txt +0 -0
@@ -1,429 +0,0 @@
1
- jinx_name: kg_search
2
- description: Search the knowledge graph with interactive TUI
3
- interactive: true
4
- inputs:
5
- - query: ""
6
- - type: "facts"
7
- - mode: "keyword"
8
- - concept: ""
9
- - depth: "2"
10
- - breadth: "5"
11
- - max_results: "20"
12
- - threshold: "0.6"
13
- - npc_name: ""
14
- - team_name: ""
15
- - db_path: ""
16
- - text: "false"
17
-
18
- steps:
19
- - name: search_kg
20
- engine: python
21
- code: |
22
- import os
23
- import sys
24
- import tty
25
- import termios
26
- from npcpy.memory.command_history import CommandHistory
27
- from npcpy.memory.knowledge_graph import (
28
- kg_search_facts, kg_list_concepts, kg_get_facts_for_concept,
29
- kg_get_all_facts, kg_link_search, kg_embedding_search,
30
- kg_hybrid_search, kg_explore_concept
31
- )
32
-
33
- query = context.get('query', '').strip()
34
- search_type = context.get('type', 'facts').lower()
35
- search_mode = context.get('mode', 'keyword').lower()
36
- concept = context.get('concept', '').strip()
37
- max_depth = int(context.get('depth') or 2)
38
- breadth = int(context.get('breadth') or 5)
39
- max_results = int(context.get('max_results') or 20)
40
- threshold = float(context.get('threshold') or 0.6)
41
- text_mode = context.get('text', '').lower() in ('true', '1', 'yes')
42
-
43
- if not query and search_type == 'facts' and not concept:
44
- lines = [
45
- "Usage: /kg_search <query> [mode=keyword|embedding|link|hybrid|all]",
46
- "",
47
- "Search Modes:",
48
- " keyword - Simple keyword matching (default, fast)",
49
- " embedding - Semantic similarity using embeddings",
50
- " link - Traverse graph links from keyword matches",
51
- " hybrid - Combine keyword + link traversal",
52
- " all - Combine all methods (slowest, most thorough)",
53
- "",
54
- "Options:",
55
- " type - Result type: facts, concepts, all",
56
- " concept - Explore a specific concept",
57
- " depth - Link traversal depth (default: 2)",
58
- " breadth - Results per traversal hop (default: 5)",
59
- " max_results - Max total results (default: 20)",
60
- " threshold - Embedding similarity threshold (default: 0.6)",
61
- " text - Text-only output, no TUI (true/false)",
62
- "",
63
- "TUI Controls:",
64
- " j/k or arrows - Navigate",
65
- " 1/2/3 - Sort by score/concept/type",
66
- " c - Toggle concepts view",
67
- " e - Explore selected concept",
68
- " p - Preview full fact/concept",
69
- " q/ESC - Quit",
70
- "",
71
- "Examples:",
72
- " /kg_search python",
73
- " /kg_search python mode=embedding",
74
- " /kg_search python mode=link depth=3",
75
- " /kg_search type=concepts",
76
- " /kg_search concept=coding",
77
- ]
78
- context['output'] = "\n".join(lines)
79
- else:
80
- db_path = context.get('db_path') or os.path.expanduser("~/npcsh_history.db")
81
-
82
- try:
83
- cmd_history = CommandHistory(db_path)
84
- engine = cmd_history.engine
85
-
86
- team_obj = None
87
- try:
88
- team_obj = state.team if 'state' in dir() and state else None
89
- except:
90
- pass
91
- npc_obj = npc if 'npc' in dir() else None
92
-
93
- emodel = None
94
- eprovider = None
95
- try:
96
- if 'state' in dir() and state:
97
- emodel = getattr(state, 'embedding_model', None)
98
- eprovider = getattr(state, 'embedding_provider', None)
99
- except:
100
- pass
101
-
102
- # Collect results
103
- results = []
104
-
105
- if concept:
106
- result = kg_explore_concept(engine, concept, max_depth=max_depth, breadth_per_step=breadth)
107
- for i, f in enumerate(result.get('direct_facts', [])):
108
- results.append({'type': 'fact', 'content': str(f), 'score': 1.0, 'concept': concept, 'source': 'direct'})
109
- for i, f in enumerate(result.get('extended_facts', [])):
110
- results.append({'type': 'fact', 'content': str(f), 'score': 0.5, 'concept': concept, 'source': 'extended'})
111
- for c in result.get('related_concepts', []):
112
- results.append({'type': 'concept', 'content': str(c), 'score': 0.8, 'concept': concept, 'source': 'related'})
113
-
114
- elif search_type == 'concepts':
115
- concepts = kg_list_concepts(engine, search_all_scopes=True)
116
- for c in concepts:
117
- results.append({'type': 'concept', 'content': str(c), 'score': 1.0, 'concept': str(c), 'source': 'list'})
118
-
119
- elif search_type == 'all' and not query:
120
- facts = kg_get_all_facts(engine, search_all_scopes=True)
121
- for f in facts[:max_results]:
122
- results.append({'type': 'fact', 'content': str(f), 'score': 1.0, 'concept': '', 'source': 'all'})
123
-
124
- elif search_mode == 'embedding':
125
- raw_results = kg_embedding_search(
126
- engine, query,
127
- embedding_model=emodel, embedding_provider=eprovider,
128
- similarity_threshold=threshold, max_results=max_results,
129
- search_all_scopes=True
130
- )
131
- for r in raw_results:
132
- results.append({
133
- 'type': r.get('type', 'fact'),
134
- 'content': str(r.get('content', '')),
135
- 'score': r.get('score', 0),
136
- 'concept': r.get('concept', ''),
137
- 'source': 'embedding'
138
- })
139
-
140
- elif search_mode == 'link':
141
- raw_results = kg_link_search(
142
- engine, query,
143
- max_depth=max_depth, breadth_per_step=breadth, max_results=max_results,
144
- search_all_scopes=True
145
- )
146
- for r in raw_results:
147
- results.append({
148
- 'type': r.get('type', 'fact'),
149
- 'content': str(r.get('content', '')),
150
- 'score': r.get('score', 0),
151
- 'concept': r.get('concept', ''),
152
- 'source': f"link-d{r.get('depth', 0)}"
153
- })
154
-
155
- elif search_mode in ['hybrid', 'all', 'keyword+link', 'keyword+embedding']:
156
- raw_results = kg_hybrid_search(
157
- engine, query,
158
- mode=search_mode if search_mode != 'hybrid' else 'keyword+link',
159
- max_depth=max_depth, breadth_per_step=breadth, max_results=max_results,
160
- embedding_model=emodel, embedding_provider=eprovider,
161
- similarity_threshold=threshold,
162
- search_all_scopes=True
163
- )
164
- for r in raw_results:
165
- results.append({
166
- 'type': r.get('type', 'fact'),
167
- 'content': str(r.get('content', '')),
168
- 'score': r.get('score', 0),
169
- 'concept': r.get('concept', ''),
170
- 'source': r.get('source', 'hybrid')
171
- })
172
-
173
- else:
174
- # Default keyword search
175
- facts = kg_search_facts(engine, query, search_all_scopes=True)
176
- for f in facts[:max_results]:
177
- results.append({'type': 'fact', 'content': str(f), 'score': 1.0, 'concept': '', 'source': 'keyword'})
178
-
179
- if not results:
180
- context['output'] = f"No KG results found for '{query}'"
181
- elif text_mode:
182
- # Text-only output
183
- lines = [f"Found {len(results)} KG results:", ""]
184
- for i, r in enumerate(results, 1):
185
- score = f"{r['score']:.2f}" if isinstance(r['score'], float) else str(r['score'])
186
- lines.append(f"{i}. [{r['type']} {score}] {r['content'][:80]}")
187
- context['output'] = "\n".join(lines)
188
- else:
189
- # Interactive TUI mode
190
- def get_terminal_size():
191
- try:
192
- size = os.get_terminal_size()
193
- return size.columns, size.lines
194
- except:
195
- return 80, 24
196
-
197
- width, height = get_terminal_size()
198
- selected = 0
199
- scroll = 0
200
- list_height = height - 5
201
- mode = 'list'
202
- preview_scroll = 0
203
- sort_mode = 'score' # score, concept, type
204
- type_filter = 'all' # all, fact, concept
205
-
206
- def sort_results(results, sort_mode):
207
- if sort_mode == 'score':
208
- return sorted(results, key=lambda x: x.get('score', 0), reverse=True)
209
- elif sort_mode == 'concept':
210
- return sorted(results, key=lambda x: (x.get('concept', ''), -x.get('score', 0)))
211
- elif sort_mode == 'type':
212
- return sorted(results, key=lambda x: (x.get('type', ''), -x.get('score', 0)))
213
- return results
214
-
215
- def filter_results(results, type_filter):
216
- if type_filter == 'all':
217
- return results
218
- return [r for r in results if r.get('type') == type_filter]
219
-
220
- display_results = filter_results(sort_results(results, sort_mode), type_filter)
221
-
222
- fd = sys.stdin.fileno()
223
- old_settings = termios.tcgetattr(fd)
224
-
225
- try:
226
- tty.setcbreak(fd)
227
- sys.stdout.write('\033[?25l')
228
- sys.stdout.write('\033[2J\033[H')
229
-
230
- while True:
231
- width, height = get_terminal_size()
232
- list_height = height - 5
233
-
234
- if mode == 'list':
235
- if selected < scroll:
236
- scroll = selected
237
- elif selected >= scroll + list_height:
238
- scroll = selected - list_height + 1
239
-
240
- sys.stdout.write('\033[H')
241
-
242
- # Header
243
- if mode == 'list':
244
- sort_ind = {'score': '1', 'concept': '2', 'type': '3'}[sort_mode]
245
- label = query or concept or search_type
246
- header = f" KG SEARCH ({len(display_results)} results): '{label}' [sort:{sort_mode}({sort_ind}) filter:{type_filter}] "
247
- else:
248
- header = f" PREVIEW: {display_results[selected]['type']} "
249
- sys.stdout.write(f'\033[7;1m{header.ljust(width)}\033[0m\n')
250
-
251
- # Column headers
252
- if mode == 'list':
253
- col_header = f' {"TYPE":<8} {"SCORE":<6} {"CONCEPT":<15} {"CONTENT":<50}'
254
- sys.stdout.write(f'\033[90m{col_header[:width]}\033[0m\n')
255
- else:
256
- sys.stdout.write(f'\033[90m{"─" * width}\033[0m\n')
257
-
258
- if mode == 'list':
259
- for i in range(list_height):
260
- idx = scroll + i
261
- sys.stdout.write(f'\033[{3+i};1H\033[K')
262
- if idx >= len(display_results):
263
- continue
264
-
265
- r = display_results[idx]
266
- rtype = r.get('type', '?')[:8]
267
- score = f"{r.get('score', 0):.2f}" if isinstance(r.get('score'), float) else str(r.get('score', ''))[:6]
268
- concept_str = (r.get('concept', '') or '')[:15]
269
- content = (r.get('content', '') or '')[:60].replace('\n', ' ')
270
-
271
- # Color by type
272
- if r.get('type') == 'concept':
273
- type_color = '\033[35m' # magenta
274
- else:
275
- type_color = '\033[36m' # cyan
276
-
277
- line = f" {type_color}{rtype:<8}\033[0m {score:<6} {concept_str:<15} {content}"
278
- line = line[:width+15]
279
-
280
- if idx == selected:
281
- sys.stdout.write(f'\033[7;1m>{line}\033[0m')
282
- else:
283
- sys.stdout.write(f' {line}')
284
-
285
- # Status bar
286
- sys.stdout.write(f'\033[{height-2};1H\033[K\033[90m{"─" * width}\033[0m')
287
- sel = display_results[selected] if display_results else {}
288
- source = sel.get('source', '')
289
- sys.stdout.write(f'\033[{height-1};1H\033[K Source: {source}'.ljust(width))
290
- sys.stdout.write(f'\033[{height};1H\033[K\033[7m j/k:Nav 1/2/3:Sort c:Concepts e:Explore p:Preview q:Quit [{selected+1}/{len(display_results)}] \033[0m')
291
-
292
- else: # preview mode
293
- sel = display_results[selected]
294
- content = sel.get('content', '')
295
- lines = content.split('\n')
296
-
297
- # Add metadata at top
298
- meta_lines = [
299
- f"Type: {sel.get('type', '')}",
300
- f"Score: {sel.get('score', '')}",
301
- f"Concept: {sel.get('concept', '')}",
302
- f"Source: {sel.get('source', '')}",
303
- "─" * 40,
304
- ]
305
- all_lines = meta_lines + lines
306
-
307
- for i in range(list_height):
308
- idx = preview_scroll + i
309
- sys.stdout.write(f'\033[{3+i};1H\033[K')
310
- if idx < len(all_lines):
311
- sys.stdout.write(all_lines[idx][:width-1])
312
-
313
- sys.stdout.write(f'\033[{height-2};1H\033[K\033[90m{"─" * width}\033[0m')
314
- sys.stdout.write(f'\033[{height-1};1H\033[K [{preview_scroll+1}/{len(all_lines)} lines]')
315
- sys.stdout.write(f'\033[{height};1H\033[K\033[7m j/k:Scroll b:Back e:Explore q:Quit \033[0m')
316
-
317
- sys.stdout.flush()
318
-
319
- c = os.read(fd, 1).decode('latin-1')
320
-
321
- if c == '\x1b':
322
- import select as _sel
323
- if _sel.select([fd], [], [], 0.05)[0]:
324
- c2 = os.read(fd, 1).decode('latin-1')
325
- else:
326
- if mode == 'preview':
327
- mode = 'list'
328
- sys.stdout.write('\033[2J\033[H')
329
- else:
330
- context['output'] = "Cancelled."
331
- break
332
- continue
333
- if c2 == '[':
334
- c3 = os.read(fd, 1).decode('latin-1')
335
- if c3 == 'A': # Up
336
- if mode == 'list' and selected > 0:
337
- selected -= 1
338
- elif mode == 'preview' and preview_scroll > 0:
339
- preview_scroll -= 1
340
- elif c3 == 'B': # Down
341
- if mode == 'list' and selected < len(display_results) - 1:
342
- selected += 1
343
- elif mode == 'preview':
344
- sel = display_results[selected]
345
- content = sel.get('content', '')
346
- all_lines = content.split('\n')
347
- if preview_scroll < max(0, len(all_lines) + 5 - list_height):
348
- preview_scroll += 1
349
- else:
350
- if mode == 'preview':
351
- mode = 'list'
352
- sys.stdout.write('\033[2J\033[H')
353
- else:
354
- context['output'] = "Cancelled."
355
- break
356
- continue
357
-
358
- if c == 'q' or c == '\x03':
359
- context['output'] = "Cancelled."
360
- break
361
- elif c == 'k':
362
- if mode == 'list' and selected > 0:
363
- selected -= 1
364
- elif mode == 'preview' and preview_scroll > 0:
365
- preview_scroll -= 1
366
- elif c == 'j':
367
- if mode == 'list' and selected < len(display_results) - 1:
368
- selected += 1
369
- elif mode == 'preview':
370
- sel = display_results[selected]
371
- content = sel.get('content', '')
372
- all_lines = content.split('\n')
373
- if preview_scroll < max(0, len(all_lines) + 5 - list_height):
374
- preview_scroll += 1
375
- elif c == '1':
376
- sort_mode = 'score'
377
- display_results = filter_results(sort_results(results, sort_mode), type_filter)
378
- selected = 0
379
- scroll = 0
380
- elif c == '2':
381
- sort_mode = 'concept'
382
- display_results = filter_results(sort_results(results, sort_mode), type_filter)
383
- selected = 0
384
- scroll = 0
385
- elif c == '3':
386
- sort_mode = 'type'
387
- display_results = filter_results(sort_results(results, sort_mode), type_filter)
388
- selected = 0
389
- scroll = 0
390
- elif c == 'c' and mode == 'list':
391
- # Toggle type filter
392
- if type_filter == 'all':
393
- type_filter = 'concept'
394
- elif type_filter == 'concept':
395
- type_filter = 'fact'
396
- else:
397
- type_filter = 'all'
398
- display_results = filter_results(sort_results(results, sort_mode), type_filter)
399
- selected = 0
400
- scroll = 0
401
- elif c == 'e' and display_results:
402
- # Explore the selected concept
403
- sel = display_results[selected]
404
- explore_concept = sel.get('concept') or sel.get('content', '')
405
- if sel.get('type') == 'concept':
406
- explore_concept = sel.get('content', '')
407
- context['output'] = f"Explore concept: {explore_concept}\n\nRun: /kg_search concept={explore_concept}"
408
- break
409
- elif c == 'p' and mode == 'list' and display_results:
410
- mode = 'preview'
411
- preview_scroll = 0
412
- sys.stdout.write('\033[2J\033[H')
413
- elif c == 'b' and mode == 'preview':
414
- mode = 'list'
415
- sys.stdout.write('\033[2J\033[H')
416
- elif c in ('\r', '\n') and display_results:
417
- sel = display_results[selected]
418
- context['output'] = f"Selected: {sel.get('content', '')[:100]}"
419
- break
420
-
421
- finally:
422
- termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
423
- sys.stdout.write('\033[?25h')
424
- sys.stdout.write('\033[2J\033[H')
425
- sys.stdout.flush()
426
-
427
- except Exception as e:
428
- import traceback
429
- context['output'] = "KG search error: " + str(e) + "\n" + traceback.format_exc()
@@ -1,8 +0,0 @@
1
- jinx_name: studio_list_panes
2
- description: List all open panes in NPC Studio. Returns pane IDs, types, titles, and which is active.
3
- inputs: []
4
- steps:
5
- - name: frontend_action
6
- engine: python
7
- code: |
8
- context['output'] = "Action executed by frontend"
@@ -1,10 +0,0 @@
1
- jinx_name: studio_navigate
2
- description: Navigate a browser pane to a specific URL.
3
- inputs:
4
- - paneId: "active"
5
- - url: ""
6
- steps:
7
- - name: frontend_action
8
- engine: python
9
- code: |
10
- context['output'] = "Action executed by frontend"
@@ -1,10 +0,0 @@
1
- jinx_name: studio_notify
2
- description: Show a notification toast in NPC Studio.
3
- inputs:
4
- - message: ""
5
- - type: "info"
6
- steps:
7
- - name: frontend_action
8
- engine: python
9
- code: |
10
- context['output'] = "Action executed by frontend"
@@ -1,18 +0,0 @@
1
- context: |
2
- The npcsh NPC team is devoted to providing a safe and helpful
3
- environment for users where they can work and be as successful as possible.
4
- npcsh is a command-line tool that makes it easy for users to harness
5
- the power of LLMs from a command line shell. npcsh is a command line toolkit consisting of several programs.
6
- databases:
7
- - ~/npcsh_history.db
8
- mcp_servers:
9
- - ~/.npcsh/mcp_server.py
10
- use_global_jinxs: true
11
- forenpc: sibiji
12
- preferences:
13
- - If you come up with an idea, it is critical that you also provide a way to validate the idea.
14
- - Never change function names unless requested. keep things idempotent.
15
- - If plots are requested for python code, prefer to use matplotlib. Do not ever use seaborn.
16
- - Object oriented programming should be used sparingly and only when practical. Otherwise, opt for functional implementations.
17
- - Never write unit tests unless explicitly requested.
18
- - If we want you to write tests, we mean we want you to write example use cases that show how the code works.
@@ -1,13 +0,0 @@
1
- jinx_name: studio_open_pane
2
- description: Open a new pane in NPC Studio. Supports editor, terminal, browser, pdf, csv, chat, image, folder, and other content types.
3
- inputs:
4
- - type: ""
5
- - path: ""
6
- - position: "right"
7
- steps:
8
- - name: frontend_action
9
- engine: python
10
- code: |
11
- # This action is executed by the NPC Studio frontend
12
- # The frontend intercepts studio.* tool calls and handles them directly
13
- context['output'] = "Action executed by frontend"
@@ -1,9 +0,0 @@
1
- jinx_name: studio_read_pane
2
- description: Read the contents of a pane. For editor panes returns file content, for chat panes returns messages, for browser panes returns URL/title.
3
- inputs:
4
- - paneId: "active"
5
- steps:
6
- - name: frontend_action
7
- engine: python
8
- code: |
9
- context['output'] = "Action executed by frontend"
@@ -1,65 +0,0 @@
1
- jinx_name: "roll"
2
- description: "Generate a video from a text prompt."
3
- inputs:
4
- - prompt: ""
5
- - vgmodel: ""
6
- - vgprovider: ""
7
- - num_frames: 125
8
- - width: 256
9
- - height: 256
10
- - output_path: "output.mp4"
11
- steps:
12
- - name: "generate_video"
13
- engine: "python"
14
- code: |
15
- import traceback
16
- from npcpy.llm_funcs import gen_video
17
-
18
- prompt = context.get('prompt')
19
- num_frames = int(context.get('num_frames', 125))
20
- width = int(context.get('width', 256))
21
- height = int(context.get('height', 256))
22
- output_path = context.get('output_path')
23
- video_gen_model = context.get('vgmodel')
24
- video_gen_provider = context.get('vgprovider')
25
- output_messages = context.get('messages', [])
26
- current_npc = context.get('npc')
27
-
28
- if not prompt or not prompt.strip():
29
- context['output'] = "Usage: /roll <your prompt>"
30
- context['messages'] = output_messages
31
- exit()
32
-
33
- if not video_gen_model and current_npc and current_npc.model:
34
- video_gen_model = current_npc.model
35
- if not video_gen_provider and current_npc and current_npc.provider:
36
- video_gen_provider = current_npc.provider
37
-
38
- if not video_gen_model:
39
- video_gen_model = "stable-video-diffusion"
40
- if not video_gen_provider:
41
- video_gen_provider = "diffusers"
42
-
43
- try:
44
- result = gen_video(
45
- prompt=prompt,
46
- model=video_gen_model,
47
- provider=video_gen_provider,
48
- npc=current_npc,
49
- num_frames=num_frames,
50
- width=width,
51
- height=height,
52
- output_path=output_path,
53
- **context.get('api_kwargs', {})
54
- )
55
-
56
- if isinstance(result, dict):
57
- context['output'] = result.get('output', 'Video generated.')
58
- context['messages'] = result.get('messages', output_messages)
59
- else:
60
- context['output'] = str(result)
61
- context['messages'] = output_messages
62
- except Exception as e:
63
- traceback.print_exc()
64
- context['output'] = f"Error generating video: {e}"
65
- context['messages'] = output_messages
@@ -1,10 +0,0 @@
1
- jinx_name: studio_run_terminal
2
- description: Execute a command in a terminal pane.
3
- inputs:
4
- - paneId: "active"
5
- - command: ""
6
- steps:
7
- - name: frontend_action
8
- engine: python
9
- code: |
10
- context['output'] = "Action executed by frontend"
@@ -1,54 +0,0 @@
1
- jinx_name: search
2
- description: Unified search - routes to web_search, mem_search, kg_search, file_search, db_search
3
- inputs:
4
- - query: ""
5
- - type: "web"
6
- - provider: ""
7
- - num_results: ""
8
- - status: ""
9
- - npc_name: ""
10
- - team_name: ""
11
- - file_paths: ""
12
- - emodel: ""
13
- - eprovider: ""
14
- - concept: ""
15
- - db_path: ""
16
- - limit: ""
17
-
18
- steps:
19
- {% if type == "mem" %}
20
- - name: search
21
- engine: mem_search
22
- query: "{{ query }}"
23
- status: "{{ status }}"
24
- npc_name: "{{ npc_name }}"
25
- team_name: "{{ team_name }}"
26
- db_path: "{{ db_path }}"
27
- {% elif type == "kg" %}
28
- - name: search
29
- engine: kg_search
30
- query: "{{ query }}"
31
- concept: "{{ concept }}"
32
- npc_name: "{{ npc_name }}"
33
- team_name: "{{ team_name }}"
34
- db_path: "{{ db_path }}"
35
- {% elif type == "file" %}
36
- - name: search
37
- engine: file_search
38
- query: "{{ query }}"
39
- file_paths: "{{ file_paths }}"
40
- emodel: "{{ emodel }}"
41
- eprovider: "{{ eprovider }}"
42
- {% elif type == "db" %}
43
- - name: search
44
- engine: db_search
45
- query: "{{ query }}"
46
- db_path: "{{ db_path }}"
47
- limit: "{{ limit }}"
48
- {% else %}
49
- - name: search
50
- engine: web_search
51
- query: "{{ query }}"
52
- provider: "{{ provider }}"
53
- num_results: "{{ num_results }}"
54
- {% endif %}
@@ -1,10 +0,0 @@
1
- jinx_name: studio_send_message
2
- description: Send a message in a chat pane.
3
- inputs:
4
- - paneId: "active"
5
- - message: ""
6
- steps:
7
- - name: frontend_action
8
- engine: python
9
- code: |
10
- context['output'] = "Action executed by frontend"