raindown 0.11.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/lib/raindown/actions.rb +1 -1
- data/lib/raindown/blueprints.rb +7 -6
- data/lib/raindown/cli.rb +22 -16
- data/lib/raindown/code_blocks.rb +3 -1
- data/lib/raindown/editing.rb +2 -2
- data/lib/raindown/endpoint/endpoint_runner.rb +0 -5
- data/lib/raindown/markdown_nodes.rb +1 -1
- data/lib/raindown/pages.rb +58 -11
- data/lib/raindown/rewriter.rb +13 -12
- data/lib/raindown/tui/blueprints/blueprints_view.rb +1 -1
- data/lib/raindown/tui/feelings/feelings_log.rb +4 -4
- data/lib/raindown/watcher/file_watcher.rb +3 -3
- data/lib/raindown/watcher/project.rb +3 -4
- data/lib/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6ea9df31d83ebabf0649ad353b75e2f2546aeb10c31aa97038bdd85f5e45fd2c
|
|
4
|
+
data.tar.gz: cc736f0d57e576c476629bfa7fe37405139cb1b880e86908e71e7e8d8b22926c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 851438f9079a944aca1dc2f97d0c5081822d0ce31df12e536ca10dce8d471233ee0879fc91b7004fcf7949159554dec7cd0d1e4aa3d50b8cee4009dbd3180766
|
|
7
|
+
data.tar.gz: 27863d35bb59084303144bdc0b1ea3852028a55c854e8142456c3e1d8f9ee495a3d5489dc00c91ce474bcd9eda833deb42d4f3696118bb24c25e913dabb54372
|
data/lib/raindown/actions.rb
CHANGED
|
@@ -24,7 +24,7 @@ module Raindown
|
|
|
24
24
|
# Writes back the whole file, like saving it while it's watched, with bare directives allowed, and endpoints with no
|
|
25
25
|
# response run, when asked, like with --bare and -x.
|
|
26
26
|
def write_back(path, bare: false, execute: false)
|
|
27
|
-
Rewriter.update(path:, bare:, execute:, metadata: Pages.for(path))
|
|
27
|
+
Rewriter.update(path:, bare:, execute:, metadata: Pages.for(path)).changed?
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
# Runs one endpoint's request and writes its response, replacing any response it had, then raises when the response
|
data/lib/raindown/blueprints.rb
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'fileutils'
|
|
3
4
|
require 'json'
|
|
4
5
|
require 'time'
|
|
5
6
|
require 'unicode/display_width'
|
|
6
7
|
require_relative 'projects'
|
|
7
8
|
|
|
8
9
|
module Raindown
|
|
9
|
-
# Blueprints from Claude Code's replies, kept in a project's .raindown/blueprints.md, which the terminal UI's Blueprints page
|
|
10
|
+
# Blueprints from Claude Code's replies, kept in a project's .raindown/data/blueprints.md, which the terminal UI's Blueprints page
|
|
10
11
|
# shows, newest first. Each is a code block under a heading of when it was saved and its kind:
|
|
11
12
|
# ## 2026-10-30T03:14:07Z · mockup
|
|
12
13
|
#
|
|
@@ -32,7 +33,7 @@ module Raindown
|
|
|
32
33
|
"#{time.sub('T', ' ').sub(/:\d\dZ\z/, ' UTC')}#{SEPARATOR}#{kind}"
|
|
33
34
|
end
|
|
34
35
|
|
|
35
|
-
# As it's saved in .raindown/blueprints.md, under when it was saved and its kind, then its description.
|
|
36
|
+
# As it's saved in .raindown/data/blueprints.md, under when it was saved and its kind, then its description.
|
|
36
37
|
def markdown(saved_at)
|
|
37
38
|
about = "#{description}\n\n" if description
|
|
38
39
|
"## #{saved_at}#{SEPARATOR}#{kind}\n\n#{about}```#{kind}\n#{lines.join("\n")}\n```\n\n"
|
|
@@ -49,7 +50,7 @@ module Raindown
|
|
|
49
50
|
def distance(other) = [(top - other.top).abs, (left + right - other.left - other.right).abs]
|
|
50
51
|
end
|
|
51
52
|
|
|
52
|
-
FILE = File.join('.raindown', 'blueprints.md')
|
|
53
|
+
FILE = File.join('.raindown', 'data', 'blueprints.md')
|
|
53
54
|
# The kinds a code block can be marked with to be a blueprint.
|
|
54
55
|
KINDS = %w[mockup diagram chart table other].freeze
|
|
55
56
|
# What a blueprint's drawn with: lines, like boxes' and arrows', and blocks, like charts' bars.
|
|
@@ -79,8 +80,7 @@ module Raindown
|
|
|
79
80
|
return [] unless File.exist?(path)
|
|
80
81
|
|
|
81
82
|
lines = File.readlines(path, chomp: true)
|
|
82
|
-
|
|
83
|
-
starts.filter_map { |start| blueprint(lines, start) }.reverse
|
|
83
|
+
lines.each_index.select { lines[it].start_with?('## ') }.filter_map { blueprint(lines, it) }.reverse
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
# Saves the blueprints in Claude Code's last reply, from its Stop hook's JSON, to the project it's working in, if any,
|
|
@@ -91,7 +91,8 @@ module Raindown
|
|
|
91
91
|
return if drawn.empty?
|
|
92
92
|
|
|
93
93
|
time = Time.now.utc.iso8601
|
|
94
|
-
File.
|
|
94
|
+
path = File.join(root, FILE).tap { FileUtils.mkdir_p(File.dirname(it)) }
|
|
95
|
+
File.open(path, 'a') { |file| drawn.each { file.write(it.markdown(time)) } }
|
|
95
96
|
end
|
|
96
97
|
|
|
97
98
|
# The text's code blocks that are blueprints, each with its kind and the paragraph above it.
|
data/lib/raindown/cli.rb
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'fileutils'
|
|
4
|
+
require 'open3'
|
|
5
|
+
require 'tmpdir'
|
|
4
6
|
require 'trees'
|
|
5
7
|
require_relative '../raindown'
|
|
6
8
|
require_relative '../version'
|
|
@@ -32,7 +34,7 @@ module Raindown
|
|
|
32
34
|
init [DIRECTORY] Create a .raindown folder for the project's config and nodes (default: current directory)
|
|
33
35
|
server Keep Raindown loaded for an editor, answering a JSON request a line on stdin with its HTML,
|
|
34
36
|
or its Markdown as it would be written back, like --stdin and --stdin --rewrite
|
|
35
|
-
blueprints Save the blueprints in Claude Code's last reply to .raindown/blueprints.md, for the terminal
|
|
37
|
+
blueprints Save the blueprints in Claude Code's last reply to .raindown/data/blueprints.md, for the terminal
|
|
36
38
|
UI's Blueprints page, as a Stop hook that's given the hook's JSON
|
|
37
39
|
|
|
38
40
|
Options:
|
|
@@ -52,12 +54,8 @@ module Raindown
|
|
|
52
54
|
Set RAINDOWN_DEBUG=1 to show where an error happened.
|
|
53
55
|
TEXT
|
|
54
56
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
#
|
|
58
|
-
# Leave code blocks unhighlighted, so the browser can highlight them instead:
|
|
59
|
-
# highlight_code: false
|
|
60
|
-
YAML
|
|
57
|
+
# The files "raindown init" puts in a project's .raindown folder, like "rain new" clones raindeer-template.
|
|
58
|
+
TEMPLATE_URL = 'https://github.com/rainpour/raindown-init'
|
|
61
59
|
|
|
62
60
|
# A path opens the terminal UI, or after "watch" it's watched, and with --html or --stdin it's rendered. The flags
|
|
63
61
|
# are keyword arguments, in any order, like -x for x:, and --html is the OUTPUT when it's given one.
|
|
@@ -111,16 +109,20 @@ module Raindown
|
|
|
111
109
|
abort("raindown: #{path}: #{error.message}")
|
|
112
110
|
end
|
|
113
111
|
|
|
114
|
-
#
|
|
112
|
+
# Clones raindown-init into the .raindown folder that marks a project's root and holds its config.yml and nodes,
|
|
113
|
+
# keeping any files already there.
|
|
115
114
|
def self.init(directory = nil)
|
|
116
115
|
folder = File.join(directory || Dir.pwd, Nodes::FOLDER)
|
|
117
116
|
config = File.join(folder, Config::FILE)
|
|
118
117
|
return puts("#{config} already exists") if File.exist?(config)
|
|
119
118
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
119
|
+
Dir.mktmpdir do |clone|
|
|
120
|
+
clone_template(clone)
|
|
121
|
+
FileUtils.mkdir_p(folder)
|
|
122
|
+
(Dir.children(clone) - ['.git']).each do |name|
|
|
123
|
+
FileUtils.cp_r(File.join(clone, name), folder) unless File.exist?(File.join(folder, name))
|
|
124
|
+
end
|
|
125
|
+
end
|
|
124
126
|
puts "Created #{config}"
|
|
125
127
|
puts 'Put your own nodes in the same folder. See https://raindown.dev/docs/components'
|
|
126
128
|
end
|
|
@@ -136,6 +138,11 @@ module Raindown
|
|
|
136
138
|
class << self
|
|
137
139
|
private
|
|
138
140
|
|
|
141
|
+
def clone_template(directory)
|
|
142
|
+
output, status = Open3.capture2e('git', 'clone', '--quiet', '--depth', '1', TEMPLATE_URL, directory)
|
|
143
|
+
abort("Couldn't clone #{TEMPLATE_URL}:\n#{output}") unless status.success?
|
|
144
|
+
end
|
|
145
|
+
|
|
139
146
|
# Endpoints run while formatting, which --stdin skips, so -x can't go with it. --html - prints, like --html.
|
|
140
147
|
def open_path(path, html:, stdin:, bare:, execute:)
|
|
141
148
|
output = html unless [true, '-'].include?(html)
|
|
@@ -152,14 +159,13 @@ module Raindown
|
|
|
152
159
|
Nodes.load(path)
|
|
153
160
|
|
|
154
161
|
metadata = Pages.for(path)
|
|
155
|
-
Rewriter.update(path:, bare:, execute:, metadata:)
|
|
162
|
+
failures = Rewriter.update(path:, bare:, execute:, metadata:).failures
|
|
156
163
|
write(output, Raindown.render_file(path:, bare:, metadata:))
|
|
157
|
-
check_endpoints(
|
|
164
|
+
check_endpoints(failures)
|
|
158
165
|
end
|
|
159
166
|
|
|
160
167
|
# Endpoint responses that fail their checks, like status: 200, are listed, and fail the command, for CI.
|
|
161
|
-
def check_endpoints(
|
|
162
|
-
failures = EndpointRunner.failures(content: Utils.read_markdown(path), path:, bare:)
|
|
168
|
+
def check_endpoints(failures)
|
|
163
169
|
return if failures.empty?
|
|
164
170
|
|
|
165
171
|
warn(*failures.map { "Endpoint check failed: #{it}" })
|
data/lib/raindown/code_blocks.rb
CHANGED
|
@@ -5,6 +5,8 @@ module Raindown
|
|
|
5
5
|
module CodeBlocks
|
|
6
6
|
FENCE = /\A {0,3}(`{3,}|~{3,})[ \t]*(\S*)/
|
|
7
7
|
SNIPPET_LANGUAGES = %w[sh bash shell zsh console curl].freeze
|
|
8
|
+
# The line closing a fence, like ``` or ~~~~, compiled once per fence.
|
|
9
|
+
CLOSINGS = Hash.new { |closings, fence| closings[fence] = /\A {0,3}#{fence[0]}{#{fence.length},}[ \t]*$/ }
|
|
8
10
|
|
|
9
11
|
module_function
|
|
10
12
|
|
|
@@ -13,7 +15,7 @@ module Raindown
|
|
|
13
15
|
fence, language = lines[index]&.match(FENCE)&.captures
|
|
14
16
|
return unless fence
|
|
15
17
|
|
|
16
|
-
close = (index + 1...lines.length).find { |i| lines[i].match?(
|
|
18
|
+
close = (index + 1...lines.length).find { |i| lines[i].match?(CLOSINGS[fence]) }
|
|
17
19
|
{ open: index, close:, language:, body: lines[index + 1...close].join } if close
|
|
18
20
|
end
|
|
19
21
|
|
data/lib/raindown/editing.rb
CHANGED
|
@@ -17,8 +17,8 @@ module Raindown
|
|
|
17
17
|
|
|
18
18
|
def rewrite_markdown(markdown, path:, bare: false, execute: false)
|
|
19
19
|
Nodes.load(path)
|
|
20
|
-
|
|
21
|
-
{ output:
|
|
20
|
+
rewrite = Rewriter.rewrite(content: markdown, path:, bare:, execute:, metadata: Pages.for(path))
|
|
21
|
+
{ output: rewrite.content, warnings: rewrite.failures.map { "Endpoint check failed: #{it}" } }
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
end
|
|
@@ -42,11 +42,6 @@ module Raindown
|
|
|
42
42
|
new(content:, path:, bare: BareDirectives.enabled?(path:, content:, bare:), execute:).rewrite
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
# The file's failed checks, like "api.md:3: expected status 200, received 404", from the responses in it.
|
|
46
|
-
def self.failures(content:, path: nil, bare: false)
|
|
47
|
-
new(content:, path:, bare: BareDirectives.enabled?(path:, content:, bare:)).tap(&:rewrite).failures
|
|
48
|
-
end
|
|
49
|
-
|
|
50
45
|
def initialize(content:, path: nil, bare: false, execute: false, only: nil)
|
|
51
46
|
@lines = content.lines
|
|
52
47
|
@path = path
|
|
@@ -26,7 +26,7 @@ module Raindown
|
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
# Whether a file is a Markdown Component, by its extension and name, so .raindown/blueprints.md isn't one.
|
|
29
|
+
# Whether a file is a Markdown Component, by its extension and name, so .raindown/data/blueprints.md isn't one.
|
|
30
30
|
def file?(path)
|
|
31
31
|
EXTENSIONS.include?(File.extname(path)) && File.basename(path, '.*').match?(NAME)
|
|
32
32
|
end
|
data/lib/raindown/pages.rb
CHANGED
|
@@ -13,23 +13,49 @@ module Raindown
|
|
|
13
13
|
# folder: basics, order: 1, path: docs/_basics/_1-getting-started.md
|
|
14
14
|
# Folder tags can share a folder name with "&", like docs/_1&_basics/getting-started.md.
|
|
15
15
|
# An app can add its own metadata by subclassing and overriding implicit_metadata, like Raindeer's URL paths.
|
|
16
|
+
# Each file's front matter is only read once something lists the files. The listing and the front matter are kept for
|
|
17
|
+
# as long as the process runs, like "raindown server", until they change.
|
|
16
18
|
class Pages
|
|
17
19
|
EXTENSIONS = %w[md rd markdown raindown].freeze
|
|
18
20
|
SEGMENTS = Regexp.union(['/', '&', '-'])
|
|
19
21
|
|
|
20
22
|
class << self
|
|
23
|
+
# Each file's front matter, by its path, with the time it was modified when it was read.
|
|
24
|
+
def front_matters
|
|
25
|
+
@front_matters ||= {}
|
|
26
|
+
end
|
|
27
|
+
|
|
21
28
|
# The Markdown files under a project's root, the folder with its .raindown folder or else the file's folder.
|
|
22
29
|
def for(path)
|
|
23
30
|
file_directory = File.dirname(File.expand_path(path))
|
|
24
31
|
load(root: Nodes.find_folder(file_directory)&.then { File.dirname(it) } || file_directory)
|
|
25
32
|
end
|
|
26
33
|
|
|
27
|
-
# Lists the files under the root
|
|
34
|
+
# Lists the files under the root, without loading them, for Dirload.
|
|
28
35
|
def load(root:)
|
|
29
36
|
require 'dirload'
|
|
30
|
-
file_types =
|
|
37
|
+
file_types = file_paths(root).group_by { Dirload.extension(file_path: it) }
|
|
31
38
|
new(metadata: Dirload::Metadata.new(loaded_paths: {}, missed_paths: [], file_types:), root:)
|
|
32
39
|
end
|
|
40
|
+
|
|
41
|
+
# The files under the root, outside hidden folders, like Dirload lists them. They're listed again once a folder
|
|
42
|
+
# among them has changed, which adding, removing or renaming a file or folder in it does.
|
|
43
|
+
def file_paths(root)
|
|
44
|
+
listing = listings[root]
|
|
45
|
+
return listing[:file_paths] if listing&.dig(:folders)&.all? { |folder, modified| modified_at(folder) == modified }
|
|
46
|
+
|
|
47
|
+
folders, file_paths = Dir["#{root}/**/*"].partition { File.directory?(it) }
|
|
48
|
+
listings[root] = { file_paths:, folders: [root, *folders].to_h { [it, modified_at(it)] } }
|
|
49
|
+
file_paths
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def listings
|
|
53
|
+
@listings ||= {}
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def modified_at(path)
|
|
57
|
+
File.mtime(path) if File.exist?(path)
|
|
58
|
+
end
|
|
33
59
|
end
|
|
34
60
|
|
|
35
61
|
attr_reader :root
|
|
@@ -39,10 +65,6 @@ module Raindown
|
|
|
39
65
|
@root = root
|
|
40
66
|
@render_options = render_options
|
|
41
67
|
@file_paths = metadata.file_types.values_at(*EXTENSIONS).flatten.compact
|
|
42
|
-
@metadata = {}
|
|
43
|
-
@tags = {}
|
|
44
|
-
|
|
45
|
-
@file_paths.each { |file_path| process(file_path:) }
|
|
46
68
|
end
|
|
47
69
|
|
|
48
70
|
# The files with any of the tags, like list(folder: 'basics') or list(tags: 'ruby'), sorted by order.
|
|
@@ -53,21 +75,46 @@ module Raindown
|
|
|
53
75
|
end
|
|
54
76
|
|
|
55
77
|
def tagged(**tags)
|
|
56
|
-
tags.flat_map { |type, tag|
|
|
78
|
+
tags.flat_map { |type, tag| self.tags.dig(type.to_sym, tag) || [] }.uniq
|
|
57
79
|
end
|
|
58
80
|
|
|
59
81
|
private
|
|
60
82
|
|
|
83
|
+
def metadata
|
|
84
|
+
process_files unless @metadata
|
|
85
|
+
@metadata
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def tags
|
|
89
|
+
process_files unless @tags
|
|
90
|
+
@tags
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def process_files
|
|
94
|
+
@metadata = {}
|
|
95
|
+
@tags = {}
|
|
96
|
+
@file_paths.each { |file_path| process(file_path:) }
|
|
97
|
+
end
|
|
98
|
+
|
|
61
99
|
def listed_page(file_path)
|
|
62
|
-
Page.new(file_path:, metadata:
|
|
100
|
+
Page.new(file_path:, metadata: metadata[file_path], render_options: @render_options)
|
|
63
101
|
end
|
|
64
102
|
|
|
65
103
|
def process(file_path:)
|
|
66
|
-
metadata = implicit_metadata(file_path:).merge(
|
|
104
|
+
metadata = implicit_metadata(file_path:).merge(front_matter(file_path))
|
|
67
105
|
@metadata[file_path] = metadata
|
|
68
106
|
metadata.each { |type, tag| Array(tag).each { |item| tag(type:, tag: item, file_path:) } }
|
|
69
107
|
end
|
|
70
108
|
|
|
109
|
+
# The file's front matter, read again only when the file's changed since it was last read.
|
|
110
|
+
def front_matter(file_path)
|
|
111
|
+
modified = File.mtime(file_path)
|
|
112
|
+
modified_then, front_matter = self.class.front_matters[file_path]
|
|
113
|
+
return front_matter if modified_then == modified
|
|
114
|
+
|
|
115
|
+
FrontMatter.parse(Utils.read_markdown(file_path)).first.tap { self.class.front_matters[file_path] = [modified, it] }
|
|
116
|
+
end
|
|
117
|
+
|
|
71
118
|
# The path is relative to the root, like docs/_basics/_1-getting-started.md.
|
|
72
119
|
# A subclass can add or replace metadata here, like Raindeer's Rain::Pages replacing the path with a URL path.
|
|
73
120
|
def implicit_metadata(file_path:)
|
|
@@ -104,8 +151,8 @@ module Raindown
|
|
|
104
151
|
|
|
105
152
|
# Files with an order come first, by order, then the rest by path.
|
|
106
153
|
def sort_key(file_path)
|
|
107
|
-
order =
|
|
108
|
-
[order.is_a?(Numeric) ? 0 : 1, order.is_a?(Numeric) ? order : 0,
|
|
154
|
+
order = metadata[file_path][:order]
|
|
155
|
+
[order.is_a?(Numeric) ? 0 : 1, order.is_a?(Numeric) ? order : 0, metadata[file_path][:path].to_s]
|
|
109
156
|
end
|
|
110
157
|
end
|
|
111
158
|
end
|
data/lib/raindown/rewriter.rb
CHANGED
|
@@ -8,26 +8,27 @@ require_relative 'utils'
|
|
|
8
8
|
module Raindown
|
|
9
9
|
# Rewrites the generated parts of a Markdown file in place before it's rendered:
|
|
10
10
|
# node output, like the TOC, endpoint responses and table alignment.
|
|
11
|
-
# Each rewriter responds to rewrite(content:, path:, bare:, execute:, metadata:) and returns the new content.
|
|
12
11
|
module Rewriter
|
|
13
|
-
|
|
12
|
+
# The content written back, and the endpoint checks it failed, like "api.md:3: expected status 200, received 404".
|
|
13
|
+
Rewrite = Data.define(:original, :content, :failures) do
|
|
14
|
+
def changed?
|
|
15
|
+
content != original
|
|
16
|
+
end
|
|
17
|
+
end
|
|
14
18
|
|
|
15
19
|
module_function
|
|
16
20
|
|
|
17
21
|
def rewrite(content:, path:, bare: false, execute: false, metadata: NoMetadata)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
injected = NodeInjector.rewrite(content:, path:, bare:, metadata:)
|
|
23
|
+
endpoints = EndpointRunner.new(content: injected, path:, bare: BareDirectives.enabled?(path:, content: injected, bare:), execute:)
|
|
24
|
+
Rewrite.new(original: content, content: TableFormatter.rewrite(content: endpoints.rewrite), failures: endpoints.failures)
|
|
21
25
|
end
|
|
22
26
|
|
|
23
|
-
#
|
|
27
|
+
# Writes the file only when it changed.
|
|
24
28
|
def update(path:, bare: false, execute: false, metadata: NoMetadata)
|
|
25
|
-
content
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
File.write(path, rewritten)
|
|
30
|
-
true
|
|
29
|
+
rewrite(content: Utils.read_markdown(path), path:, bare:, execute:, metadata:).tap do |rewrite|
|
|
30
|
+
File.write(path, rewrite.content) if rewrite.changed?
|
|
31
|
+
end
|
|
31
32
|
end
|
|
32
33
|
end
|
|
33
34
|
end
|
|
@@ -11,7 +11,7 @@ require_relative 'filter'
|
|
|
11
11
|
|
|
12
12
|
module Raindown
|
|
13
13
|
module TUI
|
|
14
|
-
# The blueprints from Claude Code's replies, in the project's .raindown/blueprints.md, newest first, then those in its
|
|
14
|
+
# The blueprints from Claude Code's replies, in the project's .raindown/data/blueprints.md, newest first, then those in its
|
|
15
15
|
# code's comments, one at a time, of
|
|
16
16
|
# those its Filter's keywords match, each traced to its code: the arrows move between the boxes drawn in one, or a
|
|
17
17
|
# click on one, the code each box names shows below, Space or another click moving on to
|
|
@@ -22,16 +22,16 @@ module Raindown
|
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
# The feelings everyone's sorted about the code as it is now, kept in .raindown/feelings.csv with the commit they were
|
|
25
|
+
# The feelings everyone's sorted about the code as it is now, kept in .raindown/data/feelings.csv with the commit they were
|
|
26
26
|
# felt at, which a node can count, like the README's code debt. A feeling about code that's since changed, or gone, moves to
|
|
27
|
-
# .raindown/memories.csv, so the feelings stay few, and the history's kept.
|
|
27
|
+
# .raindown/data/memories.csv, so the feelings stay few, and the history's kept.
|
|
28
28
|
class FeelingsLog
|
|
29
29
|
TEMPERS = %w[frolic numb woe dread malice].freeze
|
|
30
30
|
HEADER = %i[timestamp commit checksum location method temper note].freeze
|
|
31
31
|
def initialize(root:)
|
|
32
32
|
@root = root
|
|
33
|
-
@csv = File.join(root, '.raindown', 'feelings.csv')
|
|
34
|
-
@memories = File.join(root, '.raindown', 'memories.csv')
|
|
33
|
+
@csv = File.join(root, '.raindown', 'data', 'feelings.csv')
|
|
34
|
+
@memories = File.join(root, '.raindown', 'data', 'memories.csv')
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
# Adds the feeling, as the newest, in place of one about the method before, which moves to the memories if it was
|
|
@@ -51,9 +51,9 @@ module Raindown
|
|
|
51
51
|
|
|
52
52
|
# The files' metadata is loaded again each time, so new and renamed files are listed.
|
|
53
53
|
def rewrite
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
changed
|
|
54
|
+
rewrite = Rewriter.update(path:, bare:, execute:, metadata: Pages.for(path))
|
|
55
|
+
rewrite.failures.each { warn "Endpoint check failed: #{it}" }
|
|
56
|
+
rewrite.changed?
|
|
57
57
|
end
|
|
58
58
|
end
|
|
59
59
|
end
|
|
@@ -28,10 +28,9 @@ module Raindown
|
|
|
28
28
|
|
|
29
29
|
# Writing back saves the file again, and that save changes nothing, so it ends there.
|
|
30
30
|
def update(path)
|
|
31
|
-
|
|
32
|
-
puts "[#{name}] Updated #{relative(path)}" if changed
|
|
33
|
-
|
|
34
|
-
.each { warn "[#{name}] Endpoint check failed: #{it}" }
|
|
31
|
+
rewrite = Rewriter.update(path:, bare: @bare, execute: @execute, metadata: Pages.for(path))
|
|
32
|
+
puts "[#{name}] Updated #{relative(path)}" if rewrite.changed?
|
|
33
|
+
rewrite.failures.each { warn "[#{name}] Endpoint check failed: #{it}" }
|
|
35
34
|
rescue StandardError => e
|
|
36
35
|
raise if ENV['RAINDOWN_DEBUG']
|
|
37
36
|
|
data/lib/version.rb
CHANGED