intent 0.5.5 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +10 -28
  5. data/bin/{review → intent} +1 -2
  6. data/bin/inventory +12 -0
  7. data/bin/project_doc +30 -0
  8. data/bin/projects +1 -1
  9. data/bin/projects_active +123 -0
  10. data/bin/todo +1 -1
  11. data/bin/todo_review +123 -0
  12. data/intent.gemspec +15 -7
  13. data/lib/gem_ext/todo-txt.rb +26 -2
  14. data/lib/intent/commands/base.rb +40 -0
  15. data/lib/intent/commands/errors.rb +7 -0
  16. data/lib/intent/commands/intent.rb +9 -0
  17. data/lib/intent/commands/inventory.rb +174 -0
  18. data/lib/intent/commands/projects.rb +34 -0
  19. data/lib/intent/commands/todo.rb +51 -0
  20. data/lib/intent/commands.rb +9 -0
  21. data/lib/intent/core.rb +184 -0
  22. data/lib/intent/desktop.rb +3 -0
  23. data/lib/intent/projects.rb +1 -1
  24. data/lib/intent/review.rb +10 -1
  25. data/lib/intent/text/inventory.help.txt +6 -0
  26. data/lib/intent/text/project.help.txt +7 -0
  27. data/lib/intent/text/projects.help.txt +9 -0
  28. data/lib/intent/text/todo.help.txt +15 -0
  29. data/lib/intent/todo.rb +3 -1
  30. data/lib/intent/ui/ttyui.rb +90 -0
  31. data/lib/intent/verbs/add.rb +21 -0
  32. data/lib/intent/verbs/cite.rb +14 -0
  33. data/lib/intent/verbs/install.rb +20 -0
  34. data/lib/intent/verbs/review.rb +131 -0
  35. data/lib/intent/{todo/manager.rb → verbs/todo.rb} +3 -16
  36. data/lib/intent/version.rb +1 -1
  37. data/lib/intent.rb +20 -3
  38. metadata +179 -32
  39. data/.travis.yml +0 -4
  40. data/CODE_OF_CONDUCT.md +0 -13
  41. data/lib/intent/projects/manager.rb +0 -20
  42. data/lib/intent/review/manager.rb +0 -17
  43. /data/lib/intent/{projects → verbs}/status.rb +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 63f5433d3a5bff8c95271194b5bc5df740274ae9d42f405f16a1bf11a5f11cc3
4
- data.tar.gz: a7fc46a07d03ce70c2339d3cc535038a040fb60847ad224997e57316642e1ec8
3
+ metadata.gz: a80e9d9082642b1f2325b40629ff95ffb096cdba644d0f2d5aa2af25541c2d2b
4
+ data.tar.gz: 90ed06f5afbf84636fbeb70e6fab66a4868dc0d485b751178b2ff31568315237
5
5
  SHA512:
6
- metadata.gz: a314b0917a17bce8e386964fcdfaa4c791f2ed36168428fd9412ddace6c4e847efc1a831519c8d095ee1721bd4e60e4c2a5265a895ef202d2cbdc8d8ec507247
7
- data.tar.gz: 82607a04b4d1d3f8a91f78a3dc1cba8e2c0dd4e82348fb1dc98b1270e76d32de543d9977a88643495c019529b2fed317cd6e68481009bd9376fbeef1e956390a
6
+ metadata.gz: a4a23b8d4882af953e2d330c26388c91bb86513c1881192952998f6f74b5f49b682f4b609566ea18b0b6f0ad7517020ac15ccc22ce8756edc001ac5fc25c455b
7
+ data.tar.gz: cfc14324613fc505a6ec0a591e73f667dd3360b4ec1d8ee7f05d86813fcbaf5e645f12b917579adc6b046223dbc251f665f65b0cd70087dac39d59cc7adfdccf
data/.gitignore CHANGED
@@ -7,4 +7,6 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ *.gem
10
11
  NOTES.md
