@besales/ops-framework 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 (70) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +328 -0
  3. package/bin/build-check-context.mjs +67 -0
  4. package/bin/build-execution-ledger.mjs +54 -0
  5. package/bin/estimate-llm-input.mjs +160 -0
  6. package/bin/guard-task.mjs +384 -0
  7. package/bin/hash-task-artifacts.mjs +44 -0
  8. package/bin/init-project.mjs +49 -0
  9. package/bin/intake-execution-feedback.mjs +207 -0
  10. package/bin/intake-feedback.test.mjs +73 -0
  11. package/bin/learning-loop.mjs +658 -0
  12. package/bin/learning-loop.test.mjs +175 -0
  13. package/bin/lib/bootstrap-utils.mjs +542 -0
  14. package/bin/lib/bootstrap-utils.test.mjs +156 -0
  15. package/bin/lib/check-context-utils.mjs +1448 -0
  16. package/bin/lib/check-context-utils.test.mjs +497 -0
  17. package/bin/lib/execution-ledger-utils.mjs +162 -0
  18. package/bin/lib/execution-ledger-utils.test.mjs +74 -0
  19. package/bin/lib/llm-input-pack-utils.mjs +663 -0
  20. package/bin/lib/llm-input-pack-utils.test.mjs +262 -0
  21. package/bin/lib/project-config.mjs +229 -0
  22. package/bin/lib/project-config.test.mjs +102 -0
  23. package/bin/lib/task-manifest-utils.mjs +512 -0
  24. package/bin/lib/task-manifest-utils.test.mjs +218 -0
  25. package/bin/lib/task-metrics-utils.mjs +63 -0
  26. package/bin/lib/task-metrics-utils.test.mjs +40 -0
  27. package/bin/lib/test-setup.mjs +37 -0
  28. package/bin/new-task.mjs +42 -0
  29. package/bin/ops-agent.mjs +81 -0
  30. package/bin/preflight.mjs +56 -0
  31. package/bin/providers/external-cli-checker.mjs +190 -0
  32. package/bin/providers/openai-checker.mjs +62 -0
  33. package/bin/quality-gates.mjs +92 -0
  34. package/bin/run-check.mjs +559 -0
  35. package/bin/run-plan-check-loop.mjs +392 -0
  36. package/bin/run-verify.mjs +627 -0
  37. package/bin/self-lint.mjs +88 -0
  38. package/bin/supervisor-turn.mjs +146 -0
  39. package/bin/supervisor-turn.test.mjs +72 -0
  40. package/bin/task-manifest.mjs +57 -0
  41. package/bin/task-metrics.mjs +48 -0
  42. package/bin/transition.mjs +94 -0
  43. package/bin/validate-check-artifacts.mjs +418 -0
  44. package/config/default-agents.json +100 -0
  45. package/package.json +28 -0
  46. package/playbooks/checker-context.md +9 -0
  47. package/playbooks/complexity-performance.md +13 -0
  48. package/playbooks/production-rollout.md +9 -0
  49. package/playbooks/source-sync-provider.md +9 -0
  50. package/playbooks/ui-acceptance.md +9 -0
  51. package/prompts/checker.md +170 -0
  52. package/prompts/executor.md +54 -0
  53. package/prompts/planner.md +128 -0
  54. package/prompts/researcher.md +44 -0
  55. package/prompts/supervisor.md +337 -0
  56. package/prompts/verifier.md +128 -0
  57. package/templates/brief.md +15 -0
  58. package/templates/check-resolution.md +69 -0
  59. package/templates/check-result.json +32 -0
  60. package/templates/check.md +46 -0
  61. package/templates/execution-feedback.md +25 -0
  62. package/templates/execution.md +101 -0
  63. package/templates/human-gate-summary.md +49 -0
  64. package/templates/orchestration-log.md +8 -0
  65. package/templates/plan.md +86 -0
  66. package/templates/research.md +13 -0
  67. package/templates/retrospective.md +48 -0
  68. package/templates/status.md +53 -0
  69. package/templates/verify-result.json +19 -0
  70. package/templates/verify.md +41 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Created the first local shared Ops Framework package boundary.
