@kylecheng3146/agent-ops 0.0.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.
Files changed (115) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +83 -0
  3. package/SECURITY.md +22 -0
  4. package/dist/packages/cli/src/args.js +322 -0
  5. package/dist/packages/cli/src/bin.js +290 -0
  6. package/dist/packages/cli/src/cli.js +80 -0
  7. package/dist/packages/cli/src/commands/config.js +8 -0
  8. package/dist/packages/cli/src/commands/doctor.js +32 -0
  9. package/dist/packages/cli/src/commands/index.js +16 -0
  10. package/dist/packages/cli/src/commands/init.js +65 -0
  11. package/dist/packages/cli/src/commands/review.js +71 -0
  12. package/dist/packages/cli/src/commands/task.js +141 -0
  13. package/dist/packages/cli/src/commands/trust.js +48 -0
  14. package/dist/packages/cli/src/commands/uninstall.js +58 -0
  15. package/dist/packages/cli/src/commands/update.js +58 -0
  16. package/dist/packages/cli/src/commands/verify.js +108 -0
  17. package/dist/packages/cli/src/output.js +53 -0
  18. package/dist/packages/cli/src/plan-output.js +28 -0
  19. package/dist/packages/cli/src/wizard.js +75 -0
  20. package/dist/runtime/src/adapters/claude/config.js +109 -0
  21. package/dist/runtime/src/adapters/claude/events.js +8 -0
  22. package/dist/runtime/src/adapters/claude/input.js +38 -0
  23. package/dist/runtime/src/adapters/claude/output.js +30 -0
  24. package/dist/runtime/src/adapters/codex/config.js +93 -0
  25. package/dist/runtime/src/adapters/codex/events.js +8 -0
  26. package/dist/runtime/src/adapters/codex/input.js +41 -0
  27. package/dist/runtime/src/adapters/codex/output.js +24 -0
  28. package/dist/runtime/src/config/explain.js +34 -0
  29. package/dist/runtime/src/config/load.js +25 -0
  30. package/dist/runtime/src/config/merge.js +170 -0
  31. package/dist/runtime/src/config/migrate.js +61 -0
  32. package/dist/runtime/src/contracts.js +1 -0
  33. package/dist/runtime/src/discovery/go.js +145 -0
  34. package/dist/runtime/src/discovery/index.js +40 -0
  35. package/dist/runtime/src/discovery/make.js +162 -0
  36. package/dist/runtime/src/discovery/node.js +175 -0
  37. package/dist/runtime/src/discovery/python.js +163 -0
  38. package/dist/runtime/src/discovery/rust.js +159 -0
  39. package/dist/runtime/src/discovery/types.js +1 -0
  40. package/dist/runtime/src/fs/hash.js +19 -0
  41. package/dist/runtime/src/fs/managed-block.js +90 -0
  42. package/dist/runtime/src/fs/manifest.js +24 -0
  43. package/dist/runtime/src/fs/mutation-worker.js +185 -0
  44. package/dist/runtime/src/fs/paths.js +96 -0
  45. package/dist/runtime/src/fs/transaction.js +498 -0
  46. package/dist/runtime/src/guardrails/destructive.js +207 -0
  47. package/dist/runtime/src/guardrails/evaluate.js +9 -0
  48. package/dist/runtime/src/guardrails/exceptions.js +49 -0
  49. package/dist/runtime/src/guardrails/secrets.js +97 -0
  50. package/dist/runtime/src/guardrails/types.js +9 -0
  51. package/dist/runtime/src/hooks/dispatch.js +78 -0
  52. package/dist/runtime/src/hooks/events.js +1 -0
  53. package/dist/runtime/src/hooks/hook-entry.js +19 -0
  54. package/dist/runtime/src/hooks/normalize.js +59 -0
  55. package/dist/runtime/src/hooks/output.js +12 -0
  56. package/dist/runtime/src/hooks/shell.js +138 -0
  57. package/dist/runtime/src/hooks/stop-verify.js +70 -0
  58. package/dist/runtime/src/install/apply.js +70 -0
  59. package/dist/runtime/src/install/doctor.js +196 -0
  60. package/dist/runtime/src/install/harness.js +87 -0
  61. package/dist/runtime/src/install/ownership.js +84 -0
  62. package/dist/runtime/src/install/plan.js +257 -0
  63. package/dist/runtime/src/install/profiles.js +28 -0
  64. package/dist/runtime/src/install/types.js +1 -0
  65. package/dist/runtime/src/install/uninstall.js +206 -0
  66. package/dist/runtime/src/install/update.js +123 -0
  67. package/dist/runtime/src/logging/local-log.js +158 -0
  68. package/dist/runtime/src/registry/npm.js +141 -0
  69. package/dist/runtime/src/review/claude-runner.js +4 -0
  70. package/dist/runtime/src/review/codex-runner.js +4 -0
  71. package/dist/runtime/src/review/packet.js +10 -0
  72. package/dist/runtime/src/review/result.js +24 -0
  73. package/dist/runtime/src/review/roles.js +3 -0
  74. package/dist/runtime/src/review/runner.js +45 -0
  75. package/dist/runtime/src/schema/validate.js +584 -0
  76. package/dist/runtime/src/security/permissions.js +654 -0
  77. package/dist/runtime/src/security/redact.js +41 -0
  78. package/dist/runtime/src/security/trust.js +209 -0
  79. package/dist/runtime/src/task/render.js +43 -0
  80. package/dist/runtime/src/task/service.js +235 -0
  81. package/dist/runtime/src/task/store.js +265 -0
  82. package/dist/runtime/src/verify/change-surface.js +86 -0
  83. package/dist/runtime/src/verify/evidence.js +89 -0
  84. package/dist/runtime/src/verify/fingerprint.js +67 -0
  85. package/dist/runtime/src/verify/scope.js +69 -0
  86. package/dist/runtime/src/verify/service.js +217 -0
  87. package/dist/runtime/src/verify/spawn.js +326 -0
  88. package/dist/runtime/src/verify/test-count.js +148 -0
  89. package/docs/en/spec/README.md +13 -0
  90. package/docs/en/spec/acceptance-and-evidence.md +21 -0
  91. package/docs/en/spec/delegation.md +21 -0
  92. package/docs/en/spec/guardrails.md +21 -0
  93. package/docs/en/spec/harness-adapters.md +21 -0
  94. package/docs/en/spec/judgment.md +21 -0
  95. package/docs/en/spec/loop-engineering.md +23 -0
  96. package/docs/en/spec/maintenance.md +21 -0
  97. package/docs/en/spec/review.md +21 -0
  98. package/docs/en/spec/troubleshooting.md +21 -0
  99. package/docs/zh-TW/spec/README.md +13 -0
  100. package/docs/zh-TW/spec/acceptance-and-evidence.md +23 -0
  101. package/docs/zh-TW/spec/delegation.md +23 -0
  102. package/docs/zh-TW/spec/guardrails.md +23 -0
  103. package/docs/zh-TW/spec/harness-adapters.md +23 -0
  104. package/docs/zh-TW/spec/judgment.md +23 -0
  105. package/docs/zh-TW/spec/loop-engineering.md +23 -0
  106. package/docs/zh-TW/spec/maintenance.md +23 -0
  107. package/docs/zh-TW/spec/review.md +23 -0
  108. package/docs/zh-TW/spec/troubleshooting.md +23 -0
  109. package/package.json +41 -0
  110. package/schemas/config.schema.json +231 -0
  111. package/schemas/evidence.schema.json +115 -0
  112. package/schemas/manifest.schema.json +116 -0
  113. package/schemas/task.schema.json +59 -0
  114. package/templates/common/AGENTS.block.md +3 -0
  115. package/templates/common/CLAUDE.block.md +3 -0
