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
@@ -0,0 +1,532 @@
1
+ jinx_name: init
2
+ description: Interactive TUI for initializing NPC projects with model detection and configuration
3
+ inputs:
4
+ - directory: ""
5
+ - templates: ""
6
+ - team_ctx: ""
7
+ - model: ""
8
+ - provider: ""
9
+ steps:
10
+ - name: init_wizard
11
+ engine: python
12
+ code: |
13
+ import os
14
+ import sys
15
+ import tty
16
+ import termios
17
+ import select
18
+ from pathlib import Path
19
+
20
+ # Direct mode: if directory passed explicitly or non-interactive
21
+ _direct_dir = (context.get('directory') or '').strip()
22
+ _direct = bool(_direct_dir) or not sys.stdin.isatty()
23
+
24
+ if _direct:
25
+ from npcpy.npc_compiler import initialize_npc_project
26
+ try:
27
+ initialize_npc_project(
28
+ directory=_direct_dir or '.',
29
+ templates=context.get('templates'),
30
+ context=context.get('team_ctx'),
31
+ model=context.get('model'),
32
+ provider=context.get('provider')
33
+ )
34
+ context['output'] = f"NPC project initialized in {os.path.abspath(_direct_dir or '.')}"
35
+ except Exception as e:
36
+ context['output'] = f"Error: {e}"
37
+ context['messages'] = context.get('messages', [])
38
+
39
+ else:
40
+ # ========== Detection Functions ==========
41
+ def detect_ollama_models():
42
+ models = []
43
+ try:
44
+ import ollama
45
+ result = ollama.list()
46
+ for model in result.get('models', []):
47
+ name = model.get('model', model.get('name', ''))
48
+ if name:
49
+ models.append(name)
50
+ except:
51
+ pass
52
+ return models
53
+
54
+ def detect_lm_studio():
55
+ try:
56
+ import requests
57
+ resp = requests.get('http://localhost:1234/v1/models', timeout=2)
58
+ if resp.status_code == 200:
59
+ return [m.get('id', '') for m in resp.json().get('data', [])]
60
+ except:
61
+ pass
62
+ return []
63
+
64
+ def detect_api_keys():
65
+ keys = {}
66
+ for key in ['ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'GEMINI_API_KEY', 'DEEPSEEK_API_KEY']:
67
+ if os.environ.get(key):
68
+ keys[key] = True
69
+ return keys
70
+
71
+ def get_cloud_models(api_keys):
72
+ models = []
73
+ if api_keys.get('ANTHROPIC_API_KEY'):
74
+ models.extend([('anthropic', 'claude-sonnet-4-20250514'), ('anthropic', 'claude-3-5-haiku-20241022')])
75
+ if api_keys.get('GEMINI_API_KEY'):
76
+ models.extend([('gemini', 'gemini-2.5-flash'), ('gemini', 'gemini-1.5-pro')])
77
+ if api_keys.get('DEEPSEEK_API_KEY'):
78
+ models.extend([('deepseek', 'deepseek-chat')])
79
+ return models
80
+
81
+ # ========== State ==========
82
+ class InitState:
83
+ def __init__(self):
84
+ self.phase = 0 # 0=dir, 1=detect, 2=model, 3=config, 4=confirm, 5=done
85
+ self.sel = 0
86
+ self.scroll = 0
87
+ self.directory = os.path.abspath(context.get('directory') or './npc_project')
88
+ self.editing = False
89
+ self.edit_buf = ""
90
+ self.edit_field = ""
91
+
92
+ # Detection results
93
+ self.ollama_models = []
94
+ self.lm_studio_models = []
95
+ self.cloud_models = []
96
+ self.api_keys = {}
97
+ self.all_models = [] # (provider, model) tuples
98
+
99
+ # Config
100
+ self.config = {
101
+ 'model': context.get('model') or '',
102
+ 'provider': context.get('provider') or '',
103
+ 'team_name': 'npc_team',
104
+ 'forenpc': 'forenpc',
105
+ 'context_desc': 'A team of AI agents',
106
+ }
107
+ self.config_keys = ['model', 'provider', 'team_name', 'forenpc', 'context_desc']
108
+ self.config_labels = {
109
+ 'model': 'Default Model',
110
+ 'provider': 'Default Provider',
111
+ 'team_name': 'Team Directory',
112
+ 'forenpc': 'Coordinator NPC',
113
+ 'context_desc': 'Team Description',
114
+ }
115
+ self.config_sel = 0
116
+
117
+ self.status = ""
118
+ self.error = ""
119
+ self.result = ""
120
+
121
+ ui = InitState()
122
+
123
+ # ========== Helpers ==========
124
+ def get_size():
125
+ try:
126
+ s = os.get_terminal_size()
127
+ return s.columns, s.lines
128
+ except:
129
+ return 80, 24
130
+
131
+ def run_detection():
132
+ ui.status = "Detecting Ollama models..."
133
+ render()
134
+ ui.ollama_models = detect_ollama_models()
135
+
136
+ ui.status = "Checking LM Studio..."
137
+ render()
138
+ ui.lm_studio_models = detect_lm_studio()
139
+
140
+ ui.status = "Checking API keys..."
141
+ render()
142
+ ui.api_keys = detect_api_keys()
143
+ ui.cloud_models = get_cloud_models(ui.api_keys)
144
+
145
+ # Build all_models list
146
+ ui.all_models = []
147
+ for m in ui.ollama_models:
148
+ ui.all_models.append(('ollama', m))
149
+ for m in ui.lm_studio_models:
150
+ ui.all_models.append(('lm_studio', m))
151
+ ui.all_models.extend(ui.cloud_models)
152
+
153
+ # Set defaults if found
154
+ if ui.all_models and not ui.config['model']:
155
+ ui.config['provider'], ui.config['model'] = ui.all_models[0]
156
+
157
+ total = len(ui.all_models)
158
+ ui.status = f"Found {total} models"
159
+
160
+ # ========== Rendering ==========
161
+ def render():
162
+ width, height = get_size()
163
+ out = []
164
+ out.append('\033[H') # Home cursor only, no full clear
165
+
166
+ phase_names = ['Directory', 'Detecting...', 'Select Model', 'Configure', 'Confirm', 'Complete']
167
+ phase_icons = ['1', '2', '3', '4', '5', '*']
168
+
169
+ # Progress bar
170
+ progress = ''
171
+ for i, name in enumerate(phase_names[:-1]):
172
+ if i < ui.phase:
173
+ progress += f'\033[32m[{phase_icons[i]}]\033[0m '
174
+ elif i == ui.phase:
175
+ progress += f'\033[33;1m[{phase_icons[i]}]\033[0m '
176
+ else:
177
+ progress += f'\033[90m[{phase_icons[i]}]\033[0m '
178
+
179
+ header = ' NPC INIT '
180
+ out.append(f'\033[1;1H\033[7;1m{header.ljust(width)}\033[0m')
181
+ out.append(f'\033[2;2H{progress}')
182
+
183
+ if ui.phase == 0:
184
+ render_directory(out, width, height)
185
+ elif ui.phase == 1:
186
+ render_detecting(out, width, height)
187
+ elif ui.phase == 2:
188
+ render_model_select(out, width, height)
189
+ elif ui.phase == 3:
190
+ render_config(out, width, height)
191
+ elif ui.phase == 4:
192
+ render_confirm(out, width, height)
193
+ elif ui.phase == 5:
194
+ render_done(out, width, height)
195
+
196
+ if ui.error:
197
+ out.append(f'\033[{height-1};1H\033[K\033[31m {ui.error[:width-3]}\033[0m')
198
+
199
+ sys.stdout.write(''.join(out))
200
+ sys.stdout.flush()
201
+
202
+ def render_directory(out, width, height):
203
+ banner = [
204
+ '\033[36m ██╗███╗ ██╗██╗████████╗\033[0m',
205
+ '\033[36m ██║████╗ ██║██║╚══██╔══╝\033[0m',
206
+ '\033[36m ██║██╔██╗ ██║██║ ██║ \033[0m',
207
+ '\033[36m ██║██║╚██╗██║██║ ██║ \033[0m',
208
+ '\033[36m ██║██║ ╚████║██║ ██║ \033[0m',
209
+ '\033[36m ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ \033[0m',
210
+ ]
211
+ for i, line in enumerate(banner):
212
+ out.append(f'\033[{4+i};3H{line}')
213
+
214
+ y = 4 + len(banner) + 2
215
+ out.append(f'\033[{y};3H\033[1mProject Directory:\033[0m')
216
+ y += 2
217
+
218
+ if ui.editing:
219
+ out.append(f'\033[{y};3H\033[7m > {ui.edit_buf}_ \033[0m')
220
+ else:
221
+ exists = os.path.exists(ui.directory)
222
+ status = '\033[33m(exists)\033[0m' if exists else '\033[32m(will create)\033[0m'
223
+ out.append(f'\033[{y};3H\033[7m > {ui.directory} \033[0m {status}')
224
+
225
+ y += 2
226
+ out.append(f'\033[{y};3H\033[90mThis will create:\033[0m')
227
+ y += 1
228
+ preview_dir = ui.edit_buf if ui.editing else ui.directory
229
+ out.append(f'\033[{y};5H\033[90m{preview_dir}/\033[0m')
230
+ out.append(f'\033[{y+1};5H\033[90m├── npc_team/\033[0m')
231
+ out.append(f'\033[{y+2};5H\033[90m│ ├── forenpc.npc\033[0m')
232
+ out.append(f'\033[{y+3};5H\033[90m│ ├── team.ctx\033[0m')
233
+ out.append(f'\033[{y+4};5H\033[90m│ ├── jinxs/\033[0m')
234
+ out.append(f'\033[{y+5};5H\033[90m│ └── tools/\033[0m')
235
+ out.append(f'\033[{y+6};5H\033[90m├── images/\033[0m')
236
+ out.append(f'\033[{y+7};5H\033[90m├── models/\033[0m')
237
+ out.append(f'\033[{y+8};5H\033[90m└── mcp_servers/\033[0m')
238
+
239
+ if ui.editing:
240
+ out.append(f'\033[{height};1H\033[K\033[7m Enter:Confirm Esc:Cancel \033[0m'.ljust(width))
241
+ else:
242
+ out.append(f'\033[{height};1H\033[K\033[7m e:Edit Enter:Next q:Quit \033[0m'.ljust(width))
243
+
244
+ def render_detecting(out, width, height):
245
+ mid = height // 2
246
+ out.append(f'\033[{mid-1};3H\033[1mDetecting available models...\033[0m')
247
+ out.append(f'\033[{mid+1};3H\033[33m{ui.status}\033[0m')
248
+
249
+ y = mid + 3
250
+ # Show what we've found so far
251
+ if ui.ollama_models:
252
+ out.append(f'\033[{y};5H\033[32m✓\033[0m Ollama: {len(ui.ollama_models)} models')
253
+ y += 1
254
+ if ui.lm_studio_models:
255
+ out.append(f'\033[{y};5H\033[32m✓\033[0m LM Studio: {len(ui.lm_studio_models)} models')
256
+ y += 1
257
+ for key in ['ANTHROPIC_API_KEY', 'GEMINI_API_KEY', 'DEEPSEEK_API_KEY']:
258
+ if ui.api_keys.get(key):
259
+ short = key.replace('_API_KEY', '').lower()
260
+ out.append(f'\033[{y};5H\033[32m✓\033[0m {short}')
261
+ y += 1
262
+
263
+ def render_model_select(out, width, height):
264
+ y = 4
265
+ out.append(f'\033[{y};3H\033[1mSelect Default Model:\033[0m')
266
+ y += 1
267
+
268
+ # Show detection summary
269
+ out.append(f'\033[{y};3H\033[90mDetected: {len(ui.ollama_models)} ollama, {len(ui.lm_studio_models)} lm_studio, {len(ui.cloud_models)} cloud\033[0m')
270
+ y += 2
271
+
272
+ list_height = height - 10
273
+ visible = ui.all_models[ui.scroll:ui.scroll + list_height]
274
+
275
+ for i, (provider, model) in enumerate(visible):
276
+ idx = ui.scroll + i
277
+ row = y + i
278
+
279
+ # Provider color
280
+ pcolor = {'ollama': '\033[33m', 'lm_studio': '\033[35m', 'anthropic': '\033[34m',
281
+ 'gemini': '\033[32m', 'deepseek': '\033[36m'}.get(provider, '\033[37m')
282
+
283
+ if idx == ui.sel:
284
+ out.append(f'\033[{row};2H\033[7;1m > {model:<40} {pcolor}{provider}\033[0m\033[7m \033[0m')
285
+ else:
286
+ out.append(f'\033[{row};2H {model:<40} {pcolor}{provider}\033[0m')
287
+
288
+ if not ui.all_models:
289
+ out.append(f'\033[{y};3H\033[31mNo models found! Install Ollama or set API keys.\033[0m')
290
+ out.append(f'\033[{y+2};3H\033[90mYou can still continue and configure manually.\033[0m')
291
+
292
+ out.append(f'\033[{height};1H\033[K\033[7m j/k:Navigate Enter:Select s:Skip q:Quit \033[0m'.ljust(width))
293
+
294
+ def render_config(out, width, height):
295
+ y = 4
296
+ out.append(f'\033[{y};3H\033[1mConfigure Team:\033[0m')
297
+ y += 2
298
+
299
+ for i, key in enumerate(ui.config_keys):
300
+ label = ui.config_labels[key]
301
+ val = ui.config[key]
302
+ row = y + i * 2
303
+
304
+ if ui.editing and ui.edit_field == key:
305
+ out.append(f'\033[{row};3H\033[33m{label}:\033[0m')
306
+ out.append(f'\033[{row+1};5H\033[7m {ui.edit_buf}_ \033[0m')
307
+ elif i == ui.config_sel:
308
+ out.append(f'\033[{row};3H\033[1m{label}:\033[0m')
309
+ out.append(f'\033[{row+1};5H\033[7m {val or "(not set)"} \033[0m')
310
+ else:
311
+ out.append(f'\033[{row};3H\033[90m{label}:\033[0m')
312
+ not_set = '\033[90m(not set)\033[0m'
313
+ out.append(f'\033[{row+1};5H {val or not_set}')
314
+
315
+ if ui.editing:
316
+ out.append(f'\033[{height};1H\033[K\033[7m Enter:Save Esc:Cancel \033[0m'.ljust(width))
317
+ else:
318
+ out.append(f'\033[{height};1H\033[K\033[7m j/k:Navigate e:Edit Enter:Next Backspace:Back q:Quit \033[0m'.ljust(width))
319
+
320
+ def render_confirm(out, width, height):
321
+ y = 4
322
+ out.append(f'\033[{y};3H\033[1mReady to Initialize:\033[0m')
323
+ y += 2
324
+
325
+ out.append(f'\033[{y};5H\033[1mDirectory:\033[0m {ui.directory}')
326
+ y += 1
327
+ out.append(f'\033[{y};5H\033[1mModel:\033[0m {ui.config["model"]} ({ui.config["provider"]})')
328
+ y += 1
329
+ out.append(f'\033[{y};5H\033[1mTeam:\033[0m {ui.config["team_name"]}')
330
+ y += 1
331
+ out.append(f'\033[{y};5H\033[1mCoordinator:\033[0m {ui.config["forenpc"]}')
332
+ y += 1
333
+ out.append(f'\033[{y};5H\033[1mDescription:\033[0m {ui.config["context_desc"]}')
334
+ y += 2
335
+
336
+ out.append(f'\033[{y};3H\033[90mWill create:\033[0m')
337
+ y += 1
338
+ files = [
339
+ f'{ui.directory}/{ui.config["team_name"]}/team.ctx',
340
+ f'{ui.directory}/{ui.config["team_name"]}/{ui.config["forenpc"]}.npc',
341
+ f'{ui.directory}/{ui.config["team_name"]}/jinxs/',
342
+ f'{ui.directory}/{ui.config["team_name"]}/tools/',
343
+ f'{ui.directory}/images/',
344
+ f'{ui.directory}/models/',
345
+ f'{ui.directory}/mcp_servers/',
346
+ ]
347
+ for f in files:
348
+ out.append(f'\033[{y};5H\033[90m{f}\033[0m')
349
+ y += 1
350
+
351
+ out.append(f'\033[{height};1H\033[K\033[7m Enter:Create Backspace:Back q:Quit \033[0m'.ljust(width))
352
+
353
+ def render_done(out, width, height):
354
+ y = 4
355
+ if ui.error:
356
+ out.append(f'\033[{y};3H\033[31;1mInitialization Failed\033[0m')
357
+ y += 2
358
+ out.append(f'\033[{y};3H\033[31m{ui.error}\033[0m')
359
+ else:
360
+ out.append(f'\033[{y};3H\033[32;1mProject Initialized!\033[0m')
361
+ y += 2
362
+ out.append(f'\033[{y};3H{ui.result}')
363
+ y += 2
364
+ out.append(f'\033[{y};3H\033[1mNext steps:\033[0m')
365
+ y += 1
366
+ out.append(f'\033[{y};5H\033[36mcd {ui.directory}\033[0m')
367
+ y += 1
368
+ out.append(f'\033[{y};5H\033[36mnpcsh\033[0m')
369
+ y += 2
370
+ out.append(f'\033[{y};3H\033[90mOr add more NPCs:\033[0m')
371
+ y += 1
372
+ out.append(f'\033[{y};5H\033[90mCreate {ui.config["team_name"]}/analyst.npc, {ui.config["team_name"]}/writer.npc, etc.\033[0m')
373
+
374
+ out.append(f'\033[{height};1H\033[K\033[7m Enter/q:Exit \033[0m'.ljust(width))
375
+
376
+ # ========== Execution ==========
377
+ def do_init():
378
+ from npcpy.npc_compiler import initialize_npc_project
379
+ try:
380
+ initialize_npc_project(
381
+ directory=ui.directory,
382
+ templates=context.get('templates'),
383
+ context=ui.config['context_desc'],
384
+ model=ui.config['model'],
385
+ provider=ui.config['provider']
386
+ )
387
+ ui.result = f"Created project at {os.path.abspath(ui.directory)}"
388
+ ui.error = ""
389
+ except Exception as e:
390
+ ui.error = str(e)
391
+ ui.result = ""
392
+
393
+ # ========== Input ==========
394
+ def handle_input(c, fd):
395
+ if ui.editing:
396
+ return handle_edit(c, fd)
397
+
398
+ # Escape sequences
399
+ if c == '\x1b':
400
+ if select.select([fd], [], [], 0.05)[0]:
401
+ c2 = os.read(fd, 1).decode('latin-1')
402
+ if c2 == '[':
403
+ c3 = os.read(fd, 1).decode('latin-1')
404
+ if c3 == 'A': move_up()
405
+ elif c3 == 'B': move_down()
406
+ return True
407
+
408
+ if c == 'q' or c == '\x03':
409
+ return False
410
+
411
+ if ui.phase == 0: # Directory
412
+ if c == 'e':
413
+ ui.editing = True
414
+ ui.edit_buf = ui.directory
415
+ elif c in ('\r', '\n'):
416
+ ui.phase = 1
417
+ render()
418
+ run_detection()
419
+ ui.phase = 2
420
+ ui.sel = 0
421
+ ui.scroll = 0
422
+
423
+ elif ui.phase == 2: # Model select
424
+ if c == 'j': move_down()
425
+ elif c == 'k': move_up()
426
+ elif c == 's': # Skip
427
+ ui.phase = 3
428
+ ui.config_sel = 0
429
+ elif c in ('\r', '\n'):
430
+ if ui.all_models and ui.sel < len(ui.all_models):
431
+ ui.config['provider'], ui.config['model'] = ui.all_models[ui.sel]
432
+ ui.phase = 3
433
+ ui.config_sel = 0
434
+
435
+ elif ui.phase == 3: # Config
436
+ if c == 'j': move_down()
437
+ elif c == 'k': move_up()
438
+ elif c == 'e':
439
+ key = ui.config_keys[ui.config_sel]
440
+ ui.editing = True
441
+ ui.edit_field = key
442
+ ui.edit_buf = ui.config[key]
443
+ elif c == '\x7f' or c == '\x08': # Backspace
444
+ ui.phase = 2
445
+ ui.sel = 0
446
+ elif c in ('\r', '\n'):
447
+ ui.phase = 4
448
+
449
+ elif ui.phase == 4: # Confirm
450
+ if c == '\x7f' or c == '\x08':
451
+ ui.phase = 3
452
+ ui.config_sel = 0
453
+ elif c in ('\r', '\n'):
454
+ ui.phase = 5
455
+ render()
456
+ do_init()
457
+
458
+ elif ui.phase == 5: # Done
459
+ if c in ('\r', '\n', 'q'):
460
+ return False
461
+
462
+ return True
463
+
464
+ def handle_edit(c, fd):
465
+ if c == '\x1b':
466
+ if select.select([fd], [], [], 0.05)[0]:
467
+ os.read(fd, 2)
468
+ ui.editing = False
469
+ ui.edit_buf = ""
470
+ return True
471
+ if c in ('\r', '\n'):
472
+ if ui.phase == 0:
473
+ ui.directory = os.path.abspath(os.path.expanduser(ui.edit_buf)) if ui.edit_buf else ui.directory
474
+ elif ui.phase == 3:
475
+ ui.config[ui.edit_field] = ui.edit_buf
476
+ ui.editing = False
477
+ return True
478
+ if c == '\x7f' or c == '\x08':
479
+ ui.edit_buf = ui.edit_buf[:-1]
480
+ return True
481
+ if c >= ' ' and c <= '~':
482
+ ui.edit_buf += c
483
+ return True
484
+
485
+ def move_up():
486
+ if ui.phase == 2:
487
+ ui.sel = max(0, ui.sel - 1)
488
+ if ui.sel < ui.scroll:
489
+ ui.scroll = ui.sel
490
+ elif ui.phase == 3:
491
+ ui.config_sel = max(0, ui.config_sel - 1)
492
+
493
+ def move_down():
494
+ _, height = get_size()
495
+ if ui.phase == 2:
496
+ ui.sel = min(len(ui.all_models) - 1, ui.sel + 1) if ui.all_models else 0
497
+ list_height = height - 10
498
+ if ui.sel >= ui.scroll + list_height:
499
+ ui.scroll = ui.sel - list_height + 1
500
+ elif ui.phase == 3:
501
+ ui.config_sel = min(len(ui.config_keys) - 1, ui.config_sel + 1)
502
+
503
+ # ========== Main Loop ==========
504
+ fd = sys.stdin.fileno()
505
+ old_settings = termios.tcgetattr(fd)
506
+
507
+ try:
508
+ tty.setcbreak(fd)
509
+ sys.stdout.write('\033[?25l') # hide cursor
510
+ sys.stdout.write('\033[2J\033[H') # initial full clear
511
+ sys.stdout.flush()
512
+ render()
513
+
514
+ while True:
515
+ c = os.read(fd, 1).decode('latin-1')
516
+ if not handle_input(c, fd):
517
+ break
518
+ render()
519
+ finally:
520
+ termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
521
+ sys.stdout.write('\033[?25h\033[2J\033[H')
522
+ sys.stdout.flush()
523
+
524
+ if ui.result:
525
+ print(ui.result)
526
+ context['output'] = ui.result
527
+ elif ui.error:
528
+ context['output'] = f"Error: {ui.error}"
529
+ else:
530
+ context['output'] = "Init cancelled."
531
+
532
+ context['messages'] = context.get('messages', [])
@@ -1,6 +1,5 @@
1
1
  jinx_name: jinxs
2
2
  description: Interactive jinx browser - browse, search, and preview available jinxs
3
- interactive: true
4
3
  inputs: []
5
4
  steps:
6
5
  - name: jinxs_browser
@@ -11,11 +11,11 @@ colors:
11
11
  top: "255,255,255"
12
12
  bottom: "173,216,230"
13
13
  primary_directive: |
14
- You are kadiefa, the exploratory snow leopard. You love to find new paths and to explore hidden gems.
15
- You go into caverns no cat has ventured into before. You climb peaks that others call crazy.
16
- Your role is to lead users on wandering explorations - deep research journeys that follow threads
17
- wherever they lead. You help users explore complex research questions and think outside the box.
18
- Use web search and browsing tools to discover new information.
14
+ You are kadiefa, the exploratory snow leopard. You wander into places no one else looks.
15
+ Think outside the box. Make unexpected connections across domains.
16
+ Reframe problems. Pull in ideas from biology, music, history, anywhere surprising.
17
+ Follow the most interesting thread, not the most obvious one.
19
18
  jinxs:
20
19
  - lib/core/python
21
- - bin/wander
20
+ - lib/core/sh
21
+ - lib/core/search/web_search
@@ -1,7 +1,11 @@
1
1
  jinx_name: kg
2
2
  description: Interactive knowledge graph browser - explore facts, concepts, and links
3
3
  interactive: true
4
- inputs: []
4
+ inputs:
5
+ - action: ""
6
+ - dream: false
7
+ - backfill: false
8
+ - ops: ""
5
9
  steps:
6
10
  - name: kg_browser
7
11
  engine: python
@@ -12,7 +16,70 @@ steps:
12
16
  import termios
13
17
  import select
14
18
 
15
- if not sys.stdin.isatty():
19
+ _kg_action = (context.get('action') or '').strip().lower()
20
+
21
+ if _kg_action in ('sleep', 'evolve', 'dream'):
22
+ # Route to KG evolution operations
23
+ import traceback
24
+ from npcpy.memory.command_history import CommandHistory, load_kg_from_db, save_kg_to_db
25
+ from npcpy.memory.knowledge_graph import kg_sleep_process, kg_dream_process, kg_backfill_from_memories
26
+
27
+ _npc = context.get('npc')
28
+ _team = context.get('team')
29
+ _msgs = context.get('messages', [])
30
+ _do_dream = _kg_action == 'dream' or str(context.get('dream', '')).lower() in ('true', '1', 'yes')
31
+ _do_backfill = str(context.get('backfill', '')).lower() in ('true', '1', 'yes')
32
+ _ops_str = context.get('ops', '')
33
+ _ops_config = [op.strip() for op in _ops_str.split(',') if op.strip()] if _ops_str else None
34
+
35
+ _model = (_npc.model if _npc and hasattr(_npc, 'model') else None) or (state.chat_model if state else 'llama3.2')
36
+ _provider = (_npc.provider if _npc and hasattr(_npc, 'provider') else None) or (state.chat_provider if state else 'ollama')
37
+
38
+ _team_name = _team.name if _team else '__none__'
39
+ _npc_name = _npc.name if _npc else '__none__'
40
+ _cur_path = os.getcwd()
41
+
42
+ try:
43
+ _db_path = os.getenv('NPCSH_DB_PATH', os.path.expanduser('~/npcsh_history.db'))
44
+ _ch = CommandHistory(_db_path)
45
+ _eng = _ch.engine
46
+
47
+ _result = ''
48
+ if _do_backfill:
49
+ print('Backfilling from approved memories...')
50
+ _stats = kg_backfill_from_memories(_eng, model=_model, provider=_provider, npc=_npc, get_concepts=True, dry_run=False)
51
+ _result += f"Backfill: +{_stats['facts_after'] - _stats['facts_before']} facts, +{_stats['concepts_after'] - _stats['concepts_before']} concepts\n"
52
+
53
+ _kg = load_kg_from_db(_eng, _team_name, _npc_name, _cur_path)
54
+ if not _kg or not _kg.get('facts'):
55
+ context['output'] = _result + 'Knowledge graph is empty. Use /kg backfill=true or have conversations first.'
56
+ context['messages'] = _msgs
57
+ _ch.close()
58
+ exit()
59
+
60
+ _f0 = len(_kg.get('facts', []))
61
+ _c0 = len(_kg.get('concepts', []))
62
+ _label = 'Sleep'
63
+
64
+ _kg, _ = kg_sleep_process(existing_kg=_kg, model=_model, provider=_provider, npc=_npc, operations_config=_ops_config)
65
+
66
+ if _do_dream:
67
+ _label += ' & Dream'
68
+ _kg, _ = kg_dream_process(existing_kg=_kg, model=_model, provider=_provider, npc=_npc)
69
+
70
+ save_kg_to_db(_eng, _kg, _team_name, _npc_name, _cur_path)
71
+ _f1 = len(_kg.get('facts', []))
72
+ _c1 = len(_kg.get('concepts', []))
73
+ _result += f"{_label} complete. Facts: {_f0} -> {_f1} ({_f1-_f0:+}), Concepts: {_c0} -> {_c1} ({_c1-_c0:+})"
74
+ context['output'] = _result
75
+ context['messages'] = _msgs
76
+ _ch.close()
77
+ except Exception as e:
78
+ traceback.print_exc()
79
+ context['output'] = f'KG evolution error: {e}'
80
+ context['messages'] = _msgs
81
+
82
+ elif not sys.stdin.isatty():
16
83
  context['output'] = "KG browser requires an interactive terminal."
17
84
 
18
85
  else:
@@ -0,0 +1,34 @@
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
+
7
+ ## How to solve tasks with tools
8
+ You have access to tools (jinxs) like sh, python, edit_file, load_file, etc.
9
+ When solving tasks that require action:
10
+ 1. PLAN: Break the task into concrete steps before doing anything.
11
+ 2. ORIENT: Check your environment first (ls, pwd, which, cat). Never assume files or tools exist.
12
+ 3. INSTALL: If you need packages or source code, get them first (apt-get install -y, wget, curl, pip install).
13
+ 4. EXECUTE: Run one command at a time. Read the output carefully before proceeding.
14
+ 5. ADAPT: If a command fails, read the error. Diagnose the actual cause and fix it. Never repeat the same failed command without changing something.
15
+ 6. VERIFY: Confirm your work (check files exist, test the output).
16
+
17
+ Common mistakes to avoid:
18
+ - Running ./configure or make before downloading and extracting source code
19
+ - Retrying the exact same command that just failed
20
+ - Not checking what directory you're in or what files are present
21
+ - Trying to use a tool/package without installing it first
22
+ databases:
23
+ - ~/npcsh_history.db
24
+ mcp_servers:
25
+ - ~/.npcsh/mcp_server.py
26
+ use_global_jinxs: true
27
+ forenpc: sibiji
28
+ preferences:
29
+ - If you come up with an idea, it is critical that you also provide a way to validate the idea.
30
+ - Never change function names unless requested. keep things idempotent.
31
+ - If plots are requested for python code, prefer to use matplotlib. Do not ever use seaborn.
32
+ - Object oriented programming should be used sparingly and only when practical. Otherwise, opt for functional implementations.
33
+ - Never write unit tests unless explicitly requested.
34
+ - If we want you to write tests, we mean we want you to write example use cases that show how the code works.