ocak 0.6.0 → 0.6.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 21e35193d8d3fc3b9c24f7e75502749ce4f9a57352da20d9bb0c980c2191ad9a
4
- data.tar.gz: 2204cd3797a57348f3a3742fa17385375050d8b66fe37d4bdb466a4f603f31dd
3
+ metadata.gz: 7363b1ed65b047ead5c507fdc210dea94400edca689dff88200b6cbb2a666a5d
4
+ data.tar.gz: 39d9e7021f666cccc84643c7d66c00db9080adaab63d9163069ca963a030963c
5
5
  SHA512:
6
- metadata.gz: 7fddde997f1ba1c8f5e5e9db9e3f6dc9281ac1db82743b651f72f658967fcefeca67b05c5e338dbbaad9be798adae10f24807d1fec3ddd9975e72daba853f709
7
- data.tar.gz: 0d5f6f2f9e7300eb8b242e654296dd156192e4993976a40d0ebb050a1bf417e9b36e85f1b6b4f4e8e99cc1e375275be5175f6ef9489056c2dcc1d3025cffaa9e
6
+ metadata.gz: 53330c5ca283948f69a63aecc4c4b14866dd6c6fc9f8c2b5f46a2fb9f73572b92c81ff43df6c2c2fdd05ecc35135d5cd236bab8cd156527477e7da55995d12c7
7
+ data.tar.gz: dd9f956429a2b37f1aa6dec984795bdd752d28f80ea282bbc2709377e5777f11cad181dc9c64b97505037a544e053510b070f744860b1287788da5d6ebe88693
@@ -22,7 +22,7 @@ module Ocak
22
22
  end
23
23
 
24
24
  def build_merge_manager(logger:, issues:)
25
- if issues.is_a?(LocalIssueFetcher) && !gh_available?
25
+ if issues.is_a?(LocalIssueFetcher) && !@config.multi_repo? && !gh_available?
26
26
  LocalMergeManager.new(config: @config, logger: logger, issues: issues)
27
27
  else
28
28
  MergeManager.new(config: @config, claude: build_claude(logger), logger: logger,
@@ -73,12 +73,15 @@ module Ocak
73
73
  private
74
74
 
75
75
  def merger_prompt(issue_number, worktree)
76
- if worktree.target_repo
77
- "Create a PR and merge it for issue ##{issue_number}. Branch: #{worktree.branch}. " \
78
- 'Do NOT close any issues (the issue lives in a different repository).'
79
- else
80
- "Create a PR, merge it, and close issue ##{issue_number}. Branch: #{worktree.branch}"
81
- end
76
+ base = if worktree.target_repo
77
+ "Create a PR and merge it for issue ##{issue_number}. Branch: #{worktree.branch}. " \
78
+ 'Do NOT close any issues (the issue lives in a different repository).'
79
+ else
80
+ "Create a PR, merge it, and close issue ##{issue_number}. Branch: #{worktree.branch}"
81
+ end
82
+ issue_data = @issues.view(issue_number)
83
+ base += "\n\n<issue_data>\nTitle: #{issue_data['title']}\n\n#{issue_data['body']}\n</issue_data>" if issue_data
84
+ base
82
85
  end
83
86
 
84
87
  def log_and_nil(message)
@@ -38,6 +38,7 @@ module Ocak
38
38
  else
39
39
  "Create a PR, merge it, and close issue ##{issue_number}"
40
40
  end
41
+ prompt += merger_issue_context(issue_number, issues)
41
42
  claude.run_agent('merger', prompt, chdir: target_dir)
42
43
  end
43
44
  @state_machine.mark_completed(issue_number)
@@ -45,10 +46,10 @@ module Ocak
45
46
  end
46
47
  end
47
48
 
48
- def handle_single_manual_review(issue_number, logger:, claude:, issues: nil, chdir: @config.project_dir) # rubocop:disable Lint/UnusedMethodArgument
49
- claude.run_agent('merger',
50
- "Create a PR for issue ##{issue_number} but do NOT merge it and do NOT close the issue",
51
- chdir: chdir)
49
+ def handle_single_manual_review(issue_number, logger:, claude:, issues: nil, chdir: @config.project_dir)
50
+ prompt = "Create a PR for issue ##{issue_number} but do NOT merge it and do NOT close the issue"
51
+ prompt += merger_issue_context(issue_number, issues)
52
+ claude.run_agent('merger', prompt, chdir: chdir)
52
53
  @state_machine.mark_for_review(issue_number)
53
54
  logger.info("Issue ##{issue_number} PR created (manual review mode)")
54
55
  end
@@ -96,6 +97,13 @@ module Ocak
96
97
  logger.info("Posted audit comment on PR ##{pr_number}")
97
98
  end
98
99
 
100
+ def merger_issue_context(issue_number, issues)
101
+ issue_data = issues&.view(issue_number)
102
+ return '' unless issue_data
103
+
104
+ "\n\n<issue_data>\nTitle: #{issue_data['title']}\n\n#{issue_data['body']}\n</issue_data>"
105
+ end
106
+
99
107
  def pipeline_has_merge_step?
100
108
  @config.steps.any? { |s| s[:role].to_s == 'merge' || s['role'].to_s == 'merge' }
101
109
  end
@@ -44,6 +44,7 @@ module Ocak
44
44
 
45
45
  report = RunReport.new(complexity: complexity)
46
46
  state = build_initial_state(complexity, report)
47
+ state[:issue_data] = @issues&.view(issue_number)
47
48
  start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
48
49
  post_pipeline_start_comment(issue_number, state) if post_start_comment
49
50
 
data/lib/ocak/planner.rb CHANGED
@@ -14,14 +14,27 @@ module Ocak
14
14
  'merge' => 'Create a PR, merge it, and close issue #%<issue>s'
15
15
  }.freeze