12
+ .DS_Store
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2016 Mark Rickerby
3
+ Copyright (c) 2016-2024 Mark Rickerby <https://maetl.net>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,41 +1,23 @@
1
1
  # Intent
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/intent`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ Automation engine with todo.txt
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ This tool is mired deep in the obscurity and pedantry of the text-based computing culture that originated in the 1970s and still continues today. You probably don’t want to use this.
6
6
 
7
7
  ## Installation
8
8
 
9
- Add this line to your application's Gemfile:
10
-
11
- ```ruby
12
- gem 'intent'
13
9
  ```
14
-
15
- And then execute:
16
-
17
- $ bundle
18
-
19
- Or install it yourself as:
20
-
21
- $ gem install intent
10
+ gem install intent
11
+ ```
22
12
 
23
13
  ## Usage
24
14
 
25
- TODO: Write usage instructions here
26
-
27
- ## Development
28
-
29
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
-
31
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
-
33
- ## Contributing
34
-
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/intent. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
36
-
15
+ ```
16
+ intent help
17
+ ```
37
18
 
38
- ## License
19
+ ## Copyright & License
39
20
 
40
- The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
21
+ Copyright 2024 Mark Rickerby <https://maetl.net>.
41
22
 
23
+ All documentation and modelling concepts are **CC BY-NC**. Source code and command line tools are MIT.
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
-
3
2
  path = __FILE__
4
3
  while File.symlink?(path)
5
4
  path = File.expand_path(File.readlink(path), File.dirname(path))
@@ -8,4 +7,4 @@ $:.unshift(File.join(File.dirname(File.expand_path(path)), '..', 'lib'))
8
7
 
9
8
  require 'intent'
10
9
 
11
- Intent::Review::Manager.run(ARGV.dup)
10
+ Intent::Dispatcher.exec_command(:intent, ARGV.dup)
data/bin/inventory ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ path = __FILE__
3
+
4
+ while File.symlink?(path)
5
+ path = File.expand_path(File.readlink(path), File.dirname(path))
6
+ end
7
+
8
+ $:.unshift(File.join(File.dirname(File.expand_path(path)), '..', 'lib'))
9
+
10
+ require 'intent'
11
+
12
+ Intent::Dispatcher.exec_command(:inventory, ARGV.dup)
data/bin/project_doc ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+ require "tty-markdown"
3
+ require "tty-pager"
4
+
5
+ PROJECTS_PATH = File.dirname(ENV['TODO_TXT'])
6
+
7
+ class ProjectDocCommand
8
+ def self.print_help(output)
9
+ output.puts "Pass in a project name to read its main document."
10
+ output.puts "Future iterations could display a list of available documents."
11
+ # Command idea: Add milestone to document
12
+ end
13
+
14
+ def self.run(args, output=STDOUT)
15
+ if args.empty?
16
+ print_help(output)
17
+ else
18
+ project_name = args.first.delete("^0–9a-z\-")
19
+ project_path = "#{PROJECTS_PATH}/#{project_name}"
20
+
21
+ if Dir.exists?(project_path)
22
+ pager = TTY::Pager.new
23
+ intent = File.read("#{project_path}/#{project_name}.md")
24
+ pager.page(TTY::Markdown.parse(intent))
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ ProjectDocCommand.run(ARGV.dup)
data/bin/projects CHANGED
@@ -8,4 +8,4 @@ $:.unshift(File.join(File.dirname(File.expand_path(path)), '..', 'lib'))
8
8
 
9
9
  require 'intent'
10
10
 
