carson 3.3.0 → 3.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 91c767b37c33c1d69a377f509b5503ef342fef4ff4f3629439e998938d99b24b
4
- data.tar.gz: 2b615ad6df7610917d400166a5d735c9bc3f7e51b52eeda33d48446be9916d9c
3
+ metadata.gz: 514509a6422a4c2237a48feac1bff1c10e4705afe62fb574d400b8364cf131ba
4
+ data.tar.gz: 1f7bbf53a01ee92d8a97d62d0a7957bb823c0aabd70e31a7e27e37de8ab3b8c6
5
5
  SHA512:
6
- metadata.gz: df671d4ed658b1aa98290cfcdbc5cd47c78c702d83254bdc4e79d9ed5755320b3cd9c08f664e20633830661506fe8a0cdcb0f8c03e1314623a6074a444f7d23d
7
- data.tar.gz: e05a41a7fb961deac1f63eccb77b8d03eb7d694a557baebaead88f5f38d9bab91a1b4643f71b4bf09741e51946ab2c6707a288e7a22fe2ea981ee5769e370b2f
6
+ metadata.gz: a536b8a85ba6b2a737ce913485e3d58b5672b6b13af40261b181dd234bf1052ffa93a5f9b51307d9ce7e433e588b92ce294e88118b758747d4bb5cbad898c6c5
7
+ data.tar.gz: d476ba9ed57c7c97967c0cb2fab8b8390debfa196d657ae6d2e6c51e143248f8160bc543eb0d9b4e5c3c434ba4a25aa16b8af9aa6ec3b9cfcf5b7c974cd8fa9e
data/RELEASE.md CHANGED
@@ -5,6 +5,23 @@ Release-note scope rule:
5
5
  - `RELEASE.md` records only version deltas, breaking changes, and migration actions.
6
6
  - Operational usage guides live in `MANUAL.md` and `API.md`.
7
7
 
8
+ ## 3.4.0 — Audit JSON
9
+
10
+ ### What changed
11
+
12
+ - **`carson audit --json`** — machine-readable JSON output for the audit command. The JSON envelope includes `command`, `status`, `branch`, `hooks`, `main_sync`, `pr`, `checks`, `baseline`, `problems`, and `exit_code`. Agents can parse audit results programmatically instead of regex-matching human-readable text.
13
+
14
+ ### UX
15
+
16
+ - JSON output is only produced when `--json` is passed; default human-readable output is unchanged.
17
+ - The `problems` array contains the same concise problem strings shown in non-verbose human output, making it easy for agents to surface actionable issues.
18
+ - `hooks.status` is `"ok"` or `"mismatch"`, `main_sync.status` is `"ok"`, `"ahead"`, `"behind"`, or `"unknown"`.
19
+
20
+ ### Migration
21
+
22
+ - No breaking changes. `carson audit` without `--json` behaves identically to 3.3.0.
23
+ - The `audit!` method now accepts `json_output:` keyword argument (default `false`).
24
+
8
25
  ## 3.3.0 — Deliver Refinements
9
26
 
10
27
  ### What changed
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.3.0
1
+ 3.4.0
data/lib/carson/cli.rb CHANGED
@@ -53,7 +53,7 @@ module Carson
53
53
 
54
54
  def self.build_parser
55
55
  OptionParser.new do |opts|
56
- opts.banner = "Usage: carson [status [--json]|setup|audit|sync|deliver [--merge] [--json] [--title T] [--body-file F]|prune [--all]|worktree create|done|remove <name>|onboard|refresh [--all]|offboard|template check|apply|review gate|sweep|govern [--dry-run] [--json] [--loop SECONDS]|version]"
56
+ opts.banner = "Usage: carson [status [--json]|setup|audit [--json]|sync|deliver [--merge] [--json] [--title T] [--body-file F]|prune [--all]|worktree create|done|remove <name>|onboard|refresh [--all]|offboard|template check|apply|review gate|sweep|govern [--dry-run] [--json] [--loop SECONDS]|version]"
57
57
  end