@@ -0,0 +1,290 @@
1
+ #!/usr/bin/env node
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { createInterface } from "node:readline/promises";
5
+ import { execFileSync } from "node:child_process";
6
+ import { commonHarnessAdapters } from "../../../runtime/src/install/harness.js";
7
+ import { NpmRegistryClient } from "../../../runtime/src/registry/npm.js";
8
+ import { TaskService } from "../../../runtime/src/task/service.js";
9
+ import { FileTaskStore } from "../../../runtime/src/task/store.js";
10
+ import { mergeConfigLayers } from "../../../runtime/src/config/merge.js";
11
+ import { loadConfigFile } from "../../../runtime/src/config/load.js";
12
+ import { AgentOpsError } from "../../../runtime/src/fs/paths.js";
13
+ import { FileTrustStore, calculateTrustBinding } from "../../../runtime/src/security/trust.js";
14
+ import { localStatePaths } from "../../../runtime/src/security/permissions.js";
15
+ import { sha256 } from "../../../runtime/src/fs/hash.js";
16
+ import { FileEvidenceStore } from "../../../runtime/src/verify/evidence.js";
17
+ import { VerificationService } from "../../../runtime/src/verify/service.js";
18
+ import { NodeVerificationProcessRunner } from "../../../runtime/src/verify/spawn.js";
19
+ import { runCli } from "./cli.js";
20
+ import { createCommandRegistry } from "./commands/index.js";
21
+ import { explainConfigCommand } from "./commands/config.js";
22
+ import { runDoctorCommand } from "./commands/doctor.js";
23
+ import { formatInstallPlan, runInitCommand } from "./commands/init.js";
24
+ import { formatUninstallPlan, runUninstallCommand } from "./commands/uninstall.js";
25
+ import { runTaskCommand } from "./commands/task.js";
26
+ import { runReviewCommand } from "./commands/review.js";
27
+ import { runTrustCommand } from "./commands/trust.js";
28
+ import { runVerifyCommand } from "./commands/verify.js";
29
+ import { formatUpdatePlan, runUpdateCommand } from "./commands/update.js";
30
+ import { errorEnvelope } from "./output.js";
31
+ const CLI_VERSION = "0.1.0";
32
+ const DEFAULT_CONFIG = {
33
+ schemaVersion: 1,
34
+ profiles: [],
35
+ verification: { commands: [] },
36
+ pathMappings: [],
37
+ securityExceptions: []
38
+ };
39
+ function defaultConfigLayer() {
40
+ return {
41
+ source: "default",
42
+ sourcePath: "built-in defaults",
43
+ config: DEFAULT_CONFIG
44
+ };
45
+ }
46
+ async function loadOptionalConfig(path) {
47
+ try {
48
+ return await loadConfigFile(path);
49
+ }
50
+ catch (error) {
51
+ if (error instanceof AgentOpsError &&
52
+ error.code === "CONFIG_READ_FAILED" &&
53
+ typeof error.cause === "object" &&
54
+ error.cause !== null &&
55
+ "code" in error.cause &&
56
+ error.cause.code === "ENOENT") {
57
+ return null;
58
+ }
59
+ throw error;
60
+ }
61
+ }
62
+ async function loadEffectiveConfig(root, scope) {
63
+ const home = process.env.AGENT_OPS_HOME ?? homedir();
64
+ const userPath = join(home, ".agent-ops", "config.json");
65
+ const projectPath = join(root, ".agent-ops", "config.json");
66
+ const layers = [defaultConfigLayer()];
67
+ if (scope === "user") {
68
+ const user = await loadOptionalConfig(userPath);
69
+ if (user !== null) {
70
+ layers.push({
71
+ source: "user",
72
+ sourcePath: user.sourcePath,
73
+ config: user.config
74
+ });
75
+ }
76
+ return mergeConfigLayers(layers);
77
+ }
78
+ if (projectPath !== userPath) {
79
+ const user = await loadOptionalConfig(userPath);
80
+ if (user !== null) {
81
+ layers.push({
82
+ source: "user",
83
+ sourcePath: user.sourcePath,
84
+ config: user.config
85
+ });
86
+ }
87
+ }
88
+ const project = await loadOptionalConfig(projectPath);
89
+ if (project !== null) {
90
+ layers.push({
91
+ source: "project",
92
+ sourcePath: project.sourcePath,
93
+ config: project.config
94
+ });
95
+ }
96
+ return mergeConfigLayers(layers);
97
+ }
98
+ async function repositoryTrust(root, config) {
99
+ const home = process.env.AGENT_OPS_HOME ?? homedir();
100
+ const state = localStatePaths(home);
101
+ const remote = (() => {
102
+ try {
103
+ return execFileSync("git", ["config", "--get", "remote.origin.url"], {
104
+ cwd: root,
105
+ encoding: "utf8"
106
+ }).trim();
107
+ }
108
+ catch {
109
+ return `local:${root}`;
110
+ }
111
+ })();
112
+ try {
113
+ const binding = await calculateTrustBinding({
114
+ repositoryPath: root,
115
+ remoteUrl: remote,
116
+ configHash: sha256(JSON.stringify(config)),
117
+ runtimeHash: sha256(CLI_VERSION)
118
+ });
119
+ return (await new FileTrustStore(state.trustStore, state.anchorDirectory).status(binding)).status;
120
+ }
121
+ catch {
122
+ return "UNTRUSTED";
123
+ }
124
+ }
125
+ async function confirmInit(plan) {
126
+ return await confirmPlan(formatInstallPlan(plan));
127
+ }
128
+ async function confirmPlan(text) {
129
+ process.stdout.write(text);
130
+ const prompt = createInterface({
131
+ input: process.stdin,
132
+ output: process.stdout
133
+ });
134
+ try {
135
+ const answer = await prompt.question("Apply this installation plan? [y/N]: ");
136
+ return ["y", "yes"].includes(answer.trim().toLowerCase());
137
+ }
138
+ finally {
139
+ prompt.close();
140
+ }
141
+ }
142
+ process.exitCode = await runCli(process.argv.slice(2), {
143
+ isTTY: process.stdin.isTTY === true && process.stdout.isTTY === true,
144
+ input: process.stdin,
145
+ output: process.stdout,
146
+ writeStdout: (value) => process.stdout.write(value),
147
+ writeStderr: (value) => process.stderr.write(value)
148
+ }, {
149
+ version: CLI_VERSION,
150
+ registry: createCommandRegistry(Object.fromEntries([
151
+ "init",
152
+ "config",
153
+ "trust",
154
+ "doctor",
155
+ "update",
156
+ "uninstall",
157
+ "task",
158
+ "verify",
159
+ "review"
160
+ ].map((command) => [command, async (args) => {
161
+ const root = args.scope === "user"
162
+ ? process.env.AGENT_OPS_HOME ?? homedir()
163
+ : process.cwd();
164
+ const isTTY = !args.json &&
165
+ process.stdin.isTTY === true &&
166
+ process.stdout.isTTY === true;
167
+ if (args.command === "init") {
168
+ return await runInitCommand({
169
+ args,
170
+ root,
171
+ adapters: commonHarnessAdapters(),
172
+ isTTY,
173
+ toolkitVersion: CLI_VERSION,
174
+ confirm: async (plan) => await confirmInit(plan)
175
+ });
176
+ }
177
+ if (args.command === "doctor") {
178
+ return await runDoctorCommand({ root });
179
+ }
180
+ if (args.command === "uninstall") {
181
+ return await runUninstallCommand({
182
+ args,
183
+ root,
184
+ isTTY,
185
+ confirm: async (plan) => await confirmPlan(formatUninstallPlan(plan))
186
+ });
187
+ }
188
+ if (args.command === "update") {
189
+ return await runUpdateCommand({
190
+ args,
191
+ root,
192
+ adapters: commonHarnessAdapters(),
193
+ registry: new NpmRegistryClient(),
194
+ isTTY,
195
+ confirm: async (plan) => await confirmPlan(formatUpdatePlan(plan)),
196
+ ...(args.targetVersion === undefined
197
+ ? {}
198
+ : { targetVersion: args.targetVersion })
199
+ });
200
+ }
201
+ const taskService = new TaskService(new FileTaskStore(join(root, ".agent-ops", "tasks", "state.json"), root));
202
+ if (args.command === "task") {
203
+ const sessionId = process.env.AGENT_OPS_SESSION_ID;
204
+ return await runTaskCommand({
205
+ args,
206
+ service: taskService,
207
+ ...(sessionId === undefined ? {} : { sessionId })
208
+ });
209
+ }
210
+ if (args.command === "review") {
211
+ return await runReviewCommand({
212
+ args,
213
+ authorized: args.yes
214
+ });
215
+ }
216
+ if (args.command === "config") {
217
+ return explainConfigCommand(await loadEffectiveConfig(root, args.scope === "user" ? "user" : "project"));
218
+ }
219
+ if (args.command === "verify") {
220
+ const merged = await loadEffectiveConfig(root, args.scope === "user" ? "user" : "project");
221
+ const config = merged.config;
222
+ const trustStatus = await repositoryTrust(root, config);
223
+ return await runVerifyCommand({
224
+ args,
225
+ taskService,
226
+ service: new VerificationService({
227
+ root,
228
+ scope: args.scope === "user" ? "user" : "project",
229
+ config,
230
+ gitRunner: {
231
+ run: async (gitArgs) => {
232
+ try {
233
+ return {
234
+ exitCode: 0,
235
+ stdout: execFileSync("git", [...gitArgs], {
236
+ cwd: root,
237
+ encoding: "buffer",
238
+ stdio: ["ignore", "pipe", "ignore"]
239
+ })
240
+ };
241
+ }
242
+ catch (error) {
243
+ const failure = error;
244
+ return {
245
+ exitCode: failure.status ?? 1,
246
+ stdout: failure.stdout ?? new Uint8Array()
247
+ };
248
+ }
249
+ }
250
+ },
251
+ processRunner: new NodeVerificationProcessRunner(),
252
+ taskService,
253
+ evidenceStore: new FileEvidenceStore(root, root),
254
+ trusted: trustStatus === "TRUSTED"
255
+ })
256
+ });
257
+ }
258
+ if (args.command === "trust") {
259
+ const config = (await loadEffectiveConfig(root, args.scope === "user" ? "user" : "project")).config;
260
+ const state = localStatePaths(process.env.AGENT_OPS_HOME ?? homedir());
261
+ const remote = (() => {
262
+ try {
263
+ return execFileSync("git", ["config", "--get", "remote.origin.url"], {
264
+ cwd: root,
265
+ encoding: "utf8"
266
+ }).trim();
267
+ }
268
+ catch {
269
+ return `local:${root}`;
270
+ }
271
+ })();
272
+ const binding = await calculateTrustBinding({
273
+ repositoryPath: root,
274
+ remoteUrl: remote,
275
+ configHash: sha256(JSON.stringify(config)),
276
+ runtimeHash: sha256(CLI_VERSION)
277
+ });
278
+ return await runTrustCommand({
279
+ action: args.action,
280
+ yes: args.yes,
281
+ isTTY,
282
+ calculateBinding: async () => binding,
283
+ presentBinding: async () => undefined,
284
+ confirmGrant: async () => await confirmPlan(JSON.stringify(binding, null, 2)),
285
+ store: new FileTrustStore(state.trustStore, state.anchorDirectory)
286
+ });
287
+ }
288
+ return errorEnvelope("CLI_COMMAND_UNAVAILABLE", `Command is not implemented yet: ${args.command}`);
289
+ }])))
290
+ });
@@ -0,0 +1,80 @@
1
+ import { CliArgumentError, parseArgs } from "./args.js";
2
+ import { errorEnvelope, okEnvelope, writeEnvelope } from "./output.js";
3
+ import { completeInitChoices } from "./wizard.js";
4
+ export const HELP_TEXT = `Usage: agent-ops <command> [options]
5
+
6
+ Commands:
7
+ init Plan or install agent-ops
8
+ config explain
9
+ Inspect effective configuration provenance
10
+ trust <status|grant|revoke>
11
+ Manage explicit repository trust
12
+ doctor Diagnose an installation
13
+ update Update managed artifacts
14
+ uninstall Remove managed artifacts
15
+ task <create|status|attach|complete|archive|export>
16
+ Manage independent task acceptance state
17
+ verify Run configured verification
18
+ review Run an independent review
19
+
20
+ Options:
21
+ --scope <project|user>
22
+ --harness <both|claude|codex>
23
+ --profile <core|advisory|guardrails> Repeatable
24
+ --task <id>
25
+ --target-version <version> Update target version (offline-capable)
26
+ --title <text>
27
+ --criterion <json> Repeatable
28
+ --evidence <criterion-id=reference> Repeatable
29
+ --session <id>
30
+ --dry-run
31
+ --json
32
+ --yes
33
+ --help
34
+ --version
35
+ `;
36
+ function wantsJson(argv) {
37
+ return argv.includes("--json");
38
+ }
39
+ function writeAndReturn(io, envelope, json, exitCode) {
40
+ writeEnvelope(io, envelope, json);
41
+ return exitCode;
42
+ }
43
+ export async function runCli(argv, io, services) {
44
+ const json = wantsJson(argv);
45
+ let args;
46
+ try {
47
+ args = parseArgs(argv);
48
+ }
49
+ catch (error) {
50
+ if (error instanceof CliArgumentError) {
51
+ return writeAndReturn(io, errorEnvelope(error.code, error.message), json, 2);
52
+ }
53
+ return writeAndReturn(io, errorEnvelope("CLI_INTERNAL_ERROR", "Unable to parse command arguments."), json, 1);
54
+ }
55
+ if (args.command === "help") {
56
+ return writeAndReturn(io, okEnvelope("CLI_HELP", { text: HELP_TEXT }), args.json, 0);
57
+ }
58
+ if (args.command === "version") {
59
+ return writeAndReturn(io, okEnvelope("CLI_VERSION", { version: services.version }), args.json, 0);
60
+ }
61
+ try {
62
+ if (args.command === "init") {
63
+ args = await completeInitChoices(args, args.json ? { ...io, isTTY: false } : io);
64
+ }
65
+ const execute = args.command === "help" || args.command === "version"
66
+ ? services.execute
67
+ : services.registry?.get(args.command) ?? services.execute;
68
+ if (execute === undefined) {
69
+ return writeAndReturn(io, errorEnvelope("CLI_COMMAND_UNAVAILABLE", `Command is not implemented yet: ${args.command}`), args.json, 1);
70
+ }
71
+ const result = await execute(args);
72
+ return writeAndReturn(io, result, args.json, result.status === "ok" ? 0 : 1);
73
+ }
74
+ catch (error) {
75
+ if (error instanceof CliArgumentError) {
76
+ return writeAndReturn(io, errorEnvelope(error.code, error.message), args.json, 2);
77
+ }
78
+ return writeAndReturn(io, errorEnvelope("CLI_INTERNAL_ERROR", "Command execution failed."), args.json, 1);
79
+ }
80
+ }
@@ -0,0 +1,8 @@
1
+ import { explainConfig } from "../../../../runtime/src/config/explain.js";
2
+ import { okEnvelope } from "../output.js";
3
+ export function explainConfigCommand(merged) {
4
+ return okEnvelope("CONFIG_EXPLAIN", {
5
+ ...explainConfig(merged),
6
+ message: "Effective configuration provenance calculated."
7
+ });
8
+ }
@@ -0,0 +1,32 @@
1
+ import { doctorInstallation } from "../../../../runtime/src/install/doctor.js";
2
+ function formatDoctorReport(report) {
3
+ return `${[
4
+ "Installation doctor",
5
+ ...report.checks.map(({ id, status, message }) => `- ${status} ${id}: ${message}`)
6
+ ].join("\n")}\n`;
7
+ }
8
+ export async function runDoctorCommand(options) {
9
+ const report = await doctorInstallation(options);
10
+ const hasFailure = report.checks.some(({ status }) => status === "FAIL");
11
+ const hasUnknown = report.checks.some(({ status }) => status === "UNKNOWN");
12
+ const code = hasFailure
13
+ ? "DOCTOR_FAILED"
14
+ : hasUnknown
15
+ ? "DOCTOR_UNKNOWN"
16
+ : "DOCTOR_OK";
17
+ const message = hasFailure
18
+ ? "Installation diagnostics found failures."
19
+ : hasUnknown
20
+ ? "Installation diagnostics contain unknown checks."
21
+ : "Installation diagnostics passed.";
22
+ return {
23
+ code,
24
+ status: hasFailure || hasUnknown ? "error" : "ok",
25
+ data: {
26
+ report,
27
+ message,
28
+ text: formatDoctorReport(report)
29
+ },
30
+ errors: hasFailure || hasUnknown ? [{ code, message }] : []
31
+ };
32
+ }
@@ -0,0 +1,16 @@
1
+ export function createCommandRegistry(handlers) {
2
+ const entries = new Map();
3
+ for (const [command, handler] of Object.entries(handlers)) {
4
+ if (handler !== undefined) {
5
+ entries.set(command, handler);
6
+ }
7
+ }
8
+ return {
9
+ get(command) {
10
+ return entries.get(command);
11
+ },
12
+ commands() {
13
+ return [...entries.keys()];
14
+ }
15
+ };
16
+ }
@@ -0,0 +1,65 @@
1
+ import { AgentOpsError } from "../../../../runtime/src/fs/paths.js";
2
+ import { createInstallPlan } from "../../../../runtime/src/install/plan.js";
3
+ import { applyInstallPlan } from "../../../../runtime/src/install/apply.js";
4
+ import { okEnvelope } from "../output.js";
5
+ import { formatOperationPlan } from "../plan-output.js";
6
+ export function formatInstallPlan(plan) {
7
+ return formatOperationPlan({
8
+ title: "Installation plan",
9
+ metadata: [
10
+ `Scope: ${plan.scope}`,
11
+ `Harness: ${plan.harness}`,
12
+ `Profiles: ${plan.profiles.join(", ")}`
13
+ ],
14
+ operations: plan.operations
15
+ });
16
+ }
17
+ function initError(code, message, plan) {
18
+ return {
19
+ code,
20
+ status: "error",
21
+ data: { applied: false, plan, message },
22
+ errors: [{ code, message }]
23
+ };
24
+ }
25
+ export async function runInitCommand(options) {
26
+ const { args } = options;
27
+ if (args.command !== "init" ||
28
+ args.scope === undefined ||
29
+ args.harness === undefined ||
30
+ args.profiles.length === 0) {
31
+ throw new AgentOpsError("INIT_CHOICES_REQUIRED", "Init requires complete scope, harness, and profile choices.");
32
+ }
33
+ const plan = await createInstallPlan({
34
+ root: options.root,
35
+ scope: args.scope,
36
+ harness: args.harness,
37
+ profiles: args.profiles,
38
+ adapters: options.adapters,
39
+ ...(options.toolkitVersion === undefined
40
+ ? {}
41
+ : { toolkitVersion: options.toolkitVersion })
42
+ });
43
+ if (args.dryRun) {
44
+ return okEnvelope("INIT_PLAN_READY", {
45
+ applied: false,
46
+ plan,
47
+ message: "Installation plan calculated; no files were written.",
48
+ text: formatInstallPlan(plan)
49
+ });
50
+ }
51
+ if (!args.yes && !options.isTTY) {
52
+ return initError("INIT_CONFIRMATION_REQUIRED", "Non-interactive init requires --yes after all choices are explicit.", plan);
53
+ }
54
+ if (!args.yes &&
55
+ options.isTTY &&
56
+ !(await options.confirm(plan))) {
57
+ return initError("INIT_CANCELLED", "Installation was cancelled; no files were written.", plan);
58
+ }
59
+ await applyInstallPlan(options.root, plan);
60
+ return okEnvelope("INIT_APPLIED", {
61
+ applied: true,
62
+ plan,
63
+ message: "Loop Engineering Toolkit installation applied."
64
+ });
65
+ }
@@ -0,0 +1,71 @@
1
+ import { buildReviewPacket } from "../../../../runtime/src/review/packet.js";
2
+ import { runIndependentReview } from "../../../../runtime/src/review/runner.js";
3
+ import { resolveReviewRole } from "../../../../runtime/src/review/roles.js";
4
+ import { okEnvelope } from "../output.js";
5
+ function harness(value) {
6
+ return value === "claude" ? "claude" : "codex";
7
+ }
8
+ export async function runReviewCommand(options) {
9
+ const ids = options.args.criteria ?? [];
10
+ const criteria = ids.map((id) => ({
11
+ id,
12
+ description: id
13
+ }));
14
+ const evidenceRequirements = (options.args.evidence ?? []).map((value) => {
15
+ const separator = value.indexOf("=");
16
+ return {
17
+ criterionId: separator < 0 ? value : value.slice(0, separator),
18
+ requirement: separator < 0 ? value : value.slice(separator + 1)
19
+ };
20
+ });
21
+ const selectedHarness = harness(options.args.harness);
22
+ const role = resolveReviewRole(options.role ?? "independent-review", options.roles ?? []);
23
+ const result = await runIndependentReview({
24
+ invocation: {
25
+ harness: role?.harness ?? selectedHarness,
26
+ model: role?.model ?? options.model ?? "configured",
27
+ effort: role?.effort ?? options.effort ?? "configured",
28
+ packet: buildReviewPacket({
29
+ request: "Review the requested implementation.",
30
+ criteria,
31
+ artifactRefs: [],
32
+ evidenceRequirements
33
+ })
34
+ },
35
+ authorized: options.authorized,
36
+ execute: options.execute ?? (async () => ({
37
+ status: "NOT_RUN",
38
+ reason: "missing-cli"
39
+ }))
40
+ });
41
+ const message = result.status === "PASS"
42
+ ? "Independent review passed."
43
+ : result.status === "FAIL"
44
+ ? "Independent review failed."
45
+ : "Independent review was not run.";
46
+ const data = {
47
+ message,
48
+ result,
49
+ text: [
50
+ message,
51
+ `Status: ${result.status}`,
52
+ `Harness: ${result.harness}; model: ${result.model}; effort: ${result.effort}.`,
53
+ ...(result.reason === undefined ? [] : [`Reason: ${result.reason}.`]),
54
+ ...(result.results === undefined
55
+ ? []
56
+ : result.results.map((item) => `${item.criterionId}: ${item.status} [${item.evidence.join(", ")}]`)),
57
+ result.prompt,
58
+ ""
59
+ ].join("\n")
60
+ };
61
+ if (result.status === "PASS") {
62
+ return okEnvelope("REVIEW_RESULT", data);
63
+ }
64
+ const code = result.status === "FAIL" ? "REVIEW_FAILED" : "REVIEW_NOT_RUN";
65
+ return {
66
+ code,
67
+ status: "error",
68
+ data,
69
+ errors: [{ code, message }]
70
+ };
71
+ }