Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,4 @@ orca-dashboard-ui/

.skynex/production-gate.json
.skynex/audit.log
.orchestrator/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ brew tap christopherkarani/orca && brew install --formula orca
# Or install with the official script
curl -fsSL https://raw.githubusercontent.com/christopherkarani/Orca/main/scripts/install.sh | sh

# Or build from source (Zig 0.15.2)
# Or build from source (Zig 0.16.0 — the current star version)
zig build
```

Expand Down
2 changes: 1 addition & 1 deletion build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
.name = .orca,
.version = "1.1.4",
.fingerprint = 0x6111eddd14dbd5f4, // Changing this has security and trust implications.
.minimum_zig_version = "0.15.2",
.minimum_zig_version = "0.16.0",
.dependencies = .{},
.paths = .{
"build.zig",
Expand Down
2 changes: 1 addition & 1 deletion docs/install.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ zig build
./zig-out/bin/orca version --json
```

Use Zig `0.15.2`.
Use Zig `0.16.0` (the current star / minimum supported version). Older 0.15.x may still work for a transition period but is no longer the target.

## Release Artifacts

Expand Down
2 changes: 1 addition & 1 deletion docs/integrations/current-baseline.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
> Generated: 2026-05-09
> Branch: `phase-35-edge-network-telemetry-data-guard`
> Commit: `5b271b9` (Phase 35 committed)
> Zig version: 0.15.2
> Zig version: 0.16.0 (star)
> Version: 1.1.0

