@codeam/shared 2.75.7 → 2.75.9

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.
package/dist/index.mjs CHANGED
@@ -2592,6 +2592,9 @@ function normalizeGuardrailPolicy(raw) {
2592
2592
  var GUARDRAIL_CONFIGURE_COMMAND = "guardrail_configure";
2593
2593
 
2594
2594
  // src/packs/types.ts
2595
+ var PACK_FINDINGS_FILE = "REVIEW-FINDINGS.pack.json";
2596
+ var MAX_PACK_FINDINGS = 50;
2597
+ var PACK_HANDOFF_HEADING = "## Handoff";
2595
2598
  var PACK_START_COMMAND = "pack_start";
2596
2599
  var PACK_ACTION_COMMAND = "pack_action";
2597
2600
  var PACK_STATUS_COMMAND = "pack_status";
@@ -2611,6 +2614,8 @@ Method:
2611
2614
  - **Verification plan** \u2014 for each criterion, the level that proves it (unit / integration / manual) and why.
2612
2615
  3. Right-size: if the task is clearly too large for one pipeline run, narrow the criteria to a coherent first slice and record the rest under "Out of scope / next".
2613
2616
 
2617
+ 4. Close with a \`## Handoff\` section: the criteria count, what you scoped out, and any assumption the Coder must know.
2618
+
2614
2619
  Handoff bar: the spec file is committed; every acceptance criterion is testable as written; a competent implementer could start without asking you anything.`;
2615
2620
  var CODER_PROMPT = `# Role: Coder
2616
2621
 
@@ -2621,6 +2626,7 @@ Method:
2621
2626
  2. Test-first where it fits: write the test that proves a criterion, watch it fail, implement until it passes. Where strict test-first doesn't fit, still land tests alongside the change.
2622
2627
  3. Match the project's existing style, structure, and conventions. Simplest design that fully solves the problem \u2014 no speculative abstractions, no "while I'm here" changes.
2623
2628
  4. Run the project's tests / linters / build and make them pass.
2629
+ 5. Close with a \`## Handoff\` section: which criteria are implemented (by number) and where their tests live, anything you consciously did NOT do, and what the Reviewer should look at first.
2624
2630
 
2625
2631
  Handoff bar: every acceptance criterion is implemented and covered by a test; the project's checks pass; the work is committed in focused commits.`;
2626
2632
  var REVIEWER_PROMPT = `# Role: Reviewer
@@ -2636,21 +2642,42 @@ Method:
2636
2642
  - **Conventions & naming** \u2014 matches the surrounding code; names say what things are.
2637
2643
  - **Safety** \u2014 no secrets, credentials, or debugging remnants in code, tests, or fixtures.
2638
2644
  3. Fix what is justified \u2014 smallest change that resolves the finding, keeping behavior. Re-run the checks after material fixes.
2639
- 4. Record your findings honestly in your closing summary: what you found, what you fixed, what you deliberately left, and what you could not verify.
2640
-
2641
- Handoff bar: checks pass on YOUR final commit; every fix is committed; your summary lists findings \u2192 resolutions (an empty findings list must say what you checked).`;
2645
+ 4. Write your findings as DATA to \`REVIEW-FINDINGS.pack.json\` at the repo root \u2014 the next stage verifies them one by one, so what is not in this file does not get verified. Exact shape:
2646
+ \`\`\`json
2647
+ {
2648
+ "findings": [
2649
+ {
2650
+ "id": "R1",
2651
+ "severity": "blocker | major | minor | nit",
2652
+ "title": "one line: what is wrong",
2653
+ "detail": "why it matters and what you did about it",
2654
+ "file": "src/path/to/file.ts",
2655
+ "line": 42,
2656
+ "resolution": "fixed | deferred | wont_fix | needs_verification",
2657
+ "commit": "abcdef1234"
2658
+ }
2659
+ ],
2660
+ "checked": ["what you audited when the list is empty: e.g. error paths of X, test coverage of Y"]
2661
+ }
2662
+ \`\`\`
2663
+ Every finding you fixed says \`fixed\` with its commit; anything you deliberately left says \`deferred\` or \`wont_fix\` with the reason in \`detail\`; anything you could not verify says \`needs_verification\`. An empty \`findings\` array is a valid, honest result \u2014 then \`checked\` must say what you looked at. Commit this file with your stage.
2664
+ 5. Close with a \`## Handoff\` section: what you found (counts by severity), what you fixed, what you deliberately left, and what the next stage must verify.
2665
+
2666
+ Handoff bar: checks pass on YOUR final commit; every fix is committed; \`REVIEW-FINDINGS.pack.json\` is committed and lists every finding with its resolution (an empty list says what you checked).`;
2642
2667
  var QA_PROMPT = `# Role: QA
2643
2668
 
2644
2669
  You are the final gate. You verify the delivered work against the acceptance criteria as a whole \u2014 end to end, the way a demanding user would \u2014 and produce the run's closing report.
2645
2670
 
2646
2671
  Method:
2647
2672
  1. Read the task and \`SPEC.pack.md\` (when present). Your contract is the acceptance criteria; without a spec, derive them from the task.
2648
- 2. For EACH criterion, verify it against the real project: run the relevant tests, execute the code paths where feasible, inspect actual behavior/output. Do not take earlier stages' word for anything.
2649
- 3. Run the project's full checks (tests, lint, types, build) one final time.
2650
- 4. Write \`QA-REPORT.pack.md\` at the repo root: per-criterion verdict (\u2705 verified / \u26A0\uFE0F partially / \u274C failed \u2014 with evidence for each), the checks' results, anything not verifiable in this environment (stated plainly), and a short "ready to ship?" conclusion.
2651
- 5. If a criterion FAILS: fix it only when the fix is small and unambiguous; otherwise mark it failed with exact evidence \u2014 the user decides. Never paper over a failure.
2673
+ 2. Read \`REVIEW-FINDINGS.pack.json\` (when present \u2014 the Reviewer's structured findings, also summarized in your handoff input). Every finding there is an item on YOUR checklist: a \`fixed\` one must be verified fixed without regression; a \`deferred\`/\`wont_fix\` one must be judged acceptable or escalated; a \`needs_verification\` one is yours to settle.
2674
+ 3. For EACH criterion, verify it against the real project: run the relevant tests, execute the code paths where feasible, inspect actual behavior/output. Do not take earlier stages' word for anything.
2675
+ 4. Run the project's full checks (tests, lint, types, build) one final time.
2676
+ 5. Write \`QA-REPORT.pack.md\` at the repo root with two sections: **Acceptance criteria** \u2014 per-criterion verdict (\u2705 verified / \u26A0\uFE0F partially / \u274C failed \u2014 with evidence for each); **Review findings** \u2014 per finding id, verdict (\u2705 verified fixed / \u26A0\uFE0F still open / \u274C regressed \u2014 with evidence). Then the checks' results, anything not verifiable in this environment (stated plainly), and a short "ready to ship?" conclusion.
2677
+ 6. If a criterion FAILS: fix it only when the fix is small and unambiguous; otherwise mark it failed with exact evidence \u2014 the user decides. Never paper over a failure.
2678
+ 7. Close with a \`## Handoff\` section: the ship/no-ship conclusion, the failed or open items by id, and what you could not verify.
2652
2679
 
2653
- Handoff bar: the report is committed; every verdict carries evidence; the conclusion is honest about anything unverified.`;
2680
+ Handoff bar: the report is committed; every verdict carries evidence; every finding id from the Reviewer appears in the report; the conclusion is honest about anything unverified.`;
2654
2681
 
2655
2682
  // src/packs/registry.ts
2656
2683
  var PACK_REGISTRY = {
@@ -2674,7 +2701,9 @@ var PACK_REGISTRY = {
2674
2701
  skillIds: ["code-review", "code-naming"],
2675
2702
  prompt: REVIEWER_PROMPT,
2676
2703
  // Review-only: approving clean with no change is success, not a stall.
2677
- requiresCommit: false
2704
+ requiresCommit: false,
2705
+ // Leaves REVIEW-FINDINGS.pack.json — the next stage gets a list, not prose.
2706
+ producesFindings: true
2678
2707
  }
2679
2708
  ]
2680
2709
  },
@@ -2705,7 +2734,9 @@ var PACK_REGISTRY = {
2705
2734
  skillIds: ["code-review", "code-naming"],
2706
2735
  prompt: REVIEWER_PROMPT,
2707
2736
  // Review-only: approving clean with no change is success, not a stall.
2708
- requiresCommit: false
2737
+ requiresCommit: false,
2738
+ // Leaves REVIEW-FINDINGS.pack.json — the next stage gets a list, not prose.
2739
+ producesFindings: true
2709
2740
  },
2710
2741
  {
2711
2742
  role: "qa",
@@ -2735,8 +2766,140 @@ You are ONE specialist role in a multi-role pipeline running on this repository.
2735
2766
  - **Never leave the tree broken.** Run the project's checks before finishing when the project has them; your stage ends with a working tree the next role can build on.
2736
2767
  - **Do not push, force-push, or touch remotes** \u2014 the pipeline works locally; publishing is the user's call at the end.
2737
2768
  - **Never read, edit, or commit anything under \`.codeam/\`** \u2014 that is the pipeline's own ledger, not project code.
2738
- - **Finish decisively.** When your stage's job is done and committed, say so in 2-4 lines (what you did, what you verified, anything the next stage should know) and stop. Don't ask "should I continue?" \u2014 the pipeline advances automatically.
2739
- - **If you are genuinely blocked** (contradictory requirements, missing access), say exactly what is blocking you and stop \u2014 the user is supervising and will decide.`;
2769
+ - **Finish with a \`## Handoff\` section.** When your stage's job is done and committed, end your reply with a heading \`## Handoff\` followed by 2-6 lines: what you did, what you verified, anything the next stage must know. That section \u2014 not the rest of your reply \u2014 is what the next role receives. Don't ask "should I continue?" \u2014 the pipeline advances automatically.
2770
+ - **If you need the user's decision** (contradictory requirements, a real product choice), ask ONE clear question and end your reply with 2-4 numbered options on their own lines (\`1. \u2026\`), then stop. The pipeline pauses until they answer in this conversation; keep working in it once they do, and commit as usual.
2771
+ - **If you are genuinely blocked** (missing access, a broken environment you cannot fix), say exactly what is blocking you and stop \u2014 the user is supervising and will decide.`;
2772
+
2773
+ // src/packs/findings.ts
2774
+ var SEVERITIES = /* @__PURE__ */ new Set([
2775
+ "blocker",
2776
+ "major",
2777
+ "minor",
2778
+ "nit"
2779
+ ]);
2780
+ var RESOLUTIONS = /* @__PURE__ */ new Set([
2781
+ "fixed",
2782
+ "deferred",
2783
+ "wont_fix",
2784
+ "needs_verification"
2785
+ ]);
2786
+ var TITLE_MAX = 200;
2787
+ var DETAIL_MAX = 1200;
2788
+ function parsePackFindings(raw) {
2789
+ const text = stripFence(raw).trim();
2790
+ if (text.length === 0) return { ok: false, error: "findings file is empty" };
2791
+ let data;
2792
+ try {
2793
+ data = JSON.parse(text);
2794
+ } catch {
2795
+ const salvaged = salvageJson(text);
2796
+ if (salvaged === null) return { ok: false, error: "findings file is not valid JSON" };
2797
+ data = salvaged;
2798
+ }
2799
+ const container = Array.isArray(data) ? { findings: data } : data;
2800
+ if (!isRecord(container) || !Array.isArray(container.findings)) {
2801
+ return { ok: false, error: 'findings file has no "findings" array' };
2802
+ }
2803
+ const findings = [];
2804
+ let dropped = 0;
2805
+ const seen = /* @__PURE__ */ new Set();
2806
+ for (const entry of container.findings) {
2807
+ const f = coerceFinding(entry, findings.length + 1);
2808
+ if (!f) {
2809
+ dropped += 1;
2810
+ continue;
2811
+ }
2812
+ if (seen.has(f.id)) f.id = `${f.id}-${findings.length + 1}`;
2813
+ seen.add(f.id);
2814
+ findings.push(f);
2815
+ if (findings.length >= MAX_PACK_FINDINGS) break;
2816
+ }
2817
+ const truncated = container.findings.length - dropped - findings.length;
2818
+ const checked = Array.isArray(container.checked) ? container.checked.filter((c) => typeof c === "string" && c.trim().length > 0).slice(0, 20) : void 0;
2819
+ const notes = [];
2820
+ if (dropped > 0) notes.push(`${dropped} malformed entr${dropped === 1 ? "y" : "ies"} dropped`);
2821
+ if (truncated > 0)
2822
+ notes.push(`list truncated to ${MAX_PACK_FINDINGS} (${truncated} more in the file)`);
2823
+ return {
2824
+ ok: true,
2825
+ value: {
2826
+ findings,
2827
+ ...checked && checked.length > 0 ? { checked } : {},
2828
+ ...notes.length > 0 ? { note: notes.join("; ") } : {}
2829
+ }
2830
+ };
2831
+ }
2832
+ function coerceFinding(entry, ordinal) {
2833
+ if (!isRecord(entry)) return null;
2834
+ const title = str(entry.title) ?? str(entry.summary);
2835
+ if (!title) return null;
2836
+ const id = str(entry.id) ?? `R${ordinal}`;
2837
+ const severityRaw = (str(entry.severity) ?? "").toLowerCase();
2838
+ const resolutionRaw = (str(entry.resolution) ?? str(entry.status) ?? "").toLowerCase().replace(/[\s-]+/g, "_");
2839
+ const line = typeof entry.line === "number" && Number.isFinite(entry.line) && entry.line > 0 ? Math.floor(entry.line) : void 0;
2840
+ const detail = str(entry.detail) ?? str(entry.description);
2841
+ const file = str(entry.file) ?? str(entry.path);
2842
+ const commit = str(entry.commit);
2843
+ return {
2844
+ id: id.slice(0, 32),
2845
+ severity: SEVERITIES.has(severityRaw) ? severityRaw : "major",
2846
+ title: title.slice(0, TITLE_MAX),
2847
+ ...detail ? { detail: detail.slice(0, DETAIL_MAX) } : {},
2848
+ ...file ? { file: file.slice(0, 300) } : {},
2849
+ ...line !== void 0 ? { line } : {},
2850
+ resolution: RESOLUTIONS.has(resolutionRaw) ? resolutionRaw : "needs_verification",
2851
+ ...commit && /^[0-9a-f]{7,40}$/i.test(commit) ? { commit: commit.slice(0, 10) } : {}
2852
+ };
2853
+ }
2854
+ function extractHandoffSection(reply) {
2855
+ const lines = reply.split("\n");
2856
+ const want = PACK_HANDOFF_HEADING.replace(/^#+\s*/, "").toLowerCase();
2857
+ let start = -1;
2858
+ let level = 0;
2859
+ for (let i = lines.length - 1; i >= 0; i--) {
2860
+ const m = lines[i].match(/^\s{0,3}(#{1,4})\s+(.+?)\s*#*\s*$/);
2861
+ if (m && m[2].trim().toLowerCase().replace(/[:.]+$/, "") === want) {
2862
+ start = i;
2863
+ level = m[1].length;
2864
+ break;
2865
+ }
2866
+ }
2867
+ if (start < 0) return null;
2868
+ const body = [];
2869
+ for (let i = start + 1; i < lines.length; i++) {
2870
+ const m = lines[i].match(/^\s{0,3}(#{1,6})\s+/);
2871
+ if (m && m[1].length <= level) break;
2872
+ body.push(lines[i]);
2873
+ }
2874
+ const text = body.join("\n").trim();
2875
+ return text.length > 0 ? text : null;
2876
+ }
2877
+ function isRecord(v) {
2878
+ return typeof v === "object" && v !== null && !Array.isArray(v);
2879
+ }
2880
+ function str(v) {
2881
+ return typeof v === "string" && v.trim().length > 0 ? v.trim() : void 0;
2882
+ }
2883
+ function stripFence(raw) {
2884
+ const m = raw.match(/```(?:json)?\s*([\s\S]*?)```/i);
2885
+ return m ? m[1] : raw;
2886
+ }
2887
+ function salvageJson(text) {
2888
+ for (const [open, close] of [
2889
+ ["{", "}"],
2890
+ ["[", "]"]
2891
+ ]) {
2892
+ const a = text.indexOf(open);
2893
+ const b = text.lastIndexOf(close);
2894
+ if (a >= 0 && b > a) {
2895
+ try {
2896
+ return JSON.parse(text.slice(a, b + 1));
2897
+ } catch {
2898
+ }
2899
+ }
2900
+ }
2901
+ return null;
2902
+ }
2740
2903
 
2741
2904
  // src/api-url.ts
2742
2905
  var DEFAULT_API_BASE_URL = "https://api.codeagent-mobile.com";
@@ -2982,10 +3145,13 @@ export {
2982
3145
  MANAGED_AGENT_SUBTITLE,
2983
3146
  MANAGED_PROVIDER_DISPLAY_NAMES,
2984
3147
  MANAGED_PROVIDER_IDS,
3148
+ MAX_PACK_FINDINGS,
2985
3149
  MODEL_CONTEXT_WINDOW,
2986
3150
  MODEL_PRICING,
2987
3151
  OBSERVER_BRIDGE_PORT,
2988
3152
  PACK_ACTION_COMMAND,
3153
+ PACK_FINDINGS_FILE,
3154
+ PACK_HANDOFF_HEADING,
2989
3155
  PACK_REGISTRY,
2990
3156
  PACK_START_COMMAND,
2991
3157
  PACK_STATUS_COMMAND,
@@ -3013,6 +3179,7 @@ export {
3013
3179
  clampHopBudget,
3014
3180
  classifyStack,
3015
3181
  detectedIntegrationsFromDeps,
3182
+ extractHandoffSection,
3016
3183
  getAgent,
3017
3184
  getContextWindow,
3018
3185
  getEnabledAgents,
@@ -3036,6 +3203,7 @@ export {
3036
3203
  isSkillId,
3037
3204
  normalizeAgentId,
3038
3205
  normalizeGuardrailPolicy,
3206
+ parsePackFindings,
3039
3207
  publicToInternal,
3040
3208
  recommendForDeps,
3041
3209
  renderToLines,