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
|
@@ -18,12 +18,9 @@ module TrakFlow
|
|
|
18
18
|
# - Plans are exported (persistent blueprints)
|
|
19
19
|
# - Ephemeral Workflows are NOT exported (temporary only)
|
|
20
20
|
def export(db)
|
|
21
|
-
entities = []
|
|
22
|
-
|
|
23
21
|
# Export regular tasks (excluding ephemeral) and include Plans
|
|
24
|
-
db.list_tasks(include_ephemeral: false, include_plans: true, include_tombstones: true)
|
|
25
|
-
|
|
26
|
-
end
|
|
22
|
+
entities = db.list_tasks(include_ephemeral: false, include_plans: true, include_tombstones: true)
|
|
23
|
+
.map { |task| { type: "task", data: task.to_h } }
|
|
27
24
|
|
|
28
25
|
db.all_task_ids.each do |task_id|
|
|
29
26
|
db.find_dependencies(task_id, direction: :outgoing).each do |dep|
|
|
@@ -51,39 +48,42 @@ module TrakFlow
|
|
|
51
48
|
orphan_handling ||= TrakFlow.config.get("import.orphan_handling")
|
|
52
49
|
error_policy ||= TrakFlow.config.get("import.error_policy") || "warn"
|
|
53
50
|
|
|
54
|
-
|
|
55
|
-
tasks = []
|
|
56
|
-
dependencies = []
|
|
57
|
-
labels = []
|
|
58
|
-
comments = []
|
|
59
|
-
import_errors = []
|
|
60
|
-
|
|
61
|
-
entities.each do |entity|
|
|
62
|
-
case entity[:type]
|
|
63
|
-
when "task"
|
|
64
|
-
tasks << Models::Task.from_hash(entity[:data])
|
|
65
|
-
when "dependency"
|
|
66
|
-
dependencies << Models::Dependency.from_hash(entity[:data])
|
|
67
|
-
when "label"
|
|
68
|
-
labels << Models::Label.from_hash(entity[:data])
|
|
69
|
-
when "comment"
|
|
70
|
-
comments << Models::Comment.from_hash(entity[:data])
|
|
71
|
-
end
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
tasks = handle_orphans(tasks, orphan_handling)
|
|
51
|
+
models = build_import_models(read_entities)
|
|
52
|
+
tasks = handle_orphans(models[:task], orphan_handling)
|
|
75
53
|
|
|
76
54
|
db.import_tasks(tasks)
|
|
77
55
|
|
|
78
|
-
import_errors
|
|
79
|
-
import_errors += import_entities(db, :add_label,
|
|
80
|
-
import_errors += import_entities(db, :add_comment,
|
|
56
|
+
import_errors = import_entities(db, :add_dependency, models[:dependency], error_policy)
|
|
57
|
+
import_errors += import_entities(db, :add_label, models[:label], error_policy)
|
|
58
|
+
import_errors += import_entities(db, :add_comment, models[:comment], error_policy)
|
|
81
59
|
|
|
82
60
|
raise_if_strict_errors(import_errors, error_policy)
|
|
83
61
|
end
|
|
84
62
|
|
|
85
63
|
private
|
|
86
64
|
|
|
65
|
+
IMPORT_MODEL_CLASSES = {
|
|
66
|
+
"task" => Models::Task,
|
|
67
|
+
"dependency" => Models::Dependency,
|
|
68
|
+
"label" => Models::Label,
|
|
69
|
+
"comment" => Models::Comment
|
|
70
|
+
}.freeze
|
|
71
|
+
|
|
72
|
+
private_constant :IMPORT_MODEL_CLASSES
|
|
73
|
+
|
|
74
|
+
# Groups raw JSONL entities into model instances keyed by type;
|
|
75
|
+
# entities with an unknown type are dropped.
|
|
76
|
+
def build_import_models(entities)
|
|
77
|
+
models = { task: [], dependency: [], label: [], comment: [] }
|
|
78
|
+
|
|
79
|
+
entities.each do |entity|
|
|
80
|
+
klass = IMPORT_MODEL_CLASSES[entity[:type]]
|
|
81
|
+
models[entity[:type].to_sym] << klass.from_hash(entity[:data]) if klass
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
models
|
|
85
|
+
end
|
|
86
|
+
|
|
87
87
|
def import_entities(db, method, entities, error_policy)
|
|
88
88
|
errors = []
|
|
89
89
|
entities.each do |entity|
|
|
@@ -174,44 +174,43 @@ module TrakFlow
|
|
|
174
174
|
def incremental_export(db, changed_ids)
|
|
175
175
|
return export(db) unless File.exist?(path)
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
|
|
177
|
+
entities_by_key = index_entities(read_entities)
|
|
178
|
+
changed_ids.each { |task_id| apply_task_change(db, task_id, entities_by_key) }
|
|
179
179
|
|
|
180
|
-
|
|
180
|
+
write_entities(entities_by_key.values)
|
|
181
|
+
db.mark_clean!
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
private
|
|
185
|
+
|
|
186
|
+
# Indexes entities by "type-id" for incremental merging.
|
|
187
|
+
def index_entities(entities)
|
|
188
|
+
entities.each_with_object({}) do |entity, index|
|
|
181
189
|
id = entity.dig(:data, :id)
|
|
182
|
-
|
|
190
|
+
index["#{entity[:type]}-#{id}"] = entity if id
|
|
183
191
|
end
|
|
192
|
+
end
|
|
184
193
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
db.find_labels(task_id).each do |label|
|
|
197
|
-
label_key = "label-#{label.id}"
|
|
198
|
-
existing_by_id[label_key] = { type: "label", data: label.to_h }
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
db.find_comments(task_id).each do |comment|
|
|
202
|
-
comment_key = "comment-#{comment.id}"
|
|
203
|
-
existing_by_id[comment_key] = { type: "comment", data: comment.to_h }
|
|
204
|
-
end
|
|
205
|
-
else
|
|
206
|
-
existing_by_id.delete("task-#{task_id}")
|
|
207
|
-
end
|
|
194
|
+
# Replaces one task's entities in the index — or removes the task's
|
|
195
|
+
# entry when it no longer exists in the database.
|
|
196
|
+
def apply_task_change(db, task_id, index)
|
|
197
|
+
task = db.find_task(task_id)
|
|
198
|
+
return index.delete("task-#{task_id}") unless task
|
|
199
|
+
|
|
200
|
+
index["task-#{task_id}"] = { type: "task", data: task.to_h }
|
|
201
|
+
|
|
202
|
+
db.find_dependencies(task_id, direction: :outgoing).each do |dep|
|
|
203
|
+
index["dependency-#{dep.id}"] = { type: "dependency", data: dep.to_h }
|
|
208
204
|
end
|
|
209
205
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
206
|
+
db.find_labels(task_id).each do |label|
|
|
207
|
+
index["label-#{label.id}"] = { type: "label", data: label.to_h }
|
|
208
|
+
end
|
|
213
209
|
|
|
214
|
-
|
|
210
|
+
db.find_comments(task_id).each do |comment|
|
|
211
|
+
index["comment-#{comment.id}"] = { type: "comment", data: comment.to_h }
|
|
212
|
+
end
|
|
213
|
+
end
|
|
215
214
|
|
|
216
215
|
def valid_entity?(data)
|
|
217
216
|
return false unless data.is_a?(Hash)
|
|
@@ -237,8 +236,6 @@ module TrakFlow
|
|
|
237
236
|
return valid if orphans.empty?
|
|
238
237
|
|
|
239
238
|
case handling
|
|
240
|
-
when "allow"
|
|
241
|
-
valid + orphans
|
|
242
239
|
when "skip"
|
|
243
240
|
debug_me "Skipping #{orphans.size} orphaned tasks"
|
|
244
241
|
valid
|
|
@@ -251,6 +248,7 @@ module TrakFlow
|
|
|
251
248
|
when "strict"
|
|
252
249
|
raise ValidationError, "Found #{orphans.size} orphaned tasks with missing parents"
|
|
253
250
|
else
|
|
251
|
+
# "allow" and any unrecognized handling keep orphans as-is
|
|
254
252
|
valid + orphans
|
|
255
253
|
end
|
|
256
254
|
end
|
data/lib/trak_flow/version.rb
CHANGED
data/lib/trak_flow.rb
CHANGED
data/trak_flow.gemspec
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/trak_flow/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'trak_flow'
|
|
7
|
+
spec.version = TrakFlow::VERSION
|
|
8
|
+
spec.authors = ['Dewayne VanHoozer']
|
|
9
|
+
spec.email = ['dewayne@vanhoozer.me']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'A distributed task tracking system for Robots'
|
|
12
|
+
spec.description = <<~DESC
|
|
13
|
+
TrakFlow is a specialized task tracking system designed for robots.
|
|
14
|
+
It implements a dependency-aware graph, allowing
|
|
15
|
+
robots to handle complex/lengthy task workflows without losing context. Tasks are
|
|
16
|
+
stored as JSONL files within a designated directory, using Git as the
|
|
17
|
+
underlying database for versioning, branching, and merging.
|
|
18
|
+
DESC
|
|
19
|
+
spec.homepage = 'https://github.com/madbomber/trak_flow'
|
|
20
|
+
spec.license = 'MIT'
|
|
21
|
+
spec.required_ruby_version = '>= 3.1.0'
|
|
22
|
+
|
|
23
|
+
spec.files = Dir.chdir(__dir__) do
|
|
24
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
25
|
+
(File.expand_path(f) == __FILE__) ||
|
|
26
|
+
f.start_with?(*%w[bin/ test/ spec/ features/ .git .github])
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
spec.bindir = 'bin'
|
|
30
|
+
spec.executables = %w[tf tf_mcp]
|
|
31
|
+
spec.require_paths = ['lib']
|
|
32
|
+
|
|
33
|
+
spec.add_dependency 'anyway_config', '~> 2.0'
|
|
34
|
+
spec.add_dependency 'debug_me'
|
|
35
|
+
spec.add_dependency 'fast-mcp'
|
|
36
|
+
spec.add_dependency 'oj', '~> 3.16'
|
|
37
|
+
spec.add_dependency 'pastel', '~> 0.8'
|
|
38
|
+
spec.add_dependency 'sequel', '~> 5.0'
|
|
39
|
+
spec.add_dependency 'sqlite3', '~> 2.0'
|
|
40
|
+
spec.add_dependency 'thor', '~> 1.3'
|
|
41
|
+
spec.add_dependency 'tty-spinner', '~> 0.9'
|
|
42
|
+
spec.add_dependency 'tty-table', '~> 0.12'
|
|
43
|
+
|
|
44
|
+
# puma and rackup are deliberately absent: the MCP HTTP transport
|
|
45
|
+
# lazy-loads them (see TrakFlow::Mcp::Server#require_http_transport_gems)
|
|
46
|
+
# so consumers that only use the models/storage API or the stdio
|
|
47
|
+
# transport don't carry a web server. To run the HTTP transport, add
|
|
48
|
+
# `gem 'puma', '>= 7.2.1'` and `gem 'rackup'` to your Gemfile.
|
|
49
|
+
|
|
50
|
+
spec.add_development_dependency 'bundler'
|
|
51
|
+
spec.add_development_dependency 'minitest', '~> 5.0'
|
|
52
|
+
spec.add_development_dependency 'minitest-reporters', '~> 1.6'
|
|
53
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
|
54
|
+
spec.add_development_dependency 'rubocop', '~> 1.0'
|
|
55
|
+
spec.add_development_dependency 'simplecov', '~> 0.22'
|
|
56
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
57
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: trak_flow
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dewayne VanHoozer
|
|
@@ -149,34 +149,6 @@ dependencies:
|
|
|
149
149
|
- - "~>"
|
|
150
150
|
- !ruby/object:Gem::Version
|
|
151
151
|
version: '0.12'
|
|
152
|
-
- !ruby/object:Gem::Dependency
|
|
153
|
-
name: puma
|
|
154
|
-
requirement: !ruby/object:Gem::Requirement
|
|
155
|
-
requirements:
|
|
156
|
-
- - "~>"
|
|
157
|
-
- !ruby/object:Gem::Version
|
|
158
|
-
version: '6.0'
|
|
159
|
-
type: :runtime
|
|
160
|
-
prerelease: false
|
|
161
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
162
|
-
requirements:
|
|
163
|
-
- - "~>"
|
|
164
|
-
- !ruby/object:Gem::Version
|
|
165
|
-
version: '6.0'
|
|
166
|
-
- !ruby/object:Gem::Dependency
|
|
167
|
-
name: rackup
|
|
168
|
-
requirement: !ruby/object:Gem::Requirement
|
|
169
|
-
requirements:
|
|
170
|
-
- - "~>"
|
|
171
|
-
- !ruby/object:Gem::Version
|
|
172
|
-
version: '2.0'
|
|
173
|
-
type: :runtime
|
|
174
|
-
prerelease: false
|
|
175
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
176
|
-
requirements:
|
|
177
|
-
- - "~>"
|
|
178
|
-
- !ruby/object:Gem::Version
|
|
179
|
-
version: '2.0'
|
|
180
152
|
- !ruby/object:Gem::Dependency
|
|
181
153
|
name: bundler
|
|
182
154
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -276,12 +248,18 @@ extensions: []
|
|
|
276
248
|
extra_rdoc_files: []
|
|
277
249
|
files:
|
|
278
250
|
- ".envrc"
|
|
251
|
+
- ".fasterer.yml"
|
|
252
|
+
- ".loki"
|
|
253
|
+
- ".reek.yml"
|
|
254
|
+
- ".rubocop.yml"
|
|
255
|
+
- Archspec.rb
|
|
279
256
|
- CHANGELOG.md
|
|
280
257
|
- COMMITS.md
|
|
281
258
|
- Gemfile
|
|
282
259
|
- Gemfile.lock
|
|
283
260
|
- README.md
|
|
284
261
|
- Rakefile
|
|
262
|
+
- _typos.toml
|
|
285
263
|
- bin/tf
|
|
286
264
|
- bin/tf_mcp
|
|
287
265
|
- docs/.keep
|
|
@@ -368,10 +346,12 @@ files:
|
|
|
368
346
|
- lib/trak_flow/time_parser.rb
|
|
369
347
|
- lib/trak_flow/version.rb
|
|
370
348
|
- mkdocs.yml
|
|
349
|
+
- trak_flow.gemspec
|
|
371
350
|
homepage: https://github.com/madbomber/trak_flow
|
|
372
351
|
licenses:
|
|
373
352
|
- MIT
|
|
374
|
-
metadata:
|
|
353
|
+
metadata:
|
|
354
|
+
rubygems_mfa_required: 'true'
|
|
375
355
|
rdoc_options: []
|
|
376
356
|
require_paths:
|
|
377
357
|
- lib
|
|
@@ -386,7 +366,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
386
366
|
- !ruby/object:Gem::Version
|
|
387
367
|
version: '0'
|
|
388
368
|
requirements: []
|
|
389
|
-
rubygems_version: 4.0.
|
|
369
|
+
rubygems_version: 4.0.20
|
|
390
370
|
specification_version: 4
|
|
391
371
|
summary: A distributed task tracking system for Robots
|
|
392
372
|
test_files: []
|