16
16
 
17
- def build_step_prompt(role, issue_number, review_output)
18
- if role == 'fix'
19
- "Fix these review findings for issue ##{issue_number}:\n\n<review_output>\n#{review_output}\n</review_output>"
20
- elsif STEP_PROMPTS.key?(role)
21
- format(STEP_PROMPTS[role], issue: issue_number)
22
- else
23
- "Run #{role} for GitHub issue ##{issue_number}"
24
- end
17
+ ISSUE_CONTEXT_ROLES = %w[implement document merge].freeze
18
+
19
+ def build_step_prompt(role, issue_number, review_output, issue_data: nil)
20
+ prompt = if role == 'fix'
21
+ "Fix these review findings for issue ##{issue_number}:\n\n" \
22
+ "<review_output>\n#{review_output}\n</review_output>"
23
+ elsif STEP_PROMPTS.key?(role)
24
+ format(STEP_PROMPTS[role], issue: issue_number)
25
+ else
26
+ "Run #{role} for GitHub issue ##{issue_number}"
27
+ end
28
+
29
+ prompt += format_issue_context(issue_data) if issue_data && ISSUE_CONTEXT_ROLES.include?(role)
30
+ prompt
31
+ end
32
+
33
+ def format_issue_context(issue_data)
34
+ parts = []
35
+ parts << "Title: #{issue_data['title']}" if issue_data['title']
36
+ parts << issue_data['body'] if issue_data['body']
37
+ "\n\n<issue_data>\n#{parts.join("\n\n")}\n</issue_data>"
25
38
  end
26
39
 
27
40
  def plan_batches(issues, logger:, claude:)
@@ -18,7 +18,8 @@ module Ocak
18
18
  end
19
19
 
20
20
  result = execute_step(step, issue_number, state[:last_review_output], logger: logger, claude: claude,
21
- chdir: chdir)
21
+ chdir: chdir,
22
+ issue_data: state[:issue_data])
22
23
  state[:report].record_step(index: idx, agent: agent, role: role, status: 'completed', result: result)
23
24
  ctx = StateManagement::StepContext.new(issue_number, idx, role, result, state, logger, chdir)
