xtdo 0.2

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.
data/HISTORY ADDED
@@ -0,0 +1,7 @@
1
+ 0.2.0 - 28 November 2010
2
+ * Added ZSH completion
3
+ * Added XTDO_PATH environment variable for customizing storage location
4
+ * Added recurring tasks to list all
5
+
6
+ 0.1.0 - 27 November 2010
7
+ * Initial release
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010 Xavier Shay
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,58 @@
1
+ = xtdo
2
+
3
+ An ultra fast command line todo list manager, with some strong opinions about workflow.
4
+
5
+ == Usage
6
+
7
+ Every command is mapped to a single character. If you want to be fast, you need to get the key strokes down.
8
+
9
+ l = list
10
+ a = add (or all when used with list)
11
+ d = done
12
+ b = bump
13
+ r = recur
14
+ c = completion (to help enable shell tab completion)
15
+
16
+ Here is how it fits together:
17
+
18
+ gem install xtdo
19
+
20
+ alias t=xtdo # Recommended!
21
+
22
+ t a some task # Add to list
23
+ t l a # All tasks
24
+ t b 0 some task # Move task to TODAY
25
+ t a 0 another task # New task on today list, shortcut letter
26
+ t l # Today's tasks
27
+ t d another task # It is done!
28
+ t b 1w some task # Actually we will to it next week
29
+ t r a 1d,thu bin night # Bin night every Thursday
30
+ t r a 1m,4 pay rent # Pay rent on the 4th of the month
31
+ t r d pay rent # Bah who wants to pay rent
32
+
33
+ t l c # List all tasks in a format suitable for tab completion
34
+ t r c # List all recurring tasks for completion
35
+
36
+ There is a command line completion script for ZSH in bin/xtdo_completion.zsh that you should use, since it auto completes task names for you. I don't know the best way to install this yet.
37
+
38
+ By default tasks are stored in ~/.xtdo. Customize this by setting the XTDO_PATH environment variable. I store mine in Dropbox.
39
+
40
+ == Why?
41
+
42
+ I used to use a small subset of Things.app, and I really dug the workflow. I spend my life on the command-line though. Existing command line apps didn't quite match my workflow, or were too slow.
43
+
44
+ Xtdo is fast in the sense that you type very little to make it do things, but also in that it is extremely quick to respond to commands - you will never notice any delay.
45
+
46
+ == Compatibility
47
+
48
+ Requires ruby 1.9.2. Will not run on any variant of 1.8.
49
+
50
+ == Developing
51
+
52
+ Refactors accepted, as long as they don't turn everything into a First Class Object. I see no reason why this code should require more than one file. I will likely not accept any features that I will not use personally.
53
+
54
+ There are no runtime external dependencies. Let's keep it that way.
55
+
56
+ == Status
57
+
58
+ Almost, but not quite, useable. This file is the only documentation you'll get currently.
@@ -0,0 +1,10 @@
1
+ desc 'Default: run specs.'
2
+ task :default => :spec
3
+
4
+ # RSpec provided helper doesn't like me, for now just run it myself
5
+ desc "Run specs"
6
+ task :spec do
7
+ commands = []
8
+ commands << "bundle exec rspec spec/*_spec.rb"
9
+ exec commands.join(" && ")
10
+ end
data/TODO ADDED
@@ -0,0 +1,4 @@
1
+ - gemspec
2
+ - start using it
3
+ - help on the command line
4
+ - better UI feedback
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
4
+
5
+ require 'xtdo'
6
+ file = ENV['XTDO_PATH'] || "~/.xtdo"
7
+ result = Xtdo.run file, ARGV.join(" ")
8
+ puts result if result && result.is_a?(String) && result.length > 0
@@ -0,0 +1,26 @@
1
+ autoload -U compinit
2
+ compinit
3
+
4
+ _xtdo() {
5
+ # TODO: Complete a bare 'xtdo' with commands + description
6
+
7
+ if [[ $words[2] == b ]]; then
8
+ if (( CURRENT == 4 )); then
9
+ compadd `bin/xtdo l c`
10
+ fi
11
+ fi
12
+
13
+ if [[ $words[2] == d ]]; then
14
+ if (( CURRENT == 3 )); then
15
+ compadd `bin/xtdo l c`
16
+ fi
17
+ fi
18
+
19
+ if [[ $words[2] == r && $words[3] == d ]]; then
20
+ if (( CURRENT == 4 )); then
21
+ compadd `bin/xtdo r c`
22
+ fi
23
+ fi
24
+ }
25
+
26
+ compdef _xtdo xtdo
@@ -0,0 +1,203 @@
1
+ require 'date'
2
+ require 'yaml'
3
+
4
+ class Xtdo
5
+ def self.run(store, operation)
6
+ operation = operation.split(/\s+/)
7
+ verb = operation.shift
8
+
9
+ # tasks = {
10
+ # 'T1' => {:scheduled => Date.today}
11
+ # }
12
+ store = File.expand_path(store)
13
+ tasks = if File.exists?(store)
14
+ YAML.load(File.open(store))
15
+ else
16
+ {}
17
+ end
18
+ tasks[:tasks] ||= {}
19
+ tasks[:recurring] ||= {}
20
+
21
+ manager = Xtdo.new(tasks)
22
+
23
+ ret = case verb
24
+ when 'a' then # A is for add!
25
+ manager.add operation.join(' ')
26
+ when 'b' then # B is for bump!
27
+ manager.bump operation.join(' ')
28
+ when 'd' then # D is for done!
29
+ manager.done operation.join(' ')
30
+ when 'l' then # L is for list!
31
+ case operation[0]
32
+ when 'a' then
33
+ manager.list [:today, :next, :scheduled]
34
+ when 'c' then
35
+ manager.list [:today, :next, :scheduled], :format => :completion
36
+ else
37
+ manager.list [:today]
38
+ end
39
+ when 'r' then # R is for recur!
40
+ manager.recur operation.join(' ')
41
+ end
42
+
43
+ manager.save(store)
44
+ ret
45
+ end
46
+
47
+ attr_reader :tasks, :recurring
48
+
49
+ def initialize(tasks)
50
+ @tasks = tasks[:tasks]
51
+ @recurring = tasks[:recurring]
52
+ end
53
+
54
+ def parse_relative_time(number, period)
55
+ adj = {
56
+ 'd' => 1,
57
+ 'w' => 7,
58
+ 'm' => 30,
59
+ 'y' => 365
60
+ }[period] || 1
61
+ Date.today + adj * number
62
+ end
63
+
64
+ def add(task)
65
+ number, period, task = /^(?:(\d+)([dwmy])? )?(.*)/.match(task).captures
66
+ @tasks[make_key task] = {:name => task}
67
+ @tasks[make_key task][:scheduled] = parse_relative_time(number.to_i, period) if number
68
+ end
69
+
70
+ def bump(task)
71
+ number, period, task = /^(?:(\d+)([dwmy])? )?(.*)/.match(task).captures
72
+ if !number
73
+ "Invalid time"
74
+ elsif tasks[make_key task]
75
+ tasks[make_key task][:scheduled] = parse_relative_time(number.to_i, period)
76
+ else
77
+ "No such task"
78
+ end
79
+ end
80
+
81
+ def done(task)
82
+ if tasks.delete(make_key task)
83
+ "Task done"
84
+ else
85
+ "No such task"
86
+ end
87
+ end
88
+
89
+ def list(groups, opts = {})
90
+ # Check for recurring
91
+ recurring.each do |name, task|
92
+ if task[:next] <= Date.today
93
+ tasks[make_key name] = {:scheduled => Date.today, :name => task[:name] }
94
+ number, period, start, _ = self.class.extract_recur_tokens(task[:period] + ' ' + name)
95
+
96
+ task[:next] = self.class.calculate_starting_day(Date.today, number, period, start)
97
+ end
98
+ end
99
+
100
+ if opts[:format] == :completion
101
+ tasks.keys.join "\n"
102
+ else
103
+ # Print groups
104
+ task_selector = {
105
+ :today => lambda {|x| x && x <= Date.today },
106
+ :next => lambda {|x| !x },
107
+ :scheduled => lambda {|x| x && x > Date.today }
108
+ }
109
+
110
+ groups.map do |group|
111
+ t = tasks.select {|name, opts| task_selector[group][opts[:scheduled]] }
112
+ next if t.empty?
113
+ "===== #{group.to_s.upcase}\n" + t.map { |name, attrs|
114
+ attrs[:name]
115
+ }.join("\n")
116
+ end.join("\n")
117
+ end
118
+ end
119
+
120
+ def recur(task)
121
+ tokens = task.split(/\s+/)
122
+ verb = tokens.shift
123
+ task = tokens.join(' ')
124
+ case verb
125
+ when 'a' then
126
+ number, period, start, name = self.class.extract_recur_tokens(task)
127
+
128
+ period_string = "#{number}#{period}"
129
+ period_string += ",#{start}" if start
130
+ recurring[make_key name] = {
131
+ :name => name,
132
+ :next => Date.today + 1,
133
+ :period => period_string
134
+ }
135
+ when 'd' then
136
+ if recurring.delete make_key(task)
137
+ "Recurring task removed"
138
+ else
139
+ "No such recurring task"
140
+ end
141
+ when 'l' then
142
+ "===== RECURRING\n" + recurring.map do |name, task|
143
+ "%-6s%s" % [task[:period], task[:name]]
144
+ end.join("\n")
145
+ when 'c' then
146
+ recurring.keys.join "\n"
147
+ end
148
+ end
149
+
150
+ def make_key(name)
151
+ name.gsub /[^a-zA-Z0-9,.]/, '-'
152
+ end
153
+
154
+ def self.calculate_starting_day(date, number, period, start = nil)
155
+ days = %w(sun mon tue wed thu fri sat)
156
+ number = (number || 1).to_i
157
+
158
+ adjust = case period
159
+ when 'd' then 1
160
+ when 'w' then
161
+ if days.index(start)
162
+ (days.index(start) - date.wday) % 7 + (number - 1) * 7
163
+ end
164
+ when 'm' then
165
+ start = start.to_i
166
+ if start > 0
167
+ year = date.year
168
+ if start.to_i <= date.day
169
+ month = date.month + 1
170
+ if month > 12
171
+ month = 1
172
+ year += 1
173
+ end
174
+ else
175
+ month = date.month
176
+ end
177
+ Date.new(year, month, start.to_i) - date
178
+ end
179
+ end
180
+ if adjust
181
+ if adjust == 0
182
+ date + 7
183
+ else
184
+ date + adjust
185
+ end
186
+ end
187
+ end
188
+
189
+ def self.extract_recur_tokens(task)
190
+ /^(\d+)([dwmy])?(?:,(#{days.join('|')}|\d{1,2}))? (.*)/.match(task).captures
191
+ end
192
+ def self.days
193
+ days = %w(sun mon tue wed thu fri sat)
194
+ end
195
+
196
+ def save(file)
197
+ File.open(file, 'w') {|f| f.write({
198
+ :tasks => tasks,
199
+ :recurring => recurring,
200
+ :version => 1
201
+ }.to_yaml) }
202
+ end
203
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'add' do
4
+ scenario 'to next' do
5
+ t('a T1')
6
+ t('l a').should have_task('T1', :in => :next)
7
+ end
8
+
9
+ scenario 'to today' do
10
+ t('a 0 T1')
11
+ t('l a').should have_task('T1', :in => :today)
12
+ end
13
+
14
+ scenario 'to scheduled' do
15
+ t('a 1w T1')
16
+ t('l a').should have_task('T1', :in => :scheduled, :for => Date.today + 7)
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'bumping tasks' do
4
+ scenario 'moving around' do
5
+ t('a T1')
6
+ t('l').should_not have_task('T1', :in => :today)
7
+ t('b 0 T1')
8
+ t('l').should have_task('T1', :in => :today)
9
+ t('b 1 T1')
10
+ t('l a').should have_task('T1', :in => :scheduled, :for => Date.today + 1)
11
+ t('b 1w T1')
12
+ t('l a').should have_task('T1', :in => :scheduled, :for => Date.today + 7)
13
+ end
14
+
15
+ scenario 'invalid input' do
16
+ t('b 0 T1').should == 'No such task'
17
+ t('b T1').should == 'Invalid time'
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'completion' do
4
+ scenario 'normal' do
5
+ t('a 0 T1') # Today
6
+ t('a T2') # Next
7
+ t('a 1 T3') # Scheduled
8
+
9
+ t('l c').should have_completion_task('T1')
10
+ t('l c').should have_completion_task('T2')
11
+ t('l c').should have_completion_task('T3')
12
+ end
13
+
14
+ scenario 'recurring' do
15
+ t('r a 1d T1')
16
+ t('r c').should have_completion_task('T1')
17
+ end
18
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'done' do
4
+ scenario 'marking off tasks' do
5
+ t('a T1')
6
+ t('l a').should have_task('T1')
7
+ t('d T1').should == "Task done"
8
+ t('l').should_not have_task('T1')
9
+ end
10
+
11
+ scenario 'invalid input' do
12
+ t('d T1').should == "No such task"
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'list' do
4
+ scenario 'options' do
5
+ t('a 0 T1') # Today
6
+ t('a T2') # Next
7
+ t('a 1 T3') # Scheduled
8
+
9
+ # Today
10
+ t('l').should have_task('T1')
11
+ t('l').should_not have_task('T2')
12
+ t('l').should_not have_task('T3')
13
+
14
+ # All
15
+ t('l a').should have_task('T1')
16
+ t('l a').should have_task('T2')
17
+ t('l a').should have_task('T3')
18
+ end
19
+ end
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ feature 'recurring' do
4
+ describe 'extract_r_tokens' do
5
+ it { Xtdo.extract_recur_tokens('1d T1').should == ['1', 'd', nil, 'T1'] }
6
+ it { Xtdo.extract_recur_tokens('1w,thu T1').should == ['1', 'w', 'thu', 'T1'] }
7
+ it { Xtdo.extract_recur_tokens('1m,2 T1').should == ['1', 'm', '2', 'T1'] }
8
+ it { Xtdo.extract_recur_tokens('1m,20 T1').should == ['1', 'm', '20', 'T1'] }
9
+ end
10
+
11
+ describe 'starting day' do
12
+ let(:today) { Date.new(2010,11,22) } # Monday
13
+
14
+ it { Xtdo.calculate_starting_day(today, 1, 'd').should == today + 1 }
15
+ it { Xtdo.calculate_starting_day(today, 1, 'w', 'mon').should == today + 7}
16
+ it { Xtdo.calculate_starting_day(today, 1, 'w', 'tue').should == today + 1}
17
+ it { Xtdo.calculate_starting_day(today, 1, 'w', 'sun').should == today + 6}
18
+ it { Xtdo.calculate_starting_day(today, 1, 'w', '0').should == nil }
19
+ it { Xtdo.calculate_starting_day(today, 1, 'm', '1').should == Date.new(2010,12,1)}
20
+ it { Xtdo.calculate_starting_day(today, 1, 'm', '22').should == Date.new(2010,12,22)}
21
+ it { Xtdo.calculate_starting_day(today, 1, 'm', '23').should == Date.new(2010,11,23)}
22
+ it { Xtdo.calculate_starting_day(today, 1, 'm', '0').should == nil }
23
+ end
24
+
25
+ let(:today) { Date.new(2010,11,17) } # Monday
26
+
27
+ scenario 'list' do
28
+ t('r a 1d T1')
29
+ t('r l').should have_task('1d T1')
30
+ end
31
+
32
+ scenario 'daily' do
33
+
34
+ time_travel today
35
+ t('r a 1d T1')
36
+
37
+ time_travel today + 1
38
+ t('l').should have_task('T1')
39
+ t('b 1 T1')
40
+ t('l').should_not have_task('T1')
41
+ t('d T1')
42
+ t('l').should_not have_task('T1')
43
+
44
+ time_travel today + 2
45
+ t('l').should have_task('T1')
46
+ t('d T1')
47
+
48
+ time_travel today + 4
49
+ t('l').should have_task('T1')
50
+ end
51
+
52
+ scenario 'remove' do
53
+ time_travel today
54
+ t('r a 1d T1')
55
+ t('r d T1')
56
+ time_travel today + 1
57
+ t('l a').should_not have_task('T1')
58
+ end
59
+
60
+ scenario 'weekly' do
61
+ time_travel today
62
+ t('r a 1w,thu T1')
63
+
64
+ time_travel Date.new(2010,11,18) # Thursday
65
+ t('l').should have_task('T1')
66
+ t('b 1 T1')
67
+ t('l').should_not have_task('T1')
68
+ t('d T1')
69
+ t('l').should_not have_task('T1') # Do not recreate the task
70
+
71
+ time_travel Date.new(2010,11,25) # Next Thursday
72
+ t('l').should have_task('T1')
73
+ t('d T1')
74
+
75
+ time_travel Date.new(2010,12,3) # Friday after
76
+ t('l').should have_task('T1')
77
+ end
78
+
79
+ scenario 'monthly' do
80
+ time_travel today
81
+ t('r a 1m,3 T1')
82
+
83
+ time_travel Date.new(2010,12,3)
84
+ t('l').should have_task('T1')
85
+ t('b 1 T1')
86
+ t('l').should_not have_task('T1')
87
+ t('d T1')
88
+ t('l').should_not have_task('T1')
89
+
90
+ time_travel Date.new(2011,1,4)
91
+ t('l').should have_task('T1')
92
+ end
93
+ end
@@ -0,0 +1,112 @@
1
+ require 'rspec'
2
+ require 'tempfile'
3
+ require 'date'
4
+ require 'timecop'
5
+
6
+ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__)
7
+ require 'xtdo'
8
+
9
+ RSpec.configure do |config|
10
+ config.before :each do
11
+ @file = File.join(Dir.tmpdir, 'xtdo_test.yml')
12
+ end
13
+
14
+ config.after :each do
15
+ File.unlink @file if File.exists? @file
16
+ Timecop.return
17
+ end
18
+
19
+ def t(operation)
20
+ Xtdo.run @file, operation
21
+ end
22
+
23
+ def time_travel(time)
24
+ Timecop.travel(time)
25
+ end
26
+ end
27
+
28
+ RSpec::Matchers.define(:have_completion_task) do |task|
29
+ match do |result|
30
+ parse(result).include? task
31
+ end
32
+
33
+ failure_message_for_should do |result|
34
+ buffer = "Expected to find #{task}. Instead found:\n"
35
+ parse(result).each do |found|
36
+ buffer += " #{found}"
37
+ end
38
+ buffer
39
+ end
40
+
41
+ def parse(data)
42
+ data.to_s.lines.map(&:chomp)
43
+ end
44
+ end
45
+
46
+ RSpec::Matchers.define(:have_task) do |task, opts = {}|
47
+ match do |result|
48
+ parsed = parse(result)
49
+ tasks = opts[:in] ? parsed[opts[:in]] : parsed.values.flatten
50
+ tasks && tasks.include?(task)
51
+ end
52
+
53
+ failure_message_for_should do |result|
54
+ in_string = " in #{opts[:in]}" if opts[:in]
55
+ buffer = "Expected to find #{task}#{in_string}. Instead found:\n"
56
+ parse(result).each do |group, tasks|
57
+ buffer += " #{group}\n"
58
+ buffer += tasks.map {|x| " #{x}" }.join("\n")
59
+ buffer += "\n"
60
+ end
61
+ buffer
62
+ end
63
+
64
+ failure_message_for_should_not do |result|
65
+ in_string = " in #{opts[:in]}" if opts[:in]
66
+ buffer = "Expected not to find #{task}#{in_string}:\n"
67
+ parse(result).each do |group, tasks|
68
+ buffer += " #{group}\n"
69
+ buffer += tasks.map {|x| " #{x}" }.join("\n")
70
+ buffer += "\n"
71
+ end
72
+ buffer
73
+ end
74
+
75
+ def parse(data)
76
+ @parsed ||= begin
77
+ group = nil
78
+ data.to_s.lines.inject({}) do |a, line|
79
+ if line =~ /^=+ (.+)/
80
+ group = line[/^=+ (.+)/, 1].downcase.to_sym
81
+ else
82
+ a[group] ||= []
83
+ a[group] << line.chomp
84
+ end
85
+ a
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ module Steak
92
+ module AcceptanceExampleGroup
93
+ def self.included(base)
94
+ base.instance_eval do
95
+ alias scenario example
96
+ alias background before
97
+ end
98
+ end
99
+ end
100
+
101
+ module DSL
102
+ def feature(*args, &block)
103
+ args << {} unless args.last.is_a?(Hash)
104
+ args.last.update :type => :acceptance, :steak => true, :caller => caller
105
+ describe(*args, &block)
106
+ end
107
+ end
108
+ end
109
+
110
+ extend Steak::DSL
111
+
112
+ RSpec.configuration.include Steak::AcceptanceExampleGroup, :type => :acceptance
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xtdo
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ version: "0.2"
9
+ platform: ruby
10
+ authors:
11
+ - Xavier Shay
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+
16
+ date: 2010-11-28 00:00:00 +11:00
17
+ default_executable:
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: rspec
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 2
29
+ - 1
30
+ - 0
31
+ version: 2.1.0
32
+ type: :development
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: timecop
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ segments:
43
+ - 0
44
+ version: "0"
45
+ type: :development
46
+ version_requirements: *id002
47
+ description:
48
+ email:
49
+ - hello@xaviershay.com
50
+ executables:
51
+ - xtdo
52
+ extensions: []
53
+
54
+ extra_rdoc_files: []
55
+
56
+ files:
57
+ - spec/add_spec.rb
58
+ - spec/bump_spec.rb
59
+ - spec/completion_spec.rb
60
+ - spec/done_spec.rb
61
+ - spec/list_spec.rb
62
+ - spec/recurring_spec.rb
63
+ - spec/spec_helper.rb
64
+ - lib/xtdo.rb
65
+ - bin/xtdo
66
+ - bin/xtdo_completion.zsh
67
+ - README.rdoc
68
+ - HISTORY
69
+ - LICENSE
70
+ - Rakefile
71
+ - TODO
72
+ has_rdoc: false
73
+ homepage: http://github.com/xaviershay/xtdo
74
+ licenses: []
75
+
76
+ post_install_message:
77
+ rdoc_options: []
78
+
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ segments:
87
+ - 0
88
+ version: "0"
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ segments:
95
+ - 0
96
+ version: "0"
97
+ requirements: []
98
+
99
+ rubyforge_project:
100
+ rubygems_version: 1.3.7
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: Minimal and fast command line todo manager
104
+ test_files: []
105
+