@shrkcrft/inspector 0.1.0-alpha.17 → 0.1.0-alpha.19
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/agent-instructions.d.ts.map +1 -1
- package/dist/agent-instructions.js +11 -0
- package/dist/ai-readiness.d.ts.map +1 -1
- package/dist/ai-readiness.js +20 -5
- package/dist/changed-preflight.d.ts +7 -0
- package/dist/changed-preflight.d.ts.map +1 -1
- package/dist/changed-preflight.js +56 -9
- package/dist/check-guardrail-globs.d.ts +16 -0
- package/dist/check-guardrail-globs.d.ts.map +1 -0
- package/dist/check-guardrail-globs.js +38 -0
- package/dist/code-intelligence-doctor.js +25 -5
- package/dist/command-recommender.d.ts.map +1 -1
- package/dist/command-recommender.js +16 -4
- package/dist/contract-file-rule.d.ts +8 -0
- package/dist/contract-file-rule.d.ts.map +1 -1
- package/dist/contract-file-rule.js +8 -3
- package/dist/coverage-report.d.ts.map +1 -1
- package/dist/coverage-report.js +14 -1
- package/dist/delegate-catalog.d.ts +45 -0
- package/dist/delegate-catalog.d.ts.map +1 -0
- package/dist/delegate-catalog.js +50 -0
- package/dist/delegate-doctor.d.ts +15 -0
- package/dist/delegate-doctor.d.ts.map +1 -0
- package/dist/delegate-doctor.js +36 -0
- package/dist/delegate-pack-recipes.d.ts +29 -0
- package/dist/delegate-pack-recipes.d.ts.map +1 -0
- package/dist/delegate-pack-recipes.js +77 -0
- package/dist/git-helpers.d.ts +15 -0
- package/dist/git-helpers.d.ts.map +1 -1
- package/dist/git-helpers.js +41 -0
- package/dist/impact-analysis.d.ts.map +1 -1
- package/dist/impact-analysis.js +10 -2
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/pack-contributions-inventory.d.ts.map +1 -1
- package/dist/pack-contributions-inventory.js +6 -0
- package/dist/pack-signature-status.d.ts.map +1 -1
- package/dist/pack-signature-status.js +18 -1
- package/dist/plan-simulation.d.ts +13 -0
- package/dist/plan-simulation.d.ts.map +1 -1
- package/dist/plan-simulation.js +1 -1
- package/dist/propose-knowledge.d.ts +15 -0
- package/dist/propose-knowledge.d.ts.map +1 -1
- package/dist/propose-knowledge.js +37 -4
- package/dist/resolve-verification-commands.d.ts +26 -0
- package/dist/resolve-verification-commands.d.ts.map +1 -0
- package/dist/resolve-verification-commands.js +55 -0
- package/dist/rule-drift.d.ts.map +1 -1
- package/dist/rule-drift.js +24 -9
- package/dist/sharkcraft-inspector.d.ts.map +1 -1
- package/dist/sharkcraft-inspector.js +7 -0
- package/dist/start-here.d.ts +1 -1
- package/dist/start-here.d.ts.map +1 -1
- package/dist/start-here.js +15 -0
- package/dist/task-packet.d.ts.map +1 -1
- package/dist/task-packet.js +13 -1
- package/dist/test-impact.d.ts.map +1 -1
- package/dist/test-impact.js +5 -2
- package/package.json +17 -17
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-instructions.d.ts","sourceRoot":"","sources":["../src/agent-instructions.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB,
|
|
1
|
+
{"version":3,"file":"agent-instructions.d.ts","sourceRoot":"","sources":["../src/agent-instructions.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,kBAAkB,QA+BvB,CAAC"}
|
|
@@ -9,6 +9,17 @@ Default behavior:
|
|
|
9
9
|
- For generation, list_templates → get_template → create_generation_plan (dry-run by default).
|
|
10
10
|
- Respect path conventions (list_path_conventions, get_path_convention).
|
|
11
11
|
|
|
12
|
+
Understand existing code before you edit (prefer the graph over grep — it returns path:line truth):
|
|
13
|
+
- Who calls / where is a symbol used? call get_graph_callers or code_find_usages (returns path:line).
|
|
14
|
+
- What breaks if I change this file/symbol? call get_graph_impact.
|
|
15
|
+
- What is the load-bearing code (change carefully / understand first)? call get_graph_hubs — most-referenced symbols + most-imported files.
|
|
16
|
+
- Is code A actually wired to code B? call get_graph_path (shortest import/call/implements path between two files or symbols — the deterministic "is X wired to Y").
|
|
17
|
+
- Is X wired / how does this file connect (incl. subtypes/supertypes)? call get_graph_context (get_graph_search to locate it first).
|
|
18
|
+
- These read an index; if a graph tool reports it's missing or stale, run \`shrk graph index\` once.
|
|
19
|
+
|
|
20
|
+
Save your tokens on MECHANICAL edits — delegate them to a local worker:
|
|
21
|
+
- For a repetitive, deterministically-verifiable edit (add a barrel export, ensure an import, a glob-scoped rename) call delegate_task to get a compact brief + the exact \`shrk delegate run\` command, then run it. The local model produces the edit, the engine verifies it (and auto-reverts on failure), and you pay for the brief + result — not for reading the whole file and writing the edit yourself. \`shrk delegate list\` shows what's delegatable.
|
|
22
|
+
|
|
12
23
|
Safety:
|
|
13
24
|
- Never write files unless the user explicitly asks; always prefer dry-run first.
|
|
14
25
|
- Never overwrite existing files unless the plan resolves all conflicts.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-readiness.d.ts","sourceRoot":"","sources":["../src/ai-readiness.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"ai-readiness.d.ts","sourceRoot":"","sources":["../src/ai-readiness.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAYvE;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,UAAU,GAAG,eAAe,CAAC;AAEvE,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,kBAAkB;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,8FAA8F;IAC9F,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,OAAO,EAAE,kBAAkB,CAAC;IAC5B,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;AAEvE;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,4DAA4D;IAC5D,mBAAmB,EAAE,OAAO,CAAC;IAC7B,qEAAqE;IACrE,kBAAkB,EAAE,OAAO,CAAC;IAC5B,sDAAsD;IACtD,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,gBAAgB;IAC/B,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,cAAc,CAAC;IACtB,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,8EAA8E;IAC9E,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,mEAAmE;IACnE,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,wEAAwE;IACxE,cAAc,EAAE,eAAe,CAAC;CACjC;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,SAAS,EAAE,OAAO,CAAC;IACnB,oEAAoE;IACpE,SAAS,EAAE,OAAO,CAAC;IACnB,8EAA8E;IAC9E,UAAU,EAAE,OAAO,CAAC;IACpB,wHAAwH;IACxH,aAAa,EAAE,OAAO,CAAC;CACxB;AAyED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,qBAAqB,GAAG,gBAAgB,CAwV1F"}
|
package/dist/ai-readiness.js
CHANGED
|
@@ -2,6 +2,13 @@ import { KnowledgeType, hasActionHints } from '@shrkcrft/knowledge';
|
|
|
2
2
|
import { WorkspaceProfile } from '@shrkcrft/workspace';
|
|
3
3
|
import { diagnoseActionHints } from "./action-hint-diagnostics.js";
|
|
4
4
|
import { runDoctor } from "./sharkcraft-inspector.js";
|
|
5
|
+
/**
|
|
6
|
+
* Entry types that describe a *location* or a *fact* rather than an action, so
|
|
7
|
+
* they are not expected to carry actionHints and are excluded from the
|
|
8
|
+
* action-hint coverage metric. Mirrors the skip set in
|
|
9
|
+
* `action-hint-diagnostics.ts` (keep the two in sync).
|
|
10
|
+
*/
|
|
11
|
+
const HINT_EXEMPT_TYPES = new Set(['path', 'overview', 'technical']);
|
|
5
12
|
function gradeOf(score) {
|
|
6
13
|
if (score >= 85)
|
|
7
14
|
return 'excellent';
|
|
@@ -217,19 +224,27 @@ export function buildAiReadinessReport(inspection) {
|
|
|
217
224
|
});
|
|
218
225
|
// No recommendation when pipelines is advisory — the old "add a pipeline"
|
|
219
226
|
// exhortation fired on libraries it shouldn't have.
|
|
220
|
-
// 7) Action-hint coverage — fraction of entries that carry
|
|
221
|
-
|
|
222
|
-
|
|
227
|
+
// 7) Action-hint coverage — fraction of HINT-ELIGIBLE entries that carry
|
|
228
|
+
// hints. Path / overview / technical entries describe a location or a fact
|
|
229
|
+
// rather than an action, so the action-hint quality doctor deliberately
|
|
230
|
+
// doesn't grade them (see action-hint-diagnostics). Counting them in the
|
|
231
|
+
// denominator penalised the score for structural non-gaps — an entry that
|
|
232
|
+
// *can't* meaningfully carry action hints isn't a missing one — so they are
|
|
233
|
+
// excluded from both numerator and denominator here too.
|
|
234
|
+
const hintEligible = inspection.knowledgeEntries.filter((e) => !HINT_EXEMPT_TYPES.has(String(e.type).toLowerCase()));
|
|
235
|
+
const eligibleCount = hintEligible.length;
|
|
236
|
+
const withHints = hintEligible.filter((e) => hasActionHints(e)).length;
|
|
237
|
+
const hintsScore = eligibleCount === 0 ? 0 : Math.min(10, Math.round((withHints / eligibleCount) * 20));
|
|
223
238
|
dims.push({
|
|
224
239
|
id: 'action-hints',
|
|
225
240
|
title: 'Entries with action hints',
|
|
226
241
|
weight: 1.2,
|
|
227
242
|
score: hintsScore,
|
|
228
|
-
note: `${withHints} of ${
|
|
243
|
+
note: `${withHints} of ${eligibleCount} hint-eligible entries carry actionHints`,
|
|
229
244
|
applies: 'core',
|
|
230
245
|
});
|
|
231
246
|
if (hintsScore < 7)
|
|
232
|
-
recs.push('Add actionHints to high-priority
|
|
247
|
+
recs.push('Add actionHints to high-priority entries (commands, mcpTools, forbiddenActions, relatedKnowledge).');
|
|
233
248
|
// 8) Verification commands
|
|
234
249
|
const haveVerify = inspection.knowledgeEntries.some((e) => (e.actionHints?.verificationCommands?.length ?? 0) > 0);
|
|
235
250
|
dims.push({
|
|
@@ -54,6 +54,13 @@ export declare function planChangedPreflight(options: {
|
|
|
54
54
|
readonly projectRoot: string;
|
|
55
55
|
readonly changedFiles: ReadonlyArray<string>;
|
|
56
56
|
readonly profile?: PreflightProfile;
|
|
57
|
+
/** Override the test gate command (defaults to `bun test`). Grounded in the
|
|
58
|
+
* project's declared verification commands by the CLI caller. */
|
|
59
|
+
readonly testCommand?: string;
|
|
60
|
+
/** Override the typecheck gate command (defaults to the base-tsconfig run). */
|
|
61
|
+
readonly typecheckCommand?: string;
|
|
62
|
+
/** Extra path prefixes that count as engine source (e.g. config-declared). */
|
|
63
|
+
readonly sourceGlobs?: readonly string[];
|
|
57
64
|
}): IChangedPreflightPlan;
|
|
58
65
|
export declare function renderChangedPreflightText(plan: IChangedPreflightPlan): string;
|
|
59
66
|
//# sourceMappingURL=changed-preflight.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"changed-preflight.d.ts","sourceRoot":"","sources":["../src/changed-preflight.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"changed-preflight.d.ts","sourceRoot":"","sources":["../src/changed-preflight.ts"],"names":[],"mappings":"AAgBA,eAAO,MAAM,wBAAwB,oCAAoC,CAAC;AA6B1E,oBAAY,gBAAgB;IAC1B,KAAK,UAAU;IACf,QAAQ,aAAa;IACrB,MAAM,WAAW;CAClB;AAED,oBAAY,eAAe;IACzB,GAAG,QAAQ;IACX,IAAI,SAAS;IACb,SAAS,cAAc;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,eAAe,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,kFAAkF;IAClF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,IAAI,GAAG,WAAW,CAAC;IACpC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,OAAO,wBAAwB,CAAC;IACjD,QAAQ,CAAC,OAAO,EAAE,gBAAgB,CAAC;IACnC,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC7C,QAAQ,CAAC,eAAe,EAAE,2BAA2B,CAAC;IACtD,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;IAC9C,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC;AA+DD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAC5C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC7C,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAC;IACpC;sEACkE;IAClE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,+EAA+E;IAC/E,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,8EAA8E;IAC9E,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C,GAAG,qBAAqB,CAmRxB;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,qBAAqB,GAAG,MAAM,CAiB9E"}
|
|
@@ -11,8 +11,37 @@
|
|
|
11
11
|
* planner is intentionally conservative: a gate's `skip` decision is always
|
|
12
12
|
* accompanied by a reason so the operator can see what was elided.
|
|
13
13
|
*/
|
|
14
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
14
15
|
import * as nodePath from 'node:path';
|
|
15
16
|
export const CHANGED_PREFLIGHT_SCHEMA = 'sharkcraft.changed-preflight/v1';
|
|
17
|
+
/**
|
|
18
|
+
* Cheap, best-effort probe: does this look like a generic JS/TS monorepo
|
|
19
|
+
* (nx / pnpm workspaces / package.json workspaces / apps+libs)? Used to widen
|
|
20
|
+
* the "engine source changed" detection beyond SharkCraft's own
|
|
21
|
+
* `packages/<package-name>/src/` layout. A couple of stat calls — no full inspect.
|
|
22
|
+
*/
|
|
23
|
+
function isMonorepoLike(projectRoot) {
|
|
24
|
+
try {
|
|
25
|
+
if (existsSync(nodePath.join(projectRoot, 'nx.json')))
|
|
26
|
+
return true;
|
|
27
|
+
if (existsSync(nodePath.join(projectRoot, 'pnpm-workspace.yaml')))
|
|
28
|
+
return true;
|
|
29
|
+
if (existsSync(nodePath.join(projectRoot, 'apps')) ||
|
|
30
|
+
existsSync(nodePath.join(projectRoot, 'libs'))) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
const pkgPath = nodePath.join(projectRoot, 'package.json');
|
|
34
|
+
if (existsSync(pkgPath)) {
|
|
35
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
36
|
+
if (pkg.workspaces)
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Best-effort: treat any read/parse failure as "not obviously a monorepo".
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
16
45
|
export var PreflightProfile;
|
|
17
46
|
(function (PreflightProfile) {
|
|
18
47
|
PreflightProfile["Quick"] = "quick";
|
|
@@ -25,10 +54,23 @@ export var PreflightAction;
|
|
|
25
54
|
PreflightAction["Skip"] = "skip";
|
|
26
55
|
PreflightAction["Recommend"] = "recommend";
|
|
27
56
|
})(PreflightAction || (PreflightAction = {}));
|
|
28
|
-
function classifyChangedFiles(files) {
|
|
57
|
+
function classifyChangedFiles(files, opts = {}) {
|
|
29
58
|
const norm = files.map((f) => f.replace(/\\/g, '/'));
|
|
30
59
|
const match = (re) => norm.some((p) => re.test(p));
|
|
31
|
-
|
|
60
|
+
let anyEngineSource = match(/^packages\/[^/]+\/src\//);
|
|
61
|
+
// Monorepo-aware: on an nx / workspaces / apps+libs repo, source also lives
|
|
62
|
+
// under apps/*/src, libs/*/src, or a project-root src/ — not only
|
|
63
|
+
// SharkCraft's own packages/*/src/. Without this, a large nx change is
|
|
64
|
+
// misclassified as "no engine src changed" and every src-sensitive gate is
|
|
65
|
+
// skipped. Only widen when a monorepo signal is present, so this repo's own
|
|
66
|
+
// classification is unchanged.
|
|
67
|
+
if (!anyEngineSource && opts.projectRoot && isMonorepoLike(opts.projectRoot)) {
|
|
68
|
+
anyEngineSource = match(/^(apps|libs|packages)\/[^/]+\/(src|lib)\//) || match(/^src\//);
|
|
69
|
+
}
|
|
70
|
+
// Caller-supplied source roots (e.g. derived from config) always count.
|
|
71
|
+
if (!anyEngineSource && opts.sourceGlobs?.length) {
|
|
72
|
+
anyEngineSource = norm.some((p) => opts.sourceGlobs.some((g) => p.startsWith(g)));
|
|
73
|
+
}
|
|
32
74
|
const anyPackages = match(/^packages\//);
|
|
33
75
|
const anyTests = match(/__tests__\/|\.test\.ts$|e2e\//);
|
|
34
76
|
const anySharkcraftAssets = match(/^sharkcraft\//);
|
|
@@ -71,7 +113,12 @@ function classifyChangedFiles(files) {
|
|
|
71
113
|
*/
|
|
72
114
|
export function planChangedPreflight(options) {
|
|
73
115
|
const profile = options.profile ?? PreflightProfile.Standard;
|
|
74
|
-
const cls = classifyChangedFiles(options.changedFiles
|
|
116
|
+
const cls = classifyChangedFiles(options.changedFiles, {
|
|
117
|
+
projectRoot: options.projectRoot,
|
|
118
|
+
...(options.sourceGlobs ? { sourceGlobs: options.sourceGlobs } : {}),
|
|
119
|
+
});
|
|
120
|
+
const testCmd = options.testCommand ?? 'bun test';
|
|
121
|
+
const typecheckCmd = options.typecheckCommand ?? 'bun x tsc -p tsconfig.base.json --noEmit';
|
|
75
122
|
const explanations = [];
|
|
76
123
|
const gates = [];
|
|
77
124
|
// 1) Boundary check — needed when engine src changed.
|
|
@@ -249,7 +296,7 @@ export function planChangedPreflight(options) {
|
|
|
249
296
|
gates.push({
|
|
250
297
|
id: 'tests',
|
|
251
298
|
title: 'Bun test suite (focused subset where possible)',
|
|
252
|
-
command:
|
|
299
|
+
command: testCmd,
|
|
253
300
|
action: PreflightAction.Run,
|
|
254
301
|
reason: 'strict profile + engine src changed',
|
|
255
302
|
canFail: false,
|
|
@@ -259,7 +306,7 @@ export function planChangedPreflight(options) {
|
|
|
259
306
|
gates.push({
|
|
260
307
|
id: 'tests',
|
|
261
308
|
title: 'Bun test suite',
|
|
262
|
-
command:
|
|
309
|
+
command: testCmd,
|
|
263
310
|
action: PreflightAction.Recommend,
|
|
264
311
|
reason: 'engine src or tests changed; full suite not auto-run',
|
|
265
312
|
canFail: true,
|
|
@@ -269,7 +316,7 @@ export function planChangedPreflight(options) {
|
|
|
269
316
|
gates.push({
|
|
270
317
|
id: 'tests',
|
|
271
318
|
title: 'Bun test suite',
|
|
272
|
-
command:
|
|
319
|
+
command: testCmd,
|
|
273
320
|
action: PreflightAction.Skip,
|
|
274
321
|
reason: 'no engine src or test changes',
|
|
275
322
|
canFail: true,
|
|
@@ -281,7 +328,7 @@ export function planChangedPreflight(options) {
|
|
|
281
328
|
gates.push({
|
|
282
329
|
id: 'typecheck',
|
|
283
330
|
title: 'TypeScript --noEmit',
|
|
284
|
-
command:
|
|
331
|
+
command: typecheckCmd,
|
|
285
332
|
action: PreflightAction.Recommend,
|
|
286
333
|
reason: 'engine src changed; not auto-run on quick profile',
|
|
287
334
|
canFail: true,
|
|
@@ -291,7 +338,7 @@ export function planChangedPreflight(options) {
|
|
|
291
338
|
gates.push({
|
|
292
339
|
id: 'typecheck',
|
|
293
340
|
title: 'TypeScript --noEmit',
|
|
294
|
-
command:
|
|
341
|
+
command: typecheckCmd,
|
|
295
342
|
action: PreflightAction.Run,
|
|
296
343
|
reason: 'engine src changed',
|
|
297
344
|
canFail: false,
|
|
@@ -302,7 +349,7 @@ export function planChangedPreflight(options) {
|
|
|
302
349
|
gates.push({
|
|
303
350
|
id: 'typecheck',
|
|
304
351
|
title: 'TypeScript --noEmit',
|
|
305
|
-
command:
|
|
352
|
+
command: typecheckCmd,
|
|
306
353
|
action: PreflightAction.Skip,
|
|
307
354
|
reason: 'no engine src changed',
|
|
308
355
|
canFail: true,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface IGuardrailGlobResult {
|
|
2
|
+
/** Target paths covered by at least one guardrail glob. */
|
|
3
|
+
allowed: readonly string[];
|
|
4
|
+
/** Target paths covered by NO guardrail glob — these must not be written. */
|
|
5
|
+
refused: readonly string[];
|
|
6
|
+
/** True when every target path is allowed. */
|
|
7
|
+
ok: boolean;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Partition `targetPaths` into allowed / refused against the recipe's
|
|
11
|
+
* `guardrailGlobs` (allow-list). An empty glob list refuses everything — a
|
|
12
|
+
* recipe with no blast-radius fence must not write (the config validator also
|
|
13
|
+
* rejects this, but the check is defensive here too).
|
|
14
|
+
*/
|
|
15
|
+
export declare function checkGuardrailGlobs(targetPaths: readonly string[], guardrailGlobs: readonly string[]): IGuardrailGlobResult;
|
|
16
|
+
//# sourceMappingURL=check-guardrail-globs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-guardrail-globs.d.ts","sourceRoot":"","sources":["../src/check-guardrail-globs.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,6EAA6E;IAC7E,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,8CAA8C;IAC9C,EAAE,EAAE,OAAO,CAAC;CACb;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,cAAc,EAAE,SAAS,MAAM,EAAE,GAChC,oBAAoB,CAWtB"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guardrail-glob enforcement for the delegate worker.
|
|
3
|
+
*
|
|
4
|
+
* A recipe declares an ALLOW-LIST of globs (`guardrailGlobs`). A worker-emitted
|
|
5
|
+
* target path that matches NONE of them is refused before any write — this is
|
|
6
|
+
* one of the deterministic fences the model cannot influence, layered ON TOP of
|
|
7
|
+
* the generator's `safeResolveTargetPath` floor.
|
|
8
|
+
*
|
|
9
|
+
* CRITICAL: the caller MUST pass the NORMALIZED relative path that will actually
|
|
10
|
+
* be written (the output of `safeResolveTargetPath`), not the raw string a
|
|
11
|
+
* worker emitted — otherwise a `../` traversal whose `**` the glob swallows
|
|
12
|
+
* would pass the fence yet write elsewhere in-root. Matching here is
|
|
13
|
+
* CASE-SENSITIVE (file paths are case-significant on the platforms that matter),
|
|
14
|
+
* so `src/**` does not also grant `SRC/…`.
|
|
15
|
+
*
|
|
16
|
+
* Pure: deterministic glob match; no I/O, no model.
|
|
17
|
+
*/
|
|
18
|
+
import { globToRegex, toPosix } from "./contract-file-rule.js";
|
|
19
|
+
/**
|
|
20
|
+
* Partition `targetPaths` into allowed / refused against the recipe's
|
|
21
|
+
* `guardrailGlobs` (allow-list). An empty glob list refuses everything — a
|
|
22
|
+
* recipe with no blast-radius fence must not write (the config validator also
|
|
23
|
+
* rejects this, but the check is defensive here too).
|
|
24
|
+
*/
|
|
25
|
+
export function checkGuardrailGlobs(targetPaths, guardrailGlobs) {
|
|
26
|
+
const matchers = guardrailGlobs.map((g) => globToRegex(toPosix(g)));
|
|
27
|
+
const allowed = [];
|
|
28
|
+
const refused = [];
|
|
29
|
+
for (const path of targetPaths) {
|
|
30
|
+
const f = toPosix(path);
|
|
31
|
+
const covered = matchers.some((re) => re.test(f));
|
|
32
|
+
if (covered)
|
|
33
|
+
allowed.push(path);
|
|
34
|
+
else
|
|
35
|
+
refused.push(path);
|
|
36
|
+
}
|
|
37
|
+
return { allowed, refused, ok: refused.length === 0 };
|
|
38
|
+
}
|
|
@@ -56,7 +56,7 @@ export function buildCodeIntelligenceChecks(projectRoot, options = {}) {
|
|
|
56
56
|
checks.push(...graphChecks(projectRoot, nowMs, staleDays));
|
|
57
57
|
checks.push(...ruleGraphChecks(projectRoot, nowMs, staleDays));
|
|
58
58
|
checks.push(...apiSurfaceChecks(projectRoot, nowMs, staleDays));
|
|
59
|
-
checks.push(...qualityGateChecks(projectRoot, nowMs));
|
|
59
|
+
checks.push(...qualityGateChecks(projectRoot, nowMs, staleDays));
|
|
60
60
|
checks.push(...migrationChecks(projectRoot));
|
|
61
61
|
checks.push(...architectureChecks(projectRoot, nowMs, staleDays));
|
|
62
62
|
checks.push(...impactRunChecks(projectRoot, nowMs, staleDays));
|
|
@@ -319,7 +319,7 @@ function apiSurfaceChecks(projectRoot, nowMs, staleDays) {
|
|
|
319
319
|
},
|
|
320
320
|
];
|
|
321
321
|
}
|
|
322
|
-
function qualityGateChecks(projectRoot, nowMs) {
|
|
322
|
+
function qualityGateChecks(projectRoot, nowMs, staleDays) {
|
|
323
323
|
const reportPath = nodePath.join(projectRoot, '.sharkcraft', 'quality-gates', 'last.json');
|
|
324
324
|
if (!existsSync(reportPath)) {
|
|
325
325
|
return [];
|
|
@@ -338,6 +338,7 @@ function qualityGateChecks(projectRoot, nowMs) {
|
|
|
338
338
|
}
|
|
339
339
|
const days = ageDays(report.startedAt, nowMs);
|
|
340
340
|
const ageStr = days !== undefined ? ` ${fmtAge(days)}` : '';
|
|
341
|
+
const stale = days !== undefined && days > staleDays;
|
|
341
342
|
const status = report.overall ?? 'unknown';
|
|
342
343
|
const failingGates = (report.gates ?? [])
|
|
343
344
|
.filter((g) => g.status === 'fail')
|
|
@@ -354,15 +355,34 @@ function qualityGateChecks(projectRoot, nowMs) {
|
|
|
354
355
|
];
|
|
355
356
|
}
|
|
356
357
|
if (status === 'fail') {
|
|
358
|
+
const failMsg = failingGates.length > 0
|
|
359
|
+
? `Last gate FAIL${ageStr} — ${failingGates.join(', ')}.`
|
|
360
|
+
: `Last gate FAIL${ageStr}.`;
|
|
361
|
+
// An old FAIL is stale maintenance, not a verified current regression:
|
|
362
|
+
// age it out into a folded advisory that nudges a re-run instead of a
|
|
363
|
+
// hard Warning that masks the (now-unknown) state of the tree. A fresh
|
|
364
|
+
// FAIL stays loud. Mirrors the stale handling in apiSurfaceChecks /
|
|
365
|
+
// architectureChecks so the whole code-intelligence section is consistent.
|
|
366
|
+
if (stale) {
|
|
367
|
+
return [
|
|
368
|
+
{
|
|
369
|
+
id: 'code-intelligence-quality-gate',
|
|
370
|
+
title: 'Quality gate (last run)',
|
|
371
|
+
severity: DoctorSeverity.Info,
|
|
372
|
+
advisory: true,
|
|
373
|
+
category: CATEGORY,
|
|
374
|
+
message: `${failMsg} Stale (>${staleDays}d) — may not reflect the current tree.`,
|
|
375
|
+
fix: 'Re-run `shrk gate` to refresh.',
|
|
376
|
+
},
|
|
377
|
+
];
|
|
378
|
+
}
|
|
357
379
|
return [
|
|
358
380
|
{
|
|
359
381
|
id: 'code-intelligence-quality-gate',
|
|
360
382
|
title: 'Quality gate (last run)',
|
|
361
383
|
severity: DoctorSeverity.Warning,
|
|
362
384
|
category: CATEGORY,
|
|
363
|
-
message:
|
|
364
|
-
? `Last gate FAIL${ageStr} — ${failingGates.join(', ')}.`
|
|
365
|
-
: `Last gate FAIL${ageStr}.`,
|
|
385
|
+
message: failMsg,
|
|
366
386
|
fix: 'Re-run with `shrk gate` and address the failing gate(s).',
|
|
367
387
|
whyThisMatters: 'The quality gate is the one-shot pass/fail the dashboard and CI agents trust — leaving it red hides real regressions.',
|
|
368
388
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-recommender.d.ts","sourceRoot":"","sources":["../src/command-recommender.ts"],"names":[],"mappings":"AASA,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAEvE,eAAO,MAAM,0BAA0B,sCAAsC,CAAC;AAE9E,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,WAAW,GAAG,eAAe,GAAG,gBAAgB,GAAG,eAAe,GAAG,YAAY,CAAC;IAC/F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,OAAO,0BAA0B,CAAC;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,SAAS,sBAAsB,EAAE,CAAC;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,iEAAiE;IACjE,WAAW,EAAE,kBAAkB,CAAC;CACjC;
|
|
1
|
+
{"version":3,"file":"command-recommender.d.ts","sourceRoot":"","sources":["../src/command-recommender.ts"],"names":[],"mappings":"AASA,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAEvE,eAAO,MAAM,0BAA0B,sCAAsC,CAAC;AAE9E,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,WAAW,GAAG,eAAe,GAAG,gBAAgB,GAAG,eAAe,GAAG,YAAY,CAAC;IAC/F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,OAAO,0BAA0B,CAAC;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,eAAe,EAAE,SAAS,sBAAsB,EAAE,CAAC;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,iEAAiE;IACjE,WAAW,EAAE,kBAAkB,CAAC;CACjC;AAgFD,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,qBAAqB,EACjC,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,GAClD,OAAO,CAAC,4BAA4B,CAAC,CAoFvC"}
|
|
@@ -51,18 +51,30 @@ const RECIPES = [
|
|
|
51
51
|
],
|
|
52
52
|
},
|
|
53
53
|
{
|
|
54
|
-
match: /code[-\s]?intel|code intelligence|code graph|graph status|graph health|import cycle|unresolved import|blast radius|callers|dependents/i,
|
|
54
|
+
match: /code[-\s]?intel|code intelligence|code graph|graph status|graph health|import cycle|unresolved import|blast radius|callers|dependents|who calls|who uses|where is .*\bused|find usages|usages? of|is .*\bwired|wired (up|to)|wire[ds]? .*\bto\b|path (from|between)|reach(es|able)|connected to|who implements|implementations? of|subclass|subtype|load[-\s]?bearing|\bhubs?\b|most[-\s](depended|imported|referenced)|what.*change carefully|important.*\b(code|files?|symbols?)|what breaks if|what calls|call sites?|trace .*\b(symbol|function|usage)/i,
|
|
55
55
|
recommendations: [
|
|
56
|
+
{ command: 'shrk graph callers <symbol>', why: 'Who calls / references a symbol, as path:line — the grep replacement for "who calls X / where is X used".' },
|
|
57
|
+
{ command: 'shrk graph path <from> <to>', why: 'Is code A actually wired to code B? Shortest import/call/implements path between two files or symbols — the deterministic answer to "is X wired to Y".' },
|
|
58
|
+
{ command: 'shrk graph hubs', why: 'The most-depended-on symbols/files (biggest blast radius) — what to change carefully or understand first when onboarding.' },
|
|
59
|
+
{ command: 'shrk graph context <file-or-symbol>', why: 'Inspect one file or symbol with imports, callers, subtypes/supertypes, bridge context, and framework hits — answers "is X wired".' },
|
|
60
|
+
{ command: 'shrk graph impact <file-or-symbol> --full', why: 'What breaks if you change it: graph-backed dependents, caller files, rules, and likely tests.' },
|
|
56
61
|
{ command: 'shrk code-intel', why: 'One-shot health view across the code graph, bridge, and quality gates.' },
|
|
57
62
|
{ command: 'shrk graph status', why: 'Check whether the code graph is present, fresh, and internally consistent.' },
|
|
58
|
-
{ command: 'shrk graph cycles', why: 'List import cycles that inflate blast-radius calculations.' },
|
|
59
63
|
{ command: 'shrk graph unresolved', why: 'Find unresolved imports that undercut graph accuracy.' },
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
match: /delegate|mechanical (edit|task|change|refactor)|grunt (work|task)|boilerplate|repetitive edit|hand (this|it|off) (off |over )?to (a |the )?(local |worker|model)|add (a |an )?(barrel )?(export|import)\b|local (llm|model) (do|handle|make)/i,
|
|
68
|
+
recommendations: [
|
|
69
|
+
{ command: 'shrk delegate list', why: 'See the MECHANICAL task recipes a local-LLM worker can handle (the engine verifies the result + auto-reverts on failure). Each is fenced to specific files + op kinds.' },
|
|
70
|
+
{ command: 'shrk delegate run "<task>" --recipe <id> --apply', why: 'Hand a mechanical, deterministically-verifiable edit to the LOCAL worker — you pay for a compact brief + result instead of reading the whole file and writing the edit. The edit lands only if it passes the recipe verification.' },
|
|
71
|
+
{ command: 'shrk delegate explain <id>', why: 'Audit a recipe before trusting it: the allowed ops, guardrail globs, and whether its verification is bound.' },
|
|
62
72
|
],
|
|
63
73
|
},
|
|
64
74
|
];
|
|
65
75
|
function safetyLevelFor(command) {
|
|
76
|
+
if (/^shrk delegate run/i.test(command))
|
|
77
|
+
return 'writes-source';
|
|
66
78
|
if (/^shrk (gen|init|apply|import|presets apply --write|packs (sign|new))/i.test(command))
|
|
67
79
|
return 'writes-source';
|
|
68
80
|
if (/^shrk (onboard|brief|dev start|handoff|export|report site|impact|ci scaffold|simulate|orchestrate)/i.test(command))
|
|
@@ -19,6 +19,14 @@ export interface IContractFileRule {
|
|
|
19
19
|
reason?: string;
|
|
20
20
|
severity?: ContractFileRuleSeverity;
|
|
21
21
|
}
|
|
22
|
+
export declare function toPosix(p: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Translate a deterministic POSIX-style glob to a regex anchored at both ends.
|
|
25
|
+
* Case-sensitive by itself — `matchContractFileRule` lowercases its inputs for
|
|
26
|
+
* the contract use-case, but security-sensitive callers (the delegate guardrail)
|
|
27
|
+
* use this directly to keep file-path case significant.
|
|
28
|
+
*/
|
|
29
|
+
export declare function globToRegex(pattern: string): RegExp;
|
|
22
30
|
/** Pure matcher — returns true when `file` is covered by `rule`. */
|
|
23
31
|
export declare function matchContractFileRule(rule: IContractFileRule, file: string): boolean;
|
|
24
32
|
export interface IContractFileRuleMatch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract-file-rule.d.ts","sourceRoot":"","sources":["../src/contract-file-rule.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,oBAAY,oBAAoB;IAC9B,IAAI,SAAS;IACb,UAAU,gBAAgB;IAC1B,KAAK,UAAU;IACf,QAAQ,aAAa;CACtB;AAED,oBAAY,wBAAwB;IAClC,KAAK,UAAU;IACf,OAAO,YAAY;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,oBAAoB,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,wBAAwB,CAAC;CACrC;
|
|
1
|
+
{"version":3,"file":"contract-file-rule.d.ts","sourceRoot":"","sources":["../src/contract-file-rule.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,oBAAY,oBAAoB;IAC9B,IAAI,SAAS;IACb,UAAU,gBAAgB;IAC1B,KAAK,UAAU;IACf,QAAQ,aAAa;CACtB;AAED,oBAAY,wBAAwB;IAClC,KAAK,UAAU;IACf,OAAO,YAAY;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,oBAAoB,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,wBAAwB,CAAC;CACrC;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzC;AAMD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CA4BnD;AAED,oEAAoE;AACpE,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAapF;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,iBAAiB,CAAC;CACzB;AAED,sEAAsE;AACtE,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,SAAS,iBAAiB,EAAE,EACnC,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,SAAS,sBAAsB,EAAE,CAQnC;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,SAAS,MAAM,EAAE,GAC1B,SAAS,iBAAiB,EAAE,CAE9B;AAED,6DAA6D;AAC7D,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAQxE"}
|
|
@@ -15,14 +15,19 @@ export var ContractFileRuleSeverity;
|
|
|
15
15
|
ContractFileRuleSeverity["Error"] = "error";
|
|
16
16
|
ContractFileRuleSeverity["Warning"] = "warning";
|
|
17
17
|
})(ContractFileRuleSeverity || (ContractFileRuleSeverity = {}));
|
|
18
|
-
function toPosix(p) {
|
|
18
|
+
export function toPosix(p) {
|
|
19
19
|
return p.replace(/\\/g, '/').replace(/^\.\//, '');
|
|
20
20
|
}
|
|
21
21
|
function escapeRegex(s) {
|
|
22
22
|
return s.replace(/[.+^$(){}|\\[\]]/g, '\\$&');
|
|
23
23
|
}
|
|
24
|
-
/**
|
|
25
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Translate a deterministic POSIX-style glob to a regex anchored at both ends.
|
|
26
|
+
* Case-sensitive by itself — `matchContractFileRule` lowercases its inputs for
|
|
27
|
+
* the contract use-case, but security-sensitive callers (the delegate guardrail)
|
|
28
|
+
* use this directly to keep file-path case significant.
|
|
29
|
+
*/
|
|
30
|
+
export function globToRegex(pattern) {
|
|
26
31
|
const p = toPosix(pattern);
|
|
27
32
|
let re = '';
|
|
28
33
|
let i = 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coverage-report.d.ts","sourceRoot":"","sources":["../src/coverage-report.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAGvE,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAOD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,qBAAqB,GAAG,eAAe,
|
|
1
|
+
{"version":3,"file":"coverage-report.d.ts","sourceRoot":"","sources":["../src/coverage-report.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAGvE,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,yBAAyB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,6BAA6B;IAC7B,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAOD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,qBAAqB,GAAG,eAAe,CA6LtF"}
|
package/dist/coverage-report.js
CHANGED
|
@@ -181,7 +181,20 @@ export function buildCoverageReport(inspection) {
|
|
|
181
181
|
missing,
|
|
182
182
|
});
|
|
183
183
|
}
|
|
184
|
-
|
|
184
|
+
// `overall` measures how well the USER has grounded THIS repo, so two kinds
|
|
185
|
+
// of category must not inflate it:
|
|
186
|
+
// 1. Empty categories (total === 0) — they vacuously score 100 (see `pct`),
|
|
187
|
+
// so a repo that just hasn't configured boundaries/packs would read as
|
|
188
|
+
// perfectly grounded and tell the agent to skip adding rules.
|
|
189
|
+
// 2. `preset-references` — it scores the BUILT-IN preset registry's
|
|
190
|
+
// validity (≈72/72 always), not the user's configuration, so on an
|
|
191
|
+
// otherwise-empty repo it alone would peg the overall at 100.
|
|
192
|
+
// Both stay as displayed categories; they're just out of the average.
|
|
193
|
+
// 0 when nothing user-authored is configured (honest: no coverage to report).
|
|
194
|
+
const scored = categories.filter((c) => c.total > 0 && c.id !== 'preset-references');
|
|
195
|
+
const overall = scored.length > 0
|
|
196
|
+
? Math.round(scored.reduce((s, c) => s + c.score, 0) / scored.length)
|
|
197
|
+
: 0;
|
|
185
198
|
if (overall < 60)
|
|
186
199
|
suggestions.push('Pair every critical rule with action hints + verification commands.');
|
|
187
200
|
if (overall < 80)
|
|
@@ -0,0 +1,45 @@
|
|
|
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 type { IDelegateRecipe, ISharkCraftConfig } from '@shrkcrft/config';
|
|
12
|
+
export interface IResolvedDelegateRecipe extends IDelegateRecipe {
|
|
13
|
+
/** Provider after applying the delegation-level default (never undefined). */
|
|
14
|
+
resolvedProvider: 'auto' | 'ollama' | 'llamacpp';
|
|
15
|
+
/** Model after applying the delegation-level default (undefined = provider default). */
|
|
16
|
+
resolvedModel?: string;
|
|
17
|
+
/** verificationIds that do NOT resolve to a `verificationCommands[].id`. */
|
|
18
|
+
unboundVerificationIds: readonly string[];
|
|
19
|
+
/** True when every verificationId resolves AND at least one is declared. */
|
|
20
|
+
verificationBound: boolean;
|
|
21
|
+
/** True when the recipe is safe to delegate (verification fully bound). */
|
|
22
|
+
delegatable: boolean;
|
|
23
|
+
/** Where the recipe came from. */
|
|
24
|
+
source: 'config' | 'pack';
|
|
25
|
+
/** Contributing pack, when `source === 'pack'`. */
|
|
26
|
+
packageName?: string;
|
|
27
|
+
}
|
|
28
|
+
/** A pack-contributed recipe (shape from `loadDelegateRecipesFromPacks`). */
|
|
29
|
+
export interface IPackRecipeInput {
|
|
30
|
+
recipe: IDelegateRecipe;
|
|
31
|
+
packageName: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Resolve the delegate catalog from config + pack-contributed recipes.
|
|
35
|
+
*
|
|
36
|
+
* Merge order: pack recipes first, then INLINE config recipes override a pack
|
|
37
|
+
* recipe of the same id. `recipeOverrides` (by id) then patch model /
|
|
38
|
+
* verificationIds / guardrailGlobs, or drop the recipe (`enabled: false`).
|
|
39
|
+
* Finally each recipe's `verificationIds` are checked against
|
|
40
|
+
* `verificationCommands[]` — `delegatable` only when all are bound.
|
|
41
|
+
*/
|
|
42
|
+
export declare function resolveDelegateCatalog(config: ISharkCraftConfig, packRecipes?: readonly IPackRecipeInput[]): readonly IResolvedDelegateRecipe[];
|
|
43
|
+
/** Look up one resolved recipe by id (config recipes only; no pack loading). */
|
|
44
|
+
export declare function findDelegateRecipe(config: ISharkCraftConfig, recipeId: string): IResolvedDelegateRecipe | undefined;
|
|
45
|
+
//# sourceMappingURL=delegate-catalog.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the delegate catalog from config + pack-contributed recipes.
|
|
3
|
+
*
|
|
4
|
+
* Merge order: pack recipes first, then INLINE config recipes override a pack
|
|
5
|
+
* recipe of the same id. `recipeOverrides` (by id) then patch model /
|
|
6
|
+
* verificationIds / guardrailGlobs, or drop the recipe (`enabled: false`).
|
|
7
|
+
* Finally each recipe's `verificationIds` are checked against
|
|
8
|
+
* `verificationCommands[]` — `delegatable` only when all are bound.
|
|
9
|
+
*/
|
|
10
|
+
export function resolveDelegateCatalog(config, packRecipes = []) {
|
|
11
|
+
const delegation = config.delegation;
|
|
12
|
+
if (!delegation)
|
|
13
|
+
return [];
|
|
14
|
+
const known = new Set((config.verificationCommands ?? []).map((v) => v.id));
|
|
15
|
+
const overrides = delegation.recipeOverrides ?? {};
|
|
16
|
+
const byId = new Map();
|
|
17
|
+
for (const pr of packRecipes)
|
|
18
|
+
byId.set(pr.recipe.id, { recipe: pr.recipe, source: 'pack', packageName: pr.packageName });
|
|
19
|
+
for (const recipe of delegation.recipes ?? [])
|
|
20
|
+
byId.set(recipe.id, { recipe, source: 'config' });
|
|
21
|
+
const out = [];
|
|
22
|
+
for (const { recipe, source, packageName } of byId.values()) {
|
|
23
|
+
const ov = overrides[recipe.id];
|
|
24
|
+
if (ov?.enabled === false)
|
|
25
|
+
continue; // dropped by override
|
|
26
|
+
const merged = {
|
|
27
|
+
...recipe,
|
|
28
|
+
...(ov?.model !== undefined ? { model: ov.model } : {}),
|
|
29
|
+
...(ov?.verificationIds !== undefined ? { verificationIds: ov.verificationIds } : {}),
|
|
30
|
+
...(ov?.guardrailGlobs !== undefined ? { guardrailGlobs: ov.guardrailGlobs } : {}),
|
|
31
|
+
};
|
|
32
|
+
const unbound = (merged.verificationIds ?? []).filter((id) => !known.has(id));
|
|
33
|
+
const verificationBound = unbound.length === 0 && (merged.verificationIds ?? []).length > 0;
|
|
34
|
+
out.push({
|
|
35
|
+
...merged,
|
|
36
|
+
resolvedProvider: merged.provider ?? delegation.provider ?? 'auto',
|
|
37
|
+
...(merged.model ?? delegation.model ? { resolvedModel: merged.model ?? delegation.model } : {}),
|
|
38
|
+
unboundVerificationIds: unbound,
|
|
39
|
+
verificationBound,
|
|
40
|
+
delegatable: verificationBound,
|
|
41
|
+
source,
|
|
42
|
+
...(packageName ? { packageName } : {}),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
/** Look up one resolved recipe by id (config recipes only; no pack loading). */
|
|
48
|
+
export function findDelegateRecipe(config, recipeId) {
|
|
49
|
+
return resolveDelegateCatalog(config).find((r) => r.id === recipeId);
|
|
50
|
+
}
|
|
@@ -0,0 +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 type { ISharkCraftConfig } from '@shrkcrft/config';
|
|
13
|
+
import { type IDoctorCheck } from './doctor-result.js';
|
|
14
|
+
export declare function buildDelegateRecipeChecks(config: ISharkCraftConfig | null): IDoctorCheck[];
|
|
15
|
+
//# sourceMappingURL=delegate-doctor.d.ts.map
|
|
@@ -0,0 +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"}
|