@kylecheng3146/agent-ops 0.1.12 → 0.1.14

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) {
@@ -99,9 +99,12 @@ const AGENTS_ROUTING = {
99
99
  "## Loop Engineering\n\nUse `.agent-ops/AGENTS.md` as the canonical Loop Engineering specification for this project.\n"
100
100
  ]
101
101
  };
102
+ // ponytail: Claude Code only auto-loads files via the `@path` import syntax.
103
+ // Prose ("Load X as the baseline") is inert there, so the import line is the routing.
102
104
  const CLAUDE_ROUTING = {
103
- desired: "## Loop Engineering\n\nLoad `.agent-ops/CLAUDE.md` as the agent-ops managed baseline.\nProject-specific instructions in this file remain authoritative.\n",
105
+ desired: "## Loop Engineering\n\n@.agent-ops/CLAUDE.md\n\nThe file above is the agent-ops managed baseline.\nProject-specific instructions in this file remain authoritative.\n",
104
106
  legacy: [
107
+ "## Loop Engineering\n\nLoad `.agent-ops/CLAUDE.md` as the agent-ops managed baseline.\nProject-specific instructions in this file remain authoritative.\n",
105
108
  "## Loop Engineering\n\nUse `.agent-ops/CLAUDE.md` as the canonical Loop Engineering specification for this project.\n"
106
109
  ]
107
110
  };
@@ -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.12",
3
+ "version": "0.1.14",
4
4
  "description": "Evidence-driven development loops for Codex, Claude Code, and opencode",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,4 +1,6 @@
1
1
  ## Loop Engineering
2
2
 
3
- Load `.agent-ops/CLAUDE.md` as the agent-ops managed baseline.
3
+ @.agent-ops/CLAUDE.md
4
+
5
+ The file above is the agent-ops managed baseline.
4
6
  Project-specific instructions in this file remain authoritative.