@kylecheng3146/agent-ops 0.1.11 → 0.1.13

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.
@@ -6,6 +6,7 @@ import { formatOperationPlan } from "../plan-output.js";
6
6
  import { toPublicInstallPlan } from "../public-plan.js";
7
7
  export function formatInstallPlan(plan) {
8
8
  const hooks = plan.manifest.hooks ?? [];
9
+ const detectedVerification = plan.detectedVerification;
9
10
  return formatOperationPlan({
10
11
  title: "Installation plan",
11
12
  metadata: [
@@ -17,6 +18,12 @@ export function formatInstallPlan(plan) {
17
18
  : [
18
19
  "Hooks:",
19
20
  ...hooks.map((hook) => ` - ${hook.harness}: ${hook.path} (${hook.events.join(", ")})`)
21
+ ]),
22
+ ...(detectedVerification.length === 0
23
+ ? []
24
+ : [
25
+ "Detected verifiers (review before confirming):",
26
+ ...detectedVerification.map((command) => ` - ${command.id}: ${command.command} ${command.args.join(" ")}`.trimEnd())
20
27
  ])
21
28
  ],
22
29
  operations: toPublicInstallPlan(plan).operations
@@ -3,6 +3,7 @@ import { okEnvelope } from "../output.js";
3
3
  import { formatOperationPlan } from "../plan-output.js";
4
4
  import { toPublicUpdatePlan } from "../public-plan.js";
5
5
  export function formatUpdatePlan(plan) {
6
+ const detectedVerification = plan.installation.detectedVerification;
6
7
  return formatOperationPlan({
7
8
  title: "Update plan",
8
9
  metadata: [
@@ -12,7 +13,13 @@ export function formatUpdatePlan(plan) {
12
13
  ? "none"
13
14
  : plan.migrationSteps
14
15
  .map(({ fromVersion, toVersion }) => `${fromVersion} -> ${toVersion}`)
15
- .join(", ")}`
16
+ .join(", ")}`,
17
+ ...(detectedVerification.length === 0
18
+ ? []
19
+ : [
20
+ "Detected verifiers (review before confirming):",
21
+ ...detectedVerification.map((command) => ` - ${command.id}: ${command.command} ${command.args.join(" ")}`.trimEnd())
22
+ ])
16
23
  ],
17
24
  operations: toPublicUpdatePlan(plan).installation.operations
18
25
  });
@@ -42,7 +42,8 @@ export function toPublicInstallPlan(plan) {
42
42
  profiles: plan.profiles,
43
43
  capabilities: plan.capabilities,
44
44
  manifest: plan.manifest,
45
- operations: toPublicOperations(plan.operations)
45
+ operations: toPublicOperations(plan.operations),
46
+ detectedVerification: plan.detectedVerification
46
47
  };
47
48
  }
48
49
  export function toPublicUpdatePlan(plan) {
@@ -241,7 +241,7 @@ export function managedRules(descriptor, context) {
241
241
  ""
242
242
  ];
243
243
  if (context.capabilities.includes("rules")) {
244
- lines.push("For every change:", "", "1. Define two to five mechanically verifiable acceptance criteria.", "2. Inspect the smallest relevant scope and preserve unrelated changes.", "3. Apply the smallest safe change.", "4. Run evidence-producing verification for every criterion.", "5. Obtain independent review before claiming completion.", "", "Treat `.agent-ops/config.json` as verifier authority. Discovery output is", "only a proposal until a user confirms it. Repository commands require an", "explicit matching trust record; installation approval never grants trust.", "");
244
+ lines.push("For every change:", "", "1. Define two to five mechanically verifiable acceptance criteria.", "2. Inspect the smallest relevant scope and preserve unrelated changes.", "3. Apply the smallest safe change.", "4. Run evidence-producing verification for every criterion.", "5. Obtain independent review before claiming completion, via", " `agent-ops review` (or the CLI's equivalent invocation). Never call a", " review-target CLI (agy, codex, claude) directly — direct calls skip", " the enforced read-only sandbox flags and can hang or fail on command", " permission prompts.", "", "Treat `.agent-ops/config.json` as verifier authority. Discovery output is", "only a proposal until a user confirms it. Repository commands require an", "explicit matching trust record; installation approval never grants trust.", "");
245
245
  }
246
246
  if (context.capabilities.includes("lifecycle-summary")) {
247
247
  lines.push("Advisory lifecycle summaries and local logs are informational. Advisory", "failures must remain fail-open and cannot become verification evidence.", "");
@@ -1,5 +1,6 @@
1
1
  import { lstat, readFile } from "node:fs/promises";
2
2
  import { CONFIG_SCHEMA_VERSION, MANIFEST_SCHEMA_VERSION } from "../contracts.js";
3
+ import { discoverProject } from "../discovery/index.js";
3
4
  import { sha256 } from "../fs/hash.js";
4
5
  import { applyManagedBlock, managedBlockMarkers, removeManagedBlock } from "../fs/managed-block.js";
5
6
  import { formatInstallManifest, parseInstallManifest } from "../fs/manifest.js";
@@ -40,16 +41,50 @@ async function readCurrentFile(root, path) {
40
41
  throw error;
41
42
  }
42
43
  }
43
- function formatConfig(profiles, existing, reviewTargets = []) {
44
+ /**
45
+ * Converts a stack-detected verifier proposal into a verification command.
46
+ * Proposal IDs use a colon-separated namespace (e.g. "node:test"); config
47
+ * IDs must be kebab-case identifiers.
48
+ */
49
+ function verificationCommandFromProposal(proposal) {
50
+ return {
51
+ id: proposal.id.replace(/:/g, "-"),
52
+ command: proposal.command,
53
+ args: proposal.args,
54
+ cwd: proposal.cwd,
55
+ required: proposal.required,
56
+ evidence: proposal.evidence
57
+ };
58
+ }
59
+ /**
60
+ * Verification commands are never invented for a project that already
61
+ * declares them; detection only fills the gap while the field is empty, so
62
+ * a deliberate "remove all verifiers" edit is not silently reversed by a
63
+ * different stack adapter next run.
64
+ */
65
+ async function detectVerificationCommands(root) {
66
+ const discovery = await discoverProject(root);
67
+ if (discovery.kind !== "project") {
68
+ return [];
69
+ }
70
+ return discovery.proposals
71
+ .filter((proposal) => proposal.confidence === "high")
72
+ .map(verificationCommandFromProposal);
73
+ }
74
+ function formatConfig(profiles, existing, reviewTargets = [], detectedCommands = []) {
44
75
  // Absent reviewRoles means external review is disabled; an empty selection
45
76
  // must therefore omit the field rather than write an empty array.
46
77
  const reviewRoles = reviewTargets.length > 0
47
78
  ? [{ role: "independent-review", targets: [...reviewTargets] }]
48
79
  : existing?.reviewRoles;
80
+ const verification = existing?.verification !== undefined &&
81
+ existing.verification.commands.length > 0
82
+ ? existing.verification
83
+ : { commands: detectedCommands };
49
84
  return `${JSON.stringify({
50
85
  schemaVersion: CONFIG_SCHEMA_VERSION,
51
86
  profiles,
52
- verification: existing?.verification ?? { commands: [] },
87
+ verification,
53
88
  features: existing?.features ?? {
54
89
  stopVerification: {
55
90
  enabled: false
@@ -92,7 +127,11 @@ async function planConfig(root, profiles, existingManifest, suppliedConfig, revi
92
127
  }
93
128
  existingConfig = result.value;
94
129
  }
95
- const content = formatConfig(profiles, existingConfig, reviewTargets);
130
+ const detectedCommands = existingConfig === undefined ||
131
+ existingConfig.verification.commands.length === 0
132
+ ? await detectVerificationCommands(root)
133
+ : [];
134
+ const content = formatConfig(profiles, existingConfig, reviewTargets, detectedCommands);
96
135
  return {
97
136
  operation: {
98
137
  kind: "write",
@@ -105,7 +144,8 @@ async function planConfig(root, profiles, existingManifest, suppliedConfig, revi
105
144
  path: CONFIG_PATH,
106
145
  hash: sha256(content),
107
146
  owner: "agent-ops"
108
- }
147
+ },
148
+ detectedVerification: detectedCommands
109
149
  };
110
150
  }
111
151
  function pathKey(path) {
@@ -528,6 +568,7 @@ export async function createInstallPlan(options) {
528
568
  profiles: resolved.profiles,
529
569
  capabilities: resolved.capabilities,
530
570
  manifest,
531
- operations
571
+ operations,
572
+ detectedVerification: config.detectedVerification
532
573
  };
533
574
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kylecheng3146/agent-ops",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "Evidence-driven development loops for Codex, Claude Code, and opencode",
5
5
  "type": "module",
6
6
  "license": "MIT",