docopslab-dev 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.adoc +904 -0
  4. data/assets/config-packs/actionlint/base.yml +13 -0
  5. data/assets/config-packs/actionlint/project.yml +13 -0
  6. data/assets/config-packs/htmlproofer/base.yml +27 -0
  7. data/assets/config-packs/htmlproofer/project.yml +25 -0
  8. data/assets/config-packs/rubocop/base.yml +130 -0
  9. data/assets/config-packs/rubocop/project.yml +8 -0
  10. data/assets/config-packs/shellcheck/base.shellcheckrc +14 -0
  11. data/assets/config-packs/subtxt/ai-asciidoc-antipatterns.sub.txt +11 -0
  12. data/assets/config-packs/vale/asciidoc/ExplicitSectionIDs.yml +8 -0
  13. data/assets/config-packs/vale/asciidoc/ExtraLineBeforeLevel1.yml +7 -0
  14. data/assets/config-packs/vale/asciidoc/OneSentencePerLine.yml +8 -0
  15. data/assets/config-packs/vale/asciidoc/PreferSourceBlocks.yml +8 -0
  16. data/assets/config-packs/vale/asciidoc/ProperAdmonitions.yml +8 -0
  17. data/assets/config-packs/vale/asciidoc/ProperDLs.yml +7 -0
  18. data/assets/config-packs/vale/asciidoc/UncleanListStart.yml +8 -0
  19. data/assets/config-packs/vale/authoring/ButParagraph.yml +8 -0
  20. data/assets/config-packs/vale/authoring/ExNotEg.yml +8 -0
  21. data/assets/config-packs/vale/authoring/LiteralTerms.yml +20 -0
  22. data/assets/config-packs/vale/authoring/Spelling.yml +679 -0
  23. data/assets/config-packs/vale/base.ini +38 -0
  24. data/assets/config-packs/vale/config/scripts/ExplicitSectionIDs.tengo +56 -0
  25. data/assets/config-packs/vale/config/scripts/ExtraLineBeforeLevel1.tengo +121 -0
  26. data/assets/config-packs/vale/config/scripts/OneSentencePerLine.tengo +53 -0
  27. data/assets/config-packs/vale/project.ini +5 -0
  28. data/assets/hooks/pre-commit +63 -0
  29. data/assets/hooks/pre-push +72 -0
  30. data/assets/scripts/adoc_section_ids.rb +50 -0
  31. data/assets/scripts/build-common.sh +193 -0
  32. data/assets/scripts/build-docker.sh +64 -0
  33. data/assets/scripts/build.sh +56 -0
  34. data/assets/scripts/parse_jekyll_asciidoc_logs.rb +467 -0
  35. data/assets/templates/Gemfile +7 -0
  36. data/assets/templates/Rakefile +3 -0
  37. data/assets/templates/gitignore +69 -0
  38. data/assets/templates/jekyll-asciidoc-fix.prompt.yml +17 -0
  39. data/assets/templates/spellcheck.prompt.yml +16 -0
  40. data/docopslab-dev.gemspec +56 -0
  41. data/docs/agent/AGENTS.md +229 -0
  42. data/docs/agent/index.md +80 -0
  43. data/docs/agent/missions/conduct-release.md +224 -0
  44. data/docs/agent/missions/setup-new-project.md +250 -0
  45. data/docs/agent/roles/devops-release-engineer.md +152 -0
  46. data/docs/agent/roles/docops-engineer.md +193 -0
  47. data/docs/agent/roles/planner-architect.md +74 -0
  48. data/docs/agent/roles/product-engineer.md +153 -0
  49. data/docs/agent/roles/product-manager.md +130 -0
  50. data/docs/agent/roles/project-manager.md +139 -0
  51. data/docs/agent/roles/qa-testing-engineer.md +115 -0
  52. data/docs/agent/roles/tech-docs-manager.md +143 -0
  53. data/docs/agent/roles/tech-writer.md +163 -0
  54. data/docs/agent/skills/asciidoc.md +609 -0
  55. data/docs/agent/skills/code-commenting.md +347 -0
  56. data/docs/agent/skills/fix-broken-links.md +309 -0
  57. data/docs/agent/skills/fix-jekyll-asciidoc-build-errors.md +23 -0
  58. data/docs/agent/skills/fix-spelling-issues.md +13 -0
  59. data/docs/agent/skills/git.md +170 -0
  60. data/docs/agent/skills/github-issues.md +135 -0
  61. data/docs/agent/skills/product-release-rollback-and-patching.md +71 -0
  62. data/docs/agent/skills/rake-cli-dev.md +57 -0
  63. data/docs/agent/skills/readme-driven-dev.md +13 -0
  64. data/docs/agent/skills/release-history.md +29 -0
  65. data/docs/agent/skills/ruby.md +192 -0
  66. data/docs/agent/skills/schemagraphy-sgyml.md +18 -0
  67. data/docs/agent/skills/tests-running.md +25 -0
  68. data/docs/agent/skills/tests-writing.md +45 -0
  69. data/docs/agent/skills/write-the-docs.md +54 -0
  70. data/docs/agent/topics/common-project-paths.md +117 -0
  71. data/docs/agent/topics/dev-tooling-usage.md +202 -0
  72. data/docs/agent/topics/devops-ci-cd.md +55 -0
  73. data/docs/agent/topics/product-docs-deployment.md +25 -0
  74. data/lib/docopslab/dev/auto_fix_asciidoc.rb +46 -0
  75. data/lib/docopslab/dev/checkers.rb +108 -0
  76. data/lib/docopslab/dev/config_manager.rb +241 -0
  77. data/lib/docopslab/dev/file_utils.rb +140 -0
  78. data/lib/docopslab/dev/git_hooks.rb +140 -0
  79. data/lib/docopslab/dev/help.rb +121 -0
  80. data/lib/docopslab/dev/initializer.rb +95 -0
  81. data/lib/docopslab/dev/linters.rb +451 -0
  82. data/lib/docopslab/dev/log_parser.rb +31 -0
  83. data/lib/docopslab/dev/paths.rb +46 -0
  84. data/lib/docopslab/dev/script_manager.rb +136 -0
  85. data/lib/docopslab/dev/spell_check.rb +194 -0
  86. data/lib/docopslab/dev/sync_ops.rb +468 -0
  87. data/lib/docopslab/dev/tasks.rb +440 -0
  88. data/lib/docopslab/dev/tool_execution.rb +68 -0
  89. data/lib/docopslab/dev/version.rb +8 -0
  90. data/lib/docopslab/dev.rb +392 -0
  91. data/specs/data/default-manifest.yml +64 -0
  92. data/specs/data/manifest-schema.yaml +63 -0
  93. data/specs/data/tasks-def.yml +321 -0
  94. data/specs/data/tools.yml +60 -0
  95. metadata +362 -0
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+
5
+ module DocOpsLab
6
+ module Dev
7
+ module ScriptManager
8
+ class << self
9
+ def sync_scripts
10
+ puts '📜 Syncing common scripts from DocOps Lab...'
11
+
12
+ unless Dir.exist?(SCRIPTS_SOURCE_DIR)
13
+ puts '❌ No scripts directory found in gem'
14
+ return false
15
+ end
16
+
17
+ # Ensure vendor scripts directory exists
18
+ vendor_scripts_dir = File.join('scripts', '.vendor', 'docopslab')
19
+ FileUtils.mkdir_p(vendor_scripts_dir)
20
+
21
+ synced_count = 0
22
+
23
+ Dir.glob("#{SCRIPTS_SOURCE_DIR}/*").each do |script_path|
24
+ next unless File.file?(script_path)
25
+
26
+ script_name = File.basename(script_path)
27
+ dest_path = File.join(vendor_scripts_dir, script_name)
28
+
29
+ # Check if file needs updating
30
+ if !File.exist?(dest_path) || File.read(script_path) != File.read(dest_path)
31
+ FileUtils.cp(script_path, dest_path)
32
+ File.chmod(0o755, dest_path) # Make executable
33
+ puts " 📜 Synced: #{dest_path} (executable)"
34
+ synced_count += 1
35
+ else
36
+ puts " ✅ Up to date: #{dest_path}"
37
+ end
38
+ end
39
+
40
+ if synced_count.positive?
41
+ puts "✅ Script sync complete; #{synced_count} files updated"
42
+ else
43
+ puts '✅ All scripts up to date'
44
+ end
45
+
46
+ true
47
+ end
48
+
49
+ def list_script_templates
50
+ unless Dir.exist?(SCRIPTS_SOURCE_DIR)
51
+ puts '❌ No scripts directory found in gem'
52
+ return false
53
+ end
54
+
55
+ puts '📜 Available script templates:'
56
+
57
+ Dir.glob("#{SCRIPTS_SOURCE_DIR}/*").each do |script_path|
58
+ next unless File.file?(script_path)
59
+
60
+ script_name = File.basename(script_path)
61
+
62
+ # Try to extract description from script comments
63
+ description = 'No description available'
64
+ if File.readable?(script_path)
65
+ File.open(script_path, 'r') do |f|
66
+ f.each_line do |line|
67
+ if line.match(/^#\s*(.+)$/) && !line.include?('!/bin/')
68
+ description = line.match(/^#\s*(.+)$/)[1]
69
+ break
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ puts " • #{script_name}: #{description}"
76
+ end
77
+
78
+ true
79
+ end
80
+
81
+ def run_script script_name, args=[]
82
+ unless script_name
83
+ puts '❌ Script name is required'
84
+ puts 'Usage: bundle exec rake labdev:run[script_name] -- [args]'
85
+ return false
86
+ end
87
+
88
+ # Add .sh extension if NO extension provided
89
+ # (valid extensions are .sh, .rb, py, .js)
90
+ script_name += '.sh' unless File.extname(script_name).length.positive?
91
+
92
+ # Look for local script first
93
+ project_script = File.join('scripts', script_name)
94
+ vendor_script = File.join('scripts', '.vendor', 'docopslab', script_name)
95
+
96
+ script_to_run = nil
97
+ if File.exist?(project_script)
98
+ puts "📜 Running local script: #{project_script}"
99
+ script_to_run = project_script
100
+ elsif File.exist?(vendor_script)
101
+ puts "📜 Running vendor script: #{vendor_script}"
102
+ script_to_run = vendor_script
103
+ else
104
+ puts "❌ Script not found: #{script_name}"
105
+ puts 'Searched in:'
106
+ puts " - #{project_script}"
107
+ puts " - #{vendor_script}"
108
+ return false
109
+ end
110
+
111
+ # Run the script using proper runtime and args
112
+ case File.extname(script_to_run)
113
+ when '.sh', ''
114
+ cmd = ['bash']
115
+ when '.rb'
116
+ cmd = ['ruby']
117
+ when '.py'
118
+ cmd = ['python3']
119
+ when '.js'
120
+ cmd = ['node']
121
+ else
122
+ puts "❌ Unsupported script extension: #{File.extname(script_to_run)}"
123
+ return false
124
+ end
125
+ cmd << script_to_run
126
+ cmd.concat(args) if args.any?
127
+ puts "🚀 Executing: #{cmd.join(' ')}"
128
+
129
+ system(*cmd)
130
+
131
+ $CHILD_STATUS.success?
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'time'
5
+ require 'fileutils'
6
+
7
+ module DocOpsLab
8
+ module Dev
9
+ # SpellCheck functionality for parsing Vale logs and managing spelling corrections
10
+ module SpellCheck
11
+ def self.spelling_config_path
12
+ File.join(GEM_ROOT, 'assets', 'config-packs', 'vale', 'authoring', 'Spelling.yml')
13
+ end
14
+
15
+ class << self
16
+ def generate_spellcheck_report file_path=nil
17
+ puts '📝 Generating spellcheck report from Vale output...'
18
+
19
+ manifest = DocOpsLab::Dev.load_manifest
20
+ spellcheck_config = manifest['spellcheck'] || {}
21
+ output_dir = spellcheck_config['output_dir'] || '.agent/reports/'
22
+ FileUtils.mkdir_p(output_dir)
23
+ output_file = spellcheck_config['output_file'] || nil
24
+ prompt = spellcheck_config['prompt'] || nil
25
+
26
+ unless defined?(output_file) && output_file
27
+ timestamp = Time.now.strftime('%Y%m%d_%H%M%S')
28
+ output_file = "spellcheck-#{timestamp}.yml"
29
+ end
30
+
31
+ output_path = File.join(output_dir, output_file)
32
+
33
+ # Run Vale with JSON output and spelling filter
34
+ # Pass just the rule name; filter builder will add .Name== wrapper
35
+ # If file_path specified, only check that file
36
+ vale_json = DocOpsLab::Dev.run_vale(
37
+ file_path,
38
+ '',
39
+ output_format: :json,
40
+ filter: 'DocOpsLab-Authoring.Spelling')
41
+ return false if vale_json.nil?
42
+
43
+ # Parse JSON output for spelling issues
44
+ spelling_issues = parse_vale_json_output(vale_json)
45
+
46
+ if spelling_issues.empty?
47
+ puts '✅ No spelling issues found!'
48
+ return true
49
+ end
50
+
51
+ # Generate YAML report
52
+ generate_yaml_report(spelling_issues, output_path, prompt)
53
+
54
+ puts "📄 SpellCheck report generated: #{output_path}"
55
+ puts "Found #{spelling_issues.length} spelling issues to review"
56
+ true
57
+ end
58
+
59
+ private
60
+
61
+ def parse_vale_json_output json_output
62
+ require 'json'
63
+
64
+ issues = []
65
+ seen_terms = {}
66
+
67
+ begin
68
+ vale_data = JSON.parse(json_output)
69
+ rescue JSON::ParserError => e
70
+ puts "❌ Failed to parse Vale JSON output: #{e.message}"
71
+ return []
72
+ end
73
+
74
+ # Vale JSON structure: { "file_path" => [{ "Check" => "rule", "Message" => "message", "Line" => num, "Span" => [start, end], "Severity" => "level", "Match" => "word" }] }
75
+ vale_data.each do |file_path, file_issues|
76
+ file_issues.each do |issue|
77
+ rule = issue['Check']
78
+ issue['Message']
79
+ line_num = issue['Line']
80
+ span = issue['Span']
81
+ misspelled_word = issue['Match']
82
+
83
+ # Only process spelling issues (should be filtered already, but double-check)
84
+ next unless rule&.include?('Spelling')
85
+
86
+ # Skip if no misspelled word found
87
+ next unless misspelled_word && !misspelled_word.empty?
88
+
89
+ term = misspelled_word
90
+ col = span ? span[0] : 1
91
+
92
+ # Get context around the issue
93
+ context_text = get_line_context(file_path, line_num, col, term)
94
+
95
+ issue_entry = {
96
+ 'term' => term,
97
+ 'path' => file_path,
98
+ 'text' => context_text,
99
+ 'line' => "#{line_num},#{col}",
100
+ 'fix?' => nil
101
+ }
102
+
103
+ # Add 'all?' field only for the first occurrence of each term
104
+ unless seen_terms[term]
105
+ issue_entry['all?'] = nil
106
+ seen_terms[term] = true
107
+ end
108
+
109
+ issues << issue_entry
110
+ end
111
+ end
112
+
113
+ issues
114
+ end
115
+
116
+ def get_line_context file_path, line_num, _col, term
117
+ return '' unless File.exist?(file_path)
118
+
119
+ begin
120
+ lines = File.readlines(file_path)
121
+ target_line = lines[line_num - 1]
122
+ return '' unless target_line
123
+
124
+ # Get some words around the term for context
125
+ # Find the term in the line and extract surrounding words
126
+ words = target_line.split(/\s+/)
127
+ term_index = words.find_index { |word| word.include?(term) }
128
+
129
+ if term_index
130
+ # Get 3 words before and after the term
131
+ start_idx = [term_index - 3, 0].max
132
+ end_idx = [term_index + 3, words.length - 1].min
133
+ context_words = words[start_idx..end_idx]
134
+ context_words.join(' ')
135
+ else
136
+ # Fallback: just return the line trimmed
137
+ target_line.strip[0..80] + (target_line.length > 80 ? '...' : '')
138
+ end
139
+ rescue StandardError => e
140
+ puts "⚠️ Could not read context from #{file_path}: #{e.message}"
141
+ ''
142
+ end
143
+ end
144
+
145
+ def generate_yaml_report errors, output_file, agent_prompt=nil
146
+ require 'erb'
147
+
148
+ unless agent_prompt
149
+ template_path = File.join(TEMPLATES_DIR, 'spellcheck.prompt.yml')
150
+ agent_prompt = File.read(template_path) if File.exist?(template_path)
151
+ end
152
+
153
+ # Create the YAML content with ERB templating for better formatting
154
+ template = ERB.new(yaml_template)
155
+
156
+ yaml_content = template.result_with_hash(errors: errors, agent_prompt: agent_prompt)
157
+
158
+ File.write(output_file, yaml_content)
159
+ end
160
+
161
+ def yaml_template
162
+ <<~TEMPLATE
163
+ # SpellCheck Report
164
+ #
165
+ # User Instructions:
166
+ # For each entry, enter a fix?: value of:
167
+ # 'no' or 'n' to skip fixing for now (deleting the entry is more efficient)
168
+ # 'add' or 'd' for add to dictionary
169
+ # use add("[cC]orrected term") to indicate verbatim text to add
170
+ # use docops("term") or nontech("term") to better organize additions
171
+ # 'fix' if it's a typo to be corrected
172
+ # 'fix("corrected")' where corrected is the intended text
173
+ # 'pass' will wrap in <!-- vale off -->', '<!-- vale on -->'
174
+ #
175
+ # After editing this file, use an AI agent to process the fixes.
176
+ #
177
+ ---
178
+ <% errors.each do |issue| %>
179
+ - term: "<%= issue['term'] %>"
180
+ path: <%= issue['path'] %>
181
+ text: |
182
+ <%= issue['text'] %>
183
+ line: [<%= issue['line'] %>]
184
+ fix?:#{' '}
185
+ <% end %>
186
+ #
187
+ # AI Agent Instructions:
188
+ <%= agent_prompt %>
189
+ TEMPLATE
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end