intent 0.5.5 → 0.7.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.
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