lux-hammer 0.3.24 → 0.3.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/.version +1 -1
  3. data/recipes/llm.rb +0 -287
  4. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 68c2cd8d90138ab3eaf6a68a23aeabc273b31d26122224ba3966d235a9841657
4
- data.tar.gz: ae13bf1ca96c396b51a1db3801ce4aa4179ad67f5bb8e4b04467715b9744164e
3
+ metadata.gz: 273ffb060fd16cb52423002e5e8e041481a37b0487395d966a8f9ba90cfa53fa
4
+ data.tar.gz: 9dc14e358bfafc0050379d671711ca28d6d6eb3aa1935f7440c93ea897267431
5
5
  SHA512:
6
- metadata.gz: 222754053a8ca203a8f0344fd0007174e1a333f92e69935fd908567e7e6a8a1d1c8675d5bd8a443a905fd9140ba54ed4afc2fdbd7abe817c8cc90d3643e97fa1
7
- data.tar.gz: 345ebe94f0d5eb3383fb4a0df98a1cc95d611fa330d2174c2ed6b3b25de724ee0acc51100682cd93cce813c446298cd0e19d004998e3fe95e8f5b01434368faa
6
+ metadata.gz: 8c9e26f85ded5283505130548bcd1b64718d1a405013149d086881e166c59239cb1a4db3fbefcb2a990ed2a06b82d90ae54769e6650be6d1d6032a9fe48328d5
7
+ data.tar.gz: 700f84954436b3e606d2b5234ff682f19c27d651083037e0faaa0f9ca796b1df604704d6908d58f7fa6a133dc9fa314df71084a805c6219b1d46d963557fe22b
data/.version CHANGED
@@ -1 +1 @@
1
- 0.3.24
1
+ 0.3.25
data/recipes/llm.rb CHANGED
@@ -9,7 +9,6 @@ desc <<~TXT
9
9
  memory persistent memory store (backs the Claude Code memory plugin)
10
10
  plan apply a /plan bundle - sha1 checked, drift aware
11
11
  prompt token-prefix prompt expander (UserPromptSubmit hook + CLI)
12
- todo per-project task queue for agents (./LLM_TODO.local.md)
13
12
 
14
13
  Commands:
15
14
  usage subscription limits and per-model token usage
@@ -333,292 +332,6 @@ namespace :memory do
333
332
  end
334
333
  end
335
334
 