58
58
  end
59
59
 
@@ -88,6 +88,8 @@ module Carson
88
88
  parse_worktree_subcommand( argv: argv, parser: parser, err: err )
89
89
  when "review"
90
90
  parse_named_subcommand( command: command, usage: "gate|sweep", argv: argv, parser: parser, err: err )
91
+ when "audit"
92
+ parse_audit_command( argv: argv, err: err )
91
93
  when "status"
92
94
  parse_status_command( argv: argv, err: err )
93
95
  when "deliver"
@@ -242,6 +244,15 @@ module Carson
242
244
  { command: :invalid }
243
245
  end
244
246
 
247
+ def self.parse_audit_command( argv:, err: )
248
+ json_flag = argv.delete( "--json" ) ? true : false
249
+ unless argv.empty?
250
+ err.puts "#{BADGE} Unexpected arguments for audit: #{argv.join( ' ' )}"
251
+ return { command: :invalid }
252
+ end
253
+ { command: "audit", json: json_flag }
254
+ end
255
+
245
256
  def self.parse_status_command( argv:, err: )
246
257
  json_flag = argv.delete( "--json" ) ? true : false
247
258
  unless argv.empty?
@@ -321,7 +332,7 @@ module Carson
321
332
  when "setup"
322
333
  runtime.setup!( cli_choices: parsed.fetch( :cli_choices, {} ) )
323
334
  when "audit"
324
- runtime.audit!
335
+ runtime.audit!( json_output: parsed.fetch( :json, false ) )
325
336
  when "sync"
326
337
  runtime.sync!
327
338
  when "prune"
@@ -1,13 +1,20 @@
1
+ # Pre-commit audit — checks hooks, main sync, PR checks, and CI baseline.
2
+ # Exits with EXIT_BLOCK when policy violations are found.
3
+ # Supports --json for machine-readable structured output.
1
4
  require "cgi"
2
5
 
3
6
  module Carson
4
7
  class Runtime
5
8
  module Audit
6
- def audit!
9
+ def audit!( json_output: false )
7
10
  fingerprint_status = block_if_outsider_fingerprints!
8
11
  return fingerprint_status unless fingerprint_status.nil?
9
12
  unless head_exists?
10
- puts_line "No commits yet — audit skipped for initial commit."
13
+ if json_output
14
+ out.puts JSON.pretty_generate( { command: "audit", status: "skipped", reason: "no commits yet", exit_code: EXIT_OK } )
15
+ else
16
+ puts_line "No commits yet — audit skipped for initial commit."
17
+ end
11
18
  return EXIT_OK
12
19
  end
13
20
  audit_state = "ok"
@@ -22,6 +29,7 @@ module Carson
22
29
  puts_verbose ""
23
30
  puts_verbose "[Hooks]"
24
31
  hooks_ok = hooks_health_report
32
+ hooks_status = hooks_ok ? "ok" : "mismatch"
25
33
  unless hooks_ok
26
34
  audit_state = "block"
27
35
  audit_concise_problems << "Hooks: mismatch — run carson refresh."
@@ -29,23 +37,27 @@ module Carson
29
37
  puts_verbose ""
30
38
  puts_verbose "[Main Sync Status]"
31
39
  ahead_count, behind_count, main_error = main_sync_counts
40
+ main_sync = { ahead: 0, behind: 0, status: "ok" }
32
41
  if main_error
33
42
  puts_verbose "main_vs_remote_main: unknown"
34
43
  puts_verbose "WARN: unable to calculate main sync status (#{main_error})."
35
44
  audit_state = "attention" if audit_state == "ok"
36
45
  audit_concise_problems << "Main sync: unable to determine — check remote connectivity."
46
+ main_sync = { ahead: 0, behind: 0, status: "unknown", error: main_error }
37
47
  elsif ahead_count.positive?