11
- Intent::Projects::Manager.run(ARGV.dup)
11
+ Intent::Dispatcher.exec_command(:projects, ARGV.dup)
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env ruby
2
+ require "tty-box"
3
+ require "tty-screen"
4
+ require "tty-reader"
5
+ require "tty-cursor"
6
+ require "tty-pager"
7
+ require "pastel"
8
+ require "todo-txt"
9
+
10
+ list = Todo::List.new(ENV['TODO_TXT'])
11
+
12
+ box = TTY::Box.frame(
13
+ width: TTY::Screen.width,
14
+ height: TTY::Screen.height-1,
15
+ align: :center,
16
+ border: :thick,
17
+ style: {
18
+ fg: :bright_yellow,
19
+ border: {
20
+ fg: :white,
21
+ }
22
+ }
23
+ )
24
+
25
+ focus_index = 0
26
+
27
+ def truncate(str, width)
28
+ return str unless str.length >= width
29
+
30
+ "#{str.slice(0, width-3)}..."
31
+ end
32
+
33
+ def draw_card(top, item, focus)
34
+
35
+ style = unless top == focus
36
+ { fg: :white, border: { fg: :white } }
37
+ else
38
+ { fg: :bright_white, border: { fg: :bright_magenta, bg: :black } }
39
+ end
40
+
41
+ title = if top == focus
42
+ {bottom_right: "[x: close][enter: launch]"}
43
+ else
44
+ {}
45
+ end
46
+
47
+ #pastel = Pastel.new
48
+ width = 90
49
+
50
+ TTY::Box.frame(width: width, height: 4, left: 4, title: title, border: :thick, style: style) do
51
+ "\s#{truncate(item[:title], 88)}"
52
+ end
53
+ end
54
+
55
+ projects = [
56
+ { title: "maetl", index: File.read(File.expand_path("~/Documents/Projects/maetl/maetl.md")) },
57
+ { title: "calyx", index: File.read(File.expand_path("~/Documents/Projects/calyx/calyx.md")) },
58
+ { title: "yarrow", index: File.read(File.expand_path("~/Documents/Projects/yarrow/yarrow.md")) }
59
+ ]
60
+
61
+ items_per_page = (TTY::Screen.height-1) / 4
62
+
63
+ # items = list.by_not_done.by_context("@reading").slice(0, items_per_page).map do |item|
64
+ # { title: item.text, href: item.text }
65
+ # end
66
+
67
+ $cursor = TTY::Cursor
68
+ #print $cursor.hide
69
+
70
+ def draw_list(items, focus)
71
+ buffer = []
72
+ items.each_with_index do |item, i|
73
+ buffer << draw_card(i, item, focus)
74
+ end
75
+ result = buffer.join("")
76
+
77
+ print $cursor.clear_screen + $cursor.move_to(0,0)
78
+ print result
79
+ end
80
+
81
+ draw_list(projects, focus_index)
82
+
83
+ reader = TTY::Reader.new(interrupt: :exit)
84
+
85
+ reader.on(:keyctrl_x, :keyescape) do
86
+ print $cursor.clear_screen + $cursor.move_to(0,0)
87
+ print $cursor.show
88
+ exit
89
+ end
90
+
91
+ reader.on(:keytab, :keydown) do
92
+ if focus_index == projects.count - 1
93
+ focus_index = 0
94
+ else
95
+ focus_index += 1
96
+ end
97
+ draw_list(projects, focus_index)
98
+ end
99
+
100
+ reader.on(:keyup) do
101
+ if focus_index == 0
102
+ focus_index = projects.count - 1
103
+ else
104
+ focus_index -= 1
105
+ end
106
+ draw_list(projects, focus_index)
107
+ end
108
+
109
+ reader.on(:keypress) do |key|
110
+ # if key.value == "x"
111
+ # items.delete_at(focus_index)
112
+ # focus_index -= 1 if focus_index >= items.count
113
+ # draw_list(items, focus_index)
114
+ # end
115
+ end
116
+
117
+ reader.on(:keyenter) do
118
+ `chrome-cli open #{items[focus_index][:href]}`
119
+ end
120
+
121
+ loop do
122
+ reader.read_line(echo: false)
123
+ end
data/bin/todo CHANGED
@@ -8,4 +8,4 @@ $:.unshift(File.join(File.dirname(File.expand_path(path)), '..', 'lib'))
8
8
 
9
9
  require 'intent'
10
10
 