336
- TODO_GUIDE ||= <<~TXT
337
- Per-project task queue in ./LLM_TODO.local.md - workable by humans and any agent.
338
-
339
- Human usage:
340
- llm todo:add "fix login redirect" queue a task; pipe stdin for bulk:
341
- `* ` bullets delimit tasks (multi-line ok),
342
- no bullets at all = one task per line
343
- llm todo:list tasks by id, counts, format warnings
344
- llm todo:pop + llm todo:done take / finish a single task by hand
345
-
346
- Edit the file freely: only the `# todo`, `# doing` and `# done` sections are
347
- managed. `# plan`, `# idea` and any prose stay untouched. A task starts with
348
- `*` at the line start and runs until the next `*` or header; when finished it
349
- moves to `# done` verbatim.
350
-
351
- LLM usage - one of these lines is the whole prompt:
352
- "Run `llm todo go` and follow its output." work through every task
353
- "Run `llm todo pop`, do the task, run `llm todo done`." do a single task
354
-
355
- Task text is printed raw on stdout. `todo go` repeats the protocol after each
356
- task and says when the queue is empty; `todo pop` re-prints the task in
357
- progress, so an interrupted agent resumes instead of skipping ahead.
358
- TXT
359
-
360
- task :todo do
361
- desc <<~D
362
- How the todo queue works, for humans and LLMs.
363
-
364
- #{TODO_GUIDE}
365
- D
366
- example 'todo'
367
-
368
- proc do
369
- say TODO_GUIDE
370
- say ''
371
- self.class.print_help 'todo:'
372
- end
373
- end
374
-
375
- namespace :todo do
376
- # Same story as :memory - helpers must live inside the namespace block.
377
- private
378
-
379
- TODO_FILE ||= 'LLM_TODO.local.md'
380
- TODO_SECTIONS ||= { todo: 'todo', doing: 'doing', done: 'done' }.freeze
381
-
382
- def todo_path
383
- File.join(Dir.pwd, TODO_FILE)
384
- end
385
-
386
- # Exact title match on purpose - "# done ideas" must stay an unmanaged section.
387
- def todo_section_key(title)
388
- case title.strip.downcase
389
- when 'todo', 'pending' then :todo
390
- when 'doing', 'in progress', 'in-progress' then :doing
391
- when 'done', 'completed' then :done
392
- end
393
- end
394
-
395
- # The tool manages only the `# todo`, `# doing` and `# done` sections (any
396
- # heading level). Inside them a task starts at a `*` or `-` bullet in the
397
- # first column and runs until the next bullet or header, so tasks can span
398
- # multiple lines and move between sections verbatim. Every other section
399
- # (`# plan`, `# idea`, ...) and any preamble is kept as-is and never touched.
400
- # Returns [tasks, nodes]; nodes reproduce the document layout for todo_save.
401
- def todo_parse
402
- tasks = { todo: [], doing: [], done: [] }
403
- nodes = []
404
- return [tasks, nodes] unless File.file?(todo_path)
405
-
406
- section = nil # managed section key, nil while inside a raw chunk
407
- current = nil # lines of the task being collected
408
-
409
- flush_task = lambda do
410
- tasks[section] << current.join("\n").rstrip if current
411
- current = nil
412
- end
413
-
414
- File.foreach(todo_path) do |raw|
415
- line = raw.chomp
416
- if (m = line.match(/^#+\s*(\S.*?)\s*$/))
417
- flush_task.call
418
- section = todo_section_key(m[1])
419
- if section
420
- nodes << [:section, section] unless nodes.include?([:section, section])
421
- else
422
- nodes << [:raw, [line]]
423
- end
424
- elsif section
425
- if line =~ /^[-*]\s+(.*)$/
426
- flush_task.call
427
- current = [$1.strip]
428
- elsif current
429
- current << line.rstrip
430
- end
431
- else
432
- nodes << [:raw, []] unless nodes.last && nodes.last[0] == :raw
433
- nodes.last[1] << line.rstrip
434
- end
435
- end
436
- flush_task.call
437
- [tasks, nodes]
438
- end
439
-
440
- # Rewrite: managed sections in canonical form, raw chunks verbatim, original
441
- # order kept. Managed sections missing from the file are appended at the end.
442
- def todo_save(tasks, nodes)
443
- order = nodes + (TODO_SECTIONS.keys - nodes.map { |t, v| v if t == :section }).map { |k| [:section, k] }
444
-
445
- File.open(todo_path, 'w') do |io|
446
- order.each_with_index do |(type, value), i|
447
- io.puts unless i.zero?
448
- if type == :section
449
- io.puts "# #{TODO_SECTIONS[value]}"
450
- tasks[value].each do |text|
451
- io.puts
452
- io.puts "* #{text}"
453
- end
454
- else
455
- lines = value.dup
456
- lines.pop while lines.any? && lines.last.empty?
457
- lines.each { |l| io.puts l }
458
- end
459
- end
460
- end
461
- end
462
-
463
- # One-line label for confirmations - multi-line tasks show their first line.
464
- def todo_label(text)
465
- first, rest = text.split("\n", 2)
466
- rest ? "#{first} ..." : first
467
- end
468
-
469
- # Format sanity check for hand-edited files. Returns warning strings.
470
- def todo_lint(tasks)
471
- warns = []
472
- seen = Hash.new(0)
473
- section = :preamble
474
- File.foreach(todo_path) do |raw|
475
- line = raw.chomp
476
- if (m = line.match(/^#+\s*(\S.*?)\s*$/))
477
- key = todo_section_key(m[1])
478
- section = key || :other
479
- seen[key] += 1 if key
480
- elsif section == :preamble && line =~ /^[-*]\s/
481
- warns << "ignored bullet before any section header: #{line}"
482
- end
483
- end
484
- TODO_SECTIONS.each_key do |key|
485
- warns << "duplicate '# #{key}' sections - their tasks are merged" if seen[key] > 1
486
- warns << "missing '# #{key}' section - will be added on next write" if seen[key].zero?
487
- end
488
- warns << "#{tasks[:doing].length} tasks in doing - expected at most 1" if tasks[:doing].length > 1
489
- warns
490
- end
491
-
492
- task :add do
493
- desc <<~DESC
494
- Add task(s) to ./#{TODO_FILE} (created on first add).
495
-
496
- Text comes from the argument (one task), or from stdin. Piped input
497
- that has `*` / `-` bullets is a list: a bullet begins a task and the
498
- lines under it belong to it, so multi-line tasks and whole lists can be
499
- piped in at once. Input with no bullets at all is one task per line.
500
- DESC
501
- example 'todo add "migrate the user model to Sequel"'
502
- example 'cat tasks.md | llm todo add'
503
- opt :text, desc: 'task text (or pipe tasks on stdin)'
504
-
505
- proc do |opts|
506
- # unquoted multi-word input lands in :text + :args - join it back
507
- arg = [opts[:text], *opts[:args]].compact.join(' ').strip
508
- if arg.empty?
509
- lines = opts[:stdin].to_s.split("\n")
510
- bulleted = lines.any? { |l| l =~ /^[-*]\s+/ }
511
- new_tasks = []
512
- lines.each do |line|
513
- if line =~ /^[-*]\s+(.*)$/
514
- new_tasks << [$1.strip]
515
- elsif bulleted
516
- new_tasks.last << line.rstrip if new_tasks.any?
517
- elsif line.strip != ''
518
- new_tasks << [line.rstrip]
519
- end
520
- end
521
- new_tasks = new_tasks.map { |ls| ls.join("\n").rstrip }.reject(&:empty?)
522
- else
523
- new_tasks = [arg]
524
- end
525
- error 'usage: llm todo add <text> (or pipe tasks on stdin)' if new_tasks.empty?
526
-
527
- tasks, nodes = todo_parse
528
- tasks[:todo].concat new_tasks
529
- todo_save tasks, nodes
530
- new_tasks.each { |t| say "added: #{todo_label(t)}", :green }
531
- end
532
- end
533
-
534
- task :pop do
535
- desc <<~DESC
536
- Print the current task and stop there - one task, no loop.
537
-
538
- Moves the first todo task to doing if nothing is doing yet. Prints only
539
- the raw task text (agent-friendly stdout). Re-running without `todo done`
540
- prints the same task again, so an interrupted agent resumes instead of
541
- starting the next one. Exits 1 when the list is empty.
542
- DESC
543
- example 'todo pop'
544
-
545
- proc do
546
- tasks, nodes = todo_parse
547
- if tasks[:doing].empty?
548
- error 'todo list is empty' if tasks[:todo].empty?
549
- tasks[:doing] << tasks[:todo].shift
550
- todo_save tasks, nodes
551
- end
552
- puts tasks[:doing].first
553
- end
554
- end
555
-
556
- task :go do
557
- desc <<~DESC
558
- Agent entry point: work through the whole todo list.
559
-
560
- Prints the current task (pop) followed by the loop protocol, so telling
561
- any agent "run llm todo go" is enough to drain the queue. When the list
562
- is empty it says so and tells the agent to stop and summarize.
563
- DESC
564
- example 'todo go'
565
-
566
- proc do
567
- tasks, nodes = todo_parse
568
- if tasks[:doing].empty? && tasks[:todo].empty?
569
- say 'All tasks done - stop the loop and summarize what was done.'
570
- next
571
- end
572
- if tasks[:doing].empty?
573
- tasks[:doing] << tasks[:todo].shift
574
- todo_save tasks, nodes
575
- end
576
- say "TASK: #{tasks[:doing].first}"
577
- say ''
578
- say 'Do this task fully and verify it. Then run `llm todo done`, and `llm todo go` for the next one.'
579
- end
580
- end
581
-
582
- task :done do
583
- desc 'Mark the in-progress task as done'
584
- example 'todo done'
585
-
586
- proc do
587
- tasks, nodes = todo_parse
588
- error 'no task in progress (run: llm todo pop)' if tasks[:doing].empty?
589
- text = tasks[:doing].shift
590
- tasks[:done] << text
591
- todo_save tasks, nodes
592
- say "done: #{todo_label(text)}", :green
593
- end
594
- end
595
-
596
- task :list do
597
- alt :inspect
598
- desc 'Inspect the todo file: tasks by id, counts, format validity warnings'
599
- example 'todo list'
600
-
601
- proc do
602
- error "no #{TODO_FILE} in #{Dir.pwd} (run: llm todo add <text>)" unless File.file?(todo_path)
603
- tasks, = todo_parse
604
- id = 0
605
- TODO_SECTIONS.each do |key, title|
606
- say "#{title}:", :cyan
607
- color = { todo: nil, doing: :yellow, done: :gray }[key]
608
- tasks[key].each do |text|
609
- first, *rest = text.split("\n")
610
- say "#{(id += 1).to_s.rjust(3)}. #{first}", color
611
- rest.each { |l| say " #{l}", color }
612
- end
613
- say ' (none)', :gray if tasks[key].empty?
614
- end
615
- say ''
616
- say "#{tasks[:todo].length} todo, #{tasks[:doing].length} doing, #{tasks[:done].length} done"
617
- todo_lint(tasks).each { |w| say "warning: #{w}", :yellow }
618
- end
619
- end
620
- end
621
-
622
335
  namespace :prompt do
623
336
  TOKEN_PATTERN ||= /[a-z0-9_-]+/.freeze
624
337
  TOKEN_LINE_RE ||= /\A(?:\s*:[a-z0-9_-]+)+\s*\z/.freeze
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lux-hammer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.24
4
+ version: 0.3.25
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dino Reic