24
25
  record_step_result(ctx, mutex: mutex)
@@ -37,12 +38,12 @@ module Ocak
37
38
  state[:steps_skipped] += 1
38
39
  end
39
40
 
40
- def execute_step(step, issue_number, review_output, logger:, claude:, chdir:)
41
+ def execute_step(step, issue_number, review_output, logger:, claude:, chdir:, issue_data: nil) # rubocop:disable Metrics/ParameterLists
41
42
  agent = step[:agent].to_s
42
43
  role = step[:role].to_s
43
44
  logger.info("--- Phase: #{role} (#{agent}) ---")
44
45
  post_step_comment(issue_number, "🔄 **Phase: #{role}** (#{agent})")
45
- prompt = build_step_prompt(role, issue_number, review_output)
46
+ prompt = build_step_prompt(role, issue_number, review_output, issue_data: issue_data)
46
47
  opts = { chdir: chdir }
47
48
  opts[:model] = step[:model].to_s if step[:model]
48
49
  claude.run_agent(agent.tr('_', '-'), prompt, **opts)
@@ -51,9 +52,8 @@ module Ocak
51
52
  def skip_reason(step, state)
52
53
  condition = step[:condition]
53
54
 
54
- return 'merge handled by MergeManager' if step[:role].to_s == 'merge' && @skip_merge
55
- return 'audit found blocking issues' if step[:role].to_s == 'merge' && @config.audit_mode && state[:audit_blocked]
56
- return 'manual review mode' if step[:role].to_s == 'merge' && @config.manual_review
55
+ return merge_skip_reason(state) if step[:role].to_s == 'merge'
56
+ return 'audit mode not enabled' if step[:role].to_s == 'audit' && !@config.audit_mode
57
57
  return 'fast-track issue (simple complexity)' if step[:complexity] == 'full' && state[:complexity] == 'simple'
58
58
  if condition == 'has_findings' && !state[:last_review_output]&.include?('🔴')
59
59
  return 'no blocking findings from review'
@@ -62,5 +62,13 @@ module Ocak
62
62
 
63
63
  nil
64
64
  end
65
+
66
+ def merge_skip_reason(state)
67
+ return 'merge handled by MergeManager' if @skip_merge
68
+ return 'audit found blocking issues' if @config.audit_mode && state[:audit_blocked]
69
+ return 'manual review mode' if @config.manual_review
70
+
71
+ nil
72
+ end
65
73
  end
66
74
  end
@@ -12,7 +12,7 @@ You implement GitHub issues. The issue is your single source of truth — everyt
12
12
  ## Before You Start
13
13
 
14
14
  1. Check branch state: `git log main..HEAD --oneline` and `git diff main --stat` — work may already be partially done
15
- 2. Read the issue via `gh issue view <number> --json title,body,labels`
15
+ 2. Read the issue data provided in your task prompt (in `<issue_data>` tags) — this is your source of truth for the issue title and body
16
16
  3. Read `CLAUDE.md` for project conventions
17
17
  4. Read every file listed in the issue's "Relevant files" and "Patterns to Follow" sections
18
18
  5. When writing new code, find the closest existing example first (neighboring file in the same directory/namespace) and mirror its structure exactly
@@ -39,10 +39,9 @@ If anything fails after conflict resolution, STOP and report the failures. Do no
39
39
 
40
40
  ### 2. Create Pull Request
41
41
 
42
- ```bash
43
- # Get the issue title and body for context
44
- gh issue view <number> --json title,body
42
+ Use the issue data provided in your task prompt (in `<issue_data>` tags) for the issue title and body context. Do NOT run `gh issue view` — the issue may live in a different repository.
45
43
 
44
+ ```bash
46
45
  # Get full diff for PR description
47
46
  git diff main --stat
48
47
  git log main..HEAD --oneline
data/lib/ocak.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ocak
4
- VERSION = '0.6.0'
4
+ VERSION = '0.6.1'
5
5
 
6
6
  def self.root
7
7
  File.expand_path('..', __dir__)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ocak
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Clay Harmon