npcsh 1.1.19__py3-none-any.whl → 1.1.21__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 (173) hide show
  1. npcsh/_state.py +16 -78
  2. npcsh/diff_viewer.py +3 -3
  3. npcsh/npc_team/jinxs/lib/core/compress.jinx +373 -85
  4. npcsh/npc_team/jinxs/lib/core/search/db_search.jinx +18 -7
  5. npcsh/npc_team/jinxs/lib/core/search/file_search.jinx +18 -7
  6. npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +20 -9
  7. npcsh/npc_team/jinxs/lib/core/search/web_search.jinx +53 -15
  8. npcsh/npc_team/jinxs/{bin → lib/utils}/benchmark.jinx +2 -2
  9. npcsh/npc_team/jinxs/lib/utils/jinxs.jinx +393 -317
  10. npcsh/npc_team/jinxs/lib/utils/models.jinx +343 -0
  11. npcsh/npc_team/jinxs/{bin → lib/utils}/setup.jinx +8 -7
  12. npcsh/npc_team/jinxs/modes/alicanto.jinx +1573 -296
  13. npcsh/npc_team/jinxs/modes/arxiv.jinx +6 -6
  14. npcsh/npc_team/jinxs/modes/config_tui.jinx +300 -0
  15. npcsh/npc_team/jinxs/modes/corca.jinx +4 -4
  16. npcsh/npc_team/jinxs/modes/git.jinx +795 -0
  17. npcsh/npc_team/jinxs/modes/guac.jinx +4 -4
  18. npcsh/npc_team/jinxs/modes/kg.jinx +941 -0
  19. npcsh/npc_team/jinxs/modes/memories.jinx +414 -0
  20. npcsh/npc_team/jinxs/modes/nql.jinx +460 -0
  21. npcsh/npc_team/jinxs/modes/papers.jinx +578 -0
  22. npcsh/npc_team/jinxs/modes/plonk.jinx +490 -304
  23. npcsh/npc_team/jinxs/modes/pti.jinx +1 -1
  24. npcsh/npc_team/jinxs/modes/reattach.jinx +4 -4
  25. npcsh/npc_team/jinxs/modes/spool.jinx +4 -4
  26. npcsh/npc_team/jinxs/modes/team.jinx +504 -0
  27. npcsh/npc_team/jinxs/modes/vixynt.jinx +388 -0
  28. npcsh/npc_team/jinxs/modes/wander.jinx +455 -182
  29. npcsh/npc_team/jinxs/modes/yap.jinx +10 -3
  30. npcsh/npcsh.py +112 -47
  31. npcsh/routes.py +12 -3
  32. npcsh/salmon_simulation.py +0 -0
  33. npcsh-1.1.21.data/data/npcsh/npc_team/alicanto.jinx +1633 -0
  34. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/arxiv.jinx +6 -6
  35. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/benchmark.jinx +2 -2
  36. npcsh-1.1.21.data/data/npcsh/npc_team/compress.jinx +428 -0
  37. npcsh-1.1.21.data/data/npcsh/npc_team/config_tui.jinx +300 -0
  38. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca.jinx +4 -4
  39. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/db_search.jinx +18 -7
  40. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/file_search.jinx +18 -7
  41. npcsh-1.1.21.data/data/npcsh/npc_team/git.jinx +795 -0
  42. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/guac.jinx +4 -4
  43. npcsh-1.1.21.data/data/npcsh/npc_team/jinxs.jinx +407 -0
  44. npcsh-1.1.21.data/data/npcsh/npc_team/kg.jinx +941 -0
  45. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/kg_search.jinx +20 -9
  46. npcsh-1.1.21.data/data/npcsh/npc_team/memories.jinx +414 -0
  47. npcsh-1.1.21.data/data/npcsh/npc_team/models.jinx +343 -0
  48. npcsh-1.1.21.data/data/npcsh/npc_team/nql.jinx +460 -0
  49. npcsh-1.1.21.data/data/npcsh/npc_team/papers.jinx +578 -0
  50. npcsh-1.1.21.data/data/npcsh/npc_team/plonk.jinx +565 -0
  51. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/pti.jinx +1 -1
  52. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/reattach.jinx +4 -4
  53. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/setup.jinx +8 -7
  54. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/spool.jinx +4 -4
  55. npcsh-1.1.21.data/data/npcsh/npc_team/team.jinx +504 -0
  56. npcsh-1.1.21.data/data/npcsh/npc_team/vixynt.jinx +388 -0
  57. npcsh-1.1.21.data/data/npcsh/npc_team/wander.jinx +728 -0
  58. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/web_search.jinx +53 -15
  59. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/yap.jinx +10 -3
  60. {npcsh-1.1.19.dist-info → npcsh-1.1.21.dist-info}/METADATA +2 -2
  61. {npcsh-1.1.19.dist-info → npcsh-1.1.21.dist-info}/RECORD +147 -148
  62. npcsh-1.1.21.dist-info/entry_points.txt +11 -0
  63. npcsh/npc_team/jinxs/bin/config_tui.jinx +0 -299
  64. npcsh/npc_team/jinxs/bin/memories.jinx +0 -316
  65. npcsh/npc_team/jinxs/bin/nql.jinx +0 -141
  66. npcsh/npc_team/jinxs/bin/team_tui.jinx +0 -327
  67. npcsh/npc_team/jinxs/bin/vixynt.jinx +0 -122
  68. npcsh/npc_team/jinxs/lib/core/search/mem_review.jinx +0 -73
  69. npcsh/npc_team/jinxs/lib/core/search/mem_search.jinx +0 -388
  70. npcsh/npc_team/jinxs/lib/research/paper_search.jinx +0 -412
  71. npcsh/npc_team/jinxs/lib/research/semantic_scholar.jinx +0 -386
  72. npcsh/npc_team/plonkjr.npc +0 -23
  73. npcsh-1.1.19.data/data/npcsh/npc_team/alicanto.jinx +0 -356
  74. npcsh-1.1.19.data/data/npcsh/npc_team/compress.jinx +0 -140
  75. npcsh-1.1.19.data/data/npcsh/npc_team/config_tui.jinx +0 -299
  76. npcsh-1.1.19.data/data/npcsh/npc_team/jinxs.jinx +0 -331
  77. npcsh-1.1.19.data/data/npcsh/npc_team/mem_review.jinx +0 -73
  78. npcsh-1.1.19.data/data/npcsh/npc_team/mem_search.jinx +0 -388
  79. npcsh-1.1.19.data/data/npcsh/npc_team/memories.jinx +0 -316
  80. npcsh-1.1.19.data/data/npcsh/npc_team/nql.jinx +0 -141
  81. npcsh-1.1.19.data/data/npcsh/npc_team/paper_search.jinx +0 -412
  82. npcsh-1.1.19.data/data/npcsh/npc_team/plonk.jinx +0 -379
  83. npcsh-1.1.19.data/data/npcsh/npc_team/plonkjr.npc +0 -23
  84. npcsh-1.1.19.data/data/npcsh/npc_team/semantic_scholar.jinx +0 -386
  85. npcsh-1.1.19.data/data/npcsh/npc_team/team_tui.jinx +0 -327
  86. npcsh-1.1.19.data/data/npcsh/npc_team/vixynt.jinx +0 -122
  87. npcsh-1.1.19.data/data/npcsh/npc_team/wander.jinx +0 -455
  88. npcsh-1.1.19.dist-info/entry_points.txt +0 -22
  89. /npcsh/npc_team/jinxs/lib/{orchestration → core}/convene.jinx +0 -0
  90. /npcsh/npc_team/jinxs/lib/{orchestration → core}/delegate.jinx +0 -0
  91. /npcsh/npc_team/jinxs/{bin → lib/core}/sample.jinx +0 -0
  92. /npcsh/npc_team/jinxs/{bin → lib/utils}/sync.jinx +0 -0
  93. /npcsh/npc_team/jinxs/{bin → modes}/roll.jinx +0 -0
  94. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/add_tab.jinx +0 -0
  95. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/alicanto.npc +0 -0
  96. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/alicanto.png +0 -0
  97. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/browser_action.jinx +0 -0
  98. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/browser_screenshot.jinx +0 -0
  99. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/build.jinx +0 -0
  100. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/chat.jinx +0 -0
  101. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/click.jinx +0 -0
  102. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
  103. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/close_pane.jinx +0 -0
  104. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/close_tab.jinx +0 -0
  105. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/cmd.jinx +0 -0
  106. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/compile.jinx +0 -0
  107. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/confirm.jinx +0 -0
  108. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/convene.jinx +0 -0
  109. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca.npc +0 -0
  110. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca.png +0 -0
  111. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/corca_example.png +0 -0
  112. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/delegate.jinx +0 -0
  113. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/edit_file.jinx +0 -0
  114. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/focus_pane.jinx +0 -0
  115. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/frederic.npc +0 -0
  116. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/frederic4.png +0 -0
  117. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/guac.npc +0 -0
  118. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/guac.png +0 -0
  119. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/help.jinx +0 -0
  120. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/incognide.jinx +0 -0
  121. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/init.jinx +0 -0
  122. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/kadiefa.npc +0 -0
  123. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/kadiefa.png +0 -0
  124. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/key_press.jinx +0 -0
  125. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/launch_app.jinx +0 -0
  126. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/list_panes.jinx +0 -0
  127. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/load_file.jinx +0 -0
  128. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/navigate.jinx +0 -0
  129. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/notify.jinx +0 -0
  130. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
  131. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
  132. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/open_browser.jinx +0 -0
  133. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/open_pane.jinx +0 -0
  134. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/ots.jinx +0 -0
  135. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/paste.jinx +0 -0
  136. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/plonk.npc +0 -0
  137. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/plonk.png +0 -0
  138. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/plonkjr.png +0 -0
  139. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/python.jinx +0 -0
  140. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/read_pane.jinx +0 -0
  141. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/roll.jinx +0 -0
  142. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/run_terminal.jinx +0 -0
  143. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sample.jinx +0 -0
  144. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/screenshot.jinx +0 -0
  145. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/search.jinx +0 -0
  146. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/send_message.jinx +0 -0
  147. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/serve.jinx +0 -0
  148. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/set.jinx +0 -0
  149. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sh.jinx +0 -0
  150. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/shh.jinx +0 -0
  151. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sibiji.npc +0 -0
  152. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sibiji.png +0 -0
  153. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sleep.jinx +0 -0
  154. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/split_pane.jinx +0 -0
  155. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/spool.png +0 -0
  156. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sql.jinx +0 -0
  157. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switch.jinx +0 -0
  158. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switch_npc.jinx +0 -0
  159. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switch_tab.jinx +0 -0
  160. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/switches.jinx +0 -0
  161. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/sync.jinx +0 -0
  162. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/teamviz.jinx +0 -0
  163. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/trigger.jinx +0 -0
  164. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/type_text.jinx +0 -0
  165. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/usage.jinx +0 -0
  166. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/verbose.jinx +0 -0
  167. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/wait.jinx +0 -0
  168. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/write_file.jinx +0 -0
  169. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/yap.png +0 -0
  170. {npcsh-1.1.19.data → npcsh-1.1.21.data}/data/npcsh/npc_team/zen_mode.jinx +0 -0
  171. {npcsh-1.1.19.dist-info → npcsh-1.1.21.dist-info}/WHEEL +0 -0
  172. {npcsh-1.1.19.dist-info → npcsh-1.1.21.dist-info}/licenses/LICENSE +0 -0
  173. {npcsh-1.1.19.dist-info → npcsh-1.1.21.dist-info}/top_level.txt +0 -0
