holo-codex 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 (149) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/CONTRIBUTING.md +54 -0
  3. package/LICENSE +21 -0
  4. package/README.md +215 -0
  5. package/README.zh-CN.md +215 -0
  6. package/SECURITY.md +39 -0
  7. package/assets/brand/README.md +35 -0
  8. package/assets/brand/holo-codex-icon.svg +28 -0
  9. package/assets/brand/holo-codex-lockup.svg +49 -0
  10. package/assets/brand/holo-codex-mark.svg +33 -0
  11. package/assets/brand/holo-codex-plugin-card.png +0 -0
  12. package/assets/brand/holo-codex-plugin-card.svg +81 -0
  13. package/assets/brand/holo-codex-readme-hero.png +0 -0
  14. package/assets/brand/holo-codex-readme-hero.svg +140 -0
  15. package/assets/brand/holo-codex-social-preview.png +0 -0
  16. package/assets/brand/holo-codex-social-preview.svg +130 -0
  17. package/assets/brand/holo-codex-wordmark-options.svg +52 -0
  18. package/docs/checklists/agent-loop-first-delivery-audit.md +129 -0
  19. package/docs/examples/generic-loop-repo-hygiene.md +168 -0
  20. package/docs/install.md +190 -0
  21. package/docs/local-release-readiness.md +206 -0
  22. package/docs/release-checklist.md +144 -0
  23. package/docs/self-bootstrap.md +150 -0
  24. package/docs/trust-and-safety.md +45 -0
  25. package/package.json +83 -0
  26. package/plugins/autonomous-pr-loop/.codex-plugin/plugin.json +17 -0
  27. package/plugins/autonomous-pr-loop/.mcp.json +13 -0
  28. package/plugins/autonomous-pr-loop/bin/agent-loop.mjs +31 -0
  29. package/plugins/autonomous-pr-loop/core/artifacts.ts +164 -0
  30. package/plugins/autonomous-pr-loop/core/autonomy-policy.ts +206 -0
  31. package/plugins/autonomous-pr-loop/core/ci.ts +131 -0
  32. package/plugins/autonomous-pr-loop/core/cli-i18n.ts +123 -0
  33. package/plugins/autonomous-pr-loop/core/cli.ts +1413 -0
  34. package/plugins/autonomous-pr-loop/core/command-runner.ts +446 -0
  35. package/plugins/autonomous-pr-loop/core/command.ts +47 -0
  36. package/plugins/autonomous-pr-loop/core/config-editor.ts +140 -0
  37. package/plugins/autonomous-pr-loop/core/config.ts +293 -0
  38. package/plugins/autonomous-pr-loop/core/controller-host.ts +19 -0
  39. package/plugins/autonomous-pr-loop/core/dashboard-server.ts +536 -0
  40. package/plugins/autonomous-pr-loop/core/delivery-work-item.ts +217 -0
  41. package/plugins/autonomous-pr-loop/core/doctor.ts +335 -0
  42. package/plugins/autonomous-pr-loop/core/errors.ts +82 -0
  43. package/plugins/autonomous-pr-loop/core/gate-recovery.ts +176 -0
  44. package/plugins/autonomous-pr-loop/core/gates.ts +26 -0
  45. package/plugins/autonomous-pr-loop/core/generic-lifecycle.ts +399 -0
  46. package/plugins/autonomous-pr-loop/core/git.ts +213 -0
  47. package/plugins/autonomous-pr-loop/core/github.ts +269 -0
  48. package/plugins/autonomous-pr-loop/core/gitnexus.ts +90 -0
  49. package/plugins/autonomous-pr-loop/core/happy.ts +42 -0
  50. package/plugins/autonomous-pr-loop/core/hook-capture.ts +115 -0
  51. package/plugins/autonomous-pr-loop/core/hook-events.ts +22 -0
  52. package/plugins/autonomous-pr-loop/core/hook-installation.ts +85 -0
  53. package/plugins/autonomous-pr-loop/core/hook-observer.ts +84 -0
  54. package/plugins/autonomous-pr-loop/core/hook-policy.ts +423 -0
  55. package/plugins/autonomous-pr-loop/core/hook-router.ts +452 -0
  56. package/plugins/autonomous-pr-loop/core/index.ts +32 -0
  57. package/plugins/autonomous-pr-loop/core/local-install.ts +778 -0
  58. package/plugins/autonomous-pr-loop/core/locale.ts +60 -0
  59. package/plugins/autonomous-pr-loop/core/loop-shapes.ts +190 -0
  60. package/plugins/autonomous-pr-loop/core/mcp-controller.ts +1479 -0
  61. package/plugins/autonomous-pr-loop/core/notification-feed.ts +263 -0
  62. package/plugins/autonomous-pr-loop/core/plan-parser.ts +206 -0
  63. package/plugins/autonomous-pr-loop/core/plugin-paths.ts +32 -0
  64. package/plugins/autonomous-pr-loop/core/policy.ts +65 -0
  65. package/plugins/autonomous-pr-loop/core/pr-lifecycle.ts +464 -0
  66. package/plugins/autonomous-pr-loop/core/pr-selector.ts +284 -0
  67. package/plugins/autonomous-pr-loop/core/profiles.ts +439 -0
  68. package/plugins/autonomous-pr-loop/core/redaction.ts +17 -0
  69. package/plugins/autonomous-pr-loop/core/repo-root.ts +22 -0
  70. package/plugins/autonomous-pr-loop/core/review-comments.ts +77 -0
  71. package/plugins/autonomous-pr-loop/core/scope-guard.ts +179 -0
  72. package/plugins/autonomous-pr-loop/core/state-machine.ts +828 -0
  73. package/plugins/autonomous-pr-loop/core/state-types.ts +130 -0
  74. package/plugins/autonomous-pr-loop/core/storage.ts +2527 -0
  75. package/plugins/autonomous-pr-loop/core/types.ts +567 -0
  76. package/plugins/autonomous-pr-loop/core/worker-events.ts +412 -0
  77. package/plugins/autonomous-pr-loop/core/worker-policy.ts +72 -0
  78. package/plugins/autonomous-pr-loop/core/worker-prompts.ts +182 -0
  79. package/plugins/autonomous-pr-loop/core/worker.ts +809 -0
  80. package/plugins/autonomous-pr-loop/core/workflow-board.ts +1515 -0
  81. package/plugins/autonomous-pr-loop/hooks/dist/permission-request.js +2462 -0
  82. package/plugins/autonomous-pr-loop/hooks/dist/post-compact.js +2462 -0
  83. package/plugins/autonomous-pr-loop/hooks/dist/post-tool-use.js +2462 -0
  84. package/plugins/autonomous-pr-loop/hooks/dist/pre-compact.js +2462 -0
  85. package/plugins/autonomous-pr-loop/hooks/dist/pre-tool-use.js +3460 -0
  86. package/plugins/autonomous-pr-loop/hooks/dist/session-start.js +2462 -0
  87. package/plugins/autonomous-pr-loop/hooks/dist/stop.js +2462 -0
  88. package/plugins/autonomous-pr-loop/hooks/dist/user-prompt-submit.js +2462 -0
  89. package/plugins/autonomous-pr-loop/hooks/hooks.json +106 -0
  90. package/plugins/autonomous-pr-loop/hooks/observe-runner.ts +25 -0
  91. package/plugins/autonomous-pr-loop/hooks/permission-request.ts +4 -0
  92. package/plugins/autonomous-pr-loop/hooks/post-compact.ts +4 -0
  93. package/plugins/autonomous-pr-loop/hooks/post-tool-use.ts +4 -0
  94. package/plugins/autonomous-pr-loop/hooks/pre-compact.ts +4 -0
  95. package/plugins/autonomous-pr-loop/hooks/pre-tool-use.ts +44 -0
  96. package/plugins/autonomous-pr-loop/hooks/session-start.ts +4 -0
  97. package/plugins/autonomous-pr-loop/hooks/stop.ts +4 -0
  98. package/plugins/autonomous-pr-loop/hooks/user-prompt-submit.ts +4 -0
  99. package/plugins/autonomous-pr-loop/mcp-server/src/index.ts +87 -0
  100. package/plugins/autonomous-pr-loop/mcp-server/src/tools.ts +205 -0
  101. package/plugins/autonomous-pr-loop/package.json +9 -0
  102. package/plugins/autonomous-pr-loop/schemas/config.schema.json +74 -0
  103. package/plugins/autonomous-pr-loop/schemas/marketplace.schema.json +46 -0
  104. package/plugins/autonomous-pr-loop/schemas/plugin.schema.json +32 -0
  105. package/plugins/autonomous-pr-loop/schemas/state.schema.json +19 -0
  106. package/plugins/autonomous-pr-loop/schemas/worker-event.schema.json +19 -0
  107. package/plugins/autonomous-pr-loop/schemas/worker-result.schema.json +58 -0
  108. package/plugins/autonomous-pr-loop/scripts/agent-loop.ts +44 -0
  109. package/plugins/autonomous-pr-loop/skills/autonomous-pr-loop/SKILL.md +26 -0
  110. package/plugins/autonomous-pr-loop/skills/autonomous-pr-loop/agents/openai.yaml +6 -0
  111. package/plugins/autonomous-pr-loop/ui/index.html +26 -0
  112. package/plugins/autonomous-pr-loop/ui/public/favicon.svg +7 -0
  113. package/plugins/autonomous-pr-loop/ui/src/api.ts +639 -0
  114. package/plugins/autonomous-pr-loop/ui/src/app.tsx +238 -0
  115. package/plugins/autonomous-pr-loop/ui/src/components/ActivityBadge.tsx +31 -0
  116. package/plugins/autonomous-pr-loop/ui/src/components/BrandMark.tsx +36 -0
  117. package/plugins/autonomous-pr-loop/ui/src/components/Collapsible.tsx +6 -0
  118. package/plugins/autonomous-pr-loop/ui/src/components/CommandPreview.tsx +15 -0
  119. package/plugins/autonomous-pr-loop/ui/src/components/ConfigEditor.tsx +389 -0
  120. package/plugins/autonomous-pr-loop/ui/src/components/EmptyState.tsx +10 -0
  121. package/plugins/autonomous-pr-loop/ui/src/components/ErrorState.tsx +12 -0
  122. package/plugins/autonomous-pr-loop/ui/src/components/List.tsx +7 -0
  123. package/plugins/autonomous-pr-loop/ui/src/components/MetricRow.tsx +6 -0
  124. package/plugins/autonomous-pr-loop/ui/src/components/ResponsiveTable.tsx +65 -0
  125. package/plugins/autonomous-pr-loop/ui/src/components/RiskBadge.tsx +10 -0
  126. package/plugins/autonomous-pr-loop/ui/src/components/StatusBadge.tsx +29 -0
  127. package/plugins/autonomous-pr-loop/ui/src/components/TopMetric.tsx +10 -0
  128. package/plugins/autonomous-pr-loop/ui/src/fixtures.ts +1152 -0
  129. package/plugins/autonomous-pr-loop/ui/src/i18n.ts +1105 -0
  130. package/plugins/autonomous-pr-loop/ui/src/main.tsx +14 -0
  131. package/plugins/autonomous-pr-loop/ui/src/pages/CommandCenter.tsx +470 -0
  132. package/plugins/autonomous-pr-loop/ui/src/pages/CommandCenterParts.tsx +276 -0
  133. package/plugins/autonomous-pr-loop/ui/src/pages/agent-timeline/AgentTimelineView.tsx +73 -0
  134. package/plugins/autonomous-pr-loop/ui/src/pages/artifact-viewer/ArtifactViewer.tsx +44 -0
  135. package/plugins/autonomous-pr-loop/ui/src/pages/dry-run-preview/DryRunPreview.tsx +66 -0
  136. package/plugins/autonomous-pr-loop/ui/src/pages/event-ledger/EventLedger.tsx +17 -0
  137. package/plugins/autonomous-pr-loop/ui/src/pages/gate-center/GateCenter.tsx +34 -0
  138. package/plugins/autonomous-pr-loop/ui/src/pages/mission-control/MissionControl.tsx +104 -0
  139. package/plugins/autonomous-pr-loop/ui/src/pages/mission-control/WorkflowBoard.tsx +577 -0
  140. package/plugins/autonomous-pr-loop/ui/src/pages/notifications/NotificationsView.tsx +30 -0
  141. package/plugins/autonomous-pr-loop/ui/src/pages/plan-navigator/PlanNavigator.tsx +19 -0
  142. package/plugins/autonomous-pr-loop/ui/src/pages/policy-config/PolicyConfig.tsx +22 -0
  143. package/plugins/autonomous-pr-loop/ui/src/pages/pr-inbox/PrInbox.tsx +26 -0
  144. package/plugins/autonomous-pr-loop/ui/src/pages/recovery-center/RecoveryCenter.tsx +125 -0
  145. package/plugins/autonomous-pr-loop/ui/src/pages/scope-guard/ScopeGuard.tsx +16 -0
  146. package/plugins/autonomous-pr-loop/ui/src/pages/worker-runs/WorkerRuns.tsx +39 -0
  147. package/plugins/autonomous-pr-loop/ui/src/styles.css +2673 -0
  148. package/plugins/autonomous-pr-loop/ui/src/theme.ts +57 -0
  149. package/tsconfig.json +18 -0
