ariadna 1.3.0 → 2.0.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/ariadna.gemspec +0 -1
- data/data/agents/ariadna-codebase-mapper.md +34 -722
- data/data/agents/ariadna-debugger.md +44 -1139
- data/data/agents/ariadna-executor.md +75 -396
- data/data/agents/ariadna-planner.md +78 -1215
- data/data/agents/ariadna-roadmapper.md +55 -582
- data/data/agents/ariadna-verifier.md +60 -702
- data/data/ariadna/templates/config.json +8 -33
- data/data/ariadna/workflows/debug.md +28 -0
- data/data/ariadna/workflows/execute-phase.md +31 -513
- data/data/ariadna/workflows/map-codebase.md +20 -319
- data/data/ariadna/workflows/new-milestone.md +20 -365
- data/data/ariadna/workflows/new-project.md +19 -880
- data/data/ariadna/workflows/plan-phase.md +24 -443
- data/data/ariadna/workflows/progress.md +20 -376
- data/data/ariadna/workflows/quick.md +19 -221
- data/data/ariadna/workflows/roadmap-ops.md +28 -0
- data/data/ariadna/workflows/verify-work.md +23 -560
- data/data/commands/ariadna/add-phase.md +11 -22
- data/data/commands/ariadna/debug.md +11 -143
- data/data/commands/ariadna/execute-phase.md +12 -30
- data/data/commands/ariadna/insert-phase.md +7 -14
- data/data/commands/ariadna/map-codebase.md +16 -49
- data/data/commands/ariadna/new-milestone.md +12 -25
- data/data/commands/ariadna/new-project.md +22 -26
- data/data/commands/ariadna/plan-phase.md +13 -22
- data/data/commands/ariadna/progress.md +16 -6
- data/data/commands/ariadna/quick.md +9 -11
- data/data/commands/ariadna/remove-phase.md +9 -12
- data/data/commands/ariadna/verify-work.md +14 -19
- data/data/skills/rails-backend/API.md +138 -0
- data/data/skills/rails-backend/CONTROLLERS.md +154 -0
- data/data/skills/rails-backend/JOBS.md +132 -0
- data/data/skills/rails-backend/MODELS.md +213 -0
- data/data/skills/rails-backend/SKILL.md +169 -0
- data/data/skills/rails-frontend/ASSETS.md +154 -0
- data/data/skills/rails-frontend/COMPONENTS.md +253 -0
- data/data/skills/rails-frontend/SKILL.md +187 -0
- data/data/skills/rails-frontend/VIEWS.md +168 -0
- data/data/skills/rails-performance/PROFILING.md +106 -0
- data/data/skills/rails-performance/SKILL.md +217 -0
- data/data/skills/rails-security/AUDIT.md +118 -0
- data/data/skills/rails-security/SKILL.md +422 -0
- data/data/skills/rails-testing/FIXTURES.md +78 -0
- data/data/skills/rails-testing/SKILL.md +160 -0
- data/data/skills/rails-testing/SYSTEM-TESTS.md +73 -0
- data/lib/ariadna/installer.rb +11 -15
- data/lib/ariadna/tools/cli.rb +0 -12
- data/lib/ariadna/tools/config_manager.rb +10 -72
- data/lib/ariadna/tools/frontmatter.rb +23 -1
- data/lib/ariadna/tools/init.rb +201 -401
- data/lib/ariadna/tools/model_profiles.rb +6 -14
- data/lib/ariadna/tools/phase_manager.rb +1 -10
- data/lib/ariadna/tools/state_manager.rb +170 -451
- data/lib/ariadna/tools/template_filler.rb +4 -12
- data/lib/ariadna/tools/verification.rb +21 -399
- data/lib/ariadna/uninstaller.rb +9 -0
- data/lib/ariadna/version.rb +1 -1
- data/lib/ariadna.rb +1 -0
- metadata +20 -91
- data/data/agents/ariadna-backend-executor.md +0 -261
- data/data/agents/ariadna-frontend-executor.md +0 -259
- data/data/agents/ariadna-integration-checker.md +0 -418
- data/data/agents/ariadna-phase-researcher.md +0 -469
- data/data/agents/ariadna-plan-checker.md +0 -622
- data/data/agents/ariadna-project-researcher.md +0 -618
- data/data/agents/ariadna-research-synthesizer.md +0 -236
- data/data/agents/ariadna-test-executor.md +0 -266
- data/data/ariadna/references/checkpoints.md +0 -772
- data/data/ariadna/references/continuation-format.md +0 -249
- data/data/ariadna/references/decimal-phase-calculation.md +0 -65
- data/data/ariadna/references/git-integration.md +0 -248
- data/data/ariadna/references/git-planning-commit.md +0 -38
- data/data/ariadna/references/model-profile-resolution.md +0 -32
- data/data/ariadna/references/model-profiles.md +0 -73
- data/data/ariadna/references/phase-argument-parsing.md +0 -61
- data/data/ariadna/references/planning-config.md +0 -194
- data/data/ariadna/references/questioning.md +0 -153
- data/data/ariadna/references/rails-conventions.md +0 -416
- data/data/ariadna/references/tdd.md +0 -267
- data/data/ariadna/references/ui-brand.md +0 -160
- data/data/ariadna/references/verification-patterns.md +0 -853
- data/data/ariadna/templates/codebase/architecture.md +0 -481
- data/data/ariadna/templates/codebase/concerns.md +0 -380
- data/data/ariadna/templates/codebase/conventions.md +0 -434
- data/data/ariadna/templates/codebase/integrations.md +0 -328
- data/data/ariadna/templates/codebase/stack.md +0 -189
- data/data/ariadna/templates/codebase/structure.md +0 -418
- data/data/ariadna/templates/codebase/testing.md +0 -606
- data/data/ariadna/templates/context.md +0 -283
- data/data/ariadna/templates/continue-here.md +0 -78
- data/data/ariadna/templates/debug-subagent-prompt.md +0 -91
- data/data/ariadna/templates/phase-prompt.md +0 -609
- data/data/ariadna/templates/planner-subagent-prompt.md +0 -117
- data/data/ariadna/templates/research-project/ARCHITECTURE.md +0 -439
- data/data/ariadna/templates/research-project/FEATURES.md +0 -168
- data/data/ariadna/templates/research-project/PITFALLS.md +0 -406
- data/data/ariadna/templates/research-project/STACK.md +0 -251
- data/data/ariadna/templates/research-project/SUMMARY.md +0 -247
- data/data/ariadna/templates/state.md +0 -176
- data/data/ariadna/templates/summary-complex.md +0 -59
- data/data/ariadna/templates/summary-minimal.md +0 -41
- data/data/ariadna/templates/summary-standard.md +0 -48
- data/data/ariadna/templates/user-setup.md +0 -310
- data/data/ariadna/workflows/add-phase.md +0 -111
- data/data/ariadna/workflows/add-todo.md +0 -157
- data/data/ariadna/workflows/audit-milestone.md +0 -241
- data/data/ariadna/workflows/check-todos.md +0 -176
- data/data/ariadna/workflows/complete-milestone.md +0 -644
- data/data/ariadna/workflows/diagnose-issues.md +0 -219
- data/data/ariadna/workflows/discovery-phase.md +0 -289
- data/data/ariadna/workflows/discuss-phase.md +0 -408
- data/data/ariadna/workflows/execute-plan.md +0 -448
- data/data/ariadna/workflows/help.md +0 -470
- data/data/ariadna/workflows/insert-phase.md +0 -129
- data/data/ariadna/workflows/list-phase-assumptions.md +0 -178
- data/data/ariadna/workflows/pause-work.md +0 -122
- data/data/ariadna/workflows/plan-milestone-gaps.md +0 -256
- data/data/ariadna/workflows/remove-phase.md +0 -154
- data/data/ariadna/workflows/research-phase.md +0 -74
- data/data/ariadna/workflows/resume-project.md +0 -306
- data/data/ariadna/workflows/set-profile.md +0 -80
- data/data/ariadna/workflows/settings.md +0 -145
- data/data/ariadna/workflows/transition.md +0 -493
- data/data/ariadna/workflows/update.md +0 -212
- data/data/ariadna/workflows/verify-phase.md +0 -226
- data/data/commands/ariadna/add-todo.md +0 -42
- data/data/commands/ariadna/audit-milestone.md +0 -42
- data/data/commands/ariadna/check-todos.md +0 -41
- data/data/commands/ariadna/complete-milestone.md +0 -136
- data/data/commands/ariadna/discuss-phase.md +0 -86
- data/data/commands/ariadna/help.md +0 -22
- data/data/commands/ariadna/list-phase-assumptions.md +0 -50
- data/data/commands/ariadna/pause-work.md +0 -35
- data/data/commands/ariadna/plan-milestone-gaps.md +0 -40
- data/data/commands/ariadna/reapply-patches.md +0 -110
- data/data/commands/ariadna/research-phase.md +0 -187
- data/data/commands/ariadna/resume-work.md +0 -40
- data/data/commands/ariadna/set-profile.md +0 -34
- data/data/commands/ariadna/settings.md +0 -36
- data/data/commands/ariadna/update.md +0 -37
- data/data/guides/backend.md +0 -3069
- data/data/guides/frontend.md +0 -1479
- data/data/guides/performance.md +0 -1193
- data/data/guides/security.md +0 -1522
- data/data/guides/style-guide.md +0 -1091
- data/data/guides/testing.md +0 -504
- data/data/templates.md +0 -94
|
@@ -51,22 +51,14 @@ module Ariadna
|
|
|
51
51
|
end
|
|
52
52
|
file_count = file_mentions.length
|
|
53
53
|
|
|
54
|
-
template = "templates/summary
|
|
55
|
-
type = "
|
|
56
|
-
|
|
57
|
-
if task_count <= 2 && file_count <= 3 && !has_decisions
|
|
58
|
-
template = "templates/summary-minimal.md"
|
|
59
|
-
type = "minimal"
|
|
60
|
-
elsif has_decisions || file_count > 6 || task_count > 5
|
|
61
|
-
template = "templates/summary-complex.md"
|
|
62
|
-
type = "complex"
|
|
63
|
-
end
|
|
54
|
+
template = "templates/summary.md"
|
|
55
|
+
type = "summary"
|
|
64
56
|
|
|
65
57
|
Output.json({ template: template, type: type, taskCount: task_count, fileCount: file_count, hasDecisions: has_decisions },
|
|
66
58
|
raw: raw, raw_value: template)
|
|
67
59
|
rescue StandardError => e
|
|
68
|
-
Output.json({ template: "templates/summary
|
|
69
|
-
raw: raw, raw_value: "templates/summary
|
|
60
|
+
Output.json({ template: "templates/summary.md", type: "summary", error: e.message },
|
|
61
|
+
raw: raw, raw_value: "templates/summary.md")
|
|
70
62
|
end
|
|
71
63
|
end
|
|
72
64
|
|
|
@@ -8,177 +8,36 @@ module Ariadna
|
|
|
8
8
|
def self.dispatch(argv, raw: false)
|
|
9
9
|
subcommand = argv.shift
|
|
10
10
|
case subcommand
|
|
11
|
-
when "plan-structure"
|
|
12
|
-
verify_plan_structure(argv.first, raw: raw)
|
|
13
|
-
when "phase-completeness"
|
|
14
|
-
verify_phase_completeness(argv.first, raw: raw)
|
|
15
|
-
when "references"
|
|
16
|
-
verify_references(argv.first, raw: raw)
|
|
17
11
|
when "commits"
|
|
18
12
|
verify_commits(argv, raw: raw)
|
|
13
|
+
when "phase-completeness"
|
|
14
|
+
verify_phase_completeness(argv.first, raw: raw)
|
|
19
15
|
when "artifacts"
|
|
20
16
|
verify_artifacts(argv.first, raw: raw)
|
|
21
|
-
when "key-links"
|
|
22
|
-
verify_key_links(argv.first, raw: raw)
|
|
23
17
|
else
|
|
24
|
-
Output.error("Unknown verify subcommand. Available:
|
|
18
|
+
Output.error("Unknown verify subcommand. Available: commits, phase-completeness, artifacts")
|
|
25
19
|
end
|
|
26
20
|
end
|
|
27
21
|
|
|
28
|
-
def self.
|
|
29
|
-
|
|
30
|
-
case subcommand
|
|
31
|
-
when "consistency"
|
|
32
|
-
validate_consistency(raw: raw)
|
|
33
|
-
else
|
|
34
|
-
Output.error("Unknown validate subcommand. Available: consistency")
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def self.verify_summary(argv, raw: false)
|
|
39
|
-
summary_path = argv.shift
|
|
40
|
-
Output.error("summary-path required") unless summary_path
|
|
41
|
-
|
|
42
|
-
count_idx = argv.index("--check-count")
|
|
43
|
-
check_count = count_idx ? argv[count_idx + 1].to_i : 2
|
|
22
|
+
def self.verify_commits(argv, raw: false)
|
|
23
|
+
Output.error("At least one commit hash required") if argv.empty?
|
|
44
24
|
|
|
45
25
|
cwd = Dir.pwd
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
unless File.exist?(full_path)
|
|
49
|
-
result = {
|
|
50
|
-
passed: false,
|
|
51
|
-
checks: {
|
|
52
|
-
summary_exists: false,
|
|
53
|
-
files_created: { checked: 0, found: 0, missing: [] },
|
|
54
|
-
commits_exist: false,
|
|
55
|
-
self_check: "not_found"
|
|
56
|
-
},
|
|
57
|
-
errors: ["SUMMARY.md not found"]
|
|
58
|
-
}
|
|
59
|
-
Output.json(result, raw: raw, raw_value: "failed")
|
|
60
|
-
return
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
content = File.read(full_path)
|
|
64
|
-
errors = []
|
|
65
|
-
|
|
66
|
-
# Spot-check files mentioned in summary
|
|
67
|
-
mentioned_files = []
|
|
68
|
-
patterns = [
|
|
69
|
-
/`([^`]+\.[a-zA-Z]+)`/,
|
|
70
|
-
/(?:Created|Modified|Added|Updated|Edited):\s*`?([^\s`]+\.[a-zA-Z]+)`?/i
|
|
71
|
-
]
|
|
72
|
-
|
|
73
|
-
patterns.each do |pattern|
|
|
74
|
-
content.scan(pattern) do |match|
|
|
75
|
-
file_path = match[0]
|
|
76
|
-
if file_path && !file_path.start_with?("http") && file_path.include?("/")
|
|
77
|
-
mentioned_files << file_path unless mentioned_files.include?(file_path)
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
files_to_check = mentioned_files.first(check_count)
|
|
83
|
-
missing = files_to_check.reject { |f| File.exist?(File.join(cwd, f)) }
|
|
84
|
-
|
|
85
|
-
# Check commits exist
|
|
86
|
-
hashes = content.scan(/\b[0-9a-f]{7,40}\b/)
|
|
87
|
-
commits_exist = false
|
|
88
|
-
if hashes.any?
|
|
89
|
-
hashes.first(3).each do |hash|
|
|
90
|
-
result = exec_git(cwd, ["cat-file", "-t", hash])
|
|
91
|
-
if result[:exit_code] == 0 && result[:stdout].strip == "commit"
|
|
92
|
-
commits_exist = true
|
|
93
|
-
break
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
end
|
|
26
|
+
valid = []
|
|
27
|
+
invalid = []
|
|
97
28
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
elsif check_section.match?(/(?:all\s+)?(?:pass|✓|✅|complete|succeeded)/i)
|
|
105
|
-
self_check = "passed"
|
|
29
|
+
argv.each do |hash|
|
|
30
|
+
result = exec_git(cwd, ["cat-file", "-t", hash])
|
|
31
|
+
if result[:exit_code] == 0 && result[:stdout].strip == "commit"
|
|
32
|
+
valid << hash
|
|
33
|
+
else
|
|
34
|
+
invalid << hash
|
|
106
35
|
end
|
|
107
36
|
end
|
|
108
37
|
|
|
109
|
-
errors << "Missing files: #{missing.join(', ')}" if missing.any?
|
|
110
|
-
errors << "Referenced commit hashes not found in git history" if !commits_exist && hashes.any?
|
|
111
|
-
errors << "Self-check section indicates failure" if self_check == "failed"
|
|
112
|
-
|
|
113
|
-
checks = {
|
|
114
|
-
summary_exists: true,
|
|
115
|
-
files_created: { checked: files_to_check.length, found: files_to_check.length - missing.length, missing: missing },
|
|
116
|
-
commits_exist: commits_exist,
|
|
117
|
-
self_check: self_check
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
passed = missing.empty? && self_check != "failed"
|
|
121
|
-
Output.json({ passed: passed, checks: checks, errors: errors }, raw: raw, raw_value: passed ? "passed" : "failed")
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
def self.verify_plan_structure(file_path, raw: false)
|
|
125
|
-
Output.error("file path required") unless file_path
|
|
126
|
-
|
|
127
|
-
cwd = Dir.pwd
|
|
128
|
-
full_path = File.absolute_path?(file_path) ? file_path : File.join(cwd, file_path)
|
|
129
|
-
|
|
130
|
-
unless File.exist?(full_path)
|
|
131
|
-
Output.json({ error: "File not found", path: file_path }, raw: raw)
|
|
132
|
-
return
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
content = File.read(full_path)
|
|
136
|
-
fm = Frontmatter.extract(content)
|
|
137
|
-
errors = []
|
|
138
|
-
warnings = []
|
|
139
|
-
|
|
140
|
-
required = %w[phase plan type wave depends_on files_modified autonomous must_haves]
|
|
141
|
-
required.each do |field|
|
|
142
|
-
errors << "Missing required frontmatter field: #{field}" unless fm.key?(field)
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
# Parse task elements
|
|
146
|
-
tasks = []
|
|
147
|
-
content.scan(/<task[^>]*>([\s\S]*?)<\/task>/) do
|
|
148
|
-
task_content = ::Regexp.last_match(1)
|
|
149
|
-
name_match = task_content.match(/<name>([\s\S]*?)<\/name>/)
|
|
150
|
-
task_name = name_match ? name_match[1].strip : "unnamed"
|
|
151
|
-
has_files = task_content.include?("<files>")
|
|
152
|
-
has_action = task_content.include?("<action>")
|
|
153
|
-
has_verify = task_content.include?("<verify>")
|
|
154
|
-
has_done = task_content.include?("<done>")
|
|
155
|
-
|
|
156
|
-
errors << "Task missing <name> element" unless name_match
|
|
157
|
-
errors << "Task '#{task_name}' missing <action>" unless has_action
|
|
158
|
-
warnings << "Task '#{task_name}' missing <verify>" unless has_verify
|
|
159
|
-
warnings << "Task '#{task_name}' missing <done>" unless has_done
|
|
160
|
-
warnings << "Task '#{task_name}' missing <files>" unless has_files
|
|
161
|
-
|
|
162
|
-
tasks << { name: task_name, hasFiles: has_files, hasAction: has_action, hasVerify: has_verify, hasDone: has_done }
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
warnings << "No <task> elements found" if tasks.empty?
|
|
166
|
-
|
|
167
|
-
# Wave/depends_on consistency
|
|
168
|
-
if fm["wave"] && fm["wave"].to_i > 1 && (!fm["depends_on"] || (fm["depends_on"].is_a?(Array) && fm["depends_on"].empty?))
|
|
169
|
-
warnings << "Wave > 1 but depends_on is empty"
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
# Autonomous/checkpoint consistency
|
|
173
|
-
if content.match?(/<task\s+type=["']?checkpoint/) && fm["autonomous"] != "false" && fm["autonomous"] != false
|
|
174
|
-
errors << "Has checkpoint tasks but autonomous is not false"
|
|
175
|
-
end
|
|
176
|
-
|
|
177
38
|
Output.json({
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
frontmatter_fields: fm.keys
|
|
181
|
-
}, raw: raw, raw_value: errors.empty? ? "valid" : "invalid")
|
|
39
|
+
all_valid: invalid.empty?, valid: valid, invalid: invalid, total: argv.length
|
|
40
|
+
}, raw: raw, raw_value: invalid.empty? ? "valid" : "invalid")
|
|
182
41
|
end
|
|
183
42
|
|
|
184
43
|
def self.verify_phase_completeness(phase, raw: false)
|
|
@@ -216,70 +75,6 @@ module Ariadna
|
|
|
216
75
|
}, raw: raw, raw_value: errors.empty? ? "complete" : "incomplete")
|
|
217
76
|
end
|
|
218
77
|
|
|
219
|
-
def self.verify_references(file_path, raw: false)
|
|
220
|
-
Output.error("file path required") unless file_path
|
|
221
|
-
|
|
222
|
-
cwd = Dir.pwd
|
|
223
|
-
full_path = File.absolute_path?(file_path) ? file_path : File.join(cwd, file_path)
|
|
224
|
-
|
|
225
|
-
unless File.exist?(full_path)
|
|
226
|
-
Output.json({ error: "File not found", path: file_path }, raw: raw)
|
|
227
|
-
return
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
content = File.read(full_path)
|
|
231
|
-
found = []
|
|
232
|
-
missing = []
|
|
233
|
-
|
|
234
|
-
# @-references
|
|
235
|
-
content.scan(/@([^\s,)]+\/[^\s,)]+)/) do
|
|
236
|
-
ref = ::Regexp.last_match(1)
|
|
237
|
-
next if found.include?(ref) || missing.include?(ref)
|
|
238
|
-
|
|
239
|
-
resolved = if ref.start_with?("~/")
|
|
240
|
-
File.join(Dir.home, ref[2..])
|
|
241
|
-
else
|
|
242
|
-
File.join(cwd, ref)
|
|
243
|
-
end
|
|
244
|
-
File.exist?(resolved) ? found << ref : missing << ref
|
|
245
|
-
end
|
|
246
|
-
|
|
247
|
-
# Backtick file paths
|
|
248
|
-
content.scan(/`([^`]+\/[^`]+\.[a-zA-Z]{1,10})`/) do
|
|
249
|
-
ref = ::Regexp.last_match(1)
|
|
250
|
-
next if ref.start_with?("http") || ref.include?("${") || ref.include?("{{")
|
|
251
|
-
next if found.include?(ref) || missing.include?(ref)
|
|
252
|
-
|
|
253
|
-
resolved = File.join(cwd, ref)
|
|
254
|
-
File.exist?(resolved) ? found << ref : missing << ref
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
Output.json({
|
|
258
|
-
valid: missing.empty?, found: found.length, missing: missing, total: found.length + missing.length
|
|
259
|
-
}, raw: raw, raw_value: missing.empty? ? "valid" : "invalid")
|
|
260
|
-
end
|
|
261
|
-
|
|
262
|
-
def self.verify_commits(argv, raw: false)
|
|
263
|
-
Output.error("At least one commit hash required") if argv.empty?
|
|
264
|
-
|
|
265
|
-
cwd = Dir.pwd
|
|
266
|
-
valid = []
|
|
267
|
-
invalid = []
|
|
268
|
-
|
|
269
|
-
argv.each do |hash|
|
|
270
|
-
result = exec_git(cwd, ["cat-file", "-t", hash])
|
|
271
|
-
if result[:exit_code] == 0 && result[:stdout].strip == "commit"
|
|
272
|
-
valid << hash
|
|
273
|
-
else
|
|
274
|
-
invalid << hash
|
|
275
|
-
end
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
Output.json({
|
|
279
|
-
all_valid: invalid.empty?, valid: valid, invalid: invalid, total: argv.length
|
|
280
|
-
}, raw: raw, raw_value: invalid.empty? ? "valid" : "invalid")
|
|
281
|
-
end
|
|
282
|
-
|
|
283
78
|
def self.verify_artifacts(plan_file_path, raw: false)
|
|
284
79
|
Output.error("plan file path required") unless plan_file_path
|
|
285
80
|
|
|
@@ -301,7 +96,6 @@ module Ariadna
|
|
|
301
96
|
|
|
302
97
|
results = []
|
|
303
98
|
artifacts.each do |artifact|
|
|
304
|
-
next if artifact.is_a?(String)
|
|
305
99
|
next unless artifact.is_a?(Hash)
|
|
306
100
|
|
|
307
101
|
art_path = artifact["path"]
|
|
@@ -321,12 +115,6 @@ module Ariadna
|
|
|
321
115
|
if artifact["contains"] && !file_content.include?(artifact["contains"])
|
|
322
116
|
check[:issues] << "Missing pattern: #{artifact['contains']}"
|
|
323
117
|
end
|
|
324
|
-
if artifact["exports"]
|
|
325
|
-
exports = artifact["exports"].is_a?(Array) ? artifact["exports"] : [artifact["exports"]]
|
|
326
|
-
exports.each do |exp|
|
|
327
|
-
check[:issues] << "Missing export: #{exp}" unless file_content.include?(exp)
|
|
328
|
-
end
|
|
329
|
-
end
|
|
330
118
|
check[:passed] = check[:issues].empty?
|
|
331
119
|
else
|
|
332
120
|
check[:issues] << "File not found"
|
|
@@ -341,161 +129,6 @@ module Ariadna
|
|
|
341
129
|
}, raw: raw, raw_value: passed == results.length ? "valid" : "invalid")
|
|
342
130
|
end
|
|
343
131
|
|
|
344
|
-
def self.verify_key_links(plan_file_path, raw: false)
|
|
345
|
-
Output.error("plan file path required") unless plan_file_path
|
|
346
|
-
|
|
347
|
-
cwd = Dir.pwd
|
|
348
|
-
full_path = File.absolute_path?(plan_file_path) ? plan_file_path : File.join(cwd, plan_file_path)
|
|
349
|
-
|
|
350
|
-
unless File.exist?(full_path)
|
|
351
|
-
Output.json({ error: "File not found", path: plan_file_path }, raw: raw)
|
|
352
|
-
return
|
|
353
|
-
end
|
|
354
|
-
|
|
355
|
-
content = File.read(full_path)
|
|
356
|
-
key_links = parse_must_haves_block(content, "key_links")
|
|
357
|
-
|
|
358
|
-
if key_links.empty?
|
|
359
|
-
Output.json({ error: "No must_haves.key_links found in frontmatter", path: plan_file_path }, raw: raw)
|
|
360
|
-
return
|
|
361
|
-
end
|
|
362
|
-
|
|
363
|
-
results = []
|
|
364
|
-
key_links.each do |link|
|
|
365
|
-
next if link.is_a?(String)
|
|
366
|
-
next unless link.is_a?(Hash)
|
|
367
|
-
|
|
368
|
-
check = { from: link["from"], to: link["to"], via: link["via"] || "", verified: false, detail: "" }
|
|
369
|
-
source_content = safe_read_file(File.join(cwd, link["from"] || ""))
|
|
370
|
-
|
|
371
|
-
if !source_content
|
|
372
|
-
check[:detail] = "Source file not found"
|
|
373
|
-
elsif link["pattern"]
|
|
374
|
-
begin
|
|
375
|
-
regex = Regexp.new(link["pattern"])
|
|
376
|
-
if regex.match?(source_content)
|
|
377
|
-
check[:verified] = true
|
|
378
|
-
check[:detail] = "Pattern found in source"
|
|
379
|
-
else
|
|
380
|
-
target_content = safe_read_file(File.join(cwd, link["to"] || ""))
|
|
381
|
-
if target_content && regex.match?(target_content)
|
|
382
|
-
check[:verified] = true
|
|
383
|
-
check[:detail] = "Pattern found in target"
|
|
384
|
-
else
|
|
385
|
-
check[:detail] = "Pattern \"#{link['pattern']}\" not found in source or target"
|
|
386
|
-
end
|
|
387
|
-
end
|
|
388
|
-
rescue RegexpError
|
|
389
|
-
check[:detail] = "Invalid regex pattern: #{link['pattern']}"
|
|
390
|
-
end
|
|
391
|
-
elsif source_content.include?(link["to"] || "")
|
|
392
|
-
check[:verified] = true
|
|
393
|
-
check[:detail] = "Target referenced in source"
|
|
394
|
-
else
|
|
395
|
-
check[:detail] = "Target not referenced in source"
|
|
396
|
-
end
|
|
397
|
-
|
|
398
|
-
results << check
|
|
399
|
-
end
|
|
400
|
-
|
|
401
|
-
verified = results.count { |r| r[:verified] }
|
|
402
|
-
Output.json({
|
|
403
|
-
all_verified: verified == results.length, verified: verified, total: results.length, links: results
|
|
404
|
-
}, raw: raw, raw_value: verified == results.length ? "valid" : "invalid")
|
|
405
|
-
end
|
|
406
|
-
|
|
407
|
-
def self.validate_consistency(raw: false)
|
|
408
|
-
cwd = Dir.pwd
|
|
409
|
-
roadmap_path = File.join(cwd, ".ariadna_planning", "ROADMAP.md")
|
|
410
|
-
phases_dir = File.join(cwd, ".ariadna_planning", "phases")
|
|
411
|
-
errors = []
|
|
412
|
-
warnings = []
|
|
413
|
-
|
|
414
|
-
unless File.exist?(roadmap_path)
|
|
415
|
-
errors << "ROADMAP.md not found"
|
|
416
|
-
Output.json({ passed: false, errors: errors, warnings: warnings }, raw: raw, raw_value: "failed")
|
|
417
|
-
return
|
|
418
|
-
end
|
|
419
|
-
|
|
420
|
-
roadmap_content = File.read(roadmap_path)
|
|
421
|
-
|
|
422
|
-
# Extract phases from ROADMAP
|
|
423
|
-
roadmap_phases = []
|
|
424
|
-
roadmap_content.scan(/###\s*Phase\s+(\d+(?:\.\d+)?)\s*:/i) { roadmap_phases << ::Regexp.last_match(1) }
|
|
425
|
-
|
|
426
|
-
# Get phases on disk
|
|
427
|
-
disk_phases = []
|
|
428
|
-
if File.directory?(phases_dir)
|
|
429
|
-
Dir.children(phases_dir).each do |dir|
|
|
430
|
-
next unless File.directory?(File.join(phases_dir, dir))
|
|
431
|
-
|
|
432
|
-
dm = dir.match(/\A(\d+(?:\.\d+)?)/)
|
|
433
|
-
disk_phases << dm[1] if dm
|
|
434
|
-
end
|
|
435
|
-
end
|
|
436
|
-
|
|
437
|
-
# Cross-check
|
|
438
|
-
roadmap_phases.each do |p|
|
|
439
|
-
normalized = normalize_phase(p)
|
|
440
|
-
unless disk_phases.include?(p) || disk_phases.include?(normalized)
|
|
441
|
-
warnings << "Phase #{p} in ROADMAP.md but no directory on disk"
|
|
442
|
-
end
|
|
443
|
-
end
|
|
444
|
-
|
|
445
|
-
disk_phases.each do |p|
|
|
446
|
-
unpadded = p.to_i.to_s
|
|
447
|
-
unless roadmap_phases.include?(p) || roadmap_phases.include?(unpadded)
|
|
448
|
-
warnings << "Phase #{p} exists on disk but not in ROADMAP.md"
|
|
449
|
-
end
|
|
450
|
-
end
|
|
451
|
-
|
|
452
|
-
# Sequential numbering check
|
|
453
|
-
integer_phases = disk_phases.reject { |p| p.include?(".") }.map(&:to_i).sort
|
|
454
|
-
(1...integer_phases.length).each do |i|
|
|
455
|
-
if integer_phases[i] != integer_phases[i - 1] + 1
|
|
456
|
-
warnings << "Gap in phase numbering: #{integer_phases[i - 1]} \u2192 #{integer_phases[i]}"
|
|
457
|
-
end
|
|
458
|
-
end
|
|
459
|
-
|
|
460
|
-
# Plan numbering and orphans
|
|
461
|
-
if File.directory?(phases_dir)
|
|
462
|
-
Dir.children(phases_dir).sort.each do |dir|
|
|
463
|
-
dir_path = File.join(phases_dir, dir)
|
|
464
|
-
next unless File.directory?(dir_path)
|
|
465
|
-
|
|
466
|
-
phase_files = Dir.children(dir_path)
|
|
467
|
-
plans = phase_files.select { |f| f.end_with?("-PLAN.md") }.sort
|
|
468
|
-
summaries = phase_files.select { |f| f.end_with?("-SUMMARY.md") }
|
|
469
|
-
|
|
470
|
-
plan_nums = plans.filter_map { |p| m = p.match(/-(\d{2})-PLAN\.md$/); m ? m[1].to_i : nil }
|
|
471
|
-
(1...plan_nums.length).each do |i|
|
|
472
|
-
if plan_nums[i] != plan_nums[i - 1] + 1
|
|
473
|
-
warnings << "Gap in plan numbering in #{dir}: plan #{plan_nums[i - 1]} \u2192 #{plan_nums[i]}"
|
|
474
|
-
end
|
|
475
|
-
end
|
|
476
|
-
|
|
477
|
-
plan_ids = plans.map { |p| p.sub(/-PLAN\.md$/, "") }
|
|
478
|
-
summary_ids = summaries.map { |s| s.sub(/-SUMMARY\.md$/, "") }
|
|
479
|
-
summary_ids.each do |sid|
|
|
480
|
-
unless plan_ids.include?(sid)
|
|
481
|
-
warnings << "Summary #{sid}-SUMMARY.md in #{dir} has no matching PLAN.md"
|
|
482
|
-
end
|
|
483
|
-
end
|
|
484
|
-
|
|
485
|
-
# Check frontmatter wave field
|
|
486
|
-
plans.each do |plan|
|
|
487
|
-
content = File.read(File.join(dir_path, plan))
|
|
488
|
-
fm = Frontmatter.extract(content)
|
|
489
|
-
warnings << "#{dir}/#{plan}: missing 'wave' in frontmatter" unless fm["wave"]
|
|
490
|
-
end
|
|
491
|
-
end
|
|
492
|
-
end
|
|
493
|
-
|
|
494
|
-
passed = errors.empty?
|
|
495
|
-
Output.json({ passed: passed, errors: errors, warnings: warnings, warning_count: warnings.length },
|
|
496
|
-
raw: raw, raw_value: passed ? "passed" : "failed")
|
|
497
|
-
end
|
|
498
|
-
|
|
499
132
|
# --- Private helpers ---
|
|
500
133
|
|
|
501
134
|
def self.find_phase_internal(cwd, phase)
|
|
@@ -569,29 +202,18 @@ module Ariadna
|
|
|
569
202
|
|
|
570
203
|
if line.match?(/\A\s{6}-\s+/)
|
|
571
204
|
items << current if current
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
{ kv_match[1] => kv_match[2] }
|
|
579
|
-
else
|
|
580
|
-
{}
|
|
581
|
-
end
|
|
582
|
-
end
|
|
205
|
+
kv_match = line.match(/\A\s{6}-\s+(\w+):\s*"?([^"]*)"?\s*\z/)
|
|
206
|
+
current = if kv_match
|
|
207
|
+
{ kv_match[1] => kv_match[2] }
|
|
208
|
+
else
|
|
209
|
+
{}
|
|
210
|
+
end
|
|
583
211
|
elsif current.is_a?(Hash)
|
|
584
212
|
kv_match = line.match(/\A\s{8,}(\w+):\s*"?([^"]*)"?\s*\z/)
|
|
585
213
|
if kv_match
|
|
586
214
|
val = kv_match[2]
|
|
587
215
|
current[kv_match[1]] = val.match?(/\A\d+\z/) ? val.to_i : val
|
|
588
216
|
end
|
|
589
|
-
arr_match = line.match(/\A\s{10,}-\s+"?([^"]+)"?\s*\z/)
|
|
590
|
-
if arr_match && current.any?
|
|
591
|
-
last_key = current.keys.last
|
|
592
|
-
current[last_key] = current[last_key].is_a?(Array) ? current[last_key] : (current[last_key] ? [current[last_key]] : [])
|
|
593
|
-
current[last_key] << arr_match[1]
|
|
594
|
-
end
|
|
595
217
|
end
|
|
596
218
|
end
|
|
597
219
|
|
data/lib/ariadna/uninstaller.rb
CHANGED
|
@@ -13,6 +13,7 @@ module Ariadna
|
|
|
13
13
|
|
|
14
14
|
remove_commands
|
|
15
15
|
remove_agents
|
|
16
|
+
remove_skills
|
|
16
17
|
remove_content
|
|
17
18
|
remove_statusline
|
|
18
19
|
remove_patches
|
|
@@ -51,6 +52,14 @@ module Ariadna
|
|
|
51
52
|
puts " \u2713 Removed #{removed} agents" if removed > 0
|
|
52
53
|
end
|
|
53
54
|
|
|
55
|
+
def remove_skills
|
|
56
|
+
dir = File.join(@target_dir, "skills")
|
|
57
|
+
if File.directory?(dir)
|
|
58
|
+
FileUtils.rm_rf(dir)
|
|
59
|
+
puts " \u2713 Removed skills/"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
54
63
|
def remove_content
|
|
55
64
|
dir = File.join(@target_dir, "ariadna")
|
|
56
65
|
if File.directory?(dir)
|
data/lib/ariadna/version.rb
CHANGED
data/lib/ariadna.rb
CHANGED