ptimelog 0.10.0 → 0.12.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.
- checksums.yaml +4 -4
- data/AGENTS.md +42 -0
- data/PLAN-ADD-COMMAND.md +140 -0
- data/README.md +81 -13
- data/exe/ptimelog +26 -2
- data/lib/ptimelog/command/add.rb +30 -27
- data/lib/ptimelog/command/confirm.rb +82 -0
- data/lib/ptimelog/command/edit.rb +47 -7
- data/lib/ptimelog/command/info.rb +48 -0
- data/lib/ptimelog/command/show.rb +44 -27
- data/lib/ptimelog/command/upload.rb +49 -18
- data/lib/ptimelog/configuration.rb +13 -6
- data/lib/ptimelog/data_source.rb +43 -0
- data/lib/ptimelog/day.rb +7 -32
- data/lib/ptimelog/entry.rb +84 -22
- data/lib/ptimelog/error.rb +7 -0
- data/lib/ptimelog/joiner.rb +47 -0
- data/lib/ptimelog/named_date.rb +1 -1
- data/lib/ptimelog/operon.rb +170 -0
- data/lib/ptimelog/script.rb +1 -1
- data/lib/ptimelog/timelog.rb +56 -3
- data/lib/ptimelog/version.rb +9 -1
- data/lib/ptimelog.rb +14 -3
- data/opencode.json +37 -0
- metadata +136 -47
- data/.envrc +0 -1
- data/.gitignore +0 -10
- data/.overcommit.yml +0 -26
- data/.rspec +0 -2
- data/.rubocop.yml +0 -42
- data/.rubocop_todo.yml +0 -16
- data/.ruby-version +0 -1
- data/.tool-versions +0 -1
- data/.travis.yml +0 -7
- data/bin/console +0 -15
- data/bin/setup +0 -8
- data/lib/ptimelog/app.rb +0 -21
- data/lib/ptimelog/command/base.rb +0 -29
- data/lib/ptimelog/command/version.rb +0 -16
- data/ptimelog.gemspec +0 -40
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'date'
|
|
4
|
+
require 'time'
|
|
5
|
+
require 'yaml'
|
|
6
|
+
|
|
7
|
+
module Ptimelog
|
|
8
|
+
# Extract entries from the time-sessions Operon tracks on its tasks
|
|
9
|
+
class Operon # rubocop:disable Metrics/ClassLength
|
|
10
|
+
# Operon documents its own task-syntax as examples, and the archive is history
|
|
11
|
+
SKIP_PATHS = ['Operon/Docs', 'archive'].freeze
|
|
12
|
+
|
|
13
|
+
FIELD_RE = /\{\{(\w+)::\s*(.*?)\}\}/
|
|
14
|
+
TASK_RE = /^\s*-\s\[( |x|X)\]\s/
|
|
15
|
+
RANGE_RE = %r{
|
|
16
|
+
\A(?<start>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2})?)/
|
|
17
|
+
(?<stop>\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2})?)\z
|
|
18
|
+
}x
|
|
19
|
+
|
|
20
|
+
# A config naming only one key must not lose the other
|
|
21
|
+
# TODO: determine if this can be done in the Ptimelog::Configuration
|
|
22
|
+
def self.custom_keys
|
|
23
|
+
Ptimelog::Configuration::CONFIGURATION_DEFAULTS['operon_custom_keys']
|
|
24
|
+
.merge(Ptimelog::Configuration.instance['operon_custom_keys'].to_h)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def initialize(day)
|
|
28
|
+
@day = day
|
|
29
|
+
@configuration = Ptimelog::Configuration.instance
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# A missing vault would silently look like a day without any tracked time
|
|
33
|
+
class VaultNotFound < Ptimelog::Error
|
|
34
|
+
def initialize(path)
|
|
35
|
+
super(<<~MSG)
|
|
36
|
+
Could not find your Obsidian vault at #{path}
|
|
37
|
+
|
|
38
|
+
Please check 'obsidian_vault' in your ptimelog-config. It should
|
|
39
|
+
point to the folder which contains your '.obsidian'-directory.
|
|
40
|
+
MSG
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# A session belongs to the day it started on. Plans and running timers are no time yet
|
|
45
|
+
def entries
|
|
46
|
+
tasks.flat_map { |task| entries_of(task) }
|
|
47
|
+
.sort_by(&:start_time)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# What a new entry would continue from: a running timer, the last session, the start of the day
|
|
51
|
+
def previous_entry
|
|
52
|
+
start = running_timer || entries.last&.finish_time || @configuration['operon_start_time'] || '08:00'
|
|
53
|
+
|
|
54
|
+
Ptimelog::Entry.new.tap do |entry|
|
|
55
|
+
entry.date = @day
|
|
56
|
+
entry.description = 'start **'
|
|
57
|
+
entry.start_time = start
|
|
58
|
+
entry.finish_time = start
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# An entry can come from any note of the vault, so there is no one file to point at
|
|
63
|
+
def file = NullPathname.new
|
|
64
|
+
|
|
65
|
+
private
|
|
66
|
+
|
|
67
|
+
def vault
|
|
68
|
+
@vault ||= begin
|
|
69
|
+
path = @configuration['obsidian_vault']
|
|
70
|
+
raise VaultNotFound, path unless File.directory?(path)
|
|
71
|
+
|
|
72
|
+
path
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def markdown_files
|
|
77
|
+
Dir.glob(vault.join('**/*.md')).reject { |fn| ignored?(fn) }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def ignored?(fn)
|
|
81
|
+
relative = fn.delete_prefix("#{vault}/")
|
|
82
|
+
|
|
83
|
+
SKIP_PATHS.any? { |path| relative.start_with?("#{path}/") }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Every task of the vault, an inline one on its line, a file task in its frontmatter
|
|
87
|
+
def tasks
|
|
88
|
+
markdown_files.flat_map do |fn|
|
|
89
|
+
frontmatter, body = split_frontmatter(File.read(fn, encoding: 'UTF-8'))
|
|
90
|
+
|
|
91
|
+
file_tasks(frontmatter, fn) + inline_tasks(body)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def file_tasks(frontmatter, fn)
|
|
96
|
+
return [] unless frontmatter.is_a?(Hash)
|
|
97
|
+
|
|
98
|
+
[{ fields: frontmatter, description: File.basename(fn, '.md') }]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def inline_tasks(body)
|
|
102
|
+
body.each_line.filter_map do |line|
|
|
103
|
+
next unless line.match?(TASK_RE)
|
|
104
|
+
|
|
105
|
+
{ fields: fields_of(line), description: description_of(line) }
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def fields_of(line) = line.scan(FIELD_RE).to_h.transform_values(&:strip)
|
|
110
|
+
|
|
111
|
+
def description_of(line)
|
|
112
|
+
line.sub(TASK_RE, '').sub(/\s*\{\{.*\}\}\s*\z/m, '').strip
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# A file task keeps its fields in frontmatter, an inline task carries them on its line
|
|
116
|
+
def split_frontmatter(text)
|
|
117
|
+
return [nil, text] unless text.start_with?('---')
|
|
118
|
+
|
|
119
|
+
close = text.index("\n---", 3)
|
|
120
|
+
return [nil, text] unless close
|
|
121
|
+
|
|
122
|
+
[YAML.safe_load(text[4...close], permitted_classes: [Date, Time], aliases: true), text[(close + 4)..]]
|
|
123
|
+
rescue Psych::Exception
|
|
124
|
+
[nil, text]
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def entries_of(task)
|
|
128
|
+
session_list(task[:fields]['trackers']).filter_map do |session|
|
|
129
|
+
next unless on_day?(session[:start])
|
|
130
|
+
|
|
131
|
+
entry_for(session, task)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Malformed ranges are dropped, one broken note must not sink a whole day
|
|
136
|
+
def session_list(sessions)
|
|
137
|
+
list = sessions.is_a?(String) ? sessions.split('; ') : Array(sessions)
|
|
138
|
+
|
|
139
|
+
list.filter_map { |session| RANGE_RE.match(session.to_s.strip) }
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# A running timer is no session yet, but it says where the work stopped
|
|
143
|
+
def running_timer
|
|
144
|
+
started = tasks.filter_map { |task| task[:fields]['activeTracker'] }.find { |at| on_day?(at) }
|
|
145
|
+
|
|
146
|
+
started && Time.parse(started).strftime('%R')
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def on_day?(datetime)
|
|
150
|
+
Time.parse(datetime).strftime('%F') == @day
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def entry_for(session, task) # rubocop:disable Metrics/AbcSize
|
|
154
|
+
fields = task[:fields]
|
|
155
|
+
|
|
156
|
+
Ptimelog::Entry.new.tap do |entry|
|
|
157
|
+
entry.date = @day
|
|
158
|
+
entry.ticket = fields[custom_key('ticket')]
|
|
159
|
+
entry.description = task[:description]
|
|
160
|
+
entry.tags = fields[custom_key('inferer')]
|
|
161
|
+
entry.start_time = Time.parse(session[:start]).strftime('%R')
|
|
162
|
+
entry.finish_time = Time.parse(session[:stop]).strftime('%R')
|
|
163
|
+
|
|
164
|
+
entry.infer_ptime_settings
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def custom_key(name) = self.class.custom_keys.fetch(name)
|
|
169
|
+
end
|
|
170
|
+
end
|
data/lib/ptimelog/script.rb
CHANGED
|
@@ -10,7 +10,7 @@ module Ptimelog
|
|
|
10
10
|
|
|
11
11
|
def inferer(name)
|
|
12
12
|
return NullPathname.new if name.to_s.empty?
|
|
13
|
-
raise if
|
|
13
|
+
raise if %r{[\\/]}.match?(name) # prevent relavtive paths, stupidly, FIXME: really check FS
|
|
14
14
|
|
|
15
15
|
@config_dir.join('inferers').join(name).expand_path
|
|
16
16
|
end
|
data/lib/ptimelog/timelog.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'singleton'
|
|
3
4
|
require 'pathname'
|
|
4
5
|
|
|
5
6
|
module Ptimelog
|
|
@@ -13,12 +14,16 @@ module Ptimelog
|
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
def timelog_txt
|
|
16
|
-
Pathname.new(Configuration.instance[:
|
|
17
|
+
Pathname.new(Configuration.instance[:timelog_txt]).expand_path
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def previous_entry
|
|
20
|
-
lines = timelog_txt.readlines.last(
|
|
21
|
-
|
|
21
|
+
lines = timelog_txt.readlines.last(10)
|
|
22
|
+
|
|
23
|
+
last_line = lines.map(&:chomp).delete_if do |line|
|
|
24
|
+
line.empty? || line.end_with?('**')
|
|
25
|
+
end.last
|
|
26
|
+
|
|
22
27
|
last_entry = instance.tokenize(last_line)
|
|
23
28
|
|
|
24
29
|
Entry.from_timelog(last_entry)
|
|
@@ -41,6 +46,15 @@ module Ptimelog
|
|
|
41
46
|
timelog_txt.read
|
|
42
47
|
end
|
|
43
48
|
|
|
49
|
+
# TODO: spec and activate this code
|
|
50
|
+
# def add(_entry)
|
|
51
|
+
# @new_lines = []
|
|
52
|
+
# add_empty_line if @timelog.previous_entry.date == yesterday
|
|
53
|
+
# add_entry(*parse_task(task))
|
|
54
|
+
#
|
|
55
|
+
# save_file
|
|
56
|
+
# end
|
|
57
|
+
|
|
44
58
|
def parse(data)
|
|
45
59
|
data.split("\n")
|
|
46
60
|
.map { |line| tokenize(line) }
|
|
@@ -58,5 +72,44 @@ module Ptimelog
|
|
|
58
72
|
regexp = /^#{re_date} #{re_time}: #{re_tick}?#{re_desc}#{re_tags}?$/
|
|
59
73
|
line.match(regexp)
|
|
60
74
|
end
|
|
75
|
+
|
|
76
|
+
# TODO: spec and maybe activate this code. this might be covered by the "add"-command already.
|
|
77
|
+
# def parse_task(line)
|
|
78
|
+
# matches = line.match('(?<time>\d{1,2}:\d{2} )?(?<offset>[+-]\d+ )?(?<task>.*)')
|
|
79
|
+
# formatted_time = if matches[:time]
|
|
80
|
+
# Time.parse(matches[:time])
|
|
81
|
+
# else
|
|
82
|
+
# Time.now
|
|
83
|
+
# end
|
|
84
|
+
# .localtime
|
|
85
|
+
# .then { |time| time + (matches[:offset].to_i * 60) }
|
|
86
|
+
# .strftime('%F %R')
|
|
87
|
+
#
|
|
88
|
+
# [formatted_time, matches[:task]]
|
|
89
|
+
# end
|
|
90
|
+
|
|
91
|
+
# TODO: spec and activate this code.
|
|
92
|
+
# def add_entry(date_time, task)
|
|
93
|
+
# @new_lines << "#{date_time}: #{task}"
|
|
94
|
+
# end
|
|
95
|
+
|
|
96
|
+
# TODO: spec and activate this code.
|
|
97
|
+
# def add_empty_line
|
|
98
|
+
# @new_lines << ''
|
|
99
|
+
# end
|
|
100
|
+
|
|
101
|
+
# TODO: spec and activate this code.
|
|
102
|
+
# def save_file
|
|
103
|
+
# @timelog.timelog_txt.open('a') do |log|
|
|
104
|
+
# @new_lines.each do |line|
|
|
105
|
+
# log << "#{line}\n"
|
|
106
|
+
# end
|
|
107
|
+
# end
|
|
108
|
+
# end
|
|
109
|
+
|
|
110
|
+
# TODO: spec and activate this code.
|
|
111
|
+
# def yesterday
|
|
112
|
+
# NamedDate.new.named_date('yesterday')
|
|
113
|
+
# end
|
|
61
114
|
end
|
|
62
115
|
end
|
data/lib/ptimelog/version.rb
CHANGED
|
@@ -15,6 +15,14 @@
|
|
|
15
15
|
# any remaining questions, please open an issue or even better a pull-request
|
|
16
16
|
# with an improvement. Keep in mind that this is also covered by rspec so I
|
|
17
17
|
# expect (pun intended) 100% test-coverage for any additional code.
|
|
18
|
+
#
|
|
19
|
+
# Without much preparation came the addition of cmdparse to this gem. While
|
|
20
|
+
# adding it, it became apparent that a duplication between the gemspec and the
|
|
21
|
+
# main executable may exist. In order to avoid this dreadfully duplicated
|
|
22
|
+
# description, a constant for this has been added to this module. So, the
|
|
23
|
+
# correct subtitle of this documentation-block would be "and a Banner not named
|
|
24
|
+
# Bruce."
|
|
18
25
|
module Ptimelog
|
|
19
|
-
VERSION = '0.
|
|
26
|
+
VERSION = '0.12.0'
|
|
27
|
+
BANNER = 'Move time-entries from gTimelog or Obsidian to PuzzleTime'
|
|
20
28
|
end
|
data/lib/ptimelog.rb
CHANGED
|
@@ -6,20 +6,31 @@ $LOAD_PATH.unshift File.dirname(__FILE__)
|
|
|
6
6
|
module Ptimelog
|
|
7
7
|
autoload :App, 'ptimelog/app'
|
|
8
8
|
autoload :Configuration, 'ptimelog/configuration'
|
|
9
|
-
autoload :
|
|
9
|
+
autoload :DataSource, 'ptimelog/data_source'
|
|
10
10
|
autoload :DeprecationWarning, 'ptimelog/deprecation_warning'
|
|
11
11
|
autoload :Entry, 'ptimelog/entry'
|
|
12
|
+
autoload :Error, 'ptimelog/error'
|
|
13
|
+
autoload :Joiner, 'ptimelog/joiner'
|
|
12
14
|
autoload :NamedDate, 'ptimelog/named_date'
|
|
13
15
|
autoload :NullPathname, 'ptimelog/null_pathname'
|
|
14
16
|
autoload :Script, 'ptimelog/script'
|
|
15
|
-
autoload :Timelog, 'ptimelog/timelog'
|
|
16
17
|
autoload :VERSION, 'ptimelog/version'
|
|
17
18
|
|
|
19
|
+
# datasources
|
|
20
|
+
|
|
21
|
+
# obsidian
|
|
22
|
+
autoload :Operon, 'ptimelog/operon'
|
|
23
|
+
|
|
24
|
+
# timelog.txt
|
|
25
|
+
autoload :Day, 'ptimelog/day'
|
|
26
|
+
autoload :Timelog, 'ptimelog/timelog'
|
|
27
|
+
|
|
18
28
|
# Collection of commands available at the CLI
|
|
19
29
|
module Command
|
|
20
30
|
autoload :Add, 'ptimelog/command/add'
|
|
21
|
-
autoload :
|
|
31
|
+
autoload :Confirm, 'ptimelog/command/confirm'
|
|
22
32
|
autoload :Edit, 'ptimelog/command/edit'
|
|
33
|
+
autoload :Info, 'ptimelog/command/info'
|
|
23
34
|
autoload :Show, 'ptimelog/command/show'
|
|
24
35
|
autoload :Upload, 'ptimelog/command/upload'
|
|
25
36
|
autoload :Version, 'ptimelog/command/version'
|
data/opencode.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://opencode.ai/config.json",
|
|
3
|
+
"agent": {
|
|
4
|
+
"researcher": {
|
|
5
|
+
"permission": {
|
|
6
|
+
"edit": "deny",
|
|
7
|
+
"skill": "deny",
|
|
8
|
+
"question": "allow",
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"assistant": {
|
|
12
|
+
"permission": {
|
|
13
|
+
"bash": {
|
|
14
|
+
"*": "ask",
|
|
15
|
+
"rspec*": "allow",
|
|
16
|
+
"rubocop*": "allow",
|
|
17
|
+
"rake*": "allow",
|
|
18
|
+
"git status*": "allow",
|
|
19
|
+
"git diff*": "allow",
|
|
20
|
+
"git log*": "allow",
|
|
21
|
+
"git show*": "allow",
|
|
22
|
+
"git add --intent-to-add*": "allow",
|
|
23
|
+
"git add*": "ask",
|
|
24
|
+
"git commit*": "ask",
|
|
25
|
+
"git push*": "deny",
|
|
26
|
+
"ls*": "allow",
|
|
27
|
+
"tail *": "allow",
|
|
28
|
+
"head *": "allow",
|
|
29
|
+
"grep*": "allow",
|
|
30
|
+
"cat*": "allow",
|
|
31
|
+
"pwd": "allow"
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
metadata
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ptimelog
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.12.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Matthias Viehweger
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: cmdparse
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
13
26
|
- !ruby/object:Gem::Dependency
|
|
14
27
|
name: naught
|
|
15
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -24,6 +37,20 @@ dependencies:
|
|
|
24
37
|
- - ">="
|
|
25
38
|
- !ruby/object:Gem::Version
|
|
26
39
|
version: '0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rake
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
27
54
|
- !ruby/object:Gem::Dependency
|
|
28
55
|
name: bundler
|
|
29
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -42,87 +69,156 @@ dependencies:
|
|
|
42
69
|
name: overcommit
|
|
43
70
|
requirement: !ruby/object:Gem::Requirement
|
|
44
71
|
requirements:
|
|
45
|
-
- - "
|
|
72
|
+
- - ">="
|
|
46
73
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '0
|
|
74
|
+
version: '0'
|
|
48
75
|
type: :development
|
|
49
76
|
prerelease: false
|
|
50
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
78
|
requirements:
|
|
52
|
-
- - "
|
|
79
|
+
- - ">="
|
|
53
80
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '0
|
|
81
|
+
version: '0'
|
|
55
82
|
- !ruby/object:Gem::Dependency
|
|
56
83
|
name: pry
|
|
57
84
|
requirement: !ruby/object:Gem::Requirement
|
|
58
85
|
requirements:
|
|
59
|
-
- - "
|
|
86
|
+
- - ">="
|
|
60
87
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '0
|
|
88
|
+
version: '0'
|
|
62
89
|
type: :development
|
|
63
90
|
prerelease: false
|
|
64
91
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
92
|
requirements:
|
|
66
|
-
- - "
|
|
93
|
+
- - ">="
|
|
67
94
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '0
|
|
95
|
+
version: '0'
|
|
69
96
|
- !ruby/object:Gem::Dependency
|
|
70
97
|
name: rake
|
|
71
98
|
requirement: !ruby/object:Gem::Requirement
|
|
72
99
|
requirements:
|
|
73
100
|
- - ">="
|
|
74
101
|
- !ruby/object:Gem::Version
|
|
75
|
-
version:
|
|
102
|
+
version: '0'
|
|
76
103
|
type: :development
|
|
77
104
|
prerelease: false
|
|
78
105
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
106
|
requirements:
|
|
80
107
|
- - ">="
|
|
81
108
|
- !ruby/object:Gem::Version
|
|
82
|
-
version:
|
|
109
|
+
version: '0'
|
|
83
110
|
- !ruby/object:Gem::Dependency
|
|
84
111
|
name: rspec
|
|
85
112
|
requirement: !ruby/object:Gem::Requirement
|
|
86
113
|
requirements:
|
|
87
|
-
- - "
|
|
114
|
+
- - ">="
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '0'
|
|
117
|
+
type: :development
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - ">="
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '0'
|
|
124
|
+
- !ruby/object:Gem::Dependency
|
|
125
|
+
name: rspec-collection_matchers
|
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - ">="
|
|
88
129
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: '
|
|
130
|
+
version: '0'
|
|
90
131
|
type: :development
|
|
91
132
|
prerelease: false
|
|
92
133
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
134
|
requirements:
|
|
94
|
-
- - "
|
|
135
|
+
- - ">="
|
|
95
136
|
- !ruby/object:Gem::Version
|
|
96
|
-
version: '
|
|
137
|
+
version: '0'
|
|
97
138
|
- !ruby/object:Gem::Dependency
|
|
98
139
|
name: rubocop
|
|
99
140
|
requirement: !ruby/object:Gem::Requirement
|
|
100
141
|
requirements:
|
|
101
|
-
- - "
|
|
142
|
+
- - ">="
|
|
143
|
+
- !ruby/object:Gem::Version
|
|
144
|
+
version: '0'
|
|
145
|
+
type: :development
|
|
146
|
+
prerelease: false
|
|
147
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
148
|
+
requirements:
|
|
149
|
+
- - ">="
|
|
150
|
+
- !ruby/object:Gem::Version
|
|
151
|
+
version: '0'
|
|
152
|
+
- !ruby/object:Gem::Dependency
|
|
153
|
+
name: rubocop-packaging
|
|
154
|
+
requirement: !ruby/object:Gem::Requirement
|
|
155
|
+
requirements:
|
|
156
|
+
- - ">="
|
|
102
157
|
- !ruby/object:Gem::Version
|
|
103
|
-
version: '0
|
|
158
|
+
version: '0'
|
|
104
159
|
type: :development
|
|
105
160
|
prerelease: false
|
|
106
161
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
162
|
requirements:
|
|
108
|
-
- - "
|
|
163
|
+
- - ">="
|
|
109
164
|
- !ruby/object:Gem::Version
|
|
110
|
-
version: '0
|
|
165
|
+
version: '0'
|
|
166
|
+
- !ruby/object:Gem::Dependency
|
|
167
|
+
name: rubocop-performance
|
|
168
|
+
requirement: !ruby/object:Gem::Requirement
|
|
169
|
+
requirements:
|
|
170
|
+
- - ">="
|
|
171
|
+
- !ruby/object:Gem::Version
|
|
172
|
+
version: '0'
|
|
173
|
+
type: :development
|
|
174
|
+
prerelease: false
|
|
175
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
176
|
+
requirements:
|
|
177
|
+
- - ">="
|
|
178
|
+
- !ruby/object:Gem::Version
|
|
179
|
+
version: '0'
|
|
180
|
+
- !ruby/object:Gem::Dependency
|
|
181
|
+
name: rubocop-rake
|
|
182
|
+
requirement: !ruby/object:Gem::Requirement
|
|
183
|
+
requirements:
|
|
184
|
+
- - ">="
|
|
185
|
+
- !ruby/object:Gem::Version
|
|
186
|
+
version: '0'
|
|
187
|
+
type: :development
|
|
188
|
+
prerelease: false
|
|
189
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
190
|
+
requirements:
|
|
191
|
+
- - ">="
|
|
192
|
+
- !ruby/object:Gem::Version
|
|
193
|
+
version: '0'
|
|
194
|
+
- !ruby/object:Gem::Dependency
|
|
195
|
+
name: rubocop-rspec
|
|
196
|
+
requirement: !ruby/object:Gem::Requirement
|
|
197
|
+
requirements:
|
|
198
|
+
- - ">="
|
|
199
|
+
- !ruby/object:Gem::Version
|
|
200
|
+
version: '0'
|
|
201
|
+
type: :development
|
|
202
|
+
prerelease: false
|
|
203
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
204
|
+
requirements:
|
|
205
|
+
- - ">="
|
|
206
|
+
- !ruby/object:Gem::Version
|
|
207
|
+
version: '0'
|
|
111
208
|
- !ruby/object:Gem::Dependency
|
|
112
209
|
name: timecop
|
|
113
210
|
requirement: !ruby/object:Gem::Requirement
|
|
114
211
|
requirements:
|
|
115
|
-
- - "
|
|
212
|
+
- - ">="
|
|
116
213
|
- !ruby/object:Gem::Version
|
|
117
|
-
version: '0
|
|
214
|
+
version: '0'
|
|
118
215
|
type: :development
|
|
119
216
|
prerelease: false
|
|
120
217
|
version_requirements: !ruby/object:Gem::Requirement
|
|
121
218
|
requirements:
|
|
122
|
-
- - "
|
|
219
|
+
- - ">="
|
|
123
220
|
- !ruby/object:Gem::Version
|
|
124
|
-
version: '0
|
|
125
|
-
description:
|
|
221
|
+
version: '0'
|
|
126
222
|
email:
|
|
127
223
|
- kronn@kronn.de
|
|
128
224
|
executables:
|
|
@@ -130,45 +226,39 @@ executables:
|
|
|
130
226
|
extensions: []
|
|
131
227
|
extra_rdoc_files: []
|
|
132
228
|
files:
|
|
133
|
-
-
|
|
134
|
-
- ".gitignore"
|
|
135
|
-
- ".overcommit.yml"
|
|
136
|
-
- ".rspec"
|
|
137
|
-
- ".rubocop.yml"
|
|
138
|
-
- ".rubocop_todo.yml"
|
|
139
|
-
- ".ruby-version"
|
|
140
|
-
- ".tool-versions"
|
|
141
|
-
- ".travis.yml"
|
|
229
|
+
- AGENTS.md
|
|
142
230
|
- Gemfile
|
|
143
231
|
- LICENSE.txt
|
|
232
|
+
- PLAN-ADD-COMMAND.md
|
|
144
233
|
- README.md
|
|
145
234
|
- Rakefile
|
|
146
|
-
- bin/console
|
|
147
|
-
- bin/setup
|
|
148
235
|
- exe/ptimelog
|
|
149
236
|
- lib/ptimelog.rb
|
|
150
|
-
- lib/ptimelog/app.rb
|
|
151
237
|
- lib/ptimelog/command/add.rb
|
|
152
|
-
- lib/ptimelog/command/
|
|
238
|
+
- lib/ptimelog/command/confirm.rb
|
|
153
239
|
- lib/ptimelog/command/edit.rb
|
|
240
|
+
- lib/ptimelog/command/info.rb
|
|
154
241
|
- lib/ptimelog/command/show.rb
|
|
155
242
|
- lib/ptimelog/command/upload.rb
|
|
156
|
-
- lib/ptimelog/command/version.rb
|
|
157
243
|
- lib/ptimelog/configuration.rb
|
|
244
|
+
- lib/ptimelog/data_source.rb
|
|
158
245
|
- lib/ptimelog/day.rb
|
|
159
246
|
- lib/ptimelog/deprecation_warning.rb
|
|
160
247
|
- lib/ptimelog/entry.rb
|
|
248
|
+
- lib/ptimelog/error.rb
|
|
249
|
+
- lib/ptimelog/joiner.rb
|
|
161
250
|
- lib/ptimelog/named_date.rb
|
|
162
251
|
- lib/ptimelog/null_pathname.rb
|
|
252
|
+
- lib/ptimelog/operon.rb
|
|
163
253
|
- lib/ptimelog/script.rb
|
|
164
254
|
- lib/ptimelog/timelog.rb
|
|
165
255
|
- lib/ptimelog/version.rb
|
|
166
|
-
-
|
|
256
|
+
- opencode.json
|
|
167
257
|
homepage: https://github.com/kronn/ptimelog
|
|
168
258
|
licenses:
|
|
169
259
|
- MIT
|
|
170
|
-
metadata:
|
|
171
|
-
|
|
260
|
+
metadata:
|
|
261
|
+
rubygems_mfa_required: 'true'
|
|
172
262
|
rdoc_options: []
|
|
173
263
|
require_paths:
|
|
174
264
|
- lib
|
|
@@ -176,15 +266,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
176
266
|
requirements:
|
|
177
267
|
- - ">="
|
|
178
268
|
- !ruby/object:Gem::Version
|
|
179
|
-
version:
|
|
269
|
+
version: '3.1'
|
|
180
270
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
181
271
|
requirements:
|
|
182
272
|
- - ">="
|
|
183
273
|
- !ruby/object:Gem::Version
|
|
184
274
|
version: '0'
|
|
185
275
|
requirements: []
|
|
186
|
-
rubygems_version:
|
|
187
|
-
signing_key:
|
|
276
|
+
rubygems_version: 4.0.19
|
|
188
277
|
specification_version: 4
|
|
189
|
-
summary: Move time-entries from gTimelog to PuzzleTime
|
|
278
|
+
summary: Move time-entries from gTimelog or Obsidian to PuzzleTime
|
|
190
279
|
test_files: []
|
data/.envrc
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
PATH_add exe
|