@shrkcrft/inspector 0.1.0-alpha.26 → 0.1.0-alpha.28

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.
@@ -8,17 +8,34 @@
8
8
  * `shrk delegate explain` can show an author the fence is real before trusting
9
9
  * it. Pack-contributed recipes (Phase 4) will merge in here too.
10
10
  */
11
- import type { IDelegateRecipe, ISharkCraftConfig } from '@shrkcrft/config';
11
+ import { type DelegateRecipeMode, type IDelegateRecipe, type ISharkCraftConfig } from '@shrkcrft/config';
12
12
  export interface IResolvedDelegateRecipe extends IDelegateRecipe {
13
+ /**
14
+ * Recipe mode after defaulting (`'patch'` when unset). Explicit so downstream
15
+ * surfaces (doctor, list/explain, the analyze path) don't re-derive it.
16
+ */
17
+ mode: DelegateRecipeMode;
13
18
  /** Provider after applying the delegation-level default (never undefined). */
14
19
  resolvedProvider: 'auto' | 'ollama' | 'llamacpp';
15
20
  /** Model after applying the delegation-level default (undefined = provider default). */
16
21
  resolvedModel?: string;
17
- /** verificationIds that do NOT resolve to a `verificationCommands[].id`. */
22
+ /**
23
+ * (patch) The write-fence arrays, normalised to present (empty when unset) so
24
+ * patch consumers never juggle `undefined`. Analysis recipes leave them `[]`.
25
+ */
26
+ guardrailGlobs: readonly string[];
27
+ allowedOps: readonly string[];
28
+ verificationIds: readonly string[];
29
+ /** (patch) verificationIds that do NOT resolve to a `verificationCommands[].id`. */
18
30
  unboundVerificationIds: readonly string[];
19
- /** True when every verificationId resolves AND at least one is declared. */
31
+ /** (patch) True when every verificationId resolves AND at least one is declared. */
20
32
  verificationBound: boolean;
21
- /** True when the recipe is safe to delegate (verification fully bound). */
33
+ /** (analysis) True when `groundedOn` resolves to a known grounding report. */
34
+ groundingBound: boolean;
35
+ /**
36
+ * True when the recipe is safe to run — for a patch recipe, its verification is
37
+ * fully bound; for an analysis recipe, its grounding report is known.
38
+ */
22
39
  delegatable: boolean;
23
40
  /** Where the recipe came from. */
24
41
  source: 'config' | 'pack';
@@ -1 +1 @@
1
- {"version":3,"file":"delegate-catalog.d.ts","sourceRoot":"","sources":["../src/delegate-catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE3E,MAAM,WAAW,uBAAwB,SAAQ,eAAe;IAC9D,8EAA8E;IAC9E,gBAAgB,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC;IACjD,wFAAwF;IACxF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4EAA4E;IAC5E,sBAAsB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,4EAA4E;IAC5E,iBAAiB,EAAE,OAAO,CAAC;IAC3B,2EAA2E;IAC3E,WAAW,EAAE,OAAO,CAAC;IACrB,kCAAkC;IAClC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC1B,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,6EAA6E;AAC7E,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,iBAAiB,EACzB,WAAW,GAAE,SAAS,gBAAgB,EAAO,GAC5C,SAAS,uBAAuB,EAAE,CAkCpC;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,iBAAiB,EACzB,QAAQ,EAAE,MAAM,GACf,uBAAuB,GAAG,SAAS,CAErC"}
1
+ {"version":3,"file":"delegate-catalog.d.ts","sourceRoot":"","sources":["../src/delegate-catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAA0B,KAAK,kBAAkB,EAAE,KAAK,eAAe,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAEjI,MAAM,WAAW,uBAAwB,SAAQ,eAAe;IAC9D;;;OAGG;IACH,IAAI,EAAE,kBAAkB,CAAC;IACzB,8EAA8E;IAC9E,gBAAgB,EAAE,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC;IACjD,wFAAwF;IACxF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,oFAAoF;IACpF,sBAAsB,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,oFAAoF;IACpF,iBAAiB,EAAE,OAAO,CAAC;IAC3B,8EAA8E;IAC9E,cAAc,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,WAAW,EAAE,OAAO,CAAC;IACrB,kCAAkC;IAClC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC1B,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,6EAA6E;AAC7E,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,iBAAiB,EACzB,WAAW,GAAE,SAAS,gBAAgB,EAAO,GAC5C,SAAS,uBAAuB,EAAE,CAgDpC;AAED,gFAAgF;AAChF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,iBAAiB,EACzB,QAAQ,EAAE,MAAM,GACf,uBAAuB,GAAG,SAAS,CAErC"}
@@ -1,3 +1,14 @@
1
+ /**
2
+ * Resolve the delegate-recipe catalog from a loaded config (read-only, NO model).
3
+ *
4
+ * Merges the delegation-level `provider`/`model` defaults into each recipe and
5
+ * checks every `verificationId` against the config's `verificationCommands[]`.
6
+ * A recipe is `delegatable` only when ALL its verification ids are bound — a
7
+ * recipe with an unbound (or empty) verification has no deterministic gate, so
8
+ * `shrk delegate explain` can show an author the fence is real before trusting
9
+ * it. Pack-contributed recipes (Phase 4) will merge in here too.
10
+ */
11
+ import { DELEGATE_GROUNDING_IDS } from '@shrkcrft/config';
1
12
  /**
2
13
  * Resolve the delegate catalog from config + pack-contributed recipes.
3
14
  *
@@ -29,15 +40,28 @@ export function resolveDelegateCatalog(config, packRecipes = []) {
29
40
  ...(ov?.verificationIds !== undefined ? { verificationIds: ov.verificationIds } : {}),
30
41
  ...(ov?.guardrailGlobs !== undefined ? { guardrailGlobs: ov.guardrailGlobs } : {}),
31
42
  };
43
+ const mode = merged.mode ?? 'patch';
32
44
  const unbound = (merged.verificationIds ?? []).filter((id) => !known.has(id));
33
45
  const verificationBound = unbound.length === 0 && (merged.verificationIds ?? []).length > 0;
46
+ const groundingBound = typeof merged.groundedOn === 'string' &&
47
+ DELEGATE_GROUNDING_IDS.includes(merged.groundedOn);
48
+ // A patch recipe is runnable when its verification binds; an analysis recipe
49
+ // when its grounding report is known. `delegatable` unifies "safe to run".
50
+ const delegatable = mode === 'analysis' ? groundingBound : verificationBound;
34
51
  out.push({
35
52
  ...merged,
53
+ mode,
54
+ // Normalise the write-fence arrays to present so patch consumers of the
55
+ // resolved recipe never juggle `undefined` (analysis recipes stay `[]`).
56
+ guardrailGlobs: merged.guardrailGlobs ?? [],
57
+ allowedOps: merged.allowedOps ?? [],
58
+ verificationIds: merged.verificationIds ?? [],
36
59
  resolvedProvider: merged.provider ?? delegation.provider ?? 'auto',
37
60
  ...(merged.model ?? delegation.model ? { resolvedModel: merged.model ?? delegation.model } : {}),
38
61
  unboundVerificationIds: unbound,
39
62
  verificationBound,
40
- delegatable: verificationBound,
63
+ groundingBound,
64
+ delegatable,
41
65
  source,
42
66
  ...(packageName ? { packageName } : {}),
43
67
  });
@@ -9,7 +9,7 @@
9
9
  * which can load pack files async); silent when the repo hasn't opted into
10
10
  * delegation.
11
11
  */
12
- import type { ISharkCraftConfig } from '@shrkcrft/config';
12
+ import { type ISharkCraftConfig } from '@shrkcrft/config';
13
13
  import { type IDoctorCheck } from './doctor-result.js';
14
14
  export declare function buildDelegateRecipeChecks(config: ISharkCraftConfig | null): IDoctorCheck[];
15
15
  //# sourceMappingURL=delegate-doctor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"delegate-doctor.d.ts","sourceRoot":"","sources":["../src/delegate-doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,GAAG,YAAY,EAAE,CAiC1F"}
1
+ {"version":3,"file":"delegate-doctor.d.ts","sourceRoot":"","sources":["../src/delegate-doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAsB,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGvE,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,GAAG,YAAY,EAAE,CAsE1F"}
@@ -1,3 +1,15 @@
1
+ /**
2
+ * `shrk doctor` checks for delegate-worker recipe health.
3
+ *
4
+ * Surfaces recipes that are NOT delegatable — a recipe whose `verificationIds`
5
+ * don't all resolve to `verificationCommands[]` (incl. after `recipeOverrides`)
6
+ * has no deterministic gate, so `shrk delegate run` would refuse it. `runDoctor`
7
+ * has no other surface for this (it doesn't run `validateConfig`), so this is the
8
+ * proactive catch. Sync + config-only (pack-recipe health is `delegate explain`,
9
+ * which can load pack files async); silent when the repo hasn't opted into
10
+ * delegation.
11
+ */
12
+ import { DELEGATE_QUERY_IDS } from '@shrkcrft/config';
1
13
  import { DoctorSeverity } from "./doctor-result.js";
2
14
  import { resolveDelegateCatalog } from "./delegate-catalog.js";
3
15
  export function buildDelegateRecipeChecks(config) {
@@ -9,26 +21,65 @@ export function buildDelegateRecipeChecks(config) {
9
21
  const out = [];
10
22
  const broken = catalog.filter((r) => !r.delegatable);
11
23
  for (const r of broken) {
12
- const reason = r.unboundVerificationIds.length > 0
13
- ? `verificationIds not in verificationCommands[]: ${r.unboundVerificationIds.join(', ')}`
14
- : 'no verificationIds declared';
24
+ const isAnalysis = r.mode === 'analysis';
25
+ const reason = isAnalysis
26
+ ? `groundedOn "${r.groundedOn ?? '(unset)'}" is not a known grounding report`
27
+ : r.unboundVerificationIds.length > 0
28
+ ? `verificationIds not in verificationCommands[]: ${r.unboundVerificationIds.join(', ')}`
29
+ : 'no verificationIds declared';
30
+ const consequence = isAnalysis
31
+ ? `\`shrk delegate analyze --recipe ${r.id}\` has no deterministic ground truth to check the model against.`
32
+ : `\`shrk delegate run --recipe ${r.id}\` would refuse to apply an unverified edit.`;
15
33
  out.push({
16
34
  id: `delegate-recipe-${r.id}`,
17
- title: `Delegate recipe "${r.id}" is not delegatable`,
35
+ title: `Delegate recipe "${r.id}" is not ${isAnalysis ? 'usable' : 'delegatable'}`,
18
36
  severity: DoctorSeverity.Warning,
19
- message: `${reason} — \`shrk delegate run --recipe ${r.id}\` would refuse to apply an unverified edit.`,
37
+ message: `${reason} — ${consequence}`,
20
38
  category: 'delegate',
21
- code: 'recipe-unverified',
39
+ code: isAnalysis ? 'recipe-ungrounded' : 'recipe-unverified',
22
40
  recommendedFix: `shrk delegate explain ${r.id}`,
23
- whyThisMatters: 'A recipe with an unbound verification has no deterministic gate; the worker can only run a VERIFIED edit, so the recipe is unusable until its verificationIds bind to a verificationCommands[] entry.',
41
+ whyThisMatters: isAnalysis
42
+ ? 'An analysis recipe grounds the model on a deterministic report; without a known groundedOn there is no ground truth to cross-check findings against, so the recipe is unusable.'
43
+ : 'A recipe with an unbound verification has no deterministic gate; the worker can only run a VERIFIED edit, so the recipe is unusable until its verificationIds bind to a verificationCommands[] entry.',
24
44
  });
25
45
  }
26
- if (broken.length === 0) {
46
+ // Analysis-recipe config health beyond grounding (validateConfig isn't run at
47
+ // load, so these would otherwise silently misbehave): unknown query ids and an
48
+ // escalateTo that doesn't resolve to a patch recipe.
49
+ const patchIds = new Set(catalog.filter((r) => r.mode !== 'analysis').map((r) => r.id));
50
+ for (const r of catalog) {
51
+ if (r.mode !== 'analysis')
52
+ continue;
53
+ const badQueries = (r.allowedQueries ?? []).filter((q) => !DELEGATE_QUERY_IDS.includes(q));
54
+ if (badQueries.length > 0) {
55
+ out.push({
56
+ id: `delegate-recipe-${r.id}-queries`,
57
+ title: `Delegate recipe "${r.id}" has unknown allowedQueries`,
58
+ severity: DoctorSeverity.Warning,
59
+ message: `${badQueries.join(', ')} — not in DELEGATE_QUERY_IDS (${DELEGATE_QUERY_IDS.join(', ')}); the model can never call them.`,
60
+ category: 'delegate',
61
+ code: 'recipe-unknown-query',
62
+ recommendedFix: `shrk delegate explain ${r.id}`,
63
+ });
64
+ }
65
+ if (r.escalateTo !== undefined && !patchIds.has(r.escalateTo)) {
66
+ out.push({
67
+ id: `delegate-recipe-${r.id}-escalate`,
68
+ title: `Delegate recipe "${r.id}" escalates to a non-patch recipe`,
69
+ severity: DoctorSeverity.Warning,
70
+ message: `escalateTo "${r.escalateTo}" is not an existing patch recipe — \`--escalate\` would be a no-op.`,
71
+ category: 'delegate',
72
+ code: 'recipe-bad-escalation',
73
+ recommendedFix: `shrk delegate explain ${r.id}`,
74
+ });
75
+ }
76
+ }
77
+ if (out.length === 0) {
27
78
  out.push({
28
79
  id: 'delegate-recipes',
29
80
  title: 'Delegate recipes',
30
81
  severity: DoctorSeverity.Ok,
31
- message: `${catalog.length} delegate recipe(s) configured, all delegatable.`,
82
+ message: `${catalog.length} delegate recipe(s) configured, all healthy.`,
32
83
  category: 'delegate',
33
84
  });
34
85
  }
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Delegate ANALYSIS grounding (read-only, deterministic — NO model).
3
+ *
4
+ * An `analysis` delegate recipe grounds a local model's judgment on a
5
+ * deterministic report the engine runs FIRST. This module:
6
+ * 1. runs that report (`runGroundingReport`) and distils it to a compact
7
+ * summary + a set of known entities (files / constructs / reason codes);
8
+ * 2. cross-checks the model's findings against those entities
9
+ * (`crossCheckFindings`) — a finding that cites an entity absent from the
10
+ * ground truth is flagged `unverified` (or dropped, in strict mode), so the
11
+ * model can prioritise/explain but never fabricate a fact;
12
+ * 3. assembles the honest, advisory `IDelegateAnalysisReport`
13
+ * (`buildDelegateAnalysisReport`) — which NEVER writes.
14
+ *
15
+ * The model call itself happens in the cli orchestrator, not here (this layer is
16
+ * pure). MCP surfaces only the grounding, never the model — see the safety model.
17
+ */
18
+ import type { DelegateGroundingId } from '@shrkcrft/core';
19
+ import { type IBuildTaskRiskOptions } from './task-risk.js';
20
+ import { type IUncertaintyReport } from './uncertainty-report.js';
21
+ import type { ISharkcraftInspection } from './sharkcraft-inspector.js';
22
+ export declare const DELEGATE_ANALYSIS_SCHEMA = "sharkcraft.delegate-analysis/v1";
23
+ /** The deterministic ground truth an analysis recipe is checked against. */
24
+ export interface IGroundingFacts {
25
+ groundedOn: string;
26
+ /** Compact human-readable summary fed to the model as ground truth. */
27
+ summary: string;
28
+ /** Known entities (normalised references the model may legitimately cite). */
29
+ entities: readonly string[];
30
+ /** The underlying deterministic report (surfaced in `--json`). */
31
+ raw: unknown;
32
+ }
33
+ /** One model finding, tagged by the deterministic cross-check. */
34
+ export interface IAnalysisFinding {
35
+ id: string;
36
+ message: string;
37
+ /** Entities the model cited (explicit refs, or refs inferred from the message). */
38
+ refs: readonly string[];
39
+ /** True when the finding is anchored to ground truth (via refs or a message mention). */
40
+ grounded: boolean;
41
+ /** Cited refs NOT present in the ground truth. */
42
+ unverifiedRefs: readonly string[];
43
+ }
44
+ /** The read-only advisory report an `analysis` recipe produces. */
45
+ export interface IDelegateAnalysisReport {
46
+ schema: typeof DELEGATE_ANALYSIS_SCHEMA;
47
+ generatedAt: string;
48
+ recipeId: string;
49
+ mode: 'analysis';
50
+ groundedOn: string;
51
+ task: string;
52
+ /** Which provider produced the findings (`'none'` = deterministic-only). */
53
+ provider: string;
54
+ grounding: {
55
+ summary: string;
56
+ entityCount: number;
57
+ };
58
+ findings: readonly IAnalysisFinding[];
59
+ groundedCount: number;
60
+ unverifiedCount: number;
61
+ /** Optional free-form note from the model (informational, never grounded). */
62
+ modelNote?: string;
63
+ /** How many read-only query rounds the model ran (Phase 3 query loop). */
64
+ queriesRun?: number;
65
+ /** How many fan-out slices ran (Phase 4), when fan-out was used. */
66
+ fanOutSlices?: number;
67
+ /** Advisory escalation into a patch recipe, when the recipe declares escalateTo. */
68
+ escalation?: {
69
+ recipe: string;
70
+ task: string;
71
+ next: string;
72
+ };
73
+ markdown: string;
74
+ uncertainty: IUncertaintyReport;
75
+ }
76
+ /**
77
+ * A layer-neutral snapshot of a failed delegate PATCH attempt — the ground truth
78
+ * a `retry-analysis` recipe reasons over. Kept primitive (no dependency on the
79
+ * cli-layer `IExecuteDelegateRunResult`) so the cli maps its result onto this and
80
+ * the inspector grounds on it without an upward import.
81
+ */
82
+ export interface IDelegateFailureContext {
83
+ /** The failing status, e.g. `verify-failed` / `conflicts` / `guardrail-refused`. */
84
+ status: string;
85
+ message: string;
86
+ conflicts?: readonly string[];
87
+ commandsFailed?: readonly string[];
88
+ refused?: readonly string[];
89
+ droppedOps?: readonly {
90
+ kind: string;
91
+ targetPath: string;
92
+ }[];
93
+ guardrailGlobs?: readonly string[];
94
+ allowedOps?: readonly string[];
95
+ /** 1-based attempt number this failure occurred on. */
96
+ attempt?: number;
97
+ }
98
+ /** Options for `runGroundingReport` — task-risk knobs plus grounding payloads. */
99
+ export interface IRunGroundingOptions extends IBuildTaskRiskOptions {
100
+ /** Required when grounding on `delegate-failure`. */
101
+ failure?: IDelegateFailureContext;
102
+ /** Required when grounding on `plan-simulation` — path to a saved plan. */
103
+ planPath?: string;
104
+ }
105
+ /** Distil a delegate patch failure into ground truth for `retry-analysis`. */
106
+ export declare function buildDelegateFailureFacts(failure: IDelegateFailureContext): IGroundingFacts;
107
+ /**
108
+ * Run the deterministic report an analysis recipe grounds on, distilled to a
109
+ * compact summary + the entities a finding may legitimately cite. Read-only.
110
+ */
111
+ export declare function runGroundingReport(groundedOn: DelegateGroundingId | string, task: string, inspection: ISharkcraftInspection, options?: IRunGroundingOptions): Promise<IGroundingFacts>;
112
+ export interface ICrossCheckOptions {
113
+ /** Drop non-grounded findings instead of keeping+flagging them. */
114
+ strict?: boolean;
115
+ }
116
+ export interface ICrossCheckResult {
117
+ findings: IAnalysisFinding[];
118
+ groundedCount: number;
119
+ unverifiedCount: number;
120
+ }
121
+ /**
122
+ * Cross-check model findings against the deterministic ground truth. A finding is
123
+ * `grounded` when it cites ≥1 ref that all resolve to a report entity, OR — when
124
+ * it cites no explicit refs — when its MESSAGE mentions a ground-truth entity
125
+ * verbatim (models routinely state facts in prose without filling a refs array;
126
+ * the live smoke showed exactly this). A claim anchored to nothing in the ground
127
+ * truth stays unverified — judgment must ride on facts the model cannot fabricate.
128
+ */
129
+ export declare function crossCheckFindings(rawFindings: readonly {
130
+ id?: string;
131
+ message: string;
132
+ refs?: readonly string[];
133
+ }[], facts: IGroundingFacts, options?: ICrossCheckOptions): ICrossCheckResult;
134
+ export interface IBuildAnalysisReportInput {
135
+ recipeId: string;
136
+ groundedOn: string;
137
+ task: string;
138
+ provider: string;
139
+ facts: IGroundingFacts;
140
+ crossCheck: ICrossCheckResult;
141
+ modelNote?: string;
142
+ /** True when no local model ran — the report is deterministic grounding only. */
143
+ modelUnavailable?: boolean;
144
+ /** Read-only query rounds the model ran (Phase 3), when the query loop was used. */
145
+ queriesRun?: number;
146
+ /** Fan-out slices that ran (Phase 4), when fan-out was used. */
147
+ fanOutSlices?: number;
148
+ /** A patch recipe this analysis may escalate to (Phase 4, advisory). */
149
+ escalateTo?: string;
150
+ generatedAt: string;
151
+ }
152
+ /** Assemble the honest, advisory analysis report. Never writes. */
153
+ export declare function buildDelegateAnalysisReport(input: IBuildAnalysisReportInput): IDelegateAnalysisReport;
154
+ //# sourceMappingURL=delegate-grounding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delegate-grounding.d.ts","sourceRoot":"","sources":["../src/delegate-grounding.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,427 @@
1
+ import { buildTaskRiskReport } from "./task-risk.js";
2
+ import { analyzeTestImpact } from "./test-impact.js";
3
+ import { simulatePlan } from "./plan-simulation.js";
4
+ import { buildAgentBrief } from "./agent-brief.js";
5
+ import * as nodePath from 'node:path';
6
+ import { buildUncertaintyReport } from "./uncertainty-report.js";
7
+ export const DELEGATE_ANALYSIS_SCHEMA = 'sharkcraft.delegate-analysis/v1';
8
+ function norm(s) {
9
+ // Strip surrounding delimiters/punctuation so a model that echoes a ground-truth
10
+ // display form (`[intent-migration]`, `` `src/foo.ts` ``) still matches the bare
11
+ // entity — otherwise a legitimate citation is falsely flagged `unverified`.
12
+ return s
13
+ .trim()
14
+ .toLowerCase()
15
+ .replace(/^[[\](){}`'"]+/, '')
16
+ .replace(/[[\](){}`'".,;:]+$/, '');
17
+ }
18
+ function basename(s) {
19
+ const n = norm(s);
20
+ const i = n.lastIndexOf('/');
21
+ return i >= 0 ? n.slice(i + 1) : n;
22
+ }
23
+ function looksLikePath(s) {
24
+ return /[\/.]/.test(s) && /\.(ts|tsx|js|jsx|mjs|cjs|md|json|ya?ml)$/i.test(s.trim());
25
+ }
26
+ /** Distil a task-risk report into the entities a finding may legitimately cite. */
27
+ function groundingEntitiesFromTaskRisk(r) {
28
+ const out = new Set();
29
+ const add = (xs) => {
30
+ for (const x of xs)
31
+ if (x && x.trim().length > 0)
32
+ out.add(x.trim());
33
+ };
34
+ add(r.affectedFiles);
35
+ add(r.highFanInFiles);
36
+ add(r.highFanOutFiles);
37
+ add(r.affectedConstructs);
38
+ add(r.reasons.map((x) => x.code));
39
+ add(r.ownershipGaps.filter(looksLikePath));
40
+ return [...out];
41
+ }
42
+ /** The path in a `"path: reason"` conflict string (the citable entity). */
43
+ function conflictPath(c) {
44
+ const i = c.indexOf(':');
45
+ return (i >= 0 ? c.slice(0, i) : c).trim();
46
+ }
47
+ /** Distil a delegate patch failure into ground truth for `retry-analysis`. */
48
+ export function buildDelegateFailureFacts(failure) {
49
+ const entities = new Set();
50
+ const add = (xs) => {
51
+ for (const x of xs ?? [])
52
+ if (x && x.trim().length > 0)
53
+ entities.add(x.trim());
54
+ };
55
+ add((failure.conflicts ?? []).map(conflictPath));
56
+ add(failure.refused);
57
+ add((failure.droppedOps ?? []).map((d) => d.targetPath));
58
+ add(failure.commandsFailed);
59
+ add(failure.allowedOps);
60
+ entities.add(failure.status);
61
+ const lines = [];
62
+ lines.push(`Delegate patch attempt failed: ${failure.status}${failure.attempt ? ` (attempt ${failure.attempt})` : ''}.`);
63
+ lines.push(`Message: ${failure.message}`);
64
+ if (failure.conflicts?.length)
65
+ lines.push(`Conflicts: ${failure.conflicts.join('; ')}.`);
66
+ if (failure.commandsFailed?.length)
67
+ lines.push(`Verification commands failed: ${failure.commandsFailed.join(', ')}.`);
68
+ if (failure.refused?.length)
69
+ lines.push(`Targets refused (outside guardrail globs): ${failure.refused.join(', ')}.`);
70
+ if (failure.droppedOps?.length) {
71
+ lines.push(`Ops dropped (kind not allowed): ${failure.droppedOps.map((d) => `${d.kind}→${d.targetPath}`).join(', ')}.`);
72
+ }
73
+ if (failure.allowedOps?.length)
74
+ lines.push(`Only these op kinds are allowed: ${failure.allowedOps.join(', ')}.`);
75
+ if (failure.guardrailGlobs?.length)
76
+ lines.push(`Only these globs may be touched: ${failure.guardrailGlobs.join(', ')}.`);
77
+ return { groundedOn: 'delegate-failure', summary: lines.join('\n'), entities: [...entities], raw: failure };
78
+ }
79
+ /** Entities + summary for grounding on the agent brief. */
80
+ function groundingFromAgentBrief(b) {
81
+ const entities = new Set();
82
+ if (b.taskRisk) {
83
+ for (const f of b.taskRisk.affectedFiles)
84
+ entities.add(f);
85
+ for (const c of b.taskRisk.affectedConstructs)
86
+ entities.add(c);
87
+ for (const f of b.taskRisk.highFanInFiles)
88
+ entities.add(f);
89
+ }
90
+ const risk = b.taskRisk ? `Risk: ${b.taskRisk.riskLevel} (score ${b.taskRisk.score}). ` : '';
91
+ const summary = `Agent brief for: ${b.task}\n${risk}\n${b.markdown.slice(0, 1400)}`;
92
+ return { summary, entities: [...entities].filter((x) => x.trim().length > 0) };
93
+ }
94
+ /** Entities a finding may cite when grounded on a test-impact report. */
95
+ function groundingEntitiesFromTestImpact(t) {
96
+ const out = new Set();
97
+ const add = (xs) => {
98
+ for (const x of xs)
99
+ if (x && x.trim().length > 0)
100
+ out.add(x.trim());
101
+ };
102
+ add(t.inputFiles);
103
+ add(t.likelyTestFiles);
104
+ add(t.missingTestFiles);
105
+ add(t.riskAreas);
106
+ return [...out];
107
+ }
108
+ /** Entities a finding may cite when grounded on a plan simulation. */
109
+ function groundingEntitiesFromPlanSim(p) {
110
+ const out = new Set();
111
+ const add = (xs) => {
112
+ for (const x of xs)
113
+ if (x && x.trim().length > 0)
114
+ out.add(x.trim());
115
+ };
116
+ add(p.files.map((f) => f.relativePath));
117
+ add(p.affectedAreas);
118
+ add(p.affectedConstructs);
119
+ add(p.ownershipReviewFiles);
120
+ add(p.likelyTests);
121
+ return [...out];
122
+ }
123
+ /** A compact ground-truth summary of a plan simulation for the model prompt. */
124
+ function compactPlanSimSummary(p) {
125
+ const lines = [];
126
+ lines.push(`Plan: ${p.source} (${p.templateId ?? 'no-template'}), signature ${p.signature}, readiness ${p.applyReadiness}.`);
127
+ if (p.files.length > 0)
128
+ lines.push(`Files: ${p.files.map((f) => `${f.changeType} ${f.relativePath}`).slice(0, 12).join(', ')}.`);
129
+ if (p.publicApiTouched)
130
+ lines.push('Touches a public API surface.');
131
+ if (p.ownershipReviewRequired)
132
+ lines.push(`Ownership review required: ${p.ownershipReviewFiles.slice(0, 6).join(', ')}.`);
133
+ if (p.potentialBoundaryConcerns.length > 0)
134
+ lines.push(`Boundary concerns: ${p.potentialBoundaryConcerns.length}.`);
135
+ if (p.policyConcerns.length > 0)
136
+ lines.push(`Policy concerns: ${p.policyConcerns.slice(0, 6).join('; ')}.`);
137
+ if (p.applyReadinessReasons.length > 0)
138
+ lines.push(`Readiness reasons: ${p.applyReadinessReasons.slice(0, 6).join('; ')}.`);
139
+ return lines.join('\n');
140
+ }
141
+ /** A compact ground-truth summary of a test-impact report for the model prompt. */
142
+ function compactTestImpactSummary(t) {
143
+ const lines = [];
144
+ lines.push(`Test impact for: ${t.task || '(repo)'} (confidence ${t.confidence}).`);
145
+ if (t.likelyTestFiles.length > 0)
146
+ lines.push(`Existing tests: ${t.likelyTestFiles.slice(0, 12).join(', ')}.`);
147
+ if (t.missingTestFiles.length > 0)
148
+ lines.push(`Missing tests (expected locations): ${t.missingTestFiles.slice(0, 12).join(', ')}.`);
149
+ if (t.riskAreas.length > 0)
150
+ lines.push(`Risk areas: ${t.riskAreas.slice(0, 8).join('; ')}.`);
151
+ if (t.minimalCommands.length > 0)
152
+ lines.push(`Minimal test command(s): ${t.minimalCommands.join(' ; ')}.`);
153
+ return lines.join('\n');
154
+ }
155
+ /** A compact ground-truth summary for the model prompt (not the full report). */
156
+ function compactTaskRiskSummary(r) {
157
+ const lines = [];
158
+ lines.push(`Task risk: ${r.riskLevel} (score ${r.score}), intent ${r.intent.kind}.`);
159
+ lines.push(`Human approval required: ${r.humanApprovalRequired ? 'yes' : 'no'}.`);
160
+ if (r.affectedFiles.length > 0)
161
+ lines.push(`Affected files: ${r.affectedFiles.slice(0, 12).join(', ')}.`);
162
+ if (r.highFanInFiles.length > 0)
163
+ lines.push(`High fan-in: ${r.highFanInFiles.join(', ')}.`);
164
+ if (r.boundaryConcerns.length > 0)
165
+ lines.push(`Boundary concerns: ${r.boundaryConcerns.slice(0, 6).join('; ')}.`);
166
+ if (r.policyConcerns.length > 0)
167
+ lines.push(`Policy concerns: ${r.policyConcerns.slice(0, 6).join('; ')}.`);
168
+ if (r.testGaps.length > 0)
169
+ lines.push(`Test gaps: ${r.testGaps.slice(0, 6).join('; ')}.`);
170
+ lines.push('Reasons:');
171
+ for (const reason of r.reasons.slice(0, 12))
172
+ lines.push(` - [${reason.code}] ${reason.message} (+${reason.weight})`);
173
+ return lines.join('\n');
174
+ }
175
+ /**
176
+ * Run the deterministic report an analysis recipe grounds on, distilled to a
177
+ * compact summary + the entities a finding may legitimately cite. Read-only.
178
+ */
179
+ export async function runGroundingReport(groundedOn, task, inspection, options = {}) {
180
+ switch (groundedOn) {
181
+ case 'task-risk': {
182
+ const r = await buildTaskRiskReport(task, inspection, options);
183
+ return {
184
+ groundedOn,
185
+ summary: compactTaskRiskSummary(r),
186
+ entities: groundingEntitiesFromTaskRisk(r),
187
+ raw: r,
188
+ };
189
+ }
190
+ case 'test-impact': {
191
+ const t = analyzeTestImpact(inspection, { task });
192
+ return {
193
+ groundedOn,
194
+ summary: compactTestImpactSummary(t),
195
+ entities: groundingEntitiesFromTestImpact(t),
196
+ raw: t,
197
+ };
198
+ }
199
+ case 'agent-brief': {
200
+ const b = await buildAgentBrief(inspection, { task });
201
+ const { summary, entities } = groundingFromAgentBrief(b);
202
+ return { groundedOn, summary, entities, raw: b };
203
+ }
204
+ case 'plan-simulation': {
205
+ // Grounded on a saved plan the cli supplies via `options.planPath`. Absent
206
+ // it (or on a read error) there is nothing to critique.
207
+ if (!options.planPath) {
208
+ return { groundedOn, summary: '(plan-simulation grounding needs a --plan <path>)', entities: [], raw: null };
209
+ }
210
+ try {
211
+ const abs = nodePath.isAbsolute(options.planPath)
212
+ ? options.planPath
213
+ : nodePath.resolve(inspection.projectRoot, options.planPath);
214
+ const p = await simulatePlan(inspection, abs, { includeBoundaries: true, includeImpact: true, includeOwnership: true });
215
+ return { groundedOn, summary: compactPlanSimSummary(p), entities: groundingEntitiesFromPlanSim(p), raw: p };
216
+ }
217
+ catch (e) {
218
+ return { groundedOn, summary: `(could not simulate plan: ${e.message})`, entities: [], raw: null };
219
+ }
220
+ }
221
+ case 'delegate-failure':
222
+ // Grounded on a failed patch attempt — the cli supplies the failure via
223
+ // `options.failure` (this grounding needs no inspection). Absent it, there
224
+ // is nothing to reason over (e.g. a standalone MCP/CLI call with no failure).
225
+ return options.failure
226
+ ? buildDelegateFailureFacts(options.failure)
227
+ : {
228
+ groundedOn,
229
+ summary: '(delegate-failure grounding needs a failure context — run it in the delegate retry loop)',
230
+ entities: [],
231
+ raw: null,
232
+ };
233
+ default:
234
+ // Config validation gates `groundedOn` to a known id, so this is a
235
+ // defensive fallback (empty ground truth ⇒ every claim is unverified).
236
+ return { groundedOn, summary: `(no grounding runner for "${groundedOn}")`, entities: [], raw: null };
237
+ }
238
+ }
239
+ /**
240
+ * Cross-check model findings against the deterministic ground truth. A finding is
241
+ * `grounded` when it cites ≥1 ref that all resolve to a report entity, OR — when
242
+ * it cites no explicit refs — when its MESSAGE mentions a ground-truth entity
243
+ * verbatim (models routinely state facts in prose without filling a refs array;
244
+ * the live smoke showed exactly this). A claim anchored to nothing in the ground
245
+ * truth stays unverified — judgment must ride on facts the model cannot fabricate.
246
+ */
247
+ export function crossCheckFindings(rawFindings, facts, options = {}) {
248
+ const entities = new Set(facts.entities.map(norm));
249
+ const basenames = new Set(facts.entities.map(basename));
250
+ const resolves = (ref) => entities.has(norm(ref)) || basenames.has(basename(ref));
251
+ // Entities long enough to match unambiguously inside prose (avoid noise from
252
+ // very short codes). Matched case-insensitively as a substring of the message.
253
+ const scannable = facts.entities.filter((e) => e.trim().length >= 4);
254
+ const mentionedIn = (message) => {
255
+ const m = message.toLowerCase();
256
+ const hits = [];
257
+ for (const e of scannable) {
258
+ const en = e.toLowerCase();
259
+ const bn = basename(e);
260
+ if (m.includes(en) || (bn.length >= 4 && m.includes(bn)))
261
+ hits.push(e);
262
+ }
263
+ return hits;
264
+ };
265
+ const findings = [];
266
+ let groundedCount = 0;
267
+ let unverifiedCount = 0;
268
+ for (let i = 0; i < rawFindings.length; i += 1) {
269
+ const rf = rawFindings[i];
270
+ const explicit = (rf.refs ?? []).map((r) => String(r));
271
+ let refs = explicit;
272
+ let unverifiedRefs = explicit.filter((r) => !resolves(r));
273
+ let grounded = explicit.length > 0 && unverifiedRefs.length === 0;
274
+ if (!grounded && explicit.length === 0) {
275
+ // Fallback: the model stated facts in prose. Infer refs from the message.
276
+ const inferred = mentionedIn(rf.message);
277
+ if (inferred.length > 0) {
278
+ refs = inferred;
279
+ unverifiedRefs = [];
280
+ grounded = true;
281
+ }
282
+ }
283
+ if (options.strict && !grounded) {
284
+ unverifiedCount += 1;
285
+ continue; // dropped
286
+ }
287
+ findings.push({
288
+ id: rf.id?.trim() || `finding-${i + 1}`,
289
+ message: rf.message,
290
+ refs,
291
+ grounded,
292
+ unverifiedRefs,
293
+ });
294
+ if (grounded)
295
+ groundedCount += 1;
296
+ else
297
+ unverifiedCount += 1;
298
+ }
299
+ return { findings, groundedCount, unverifiedCount };
300
+ }
301
+ function buildAnalysisUncertainty(input) {
302
+ const { crossCheck, modelUnavailable } = input;
303
+ let confidence = 'medium';
304
+ const reasons = [];
305
+ const missing = [];
306
+ if (modelUnavailable) {
307
+ confidence = 'unknown';
308
+ reasons.push('No local LLM reachable — deterministic grounding only, no judgment layer.');
309
+ missing.push({ id: 'no-model', message: 'No local model produced findings.' });
310
+ }
311
+ else if (crossCheck.findings.length === 0) {
312
+ confidence = 'low';
313
+ reasons.push('The model returned no findings.');
314
+ }
315
+ else if (crossCheck.unverifiedCount > crossCheck.groundedCount) {
316
+ confidence = 'low';
317
+ reasons.push(`${crossCheck.unverifiedCount} of ${crossCheck.findings.length} finding(s) are unverified (cite entities absent from the ground truth).`);
318
+ }
319
+ if (input.facts.entities.length === 0) {
320
+ missing.push({ id: 'empty-grounding', message: 'The grounding report yielded no entities to check against.' });
321
+ }
322
+ return buildUncertaintyReport({
323
+ confidence,
324
+ reasons,
325
+ missingSignals: missing,
326
+ suggestedCommands: [`shrk risk "${input.task || '<task>'}"`, `shrk delegate explain ${input.recipeId}`],
327
+ safeFallbackCommand: `shrk risk "${input.task || '<task>'}"`,
328
+ });
329
+ }
330
+ function renderAnalysisMarkdown(input) {
331
+ const { crossCheck, facts } = input;
332
+ const lines = [];
333
+ lines.push(`# Delegate analysis — ${input.recipeId}`);
334
+ lines.push('');
335
+ lines.push(`- task: ${input.task || '(none)'}`);
336
+ lines.push(`- grounded on: **${input.groundedOn}**`);
337
+ lines.push(`- provider: ${input.provider}`);
338
+ if (input.queriesRun !== undefined)
339
+ lines.push(`- query rounds: ${input.queriesRun} (read-only fact-fetch loop)`);
340
+ if (input.fanOutSlices !== undefined)
341
+ lines.push(`- fan-out slices: ${input.fanOutSlices} (per-slice passes, merged)`);
342
+ lines.push(`- findings: ${crossCheck.findings.length} (grounded ${crossCheck.groundedCount}, unverified ${crossCheck.unverifiedCount})`);
343
+ lines.push('');
344
+ lines.push('## Ground truth');
345
+ lines.push('');
346
+ lines.push(facts.summary);
347
+ lines.push('');
348
+ lines.push('## Findings');
349
+ lines.push('');
350
+ if (crossCheck.findings.length === 0) {
351
+ lines.push(input.modelUnavailable
352
+ ? '_No local LLM reachable — deterministic grounding only. Start Ollama / set LLAMACPP_MODEL_PATH to add the judgment layer._'
353
+ : '_(none)_');
354
+ }
355
+ else {
356
+ for (const f of crossCheck.findings) {
357
+ const tag = f.grounded ? '✓ grounded' : '⚠ unverified';
358
+ lines.push(`- **[${tag}]** ${f.message}`);
359
+ if (f.refs.length > 0)
360
+ lines.push(` - refs: ${f.refs.join(', ')}`);
361
+ if (f.unverifiedRefs.length > 0)
362
+ lines.push(` - not in ground truth: ${f.unverifiedRefs.join(', ')}`);
363
+ }
364
+ }
365
+ if (input.modelNote) {
366
+ lines.push('');
367
+ lines.push('## Model note (informational)');
368
+ lines.push('');
369
+ lines.push(input.modelNote);
370
+ }
371
+ const escalation = escalationHint(input);
372
+ if (escalation) {
373
+ lines.push('');
374
+ lines.push('## Suggested escalation (advisory — you run it)');
375
+ lines.push('');
376
+ lines.push(`This gap can be handed to the patch recipe \`${escalation.recipe}\`:`);
377
+ lines.push('```');
378
+ lines.push(escalation.next);
379
+ lines.push('```');
380
+ }
381
+ lines.push('');
382
+ lines.push('---');
383
+ lines.push('_Advisory, read-only — no write performed. Findings tagged `unverified` cite entities absent from the deterministic ground truth; treat them as hypotheses, not facts._');
384
+ return lines.join('\n');
385
+ }
386
+ /** Assemble the honest, advisory analysis report. Never writes. */
387
+ export function buildDelegateAnalysisReport(input) {
388
+ const report = {
389
+ schema: DELEGATE_ANALYSIS_SCHEMA,
390
+ generatedAt: input.generatedAt,
391
+ recipeId: input.recipeId,
392
+ mode: 'analysis',
393
+ groundedOn: input.groundedOn,
394
+ task: input.task,
395
+ provider: input.provider,
396
+ grounding: { summary: input.facts.summary, entityCount: input.facts.entities.length },
397
+ findings: input.crossCheck.findings,
398
+ groundedCount: input.crossCheck.groundedCount,
399
+ unverifiedCount: input.crossCheck.unverifiedCount,
400
+ markdown: renderAnalysisMarkdown(input),
401
+ uncertainty: buildAnalysisUncertainty(input),
402
+ };
403
+ if (input.modelNote)
404
+ report.modelNote = input.modelNote;
405
+ if (input.queriesRun !== undefined)
406
+ report.queriesRun = input.queriesRun;
407
+ if (input.fanOutSlices !== undefined)
408
+ report.fanOutSlices = input.fanOutSlices;
409
+ const escalation = escalationHint(input);
410
+ if (escalation)
411
+ report.escalation = escalation;
412
+ return report;
413
+ }
414
+ /** Advisory next-command into a patch recipe, when findings warrant escalation. */
415
+ function escalationHint(input) {
416
+ if (!input.escalateTo)
417
+ return undefined;
418
+ const anchor = input.crossCheck.findings.find((f) => f.grounded) ?? input.crossCheck.findings[0];
419
+ if (!anchor)
420
+ return undefined;
421
+ const task = anchor.message.slice(0, 120);
422
+ return {
423
+ recipe: input.escalateTo,
424
+ task,
425
+ next: `shrk delegate run "${task}" --recipe ${input.escalateTo} --apply`,
426
+ };
427
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"docs-check.d.ts","sourceRoot":"","sources":["../src/docs-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,eAAO,MAAM,iBAAiB,6BAA6B,CAAC;AAE5D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EACA,sBAAsB,GACtB,wBAAwB,GACxB,oBAAoB,GACpB,0BAA0B,GAC1B,gCAAgC,GAChC,yBAAyB,CAAC;IAC9B,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,iBAAiB,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACvC,aAAa,EAAE,OAAO,CAAC;IACvB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,EAAE,EAAE,OAAO,CAAC;CACb;AAwCD,MAAM,WAAW,sBAAsB;IACrC;2CACuC;IACvC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,sBAA2B,GACnC,gBAAgB,CA+IlB;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAepE"}
1
+ {"version":3,"file":"docs-check.d.ts","sourceRoot":"","sources":["../src/docs-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,eAAO,MAAM,iBAAiB,6BAA6B,CAAC;AAE5D,MAAM,WAAW,iBAAiB;IAChC,IAAI,EACA,sBAAsB,GACtB,wBAAwB,GACxB,oBAAoB,GACpB,0BAA0B,GAC1B,gCAAgC,GAChC,yBAAyB,CAAC;IAC9B,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,OAAO,iBAAiB,CAAC;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACvC,aAAa,EAAE,OAAO,CAAC;IACvB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,EAAE,EAAE,OAAO,CAAC;CACb;AAyCD,MAAM,WAAW,sBAAsB;IACrC;2CACuC;IACvC,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,sBAA2B,GACnC,gBAAgB,CA+IlB;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,CAepE"}
@@ -19,6 +19,7 @@ const REQUIRED_DOCS = [
19
19
  'philosophy.md',
20
20
  'safety-model.md',
21
21
  'testing.md',
22
+ 'exit-codes.md',
22
23
  ];
23
24
  const README_REQUIRED_KEYWORDS = ['quick demo', 'onboard', 'safety'];
24
25
  const README_SAFETY_KEYWORDS = ['mcp', 'read-only', 'never writes', 'safety'];
@@ -21,6 +21,22 @@ export declare function isGitRepo(cwd: string): boolean;
21
21
  export declare function getGitRoot(cwd: string): string | null;
22
22
  export declare function getGitBranch(cwd: string): string | null;
23
23
  export declare function getChangedFiles(cwd: string, opts?: IGitChangedOptions): string[];
24
+ /**
25
+ * True when `ref` resolves to a commit in `cwd`'s repo. Lets a caller tell a
26
+ * genuinely-empty diff apart from a bad ref — `getChangedFiles` returns `[]` for
27
+ * both (a failed `git diff <bad-ref>` yields no names), so a gate that scopes on
28
+ * `--base <ref>` must validate the ref first or it reports "nothing changed"
29
+ * (a false verified-nothing) over a typo'd ref.
30
+ */
31
+ export declare function refExists(cwd: string, ref: string): boolean;
32
+ /**
33
+ * The content of `relPath` at `ref` (e.g. `git show HEAD:src/x.ts`), or `null`
34
+ * when the path did not exist at that ref (a newly-added file) or git failed.
35
+ * `relPath` is repo-root-relative. Used to diff a file's PAST state against the
36
+ * working tree without a checkout — e.g. to see what a now-edited file used to
37
+ * provide/register.
38
+ */
39
+ export declare function gitShowFile(cwd: string, ref: string, relPath: string): string | null;
24
40
  export declare function getStatusSummary(cwd: string): IGitStatusSummary;
25
41
  export interface ICommitInfo {
26
42
  readonly hash: string;
@@ -1 +1 @@
1
- {"version":3,"file":"git-helpers.d.ts","sourceRoot":"","sources":["../src/git-helpers.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,kBAAkB;IACjC,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gEAAgE;IAChE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;CAChB;AAqBD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAI9C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKrD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKvD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,kBAAuB,GAAG,MAAM,EAAE,CAwBpF;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAyC/D;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,WAAW,EAAE,CA6BrF;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAKjD"}
1
+ {"version":3,"file":"git-helpers.d.ts","sourceRoot":"","sources":["../src/git-helpers.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,kBAAkB;IACjC,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gEAAgE;IAChE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;CAChB;AAqBD,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAI9C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKrD;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKvD;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,kBAAuB,GAAG,MAAM,EAAE,CAwBpF;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAI3D;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGpF;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAyC/D;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACnC;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,WAAW,EAAE,CA6BrF;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAKjD"}
@@ -67,6 +67,30 @@ export function getChangedFiles(cwd, opts = {}) {
67
67
  }
68
68
  return [...set].sort();
69
69
  }
70
+ /**
71
+ * True when `ref` resolves to a commit in `cwd`'s repo. Lets a caller tell a
72
+ * genuinely-empty diff apart from a bad ref — `getChangedFiles` returns `[]` for
73
+ * both (a failed `git diff <bad-ref>` yields no names), so a gate that scopes on
74
+ * `--base <ref>` must validate the ref first or it reports "nothing changed"
75
+ * (a false verified-nothing) over a typo'd ref.
76
+ */
77
+ export function refExists(cwd, ref) {
78
+ if (!ref)
79
+ return false;
80
+ const r = runGit(cwd, ['rev-parse', '--verify', '--quiet', `${ref}^{commit}`]);
81
+ return r.ok && r.stdout.trim().length > 0;
82
+ }
83
+ /**
84
+ * The content of `relPath` at `ref` (e.g. `git show HEAD:src/x.ts`), or `null`
85
+ * when the path did not exist at that ref (a newly-added file) or git failed.
86
+ * `relPath` is repo-root-relative. Used to diff a file's PAST state against the
87
+ * working tree without a checkout — e.g. to see what a now-edited file used to
88
+ * provide/register.
89
+ */
90
+ export function gitShowFile(cwd, ref, relPath) {
91
+ const r = runGit(cwd, ['show', `${ref}:${relPath}`]);
92
+ return r.ok ? r.stdout : null;
93
+ }
70
94
  export function getStatusSummary(cwd) {
71
95
  const empty = {
72
96
  branch: null,
@@ -1 +1 @@
1
- {"version":3,"file":"import-hygiene.d.ts","sourceRoot":"","sources":["../src/import-hygiene.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"import-hygiene.d.ts","sourceRoot":"","sources":["../src/import-hygiene.ts"],"names":[],"mappings":"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"}
@@ -251,6 +251,15 @@ function stripCommentsPreservingOffsets(source) {
251
251
  }
252
252
  return buf.join('');
253
253
  }
254
+ /**
255
+ * Files import-hygiene never gates: test + fixture sources. Their whole PURPOSE
256
+ * is often to CONTAIN import-like text (`"const x = require('./y')"` as a string
257
+ * literal feeding a scanner test), so gating them produces false positives. The
258
+ * whole-tree scan has always excluded them; this constant makes the explicit
259
+ * `files` path (changed-only / `finish`) apply the SAME rule so the two can't
260
+ * drift — a change to a test file no longer spuriously fails an imports gate.
261
+ */
262
+ const EXCLUDED_FROM_HYGIENE = /__tests__|\/__fixtures__/;
254
263
  function defaultRoots(projectRoot) {
255
264
  const pkgsDir = nodePath.join(projectRoot, 'packages');
256
265
  if (!existsSync(pkgsDir))
@@ -272,10 +281,11 @@ export function buildImportHygieneReport(projectRoot, options = {}) {
272
281
  if (options.files && options.files.length > 0) {
273
282
  scanned = options.files
274
283
  .map((f) => (nodePath.isAbsolute(f) ? f : nodePath.join(projectRoot, f)))
275
- .filter((f) => existsSync(f) && /\.(ts|tsx)$/.test(f) && !/\.d\.ts$/.test(f));
284
+ .filter((f) => existsSync(f) && /\.(ts|tsx)$/.test(f) && !/\.d\.ts$/.test(f))
285
+ .filter((f) => !EXCLUDED_FROM_HYGIENE.test(f));
276
286
  }
277
287
  else {
278
- scanned = roots.flatMap(listSourceFiles).filter((f) => !/__tests__|\/__fixtures__/.test(f));
288
+ scanned = roots.flatMap(listSourceFiles).filter((f) => !EXCLUDED_FROM_HYGIENE.test(f));
279
289
  }
280
290
  const findings = [];
281
291
  const strictReasons = options.strictAllowlistReasons === true;
package/dist/index.d.ts CHANGED
@@ -146,6 +146,7 @@ export * from './migration-readiness.js';
146
146
  export * from './contract-file-rule.js';
147
147
  export * from './check-guardrail-globs.js';
148
148
  export * from './delegate-catalog.js';
149
+ export * from './delegate-grounding.js';
149
150
  export * from './delegate-pack-recipes.js';
150
151
  export * from './delegate-doctor.js';
151
152
  export * from './agent-contract.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -146,6 +146,7 @@ export * from "./migration-readiness.js";
146
146
  export * from "./contract-file-rule.js";
147
147
  export * from "./check-guardrail-globs.js";
148
148
  export * from "./delegate-catalog.js";
149
+ export * from "./delegate-grounding.js";
149
150
  export * from "./delegate-pack-recipes.js";
150
151
  export * from "./delegate-doctor.js";
151
152
  export * from "./agent-contract.js";
@@ -1,7 +1,7 @@
1
1
  import { type AppError, type Result } from '@shrkcrft/core';
2
2
  import { type LoadedConfig } from '@shrkcrft/config';
3
3
  /**
4
- * A {@link LoadedConfig} whose four data planes have had pack contributions
4
+ * A {@link LoadedConfig} whose data planes have had pack contributions
5
5
  * merged in (local-wins), plus the human-readable notes from that merge.
6
6
  */
7
7
  export interface IResolvedProjectConfig extends LoadedConfig {
@@ -1 +1 @@
1
- {"version":3,"file":"resolve-project-config.d.ts","sourceRoot":"","sources":["../src/resolve-project-config.ts"],"names":[],"mappings":"AAuBA,OAAO,EAGL,KAAK,QAAQ,EAMb,KAAK,MAAM,EACZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAOL,KAAK,YAAY,EAClB,MAAM,kBAAkB,CAAC;AAG1B;;;GAGG;AACH,MAAM,WAAW,sBAAuB,SAAQ,YAAY;IAC1D;;;;;;OAMG;IACH,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9C;AAoHD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,CAAC,sBAAsB,EAAE,QAAQ,CAAC,CAAC,CAoEnD"}
1
+ {"version":3,"file":"resolve-project-config.d.ts","sourceRoot":"","sources":["../src/resolve-project-config.ts"],"names":[],"mappings":"AAwBA,OAAO,EAGL,KAAK,QAAQ,EAQb,KAAK,MAAM,EACZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,EASL,KAAK,YAAY,EAClB,MAAM,kBAAkB,CAAC;AAG1B;;;GAGG;AACH,MAAM,WAAW,sBAAuB,SAAQ,YAAY;IAC1D;;;;;;OAMG;IACH,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9C;AAiID;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,CAAC,sBAAsB,EAAE,QAAQ,CAAC,CAAC,CAqGnD"}
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Pack-aware project-config resolution.
3
3
  *
4
- * The four "cross-file invariant as DATA" planes — `wiringRules`, `registries`,
5
- * `policyRules`, `reusePrimitives` — can be authored inline in a repo's
4
+ * The "cross-file invariant as DATA" planes — `wiringRules`, `registries`,
5
+ * `registrationGraph`, `policyRules`, `baselines`, `generatedArtifacts`,
6
+ * `reusePrimitives` — can be authored inline in a repo's
6
7
  * `sharkcraft.config.ts`. They can ALSO be SHIPPED by a framework pack (e.g. a
7
8
  * NestJS pack contributing "every @Injectable must be registered in a module"
8
9
  * as a wiring rule) via the new `wiringRuleFiles` / `registryFiles` /
@@ -22,7 +23,7 @@
22
23
  import { existsSync } from 'node:fs';
23
24
  import * as nodePath from 'node:path';
24
25
  import { importModuleViaLoader, ok, } from '@shrkcrft/core';
25
- import { loadProjectConfig, PolicyRuleSchema, RegistrationIdiomSchema, RegistryDeclarationSchema, ReusePrimitiveSchema, WiringRuleSchema, } from '@shrkcrft/config';
26
+ import { BaselineRuleSchema, GeneratedArtifactRuleSchema, loadProjectConfig, PolicyRuleSchema, RegistrationIdiomSchema, RegistryDeclarationSchema, ReusePrimitiveSchema, WiringRuleSchema, } from '@shrkcrft/config';
26
27
  import { discoverPacks } from '@shrkcrft/packs';
27
28
  /**
28
29
  * Generic per-plane load + validate + merge. Seeds the merged map from the
@@ -31,7 +32,13 @@ import { discoverPacks } from '@shrkcrft/packs';
31
32
  * elements, and key collisions all become diagnostics and are skipped — never a
32
33
  * throw.
33
34
  */
34
- async function mergePlane(localArr, packContribs, schema, keyOf, planeLabel, diagnostics) {
35
+ async function mergePlane(localArr, packContribs, schema, keyOf, planeLabel, diagnostics,
36
+ /**
37
+ * Optional per-item veto applied to PACK elements only. Returns the
38
+ * diagnostic to record when the element must not be adopted (used by the
39
+ * shell-executing planes — see the call sites).
40
+ */
41
+ packGuard) {
35
42
  const merged = new Map();
36
43
  const localKeys = new Set();
37
44
  for (const item of localArr) {
@@ -68,6 +75,11 @@ async function mergePlane(localArr, packContribs, schema, keyOf, planeLabel, dia
68
75
  continue;
69
76
  }
70
77
  const item = parsed.data;
78
+ const veto = packGuard?.(item, contrib.packageName);
79
+ if (veto !== undefined) {
80
+ diagnostics.push(veto);
81
+ continue;
82
+ }
71
83
  const key = keyOf(item);
72
84
  if (merged.has(key)) {
73
85
  diagnostics.push(localKeys.has(key)
@@ -120,6 +132,19 @@ export async function resolveProjectConfig(cwd) {
120
132
  const registries = await mergePlane(base.config.registries ?? [], gatherPackContribs(validPacks, 'registryFiles'), RegistryDeclarationSchema, (r) => r.name, 'registry', diagnostics);
121
133
  const registrationGraph = await mergePlane(base.config.registrationGraph ?? [], gatherPackContribs(validPacks, 'registrationGraphFiles'), RegistrationIdiomSchema, (r) => r.name, 'registrationIdiom', diagnostics);
122
134
  const policyRules = await mergePlane(base.config.policyRules ?? [], gatherPackContribs(validPacks, 'policyRuleFiles'), PolicyRuleSchema, (r) => r.id, 'policyRule', diagnostics);
135
+ // The two SHELL-EXECUTING planes. A pack ships code the repo did not write;
136
+ // letting it also ship a command that `shrk baseline check` would then RUN is
137
+ // the same hazard the "pack-contributed verification commands are NOT
138
+ // auto-run" contract already forbids. So the merge keeps the safe subset —
139
+ // extractor computes and header-only generated rules — and drops the rest
140
+ // with a diagnostic. The guarantee is structural: no downstream caller has to
141
+ // remember to re-check provenance.
142
+ const baselines = await mergePlane(base.config.baselines ?? [], gatherPackContribs(validPacks, 'baselineFiles'), BaselineRuleSchema, (r) => r.id, 'baseline', diagnostics, (item, packName) => item.compute?.kind === 'command'
143
+ ? `pack ${packName}: baseline "${item.id}" declares a shell \`compute.run\` — pack-contributed commands are never auto-run — skipped`
144
+ : undefined);
145
+ const generatedArtifacts = await mergePlane(base.config.generatedArtifacts ?? [], gatherPackContribs(validPacks, 'generatedArtifactFiles'), GeneratedArtifactRuleSchema, (r) => r.id, 'generatedArtifact', diagnostics, (item, packName) => item.regen !== undefined
146
+ ? `pack ${packName}: generatedArtifact "${item.id}" declares a \`regen\` command — pack-contributed commands are never auto-run — skipped`
147
+ : undefined);
123
148
  const reusePrimitives = await mergePlane(base.config.reusePrimitives ?? [], gatherPackContribs(validPacks, 'reusePrimitiveFiles'), ReusePrimitiveSchema, (r) => r.symbol, 'reusePrimitive', diagnostics);
124
149
  return ok({
125
150
  ...base,
@@ -129,6 +154,8 @@ export async function resolveProjectConfig(cwd) {
129
154
  registries,
130
155
  registrationGraph,
131
156
  policyRules,
157
+ baselines,
158
+ generatedArtifacts,
132
159
  reusePrimitives,
133
160
  },
134
161
  planeDiagnostics: diagnostics,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shrkcrft/inspector",
3
- "version": "0.1.0-alpha.26",
3
+ "version": "0.1.0-alpha.28",
4
4
  "description": "SharkCraft inspector: project overview, doctor checks, AI-agent instructions.",
5
5
  "license": "MIT",
6
6
  "author": "SharkCraft contributors",
@@ -42,22 +42,22 @@
42
42
  "typecheck": "tsc --noEmit -p tsconfig.json"
43
43
  },
44
44
  "dependencies": {
45
- "@shrkcrft/core": "^0.1.0-alpha.26",
46
- "@shrkcrft/config": "^0.1.0-alpha.26",
47
- "@shrkcrft/workspace": "^0.1.0-alpha.26",
48
- "@shrkcrft/knowledge": "^0.1.0-alpha.26",
49
- "@shrkcrft/rules": "^0.1.0-alpha.26",
50
- "@shrkcrft/paths": "^0.1.0-alpha.26",
51
- "@shrkcrft/templates": "^0.1.0-alpha.26",
52
- "@shrkcrft/context": "^0.1.0-alpha.26",
53
- "@shrkcrft/pipelines": "^0.1.0-alpha.26",
54
- "@shrkcrft/packs": "^0.1.0-alpha.26",
55
- "@shrkcrft/presets": "^0.1.0-alpha.26",
56
- "@shrkcrft/boundaries": "^0.1.0-alpha.26",
57
- "@shrkcrft/generator": "^0.1.0-alpha.26",
58
- "@shrkcrft/importer": "^0.1.0-alpha.26",
59
- "@shrkcrft/plugin-api": "^0.1.0-alpha.26",
60
- "@shrkcrft/dashboard-api": "^0.1.0-alpha.26",
45
+ "@shrkcrft/core": "^0.1.0-alpha.28",
46
+ "@shrkcrft/config": "^0.1.0-alpha.28",
47
+ "@shrkcrft/workspace": "^0.1.0-alpha.28",
48
+ "@shrkcrft/knowledge": "^0.1.0-alpha.28",
49
+ "@shrkcrft/rules": "^0.1.0-alpha.28",
50
+ "@shrkcrft/paths": "^0.1.0-alpha.28",
51
+ "@shrkcrft/templates": "^0.1.0-alpha.28",
52
+ "@shrkcrft/context": "^0.1.0-alpha.28",
53
+ "@shrkcrft/pipelines": "^0.1.0-alpha.28",
54
+ "@shrkcrft/packs": "^0.1.0-alpha.28",
55
+ "@shrkcrft/presets": "^0.1.0-alpha.28",
56
+ "@shrkcrft/boundaries": "^0.1.0-alpha.28",
57
+ "@shrkcrft/generator": "^0.1.0-alpha.28",
58
+ "@shrkcrft/importer": "^0.1.0-alpha.28",
59
+ "@shrkcrft/plugin-api": "^0.1.0-alpha.28",
60
+ "@shrkcrft/dashboard-api": "^0.1.0-alpha.28",
61
61
  "typescript": "^5.6.0"
62
62
  },
63
63
  "publishConfig": {