11
- Intent::Todo::Manager.run(ARGV.dup)
11
+ Intent::Dispatcher.exec_command(:todo, ARGV.dup)
data/bin/todo_review ADDED
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env ruby
2
+ require "tty-box"
3
+ require "tty-screen"
4
+ require "tty-reader"
5
+ require "tty-cursor"
6
+ require "tty-pager"
7
+ require "pastel"
8
+ require "todo-txt"
9
+
10
+ list = Todo::List.new(ENV['TODO_TXT'])
11
+
12
+ box = TTY::Box.frame(
13
+ width: TTY::Screen.width,
14
+ height: TTY::Screen.height-1,
15
+ align: :center,
16
+ border: :thick,
17
+ style: {
18
+ fg: :bright_yellow,
19
+ border: {
20
+ fg: :white,
21
+ }
22
+ }
23
+ )
24
+
25
+ focus_index = 0
26
+
27
+ def truncate(str, width)
28
+ return str unless str.length >= width
29
+
30
+ "#{str.slice(0, width-3)}..."
31
+ end
32
+
33
+ def draw_card(top, item, focus)
34
+
35
+ style = unless top == focus
36
+ { fg: :white, border: { fg: :white } }
37
+ else
38
+ { fg: :bright_white, border: { fg: :bright_magenta, bg: :black } }
39
+ end
40
+
41
+ title = if top == focus
42
+ {bottom_right: "[x: close][enter: launch]"}
43
+ else
44
+ {}
45
+ end
46
+
47
+ #pastel = Pastel.new
48
+ width = 90
49
+
50
+ TTY::Box.frame(width: width, height: 4, left: 4, title: title, border: :thick, style: style) do
51
+ "\s#{truncate(item[:title], 88)}\n\s#{truncate(item[:href], 88)}"
52
+ end
53
+ end
54
+
55
+ items = [
56
+ { title: "maetl website", href: "https://maetl.net/" },
57
+ { title: "tty-cursor on Github", href: "https://github.com/piotrmurach/tty-cursor" },
58
+ { title: "Tangram Design", href: "https://tangram.co.nz" },
59
+ ]
60
+
61
+ items_per_page = (TTY::Screen.height-1) / 4
62
+
63
+ items = list.by_not_done.by_context("@reading").slice(0, items_per_page).map do |item|
64
+ { title: item.text, href: item.text }
65
+ end
66
+
67
+ $cursor = TTY::Cursor
68
+ print $cursor.hide
69
+
70
+ def draw_list(items, focus)
71
+ buffer = []
72
+ items.each_with_index do |item, i|
73
+ buffer << draw_card(i, item, focus)
74
+ end
75
+ result = buffer.join("")
76
+
77
+ print $cursor.clear_screen + $cursor.move_to(0,0)
78
+ print result
79
+ end
80
+
81
+ draw_list(items, focus_index)
82
+
83
+ reader = TTY::Reader.new(interrupt: :exit)
84
+
85
+ reader.on(:keyctrl_x, :keyescape) do
86
+ print $cursor.clear_screen + $cursor.move_to(0,0)
87
+ print $cursor.show
88
+ exit
89
+ end
90
+
91
+ reader.on(:keytab, :keydown) do
92
+ if focus_index == items.count - 1
93
+ focus_index = 0
94
+ else
95
+ focus_index += 1
96
+ end
97
+ draw_list(items, focus_index)
98
+ end
99
+
100
+ reader.on(:keyup) do
101
+ if focus_index == 0
102
+ focus_index = items.count - 1
103
+ else
104
+ focus_index -= 1
105
+ end
106
+ draw_list(items, focus_index)
107
+ end
108
+
109
+ reader.on(:keypress) do |key|
110
+ if key.value == "x"
111
+ items.delete_at(focus_index)
112
+ focus_index -= 1 if focus_index >= items.count
113
+ draw_list(items, focus_index)
114
+ end
115
+ end
116
+
117
+ reader.on(:keyenter) do
118
+ `chrome-cli open #{items[focus_index][:href]}`
119
+ end
120
+
121
+ loop do
122
+ reader.read_line(echo: false)
123
+ end
data/intent.gemspec CHANGED
@@ -18,14 +18,22 @@ Gem::Specification.new do |spec|
18
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_runtime_dependency "todo-txt", "~> 0.11"
22
- spec.add_runtime_dependency "pastel", "~> 0.6"
23
- spec.add_runtime_dependency "git", "~> 1.3.0"
24
- spec.add_runtime_dependency "terminal-notifier", "~> 1.8.0"
21
+ spec.add_runtime_dependency "todo-txt", "~> 0.12"
22
+ spec.add_runtime_dependency "pastel", "~> 0.8"
23
+ spec.add_runtime_dependency "git", "~> 1.19.1"
24
+ spec.add_runtime_dependency "terminal-notifier", "~> 2.0"
25
25
  spec.add_runtime_dependency "ghost", "~> 1.0.0"
