@plures/runebook 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/ANALYSIS_LADDER.md +231 -0
  2. package/CHANGELOG.md +124 -0
  3. package/INTEGRATIONS.md +242 -0
  4. package/LICENSE +21 -0
  5. package/MEMORY.md +253 -0
  6. package/NIXOS.md +357 -0
  7. package/QUICKSTART.md +157 -0
  8. package/README.md +295 -0
  9. package/RELEASE.md +190 -0
  10. package/ValidationChecklist.md +598 -0
  11. package/docs/demo.md +338 -0
  12. package/docs/llm-integration.md +300 -0
  13. package/docs/parallel-execution-plan.md +160 -0
  14. package/flake.nix +228 -0
  15. package/integrations/README.md +242 -0
  16. package/integrations/demo-steps.sh +64 -0
  17. package/integrations/nvim-runebook.lua +140 -0
  18. package/integrations/tmux-status.sh +51 -0
  19. package/integrations/vim-runebook.vim +77 -0
  20. package/integrations/wezterm-status-simple.lua +48 -0
  21. package/integrations/wezterm-status.lua +76 -0
  22. package/nixos-module.nix +156 -0
  23. package/package.json +76 -0
  24. package/packages/design-dojo/index.js +4 -0
  25. package/packages/design-dojo/package.json +20 -0
  26. package/packages/design-dojo/tokens.css +69 -0
  27. package/playwright.config.ts +16 -0
  28. package/scripts/check-versions.cjs +62 -0
  29. package/scripts/demo.sh +220 -0
  30. package/shell.nix +31 -0
  31. package/src/app.html +13 -0
  32. package/src/cli/index.ts +1050 -0
  33. package/src/lib/agent/analysis-pipeline.ts +347 -0
  34. package/src/lib/agent/analysis-service.ts +171 -0
  35. package/src/lib/agent/analysis.ts +159 -0
  36. package/src/lib/agent/analyzers/heuristic.ts +289 -0
  37. package/src/lib/agent/analyzers/index.ts +7 -0
  38. package/src/lib/agent/analyzers/llm.ts +204 -0
  39. package/src/lib/agent/analyzers/local-search.ts +215 -0
  40. package/src/lib/agent/capture.ts +123 -0
  41. package/src/lib/agent/index.ts +244 -0
  42. package/src/lib/agent/integration.ts +81 -0
  43. package/src/lib/agent/llm/providers/base.ts +99 -0
  44. package/src/lib/agent/llm/providers/index.ts +60 -0
  45. package/src/lib/agent/llm/providers/mock.ts +67 -0
  46. package/src/lib/agent/llm/providers/ollama.ts +151 -0
  47. package/src/lib/agent/llm/providers/openai.ts +153 -0
  48. package/src/lib/agent/llm/sanitizer.ts +170 -0
  49. package/src/lib/agent/llm/types.ts +118 -0
  50. package/src/lib/agent/memory.ts +363 -0
  51. package/src/lib/agent/node-status.ts +56 -0
  52. package/src/lib/agent/node-suggestions.ts +64 -0
  53. package/src/lib/agent/status.ts +80 -0
  54. package/src/lib/agent/suggestions.ts +169 -0
  55. package/src/lib/components/Canvas.svelte +124 -0
  56. package/src/lib/components/ConnectionLine.svelte +46 -0
  57. package/src/lib/components/DisplayNode.svelte +167 -0
  58. package/src/lib/components/InputNode.svelte +158 -0
  59. package/src/lib/components/TerminalNode.svelte +237 -0
  60. package/src/lib/components/Toolbar.svelte +359 -0
  61. package/src/lib/components/TransformNode.svelte +327 -0
  62. package/src/lib/core/index.ts +31 -0
  63. package/src/lib/core/observer.ts +278 -0
  64. package/src/lib/core/redaction.ts +158 -0
  65. package/src/lib/core/shell-adapters/base.ts +325 -0
  66. package/src/lib/core/shell-adapters/bash.ts +110 -0
  67. package/src/lib/core/shell-adapters/index.ts +62 -0
  68. package/src/lib/core/shell-adapters/zsh.ts +105 -0
  69. package/src/lib/core/storage.ts +360 -0
  70. package/src/lib/core/types.ts +176 -0
  71. package/src/lib/design-dojo/Box.svelte +47 -0
  72. package/src/lib/design-dojo/Button.svelte +75 -0
  73. package/src/lib/design-dojo/Input.svelte +65 -0
  74. package/src/lib/design-dojo/List.svelte +38 -0
  75. package/src/lib/design-dojo/Select.svelte +48 -0
  76. package/src/lib/design-dojo/SplitPane.svelte +43 -0
  77. package/src/lib/design-dojo/StatusBar.svelte +61 -0
  78. package/src/lib/design-dojo/Table.svelte +47 -0
  79. package/src/lib/design-dojo/Text.svelte +36 -0
  80. package/src/lib/design-dojo/Toggle.svelte +48 -0
  81. package/src/lib/design-dojo/index.ts +10 -0
  82. package/src/lib/stores/canvas-praxis.ts +268 -0
  83. package/src/lib/stores/canvas.ts +58 -0
  84. package/src/lib/types/agent.ts +78 -0
  85. package/src/lib/types/canvas.ts +71 -0
  86. package/src/lib/utils/storage.ts +326 -0
  87. package/src/lib/utils/yaml-loader.ts +52 -0
  88. package/src/routes/+layout.svelte +5 -0
  89. package/src/routes/+layout.ts +5 -0
  90. package/src/routes/+page.svelte +32 -0
  91. package/src-tauri/Cargo.lock +5735 -0
  92. package/src-tauri/Cargo.toml +38 -0
  93. package/src-tauri/build.rs +3 -0
  94. package/src-tauri/capabilities/default.json +10 -0
  95. package/src-tauri/icons/128x128.png +0 -0
  96. package/src-tauri/icons/128x128@2x.png +0 -0
  97. package/src-tauri/icons/32x32.png +0 -0
  98. package/src-tauri/icons/Square107x107Logo.png +0 -0
  99. package/src-tauri/icons/Square142x142Logo.png +0 -0
  100. package/src-tauri/icons/Square150x150Logo.png +0 -0
  101. package/src-tauri/icons/Square284x284Logo.png +0 -0
  102. package/src-tauri/icons/Square30x30Logo.png +0 -0
  103. package/src-tauri/icons/Square310x310Logo.png +0 -0
  104. package/src-tauri/icons/Square44x44Logo.png +0 -0
  105. package/src-tauri/icons/Square71x71Logo.png +0 -0
  106. package/src-tauri/icons/Square89x89Logo.png +0 -0
  107. package/src-tauri/icons/StoreLogo.png +0 -0
  108. package/src-tauri/icons/icon.icns +0 -0
  109. package/src-tauri/icons/icon.ico +0 -0
  110. package/src-tauri/icons/icon.png +0 -0
  111. package/src-tauri/src/agents/agent1.rs +66 -0
  112. package/src-tauri/src/agents/agent2.rs +80 -0
  113. package/src-tauri/src/agents/agent3.rs +73 -0
  114. package/src-tauri/src/agents/agent4.rs +66 -0
  115. package/src-tauri/src/agents/agent5.rs +68 -0
  116. package/src-tauri/src/agents/agent6.rs +75 -0
  117. package/src-tauri/src/agents/base.rs +52 -0
  118. package/src-tauri/src/agents/mod.rs +17 -0
  119. package/src-tauri/src/core/coordination.rs +117 -0
  120. package/src-tauri/src/core/mod.rs +12 -0
  121. package/src-tauri/src/core/ownership.rs +61 -0
  122. package/src-tauri/src/core/types.rs +132 -0
  123. package/src-tauri/src/execution/mod.rs +5 -0
  124. package/src-tauri/src/execution/runner.rs +143 -0
  125. package/src-tauri/src/lib.rs +161 -0
  126. package/src-tauri/src/main.rs +6 -0
  127. package/src-tauri/src/memory/api.rs +422 -0
  128. package/src-tauri/src/memory/client.rs +156 -0
  129. package/src-tauri/src/memory/encryption.rs +79 -0
  130. package/src-tauri/src/memory/migration.rs +110 -0
  131. package/src-tauri/src/memory/mod.rs +28 -0
  132. package/src-tauri/src/memory/schema.rs +275 -0
  133. package/src-tauri/src/memory/tests.rs +192 -0
  134. package/src-tauri/src/orchestrator/coordinator.rs +232 -0
  135. package/src-tauri/src/orchestrator/mod.rs +13 -0
  136. package/src-tauri/src/orchestrator/planner.rs +304 -0
  137. package/src-tauri/tauri.conf.json +35 -0
  138. package/static/examples/date-time-example.yaml +147 -0
  139. package/static/examples/hello-world.yaml +74 -0
  140. package/static/examples/transform-example.yaml +157 -0
  141. package/static/favicon.png +0 -0
  142. package/static/svelte.svg +1 -0
  143. package/static/tauri.svg +6 -0
  144. package/static/vite.svg +1 -0
  145. package/svelte.config.js +18 -0
  146. package/tsconfig.json +19 -0
  147. package/vite.config.js +45 -0
  148. package/vitest.config.ts +21 -0