38
48
  puts_verbose "main_vs_remote_main_ahead: #{ahead_count}"
39
49
  puts_verbose "main_vs_remote_main_behind: #{behind_count}"
40
50
  puts_verbose "ACTION: local #{config.main_branch} is ahead of #{config.git_remote}/#{config.main_branch} by #{ahead_count} commit#{plural_suffix( count: ahead_count )}; reset local drift before commit/push workflows."
41
51
  audit_state = "block"
42
52
  audit_concise_problems << "Main sync (#{config.git_remote}): ahead by #{ahead_count} — git fetch #{config.git_remote}, or carson setup to switch remote."
53
+ main_sync = { ahead: ahead_count, behind: behind_count, status: "ahead" }
43
54
  elsif behind_count.positive?
44
55
  puts_verbose "main_vs_remote_main_ahead: #{ahead_count}"
45
56
  puts_verbose "main_vs_remote_main_behind: #{behind_count}"
46
57
  puts_verbose "ACTION: local #{config.main_branch} is behind #{config.git_remote}/#{config.main_branch} by #{behind_count} commit#{plural_suffix( count: behind_count )}; run carson sync."
47
58
  audit_state = "attention" if audit_state == "ok"
48
59
  audit_concise_problems << "Main sync (#{config.git_remote}): behind by #{behind_count} — run carson sync."
60
+ main_sync = { ahead: ahead_count, behind: behind_count, status: "behind" }
49
61
  else
50
62
  puts_verbose "main_vs_remote_main_ahead: 0"
51
63
  puts_verbose "main_vs_remote_main_behind: 0"
@@ -109,15 +121,40 @@ module Carson
109
121
  audit_status: audit_state
110
122
  )
111
123
  )
112
- puts_verbose ""
113
- puts_verbose "[Audit Result]"
114
- puts_verbose "status: #{audit_state}"
115
- puts_verbose( audit_state == "block" ? "ACTION: local policy block must be resolved before commit/push." : "ACTION: no local hard block detected." )
116
- unless verbose?
117
- audit_concise_problems.each { |problem| puts_line problem }
118
- puts_line "Audit: #{audit_state}"
124
+ exit_code = audit_state == "block" ? EXIT_BLOCK : EXIT_OK
125
+
126
+ if json_output
127
+ result = {
128
+ command: "audit",
129
+ status: audit_state,
130
+ branch: current_branch,
131
+ hooks: { status: hooks_status },
132
+ main_sync: main_sync,
133
+ pr: monitor_report[ :pr ],
134
+ checks: monitor_report.fetch( :checks ),
135
+ baseline: {
136
+ status: default_branch_baseline.fetch( :status ),
137
+ repository: default_branch_baseline[ :repository ],
138
+ failing_count: default_branch_baseline.fetch( :failing_count ),
139
+ pending_count: default_branch_baseline.fetch( :pending_count ),
140
+ advisory_failing_count: default_branch_baseline.fetch( :advisory_failing_count ),
141
+ advisory_pending_count: default_branch_baseline.fetch( :advisory_pending_count )
142
+ },
143
+ problems: audit_concise_problems,
144
+ exit_code: exit_code
145
+ }
146
+ out.puts JSON.pretty_generate( result )
147
+ else
148
+ puts_verbose ""
149
+ puts_verbose "[Audit Result]"
150
+ puts_verbose "status: #{audit_state}"
151
+ puts_verbose( audit_state == "block" ? "ACTION: local policy block must be resolved before commit/push." : "ACTION: no local hard block detected." )
152
+ unless verbose?
153
+ audit_concise_problems.each { |problem| puts_line problem }
154
+ puts_line "Audit: #{audit_state}"
155
+ end
119
156
  end
120
- audit_state == "block" ? EXIT_BLOCK : EXIT_OK
157
+ exit_code
121
158
  end
122
159
 
123
160
  private
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: carson
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.0
4
+ version: 3.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Hailei Wang