trak_flow 0.1.3
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 +7 -0
- data/.envrc +3 -0
- data/CHANGELOG.md +69 -0
- data/COMMITS.md +196 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +281 -0
- data/README.md +479 -0
- data/Rakefile +16 -0
- data/bin/tf +6 -0
- data/bin/tf_mcp +81 -0
- data/docs/.keep +0 -0
- data/docs/api/database.md +434 -0
- data/docs/api/ruby-library.md +349 -0
- data/docs/api/task-model.md +341 -0
- data/docs/assets/stylesheets/extra.css +53 -0
- data/docs/assets/trak_flow.jpg +0 -0
- data/docs/cli/admin-commands.md +369 -0
- data/docs/cli/dependency-commands.md +321 -0
- data/docs/cli/label-commands.md +222 -0
- data/docs/cli/overview.md +163 -0
- data/docs/cli/plan-commands.md +344 -0
- data/docs/cli/task-commands.md +333 -0
- data/docs/core-concepts/dependencies.md +232 -0
- data/docs/core-concepts/labels.md +217 -0
- data/docs/core-concepts/overview.md +178 -0
- data/docs/core-concepts/plans-workflows.md +264 -0
- data/docs/core-concepts/tasks.md +205 -0
- data/docs/getting-started/configuration.md +120 -0
- data/docs/getting-started/installation.md +79 -0
- data/docs/getting-started/quick-start.md +245 -0
- data/docs/index.md +169 -0
- data/docs/mcp/integration.md +302 -0
- data/docs/mcp/overview.md +206 -0
- data/docs/mcp/resources.md +284 -0
- data/docs/mcp/tools.md +457 -0
- data/examples/basic_usage.rb +365 -0
- data/examples/cli_demo.sh +314 -0
- data/examples/mcp/Gemfile +9 -0
- data/examples/mcp/Gemfile.lock +226 -0
- data/examples/mcp/http_demo.rb +232 -0
- data/examples/mcp/stdio_demo.rb +146 -0
- data/lib/trak_flow/cli/admin_commands.rb +136 -0
- data/lib/trak_flow/cli/config_commands.rb +260 -0
- data/lib/trak_flow/cli/dep_commands.rb +71 -0
- data/lib/trak_flow/cli/label_commands.rb +76 -0
- data/lib/trak_flow/cli/main_commands.rb +386 -0
- data/lib/trak_flow/cli/plan_commands.rb +185 -0
- data/lib/trak_flow/cli/workflow_commands.rb +133 -0
- data/lib/trak_flow/cli.rb +110 -0
- data/lib/trak_flow/config/defaults.yml +114 -0
- data/lib/trak_flow/config/section.rb +74 -0
- data/lib/trak_flow/config.rb +276 -0
- data/lib/trak_flow/graph/dependency_graph.rb +288 -0
- data/lib/trak_flow/id_generator.rb +52 -0
- data/lib/trak_flow/mcp/resources/base_resource.rb +25 -0
- data/lib/trak_flow/mcp/resources/dependency_graph.rb +31 -0
- data/lib/trak_flow/mcp/resources/label_list.rb +21 -0
- data/lib/trak_flow/mcp/resources/plan_by_id.rb +27 -0
- data/lib/trak_flow/mcp/resources/plan_list.rb +21 -0
- data/lib/trak_flow/mcp/resources/task_by_id.rb +31 -0
- data/lib/trak_flow/mcp/resources/task_list.rb +21 -0
- data/lib/trak_flow/mcp/resources/task_next.rb +30 -0
- data/lib/trak_flow/mcp/resources/workflow_by_id.rb +27 -0
- data/lib/trak_flow/mcp/resources/workflow_list.rb +21 -0
- data/lib/trak_flow/mcp/server.rb +140 -0
- data/lib/trak_flow/mcp/tools/base_tool.rb +29 -0
- data/lib/trak_flow/mcp/tools/comment_add.rb +33 -0
- data/lib/trak_flow/mcp/tools/dep_add.rb +34 -0
- data/lib/trak_flow/mcp/tools/dep_remove.rb +25 -0
- data/lib/trak_flow/mcp/tools/label_add.rb +28 -0
- data/lib/trak_flow/mcp/tools/label_remove.rb +25 -0
- data/lib/trak_flow/mcp/tools/plan_add_step.rb +35 -0
- data/lib/trak_flow/mcp/tools/plan_create.rb +33 -0
- data/lib/trak_flow/mcp/tools/plan_run.rb +58 -0
- data/lib/trak_flow/mcp/tools/plan_start.rb +58 -0
- data/lib/trak_flow/mcp/tools/task_block.rb +27 -0
- data/lib/trak_flow/mcp/tools/task_close.rb +26 -0
- data/lib/trak_flow/mcp/tools/task_create.rb +51 -0
- data/lib/trak_flow/mcp/tools/task_defer.rb +27 -0
- data/lib/trak_flow/mcp/tools/task_start.rb +25 -0
- data/lib/trak_flow/mcp/tools/task_update.rb +36 -0
- data/lib/trak_flow/mcp/tools/workflow_discard.rb +28 -0
- data/lib/trak_flow/mcp/tools/workflow_summarize.rb +34 -0
- data/lib/trak_flow/mcp.rb +38 -0
- data/lib/trak_flow/models/comment.rb +71 -0
- data/lib/trak_flow/models/dependency.rb +96 -0
- data/lib/trak_flow/models/label.rb +90 -0
- data/lib/trak_flow/models/task.rb +188 -0
- data/lib/trak_flow/storage/database.rb +638 -0
- data/lib/trak_flow/storage/jsonl.rb +259 -0
- data/lib/trak_flow/time_parser.rb +15 -0
- data/lib/trak_flow/version.rb +5 -0
- data/lib/trak_flow.rb +100 -0
- data/mkdocs.yml +143 -0
- metadata +392 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module TrakFlow
|
|
4
|
+
module Storage
|
|
5
|
+
# JSONL (JSON Lines) persistence layer for Git integration
|
|
6
|
+
# This is the git-tracked source of truth stored in .trak_flow/tasks.jsonl
|
|
7
|
+
# One JSON entity per line makes diffs readable and merges usually automatic
|
|
8
|
+
class Jsonl
|
|
9
|
+
ENTITY_TYPES = %w[task dependency label comment].freeze
|
|
10
|
+
|
|
11
|
+
attr_reader :path
|
|
12
|
+
|
|
13
|
+
def initialize(path = nil)
|
|
14
|
+
@path = path || TrakFlow.jsonl_path
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Export all data from database to JSONL file
|
|
18
|
+
# - Plans are exported (persistent blueprints)
|
|
19
|
+
# - Ephemeral Workflows are NOT exported (temporary only)
|
|
20
|
+
def export(db)
|
|
21
|
+
entities = []
|
|
22
|
+
|
|
23
|
+
# Export regular tasks (excluding ephemeral) and include Plans
|
|
24
|
+
db.list_tasks(include_ephemeral: false, include_plans: true, include_tombstones: true).each do |task|
|
|
25
|
+
entities << { type: "task", data: task.to_h }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
db.all_task_ids.each do |task_id|
|
|
29
|
+
db.find_dependencies(task_id, direction: :outgoing).each do |dep|
|
|
30
|
+
entities << { type: "dependency", data: dep.to_h }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
db.find_labels(task_id).each do |label|
|
|
34
|
+
entities << { type: "label", data: label.to_h }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
db.find_comments(task_id).each do |comment|
|
|
38
|
+
entities << { type: "comment", data: comment.to_h }
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
write_entities(entities)
|
|
43
|
+
db.mark_clean!
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Import all data from JSONL file to database
|
|
47
|
+
# @param db [Database] the database to import into
|
|
48
|
+
# @param orphan_handling [String] how to handle orphaned tasks
|
|
49
|
+
# @param error_policy [String] how to handle import errors: "warn", "strict", or "ignore"
|
|
50
|
+
def import(db, orphan_handling: nil, error_policy: nil)
|
|
51
|
+
orphan_handling ||= TrakFlow.config.get("import.orphan_handling")
|
|
52
|
+
error_policy ||= TrakFlow.config.get("import.error_policy") || "warn"
|
|
53
|
+
|
|
54
|
+
entities = read_entities
|
|
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)
|
|
75
|
+
|
|
76
|
+
db.import_tasks(tasks)
|
|
77
|
+
|
|
78
|
+
import_errors += import_entities(db, :add_dependency, dependencies, error_policy)
|
|
79
|
+
import_errors += import_entities(db, :add_label, labels, error_policy)
|
|
80
|
+
import_errors += import_entities(db, :add_comment, comments, error_policy)
|
|
81
|
+
|
|
82
|
+
raise_if_strict_errors(import_errors, error_policy)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def import_entities(db, method, entities, error_policy)
|
|
88
|
+
errors = []
|
|
89
|
+
entities.each do |entity|
|
|
90
|
+
db.send(method, entity)
|
|
91
|
+
rescue Error => e
|
|
92
|
+
error_info = { entity_type: entity.class.name, error: e.message }
|
|
93
|
+
errors << error_info
|
|
94
|
+
handle_import_error(error_info, error_policy)
|
|
95
|
+
end
|
|
96
|
+
errors
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def handle_import_error(error_info, policy)
|
|
100
|
+
case policy
|
|
101
|
+
when "strict"
|
|
102
|
+
# Errors collected for batch raise
|
|
103
|
+
when "warn"
|
|
104
|
+
debug_me "Warning: Import failed for #{error_info[:entity_type]}: #{error_info[:error]}"
|
|
105
|
+
when "ignore"
|
|
106
|
+
# Silent
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def raise_if_strict_errors(errors, policy)
|
|
111
|
+
return if errors.empty? || policy != "strict"
|
|
112
|
+
|
|
113
|
+
messages = errors.map { |e| "#{e[:entity_type]}: #{e[:error]}" }
|
|
114
|
+
raise ValidationError, "Import failed with #{errors.size} error(s):\n #{messages.join("\n ")}"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
public
|
|
118
|
+
|
|
119
|
+
# Check if JSONL file has changed since last import
|
|
120
|
+
def changed_since?(timestamp)
|
|
121
|
+
return true unless File.exist?(path)
|
|
122
|
+
|
|
123
|
+
File.mtime(path) > timestamp
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Get content hash of the JSONL file
|
|
127
|
+
def content_hash
|
|
128
|
+
return nil unless File.exist?(path)
|
|
129
|
+
|
|
130
|
+
Digest::SHA256.hexdigest(File.read(path))[0, 16]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Check if file exists
|
|
134
|
+
def exists?
|
|
135
|
+
File.exist?(path)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Read raw entities from file
|
|
139
|
+
def read_entities
|
|
140
|
+
return [] unless File.exist?(path)
|
|
141
|
+
|
|
142
|
+
entities = []
|
|
143
|
+
File.readlines(path).each_with_index do |line, index|
|
|
144
|
+
line = line.strip
|
|
145
|
+
next if line.empty? || line.start_with?("#")
|
|
146
|
+
|
|
147
|
+
begin
|
|
148
|
+
data = Oj.load(line, mode: :compat, symbol_keys: true)
|
|
149
|
+
entities << data if valid_entity?(data)
|
|
150
|
+
rescue Oj::ParseError => e
|
|
151
|
+
debug_me "Warning: Could not parse line #{index + 1}: #{e.message}"
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
entities
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Write entities to file
|
|
159
|
+
def write_entities(entities)
|
|
160
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
161
|
+
|
|
162
|
+
File.open(path, "w") do |f|
|
|
163
|
+
f.puts "# TrakFlow task tracker data"
|
|
164
|
+
f.puts "# Generated at #{Time.now.utc.iso8601}"
|
|
165
|
+
f.puts ""
|
|
166
|
+
|
|
167
|
+
entities.each do |entity|
|
|
168
|
+
f.puts Oj.dump(entity, mode: :compat)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Incremental export - only export changed entities
|
|
174
|
+
def incremental_export(db, changed_ids)
|
|
175
|
+
return export(db) unless File.exist?(path)
|
|
176
|
+
|
|
177
|
+
existing = read_entities
|
|
178
|
+
existing_by_id = {}
|
|
179
|
+
|
|
180
|
+
existing.each do |entity|
|
|
181
|
+
id = entity.dig(:data, :id)
|
|
182
|
+
existing_by_id["#{entity[:type]}-#{id}"] = entity if id
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
changed_ids.each do |task_id|
|
|
186
|
+
task = db.find_task(task_id)
|
|
187
|
+
if task
|
|
188
|
+
key = "task-#{task_id}"
|
|
189
|
+
existing_by_id[key] = { type: "task", data: task.to_h }
|
|
190
|
+
|
|
191
|
+
db.find_dependencies(task_id, direction: :outgoing).each do |dep|
|
|
192
|
+
dep_key = "dependency-#{dep.id}"
|
|
193
|
+
existing_by_id[dep_key] = { type: "dependency", data: dep.to_h }
|
|
194
|
+
end
|
|
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
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
write_entities(existing_by_id.values)
|
|
211
|
+
db.mark_clean!
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
private
|
|
215
|
+
|
|
216
|
+
def valid_entity?(data)
|
|
217
|
+
return false unless data.is_a?(Hash)
|
|
218
|
+
return false unless ENTITY_TYPES.include?(data[:type])
|
|
219
|
+
return false unless data[:data].is_a?(Hash)
|
|
220
|
+
|
|
221
|
+
true
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def handle_orphans(tasks, handling)
|
|
225
|
+
task_ids = Set.new(tasks.map(&:id))
|
|
226
|
+
orphans = []
|
|
227
|
+
valid = []
|
|
228
|
+
|
|
229
|
+
tasks.each do |task|
|
|
230
|
+
if task.parent_id && !task_ids.include?(task.parent_id)
|
|
231
|
+
orphans << task
|
|
232
|
+
else
|
|
233
|
+
valid << task
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
return valid if orphans.empty?
|
|
238
|
+
|
|
239
|
+
case handling
|
|
240
|
+
when "allow"
|
|
241
|
+
valid + orphans
|
|
242
|
+
when "skip"
|
|
243
|
+
debug_me "Skipping #{orphans.size} orphaned tasks"
|
|
244
|
+
valid
|
|
245
|
+
when "resurrect"
|
|
246
|
+
orphans.each do |orphan|
|
|
247
|
+
debug_me "Resurrecting orphan: #{orphan.id} (parent: #{orphan.parent_id})"
|
|
248
|
+
orphan.parent_id = nil
|
|
249
|
+
end
|
|
250
|
+
valid + orphans
|
|
251
|
+
when "strict"
|
|
252
|
+
raise ValidationError, "Found #{orphans.size} orphaned tasks with missing parents"
|
|
253
|
+
else
|
|
254
|
+
valid + orphans
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module TrakFlow
|
|
4
|
+
# Shared time parsing utilities for model deserialization
|
|
5
|
+
module TimeParser
|
|
6
|
+
def self.parse(value)
|
|
7
|
+
return nil if value.nil?
|
|
8
|
+
return value if value.is_a?(Time)
|
|
9
|
+
|
|
10
|
+
Time.parse(value)
|
|
11
|
+
rescue ArgumentError
|
|
12
|
+
nil
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/trak_flow.rb
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
require "fileutils"
|
|
5
|
+
require "json"
|
|
6
|
+
require "securerandom"
|
|
7
|
+
require "set"
|
|
8
|
+
require "time"
|
|
9
|
+
|
|
10
|
+
require "oj"
|
|
11
|
+
require "sequel"
|
|
12
|
+
require "sqlite3"
|
|
13
|
+
require "thor"
|
|
14
|
+
require "pastel"
|
|
15
|
+
require "tty-table"
|
|
16
|
+
require "debug_me"
|
|
17
|
+
require "anyway_config"
|
|
18
|
+
|
|
19
|
+
module TrakFlow
|
|
20
|
+
class Error < StandardError; end
|
|
21
|
+
class NotInitializedError < Error; end
|
|
22
|
+
class TaskNotFoundError < Error; end
|
|
23
|
+
class DependencyCycleError < Error; end
|
|
24
|
+
class ValidationError < Error; end
|
|
25
|
+
class ConfigurationError < Error; end
|
|
26
|
+
|
|
27
|
+
TRAK_FLOW_DIR = ".trak_flow"
|
|
28
|
+
DATABASE_FILE = "trak_flow.db"
|
|
29
|
+
JSONL_FILE = "tasks.jsonl" # Default, can be overridden via config.storage.jsonl_file
|
|
30
|
+
CONFIG_FILE = "config.yml"
|
|
31
|
+
|
|
32
|
+
STATUSES = %w[open in_progress blocked deferred closed tombstone pinned].freeze
|
|
33
|
+
PRIORITIES = (0..4).to_a.freeze
|
|
34
|
+
TYPES = %w[bug feature task epic chore].freeze
|
|
35
|
+
DEPENDENCY_TYPES = %w[blocks related parent-child discovered-from].freeze
|
|
36
|
+
|
|
37
|
+
class << self
|
|
38
|
+
def root
|
|
39
|
+
@root ||= find_root
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def trak_flow_dir
|
|
43
|
+
File.join(root, TRAK_FLOW_DIR)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def database_path
|
|
47
|
+
path = config.database.path
|
|
48
|
+
File.expand_path(path)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def jsonl_path
|
|
52
|
+
jsonl_file = config.storage.jsonl_file rescue JSONL_FILE
|
|
53
|
+
File.join(trak_flow_dir, jsonl_file)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def config_path
|
|
57
|
+
File.join(trak_flow_dir, CONFIG_FILE)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def initialized?
|
|
61
|
+
File.directory?(trak_flow_dir) && File.exist?(database_path)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def ensure_initialized!
|
|
65
|
+
raise NotInitializedError, "TrakFlow not initialized. Run 'tf init' first." unless initialized?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def reset_root!
|
|
69
|
+
@root = nil
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def find_root(start_dir = Dir.pwd)
|
|
75
|
+
dir = start_dir
|
|
76
|
+
loop do
|
|
77
|
+
trak_flow_path = File.join(dir, TRAK_FLOW_DIR)
|
|
78
|
+
return dir if File.directory?(trak_flow_path)
|
|
79
|
+
|
|
80
|
+
parent = File.dirname(dir)
|
|
81
|
+
return start_dir if parent == dir
|
|
82
|
+
|
|
83
|
+
dir = parent
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
require_relative "trak_flow/version"
|
|
90
|
+
require_relative "trak_flow/id_generator"
|
|
91
|
+
require_relative "trak_flow/time_parser"
|
|
92
|
+
require_relative "trak_flow/config"
|
|
93
|
+
require_relative "trak_flow/models/task"
|
|
94
|
+
require_relative "trak_flow/models/dependency"
|
|
95
|
+
require_relative "trak_flow/models/label"
|
|
96
|
+
require_relative "trak_flow/models/comment"
|
|
97
|
+
require_relative "trak_flow/storage/database"
|
|
98
|
+
require_relative "trak_flow/storage/jsonl"
|
|
99
|
+
require_relative "trak_flow/graph/dependency_graph"
|
|
100
|
+
require_relative "trak_flow/cli"
|
data/mkdocs.yml
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
site_name: TrakFlow Documentation
|
|
2
|
+
site_description: "A distributed task tracking system for AI agents with DAG-based workflow engine"
|
|
3
|
+
site_author: "Dewayne VanHoozer"
|
|
4
|
+
site_url: "https://madbomber.github.io/trak_flow/"
|
|
5
|
+
|
|
6
|
+
repo_name: "MadBomber/trak_flow"
|
|
7
|
+
repo_url: "https://github.com/MadBomber/trak_flow"
|
|
8
|
+
edit_uri: "edit/main/docs/"
|
|
9
|
+
|
|
10
|
+
theme:
|
|
11
|
+
name: material
|
|
12
|
+
language: en
|
|
13
|
+
favicon: assets/logo.png
|
|
14
|
+
|
|
15
|
+
palette:
|
|
16
|
+
- media: "(prefers-color-scheme: light)"
|
|
17
|
+
scheme: default
|
|
18
|
+
primary: teal
|
|
19
|
+
accent: deep-orange
|
|
20
|
+
toggle:
|
|
21
|
+
icon: material/brightness-7
|
|
22
|
+
name: Switch to dark mode
|
|
23
|
+
|
|
24
|
+
- media: "(prefers-color-scheme: dark)"
|
|
25
|
+
scheme: slate
|
|
26
|
+
primary: teal
|
|
27
|
+
accent: deep-orange
|
|
28
|
+
toggle:
|
|
29
|
+
icon: material/brightness-4
|
|
30
|
+
name: Switch to light mode
|
|
31
|
+
|
|
32
|
+
font:
|
|
33
|
+
text: Roboto
|
|
34
|
+
code: Roboto Mono
|
|
35
|
+
|
|
36
|
+
features:
|
|
37
|
+
- navigation.instant
|
|
38
|
+
- navigation.tracking
|
|
39
|
+
- navigation.tabs
|
|
40
|
+
- navigation.tabs.sticky
|
|
41
|
+
- navigation.sections
|
|
42
|
+
- navigation.path
|
|
43
|
+
- navigation.indexes
|
|
44
|
+
- navigation.top
|
|
45
|
+
- toc.follow
|
|
46
|
+
- search.suggest
|
|
47
|
+
- search.highlight
|
|
48
|
+
- search.share
|
|
49
|
+
- header.autohide
|
|
50
|
+
- content.code.copy
|
|
51
|
+
- content.code.annotate
|
|
52
|
+
- content.tabs.link
|
|
53
|
+
- content.tooltips
|
|
54
|
+
- content.action.edit
|
|
55
|
+
- content.action.view
|
|
56
|
+
|
|
57
|
+
plugins:
|
|
58
|
+
- search:
|
|
59
|
+
separator: '[\s\-,:!=\[\]()"\`/]+|\.(?!\d)|&[lg]t;|(?!\b)(?=[A-Z][a-z])'
|
|
60
|
+
|
|
61
|
+
markdown_extensions:
|
|
62
|
+
- abbr
|
|
63
|
+
- admonition
|
|
64
|
+
- attr_list
|
|
65
|
+
- def_list
|
|
66
|
+
- footnotes
|
|
67
|
+
- md_in_html
|
|
68
|
+
- toc:
|
|
69
|
+
permalink: true
|
|
70
|
+
title: On this page
|
|
71
|
+
- pymdownx.arithmatex:
|
|
72
|
+
generic: true
|
|
73
|
+
- pymdownx.betterem:
|
|
74
|
+
smart_enable: all
|
|
75
|
+
- pymdownx.caret
|
|
76
|
+
- pymdownx.details
|
|
77
|
+
- pymdownx.emoji:
|
|
78
|
+
emoji_index: !!python/name:material.extensions.emoji.twemoji
|
|
79
|
+
emoji_generator: !!python/name:material.extensions.emoji.to_svg
|
|
80
|
+
- pymdownx.highlight:
|
|
81
|
+
anchor_linenums: true
|
|
82
|
+
line_spans: __span
|
|
83
|
+
pygments_lang_class: true
|
|
84
|
+
- pymdownx.inlinehilite
|
|
85
|
+
- pymdownx.keys
|
|
86
|
+
- pymdownx.magiclink:
|
|
87
|
+
repo_url_shorthand: true
|
|
88
|
+
user: MadBomber
|
|
89
|
+
repo: trak_flow
|
|
90
|
+
- pymdownx.mark
|
|
91
|
+
- pymdownx.smartsymbols
|
|
92
|
+
- pymdownx.superfences:
|
|
93
|
+
custom_fences:
|
|
94
|
+
- name: mermaid
|
|
95
|
+
class: mermaid
|
|
96
|
+
format: !!python/name:pymdownx.superfences.fence_code_format
|
|
97
|
+
- pymdownx.tabbed:
|
|
98
|
+
alternate_style: true
|
|
99
|
+
- pymdownx.tasklist:
|
|
100
|
+
custom_checkbox: true
|
|
101
|
+
- pymdownx.tilde
|
|
102
|
+
|
|
103
|
+
extra_css:
|
|
104
|
+
- assets/stylesheets/extra.css
|
|
105
|
+
|
|
106
|
+
extra:
|
|
107
|
+
version: 0.0.1
|
|
108
|
+
social:
|
|
109
|
+
- icon: fontawesome/brands/github
|
|
110
|
+
link: https://github.com/MadBomber/trak_flow
|
|
111
|
+
name: GitHub Repository
|
|
112
|
+
generator: false
|
|
113
|
+
|
|
114
|
+
nav:
|
|
115
|
+
- Home: index.md
|
|
116
|
+
- Getting Started:
|
|
117
|
+
- Installation: getting-started/installation.md
|
|
118
|
+
- Quick Start: getting-started/quick-start.md
|
|
119
|
+
- Configuration: getting-started/configuration.md
|
|
120
|
+
- Core Concepts:
|
|
121
|
+
- Overview: core-concepts/overview.md
|
|
122
|
+
- Tasks: core-concepts/tasks.md
|
|
123
|
+
- Plans & Workflows: core-concepts/plans-workflows.md
|
|
124
|
+
- Dependencies: core-concepts/dependencies.md
|
|
125
|
+
- Labels: core-concepts/labels.md
|
|
126
|
+
- CLI Reference:
|
|
127
|
+
- Overview: cli/overview.md
|
|
128
|
+
- Task Commands: cli/task-commands.md
|
|
129
|
+
- Plan & Workflow Commands: cli/plan-commands.md
|
|
130
|
+
- Dependency Commands: cli/dependency-commands.md
|
|
131
|
+
- Label Commands: cli/label-commands.md
|
|
132
|
+
- Admin Commands: cli/admin-commands.md
|
|
133
|
+
- MCP Server:
|
|
134
|
+
- Overview: mcp/overview.md
|
|
135
|
+
- Tools Reference: mcp/tools.md
|
|
136
|
+
- Resources Reference: mcp/resources.md
|
|
137
|
+
- Integration Guide: mcp/integration.md
|
|
138
|
+
- API Reference:
|
|
139
|
+
- Ruby Library: api/ruby-library.md
|
|
140
|
+
- Task Model: api/task-model.md
|
|
141
|
+
- Database: api/database.md
|
|
142
|
+
|
|
143
|
+
copyright: Copyright © 2025 Dewayne VanHoozer
|