@@ -0,0 +1,140 @@
1
+ -- RuneBook Neovim plugin (Lua version)
2
+ -- Place this file in ~/.config/nvim/lua/runebook.lua or ~/.config/nvim/plugin/runebook.lua
3
+
4
+ local M = {}
5
+
6
+ local status_file = vim.fn.expand('~/.runebook/agent-status.json')
7
+ local suggestions_file = vim.fn.expand('~/.runebook/suggestions.json')
8
+
9
+ -- Simple JSON parser for reading status (minimal implementation)
10
+ local function read_json_file(filepath)
11
+ local file = io.open(filepath, 'r')
12
+ if not file then
13
+ return nil
14
+ end
15
+
16
+ local content = file:read('*all')
17
+ file:close()
18
+
19
+ -- Use vim.json if available (Neovim 0.10+)
20
+ if vim.json then
21
+ local ok, data = pcall(vim.json.decode, content)
22
+ if ok then
23
+ return data
24
+ end
25
+ end
26
+
27
+ -- Fallback: simple parsing for status field
28
+ local status = content:match('"status"%s*:%s*"([^"]*)"')
29
+ local high_priority = content:match('"highPriorityCount"%s*:%s*([0-9]+)')
30
+ local suggestion_count = content:match('"suggestionCount"%s*:%s*([0-9]+)')
31
+
32
+ if status then
33
+ return {
34
+ status = status,
35
+ highPriorityCount = high_priority and tonumber(high_priority) or 0,
36
+ suggestionCount = suggestion_count and tonumber(suggestion_count) or 0,
37
+ }
38
+ end
39
+
40
+ return nil
41
+ end
42
+
43
+ -- Get top suggestion
44
+ function M.get_top_suggestion()
45
+ local file = io.open(suggestions_file, 'r')
46
+ if not file then
47
+ return nil
48
+ end
49
+
50
+ local content = file:read('*all')
51
+ file:close()
52
+
53
+ -- Use vim.json if available
54
+ if vim.json then
55
+ local ok, data = pcall(vim.json.decode, content)
56
+ if ok and data.suggestions and #data.suggestions > 0 then
57
+ -- Sort by priority and return top
58
+ table.sort(data.suggestions, function(a, b)
59
+ local priority_order = { high = 3, medium = 2, low = 1 }
60
+ local a_prio = priority_order[a.priority] or 0
61
+ local b_prio = priority_order[b.priority] or 0
62
+ if a_prio ~= b_prio then
63
+ return a_prio > b_prio
64
+ end
65
+ return a.timestamp > b.timestamp
66
+ end)
67
+ return data.suggestions[1]
68
+ end
69
+ end
70
+
71
+ -- Fallback: simple extraction
72
+ local title = content:match('"title"%s*:%s*"([^"]*)"')
73
+ local priority = content:match('"priority"%s*:%s*"([^"]*)"')
74
+
75
+ if title then
76
+ return {
77
+ title = title,
78
+ priority = priority or 'low',
79
+ }
80
+ end
81
+
82
+ return nil
83
+ end
84
+
85
+ -- Show suggestion in command line
86
+ function M.show_suggestion()
87
+ local suggestion = M.get_top_suggestion()
88
+ if not suggestion then
89
+ vim.notify('No suggestions available', vim.log.levels.INFO)
90
+ return
91
+ end
92
+
93
+ local symbol = suggestion.priority == 'high' and '⚠'
94
+ or suggestion.priority == 'medium' and '▲'
95
+ or '•'
96
+
97
+ local text = string.format('%s %s', symbol, suggestion.title)
98
+ if suggestion.description then
99
+ text = text .. '\n' .. suggestion.description
100
+ end
101
+
102
+ vim.notify(text, vim.log.levels.INFO, { title = 'RuneBook Suggestion' })
103
+ end
104
+
105
+ -- Display in virtual text (optional)
106
+ function M.update_virtual_text()
107
+ if not vim.api.nvim_buf_is_valid(0) then
108
+ return
109
+ end
110
+
111
+ local ns = vim.api.nvim_create_namespace('runebook')
112
+ vim.api.nvim_buf_clear_namespace(0, ns, 0, -1)
113
+
114
+ local suggestion = M.get_top_suggestion()
115
+ if suggestion then
116
+ local symbol = suggestion.priority == 'high' and '⚠'
117
+ or suggestion.priority == 'medium' and '▲'
118
+ or '•'
119
+ local text = string.format('%s %s', symbol, suggestion.title)
120
+
121
+ -- Show at end of first line
122
+ vim.api.nvim_buf_set_extmark(0, ns, 0, -1, {
123
+ virt_text = {{ text, 'Comment' }},
124
+ virt_text_pos = 'eol',
125
+ })
126
+ end
127
+ end
128
+
129
+ -- Create command
130
+ vim.api.nvim_create_user_command('RunebookSuggestion', M.show_suggestion, {
131
+ desc = 'Show RuneBook suggestion',
132
+ })
133
+
134
+ -- Optional: Auto-update virtual text on buffer enter
135
+ -- vim.api.nvim_create_autocmd('BufEnter', {
136
+ -- callback = M.update_virtual_text,
137
+ -- })
138
+
139
+ return M
140
+
@@ -0,0 +1,51 @@
1
+ #!/bin/bash
2
+ # RuneBook tmux status line integration
3
+ # Add this to your ~/.tmux.conf:
4
+ # set -g status-right '#(bash ~/.runebook/integrations/tmux-status.sh)'
5
+
6
+ RUNebook_CLI="${RUNebook_CLI:-runebook}"
7
+ STATUS_FILE="$HOME/.runebook/agent-status.json"
8
+
9
+ if [ ! -f "$STATUS_FILE" ]; then
10
+ echo ""
11
+ exit 0
12
+ fi
13
+
14
+ # Read status from JSON file
15
+ STATUS=$(cat "$STATUS_FILE" 2>/dev/null | grep -o '"status":"[^"]*"' | cut -d'"' -f4)
16
+ SUGGESTION_COUNT=$(cat "$STATUS_FILE" 2>/dev/null | grep -o '"suggestionCount":[0-9]*' | cut -d':' -f2)
17
+ HIGH_PRIORITY=$(cat "$STATUS_FILE" 2>/dev/null | grep -o '"highPriorityCount":[0-9]*' | cut -d':' -f2)
18
+
19
+ if [ -z "$STATUS" ]; then
20
+ echo ""
21
+ exit 0
22
+ fi
23
+
24
+ case "$STATUS" in
25
+ "idle")
26
+ SYMBOL="●"
27
+ COLOR="#[fg=green]"
28
+ ;;
29
+ "analyzing")
30
+ SYMBOL="⟳"
31
+ COLOR="#[fg=yellow]"
32
+ ;;
33
+ "issues_found")
34
+ SYMBOL="⚠"
35
+ COLOR="#[fg=red]"
36
+ if [ -n "$HIGH_PRIORITY" ] && [ "$HIGH_PRIORITY" -gt 0 ]; then
37
+ TEXT="${HIGH_PRIORITY}"
38
+ fi
39
+ ;;
40
+ *)
41
+ echo ""
42
+ exit 0
43
+ ;;
44
+ esac
45
+
46
+ if [ -z "$TEXT" ]; then
47
+ TEXT=""
48
+ fi
49
+
50
+ echo "${COLOR}${SYMBOL}${TEXT}#[fg=default]"
51
+
@@ -0,0 +1,77 @@
1
+ " RuneBook Vim/Neovim plugin
2
+ " Minimal plugin to display last suggestion
3
+ " Place this file in ~/.vim/plugin/ or ~/.config/nvim/plugin/
4
+
5
+ if exists('g:loaded_runebook')
6
+ finish
7
+ endif
8
+ let g:loaded_runebook = 1
9
+
10
+ let s:status_file = expand('~/.runebook/agent-status.json')
11
+ let s:suggestions_file = expand('~/.runebook/suggestions.json')
12
+
13
+ " Get top suggestion
14
+ function! s:GetTopSuggestion()
15
+ if !filereadable(s:suggestions_file)
16
+ return ''
17
+ endif
18
+
19
+ try
20
+ let content = readfile(s:suggestions_file)
21
+ let json_str = join(content, '')
22
+ " Simple JSON parsing for suggestions array
23
+ " This is a minimal implementation - for production, use a proper JSON parser
24
+ let suggestions = matchstr(json_str, '"suggestions"\s*:\s*\[.*\]')
25
+ if empty(suggestions)
26
+ return ''
27
+ endif
28
+
29
+ " Extract first suggestion title (simplified)
30
+ let title = matchstr(suggestions, '"title"\s*:\s*"\([^"]*\)"', 0, 1)
31
+ let priority = matchstr(suggestions, '"priority"\s*:\s*"\([^"]*\)"', 0, 1)
32
+
33
+ if empty(title)
34
+ return ''
35
+ endif
36
+
37
+ let symbol = priority ==# 'high' ? '⚠' : priority ==# 'medium' ? '▲' : '•'
38
+ return symbol . ' ' . title
39
+ catch
40
+ return ''
41
+ endtry
42
+ endfunction
43
+
44
+ " Display suggestion in command line
45
+ function! RunebookShowSuggestion()
46
+ let suggestion = s:GetTopSuggestion()
47
+ if empty(suggestion)
48
+ echo 'No suggestions available'
49
+ else
50
+ echo suggestion
51
+ endif
52
+ endfunction
53
+
54
+ " Command to show suggestion
55
+ command! RunebookSuggestion call RunebookShowSuggestion()
56
+
57
+ " Optional: Show suggestion in virtual text (Neovim only)
58
+ if has('nvim')
59
+ function! s:UpdateVirtualText()
60
+ if !exists('b:runebook_ns')
61
+ let b:runebook_ns = nvim_create_namespace('runebook')
62
+ endif
63
+
64
+ " Clear existing virtual text
65
+ call nvim_buf_clear_namespace(0, b:runebook_ns, 0, -1)
66
+
67
+ let suggestion = s:GetTopSuggestion()
68
+ if !empty(suggestion)
69
+ " Show at end of first line
70
+ call nvim_buf_set_virtual_text(0, b:runebook_ns, 0, [[suggestion, 'Comment']], {})
71
+ endif
72
+ endfunction
73
+
74
+ " Auto-update on buffer enter (optional)
75
+ " autocmd BufEnter * call s:UpdateVirtualText()
76
+ endif
77
+
@@ -0,0 +1,48 @@
1
+ -- RuneBook WezTerm right-status integration (simplified version)
2
+ -- Add this to your ~/.config/wezterm/wezterm.lua:
3
+ --
4
+ -- wezterm.on('update-right-status', function(window, pane)
5
+ -- local status_file = wezterm.home_dir .. '/.runebook/agent-status.json'
6
+ -- local file = io.open(status_file, 'r')
7
+ -- if not file then
8
+ -- window:set_right_status('')
9
+ -- return
10
+ -- end
11
+ --
12
+ -- local content = file:read('*all')
13
+ -- file:close()
14
+ --
15
+ -- -- Simple JSON parsing (for status field only)
16
+ -- local status = content:match('"status"%s*:%s*"([^"]*)"')
17
+ -- local high_priority = content:match('"highPriorityCount"%s*:%s*([0-9]+)')
18
+ --
19
+ -- local symbol = ''
20
+ -- local color = ''
21
+ -- local text = ''
22
+ --
23
+ -- if status == 'idle' then
24
+ -- symbol = '●'
25
+ -- color = '#00ff00'
26
+ -- elseif status == 'analyzing' then
27
+ -- symbol = '⟳'
28
+ -- color = '#ffff00'
29
+ -- elseif status == 'issues_found' then
30
+ -- symbol = '⚠'
31
+ -- color = '#ff0000'
32
+ -- if high_priority then
33
+ -- text = high_priority
34
+ -- end
35
+ -- end
36
+ --
37
+ -- if symbol ~= '' then
38
+ -- window:set_right_status(wezterm.format({
39
+ -- { Foreground = { Color = color } },
40
+ -- { Text = symbol .. text },
41
+ -- { Foreground = { Color = '#ffffff' } },
42
+ -- { Text = ' ' },
43
+ -- }))
44
+ -- else
45
+ -- window:set_right_status('')
46
+ -- end
47
+ -- end)
48
+
@@ -0,0 +1,76 @@
1
+ -- RuneBook WezTerm right-status integration
2
+ -- Add this to your ~/.config/wezterm/wezterm.lua:
3
+ -- local runebook_status = require('integrations/wezterm-status')
4
+ -- config.set_wezterm_config(runebook_status)
5
+
6
+ local wezterm = require 'wezterm'
7
+ local json = require 'json'
8
+
9
+ local function get_agent_status()
10
+ local status_file = wezterm.home_dir .. '/.runebook/agent-status.json'
11
+ local file = io.open(status_file, 'r')
12
+
13
+ if not file then
14
+ return nil
15
+ end
16
+
17
+ local content = file:read('*all')
18
+ file:close()
19
+
20
+ local ok, data = pcall(json.decode, content)
21
+ if not ok then
22
+ return nil
23
+ end
24
+
25
+ return data
26
+ end
27
+
28
+ local function format_status(status_data)
29
+ if not status_data then
30
+ return ''
31
+ end
32
+
33
+ local status = status_data.status or 'idle'
34
+ local symbol = ''
35
+ local color = ''
36
+ local text = ''
37
+
38
+ if status == 'idle' then
39
+ symbol = '●'
40
+ color = '#00ff00'
41
+ elseif status == 'analyzing' then
42
+ symbol = '⟳'
43
+ color = '#ffff00'
44
+ elseif status == 'issues_found' then
45
+ symbol = '⚠'
46
+ color = '#ff0000'
47
+ local high_priority = status_data.highPriorityCount or 0
48
+ if high_priority > 0 then
49
+ text = tostring(high_priority)
50
+ end
51
+ end
52
+
53
+ if symbol == '' then
54
+ return ''
55
+ end
56
+
57
+ return wezterm.format({
58
+ { Foreground = { Color = color } },
59
+ { Text = symbol .. text },
60
+ { Foreground = { Color = '#ffffff' } },
61
+ { Text = ' ' },
62
+ })
63
+ end
64
+
65
+ wezterm.on('update-right-status', function(window, pane)
66
+ local status_data = get_agent_status()
67
+ local status_text = format_status(status_data)
68
+
69
+ window:set_right_status(status_text)
70
+ end)
71
+
72
+ return {
73
+ get_agent_status = get_agent_status,
74
+ format_status = format_status,
75
+ }
76
+
@@ -0,0 +1,156 @@
1
+ { config, lib, pkgs, ... }:
2
+
3
+ with lib;
4
+
5
+ let
6
+ cfg = config.services.runebook-agent;
7
+
8
+ # Configuration file path
9
+ configDir = "/var/lib/runebook-agent";
10
+ configFile = "${configDir}/agent-config.json";
11
+ observerConfigFile = "${configDir}/observer-config.json";
12
+
13
+ # Service script
14
+ serviceScript = pkgs.writeShellScript "runebook-agent-service" ''
15
+ #!/usr/bin/env bash
16
+ set -euo pipefail
17
+
18
+ # Ensure config directory exists
19
+ mkdir -p ${configDir}
20
+
21
+ # Initialize config files if they don't exist
22
+ if [ ! -f ${configFile} ]; then
23
+ cat > ${configFile} <<EOF
24
+ {
25
+ "enabled": ${if cfg.enable then "true" else "false"},
26
+ "captureEvents": ${if cfg.captureEvents then "true" else "false"},
27
+ "analyzePatterns": ${if cfg.analyzePatterns then "true" else "false"},
28
+ "suggestImprovements": ${if cfg.suggestImprovements then "true" else "false"},
29
+ "storagePath": "${cfg.dataDir}",
30
+ "maxEvents": ${toString cfg.maxEvents},
31
+ "retentionDays": ${toString cfg.retentionDays}
32
+ }
33
+ EOF
34
+ fi
35
+
36
+ if [ ! -f ${observerConfigFile} ]; then
37
+ cat > ${observerConfigFile} <<EOF
38
+ {
39
+ "enabled": ${if cfg.enable then "true" else "false"},
40
+ "redactSecrets": true,
41
+ "usePluresDB": false,
42
+ "chunkSize": 4096,
43
+ "maxEvents": ${toString cfg.maxEvents},
44
+ "retentionDays": ${toString cfg.retentionDays},
45
+ "storagePath": "${cfg.dataDir}"
46
+ }
47
+ EOF
48
+ fi
49
+
50
+ # Set up environment
51
+ export HOME=${configDir}
52
+ export XDG_CONFIG_HOME=${configDir}
53
+ export XDG_DATA_HOME=${configDir}
54
+
55
+ # Inject secrets from environment/agenix/sops if available
56
+ # OpenAI key must be provided via environment variable
57
+ if [ -n "''${OPENAI_API_KEY:-}" ]; then
58
+ export OPENAI_API_KEY
59
+ fi
60
+
61
+ # Run the agent in background mode
62
+ # Note: The actual agent runs as a shell hook, so this service
63
+ # mainly ensures the configuration is set up correctly.
64
+ # For a true background service, you would need to implement
65
+ # a daemon mode in the CLI.
66
+
67
+ # For now, we'll just ensure the config is ready
68
+ # The agent will be activated via shell hooks when enabled
69
+ exec sleep infinity
70
+ '';
71
+
72
+ in {
73
+ options.services.runebook-agent = {
74
+ enable = mkEnableOption "RuneBook agent service";
75
+
76
+ captureEvents = mkOption {
77
+ type = types.bool;
78
+ default = true;
79
+ description = "Enable event capture";
80
+ };
81
+
82
+ analyzePatterns = mkOption {
83
+ type = types.bool;
84
+ default = true;
85
+ description = "Enable pattern analysis";
86
+ };
87
+
88
+ suggestImprovements = mkOption {
89
+ type = types.bool;
90
+ default = true;
91
+ description = "Enable suggestion generation";
92
+ };
93
+
94
+ dataDir = mkOption {
95
+ type = types.str;
96
+ default = "/var/lib/runebook-agent/data";
97
+ description = "Data directory for agent storage";
98
+ };
99
+
100
+ maxEvents = mkOption {
101
+ type = types.int;
102
+ default = 10000;
103
+ description = "Maximum number of events to store";
104
+ };
105
+
106
+ retentionDays = mkOption {
107
+ type = types.int;
108
+ default = 30;
109
+ description = "Number of days to retain events";
110
+ };
111
+
112
+ openaiApiKey = mkOption {
113
+ type = types.nullOr types.str;
114
+ default = null;
115
+ description = ''
116
+ OpenAI API key (if using LLM features).
117
+ WARNING: This will be stored in the Nix store if set here.
118
+ Prefer using environment variables, agenix, or sops-nix instead.
119
+ '';
120
+ };
121
+ };
122
+
123
+ config = mkIf cfg.enable {
124
+ systemd.user.services.runebook-agent = {
125
+ description = "RuneBook Agent Service";
126
+ wantedBy = [ "default.target" ];
127
+
128
+ serviceConfig = {
129
+ Type = "simple";
130
+ ExecStart = "${serviceScript}";
131
+ Restart = "on-failure";
132
+ RestartSec = "5s";
133
+
134
+ # Security settings
135
+ PrivateTmp = true;
136
+ ProtectSystem = "strict";
137
+ ProtectHome = "read-only";
138
+ ReadWritePaths = [ configDir cfg.dataDir ];
139
+
140
+ # Environment
141
+ Environment = [
142
+ "HOME=${configDir}"
143
+ "XDG_CONFIG_HOME=${configDir}"
144
+ "XDG_DATA_HOME=${configDir}"
145
+ ] ++ optional (cfg.openaiApiKey != null) "OPENAI_API_KEY=${cfg.openaiApiKey}";
146
+ };
147
+ };
148
+
149
+ # Create data directory
150
+ systemd.tmpfiles.rules = [
151
+ "d ${configDir} 0755 root root -"
152
+ "d ${cfg.dataDir} 0755 root root -"
153
+ ];
154
+ };
155
+ }
156
+
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "@plures/runebook",
3
+ "version": "0.4.0",
4
+ "publishConfig": {
5
+ "access": "public",
6
+ "registry": "https://registry.npmjs.org/"
7
+ },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/plures/runebook.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/plures/runebook/issues"
14
+ },
15
+ "homepage": "https://github.com/plures/runebook#readme",
16
+ "description": "A reactive, canvas-native computing environment that merges terminals, notebooks, and web components",
17
+ "type": "module",
18
+ "keywords": [
19
+ "tauri",
20
+ "svelte",
21
+ "desktop-app",
22
+ "reactive",
23
+ "canvas",
24
+ "terminal",
25
+ "notebook",
26
+ "workflow",
27
+ "visual-programming",
28
+ "node-based"
29
+ ],
30
+ "author": "Plures",
31
+ "scripts": {
32
+ "dev": "vite dev",
33
+ "build": "vite build",
34
+ "preview": "vite preview",
35
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
36
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
37
+ "tauri": "tauri",
38
+ "version:check": "node scripts/check-versions.cjs",
39
+ "agent": "tsx src/cli/index.ts",
40
+ "test": "vitest",
41
+ "test:coverage": "vitest --coverage",
42
+ "test:e2e": "playwright test",
43
+ "test:all": "npm run test && npm run test:e2e"
44
+ },
45
+ "bin": {
46
+ "runebook": "./dist/cli/index.js"
47
+ },
48
+ "license": "MIT",
49
+ "workspaces": [
50
+ "packages/*"
51
+ ],
52
+ "dependencies": {
53
+ "@plures/design-dojo": "^0.1.0",
54
+ "@plures/praxis": "^1.2.13",
55
+ "@tauri-apps/api": "^2.9.1",
56
+ "@tauri-apps/plugin-opener": "^2.5.2",
57
+ "@types/js-yaml": "^4.0.9",
58
+ "js-yaml": "^4.1.1",
59
+ "pluresdb": "^1.3.1"
60
+ },
61
+ "devDependencies": {
62
+ "@playwright/test": "^1.50.1",
63
+ "@sveltejs/adapter-static": "^3.0.10",
64
+ "@sveltejs/kit": "^2.49.2",
65
+ "@sveltejs/vite-plugin-svelte": "^6.2.1",
66
+ "@tauri-apps/cli": "^2.9.6",
67
+ "@types/node": "^20.0.0",
68
+ "@vitest/coverage-v8": "^2.0.0",
69
+ "svelte": "^5.46.1",
70
+ "svelte-check": "^4.3.5",
71
+ "tsx": "^4.7.0",
72
+ "typescript": "~5.6.3",
73
+ "vite": "^6.4.1",
74
+ "vitest": "^2.0.0"
75
+ }
76
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * @plures/design-dojo
3
+ * Design tokens and UI component library for the Plures ecosystem.
4
+ */
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "@plures/design-dojo",
3
+ "version": "0.1.0",
4
+ "description": "Design tokens and UI component library for the Plures ecosystem",
5
+ "type": "module",
6
+ "main": "./index.js",
7
+ "exports": {
8
+ ".": "./index.js",
9
+ "./tokens.css": "./tokens.css"
10
+ },
11
+ "files": [
12
+ "index.js",
13
+ "tokens.css"
14
+ ],
15
+ "license": "MIT",
16
+ "publishConfig": {
17
+ "access": "public",
18
+ "registry": "https://registry.npmjs.org/"
19
+ }
20
+ }