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,365 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# TrakFlow Basic Usage Demo
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates the core functionality of TrakFlow,
|
|
7
|
+
# a distributed task tracking system designed for robots/AI agents.
|
|
8
|
+
#
|
|
9
|
+
# Run with: bundle exec ruby examples/basic_usage.rb
|
|
10
|
+
|
|
11
|
+
require "bundler/setup"
|
|
12
|
+
require "trak_flow"
|
|
13
|
+
require "fileutils"
|
|
14
|
+
require "tmpdir"
|
|
15
|
+
|
|
16
|
+
# Create a temporary directory for the demo
|
|
17
|
+
demo_dir = Dir.mktmpdir("trak_flow_demo")
|
|
18
|
+
Dir.chdir(demo_dir)
|
|
19
|
+
|
|
20
|
+
# Reset TrakFlow to use the demo directory
|
|
21
|
+
TrakFlow.reset_root!
|
|
22
|
+
TrakFlow.reset_config!
|
|
23
|
+
|
|
24
|
+
puts <<~HEADER
|
|
25
|
+
============================================================
|
|
26
|
+
TrakFlow Basic Usage Demo
|
|
27
|
+
============================================================
|
|
28
|
+
Demo directory: #{demo_dir}
|
|
29
|
+
|
|
30
|
+
HEADER
|
|
31
|
+
|
|
32
|
+
# =============================================================================
|
|
33
|
+
# 1. Initialize TrakFlow
|
|
34
|
+
# =============================================================================
|
|
35
|
+
|
|
36
|
+
puts "1. Initializing TrakFlow..."
|
|
37
|
+
trak_flow_dir = File.join(demo_dir, ".trak_flow")
|
|
38
|
+
FileUtils.mkdir_p(trak_flow_dir)
|
|
39
|
+
|
|
40
|
+
db_path = File.join(trak_flow_dir, "trak_flow.db")
|
|
41
|
+
db = TrakFlow::Storage::Database.new(db_path)
|
|
42
|
+
db.connect
|
|
43
|
+
puts " Database initialized at #{db_path}\n\n"
|
|
44
|
+
|
|
45
|
+
# =============================================================================
|
|
46
|
+
# 2. Configuration
|
|
47
|
+
# =============================================================================
|
|
48
|
+
|
|
49
|
+
puts "2. Configuration..."
|
|
50
|
+
puts " Actor: #{TrakFlow.config.actor}"
|
|
51
|
+
puts " Output JSON: #{TrakFlow.config.output.json}"
|
|
52
|
+
puts " Daemon auto-start: #{TrakFlow.config.daemon.auto_start}"
|
|
53
|
+
|
|
54
|
+
# Modify configuration
|
|
55
|
+
TrakFlow.configure do |config|
|
|
56
|
+
config.actor = "demo-robot"
|
|
57
|
+
end
|
|
58
|
+
puts " Actor (after configure): #{TrakFlow.config.actor}\n\n"
|
|
59
|
+
|
|
60
|
+
# =============================================================================
|
|
61
|
+
# 3. Creating Tasks
|
|
62
|
+
# =============================================================================
|
|
63
|
+
|
|
64
|
+
puts "3. Creating Tasks..."
|
|
65
|
+
|
|
66
|
+
# Create an epic
|
|
67
|
+
epic = TrakFlow::Models::Task.new(
|
|
68
|
+
title: "Build User Authentication System",
|
|
69
|
+
description: "Implement complete user auth with login, logout, and sessions",
|
|
70
|
+
type: "epic",
|
|
71
|
+
priority: 1
|
|
72
|
+
)
|
|
73
|
+
epic = db.create_task(epic)
|
|
74
|
+
puts " Created epic: [#{epic.id}] #{epic.title}"
|
|
75
|
+
|
|
76
|
+
# Create regular tasks
|
|
77
|
+
task1 = TrakFlow::Models::Task.new(
|
|
78
|
+
title: "Design database schema for users",
|
|
79
|
+
description: "Create tables for users, sessions, and password resets",
|
|
80
|
+
type: "task",
|
|
81
|
+
priority: 1,
|
|
82
|
+
assignee: "demo-robot"
|
|
83
|
+
)
|
|
84
|
+
task1 = db.create_task(task1)
|
|
85
|
+
puts " Created task: [#{task1.id}] #{task1.title}"
|
|
86
|
+
|
|
87
|
+
task2 = TrakFlow::Models::Task.new(
|
|
88
|
+
title: "Implement login endpoint",
|
|
89
|
+
description: "POST /api/login with email and password",
|
|
90
|
+
type: "feature",
|
|
91
|
+
priority: 2
|
|
92
|
+
)
|
|
93
|
+
task2 = db.create_task(task2)
|
|
94
|
+
puts " Created feature: [#{task2.id}] #{task2.title}"
|
|
95
|
+
|
|
96
|
+
task3 = TrakFlow::Models::Task.new(
|
|
97
|
+
title: "Write login integration tests",
|
|
98
|
+
type: "task",
|
|
99
|
+
priority: 3
|
|
100
|
+
)
|
|
101
|
+
task3 = db.create_task(task3)
|
|
102
|
+
puts " Created task: [#{task3.id}] #{task3.title}"
|
|
103
|
+
|
|
104
|
+
# Create a bug
|
|
105
|
+
bug = TrakFlow::Models::Task.new(
|
|
106
|
+
title: "Fix password validation regex",
|
|
107
|
+
description: "Current regex doesn't allow special characters",
|
|
108
|
+
type: "bug",
|
|
109
|
+
priority: 0 # Highest priority
|
|
110
|
+
)
|
|
111
|
+
bug = db.create_task(bug)
|
|
112
|
+
puts " Created bug: [#{bug.id}] #{bug.title}\n\n"
|
|
113
|
+
|
|
114
|
+
# =============================================================================
|
|
115
|
+
# 4. Dependencies
|
|
116
|
+
# =============================================================================
|
|
117
|
+
|
|
118
|
+
puts "4. Setting up Dependencies..."
|
|
119
|
+
|
|
120
|
+
# Task2 (login endpoint) is blocked by Task1 (database schema)
|
|
121
|
+
dep1 = TrakFlow::Models::Dependency.new(
|
|
122
|
+
source_id: task1.id,
|
|
123
|
+
target_id: task2.id,
|
|
124
|
+
type: "blocks"
|
|
125
|
+
)
|
|
126
|
+
db.add_dependency(dep1)
|
|
127
|
+
puts " #{task1.id} blocks #{task2.id}"
|
|
128
|
+
|
|
129
|
+
# Task3 (tests) is blocked by Task2 (login endpoint)
|
|
130
|
+
dep2 = TrakFlow::Models::Dependency.new(
|
|
131
|
+
source_id: task2.id,
|
|
132
|
+
target_id: task3.id,
|
|
133
|
+
type: "blocks"
|
|
134
|
+
)
|
|
135
|
+
db.add_dependency(dep2)
|
|
136
|
+
puts " #{task2.id} blocks #{task3.id}"
|
|
137
|
+
|
|
138
|
+
# Epic is related to all tasks
|
|
139
|
+
dep3 = TrakFlow::Models::Dependency.new(
|
|
140
|
+
source_id: epic.id,
|
|
141
|
+
target_id: task1.id,
|
|
142
|
+
type: "related"
|
|
143
|
+
)
|
|
144
|
+
db.add_dependency(dep3)
|
|
145
|
+
puts " #{epic.id} related to #{task1.id}\n\n"
|
|
146
|
+
|
|
147
|
+
# =============================================================================
|
|
148
|
+
# 5. Labels
|
|
149
|
+
# =============================================================================
|
|
150
|
+
|
|
151
|
+
puts "5. Adding Labels..."
|
|
152
|
+
|
|
153
|
+
# Add labels to the bug
|
|
154
|
+
label1 = TrakFlow::Models::Label.new(task_id: bug.id, name: "critical")
|
|
155
|
+
db.add_label(label1)
|
|
156
|
+
puts " Added 'critical' label to #{bug.id}"
|
|
157
|
+
|
|
158
|
+
label2 = TrakFlow::Models::Label.new(task_id: bug.id, name: "security")
|
|
159
|
+
db.add_label(label2)
|
|
160
|
+
puts " Added 'security' label to #{bug.id}"
|
|
161
|
+
|
|
162
|
+
# Use state labels (dimension:value format)
|
|
163
|
+
db.set_state(task1.id, "complexity", "medium", reason: "Standard CRUD operations")
|
|
164
|
+
puts " Set complexity:medium on #{task1.id}"
|
|
165
|
+
|
|
166
|
+
state = db.get_state(task1.id, "complexity")
|
|
167
|
+
puts " Retrieved state - complexity: #{state}\n\n"
|
|
168
|
+
|
|
169
|
+
# =============================================================================
|
|
170
|
+
# 6. Comments
|
|
171
|
+
# =============================================================================
|
|
172
|
+
|
|
173
|
+
puts "6. Adding Comments..."
|
|
174
|
+
|
|
175
|
+
comment1 = TrakFlow::Models::Comment.new(
|
|
176
|
+
task_id: task1.id,
|
|
177
|
+
author: "demo-robot",
|
|
178
|
+
body: "Starting work on the users table schema."
|
|
179
|
+
)
|
|
180
|
+
db.add_comment(comment1)
|
|
181
|
+
puts " Added comment to #{task1.id}"
|
|
182
|
+
|
|
183
|
+
comment2 = TrakFlow::Models::Comment.new(
|
|
184
|
+
task_id: task1.id,
|
|
185
|
+
author: "demo-robot",
|
|
186
|
+
body: "Schema draft complete. Ready for review."
|
|
187
|
+
)
|
|
188
|
+
db.add_comment(comment2)
|
|
189
|
+
puts " Added comment to #{task1.id}"
|
|
190
|
+
|
|
191
|
+
comments = db.find_comments(task1.id)
|
|
192
|
+
puts " Task #{task1.id} has #{comments.size} comments\n\n"
|
|
193
|
+
|
|
194
|
+
# =============================================================================
|
|
195
|
+
# 7. Querying Tasks
|
|
196
|
+
# =============================================================================
|
|
197
|
+
|
|
198
|
+
puts "7. Querying Tasks..."
|
|
199
|
+
|
|
200
|
+
# List all tasks
|
|
201
|
+
all_tasks = db.list_tasks
|
|
202
|
+
puts " Total tasks: #{all_tasks.size}"
|
|
203
|
+
|
|
204
|
+
# Filter by status
|
|
205
|
+
open_tasks = db.list_tasks(status: "open")
|
|
206
|
+
puts " Open tasks: #{open_tasks.size}"
|
|
207
|
+
|
|
208
|
+
# Filter by priority
|
|
209
|
+
high_priority = db.list_tasks(priority: 0)
|
|
210
|
+
puts " P0 (highest priority) tasks: #{high_priority.size}"
|
|
211
|
+
|
|
212
|
+
# Filter by type
|
|
213
|
+
bugs = db.list_tasks(type: "bug")
|
|
214
|
+
puts " Bugs: #{bugs.size}"
|
|
215
|
+
|
|
216
|
+
# Filter by assignee
|
|
217
|
+
my_tasks = db.list_tasks(assignee: "demo-robot")
|
|
218
|
+
puts " Tasks assigned to demo-robot: #{my_tasks.size}\n\n"
|
|
219
|
+
|
|
220
|
+
# =============================================================================
|
|
221
|
+
# 8. Ready Work Detection
|
|
222
|
+
# =============================================================================
|
|
223
|
+
|
|
224
|
+
puts "8. Ready Work Detection..."
|
|
225
|
+
|
|
226
|
+
ready = db.ready_tasks
|
|
227
|
+
puts " Tasks ready to work on (not blocked):"
|
|
228
|
+
ready.each do |task|
|
|
229
|
+
puts " [#{task.id}] P#{task.priority} #{task.type}: #{task.title}"
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
blocked = db.blocked_tasks
|
|
233
|
+
puts "\n Blocked tasks:"
|
|
234
|
+
blocked.each do |task|
|
|
235
|
+
deps = db.blocking_dependencies(task.id)
|
|
236
|
+
blocker_ids = deps.map(&:source_id).join(", ")
|
|
237
|
+
puts " [#{task.id}] #{task.title} (blocked by: #{blocker_ids})"
|
|
238
|
+
end
|
|
239
|
+
puts
|
|
240
|
+
|
|
241
|
+
# =============================================================================
|
|
242
|
+
# 9. Task Lifecycle
|
|
243
|
+
# =============================================================================
|
|
244
|
+
|
|
245
|
+
puts "9. Task Lifecycle..."
|
|
246
|
+
|
|
247
|
+
# Start working on the bug (highest priority, not blocked)
|
|
248
|
+
bug_fresh = db.find_task!(bug.id)
|
|
249
|
+
bug_fresh.status = "in_progress"
|
|
250
|
+
db.update_task(bug_fresh)
|
|
251
|
+
puts " Bug #{bug.id} status: #{bug_fresh.status}"
|
|
252
|
+
|
|
253
|
+
# Complete the bug
|
|
254
|
+
bug_fresh.close!(reason: "Fixed regex to allow !@#$%^&*()")
|
|
255
|
+
db.update_task(bug_fresh)
|
|
256
|
+
puts " Bug #{bug.id} closed at: #{bug_fresh.closed_at}"
|
|
257
|
+
|
|
258
|
+
# Complete task1 to unblock task2
|
|
259
|
+
task1_fresh = db.find_task!(task1.id)
|
|
260
|
+
task1_fresh.close!(reason: "Schema implemented and migrated")
|
|
261
|
+
db.update_task(task1_fresh)
|
|
262
|
+
puts " Task #{task1.id} closed"
|
|
263
|
+
|
|
264
|
+
# Check ready work again
|
|
265
|
+
ready = db.ready_tasks
|
|
266
|
+
puts "\n Tasks ready after completing task1:"
|
|
267
|
+
ready.each do |task|
|
|
268
|
+
puts " [#{task.id}] P#{task.priority} #{task.type}: #{task.title}"
|
|
269
|
+
end
|
|
270
|
+
puts
|
|
271
|
+
|
|
272
|
+
# =============================================================================
|
|
273
|
+
# 10. Child Tasks (for Epics)
|
|
274
|
+
# =============================================================================
|
|
275
|
+
|
|
276
|
+
puts "10. Creating Child Tasks..."
|
|
277
|
+
|
|
278
|
+
child1 = db.create_child_task(epic.id, {
|
|
279
|
+
title: "Setup OAuth providers",
|
|
280
|
+
type: "task",
|
|
281
|
+
priority: 2
|
|
282
|
+
})
|
|
283
|
+
puts " Created child: [#{child1.id}] #{child1.title}"
|
|
284
|
+
|
|
285
|
+
child2 = db.create_child_task(epic.id, {
|
|
286
|
+
title: "Add 2FA support",
|
|
287
|
+
type: "feature",
|
|
288
|
+
priority: 3
|
|
289
|
+
})
|
|
290
|
+
puts " Created child: [#{child2.id}] #{child2.title}"
|
|
291
|
+
|
|
292
|
+
children = db.child_tasks(epic.id)
|
|
293
|
+
puts " Epic #{epic.id} has #{children.size} children\n\n"
|
|
294
|
+
|
|
295
|
+
# =============================================================================
|
|
296
|
+
# 11. Plans and Workflows
|
|
297
|
+
# =============================================================================
|
|
298
|
+
|
|
299
|
+
puts "11. Plans and Workflows..."
|
|
300
|
+
|
|
301
|
+
# Create a Plan (workflow blueprint)
|
|
302
|
+
plan = TrakFlow::Models::Task.new(
|
|
303
|
+
title: "Deploy to Production",
|
|
304
|
+
plan: true
|
|
305
|
+
)
|
|
306
|
+
plan = db.create_task(plan)
|
|
307
|
+
puts " Created plan: [#{plan.id}] #{plan.title}"
|
|
308
|
+
|
|
309
|
+
# Add steps to the plan
|
|
310
|
+
step1 = db.create_child_task(plan.id, { title: "Run tests" })
|
|
311
|
+
step2 = db.create_child_task(plan.id, { title: "Build artifacts" })
|
|
312
|
+
step3 = db.create_child_task(plan.id, { title: "Deploy to staging" })
|
|
313
|
+
puts " Added #{db.find_plan_tasks(plan.id).size} steps to plan"
|
|
314
|
+
|
|
315
|
+
# List all plans
|
|
316
|
+
plans = db.find_plans
|
|
317
|
+
puts " Total plans: #{plans.size}"
|
|
318
|
+
|
|
319
|
+
# Create an ephemeral task (temporary, garbage collectible)
|
|
320
|
+
ephemeral = TrakFlow::Models::Task.new(
|
|
321
|
+
title: "Quick note: Check session timeout setting",
|
|
322
|
+
ephemeral: true
|
|
323
|
+
)
|
|
324
|
+
ephemeral = db.create_task(ephemeral)
|
|
325
|
+
puts " Created ephemeral task: [#{ephemeral.id}] #{ephemeral.title}"
|
|
326
|
+
|
|
327
|
+
ephemeral_tasks = db.find_ephemeral_workflows
|
|
328
|
+
puts " Total ephemeral tasks: #{ephemeral_tasks.size}"
|
|
329
|
+
|
|
330
|
+
# Garbage collection would remove old ephemeral tasks
|
|
331
|
+
# gc_count = db.garbage_collect_ephemeral(max_age_hours: 24)
|
|
332
|
+
puts
|
|
333
|
+
|
|
334
|
+
# =============================================================================
|
|
335
|
+
# Summary
|
|
336
|
+
# =============================================================================
|
|
337
|
+
|
|
338
|
+
puts <<~SUMMARY
|
|
339
|
+
============================================================
|
|
340
|
+
Demo Complete!
|
|
341
|
+
============================================================
|
|
342
|
+
|
|
343
|
+
What we demonstrated:
|
|
344
|
+
- Initialized TrakFlow database
|
|
345
|
+
- Configured settings via TrakFlow.configure
|
|
346
|
+
- Created tasks of various types (epic, task, feature, bug)
|
|
347
|
+
- Set up blocking and related dependencies
|
|
348
|
+
- Added labels and state labels (dimension:value)
|
|
349
|
+
- Added comments to tasks
|
|
350
|
+
- Queried tasks with filters
|
|
351
|
+
- Detected ready work vs blocked tasks
|
|
352
|
+
- Managed task lifecycle (open -> in_progress -> closed)
|
|
353
|
+
- Created child tasks under an epic
|
|
354
|
+
- Created Plans (workflow blueprints) with steps
|
|
355
|
+
- Created ephemeral tasks (temporary, garbage collectible)
|
|
356
|
+
|
|
357
|
+
Database location: #{db_path}
|
|
358
|
+
Dirty (needs flush): #{db.dirty?}
|
|
359
|
+
|
|
360
|
+
SUMMARY
|
|
361
|
+
|
|
362
|
+
# Cleanup
|
|
363
|
+
db.close
|
|
364
|
+
FileUtils.rm_rf(demo_dir)
|
|
365
|
+
puts "Demo directory cleaned up."
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# TrakFlow CLI Demo
|
|
4
|
+
#
|
|
5
|
+
# This script demonstrates how to use the `tf` command-line interface.
|
|
6
|
+
# Run from the examples directory: ./cli_demo.sh
|
|
7
|
+
#
|
|
8
|
+
# Options:
|
|
9
|
+
# -n, --no-pause Run without pausing between sections (for testing)
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
set -e
|
|
13
|
+
|
|
14
|
+
# Parse options
|
|
15
|
+
NO_PAUSE=false
|
|
16
|
+
while [[ $# -gt 0 ]]; do
|
|
17
|
+
case "$1" in
|
|
18
|
+
-n|--no-pause)
|
|
19
|
+
NO_PAUSE=true
|
|
20
|
+
shift
|
|
21
|
+
;;
|
|
22
|
+
*)
|
|
23
|
+
shift
|
|
24
|
+
;;
|
|
25
|
+
esac
|
|
26
|
+
done
|
|
27
|
+
|
|
28
|
+
# Determine project root and set up environment
|
|
29
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
30
|
+
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
|
|
31
|
+
export PATH="$PROJECT_DIR/bin:$PATH"
|
|
32
|
+
export BUNDLE_GEMFILE="$PROJECT_DIR/Gemfile"
|
|
33
|
+
|
|
34
|
+
# Colors for output
|
|
35
|
+
RED='\033[0;31m'
|
|
36
|
+
GREEN='\033[0;32m'
|
|
37
|
+
BLUE='\033[0;34m'
|
|
38
|
+
YELLOW='\033[1;33m'
|
|
39
|
+
NC='\033[0m' # No Color
|
|
40
|
+
|
|
41
|
+
header() {
|
|
42
|
+
echo ""
|
|
43
|
+
echo -e "${BLUE}============================================================${NC}"
|
|
44
|
+
echo -e "${BLUE}$1${NC}"
|
|
45
|
+
echo -e "${BLUE}============================================================${NC}"
|
|
46
|
+
echo ""
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
run_cmd() {
|
|
50
|
+
echo -e "${YELLOW}\$ $1${NC}"
|
|
51
|
+
eval "$1"
|
|
52
|
+
echo ""
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
pause() {
|
|
56
|
+
if [ "$NO_PAUSE" = false ]; then
|
|
57
|
+
echo -e "${GREEN}Press Enter to continue...${NC}"
|
|
58
|
+
read -r
|
|
59
|
+
fi
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Create temp directory for demo
|
|
63
|
+
DEMO_DIR=$(mktemp -d -t trak_flow_demo)
|
|
64
|
+
cd "$DEMO_DIR"
|
|
65
|
+
|
|
66
|
+
cleanup() {
|
|
67
|
+
echo ""
|
|
68
|
+
echo -e "${RED}Cleaning up demo directory...${NC}"
|
|
69
|
+
rm -rf "$DEMO_DIR"
|
|
70
|
+
echo "Done."
|
|
71
|
+
}
|
|
72
|
+
trap cleanup EXIT
|
|
73
|
+
|
|
74
|
+
header "TrakFlow CLI Demo"
|
|
75
|
+
echo "Demo directory: $DEMO_DIR"
|
|
76
|
+
echo ""
|
|
77
|
+
|
|
78
|
+
# =============================================================================
|
|
79
|
+
header "1. Initialize TrakFlow"
|
|
80
|
+
# =============================================================================
|
|
81
|
+
|
|
82
|
+
run_cmd "tf init"
|
|
83
|
+
run_cmd "tf info"
|
|
84
|
+
|
|
85
|
+
pause
|
|
86
|
+
|
|
87
|
+
# =============================================================================
|
|
88
|
+
header "2. Create Tasks"
|
|
89
|
+
# =============================================================================
|
|
90
|
+
|
|
91
|
+
echo "Creating various task types..."
|
|
92
|
+
echo ""
|
|
93
|
+
|
|
94
|
+
run_cmd "tf create 'Build user authentication' -t epic -p 1"
|
|
95
|
+
run_cmd "tf create 'Design database schema' -t task -p 1 -d 'Create tables for users and sessions'"
|
|
96
|
+
run_cmd "tf create 'Implement login endpoint' -t feature -p 2"
|
|
97
|
+
run_cmd "tf create 'Write integration tests' -t task -p 3"
|
|
98
|
+
run_cmd "tf create 'Fix password validation bug' -t bug -p 0"
|
|
99
|
+
|
|
100
|
+
pause
|
|
101
|
+
|
|
102
|
+
# =============================================================================
|
|
103
|
+
header "3. List and Show Tasks"
|
|
104
|
+
# =============================================================================
|
|
105
|
+
|
|
106
|
+
run_cmd "tf list"
|
|
107
|
+
run_cmd "tf list --status open"
|
|
108
|
+
run_cmd "tf list --priority 0"
|
|
109
|
+
|
|
110
|
+
echo "Showing task details (using first task ID from list)..."
|
|
111
|
+
TASK_ID=$(tf list -j 2>/dev/null | ruby -roj -e 'puts Oj.load(STDIN.read).first["id"]')
|
|
112
|
+
run_cmd "tf show $TASK_ID"
|
|
113
|
+
|
|
114
|
+
pause
|
|
115
|
+
|
|
116
|
+
# =============================================================================
|
|
117
|
+
header "4. Dependencies"
|
|
118
|
+
# =============================================================================
|
|
119
|
+
|
|
120
|
+
echo "Setting up blocking dependencies..."
|
|
121
|
+
echo ""
|
|
122
|
+
|
|
123
|
+
# Get task IDs
|
|
124
|
+
SCHEMA_ID=$(tf list -j 2>/dev/null | ruby -roj -e 'tasks = Oj.load(STDIN.read); puts tasks.find{|t| t["title"].include?("schema")}["id"]')
|
|
125
|
+
LOGIN_ID=$(tf list -j 2>/dev/null | ruby -roj -e 'tasks = Oj.load(STDIN.read); puts tasks.find{|t| t["title"].include?("login")}["id"]')
|
|
126
|
+
TEST_ID=$(tf list -j 2>/dev/null | ruby -roj -e 'tasks = Oj.load(STDIN.read); puts tasks.find{|t| t["title"].include?("tests")}["id"]')
|
|
127
|
+
|
|
128
|
+
run_cmd "tf dep add $SCHEMA_ID $LOGIN_ID -t blocks"
|
|
129
|
+
run_cmd "tf dep add $LOGIN_ID $TEST_ID -t blocks"
|
|
130
|
+
|
|
131
|
+
echo "Viewing dependency tree..."
|
|
132
|
+
run_cmd "tf dep tree $TEST_ID"
|
|
133
|
+
|
|
134
|
+
pause
|
|
135
|
+
|
|
136
|
+
# =============================================================================
|
|
137
|
+
header "5. Ready Work Detection"
|
|
138
|
+
# =============================================================================
|
|
139
|
+
|
|
140
|
+
echo "Finding tasks that are ready to work on (not blocked)..."
|
|
141
|
+
echo ""
|
|
142
|
+
|
|
143
|
+
run_cmd "tf ready"
|
|
144
|
+
|
|
145
|
+
pause
|
|
146
|
+
|
|
147
|
+
# =============================================================================
|
|
148
|
+
header "6. Labels"
|
|
149
|
+
# =============================================================================
|
|
150
|
+
|
|
151
|
+
BUG_ID=$(tf list -j 2>/dev/null | ruby -roj -e 'tasks = Oj.load(STDIN.read); puts tasks.find{|t| t["type"] == "bug"}["id"]')
|
|
152
|
+
|
|
153
|
+
run_cmd "tf label add $BUG_ID critical"
|
|
154
|
+
run_cmd "tf label add $BUG_ID security"
|
|
155
|
+
run_cmd "tf label list $BUG_ID"
|
|
156
|
+
run_cmd "tf label list-all"
|
|
157
|
+
|
|
158
|
+
pause
|
|
159
|
+
|
|
160
|
+
# =============================================================================
|
|
161
|
+
header "7. Task Lifecycle"
|
|
162
|
+
# =============================================================================
|
|
163
|
+
|
|
164
|
+
echo "Updating task status..."
|
|
165
|
+
echo ""
|
|
166
|
+
|
|
167
|
+
run_cmd "tf update $BUG_ID --status in_progress"
|
|
168
|
+
run_cmd "tf show $BUG_ID"
|
|
169
|
+
|
|
170
|
+
echo "Closing the task..."
|
|
171
|
+
run_cmd "tf close $BUG_ID -r 'Fixed regex to allow special characters'"
|
|
172
|
+
run_cmd "tf show $BUG_ID"
|
|
173
|
+
|
|
174
|
+
echo "Reopening if needed..."
|
|
175
|
+
run_cmd "tf reopen $BUG_ID -r 'Found another edge case'"
|
|
176
|
+
run_cmd "tf show $BUG_ID"
|
|
177
|
+
|
|
178
|
+
pause
|
|
179
|
+
|
|
180
|
+
# =============================================================================
|
|
181
|
+
header "8. Child Tasks (Epics)"
|
|
182
|
+
# =============================================================================
|
|
183
|
+
|
|
184
|
+
EPIC_ID=$(tf list -j 2>/dev/null | ruby -roj -e 'tasks = Oj.load(STDIN.read); puts tasks.find{|t| t["type"] == "epic"}["id"]')
|
|
185
|
+
|
|
186
|
+
echo "Creating child tasks under the epic..."
|
|
187
|
+
echo ""
|
|
188
|
+
|
|
189
|
+
run_cmd "tf create 'Setup OAuth providers' --parent $EPIC_ID -p 2"
|
|
190
|
+
run_cmd "tf create 'Add 2FA support' --parent $EPIC_ID -p 3"
|
|
191
|
+
|
|
192
|
+
run_cmd "tf show $EPIC_ID"
|
|
193
|
+
|
|
194
|
+
pause
|
|
195
|
+
|
|
196
|
+
# =============================================================================
|
|
197
|
+
header "9. Plans and Workflows"
|
|
198
|
+
# =============================================================================
|
|
199
|
+
|
|
200
|
+
echo "Creating a Plan (workflow blueprint)..."
|
|
201
|
+
echo ""
|
|
202
|
+
|
|
203
|
+
run_cmd "tf plan create 'Deploy to Production' -d 'Standard deployment workflow'"
|
|
204
|
+
|
|
205
|
+
PLAN_ID=$(tf plan list -j 2>/dev/null | ruby -roj -e 'puts Oj.load(STDIN.read).first["id"]')
|
|
206
|
+
|
|
207
|
+
echo "Adding steps to the Plan..."
|
|
208
|
+
run_cmd "tf plan add $PLAN_ID 'Run test suite' -p 1"
|
|
209
|
+
run_cmd "tf plan add $PLAN_ID 'Build artifacts' -p 2"
|
|
210
|
+
run_cmd "tf plan add $PLAN_ID 'Deploy to staging' -p 2"
|
|
211
|
+
run_cmd "tf plan add $PLAN_ID 'Run smoke tests' -p 2"
|
|
212
|
+
run_cmd "tf plan add $PLAN_ID 'Deploy to production' -p 1"
|
|
213
|
+
|
|
214
|
+
run_cmd "tf plan show $PLAN_ID"
|
|
215
|
+
run_cmd "tf plan list"
|
|
216
|
+
|
|
217
|
+
pause
|
|
218
|
+
|
|
219
|
+
# =============================================================================
|
|
220
|
+
header "10. Starting Workflows from Plans"
|
|
221
|
+
# =============================================================================
|
|
222
|
+
|
|
223
|
+
echo "Starting a persistent Workflow from the Plan..."
|
|
224
|
+
run_cmd "tf plan start $PLAN_ID"
|
|
225
|
+
|
|
226
|
+
run_cmd "tf workflow list"
|
|
227
|
+
|
|
228
|
+
WORKFLOW_ID=$(tf workflow list -j 2>/dev/null | ruby -roj -e 'puts Oj.load(STDIN.read).first["id"]')
|
|
229
|
+
run_cmd "tf workflow show $WORKFLOW_ID"
|
|
230
|
+
|
|
231
|
+
pause
|
|
232
|
+
|
|
233
|
+
# =============================================================================
|
|
234
|
+
header "11. Ephemeral Workflows"
|
|
235
|
+
# =============================================================================
|
|
236
|
+
|
|
237
|
+
echo "Creating an ephemeral (one-shot) Workflow..."
|
|
238
|
+
run_cmd "tf plan execute $PLAN_ID"
|
|
239
|
+
|
|
240
|
+
run_cmd "tf workflow list"
|
|
241
|
+
run_cmd "tf workflow list -e"
|
|
242
|
+
|
|
243
|
+
EPHEMERAL_ID=$(tf workflow list -e -j 2>/dev/null | ruby -roj -e 'puts Oj.load(STDIN.read).first["id"]')
|
|
244
|
+
|
|
245
|
+
echo "Discarding the ephemeral Workflow..."
|
|
246
|
+
run_cmd "tf workflow discard $EPHEMERAL_ID"
|
|
247
|
+
|
|
248
|
+
run_cmd "tf workflow list"
|
|
249
|
+
|
|
250
|
+
pause
|
|
251
|
+
|
|
252
|
+
# =============================================================================
|
|
253
|
+
header "12. Admin Commands"
|
|
254
|
+
# =============================================================================
|
|
255
|
+
|
|
256
|
+
echo "Analyzing the database..."
|
|
257
|
+
run_cmd "tf admin compact --analyze"
|
|
258
|
+
|
|
259
|
+
echo "Analyzing the dependency graph..."
|
|
260
|
+
run_cmd "tf admin analyze"
|
|
261
|
+
|
|
262
|
+
pause
|
|
263
|
+
|
|
264
|
+
# =============================================================================
|
|
265
|
+
header "13. JSON Output"
|
|
266
|
+
# =============================================================================
|
|
267
|
+
|
|
268
|
+
echo "All commands support -j for machine-readable output..."
|
|
269
|
+
echo ""
|
|
270
|
+
|
|
271
|
+
run_cmd "tf list -j | head -20"
|
|
272
|
+
|
|
273
|
+
pause
|
|
274
|
+
|
|
275
|
+
# =============================================================================
|
|
276
|
+
header "14. Syncing with JSONL"
|
|
277
|
+
# =============================================================================
|
|
278
|
+
|
|
279
|
+
echo "TrakFlow stores data in both SQLite and JSONL formats."
|
|
280
|
+
echo "The JSONL file is Git-friendly for version control."
|
|
281
|
+
echo ""
|
|
282
|
+
|
|
283
|
+
run_cmd "ls -la .trak_flow/"
|
|
284
|
+
run_cmd "head -5 .trak_flow/issues.jsonl"
|
|
285
|
+
|
|
286
|
+
pause
|
|
287
|
+
|
|
288
|
+
# =============================================================================
|
|
289
|
+
header "Demo Complete!"
|
|
290
|
+
# =============================================================================
|
|
291
|
+
|
|
292
|
+
cat << 'EOF'
|
|
293
|
+
What we demonstrated:
|
|
294
|
+
- tf init Initialize TrakFlow
|
|
295
|
+
- tf create Create tasks (bug, feature, task, epic, chore)
|
|
296
|
+
- tf list List tasks with filters
|
|
297
|
+
- tf show Show task details
|
|
298
|
+
- tf update Update task attributes
|
|
299
|
+
- tf close Close a task
|
|
300
|
+
- tf reopen Reopen a closed task
|
|
301
|
+
- tf ready Find tasks ready to work on
|
|
302
|
+
- tf dep Manage dependencies
|
|
303
|
+
- tf label Manage labels
|
|
304
|
+
- tf plan Create and manage workflow blueprints
|
|
305
|
+
- tf workflow Manage running workflows
|
|
306
|
+
- tf admin Administrative commands
|
|
307
|
+
|
|
308
|
+
For more help:
|
|
309
|
+
tf --help
|
|
310
|
+
tf <command> --help
|
|
311
|
+
EOF
|
|
312
|
+
|
|
313
|
+
echo ""
|
|
314
|
+
echo "Demo directory will be cleaned up on exit."
|