trak_flow 0.1.3 → 0.2.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.
- checksums.yaml +4 -4
- data/.fasterer.yml +10 -0
- data/.loki +33 -0
- data/.reek.yml +161 -0
- data/.rubocop.yml +187 -0
- data/Archspec.rb +45 -0
- data/CHANGELOG.md +52 -1
- data/Gemfile +12 -0
- data/Gemfile.lock +124 -93
- data/README.md +15 -2
- data/_typos.toml +17 -0
- data/docs/api/ruby-library.md +2 -2
- data/docs/cli/admin-commands.md +3 -3
- data/docs/core-concepts/overview.md +2 -2
- data/docs/getting-started/configuration.md +2 -2
- data/docs/getting-started/installation.md +22 -5
- data/docs/index.md +2 -2
- data/docs/mcp/integration.md +11 -8
- data/docs/mcp/overview.md +29 -8
- data/examples/cli_demo.sh +1 -1
- data/examples/mcp/Gemfile.lock +147 -61
- data/lib/trak_flow/cli/admin_commands.rb +77 -59
- data/lib/trak_flow/cli/config_commands.rb +9 -14
- data/lib/trak_flow/cli/dep_commands.rb +3 -3
- data/lib/trak_flow/cli/label_commands.rb +3 -3
- data/lib/trak_flow/cli/main_commands.rb +127 -94
- data/lib/trak_flow/cli/plan_commands.rb +32 -25
- data/lib/trak_flow/cli/workflow_commands.rb +5 -4
- data/lib/trak_flow/cli.rb +2 -2
- data/lib/trak_flow/config/defaults.yml +5 -1
- data/lib/trak_flow/config/section.rb +3 -5
- data/lib/trak_flow/config.rb +20 -34
- data/lib/trak_flow/graph/dependency_graph.rb +72 -69
- data/lib/trak_flow/mcp/resources/dependency_graph.rb +13 -9
- data/lib/trak_flow/mcp/resources/task_by_id.rb +4 -3
- data/lib/trak_flow/mcp/server.rb +77 -76
- data/lib/trak_flow/mcp/tools/task_create.rb +17 -17
- data/lib/trak_flow/models/task.rb +21 -17
- data/lib/trak_flow/storage/database.rb +26 -16
- data/lib/trak_flow/storage/jsonl.rb +60 -62
- data/lib/trak_flow/version.rb +1 -1
- data/lib/trak_flow.rb +0 -1
- data/trak_flow.gemspec +57 -0
- metadata +11 -31
data/docs/mcp/overview.md
CHANGED
|
@@ -18,20 +18,42 @@ require 'trak_flow'
|
|
|
18
18
|
require 'trak_flow/mcp'
|
|
19
19
|
|
|
20
20
|
# Start the MCP server over STDIO
|
|
21
|
-
TrakFlow::Mcp::Server.new.
|
|
21
|
+
TrakFlow::Mcp::Server.new.start_stdio
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
### HTTP/SSE Transport (For Remote Access)
|
|
25
25
|
|
|
26
|
+
Requires the optional `rackup` gem plus a Rack server that supports
|
|
27
|
+
`rack.hijack` (Puma, Falcon, WEBrick, ...) in your bundle.
|
|
28
|
+
|
|
26
29
|
```ruby
|
|
27
30
|
require 'trak_flow'
|
|
28
31
|
require 'trak_flow/mcp'
|
|
29
32
|
|
|
30
|
-
# Start with HTTP/SSE transport
|
|
33
|
+
# Start with HTTP/SSE transport on whatever Rack server the bundle carries
|
|
31
34
|
server = TrakFlow::Mcp::Server.new
|
|
32
|
-
server.
|
|
35
|
+
server.start_http(port: 3333)
|
|
36
|
+
|
|
37
|
+
# Or name the server explicitly (also settable via config mcp.handler
|
|
38
|
+
# or the TF_MCP__HANDLER environment variable)
|
|
39
|
+
server.start_http(port: 3333, handler: "puma")
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Embedding in an Existing Rack Application
|
|
43
|
+
|
|
44
|
+
If your application already runs its own Rack server, don't start a second
|
|
45
|
+
one — mount the MCP transport as a plain Rack app:
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
# config.ru of the host application
|
|
49
|
+
map "/mcp" do
|
|
50
|
+
run TrakFlow::Mcp::Server.new.rack_app
|
|
51
|
+
end
|
|
33
52
|
```
|
|
34
53
|
|
|
54
|
+
One process, one server — the host's server choice (Puma, Falcon, ...)
|
|
55
|
+
applies, as long as it supports `rack.hijack` for SSE.
|
|
56
|
+
|
|
35
57
|
## Architecture
|
|
36
58
|
|
|
37
59
|
```mermaid
|
|
@@ -49,7 +71,7 @@ graph LR
|
|
|
49
71
|
|
|
50
72
|
subgraph "TrakFlow Core"
|
|
51
73
|
DB[(SQLite)]
|
|
52
|
-
JSONL[
|
|
74
|
+
JSONL[tasks.jsonl]
|
|
53
75
|
end
|
|
54
76
|
|
|
55
77
|
LLM <--> Client
|
|
@@ -191,13 +213,12 @@ Currently, the MCP server does not implement authentication. For production use:
|
|
|
191
213
|
|
|
192
214
|
## Configuration
|
|
193
215
|
|
|
194
|
-
Environment variables:
|
|
216
|
+
Environment variables (via [anyway_config](https://github.com/palkan/anyway_config), prefix `TF_`):
|
|
195
217
|
|
|
196
218
|
| Variable | Description | Default |
|
|
197
219
|
|----------|-------------|---------|
|
|
198
|
-
| `
|
|
199
|
-
| `
|
|
200
|
-
| `TRAK_FLOW_MCP_HOST` | HTTP host | `127.0.0.1` |
|
|
220
|
+
| `TF_MCP__PORT` | HTTP port | `3333` |
|
|
221
|
+
| `TF_MCP__HANDLER` | Rackup handler name for the HTTP server (e.g. `puma`, `falcon`) | auto-detect from bundle |
|
|
201
222
|
|
|
202
223
|
## Next Steps
|
|
203
224
|
|
data/examples/cli_demo.sh
CHANGED
data/examples/mcp/Gemfile.lock
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
trak_flow (0.1.
|
|
4
|
+
trak_flow (0.1.4)
|
|
5
5
|
anyway_config (~> 2.0)
|
|
6
6
|
debug_me
|
|
7
7
|
fast-mcp
|
|
8
8
|
oj (~> 3.16)
|
|
9
9
|
pastel (~> 0.8)
|
|
10
|
-
puma (~> 6.0)
|
|
11
|
-
rackup (~> 2.0)
|
|
12
10
|
sequel (~> 5.0)
|
|
13
11
|
sqlite3 (~> 2.0)
|
|
14
12
|
thor (~> 1.3)
|
|
@@ -20,7 +18,7 @@ GEM
|
|
|
20
18
|
specs:
|
|
21
19
|
addressable (2.8.8)
|
|
22
20
|
public_suffix (>= 2.0.2, < 8.0)
|
|
23
|
-
ansi (1.
|
|
21
|
+
ansi (1.6.0)
|
|
24
22
|
anyway_config (2.7.2)
|
|
25
23
|
ruby-next-core (~> 1.0)
|
|
26
24
|
ast (2.4.3)
|
|
@@ -59,6 +57,17 @@ GEM
|
|
|
59
57
|
dry-inflector (~> 1.0)
|
|
60
58
|
dry-logic (~> 1.4)
|
|
61
59
|
zeitwerk (~> 2.6)
|
|
60
|
+
event_stream_parser (1.0.0)
|
|
61
|
+
faraday (2.14.3)
|
|
62
|
+
faraday-net_http (>= 2.0, < 3.5)
|
|
63
|
+
json
|
|
64
|
+
logger
|
|
65
|
+
faraday-multipart (1.2.0)
|
|
66
|
+
multipart-post (~> 2.0)
|
|
67
|
+
faraday-net_http (3.4.4)
|
|
68
|
+
net-http (~> 0.5)
|
|
69
|
+
faraday-retry (2.4.0)
|
|
70
|
+
faraday (~> 2.0)
|
|
62
71
|
fast-mcp (1.6.0)
|
|
63
72
|
addressable (~> 2.8)
|
|
64
73
|
base64
|
|
@@ -66,66 +75,113 @@ GEM
|
|
|
66
75
|
json (~> 2.0)
|
|
67
76
|
mime-types (~> 3.4)
|
|
68
77
|
rack (>= 2.0, < 4.0)
|
|
78
|
+
fasterer (0.11.0)
|
|
79
|
+
ruby_parser (>= 3.19.1)
|
|
80
|
+
hana (1.3.7)
|
|
81
|
+
http-2 (1.2.2)
|
|
82
|
+
httpx (1.8.3)
|
|
83
|
+
http-2 (>= 1.2.0)
|
|
69
84
|
json (2.18.0)
|
|
70
|
-
|
|
85
|
+
json-schema (6.2.0)
|
|
86
|
+
addressable (~> 2.8)
|
|
87
|
+
bigdecimal (>= 3.1, < 5)
|
|
88
|
+
json_schemer (2.5.0)
|
|
89
|
+
bigdecimal
|
|
90
|
+
hana (~> 1.3)
|
|
91
|
+
regexp_parser (~> 2.0)
|
|
92
|
+
simpleidn (~> 0.2)
|
|
93
|
+
language_server-protocol (3.17.0.6)
|
|
71
94
|
lint_roller (1.1.0)
|
|
72
95
|
logger (1.7.0)
|
|
96
|
+
marcel (1.2.1)
|
|
73
97
|
mime-types (3.7.0)
|
|
74
98
|
logger
|
|
75
99
|
mime-types-data (~> 3.2025, >= 3.2025.0507)
|
|
76
100
|
mime-types-data (3.2025.0924)
|
|
77
101
|
minitest (5.27.0)
|
|
78
|
-
minitest-reporters (1.
|
|
102
|
+
minitest-reporters (1.8.0)
|
|
79
103
|
ansi
|
|
80
104
|
builder
|
|
81
|
-
minitest (>= 5.0)
|
|
105
|
+
minitest (>= 5.0, < 7)
|
|
82
106
|
ruby-progressbar
|
|
107
|
+
multipart-post (2.4.1)
|
|
108
|
+
net-http (0.9.1)
|
|
109
|
+
uri (>= 0.11.1)
|
|
83
110
|
nio4r (2.7.5)
|
|
84
111
|
oj (3.16.13)
|
|
85
112
|
bigdecimal (>= 3.0)
|
|
86
113
|
ostruct (>= 0.2)
|
|
87
114
|
ostruct (0.6.3)
|
|
88
|
-
parallel (
|
|
89
|
-
parser (3.3.
|
|
115
|
+
parallel (2.2.0)
|
|
116
|
+
parser (3.3.12.0)
|
|
90
117
|
ast (~> 2.4.1)
|
|
91
118
|
racc
|
|
92
119
|
pastel (0.8.0)
|
|
93
120
|
tty-color (~> 0.5)
|
|
94
|
-
prism (1.
|
|
95
|
-
public_suffix (7.0.
|
|
96
|
-
puma (
|
|
121
|
+
prism (1.9.0)
|
|
122
|
+
public_suffix (7.0.5)
|
|
123
|
+
puma (8.0.2)
|
|
97
124
|
nio4r (~> 2.0)
|
|
98
125
|
racc (1.8.1)
|
|
99
126
|
rack (3.2.4)
|
|
100
127
|
rackup (2.3.1)
|
|
101
128
|
rack (>= 3)
|
|
102
129
|
rainbow (3.1.1)
|
|
103
|
-
rake (13.
|
|
130
|
+
rake (13.4.2)
|
|
131
|
+
reek (6.5.0)
|
|
132
|
+
dry-schema (~> 1.13)
|
|
133
|
+
logger (~> 1.6)
|
|
134
|
+
parser (~> 3.3.0)
|
|
135
|
+
rainbow (>= 2.0, < 4.0)
|
|
136
|
+
rexml (~> 3.1)
|
|
104
137
|
regexp_parser (2.11.3)
|
|
105
|
-
|
|
106
|
-
|
|
138
|
+
rexml (3.4.4)
|
|
139
|
+
rubocop (1.90.0)
|
|
140
|
+
json (>= 2.3)
|
|
107
141
|
language_server-protocol (~> 3.17.0.2)
|
|
108
142
|
lint_roller (~> 1.1.0)
|
|
109
|
-
parallel (
|
|
143
|
+
parallel (>= 1.10)
|
|
110
144
|
parser (>= 3.3.0.2)
|
|
111
145
|
rainbow (>= 2.2.2, < 4.0)
|
|
112
146
|
regexp_parser (>= 2.9.3, < 3.0)
|
|
113
|
-
rubocop-ast (>= 1.
|
|
147
|
+
rubocop-ast (>= 1.49.0, < 2.0)
|
|
114
148
|
ruby-progressbar (~> 1.7)
|
|
115
149
|
unicode-display_width (>= 2.4.0, < 4.0)
|
|
116
|
-
rubocop-ast (1.
|
|
150
|
+
rubocop-ast (1.50.0)
|
|
117
151
|
parser (>= 3.3.7.2)
|
|
118
152
|
prism (~> 1.7)
|
|
119
153
|
ruby-next-core (1.1.2)
|
|
120
154
|
ruby-progressbar (1.13.0)
|
|
155
|
+
ruby_llm (1.16.0)
|
|
156
|
+
base64
|
|
157
|
+
event_stream_parser (~> 1)
|
|
158
|
+
faraday (>= 1.10.0)
|
|
159
|
+
faraday-multipart (>= 1)
|
|
160
|
+
faraday-net_http (>= 1)
|
|
161
|
+
faraday-retry (>= 1)
|
|
162
|
+
marcel (~> 1)
|
|
163
|
+
ruby_llm-schema (~> 0)
|
|
164
|
+
zeitwerk (~> 2)
|
|
165
|
+
ruby_llm-mcp (1.0.1)
|
|
166
|
+
httpx (~> 1.4)
|
|
167
|
+
json-schema (>= 5.0, < 7)
|
|
168
|
+
json_schemer (~> 2.4)
|
|
169
|
+
ruby_llm (~> 1.9)
|
|
170
|
+
zeitwerk (~> 2)
|
|
171
|
+
ruby_llm-schema (0.4.0)
|
|
172
|
+
ruby_parser (3.22.0)
|
|
173
|
+
racc (~> 1.5)
|
|
174
|
+
sexp_processor (~> 4.16)
|
|
121
175
|
sequel (5.100.0)
|
|
122
176
|
bigdecimal
|
|
177
|
+
sexp_processor (4.17.5)
|
|
123
178
|
simplecov (0.22.0)
|
|
124
179
|
docile (~> 1.1)
|
|
125
180
|
simplecov-html (~> 0.11)
|
|
126
181
|
simplecov_json_formatter (~> 0.1)
|
|
127
182
|
simplecov-html (0.13.2)
|
|
128
183
|
simplecov_json_formatter (0.1.4)
|
|
184
|
+
simpleidn (0.3.0)
|
|
129
185
|
sqlite3 (2.9.0-arm64-darwin)
|
|
130
186
|
strings (0.2.1)
|
|
131
187
|
strings-ansi (~> 0.2)
|
|
@@ -144,6 +200,7 @@ GEM
|
|
|
144
200
|
tty-screen (~> 0.8)
|
|
145
201
|
unicode-display_width (2.6.0)
|
|
146
202
|
unicode_utils (1.4.0)
|
|
203
|
+
uri (1.1.1)
|
|
147
204
|
zeitwerk (2.7.4)
|
|
148
205
|
|
|
149
206
|
PLATFORMS
|
|
@@ -151,75 +208,104 @@ PLATFORMS
|
|
|
151
208
|
|
|
152
209
|
DEPENDENCIES
|
|
153
210
|
bundler
|
|
211
|
+
fasterer
|
|
154
212
|
minitest (~> 5.0)
|
|
155
213
|
minitest-reporters (~> 1.6)
|
|
214
|
+
puma (>= 7.2.1)
|
|
215
|
+
rackup
|
|
156
216
|
rake (~> 13.0)
|
|
217
|
+
reek
|
|
157
218
|
rubocop (~> 1.0)
|
|
219
|
+
ruby_llm
|
|
220
|
+
ruby_llm-mcp
|
|
158
221
|
simplecov (~> 0.22)
|
|
159
222
|
trak_flow!
|
|
160
223
|
|
|
161
224
|
CHECKSUMS
|
|
162
225
|
addressable (2.8.8) sha256=7c13b8f9536cf6364c03b9d417c19986019e28f7c00ac8132da4eb0fe393b057
|
|
163
|
-
ansi (1.
|
|
164
|
-
anyway_config (2.7.2)
|
|
226
|
+
ansi (1.6.0)
|
|
227
|
+
anyway_config (2.7.2) sha256=30f6b087c0b41afdd43fe46c81d65a16f052a8489dab453abaeb4ea67aa74bad
|
|
165
228
|
ast (2.4.3)
|
|
166
229
|
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
|
|
167
230
|
bigdecimal (3.3.1) sha256=eaa01e228be54c4f9f53bf3cc34fe3d5e845c31963e7fcc5bedb05a4e7d52218
|
|
168
231
|
builder (3.3.0)
|
|
169
|
-
concurrent-ruby (1.3.6)
|
|
170
|
-
debug_me (1.1.3)
|
|
232
|
+
concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab
|
|
233
|
+
debug_me (1.1.3) sha256=ae8cf61993350d9b3cbe337a7f9a0f595be5b4976c10891a4e1eacc63d09f717
|
|
171
234
|
docile (1.4.1)
|
|
172
|
-
dry-configurable (1.3.0)
|
|
173
|
-
dry-core (1.2.0)
|
|
174
|
-
dry-inflector (1.2.0)
|
|
175
|
-
dry-initializer (3.2.0)
|
|
176
|
-
dry-logic (1.6.0)
|
|
177
|
-
dry-schema (1.14.1)
|
|
178
|
-
dry-types (1.8.3)
|
|
179
|
-
|
|
235
|
+
dry-configurable (1.3.0) sha256=882d862858567fc1210d2549d4c090f34370fc1bb7c5c1933de3fe792e18afa8
|
|
236
|
+
dry-core (1.2.0) sha256=0cc5a7da88df397f153947eeeae42e876e999c1e30900f3c536fb173854e96a1
|
|
237
|
+
dry-inflector (1.2.0) sha256=22f5d0b50fd57074ae57e2ca17e3b300e57564c218269dcf82ff3e42d3f38f2e
|
|
238
|
+
dry-initializer (3.2.0) sha256=37d59798f912dc0a1efe14a4db4a9306989007b302dcd5f25d0a2a20c166c4e3
|
|
239
|
+
dry-logic (1.6.0) sha256=da6fedbc0f90fc41f9b0cc7e6f05f5d529d1efaef6c8dcc8e0733f685745cea2
|
|
240
|
+
dry-schema (1.14.1) sha256=2fcd7539a7099cacae6a22f6a3a2c1846fe5afeb1c841cde432c89c6cb9b9ff1
|
|
241
|
+
dry-types (1.8.3) sha256=b5d97a45e0ed273131c0c3d5bc9f5633c2d1242e092ee47401ce7d5eab65c1bc
|
|
242
|
+
event_stream_parser (1.0.0) sha256=a2683bab70126286f8184dc88f7968ffc4028f813161fb073ec90d171f7de3c8
|
|
243
|
+
faraday (2.14.3) sha256=1882247e6766615c8220b4392bf1d27f6ebb63d8e28267587cef1fb0bf37f278
|
|
244
|
+
faraday-multipart (1.2.0) sha256=7d89a949693714176f612323ca13746a2ded204031a6ba528adee788694ef757
|
|
245
|
+
faraday-net_http (3.4.4) sha256=0e78af151747ed1b00f33e25973b4bc220d7f16c00c39676817c8b12331eb588
|
|
246
|
+
faraday-retry (2.4.0) sha256=7b79c48fb7e56526faf247b12d94a680071ff40c9fda7cf1ec1549439ad11ebe
|
|
247
|
+
fast-mcp (1.6.0) sha256=d68abb45d2daab9e7ae2934417460e4bf9ac87493c585dc5bb626f1afb7d12c4
|
|
248
|
+
fasterer (0.11.0)
|
|
249
|
+
hana (1.3.7) sha256=5425db42d651fea08859811c29d20446f16af196308162894db208cac5ce9b0d
|
|
250
|
+
http-2 (1.2.2) sha256=81b5d45f50fd4cd5f8c5d09651184bec9401e3ef169c3eb3e5b003d5614a92b9
|
|
251
|
+
httpx (1.8.3) sha256=cb88f2285c4ef17164d803a640dc393d28722cba7f282ad0e7f9f926cd02dc47
|
|
180
252
|
json (2.18.0) sha256=b10506aee4183f5cf49e0efc48073d7b75843ce3782c68dbeb763351c08fd505
|
|
181
|
-
|
|
253
|
+
json-schema (6.2.0) sha256=e8bff46ed845a22c1ab2bd0d7eccf831c01fe23bb3920caa4c74db4306813666
|
|
254
|
+
json_schemer (2.5.0) sha256=2f01fb4cce721a4e08dd068fc2030cffd0702a7f333f1ea2be6e8991f00ae396
|
|
255
|
+
language_server-protocol (3.17.0.6)
|
|
182
256
|
lint_roller (1.1.0)
|
|
183
257
|
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
|
184
|
-
|
|
185
|
-
mime-types
|
|
258
|
+
marcel (1.2.1) sha256=1678e9360e32f9eafa917c80029e2f6d10b2715c66a4b87b6d0da9b9cd1f859f
|
|
259
|
+
mime-types (3.7.0) sha256=dcebf61c246f08e15a4de34e386ebe8233791e868564a470c3fe77c00eed5e56
|
|
260
|
+
mime-types-data (3.2025.0924) sha256=f276bca15e59f35767cbcf2bc10e023e9200b30bd6a572c1daf7f4cc24994728
|
|
186
261
|
minitest (5.27.0)
|
|
187
|
-
minitest-reporters (1.
|
|
262
|
+
minitest-reporters (1.8.0)
|
|
263
|
+
multipart-post (2.4.1) sha256=9872d03a8e552020ca096adadbf5e3cb1cd1cdd6acd3c161136b8a5737cdb4a8
|
|
264
|
+
net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996
|
|
188
265
|
nio4r (2.7.5)
|
|
189
|
-
oj (3.16.13)
|
|
190
|
-
ostruct (0.6.3)
|
|
191
|
-
parallel (
|
|
192
|
-
parser (3.3.
|
|
193
|
-
pastel (0.8.0)
|
|
194
|
-
prism (1.
|
|
195
|
-
public_suffix (7.0.
|
|
196
|
-
puma (
|
|
266
|
+
oj (3.16.13) sha256=b114bcb83ef884f1736b3112108f77cf9ca90aa8111a775318cc5d7a6a1e0303
|
|
267
|
+
ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912
|
|
268
|
+
parallel (2.2.0)
|
|
269
|
+
parser (3.3.12.0)
|
|
270
|
+
pastel (0.8.0) sha256=481da9fb7d2f6e6b1a08faf11fa10363172dc40fd47848f096ae21209f805a75
|
|
271
|
+
prism (1.9.0)
|
|
272
|
+
public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
|
|
273
|
+
puma (8.0.2)
|
|
197
274
|
racc (1.8.1)
|
|
198
|
-
rack (3.2.4)
|
|
275
|
+
rack (3.2.4) sha256=5d74b6f75082a643f43c1e76b419c40f0e5527fcfee1e669ac1e6b73c0ccb6f6
|
|
199
276
|
rackup (2.3.1)
|
|
200
277
|
rainbow (3.1.1)
|
|
201
|
-
rake (13.
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
278
|
+
rake (13.4.2)
|
|
279
|
+
reek (6.5.0)
|
|
280
|
+
regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4
|
|
281
|
+
rexml (3.4.4)
|
|
282
|
+
rubocop (1.90.0)
|
|
283
|
+
rubocop-ast (1.50.0)
|
|
284
|
+
ruby-next-core (1.1.2) sha256=1b095fe2e45929f2581b3ecdb8d92b601ce4d72c12a466057c10e23b6f0c4512
|
|
206
285
|
ruby-progressbar (1.13.0)
|
|
207
|
-
|
|
286
|
+
ruby_llm (1.16.0) sha256=26bd5310cf2ce55f74a60f8aae0b0d0327b586ff4532c84828103c3b2b905a18
|
|
287
|
+
ruby_llm-mcp (1.0.1) sha256=22121bfdea1874935debdd9cee0235d02473885ce8347cf745a323f0707fd248
|
|
288
|
+
ruby_llm-schema (0.4.0) sha256=e930f5a5316f9301bff3fb7fe572e44727d05bb8e50621001bbb49a47d63b8da
|
|
289
|
+
ruby_parser (3.22.0)
|
|
290
|
+
sequel (5.100.0) sha256=cb0329b62287a01db68eead46759c14497a3fae01b174e2c41da108a9e9b4a12
|
|
291
|
+
sexp_processor (4.17.5)
|
|
208
292
|
simplecov (0.22.0)
|
|
209
293
|
simplecov-html (0.13.2)
|
|
210
294
|
simplecov_json_formatter (0.1.4)
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
strings
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
tty-
|
|
218
|
-
tty-
|
|
219
|
-
tty-
|
|
220
|
-
tty-
|
|
221
|
-
|
|
222
|
-
|
|
295
|
+
simpleidn (0.3.0) sha256=12ca730bed2f3db04d11e9bfd1bca3e11fb37f55b21eb2e9793fb5814bf54d03
|
|
296
|
+
sqlite3 (2.9.0-arm64-darwin) sha256=a917bd9b84285766ff3300b7d79cd583f5a067594c8c1263e6441618c04a6ed3
|
|
297
|
+
strings (0.2.1) sha256=933293b3c95cf85b81eb44b3cf673e3087661ba739bbadfeadf442083158d6fb
|
|
298
|
+
strings-ansi (0.2.0) sha256=90262d760ea4a94cc2ae8d58205277a343409c288cbe7c29416b1826bd511c88
|
|
299
|
+
thor (1.4.0) sha256=8763e822ccb0f1d7bee88cde131b19a65606657b847cc7b7b4b82e772bcd8a3d
|
|
300
|
+
trak_flow (0.1.4)
|
|
301
|
+
tty-color (0.6.0) sha256=6f9c37ca3a4e2367fb2e6d09722762647d6f455c111f05b59f35730eeb24332a
|
|
302
|
+
tty-cursor (0.7.1) sha256=79534185e6a777888d88628b14b6a1fdf5154a603f285f80b1753e1908e0bf48
|
|
303
|
+
tty-screen (0.8.2) sha256=c090652115beae764336c28802d633f204fb84da93c6a968aa5d8e319e819b50
|
|
304
|
+
tty-spinner (0.9.3) sha256=0e036f047b4ffb61f2aa45f5a770ec00b4d04130531558a94bfc5b192b570542
|
|
305
|
+
tty-table (0.12.0) sha256=fdc27a4750835c1a16efe19a0b857e3ced3652cc7aceafe6dca94908965b9939
|
|
306
|
+
unicode-display_width (2.6.0) sha256=12279874bba6d5e4d2728cef814b19197dbb10d7a7837a869bab65da943b7f5a
|
|
307
|
+
unicode_utils (1.4.0) sha256=b922d0cf2313b6b7136ada6645ce7154ffc86418ca07d53b058efe9eb72f2a40
|
|
308
|
+
uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
|
|
223
309
|
zeitwerk (2.7.4) sha256=2bef90f356bdafe9a6c2bd32bcd804f83a4f9b8bc27f3600fff051eb3edcec8b
|
|
224
310
|
|
|
225
311
|
BUNDLED WITH
|
|
@@ -13,33 +13,21 @@ module TrakFlow
|
|
|
13
13
|
option :cascade, type: :boolean, default: false, desc: "Also delete children"
|
|
14
14
|
def cleanup
|
|
15
15
|
with_database do |db|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
.select { |i| i.closed_at && i.closed_at < cutoff }
|
|
16
|
+
candidates = cleanup_candidates(db)
|
|
17
|
+
count = candidates.size
|
|
19
18
|
|
|
20
19
|
if candidates.empty?
|
|
21
20
|
puts "No tasks to clean up"
|
|
22
21
|
return
|
|
23
22
|
end
|
|
24
23
|
|
|
25
|
-
if options[:dry_run]
|
|
26
|
-
|
|
27
|
-
candidates.each { |i| puts " #{i.id}: #{i.title}" }
|
|
28
|
-
return
|
|
29
|
-
end
|
|
24
|
+
return print_cleanup_dry_run(candidates) if options[:dry_run]
|
|
25
|
+
return unless options[:force] || confirm_cleanup?(count)
|
|
30
26
|
|
|
31
|
-
|
|
32
|
-
puts "About to delete #{candidates.size} task(s). Continue? (y/n)"
|
|
33
|
-
return unless $stdin.gets.strip.downcase == "y"
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
candidates.each do |task|
|
|
37
|
-
db.child_tasks(task.id).each { |c| db.delete_task(c.id) } if options[:cascade]
|
|
38
|
-
db.delete_task(task.id)
|
|
39
|
-
end
|
|
27
|
+
delete_candidates(db, candidates)
|
|
40
28
|
|
|
41
|
-
output({ deleted:
|
|
42
|
-
puts "Deleted #{
|
|
29
|
+
output({ deleted: count }) do
|
|
30
|
+
puts "Deleted #{count} task(s)"
|
|
43
31
|
end
|
|
44
32
|
end
|
|
45
33
|
end
|
|
@@ -49,28 +37,13 @@ module TrakFlow
|
|
|
49
37
|
option :apply, type: :boolean, default: false, desc: "Apply compaction"
|
|
50
38
|
def compact
|
|
51
39
|
with_database do |db|
|
|
52
|
-
stats = {
|
|
53
|
-
total_tasks: db.all_task_ids.size,
|
|
54
|
-
closed_tasks: db.list_tasks(status: "closed", include_tombstones: true).size,
|
|
55
|
-
ephemeral: db.find_ephemeral_workflows.size,
|
|
56
|
-
plans: db.find_plans.size,
|
|
57
|
-
workflows: db.find_workflows.size
|
|
58
|
-
}
|
|
59
|
-
|
|
60
40
|
if options[:analyze]
|
|
41
|
+
stats = compaction_stats(db)
|
|
61
42
|
output(stats) do
|
|
62
43
|
stats.each { |k, v| puts "#{k}: #{v}" }
|
|
63
44
|
end
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if options[:apply]
|
|
68
|
-
db.list_tasks(status: "closed").each do |task|
|
|
69
|
-
next unless task.closed_at && task.closed_at < (Time.now.utc - 30 * 24 * 60 * 60)
|
|
70
|
-
|
|
71
|
-
task.status = "tombstone"
|
|
72
|
-
db.update_task(task)
|
|
73
|
-
end
|
|
45
|
+
elsif options[:apply]
|
|
46
|
+
tombstone_old_closed_tasks(db)
|
|
74
47
|
puts "Compaction complete"
|
|
75
48
|
else
|
|
76
49
|
puts "Use --analyze to see stats or --apply to compact"
|
|
@@ -87,13 +60,13 @@ module TrakFlow
|
|
|
87
60
|
dep_graph = Graph::DependencyGraph.new(db)
|
|
88
61
|
|
|
89
62
|
graph_output = case options[:format]
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
63
|
+
when "svg" then dep_graph.to_svg(include_closed: options[:include_closed])
|
|
64
|
+
else dep_graph.to_dot(include_closed: options[:include_closed])
|
|
65
|
+
end
|
|
93
66
|
|
|
94
|
-
if options[:output]
|
|
95
|
-
File.write(
|
|
96
|
-
puts "Graph written to #{
|
|
67
|
+
if (output_path = options[:output])
|
|
68
|
+
File.write(output_path, graph_output)
|
|
69
|
+
puts "Graph written to #{output_path}"
|
|
97
70
|
else
|
|
98
71
|
puts graph_output
|
|
99
72
|
end
|
|
@@ -103,32 +76,77 @@ module TrakFlow
|
|
|
103
76
|
desc "analyze", "Analyze the task graph"
|
|
104
77
|
def analyze
|
|
105
78
|
with_database do |db|
|
|
106
|
-
|
|
107
|
-
analysis
|
|
108
|
-
|
|
109
|
-
output(analysis) do
|
|
110
|
-
analysis.each do |k, v|
|
|
111
|
-
if v.is_a?(Array)
|
|
112
|
-
puts "#{k}:"
|
|
113
|
-
v.each { |item| puts " - #{item}" }
|
|
114
|
-
else
|
|
115
|
-
puts "#{k}: #{v}"
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
end
|
|
79
|
+
analysis = Graph::DependencyGraph.new(db).analyze
|
|
80
|
+
output(analysis) { print_analysis(analysis) }
|
|
119
81
|
end
|
|
120
82
|
end
|
|
121
83
|
|
|
122
84
|
private
|
|
123
85
|
|
|
86
|
+
def print_analysis(analysis)
|
|
87
|
+
analysis.each do |k, v|
|
|
88
|
+
if v.is_a?(Array)
|
|
89
|
+
puts "#{k}:"
|
|
90
|
+
v.each { |item| puts " - #{item}" }
|
|
91
|
+
else
|
|
92
|
+
puts "#{k}: #{v}"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Closed tasks (tombstones included) whose closed_at is older than --older-than days.
|
|
98
|
+
def cleanup_candidates(db)
|
|
99
|
+
cutoff = Time.now.utc - (options[:older_than] * 24 * 60 * 60)
|
|
100
|
+
db.list_tasks(status: "closed", include_tombstones: true)
|
|
101
|
+
.select { |i| i.closed_at && i.closed_at < cutoff }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def print_cleanup_dry_run(candidates)
|
|
105
|
+
puts "Would delete #{candidates.size} task(s):"
|
|
106
|
+
candidates.each { |i| puts " #{i.id}: #{i.title}" }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def confirm_cleanup?(count)
|
|
110
|
+
puts "About to delete #{count} task(s). Continue? (y/n)"
|
|
111
|
+
$stdin.gets.strip.downcase == "y"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def delete_candidates(db, candidates)
|
|
115
|
+
candidates.each do |task|
|
|
116
|
+
db.child_tasks(task.id).each { |c| db.delete_task(c.id) } if options[:cascade]
|
|
117
|
+
db.delete_task(task.id)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def compaction_stats(db)
|
|
122
|
+
{
|
|
123
|
+
total_tasks: db.all_task_ids.size,
|
|
124
|
+
closed_tasks: db.list_tasks(status: "closed", include_tombstones: true).size,
|
|
125
|
+
ephemeral: db.find_ephemeral_workflows.size,
|
|
126
|
+
plans: db.find_plans.size,
|
|
127
|
+
workflows: db.find_workflows.size
|
|
128
|
+
}
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Marks closed tasks older than 30 days as tombstones.
|
|
132
|
+
def tombstone_old_closed_tasks(db)
|
|
133
|
+
cutoff = Time.now.utc - (30 * 24 * 60 * 60)
|
|
134
|
+
db.list_tasks(status: "closed").each do |task|
|
|
135
|
+
next unless task.closed_at && task.closed_at < cutoff
|
|
136
|
+
|
|
137
|
+
task.status = "tombstone"
|
|
138
|
+
db.update_task(task)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
124
142
|
# Delegate helper methods to parent CLI
|
|
125
|
-
def with_database(&
|
|
143
|
+
def with_database(&) = CLI.new.with_database(&)
|
|
126
144
|
|
|
127
|
-
def output(json_data
|
|
145
|
+
def output(json_data)
|
|
128
146
|
if options[:json]
|
|
129
147
|
puts Oj.dump(json_data, mode: :compat, indent: 2)
|
|
130
148
|
else
|
|
131
|
-
|
|
149
|
+
yield
|
|
132
150
|
end
|
|
133
151
|
end
|
|
134
152
|
end
|
|
@@ -103,7 +103,7 @@ module TrakFlow
|
|
|
103
103
|
|
|
104
104
|
# Load existing config or create new
|
|
105
105
|
config_data = if File.exist?(config_path)
|
|
106
|
-
YAML.
|
|
106
|
+
YAML.safe_load_file(config_path, permitted_classes: [Symbol], symbolize_names: true) || {}
|
|
107
107
|
else
|
|
108
108
|
{}
|
|
109
109
|
end
|
|
@@ -161,20 +161,18 @@ module TrakFlow
|
|
|
161
161
|
@pastel ||= Pastel.new
|
|
162
162
|
end
|
|
163
163
|
|
|
164
|
-
def output(json_data
|
|
164
|
+
def output(json_data)
|
|
165
165
|
if options[:json]
|
|
166
166
|
puts Oj.dump(json_data, mode: :compat, indent: 2)
|
|
167
167
|
else
|
|
168
|
-
|
|
168
|
+
yield
|
|
169
169
|
end
|
|
170
170
|
end
|
|
171
171
|
|
|
172
172
|
def determine_config_path(global)
|
|
173
|
-
if global
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
File.expand_path(PROJECT_CONFIG_PATH)
|
|
177
|
-
elsif File.directory?(".trak_flow")
|
|
173
|
+
return XDG_CONFIG_PATH if global
|
|
174
|
+
|
|
175
|
+
if File.exist?(PROJECT_CONFIG_PATH) || File.directory?(".trak_flow")
|
|
178
176
|
File.expand_path(PROJECT_CONFIG_PATH)
|
|
179
177
|
else
|
|
180
178
|
XDG_CONFIG_PATH
|
|
@@ -235,12 +233,9 @@ module TrakFlow
|
|
|
235
233
|
when "false" then false
|
|
236
234
|
when "nil", "null" then nil
|
|
237
235
|
else
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
# Try float
|
|
242
|
-
elsif value.match?(/\A-?\d+\.\d+\z/)
|
|
243
|
-
value.to_f
|
|
236
|
+
case value
|
|
237
|
+
when /\A-?\d+\z/ then value.to_i
|
|
238
|
+
when /\A-?\d+\.\d+\z/ then value.to_f
|
|
244
239
|
else
|
|
245
240
|
# Keep as string, expand ~ for paths
|
|
246
241
|
value.start_with?("~") ? File.expand_path(value) : value
|
|
@@ -56,14 +56,14 @@ module TrakFlow
|
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
# Delegate helper methods to parent CLI
|
|
59
|
-
def with_database(&
|
|
59
|
+
def with_database(&) = CLI.new.with_database(&)
|
|
60
60
|
def status_icon(status) = CLI.new.status_icon(status)
|
|
61
61
|
|
|
62
|
-
def output(json_data
|
|
62
|
+
def output(json_data)
|
|
63
63
|
if options[:json]
|
|
64
64
|
puts Oj.dump(json_data, mode: :compat, indent: 2)
|
|
65
65
|
else
|
|
66
|
-
|
|
66
|
+
yield
|
|
67
67
|
end
|
|
68
68
|
end
|
|
69
69
|
end
|
|
@@ -62,13 +62,13 @@ module TrakFlow
|
|
|
62
62
|
private
|
|
63
63
|
|
|
64
64
|
# Delegate helper methods to parent CLI
|
|
65
|
-
def with_database(&
|
|
65
|
+
def with_database(&) = CLI.new.with_database(&)
|
|
66
66
|
|
|
67
|
-
def output(json_data
|
|
67
|
+
def output(json_data)
|
|
68
68
|
if options[:json]
|
|
69
69
|
puts Oj.dump(json_data, mode: :compat, indent: 2)
|
|
70
70
|
else
|
|
71
|
-
|
|
71
|
+
yield
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
74
|
end
|