6
+ - Added `ops-agent` command dispatch for the current agent command surface.
7
+ - Added project config resolution through `ops/project.ops.yaml`.
8
+ - Kept task, memory, cache and project-specific provider overrides outside shared defaults.
9
+ - Added `ops-agent init` and `ops-agent new-task` for project bootstrap and task scaffolding.
10
+ - Added bootstrap tests for idempotent project setup, portable generated config and fixture lifecycle smoke.
package/README.md ADDED
@@ -0,0 +1,328 @@
1
+ # Ops Framework
2
+
3
+ Reusable human-in-the-loop delivery framework for planning, checking, executing and verifying software tasks across separate project repositories.
4
+
5
+ The package owns the process engine and reusable assets. Each project owns its task history, memory, cache, project playbook overlays and local provider overrides.
6
+
7
+ ## What Lives In The Package
8
+
9
+ - `bin/`: CLI commands and shared implementation utilities.
10
+ - `prompts/`: reusable agent prompts for supervisor, researcher, planner, checker, executor and verifier.
11
+ - `templates/`: task artifact templates.
12
+ - `playbooks/`: shared reusable procedures for UI acceptance, complexity, rollout, provider/source-sync and checker context.
13
+ - `config/default-agents.json`: portable defaults with environment placeholders, not local machine paths.
14
+
15
+ ## What Stays In Each Project
16
+
17
+ - `ops/project.ops.yaml`: project binding file.
18
+ - `ops/agent-pipeline/tasks/`: task artifacts and history.
19
+ - `ops/agent-pipeline/memory/`: project memory.
20
+ - `ops/agent-pipeline/playbooks/`: project playbook overlays with project-specific routes, commands, services, environments and provider quirks.
21
+ - `ops/agent-pipeline/cache/`: project-local generated cache.
22
+ - `ops/agent-pipeline/config/agents.json`: optional project/user provider override.
23
+ - Project-specific deferred work, SQL, runtime notes and learning candidates.
24
+
25
+ Do not put project memory, task history, secrets, project-specific paths, project service names, routes, provider names or local absolute provider paths into this shared package unless they are explicitly marked as generic examples.
26
+
27
+ ## Layer Model
28
+
29
+ Ops Framework has three layers:
30
+
31
+ - Shared framework core: CLI, lifecycle, prompts, templates, config defaults and generic gates.
32
+ - Shared playbooks: reusable procedures that apply across projects.
33
+ - Project overlays: project memory, project playbooks, tasks, cache, local providers and runtime commands.
34
+
35
+ When a task has relevant risk triggers, checker/verifier context can include both layers:
36
+
37
+ ```text
38
+ Shared Playbook: UI Acceptance
39
+ Project Overlay: UI Acceptance
40
+ ```
41
+
42
+ Shared playbooks stay portable. Project overlays add concrete routes, commands, services, environments and known project quirks.
43
+
44
+ ## Project Config
45
+
46
+ The recommended setup is:
47
+
48
+ ```bash
49
+ corepack yarn dlx @besales/ops-framework@latest init --project-name ExampleProject --install-scripts
50
+ ```
51
+
52
+ This creates the project-owned `ops/**` structure, including `ops/project.ops.yaml`, task roots, cache, memory files and project-local agent config. With `--install-scripts`, it also writes package-manager scripts that call the pinned framework package through `corepack yarn dlx`; it does not add the framework as a dependency, so production installs do not need access to a local framework checkout.
53
+
54
+ The generated scripts look like:
55
+
56
+ ```json
57
+ {
58
+ "scripts": {
59
+ "ops": "corepack yarn dlx @besales/ops-framework@0.1.0",
60
+ "agent:run-check": "corepack yarn dlx @besales/ops-framework@0.1.0 run-check",
61
+ "agent:run-verify": "corepack yarn dlx @besales/ops-framework@0.1.0 run-verify"
62
+ }
63
+ }
64
+ ```
65
+
66
+ Use `--framework-version 0.1.0` to pin a specific version, or `--framework-package @scope/name` for a different registry package name.
67
+
68
+ The generated `ops/project.ops.yaml` shape is:
69
+
70
+ ```yaml
71
+ name: ExampleProject
72
+ ops:
73
+ legacyPipelineDir: ops/agent-pipeline
74
+ tasksDir: ops/agent-pipeline/tasks
75
+ memoryDir: ops/agent-pipeline/memory
76
+ playbooksDir: ops/agent-pipeline/playbooks
77
+ cacheDir: ops/agent-pipeline/cache
78
+ agents:
79
+ configFile: ops/agent-pipeline/config/agents.json
80
+ risk:
81
+ uiRoots:
82
+ - apps/web
83
+ backendRoots:
84
+ - apps/api
85
+ workerRoots:
86
+ - apps/workers
87
+ ```
88
+
89
+ `ops-agent` walks upward from the current working directory until it finds `ops/project.ops.yaml`.
90
+
91
+ It then resolves:
92
+
93
+ - project artifacts from the configured project paths;
94
+ - framework prompts/templates/config from this package;
95
+ - shared playbooks from the package and project playbook overlays from `ops.playbooksDir`;
96
+ - project provider overrides from `agents.configFile`, if present.
97
+
98
+ Risk roots are project-owned. They decide when generic triggers such as UI, user-visible API, worker, production or performance gates apply. Do not hardcode one project's app layout into shared playbooks or shared risk classification.
99
+
100
+ Fresh bootstrap projects start with commented placeholder root examples. Until a project fills them, `build-check-context` and `preflight` warn that path-based UI/API/worker detection is disabled or limited.
101
+
102
+ If no project config exists, the resolver falls back to a legacy `ops/agent-pipeline` layout when it can find one.
103
+
104
+ ## Optimization Gate
105
+
106
+ Optimization is built into the normal Plan -> Check -> Execute -> Verify flow, but it is bounded by risk tier so delivery stays fast:
107
+
108
+ - `O0`: no dedicated optimization section.
109
+ - `O1`: normal implementation checklist.
110
+ - `O2`: one focused review for touched UI/API/read-model hot paths.
111
+ - `O3`: one focused review plus one representative measurement for workers, materializers, source-sync/provider or production-runtime hot paths.
112
+
113
+ For O2/O3 work, `plan.md` must include `## Optimization Strategy` with tier, hot paths, expected data size, chosen efficient approach, avoided anti-patterns and a stop rule. `execution.md` must then include `## Optimization Review Evidence` before Verify can pass.
114
+
115
+ The framework blocks obvious inefficiency early, but speculative or broad optimization ideas should be deferred instead of expanding the task.
116
+
117
+ ## CLI
118
+
119
+ From a project root, run commands through the package entry point:
120
+
121
+ ```bash
122
+ corepack yarn dlx @besales/ops-framework@0.1.0 init --project-name ExampleProject --install-scripts
123
+ corepack yarn dlx @besales/ops-framework@0.1.0 new-task TASK-001-example --title "Example task"
124
+ corepack yarn dlx @besales/ops-framework@0.1.0 manifest TASK-001-example
125
+ corepack yarn dlx @besales/ops-framework@0.1.0 preflight TASK-001-example --target execute
126
+ corepack yarn dlx @besales/ops-framework@0.1.0 build-check-context TASK-001-example
127
+ corepack yarn dlx @besales/ops-framework@0.1.0 run-check TASK-001-example
128
+ corepack yarn dlx @besales/ops-framework@0.1.0 run-verify TASK-001-example
129
+ ```
130
+
131
+ When installed as a package, the intended command is:
132
+
133
+ ```bash
134
+ ops-agent init --project-name ExampleProject
135
+ ops-agent new-task TASK-001-example --title "Example task"
136
+ ops-agent manifest TASK-001-example
137
+ ```
138
+
139
+ For local package development in another project, use a `file:` dependency:
140
+
141
+ ```json
142
+ {
143
+ "devDependencies": {
144
+ "@besales/ops-framework": "file:../shared-platform/packages/ops-framework"
145
+ }
146
+ }
147
+ ```
148
+
149
+ Do not commit that `file:` dependency to production projects. It is only for package development because production builders usually do not have a sibling `../shared-platform` checkout.
150
+
151
+ ## Supported Commands
152
+
153
+ - `init`
154
+ - `new-task`
155
+ - `manifest`
156
+ - `preflight`
157
+ - `transition`
158
+ - `build-check-context`
159
+ - `validate-check-artifacts`
160
+ - `estimate-llm-input`
161
+ - `quality-gates`
162
+ - `run-check`
163
+ - `run-verify`
164
+ - `hash-task-artifacts`
165
+ - `build-execution-ledger`
166
+ - `task-metrics`
167
+ - `run-plan-check-loop`
168
+ - `supervisor-turn`
169
+ - `intake-feedback`
170
+ - `intake-execution-feedback`
171
+ - `guard-task`
172
+ - `memory-candidates`
173
+ - `learning-index`
174
+ - `learning-review`
175
+ - `update-memory`
176
+ - `learning-audit`
177
+ - `learning-report`
178
+ - `test/self-test`
179
+
180
+ ## Learning Loop
181
+
182
+ Learning is controlled and human-approved:
183
+
184
+ ```text
185
+ retrospective/feedback/check/verify artifacts
186
+ -> memory/playbook candidates
187
+ -> learning index
188
+ -> human promote/defer/reject
189
+ -> update memory/playbooks
190
+ ```
191
+
192
+ Use:
193
+
194
+ ```bash
195
+ ops-agent memory-candidates
196
+ ops-agent learning-index
197
+ ops-agent learning-review
198
+ ops-agent learning-audit
199
+ ops-agent update-memory --apply-approved
200
+ ops-agent learning-report
201
+ ```
202
+
203
+ `memory-candidates` creates structured learning cards with source, reason hash, learning layer, confidence, problem, lesson, repeat risk, proposed wording and suggested target. `learning-index` turns those cards into human-reviewable decisions. `update-memory` only writes approved entries when `--apply-approved` is passed.
204
+
205
+ `learning-review` writes `ops/agent-pipeline/memory/learning-review.md`, a human approval pack with each pending candidate and the allowed decisions: `promote`, `defer`, `reject` or `rewrite`.
206
+
207
+ `learning-report` writes `ops/agent-pipeline/memory/learning-report.md`. Show the review pack before approval and the report during closeout so the human can see what the framework learned, what is still pending, and which approved entries were written to project memory or project playbooks.
208
+
209
+ Shared playbook candidates are intentionally manual-review only. Promote them through a separate reviewed framework task, not by auto-writing project-specific observations into the shared package.
210
+
211
+ ## Feedback Intake
212
+
213
+ Feedback is stage-agnostic. Any user question, correction, review note or learning observation during an active task should be captured before it is acted on:
214
+
215
+ ```bash
216
+ ops-agent intake-feedback TASK-001-example "Feedback text"
217
+ ```
218
+
219
+ This appends a classified event to `feedback.md`, updates `status.md`, appends `orchestration-log.md` and makes the event available to checker/verifier context and learning candidates.
220
+
221
+ Use `learning_capture` for process/memory/playbook observations that should feed retrospective without invalidating the current implementation scope.
222
+
223
+ For active task conversations, prefer the turn router:
224
+
225
+ ```bash
226
+ ops-agent supervisor-turn TASK-001-example "User message"
227
+ ```
228
+
229
+ It records the message, appends a `supervisor_turn` log event and prints the mandatory response contract:
230
+
231
+ ```text
232
+ TASK: ...
233
+ STAGE: ...
234
+ SUPERVISOR: active
235
+ FEEDBACK INTAKE: recorded (...)
236
+ ALLOWED NOW: ...
237
+ NOT ALLOWED NOW: ...
238
+ Следующий шаг: ...
239
+ ```
240
+
241
+ ## Provider Config
242
+
243
+ Shared defaults use environment placeholders:
244
+
245
+ - `CODEX_CLI_COMMAND`
246
+ - `CLAUDE_CLI_COMMAND`
247
+ - `CLOUD_CLI_COMMAND`
248
+ - `CHECKER_CLI_COMMAND`
249
+
250
+ Project-local overrides may point to real local binaries in the project or user environment. Keep those overrides out of shared defaults.
251
+
252
+ ## Verification Model
253
+
254
+ The normal lifecycle is:
255
+
256
+ ```text
257
+ Brief -> Research -> Plan -> Check -> Human Gate -> Execute -> Verify
258
+ ```
259
+
260
+ `Check` reviews the plan before implementation. `Verify` reviews the result after execution evidence exists.
261
+
262
+ Internal supervisor verify is acceptable for ordinary local engineering slices. External CLI verify should be used for production readiness, destructive or security-sensitive work, material data migrations/backfills, broad ambiguous refactors or explicit human request.
263
+
264
+ ## Release
265
+
266
+ `@besales/ops-framework` is published as a public scoped npm package through GitHub Actions trusted publishing.
267
+
268
+ The release workflow is `.github/workflows/publish-ops-framework.yml`.
269
+
270
+ Required npm trusted publisher configuration:
271
+
272
+ - Package: `@besales/ops-framework`
273
+ - Provider: GitHub Actions
274
+ - Organization / user: `be-sales`
275
+ - Repository: `shared-platform`
276
+ - Workflow filename: `publish-ops-framework.yml`
277
+ - Environment: leave empty
278
+ - Allowed action: `npm publish`
279
+
280
+ The workflow uses GitHub OIDC (`id-token: write`) and does not require an `NPM_TOKEN` secret.
281
+
282
+ For first-time package creation, npm requires the package to exist before a trusted publisher can be attached. Bootstrap the first version with a short-lived manual publish path, configure the trusted publisher, then use the workflow for all future versions.
283
+
284
+ Manual workflow release:
285
+
286
+ ```bash
287
+ gh workflow run publish-ops-framework.yml --repo be-sales/shared-platform -f version=0.1.0
288
+ ```
289
+
290
+ Tag workflow release:
291
+
292
+ ```bash
293
+ git tag ops-framework-v0.1.0
294
+ git push origin ops-framework-v0.1.0
295
+ ```
296
+
297
+ ## Validation
298
+
299
+ Run package tests:
300
+
301
+ ```bash
302
+ yarn workspace @besales/ops-framework test
303
+ ```
304
+
305
+ Run package lint:
306
+
307
+ ```bash
308
+ node bin/self-lint.mjs
309
+ ```
310
+
311
+ Auto-fix supported whitespace issues:
312
+
313
+ ```bash
314
+ node bin/self-lint.mjs --fix
315
+ ```
316
+
317
+ The lint script checks:
318
+
319
+ - no `.DS_Store` files;
320
+ - no open-work markers in package files;
321
+ - no local home-directory or application-bundle absolute paths in shared files;
322
+ - no known project-specific paths or project names in shared files;
323
+ - no trailing whitespace;
324
+ - text files end with a newline.
325
+
326
+ ## Pilot Status
327
+
328
+ This package was first extracted from a project-local Ops Framework. Pilot task history, memory and provider paths remain project-owned and are connected through that project's `ops/project.ops.yaml`.
@@ -0,0 +1,67 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import {
4
+ buildCheckerContextPack,
5
+ buildCheckContext,
6
+ buildEvidenceMarkdown,
7
+ computeTaskContextInputs,
8
+ riskRootWarnings,
9
+ resolveTaskDir,
10
+ } from './lib/check-context-utils.mjs';
11
+
12
+ function main() {
13
+ const taskArg = process.argv[2];
14
+ if (!taskArg) {
15
+ fail('Usage: node ops/agent-pipeline/bin/build-check-context.mjs <TASK-id-or-task-path>');
16
+ }
17
+
18
+ try {
19
+ const taskDir = resolveTaskDir(taskArg);
20
+ const taskId = path.basename(taskDir);
21
+ const inputs = computeTaskContextInputs(taskDir);
22
+ const checkContext = buildCheckContext({
23
+ taskId,
24
+ inputs,
25
+ });
26
+ const evidence = buildEvidenceMarkdown({
27
+ taskId,
28
+ risk: inputs.risk,
29
+ qualityGates: inputs.qualityGates,
30
+ referencedFiles: inputs.referencedFiles,
31
+ structuralLines: inputs.structuralLines,
32
+ planFingerprint: inputs.planFingerprint,
33
+ memorySha: inputs.memorySha,
34
+ taskArtifacts: inputs.taskArtifacts,
35
+ });
36
+ const checkerContextPack = buildCheckerContextPack({
37
+ taskId,
38
+ risk: inputs.risk,
39
+ qualityGates: inputs.qualityGates,
40
+ referencedFiles: inputs.referencedFiles,
41
+ structuralLines: inputs.structuralLines,
42
+ taskArtifacts: inputs.taskArtifacts,
43
+ });
44
+
45
+ fs.writeFileSync(path.join(taskDir, 'check-context.json'), `${JSON.stringify(checkContext, null, 2)}\n`);
46
+ fs.writeFileSync(path.join(taskDir, 'check-evidence.md'), evidence);
47
+ fs.writeFileSync(path.join(taskDir, checkContext.checkerContextPackFile), checkerContextPack);
48
+
49
+ console.log(`Built check context for ${taskId}`);
50
+ console.log(`- riskProfile: ${inputs.risk.riskProfile}`);
51
+ console.log(`- riskTriggers: ${inputs.risk.riskTriggers.join(', ') || 'none'}`);
52
+ for (const warning of riskRootWarnings()) {
53
+ console.log(`- warning: ${warning}`);
54
+ }
55
+ console.log(`- referencedFiles: ${inputs.referencedFiles.length}`);
56
+ console.log(`- checkerContextPack: ${checkContext.checkerContextPackFile}`);
57
+ } catch (error) {
58
+ fail(error.message);
59
+ }
60
+ }
61
+
62
+ function fail(message) {
63
+ console.error(`Error: ${message}`);
64
+ process.exit(1);
65
+ }
66
+
67
+ main();
@@ -0,0 +1,54 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import {
4
+ normalizeMarkdownBody,
5
+ parseCliArgs,
6
+ readTaskFile,
7
+ repoRoot,
8
+ resolveTaskDir,
9
+ sha256,
10
+ writeTaskFile,
11
+ } from './lib/check-context-utils.mjs';
12
+ import { buildExecutionLedger } from './lib/execution-ledger-utils.mjs';
13
+
14
+ function main() {
15
+ const args = parseCliArgs(process.argv.slice(2));
16
+ const taskArg = args.positional[0];
17
+ if (!taskArg) {
18
+ fail('Usage: node ops/agent-pipeline/bin/build-execution-ledger.mjs <TASK-id-or-task-path>');
19
+ }
20
+
21
+ try {
22
+ const taskDir = resolveTaskDir(taskArg);
23
+ const taskId = path.basename(taskDir);
24
+ const ledger = buildExecutionLedger({
25
+ taskId,
26
+ taskDir,
27
+ repoRoot,
28
+ planSha: hashTaskMarkdown(taskDir, 'plan.md'),
29
+ executionSha: hashTaskMarkdown(taskDir, 'execution.md'),
30
+ });
31
+
32
+ writeTaskFile(taskDir, 'execution-ledger.json', JSON.stringify(ledger, null, 2));
33
+ console.log(`Built execution ledger for ${taskId}`);
34
+ console.log(`- changedFiles: ${ledger.git.changedFiles.length}`);
35
+ console.log(`- unrelatedDirtyFiles: ${ledger.git.unrelatedDirtyFiles.length}`);
36
+ } catch (error) {
37
+ fail(error.message);
38
+ }
39
+ }
40
+
41
+ function hashTaskMarkdown(taskDir, fileName) {
42
+ const content = readTaskFile(taskDir, fileName);
43
+ if (!content) {
44
+ throw new Error(`Task artifact is missing or empty: ${fileName}`);
45
+ }
46
+ return sha256(normalizeMarkdownBody(content));
47
+ }
48
+
49
+ function fail(message) {
50
+ console.error(`Error: ${message}`);
51
+ process.exit(1);
52
+ }
53
+
54
+ main();
@@ -0,0 +1,160 @@
1
+ import path from 'node:path';
2
+ import {
3
+ buildCheckerContextPack,
4
+ buildCheckContext,
5
+ buildEvidenceMarkdown,
6
+ computePromptSha,
7
+ computeTaskContextInputs,
8
+ getFlag,
9
+ normalizeMarkdownBody,
10
+ parseCliArgs,
11
+ readMemorySnapshot,
12
+ readPrompt,
13
+ readTaskFile,
14
+ resolveTaskDir,
15
+ sha256,
16
+ } from './lib/check-context-utils.mjs';
17
+ import {
18
+ buildCheckerLlmInputPack,
19
+ buildVerifierLlmInputPack,
20
+ estimatePayload,
21
+ resolveLlmContextMode,
22
+ summarizePackForConsole,
23
+ } from './lib/llm-input-pack-utils.mjs';
24
+
25
+ function main() {
26
+ const args = parseCliArgs(process.argv.slice(2));
27
+ const taskArg = args.positional[0];
28
+ if (!taskArg) {
29
+ fail('Usage: node ops/agent-pipeline/bin/estimate-llm-input.mjs <TASK-id-or-task-path> --stage check|verify [--mode fast|standard|strict] [--json]');
30
+ }
31
+
32
+ try {
33
+ const taskDir = resolveTaskDir(taskArg);
34
+ const taskId = path.basename(taskDir);
35
+ const stage = String(getFlag(args, 'stage') || args.positional[1] || 'check').toLowerCase();
36
+ const pack = stage === 'verify'
37
+ ? buildVerifyEstimatePack({ taskDir, taskId, args })
38
+ : buildCheckEstimatePack({ taskDir, taskId, args });
39
+ const promptName = stage === 'verify' ? 'verifier.md' : 'checker.md';
40
+ const fullPromptEstimate = estimatePayload([
41
+ readPrompt(promptName),
42
+ '',
43
+ JSON.stringify(pack.input, null, 2),
44
+ ].join('\n'));
45
+ const result = {
46
+ taskId,
47
+ stage,
48
+ mode: pack.meta.mode,
49
+ capTokens: pack.meta.capTokens,
50
+ packBytes: pack.meta.bytes,
51
+ packEstimatedTokens: pack.meta.estimatedTokens,
52
+ fullPromptBytes: fullPromptEstimate.bytes,
53
+ fullPromptEstimatedTokens: fullPromptEstimate.estimatedTokens,
54
+ overCap: pack.meta.overCap,
55
+ compactedArtifacts: pack.meta.compactedArtifacts,
56
+ };
57
+
58
+ if (args.flags.has('json')) {
59
+ console.log(JSON.stringify(result, null, 2));
60
+ } else {
61
+ console.log(`LLM input estimate for ${taskId} (${stage})`);
62
+ for (const line of summarizePackForConsole(pack)) {
63
+ console.log(line);
64
+ }
65
+ console.log(`- fullPromptEstimatedTokens: ${result.fullPromptEstimatedTokens}`);
66
+ console.log(`- overCap: ${result.overCap ? 'yes' : 'no'}`);
67
+ }
68
+ process.exit(result.overCap ? 1 : 0);
69
+ } catch (error) {
70
+ fail(error.message);
71
+ }
72
+ }
73
+
74
+ function buildCheckEstimatePack({ taskDir, taskId, args }) {
75
+ const inputs = computeTaskContextInputs(taskDir);
76
+ const checkContext = buildCheckContext({ taskId, inputs });
77
+ const checkerContextPack = buildCheckerContextPack({
78
+ taskId,
79
+ risk: inputs.risk,
80
+ qualityGates: inputs.qualityGates,
81
+ referencedFiles: inputs.referencedFiles,
82
+ structuralLines: inputs.structuralLines,
83
+ taskArtifacts: inputs.taskArtifacts,
84
+ });
85
+ const checkEvidence = buildEvidenceMarkdown({
86
+ taskId,
87
+ risk: inputs.risk,
88
+ qualityGates: inputs.qualityGates,
89
+ referencedFiles: inputs.referencedFiles,
90
+ structuralLines: inputs.structuralLines,
91
+ planFingerprint: inputs.planFingerprint,
92
+ memorySha: inputs.memorySha,
93
+ taskArtifacts: inputs.taskArtifacts,
94
+ });
95
+ const memorySnapshot = readMemorySnapshot(checkContext.memoryFiles);
96
+ const mode = resolveLlmContextMode({
97
+ requestedMode: getFlag(args, 'mode') || getFlag(args, 'context-mode'),
98
+ riskTriggers: checkContext.riskTriggers,
99
+ });
100
+ return buildCheckerLlmInputPack({
101
+ taskDir,
102
+ taskId,
103
+ checkerPromptSha: computePromptSha('checker.md'),
104
+ cacheKey: { taskId, estimate: true, contextMode: mode },
105
+ checkContext,
106
+ checkEvidence,
107
+ checkerContextPack,
108
+ taskManifest: readTaskFile(taskDir, 'task-manifest.json'),
109
+ projectMemory: memorySnapshot.map((item) => ({
110
+ path: item.path,
111
+ sha: item.sha,
112
+ content: item.content,
113
+ })),
114
+ mode,
115
+ });
116
+ }
117
+
118
+ function buildVerifyEstimatePack({ taskDir, taskId, args }) {
119
+ const manifest = readOptionalJson(taskDir, 'task-manifest.json');
120
+ const mode = resolveLlmContextMode({
121
+ requestedMode: getFlag(args, 'mode') || getFlag(args, 'context-mode'),
122
+ riskTriggers: manifest?.context?.riskTriggers || [],
123
+ });
124
+ return buildVerifierLlmInputPack({
125
+ taskDir,
126
+ taskId,
127
+ planSha: hashTaskMarkdown(taskDir, 'plan.md'),
128
+ executionSha: hashTaskMarkdown(taskDir, 'execution.md'),
129
+ verifier: {
130
+ provider: 'estimate',
131
+ model: 'estimate',
132
+ reasoningEffort: 'none',
133
+ runId: 'estimate',
134
+ },
135
+ mode,
136
+ });
137
+ }
138
+
139
+ function hashTaskMarkdown(taskDir, fileName) {
140
+ return sha256(normalizeMarkdownBody(readTaskFile(taskDir, fileName)));
141
+ }
142
+
143
+ function readOptionalJson(taskDir, fileName) {
144
+ const raw = readTaskFile(taskDir, fileName);
145
+ if (!raw) {
146
+ return null;
147
+ }
148
+ try {
149
+ return JSON.parse(raw);
150
+ } catch {
151
+ return null;
152
+ }
153
+ }
154
+
155
+ function fail(message) {
156
+ console.error(`Error: ${message}`);
157
+ process.exit(1);
158
+ }
159
+
160
+ main();