26
+ spec.add_runtime_dependency "unicode_plot", "0.0.5"
27
+ spec.add_runtime_dependency "kdl", "1.0.3"
28
+ spec.add_runtime_dependency "sorted_set", "1.0.3"
29
+ spec.add_runtime_dependency "strings-ansi", "0.2.0"
30
+ spec.add_runtime_dependency "bibtex-ruby", "6.1.0"
31
+ spec.add_runtime_dependency "tty-prompt", "~> 0.23.1"
32
+ spec.add_runtime_dependency "tty-table", "~> 0.12.0"
33
+ spec.add_runtime_dependency "tty-tree", "~> 0.4.0"
34
+ spec.add_runtime_dependency "nanoid", "~> 2.0.0"
26
35
 
27
-
28
- spec.add_development_dependency "bundler", "~> 1.10"
29
- spec.add_development_dependency "rake", "~> 10.0"
36
+ spec.add_development_dependency "bundler"
37
+ spec.add_development_dependency "rake"
30
38
  spec.add_development_dependency "rspec"
31
39
  end
@@ -10,13 +10,37 @@ class Todo::Task
10
10
  # Completed tasks are rendered with a strikethrough
11
11
  pastel.strikethrough(to_s)
12
12
  else
13
- # Open tasks delegate to the custom formatting function
14
- print_open_task(pastel)
13
+ [
14
+ pastel.red(print_priority),
15
+ pastel.yellow(created_on.to_s),
16
+ text,
17
+ pastel.bold.magenta(print_contexts),
18
+ pastel.bold.blue(print_projects),
19
+ pastel.bold.cyan(print_tags)
20
+ ].reject { |item| !item || item.nil? || item.empty? }.join(' ')
15
21
  end
16
22
  end
17
23
 
24
+ def highlight_as_project
25
+ return to_s unless STDOUT.tty?
26
+
27
+ pastel = Pastel.new
28
+ [
29
+ pastel.bold.cyan(print_projects),
30
+ pastel.red(print_project_context)
31
+ ].reject { |item| !item || item.nil? || item.empty? }.join(' ')
32
+ end
33
+
34
+ def text=(label)
35
+ @text = label
36
+ end
37
+
18
38
  private
19
39
 
40
+ def print_project_context
41
+ 'active' if contexts.include?('@active')
42
+ end
43
+
20
44
  def print_open_task(pastel)
21
45
  [
22
46
  pastel.red(print_priority),
@@ -0,0 +1,40 @@
1
+ module Intent
2
+ module Commands
3
+ class Base
4
+ attr_reader :identity
5
+ attr_reader :documents
6
+
7
+ def initialize
8
+ @identity = strip_classname
9
+ @documents = ::Intent::Core::Documents.new
10
+ end
11
+
12
+ def print_help(output)
13
+ output.puts(File.read(help_txt_path))
14
+ end
15
+
16
+ def generate_id
17
+ Nanoid.generate(size: 8, alphabet: ID_ALPHABET)
18
+ end
19
+
20
+ private
21
+
22
+ T_CLASS_PREFIX = 'Intent::Commands::'
23
+
24
+ ID_ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
25
+
26
+ def strip_classname
27
+ self.class.to_s.sub(T_CLASS_PREFIX, '').downcase
28
+ end
29
+
30
+ def help_txt_path
31
+ # TODO: does this work on Windows?
32
+ "#{__dir__}/../text/#{identity}.help.txt"
33
+ end
34
+
35
+ def create_noun(type, label, tags)
36
+ Intent::Core::Noun.new(type, label, tags)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,7 @@
1
+ module Intent
2
+ module Commands
3
+ module Errors
4
+ COMMAND_NOT_FOUND = "Command not found"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ module Intent
2
+ module Commands
3
+ class Intent
4
+ def run(args, output)
5
+ output.puts args
6
+ end
7
+ end
8
+ end
9
+ end