---
Expand Down
2 changes: 1 addition & 1 deletion docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ zig build
./zig-out/bin/orca version --json
```

The repository is pinned to Zig `0.15.2`. Release installs are covered in [install.md](install.md).
The repository is pinned to Zig `0.16.0` (the new star version). Release installs are covered in [install.md](install.md).

## 2. Initialize A Policy

Expand Down
107 changes: 100 additions & 7 deletions src/cli/doctor.zig
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,16 @@ const IntegrationContext = struct {
};

pub fn command(argv: []const []const u8, stdout: anytype, stderr: anytype) !u8 {
var verbose = false;
for (argv) |arg| {
if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) {
_ = try help.writeCommand(stdout, "doctor");
return exit_codes.success;
}
if (std.mem.eql(u8, arg, "-v") or std.mem.eql(u8, arg, "--verbose")) {
verbose = true;
continue;
}
try stderr.print("orca doctor: unknown option '{s}'.\n", .{arg});
return exit_codes.usage;
}
Expand All @@ -90,12 +95,59 @@ pub fn command(argv: []const []const u8, stdout: anytype, stderr: anytype) !u8 {
return exit_codes.general;
};
defer context.deinit();
try writeReport(stdout, os, backend_report, context);
try writeReport(stdout, os, backend_report, context, verbose);
return exit_codes.success;
}

fn writeReport(stdout: anytype, os: core.platform.Os, backend_report: sandbox.backend.ReportSet, context: IntegrationContext) !void {
fn writeReport(stdout: anytype, os: core.platform.Os, backend_report: sandbox.backend.ReportSet, context: IntegrationContext, verbose: bool) !void {
try stdout.writeAll("Orca Doctor\n\n");

// Phase 3: always compute and print the one-line summary
var active_count: usize = 0;
var limited_count: usize = 0;
var unavailable_count: usize = 0;
for (doctor_capabilities) |item| {
if (item.feature) |f| {
const lvl = backend_report.get(f).level;
switch (lvl) {
.active => active_count += 1,
.limited, .partial, .observe_only, .wrapper_only => limited_count += 1,
.unavailable, .unsupported, .failed => unavailable_count += 1,
}
} else if (item.capability) |cap| {
const st = core.platform.reportCapability(os, cap).state;
switch (st) {
.active => active_count += 1,
.limited, .partial => limited_count += 1,
.unavailable => unavailable_count += 1,
// other states (if any) fall through as limited for summary purposes
else => limited_count += 1,
}
}
}

const policy_status = if (!context.policy_present)
"no policy"
else if (!context.policy_valid)
"policy invalid"
else
"policy valid";

try stdout.print("Summary: {s} · {d} active · {d} limited · {d} unavailable · {s}\n\n", .{
os.toString(),
active_count,
limited_count,
unavailable_count,
policy_status,
});

if (!verbose) {
// Brief mode: summary + recommendations only (Phase 3 default)
try writeRecommendations(stdout, context);
return;
}

// Verbose: full previous report
try stdout.print("OS: {s}\n", .{os.toString()});
try stdout.print("Version: {s}\n\n", .{cli.version});
try writeIntegrationReport(stdout, context);
Expand Down Expand Up @@ -404,7 +456,7 @@ test "doctor can render Linux backend details from an injected report" {
var context = try testContext(std.testing.allocator, .{});
defer context.deinit();

try writeReport(stdout_stream.writer(), .linux, report, context);
try writeReport(stdout_stream.writer(), .linux, report, context, true);

const written = stdout_stream.getWritten();
try std.testing.expect(std.mem.indexOf(u8, written, "Linux backend:") != null);
Expand All @@ -422,7 +474,7 @@ test "doctor can render macOS backend details from an injected report" {
var context = try testContext(std.testing.allocator, .{});
defer context.deinit();

try writeReport(stdout_stream.writer(), .macos, report, context);
try writeReport(stdout_stream.writer(), .macos, report, context, true);

const written = stdout_stream.getWritten();
try std.testing.expect(std.mem.indexOf(u8, written, "macOS backend:") != null);
Expand All @@ -445,7 +497,7 @@ test "doctor can render Windows backend details from an injected report" {
var context = try testContext(std.testing.allocator, .{});
defer context.deinit();

try writeReport(stdout_stream.writer(), .windows, report, context);
try writeReport(stdout_stream.writer(), .windows, report, context, true);

const written = stdout_stream.getWritten();
try std.testing.expect(std.mem.indexOf(u8, written, "Windows backend:") != null);
Expand Down Expand Up @@ -483,7 +535,7 @@ test "doctor detects valid policy in current workspace" {
var context = try collectIntegrationContextAt(std.testing.allocator, tmp_path);
defer context.deinit();

try writeReport(stdout_stream.writer(), core.platform.detectOs(), sandbox.backend.detect(core.platform.detectOs()), context);
try writeReport(stdout_stream.writer(), core.platform.detectOs(), sandbox.backend.detect(core.platform.detectOs()), context, true);
try std.testing.expect(std.mem.indexOf(u8, stdout_stream.getWritten(), ".orca/policy.yaml: present and valid") != null);
try std.testing.expect(std.mem.indexOf(u8, stdout_stream.getWritten(), "git repository: detected") != null);
try std.testing.expectEqualStrings("", stderr_stream.getWritten());
Expand Down Expand Up @@ -512,7 +564,7 @@ test "doctor reports invalid policy clearly without printing synthetic secrets"
var context = try collectIntegrationContextAt(std.testing.allocator, tmp_path);
defer context.deinit();

try writeReport(stdout_stream.writer(), core.platform.detectOs(), sandbox.backend.detect(core.platform.detectOs()), context);
try writeReport(stdout_stream.writer(), core.platform.detectOs(), sandbox.backend.detect(core.platform.detectOs()), context, true);
const output = stdout_stream.getWritten();
try std.testing.expect(std.mem.indexOf(u8, output, ".orca/policy.yaml: invalid") != null);
try std.testing.expect(std.mem.indexOf(u8, output, "UnsupportedPolicyMode") != null);
Expand Down Expand Up @@ -548,3 +600,44 @@ fn testContext(allocator: std.mem.Allocator, options: TestContextOptions) !Integ
.redteam_fixtures_present = true,
};
}

// ---------------------------------------------------------------------------
// Phase 3: doctor summary line + --verbose / -v (TDD tests written FIRST)
// Default (no flag) is now brief (Summary + recommendations only).
// --verbose restores the previous full dense report.
// ---------------------------------------------------------------------------

test "doctor default is brief (summary + recommendations, no full Capabilities dump)" {
var stdout_buf: [8192]u8 = undefined;
var stderr_buf: [256]u8 = undefined;
var stdout_stream = std.io.fixedBufferStream(&stdout_buf);
var stderr_stream = std.io.fixedBufferStream(&stderr_buf);

const code = try command(&.{}, stdout_stream.writer(), stderr_stream.writer());
try std.testing.expectEqual(exit_codes.success, code);

const output = stdout_stream.getWritten();
try std.testing.expect(std.mem.indexOf(u8, output, "Summary:") != null);
try std.testing.expect(std.mem.indexOf(u8, output, "active") != null);
try std.testing.expect(std.mem.indexOf(u8, output, "Recommendations") != null or std.mem.indexOf(u8, output, "Next steps") != null or std.mem.indexOf(u8, output, "orca ") != null);
// Brief mode should not dump the full 9+ capability lines or backend details
try std.testing.expect(std.mem.indexOf(u8, output, "Capabilities:") == null or output.len < 800);
try std.testing.expectEqualStrings("", stderr_stream.getWritten());
}

test "doctor --verbose prints full report (Capabilities and backend details)" {
var stdout_buf: [8192]u8 = undefined;
var stderr_buf: [256]u8 = undefined;
var stdout_stream = std.io.fixedBufferStream(&stdout_buf);
var stderr_stream = std.io.fixedBufferStream(&stderr_buf);

const code = try command(&.{ "--verbose" }, stdout_stream.writer(), stderr_stream.writer());
try std.testing.expectEqual(exit_codes.success, code);

const output = stdout_stream.getWritten();
try std.testing.expect(std.mem.indexOf(u8, output, "Summary:") != null);
try std.testing.expect(std.mem.indexOf(u8, output, "Capabilities:") != null);
try std.testing.expect(std.mem.indexOf(u8, output, "Backend:") != null or std.mem.indexOf(u8, output, "Linux backend:") != null);
try std.testing.expect(std.mem.indexOf(u8, output, "process supervision:") != null);
try std.testing.expectEqualStrings("", stderr_stream.getWritten());
}
22 changes: 20 additions & 2 deletions src/cli/help.zig
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,23 @@ pub const commands = [_]CommandInfo{
"After setup, run 'orca run -- <your-command>' for immediate protection.",
},
},
.{
.name = "quickstart",
.summary = "One-command onboarding: doctor, init, setup",
.usage = "orca quickstart [--auto] [--preset <name>]",
.category = .getting_started,
.examples = &.{
"orca quickstart",
"orca quickstart --auto",
"orca quickstart --preset strict-local",
},
.details = &.{
"Runs doctor → init (if needed) → setup in one command.",
"On interactive terminals, setup runs in guided mode.",
"Use --auto for non-interactive environments (CI, scripts).",
"Use --preset to choose a policy preset (default: generic-agent).",
},
},
.{
.name = "env",
.summary = "Print shell environment for Orca",
Expand Down Expand Up @@ -153,10 +170,11 @@ pub const commands = [_]CommandInfo{
"Local workspace .orca/ directories are not removed automatically;",
"run 'find . -type d -name .orca' to locate them manually.",
} },
.{ .name = "replay", .summary = "Replay an audit session", .usage = "orca replay [--session <id|last>] [--json] [--only denied] [--verify]", .category = .core_workflow, .examples = &.{
.{ .name = "replay", .summary = "Replay an audit session", .usage = "orca replay [--list] [--session <id|last>] [--json] [--only denied] [--verify]", .category = .core_workflow, .examples = &.{
"orca replay --list",
"orca replay --session last",
"orca replay --session 2026-05-29-abc123",
}, .details = &.{"Reads .orca session artifacts, renders a timeline, and can verify the event hash chain."} },
}, .details = &.{"Reads .orca session artifacts, renders a timeline, and can verify the event hash chain. Use --list to enumerate past sessions (or run bare `orca replay` with none)."} },
.{
.name = "diff",
.summary = "Show pending file changes",
Expand Down
Loading
Loading