@@ -0,0 +1,179 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { createHash } from "node:crypto";
3
+ import { closeSync, existsSync, openSync, readSync, statSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { matchesProtectedPath } from "./policy.js";
6
+ import type {
7
+ AgentLoopConfig,
8
+ AgentLoopStorage,
9
+ ScopeGuardReport,
10
+ WorkerResult
11
+ } from "./types.js";
12
+
13
+ export interface ScopeBaselineEntry {
14
+ path: string;
15
+ digest: string | null;
16
+ }
17
+
18
+ /** Capture the current git porcelain paths, including untracked files. */
19
+ export function captureScopeBaseline(repoRoot: string): ScopeBaselineEntry[] {
20
+ return readPorcelainPaths(repoRoot).map((path) => ({ path, digest: digestPath(repoRoot, path) }));
21
+ }
22
+
23
+ /** Validate worker changes against real git state, protected paths, and worker claims. */
24
+ export function evaluateWorkerScope(input: {
25
+ repoRoot: string;
26
+ storage: AgentLoopStorage;
27
+ runId: string;
28
+ workerId: string;
29
+ config: AgentLoopConfig;
30
+ baseline: ScopeBaselineEntry[];
31
+ result: WorkerResult;
32
+ allowedPaths?: string[];
33
+ outOfScopeGate?: "review_out_of_scope" | "generic_scope_change_requested";
34
+ }): ScopeGuardReport {
35
+ const after = readPorcelainPaths(input.repoRoot).map((path) => ({ path, digest: digestPath(input.repoRoot, path) }));
36
+ const baseline = new Map(input.baseline.map((entry) => [entry.path, entry.digest]));
37
+ const afterMap = new Map(after.map((entry) => [entry.path, entry.digest]));
38
+ const actualChangedFiles = unique([...baseline.keys(), ...afterMap.keys()])
39
+ .filter((path) => baseline.get(path) !== afterMap.get(path))
40
+ .filter((path) => !isSupervisorRuntimePath(path, input.runId, input.workerId));
41
+ const workerPaths = [...input.result.changedFiles, ...input.result.outOfScope.map((item) => item.item)];
42
+ const invalidWorkerPaths = unique(workerPaths.filter((path) => !isSafeRepoRelativePath(path)));
43
+ const reportedChangedFiles = unique(input.result.changedFiles.filter(isSafeRepoRelativePath));
44
+ const missingFromReport = actualChangedFiles.filter((path) => !reportedChangedFiles.includes(path));
45
+ const extraInReport = reportedChangedFiles.filter((path) => !actualChangedFiles.includes(path));
46
+ const protectedPathHits = actualChangedFiles.filter((path) =>
47
+ input.config.protectedPaths.some((pattern) => matchesProtectedPath(pattern, path))
48
+ );
49
+ const outOfScope = [
50
+ ...input.result.outOfScope,
51
+ ...actualChangedFiles
52
+ .filter((path) => !isAllowed(path, input.allowedPaths))
53
+ .map((path) => ({ item: path, reason: "Changed path is outside worker allowed paths." }))
54
+ ];
55
+ const gate = invalidWorkerPaths.length > 0
56
+ ? "policy_violation"
57
+ : protectedPathHits.length > 0
58
+ ? "policy_violation"
59
+ : outOfScope.length > 0
60
+ ? input.outOfScopeGate ?? "review_out_of_scope"
61
+ : input.config.gitnexusRequired &&
62
+ (!input.result.gitnexus.impactRun || !input.result.gitnexus.detectChangesRun)
63
+ ? "policy_violation"
64
+ : undefined;
65
+
66
+ if (missingFromReport.length > 0 || extraInReport.length > 0) {
67
+ input.storage.appendEvent({
68
+ runId: input.runId,
69
+ kind: "worker_changed_files_mismatch",
70
+ message: "Worker changedFiles did not match git status.",
71
+ payload: {
72
+ workerId: input.workerId,
73
+ actualChangedFiles,
74
+ reportedChangedFiles,
75
+ missingFromReport,
76
+ extraInReport
77
+ }
78
+ });
79
+ }
80
+
81
+ return {
82
+ ok: gate === undefined,
83
+ actualChangedFiles,
84
+ reportedChangedFiles,
85
+ missingFromReport,
86
+ extraInReport,
87
+ protectedPathHits,
88
+ invalidWorkerPaths,
89
+ outOfScope,
90
+ ...(gate ? { gate } : {})
91
+ };
92
+ }
93
+
94
+ function readPorcelainPaths(repoRoot: string): string[] {
95
+ const output = execFileSync("git", ["status", "--porcelain=v1", "-z", "--untracked-files=all"], {
96
+ cwd: repoRoot,
97
+ stdio: ["ignore", "pipe", "pipe"]
98
+ }) as Buffer;
99
+ return parsePorcelainPaths(output);
100
+ }
101
+
102
+ function digestPath(repoRoot: string, path: string): string | null {
103
+ const absolutePath = join(repoRoot, path);
104
+ if (!existsSync(absolutePath)) {
105
+ return null;
106
+ }
107
+ const stat = statSync(absolutePath);
108
+ if (!stat.isFile()) {
109
+ return "directory";
110
+ }
111
+ return hashFile(absolutePath);
112
+ }
113
+
114
+ function hashFile(path: string): string {
115
+ const hash = createHash("sha256");
116
+ const fd = openSync(path, "r");
117
+ const buffer = Buffer.allocUnsafe(64 * 1024);
118
+ try {
119
+ let bytesRead = readSync(fd, buffer, 0, buffer.length, null);
120
+ while (bytesRead > 0) {
121
+ hash.update(buffer.subarray(0, bytesRead));
122
+ bytesRead = readSync(fd, buffer, 0, buffer.length, null);
123
+ }
124
+ return hash.digest("hex");
125
+ } finally {
126
+ closeSync(fd);
127
+ }
128
+ }
129
+
130
+ function parsePorcelainPaths(output: Buffer): string[] {
131
+ const records = output.toString("utf8").split("\0").filter(Boolean);
132
+ const paths: string[] = [];
133
+ for (let index = 0; index < records.length; index += 1) {
134
+ const record = records[index] ?? "";
135
+ const status = record.slice(0, 2);
136
+ const path = record.slice(3);
137
+ if (path) {
138
+ paths.push(normalizePath(path));
139
+ }
140
+ if (status.includes("R") || status.includes("C")) {
141
+ index += 1;
142
+ }
143
+ }
144
+ return unique(paths);
145
+ }
146
+
147
+ function isAllowed(path: string, allowedPaths: string[] | undefined): boolean {
148
+ if (!allowedPaths) {
149
+ return true;
150
+ }
151
+ return allowedPaths.some((allowedPath) => {
152
+ const normalized = normalizePath(allowedPath);
153
+ const file = normalizePath(path);
154
+ return file === normalized || file.startsWith(`${normalized.replace(/\/$/, "")}/`);
155
+ });
156
+ }
157
+
158
+ function normalizePath(path: string): string {
159
+ return path.replaceAll("\\", "/").replace(/^\.\//, "");
160
+ }
161
+
162
+ function isSafeRepoRelativePath(path: string): boolean {
163
+ const normalized = normalizePath(path);
164
+ return normalized.length > 0 &&
165
+ !normalized.startsWith("/") &&
166
+ !normalized.split("/").includes("..");
167
+ }
168
+
169
+ function unique(values: string[]): string[] {
170
+ return [...new Set(values)];
171
+ }
172
+
173
+ function isSupervisorRuntimePath(path: string, runId: string, workerId: string): boolean {
174
+ return path === `.agent-loop/artifacts/${runId}/worker-jsonl/${workerId}.jsonl` ||
175
+ path === `.agent-loop/artifacts/${runId}/worker-result/${workerId}-worker-final.json` ||
176
+ path === ".agent-loop/state.sqlite" ||
177
+ path === ".agent-loop/state.sqlite-shm" ||
178
+ path === ".agent-loop/state.sqlite-wal";
179
+ }