@@ -1,141 +0,0 @@
1
- jinx_name: nql
2
- description: "Run NPC-SQL models with AI-powered transformations. Supports cron scheduling."
3
- inputs:
4
- - models_dir: "~/.npcsh/npc_team/models"
5
- - db: "~/npcsh_history.db"
6
- - model: ""
7
- - schema: ""
8
- - show: ""
9
- - cron: ""
10
- - install_cron: ""
11
-
12
- steps:
13
- - name: run_nql
14
- engine: python
15
- code: |
16
- import os
17
- import sys
18
- from pathlib import Path
19
-
20
- models_dir = context.get('models_dir') or '~/.npcsh/npc_team/models'
21
- db_path = context.get('db') or '~/npcsh_history.db'
22
- model_name = context.get('model') or ''
23
- schema = context.get('schema') or ''
24
- list_models = context.get('show') or ''
25
- cron_expr = context.get('cron') or ''
26
- install_cron = context.get('install_cron') or ''
27
-
28
- models_dir = os.path.expanduser(models_dir)
29
- db_path = os.path.expanduser(db_path)
30
-
31
- # Find NPC team directory
32
- npc_dir = None
33
- if os.path.exists('./npc_team'):
34
- npc_dir = './npc_team'
35
- elif os.path.exists(os.path.expanduser('~/.npcsh/npc_team')):
36
- npc_dir = os.path.expanduser('~/.npcsh/npc_team')
37
-
38
- # Import npcsql
39
- try:
40
- from npcpy.sql.npcsql import ModelCompiler, SQLModel
41
- except ImportError:
42
- output = "Error: npcpy.sql.npcsql not found. Install npcpy first."
43
- raise SystemExit(1)
44
-
45
- # List models mode
46
- if list_models:
47
- if not os.path.exists(models_dir):
48
- output = "No models directory found at " + models_dir
49
- else:
50
- models_path = Path(models_dir)
51
- sql_files = list(models_path.glob("**/*.sql"))
52
- if not sql_files:
53
- output = "No .sql models found in " + models_dir
54
- else:
55
- lines = ["Available NQL models in " + models_dir + ":", ""]
56
- for f in sorted(sql_files):
57
- rel = f.relative_to(models_path)
58
- # Check for nql functions
59
- with open(f, 'r') as fh:
60
- content = fh.read()
61
- has_nql = "nql." in content
62
- marker = " [NQL]" if has_nql else ""
63
- lines.append(" " + str(rel) + marker)
64
- output = "\n".join(lines)
65
-
66
- # Install cron mode
67
- elif install_cron:
68
- import subprocess
69
- # Parse cron expression and model
70
- parts = install_cron.split()
71
- if len(parts) < 5:
72
- output = "Error: cron expression must have 5 fields (min hour day month weekday)"
73
- else:
74
- cron_time = " ".join(parts[:5])
75
- cron_model = parts[5] if len(parts) > 5 else ""
76
-
77
- # Build command
78
- nql_cmd = "npc nql"
79
- if cron_model:
80
- nql_cmd += " model=" + cron_model
81
- nql_cmd += " models_dir=" + models_dir
82
- nql_cmd += " db=" + db_path
83
- if schema:
84
- nql_cmd += " schema=" + schema
85
-
86
- cron_line = cron_time + " " + nql_cmd
87
-
88
- # Get current crontab
89
- try:
90
- result = subprocess.run(['crontab', '-l'], capture_output=True, text=True)
91
- current = result.stdout if result.returncode == 0 else ""
92
- except:
93
- current = ""
94
-
95
- # Check if already installed
96
- if nql_cmd in current:
97
- output = "Cron job already exists for: " + nql_cmd
98
- else:
99
- # Add new cron line
100
- new_crontab = current.rstrip() + "\n" + cron_line + "\n"
101
- proc = subprocess.run(['crontab', '-'], input=new_crontab, text=True, capture_output=True)
102
- if proc.returncode == 0:
103
- output = "Installed cron job:\n " + cron_line
104
- else:
105
- output = "Failed to install cron: " + proc.stderr
106
-
107
- # Run models mode
108
- else:
109
- if not os.path.exists(models_dir):
110
- output = "Models directory not found: " + models_dir + "\nCreate it with: mkdir -p " + models_dir
111
- else:
112
- try:
113
- compiler = ModelCompiler(
114
- models_dir=models_dir,
115
- target_engine=db_path,
116
- npc_directory=npc_dir,
117
- target_schema=schema if schema else None
118
- )
119
-
120
- if model_name:
121
- # Run specific model
122
- compiler.discover_models()
123
- if model_name not in compiler.models:
124
- available = list(compiler.models.keys())
125
- output = "Model '" + model_name + "' not found. Available: " + str(available)
126
- else:
127
- print("Running model: " + model_name)
128
- df = compiler.execute_model(model_name)
129
- output = "Model '" + model_name + "' completed. Rows: " + str(len(df))
130
- else:
131
- # Run all models in dependency order
132
- results = compiler.run_all_models()
133
- lines = ["NQL run complete:", ""]
134
- for name, df in results.items():
135
- lines.append(" " + name + ": " + str(len(df)) + " rows")
136
- output = "\n".join(lines)
137
-
138
- except Exception as e:
139
- output = "NQL Error: " + str(e)
140
- import traceback
141
- traceback.print_exc()
@@ -1,327 +0,0 @@
1
- jinx_name: team_tui
2
- description: Interactive TUI for managing team context, NPCs, and jinxs
3
- inputs: []
4
- steps:
5
- - name: team_manager
6
- engine: python
7
- code: |
8
- import os
9
- import sys
10
- import tty
11
- import termios
12
- import select
13
- import yaml
14
- from pathlib import Path
15
-
16
- if not sys.stdin.isatty():
17
- context['output'] = "Team TUI requires an interactive terminal."
18
- return
19
-
20
- team = context.get('team')
21
- if not team:
22
- context['output'] = "No team loaded."
23
- return
24
-
25
- # ========== State ==========
26
- class TeamState:
27
- def __init__(self):
28
- self.tab = 0 # 0=Team Context, 1=NPCs, 2=Jinxs
29
- self.tabs = ['Team Context', 'NPCs', 'Jinxs']
30
- self.selected_idx = 0
31
- self.scroll_offset = 0
32
- self.editing = False
33
- self.edit_field = None
34
- self.edit_buffer = ""
35
- self.edit_cursor = 0
36
- self.status = ""
37
- self.team_ctx = {}
38
- self.npcs = []
39
- self.jinxs = []
40
-
41
- state = TeamState()
42
-
43
- # ========== Helpers ==========
44
- def get_size():
45
- try:
46
- s = os.get_terminal_size()
47
- return s.columns, s.lines
48
- except:
49
- return 80, 24
50
-
51
- def load_team_data():
52
- """Load team context, NPCs, and jinxs."""
53
- # Load team.ctx
54
- if hasattr(team, 'team_ctx'):
55
- state.team_ctx = team.team_ctx or {}
56
- else:
57
- ctx_path = Path(team.team_path) / 'team.ctx'
58
- if ctx_path.exists():
59
- with open(ctx_path) as f:
60
- state.team_ctx = yaml.safe_load(f) or {}
61
-
62
- # Load NPCs
63
- if hasattr(team, 'npcs'):
64
- state.npcs = list(team.npcs.keys())
65
- else:
66
- npc_dir = Path(team.team_path)
67
- state.npcs = [f.stem for f in npc_dir.glob('*.npc')]
68
-
69
- # Load Jinxs count by folder
70
- jinxs_dir = Path(team.team_path) / 'jinxs'
71
- state.jinxs = []
72
- if jinxs_dir.exists():
73
- for subdir in sorted(jinxs_dir.iterdir()):
74
- if subdir.is_dir():
75
- count = len(list(subdir.glob('*.jinx')))
76
- state.jinxs.append((subdir.name, count))
77
-
78
- def save_team_ctx():
79
- """Save team.ctx file."""
80
- ctx_path = Path(team.team_path) / 'team.ctx'
81
- with open(ctx_path, 'w') as f:
82
- yaml.dump(state.team_ctx, f, default_flow_style=False)
83
- state.status = "Team context saved!"
84
-
85
- # ========== Rendering ==========
86
- def render_screen():
87
- width, height = get_size()
88
- out = []
89
- out.append("\033[2J\033[H")
90
-
91
- # Header
92
- team_name = getattr(team, 'name', 'Unknown')
93
- header = f" Team: {team_name} "
94
- out.append(f"\033[1;1H\033[44;37;1m{'=' * width}\033[0m")
95
- out.append(f"\033[1;{(width - len(header)) // 2}H\033[44;37;1m{header}\033[0m")
96
-
97
- # Tabs
98
- tab_str = ""
99
- for i, tab in enumerate(state.tabs):
100
- if i == state.tab:
101
- tab_str += f"\033[47;30m [{tab}] \033[0m"
102
- else:
103
- tab_str += f" [{tab}] "
104
- out.append(f"\033[2;2H{tab_str}")
105
-
106
- # Content area
107
- out.append(f"\033[3;1H\033[90m{'─' * width}\033[0m")
108
-
109
- if state.tab == 0:
110
- render_team_ctx(out, width, height)
111
- elif state.tab == 1:
112
- render_npcs(out, width, height)
113
- elif state.tab == 2:
114
- render_jinxs(out, width, height)
115
-
116
- # Status
117
- if state.status:
118
- out.append(f"\033[{height-2};2H\033[33m{state.status}\033[0m")
119
-
120
- # Footer
121
- if state.editing:
122
- footer = "[Enter] Save [Esc] Cancel"
123
- else:
124
- footer = "[Tab] Switch Tab [j/k] Navigate [e] Edit [s] Save [q] Quit"
125
- out.append(f"\033[{height};1H\033[90m{footer[:width]}\033[0m")
126
-
127
- sys.stdout.write(''.join(out))
128
- sys.stdout.flush()
129
-
130
- def render_team_ctx(out, width, height):
131
- """Render team context tab."""
132
- fields = [
133
- ('forenpc', 'Forenpc'),
134
- ('model', 'Model'),
135
- ('provider', 'Provider'),
136
- ('context', 'Context'),
137
- ]
138
-
139
- row = 5
140
- for i, (key, label) in enumerate(fields):
141
- value = state.team_ctx.get(key, '')
142
- if isinstance(value, str) and len(value) > 50:
143
- value = value[:50] + '...'
144
-
145
- if i == state.selected_idx:
146
- if state.editing:
147
- out.append(f"\033[{row};4H\033[1m{label}:\033[0m")
148
- out.append(f"\033[{row+1};6H{state.edit_buffer[:width-10]}\033[7m \033[0m")
149
- row += 2
150
- else:
151
- out.append(f"\033[{row};4H\033[47;30m{label}: {value}\033[0m")
152
- row += 1
153
- else:
154
- if value:
155
- out.append(f"\033[{row};4H{label}: \033[32m{value}\033[0m")
156
- else:
157
- out.append(f"\033[{row};4H{label}: \033[90m(not set)\033[0m")
158
- row += 1
159
-
160
- def render_npcs(out, width, height):
161
- """Render NPCs tab."""
162
- visible_height = height - 8
163
- visible = state.npcs[state.scroll_offset:state.scroll_offset + visible_height]
164
-
165
- row = 5
166
- for i, npc_name in enumerate(visible):
167
- idx = i + state.scroll_offset
168
- npc_obj = team.npcs.get(npc_name) if hasattr(team, 'npcs') else None
169
-
170
- if npc_obj:
171
- model_info = f"{npc_obj.model or 'default'}/{npc_obj.provider or 'default'}"
172
- else:
173
- model_info = ""
174
-
175
- if idx == state.selected_idx:
176
- out.append(f"\033[{row};4H\033[47;30m> {npc_name:<15} {model_info}\033[0m")
177
- else:
178
- out.append(f"\033[{row};4H {npc_name:<15} \033[90m{model_info}\033[0m")
179
- row += 1
180
-
181
- if not state.npcs:
182
- out.append(f"\033[5;4H\033[90mNo NPCs found in team.\033[0m")
183
-
184
- def render_jinxs(out, width, height):
185
- """Render Jinxs tab."""
186
- visible_height = height - 8
187
- visible = state.jinxs[state.scroll_offset:state.scroll_offset + visible_height]
188
-
189
- row = 5
190
- for i, (folder, count) in enumerate(visible):
191
- idx = i + state.scroll_offset
192
- if idx == state.selected_idx:
193
- out.append(f"\033[{row};4H\033[47;30m> {folder}/ ({count} jinxs)\033[0m")
194
- else:
195
- out.append(f"\033[{row};4H {folder}/ \033[90m({count} jinxs)\033[0m")
196
- row += 1
197
-
198
- if not state.jinxs:
199
- out.append(f"\033[5;4H\033[90mNo jinxs folders found.\033[0m")
200
-
201
- # ========== Input Handling ==========
202
- def handle_input(c):
203
- if state.editing:
204
- return handle_edit_input(c)
205
-
206
- if c == 'q':
207
- return False
208
-
209
- if c == '\t': # Tab - switch tabs
210
- state.tab = (state.tab + 1) % len(state.tabs)
211
- state.selected_idx = 0
212
- state.scroll_offset = 0
213
- state.status = ""
214
-
215
- elif c == '\x1b': # Escape sequence
216
- if select.select([sys.stdin], [], [], 0.05)[0]:
217
- c2 = sys.stdin.read(1)
218
- if c2 == '[':
219
- c3 = sys.stdin.read(1)
220
- if c3 == 'A': # Up
221
- move_up()
222
- elif c3 == 'B': # Down
223
- move_down()
224
-
225
- elif c == 'k':
226
- move_up()
227
- elif c == 'j':
228
- move_down()
229
- elif c == 'e' or c == '\r' or c == '\n':
230
- start_edit()
231
- elif c == 's':
232
- save_team_ctx()
233
-
234
- return True
235
-
236
- def handle_edit_input(c):
237
- if c == '\x1b': # Escape - cancel
238
- state.editing = False
239
- state.edit_buffer = ""
240
- state.status = "Edit cancelled"
241
- return True
242
-
243
- if c == '\r' or c == '\n': # Enter - save
244
- if state.edit_field:
245
- state.team_ctx[state.edit_field] = state.edit_buffer
246
- state.status = f"Updated {state.edit_field}"
247
- state.editing = False
248
- state.edit_buffer = ""
249
- state.edit_field = None
250
- return True
251
-
252
- if c == '\x7f' or c == '\x08': # Backspace
253
- if state.edit_cursor > 0:
254
- state.edit_buffer = state.edit_buffer[:state.edit_cursor-1] + state.edit_buffer[state.edit_cursor:]
255
- state.edit_cursor -= 1
256
-
257
- elif c >= ' ' and c <= '~': # Printable
258
- state.edit_buffer = state.edit_buffer[:state.edit_cursor] + c + state.edit_buffer[state.edit_cursor:]
259
- state.edit_cursor += 1
260
-
261
- return True
262
-
263
- def move_up():
264
- state.selected_idx = max(0, state.selected_idx - 1)
265
- if state.selected_idx < state.scroll_offset:
266
- state.scroll_offset = state.selected_idx
267
- state.status = ""
268
-
269
- def move_down():
270
- _, height = get_size()
271
- visible_height = height - 8
272
-
273
- if state.tab == 0:
274
- max_idx = 3 # 4 fields in team ctx
275
- elif state.tab == 1:
276
- max_idx = len(state.npcs) - 1
277
- else:
278
- max_idx = len(state.jinxs) - 1
279
-
280
- state.selected_idx = min(max_idx, state.selected_idx + 1)
281
- if state.selected_idx >= state.scroll_offset + visible_height:
282
- state.scroll_offset = state.selected_idx - visible_height + 1
283
- state.status = ""
284
-
285
- def start_edit():
286
- if state.tab == 0:
287
- fields = ['forenpc', 'model', 'provider', 'context']
288
- if state.selected_idx < len(fields):
289
- state.edit_field = fields[state.selected_idx]
290
- state.edit_buffer = str(state.team_ctx.get(state.edit_field, ''))
291
- state.edit_cursor = len(state.edit_buffer)
292
- state.editing = True
293
- state.status = "Editing..."
294
- elif state.tab == 1:
295
- if state.npcs and state.selected_idx < len(state.npcs):
296
- npc_name = state.npcs[state.selected_idx]
297
- state.status = f"Selected NPC: {npc_name} (edit NPC files directly)"
298
- elif state.tab == 2:
299
- if state.jinxs and state.selected_idx < len(state.jinxs):
300
- folder, _ = state.jinxs[state.selected_idx]
301
- state.status = f"Selected folder: {folder}/"
302
-
303
- # ========== Main Loop ==========
304
- load_team_data()
305
-
306
- fd = sys.stdin.fileno()
307
- old_settings = termios.tcgetattr(fd)
308
-
309
- try:
310
- tty.setcbreak(fd)
311
- sys.stdout.write('\033[?25l') # Hide cursor
312
-
313
- render_screen()
314
-
315
- while True:
316
- c = sys.stdin.read(1)
317
- if not handle_input(c):
318
- break
319
- render_screen()
320
-
321
- finally:
322
- termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
323
- sys.stdout.write('\033[?25h') # Show cursor
324
- sys.stdout.write('\033[2J\033[H') # Clear screen
325
- sys.stdout.flush()
326
-
327
- context['output'] = "Team manager closed."
@@ -1,122 +0,0 @@
1
- jinx_name: "vixynt"
2
- description: "Generates images from text descriptions or edits existing ones."
3
- inputs:
4
- - prompt
5
- - model: null
6
- - provider: null
7
- - output_name: null
8
- - attachments: null
9
- - n_images: null
10
- - height: null
11
- - width: null
12
- steps:
13
- - name: "generate_or_edit_image"
14
- engine: "python"
15
- code: |
16
- import os
17
- import base64
18
- from io import BytesIO
19
- from datetime import datetime
20
- from PIL import Image
21
- from npcpy.llm_funcs import gen_image
22
-
23
- # Extract inputs from context with proper type conversion
24
- image_prompt = str(context.get('prompt', '')).strip()
25
- output_name = context.get('output_name')
26
- attachments_str = context.get('attachments')
27
-
28
- # Handle integer inputs - they may come as strings or ints
29
- try:
30
- n_images = int(context.get('n_images', 1))
31
- except (ValueError, TypeError):
32
- n_images = 1
33
-
34
- try:
35
- height = int(context.get('height', 1024))
36
- except (ValueError, TypeError):
37
- height = 1024
38
-
39
- try:
40
- width = int(context.get('width', 1024))
41
- except (ValueError, TypeError):
42
- width = 1024
43
-
44
- # Get model and provider from context or environment
45
- model = context.get('model')
46
- provider = context.get('provider')
47
-
48
- # Fallback to environment variables
49
- if not model:
50
- model = os.getenv('NPCSH_IMAGE_GEN_MODEL')
51
- if not provider:
52
- provider = os.getenv('NPCSH_IMAGE_GEN_PROVIDER')
53
-
54
- # Parse attachments
55
- input_images = []
56
- if attachments_str and str(attachments_str).strip():
57
- input_images = [p.strip() for p in str(attachments_str).split(',')]
58
-
59
- output_messages = context.get('messages', [])
60
-
61
- if not image_prompt:
62
- output = "Error: No prompt provided for image generation."
63
- else:
64
- try:
65
- # Generate image(s)
66
- result = gen_image(
67
- prompt=image_prompt,
68
- model=model,
69
- provider=provider,
70
- npc=npc,
71
- height=height,
72
- width=width,
73
- n_images=n_images,
74
- input_images=input_images if input_images else None
75
- )
76
-
77
- # Ensure we have a list of images
78
- if not isinstance(result, list):
79
- images_list = [result] if result is not None else []
80
- else:
81
- images_list = result
82
-
83
- saved_files = []
84
-
85
- for i, image in enumerate(images_list):
86
- if image is None:
87
- continue
88
-
89
- # Determine output filename
90
- if output_name and str(output_name).strip():
91
- base_name, ext = os.path.splitext(os.path.expanduser(str(output_name)))
92
- if not ext:
93
- ext = ".png"
94
- current_output_file = f"{base_name}_{i}{ext}" if len(images_list) > 1 else f"{base_name}{ext}"
95
- else:
96
- os.makedirs(os.path.expanduser("~/.npcsh/images/"), exist_ok=True)
97
- current_output_file = (
98
- os.path.expanduser("~/.npcsh/images/")
99
- + f"image_{datetime.now().strftime('%Y%m%d_%H%M%S')}_{i}.png"
100
- )
101
-
102
- # Save image to file
103
- image.save(current_output_file)
104
- saved_files.append(current_output_file)
105
-
106
- if saved_files:
107
- output = f"Image(s) generated: {', '.join(saved_files)}"
108
- if input_images:
109
- output = f"Image(s) edited: {', '.join(saved_files)}"
110
- context['generated_images'] = saved_files
111
- else:
112
- output = "No images were generated."
113
-
114
- except Exception as e:
115
- import traceback
116
- traceback.print_exc()
117
- output = f"Error {'editing' if input_images else 'generating'} image: {str(e)}"
118
-
119
- context['output'] = output
120
- context['messages'] = output_messages
121
- context['model'] = model
122
- context['provider'] = provider
@@ -1,73 +0,0 @@
1
- jinx_name: mem_review
2
- description: Review pending memories with interactive UI
3
- inputs:
4
- - limit: "20"
5
- - npc_filter: ""
6
- - team_filter: ""
7
- - db_path: ""
8
-
9
- steps:
10
- - name: review_memories
11
- engine: python
12
- code: |
13
- import os
14
- from npcpy.memory.command_history import CommandHistory
15
- from npcpy.memory.memory_processor import memory_batch_review_ui
16
-
17
- limit = int(context.get('limit') or 20)
18
- npc_filter = context.get('npc_filter') or None
19
- team_filter = context.get('team_filter') or None
20
- db_path = context.get('db_path') or os.path.expanduser("~/npcsh_history.db")
21
-
22
- # Get current npc/team from context if not specified
23
- if not npc_filter:
24
- try:
25
- npc_filter = npc.name if 'npc' in dir() and npc else None
26
- except:
27
- pass
28
-
29
- if not team_filter:
30
- try:
31
- team_filter = state.team.name if 'state' in dir() and state and state.team else None
32
- except:
33
- pass
34
-
35
- try:
36
- cmd_history = CommandHistory(db_path)
37
-
38
- # Show what we're about to review
39
- pending = cmd_history.get_pending_memories(limit=limit)
40
- if npc_filter:
41
- pending = [m for m in pending if m.get('npc') == npc_filter]
42
- if team_filter:
43
- pending = [m for m in pending if m.get('team') == team_filter]
44
-
45
- if not pending:
46
- context['output'] = "No pending memories to review."
47
- else:
48
- print(f"\nFound {len(pending)} pending memories")
49
- if npc_filter:
50
- print(f" NPC filter: {npc_filter}")
51
- if team_filter:
52
- print(f" Team filter: {team_filter}")
53
-
54
- # Run interactive review
55
- stats = memory_batch_review_ui(
56
- cmd_history,
57
- npc_filter=npc_filter,
58
- team_filter=team_filter,
59
- limit=limit
60
- )
61
-
62
- lines = [
63
- "Review complete:",
64
- f" Approved: {stats.get('approved', 0)}",
65
- f" Rejected: {stats.get('rejected', 0)}",
66
- f" Edited: {stats.get('edited', 0)}",
67
- f" Skipped: {stats.get('skipped', 0)}",
68
- ]
69
- context['output'] = "\n".join(lines)
70
-
71
- except Exception as e:
72
- import traceback
73
- context['output'] = "Memory review error: " + str(e) + "\n" + traceback.format_exc()