@shrkcrft/cli 0.1.0-alpha.21 → 0.1.0-alpha.23
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/command-registry.d.ts +9 -0
- package/dist/command-registry.d.ts.map +1 -1
- package/dist/command-registry.js +15 -0
- package/dist/commands/ai-status.command.d.ts.map +1 -1
- package/dist/commands/ai-status.command.js +10 -0
- package/dist/commands/apply.command.d.ts.map +1 -1
- package/dist/commands/apply.command.js +9 -2
- package/dist/commands/ask.command.d.ts.map +1 -1
- package/dist/commands/ask.command.js +2 -1
- package/dist/commands/changes.command.d.ts.map +1 -1
- package/dist/commands/changes.command.js +14 -4
- package/dist/commands/check.command.d.ts.map +1 -1
- package/dist/commands/check.command.js +119 -3
- package/dist/commands/command-catalog.d.ts.map +1 -1
- package/dist/commands/command-catalog.js +224 -0
- package/dist/commands/commands.command.d.ts +15 -0
- package/dist/commands/commands.command.d.ts.map +1 -1
- package/dist/commands/commands.command.js +162 -3
- package/dist/commands/compress.command.d.ts.map +1 -1
- package/dist/commands/compress.command.js +39 -4
- package/dist/commands/constructs.command.d.ts.map +1 -1
- package/dist/commands/constructs.command.js +40 -9
- package/dist/commands/context.command.d.ts.map +1 -1
- package/dist/commands/context.command.js +3 -1
- package/dist/commands/daily.commands.d.ts.map +1 -1
- package/dist/commands/daily.commands.js +48 -5
- package/dist/commands/diff-check.command.d.ts.map +1 -1
- package/dist/commands/diff-check.command.js +6 -2
- package/dist/commands/feedback.command.d.ts.map +1 -1
- package/dist/commands/feedback.command.js +4 -0
- package/dist/commands/framework.command.d.ts.map +1 -1
- package/dist/commands/framework.command.js +20 -3
- package/dist/commands/gate.command.d.ts.map +1 -1
- package/dist/commands/gate.command.js +99 -2
- package/dist/commands/graph-code-subverbs.d.ts.map +1 -1
- package/dist/commands/graph-code-subverbs.js +183 -68
- package/dist/commands/graph.command.d.ts.map +1 -1
- package/dist/commands/graph.command.js +6 -0
- package/dist/commands/impact.command.d.ts.map +1 -1
- package/dist/commands/impact.command.js +91 -1
- package/dist/commands/knowledge.command.d.ts.map +1 -1
- package/dist/commands/knowledge.command.js +21 -1
- package/dist/commands/onboard.command.d.ts.map +1 -1
- package/dist/commands/onboard.command.js +11 -2
- package/dist/commands/orchestrate.command.d.ts.map +1 -1
- package/dist/commands/orchestrate.command.js +6 -1
- package/dist/commands/pack-author.command.d.ts.map +1 -1
- package/dist/commands/pack-author.command.js +13 -1
- package/dist/commands/packs.command.d.ts.map +1 -1
- package/dist/commands/packs.command.js +95 -17
- package/dist/commands/paths.command.d.ts.map +1 -1
- package/dist/commands/paths.command.js +8 -1
- package/dist/commands/pipelines.command.d.ts.map +1 -1
- package/dist/commands/pipelines.command.js +2 -1
- package/dist/commands/plan-context.command.d.ts.map +1 -1
- package/dist/commands/plan-context.command.js +6 -3
- package/dist/commands/plan-simulate.command.js +1 -1
- package/dist/commands/policy-lint.command.d.ts +3 -0
- package/dist/commands/policy-lint.command.d.ts.map +1 -0
- package/dist/commands/policy-lint.command.js +135 -0
- package/dist/commands/recommend.command.d.ts.map +1 -1
- package/dist/commands/recommend.command.js +10 -2
- package/dist/commands/registry.command.d.ts.map +1 -1
- package/dist/commands/registry.command.js +117 -9
- package/dist/commands/report.command.js +1 -1
- package/dist/commands/reuse.command.d.ts +3 -0
- package/dist/commands/reuse.command.d.ts.map +1 -0
- package/dist/commands/reuse.command.js +202 -0
- package/dist/commands/review.command.d.ts.map +1 -1
- package/dist/commands/review.command.js +20 -2
- package/dist/commands/rule-graph-subverbs.d.ts.map +1 -1
- package/dist/commands/rule-graph-subverbs.js +31 -4
- package/dist/commands/search-structural.command.d.ts.map +1 -1
- package/dist/commands/search-structural.command.js +2 -2
- package/dist/commands/search.command.d.ts.map +1 -1
- package/dist/commands/search.command.js +19 -1
- package/dist/commands/simulate.command.d.ts.map +1 -1
- package/dist/commands/simulate.command.js +6 -1
- package/dist/commands/smart-context.command.d.ts.map +1 -1
- package/dist/commands/smart-context.command.js +87 -4
- package/dist/commands/spec.command.d.ts.map +1 -1
- package/dist/commands/spec.command.js +73 -13
- package/dist/commands/task-context.command.d.ts.map +1 -1
- package/dist/commands/task-context.command.js +26 -2
- package/dist/commands/test.command.d.ts.map +1 -1
- package/dist/commands/test.command.js +17 -0
- package/dist/commands/trace.command.d.ts.map +1 -1
- package/dist/commands/trace.command.js +14 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +19 -1
- package/dist/status/freshness.d.ts +30 -0
- package/dist/status/freshness.d.ts.map +1 -0
- package/dist/status/freshness.js +81 -0
- package/package.json +33 -33
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search-structural.command.d.ts","sourceRoot":"","sources":["../../src/commands/search-structural.command.ts"],"names":[],"mappings":"AAgBA,OAAO,
|
|
1
|
+
{"version":3,"file":"search-structural.command.d.ts","sourceRoot":"","sources":["../../src/commands/search-structural.command.ts"],"names":[],"mappings":"AAgBA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uBAAuB,EAAE,eAkErC,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import * as nodePath from 'node:path';
|
|
3
3
|
import { applyRewritePlan, PatternRegistryStore, planRewrite, runSearch, signRewritePlan, STARTER_PATTERNS, verifySignedRewritePlan, } from '@shrkcrft/structural-search';
|
|
4
|
-
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
4
|
+
import { flagBool, flagPositiveInt, flagString, resolveCwd, } from "../command-registry.js";
|
|
5
5
|
import { asJson, header, kv } from "../output/format-output.js";
|
|
6
6
|
/**
|
|
7
7
|
* `shrk search structural` — declarative AST pattern matching with
|
|
@@ -49,7 +49,7 @@ export const searchStructuralCommand = {
|
|
|
49
49
|
process.stderr.write(`Pattern parse error: ${e.message}\n`);
|
|
50
50
|
return 2;
|
|
51
51
|
}
|
|
52
|
-
const limit =
|
|
52
|
+
const limit = flagPositiveInt(args, 'limit', 200);
|
|
53
53
|
// Rewrite path?
|
|
54
54
|
const recipeInline = flagString(args, 'rewrite');
|
|
55
55
|
const recipeFile = flagString(args, 'rewrite-file');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"search.command.d.ts","sourceRoot":"","sources":["../../src/commands/search.command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"search.command.d.ts","sourceRoot":"","sources":["../../src/commands/search.command.ts"],"names":[],"mappings":"AAiBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAkChC,eAAO,MAAM,aAAa,EAAE,eAsH3B,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,eA+FrC,CAAC;AAwEF;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,EAAE,eASzC,CAAC"}
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
import { buildSearchIndex, entrypointBanner, explainSearchTuning, inspectSharkcraft, loadConstructs, loadPlaybooks, loadSearchTuning, renderSearchText, renderTuningExplainHtml, renderTuningExplainMarkdown, searchIndex, SearchKind, SearchSource, } from '@shrkcrft/inspector';
|
|
1
|
+
import { buildSearchIndex, entrypointBanner, explainSearchTuning, inspectSharkcraft, loadConstructs, loadPlaybooks, loadSearchTuning, renderSearchText, renderTuningExplainHtml, renderTuningExplainMarkdown, searchIndex, SearchKind, SearchResultKind, SearchSource, } from '@shrkcrft/inspector';
|
|
2
2
|
import { flagBool, flagNumber, flagString, flagList, resolveCwd, } from "../command-registry.js";
|
|
3
3
|
import { asJson } from "../output/format-output.js";
|
|
4
4
|
const VALID_KINDS = new Set(Object.values(SearchKind));
|
|
5
5
|
const VALID_SOURCES = new Set(Object.values(SearchSource));
|
|
6
|
+
// Universal-search (v2) filter vocabularies. `--kind` is one of the
|
|
7
|
+
// `SearchResultKind` enum values; `--source` is the local/pack origin the
|
|
8
|
+
// engine actually filters hits by (`makeHit` only ever emits these two). Both
|
|
9
|
+
// are validated up front so a typo'd value rejects loudly instead of silently
|
|
10
|
+
// filtering every hit out and returning an empty result with exit 0.
|
|
11
|
+
const VALID_RESULT_KINDS = Object.values(SearchResultKind);
|
|
12
|
+
const VALID_RESULT_SOURCES = ['local', 'pack'];
|
|
6
13
|
function parseKinds(args) {
|
|
7
14
|
const list = flagList(args, 'type').concat(flagList(args, 'kind'));
|
|
8
15
|
if (list.length === 0)
|
|
@@ -48,6 +55,17 @@ export const searchCommand = {
|
|
|
48
55
|
const { buildUniversalSearch, renderUniversalSearchText, renderUniversalSearchMarkdown, } = await import('@shrkcrft/inspector');
|
|
49
56
|
const kindFilter = flagString(args, 'kind');
|
|
50
57
|
const sourceFilter = flagString(args, 'source');
|
|
58
|
+
// Validate the filter vocabularies BEFORE building opts — universal-search
|
|
59
|
+
// silently drops every non-matching hit, so an unknown `--kind`/`--source`
|
|
60
|
+
// would otherwise return an empty result with exit 0.
|
|
61
|
+
if (kindFilter && !VALID_RESULT_KINDS.includes(kindFilter)) {
|
|
62
|
+
process.stderr.write(`Unknown --kind ${kindFilter}. Valid: ${VALID_RESULT_KINDS.join(', ')}\n`);
|
|
63
|
+
return 2;
|
|
64
|
+
}
|
|
65
|
+
if (sourceFilter && !VALID_RESULT_SOURCES.includes(sourceFilter)) {
|
|
66
|
+
process.stderr.write(`Unknown --source ${sourceFilter}. Valid: ${VALID_RESULT_SOURCES.join(', ')}\n`);
|
|
67
|
+
return 2;
|
|
68
|
+
}
|
|
51
69
|
const limit = flagNumber(args, 'limit');
|
|
52
70
|
const opts = {};
|
|
53
71
|
if (kindFilter)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simulate.command.d.ts","sourceRoot":"","sources":["../../src/commands/simulate.command.ts"],"names":[],"mappings":"AAQA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"simulate.command.d.ts","sourceRoot":"","sources":["../../src/commands/simulate.command.ts"],"names":[],"mappings":"AAQA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAKhC,eAAO,MAAM,eAAe,EAAE,eAyD7B,CAAC"}
|
|
@@ -3,10 +3,11 @@ import * as nodePath from 'node:path';
|
|
|
3
3
|
import { inspectSharkcraft, OrchestrationMode, renderWorkflowSimulationText, simulateWorkflow, } from '@shrkcrft/inspector';
|
|
4
4
|
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
5
5
|
import { asJson } from "../output/format-output.js";
|
|
6
|
+
const VALID_SIMULATION_MODES = ['conservative', 'balanced', 'aggressive'];
|
|
6
7
|
export const simulateCommand = {
|
|
7
8
|
name: 'simulate',
|
|
8
9
|
description: 'Predict what a workflow would do without executing anything. Read-only. Optionally takes a playbook or pipeline id.',
|
|
9
|
-
usage: 'shrk simulate "<task>" [--playbook <id>] [--pipeline <id>] [--
|
|
10
|
+
usage: 'shrk simulate "<task>" [--playbook <id>] [--pipeline <id>] [--mode conservative|balanced|aggressive] [--json] [--output <file>]',
|
|
10
11
|
async run(args) {
|
|
11
12
|
const task = args.positional.join(' ').trim();
|
|
12
13
|
if (!task) {
|
|
@@ -18,6 +19,10 @@ export const simulateCommand = {
|
|
|
18
19
|
const playbookId = flagString(args, 'playbook');
|
|
19
20
|
const pipelineId = flagString(args, 'pipeline');
|
|
20
21
|
const modeRaw = (flagString(args, 'mode') ?? '').toLowerCase();
|
|
22
|
+
// Warn on a typo'd --mode rather than silently treating it as balanced.
|
|
23
|
+
if (modeRaw && !VALID_SIMULATION_MODES.includes(modeRaw)) {
|
|
24
|
+
process.stderr.write(`Unknown --mode ${modeRaw}. Valid: ${VALID_SIMULATION_MODES.join(', ')} (defaulting to balanced).\n`);
|
|
25
|
+
}
|
|
21
26
|
const mode = modeRaw === 'conservative'
|
|
22
27
|
? OrchestrationMode.Conservative
|
|
23
28
|
: modeRaw === 'aggressive'
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"smart-context.command.d.ts","sourceRoot":"","sources":["../../src/commands/smart-context.command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"smart-context.command.d.ts","sourceRoot":"","sources":["../../src/commands/smart-context.command.ts"],"names":[],"mappings":"AA0BA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAmFhC,eAAO,MAAM,mBAAmB,EAAE,eAmQjC,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,4BAA4B,EAAE,eAsF1C,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,uBAAuB,EAAE,eAwBrC,CAAC;AAEF,8DAA8D;AAC9D,eAAO,MAAM,uBAAuB,EAAE,eAkCrC,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,iCAAiC,EAAE,eAmH/C,CAAC;AA2JF;;;;;;;GAOG;AACH,eAAO,MAAM,iCAAiC,EAAE,eAoH/C,CAAC;AA2JF;;;;;;GAMG;AACH,eAAO,MAAM,iCAAiC,EAAE,eAgG/C,CAAC;AAyPF,4EAA4E;AAC5E,eAAO,MAAM,kCAAkC,EAAE,eAuHhD,CAAC;AAMF,iFAAiF;AACjF,eAAO,MAAM,mCAAmC,EAAE,eAsCjD,CAAC;AAqJF,UAAU,eAAe;IACvB,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,kFAAkF;IAClF,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,uFAAuF;IACvF,SAAS,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mEAAmE;IACnE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,CAAC,EAAE,eAAe,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAWzF"}
|
|
@@ -5,7 +5,7 @@ import * as os from 'node:os';
|
|
|
5
5
|
import { AiMessageRole, buildPromptMessages, EnhancementPipeline, EnhancementStageKind, OllamaProvider, buildDefaultEnhancementStages, buildFastEnhancementStages, selectAiProvider, } from '@shrkcrft/ai';
|
|
6
6
|
import { buildContext } from '@shrkcrft/context';
|
|
7
7
|
import { EdgeKind, GraphQueryApi, GraphStore, NodeKind } from '@shrkcrft/graph';
|
|
8
|
-
import { buildProjectOverview, buildTaskPacket, inspectSharkcraft, renderOverviewText, } from '@shrkcrft/inspector';
|
|
8
|
+
import { buildProjectOverview, buildTaskPacket, contextTuningBoostFor, inspectSharkcraft, renderOverviewText, } from '@shrkcrft/inspector';
|
|
9
9
|
import { flagBool, flagList, flagNumber, flagString, resolveCwd, } from "../command-registry.js";
|
|
10
10
|
import { DeclarationKind, PLAN_CACHE_SCHEMA, PlanCache, SemanticIndex, TaskType, buildFocusedContext, classifyTask, encodeEmbedding, getDefaultSourceRoots, listIndexableFiles, parseTaskTypeOverride, pruneDeletedHits, renderFocusedContextForPrompt, } from '@shrkcrft/embeddings';
|
|
11
11
|
import { SmartContextDetailedPlanSchema, SmartContextExpansionRequestSchema, } from "../schemas/json-schemas.js";
|
|
@@ -150,6 +150,42 @@ export const smartContextCommand = {
|
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
const enh = enhanced.value.enhancement;
|
|
153
|
+
// Deterministic-always contract (CLAUDE.md): when the enhancement
|
|
154
|
+
// pipeline fully degrades — every stage failed (or none ran), so the
|
|
155
|
+
// pipeline's finalOutput is just the echoed system message (preamble +
|
|
156
|
+
// "## Repository context" + the assembled deterministic seed) — fall
|
|
157
|
+
// back to the clean deterministic seed instead of leaking the raw
|
|
158
|
+
// internal prompt to stdout. This mirrors the single-shot
|
|
159
|
+
// provider-failure fallback below: clean renderSeed(seed) content,
|
|
160
|
+
// finishReason 'deterministic-fallback', no LLM/system-prompt wrapper.
|
|
161
|
+
const systemMsg = messages.find((m) => m.role === AiMessageRole.System);
|
|
162
|
+
const originalContext = (systemMsg?.content ?? '').trim();
|
|
163
|
+
const fullyDegraded = (enh.stages.length > 0 && enh.stages.every((s) => s.degraded)) ||
|
|
164
|
+
(originalContext.length > 0 && enhanced.value.content.trim() === originalContext);
|
|
165
|
+
if (fullyDegraded) {
|
|
166
|
+
if (!opts.json) {
|
|
167
|
+
process.stderr.write('[smart-context] enhancement fully degraded — returning deterministic context only.\n');
|
|
168
|
+
}
|
|
169
|
+
const fallbackEnvelope = buildEnvelope({
|
|
170
|
+
task,
|
|
171
|
+
seed,
|
|
172
|
+
ai: {
|
|
173
|
+
content: renderSeed(seed),
|
|
174
|
+
model: 'deterministic',
|
|
175
|
+
finishReason: 'deterministic-fallback',
|
|
176
|
+
usage: null,
|
|
177
|
+
providerId: 'deterministic',
|
|
178
|
+
},
|
|
179
|
+
mode: opts.mode,
|
|
180
|
+
});
|
|
181
|
+
if (opts.save) {
|
|
182
|
+
const saved = saveEnvelope(cwd, fallbackEnvelope);
|
|
183
|
+
writeSavedNotice(saved, opts.json, fallbackEnvelope);
|
|
184
|
+
return 0;
|
|
185
|
+
}
|
|
186
|
+
writeEnvelope(fallbackEnvelope, opts.json, opts.debug);
|
|
187
|
+
return 0;
|
|
188
|
+
}
|
|
153
189
|
if (!opts.json && !enh.deterministicFallback) {
|
|
154
190
|
if (enh.budgetExhausted) {
|
|
155
191
|
process.stderr.write(`[smart-context] budget reached before all ${enh.plannedPasses} passes finished — output is the best so far. Try a smaller --model or raise --budget.\n`);
|
|
@@ -179,6 +215,9 @@ export const smartContextCommand = {
|
|
|
179
215
|
messages,
|
|
180
216
|
maxTokens: opts.maxTokens,
|
|
181
217
|
model: opts.model,
|
|
218
|
+
// Bound the single shot so a hung local model degrades to the
|
|
219
|
+
// deterministic seed below instead of hanging the command.
|
|
220
|
+
timeoutMs: perCallTimeoutMs(opts.budgetMs),
|
|
182
221
|
});
|
|
183
222
|
if (!aiResult.ok) {
|
|
184
223
|
// Deterministic-always contract (CLAUDE.md): a provider failure must
|
|
@@ -293,6 +332,9 @@ export const smartContextPlanAheadCommand = {
|
|
|
293
332
|
messages,
|
|
294
333
|
maxTokens: opts.maxTokens,
|
|
295
334
|
model: opts.model,
|
|
335
|
+
// Bound each task's call so one hung model can't stall the queue;
|
|
336
|
+
// a timeout is reported as that task's error and the loop continues.
|
|
337
|
+
timeoutMs: perCallTimeoutMs(opts.budgetMs),
|
|
296
338
|
});
|
|
297
339
|
if (!aiResult.ok) {
|
|
298
340
|
results.push({ task, status: 'error', error: aiResult.error.message, slug: slug(task) });
|
|
@@ -1418,6 +1460,7 @@ async function buildSmartContextSeed(input) {
|
|
|
1418
1460
|
task,
|
|
1419
1461
|
maxTokens: options.seedTokens,
|
|
1420
1462
|
projectOverview: overviewText,
|
|
1463
|
+
boostFor: contextTuningBoostFor(inspection, task),
|
|
1421
1464
|
});
|
|
1422
1465
|
const graphGrounding = buildInitialGraphGrounding(cwd, task);
|
|
1423
1466
|
const semantic = await tryLoadSemanticHits(cwd, task, 10, options);
|
|
@@ -2693,6 +2736,23 @@ function truncateLine(text, max) {
|
|
|
2693
2736
|
const PER_STAGE_TIMEOUT_MS = 90_000;
|
|
2694
2737
|
const FAST_ENHANCE_BUDGET_MS = 150_000;
|
|
2695
2738
|
const PLUS_ENHANCE_BUDGET_MS = 360_000;
|
|
2739
|
+
/**
|
|
2740
|
+
* Per-call wall-clock bounds for the paths that DON'T run through
|
|
2741
|
+
* `runEnhancementPipeline` (single-shot brief, plan-ahead, and the two
|
|
2742
|
+
* `--ai-plan` stages). Without these, `--budget` only constrained the
|
|
2743
|
+
* enhancement pipeline, so a hung local model could still hang those modes.
|
|
2744
|
+
*
|
|
2745
|
+
* - `SINGLE_CALL_DEFAULT_BUDGET_MS` — the cap applied when no `--budget` is
|
|
2746
|
+
* given (mirrors the fast-enhance ceiling).
|
|
2747
|
+
* - `PER_CALL_TIMEOUT_CAP_MS` — an absolute ceiling, so even a generous
|
|
2748
|
+
* `--budget` can't let a single call run unbounded.
|
|
2749
|
+
*/
|
|
2750
|
+
const SINGLE_CALL_DEFAULT_BUDGET_MS = 150_000;
|
|
2751
|
+
const PER_CALL_TIMEOUT_CAP_MS = 360_000;
|
|
2752
|
+
/** Derive a per-call timeout (ms) from the optional `--budget` (budgetMs). */
|
|
2753
|
+
function perCallTimeoutMs(budgetMs) {
|
|
2754
|
+
return Math.min(budgetMs ?? SINGLE_CALL_DEFAULT_BUDGET_MS, PER_CALL_TIMEOUT_CAP_MS);
|
|
2755
|
+
}
|
|
2696
2756
|
async function runEnhancementPipeline(input) {
|
|
2697
2757
|
const provider = input.provider;
|
|
2698
2758
|
const systemMsg = input.messages.find((m) => m.role === AiMessageRole.System);
|
|
@@ -2830,6 +2890,7 @@ async function callProvider(input) {
|
|
|
2830
2890
|
...(input.model ? { model: input.model } : {}),
|
|
2831
2891
|
...(input.responseFormat ? { responseFormat: input.responseFormat } : {}),
|
|
2832
2892
|
...(input.onTokenStream ? { onTokenStream: input.onTokenStream } : {}),
|
|
2893
|
+
...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),
|
|
2833
2894
|
});
|
|
2834
2895
|
if (!res.ok || !res.value)
|
|
2835
2896
|
return { ok: false, error: res.error };
|
|
@@ -2927,10 +2988,23 @@ function writeDryRun(messages, mode, provider) {
|
|
|
2927
2988
|
}
|
|
2928
2989
|
}
|
|
2929
2990
|
function displayProviderName(explicit) {
|
|
2930
|
-
|
|
2931
|
-
|
|
2991
|
+
// Mirror provider-resolver.ts `normaliseKind` so the --dry-run header names
|
|
2992
|
+
// the provider that will ACTUALLY run, not a stale 'auto'. An explicit
|
|
2993
|
+
// `auto` is a local-first request that AI_PROVIDER never overrides; with no
|
|
2994
|
+
// --provider, AI_PROVIDER (including a hosted claude/gemini) decides what
|
|
2995
|
+
// runs, so the header must say so truthfully.
|
|
2996
|
+
if (explicit !== undefined) {
|
|
2997
|
+
const e = explicit.trim().toLowerCase();
|
|
2998
|
+
if (e === 'auto')
|
|
2999
|
+
return 'auto';
|
|
3000
|
+
if (e === 'claude' || e === 'gemini' || e === 'ollama' || e === 'llamacpp')
|
|
3001
|
+
return e;
|
|
3002
|
+
}
|
|
2932
3003
|
const envProvider = (process.env.AI_PROVIDER ?? '').trim().toLowerCase();
|
|
2933
|
-
if (envProvider === 'ollama' ||
|
|
3004
|
+
if (envProvider === 'ollama' ||
|
|
3005
|
+
envProvider === 'llamacpp' ||
|
|
3006
|
+
envProvider === 'claude' ||
|
|
3007
|
+
envProvider === 'gemini') {
|
|
2934
3008
|
return envProvider;
|
|
2935
3009
|
}
|
|
2936
3010
|
return 'auto';
|
|
@@ -3212,6 +3286,11 @@ async function buildAiPlanEnvelope(input) {
|
|
|
3212
3286
|
}
|
|
3213
3287
|
progressMarker(`preflight ok — host=${preflight.value.host} models=${preflight.value.models.length}`, input.options);
|
|
3214
3288
|
}
|
|
3289
|
+
// Split the (optional) --budget across the two stages so a hung local
|
|
3290
|
+
// model can't hang the ai-plan path the way it previously could. Each
|
|
3291
|
+
// call is independently bounded; a TIMEOUT surfaces as a call-failure and
|
|
3292
|
+
// is handled exactly like any other provider failure below.
|
|
3293
|
+
const aiPlanStageTimeoutMs = Math.min(Math.floor((input.options.budgetMs ?? SINGLE_CALL_DEFAULT_BUDGET_MS) / 2), PER_CALL_TIMEOUT_CAP_MS);
|
|
3215
3294
|
progressMarker(`stage 1 calling ${selection.provider.id}${input.options.model ? `:${input.options.model}` : ''}…`, input.options);
|
|
3216
3295
|
const stage1Messages = buildStage1Messages(input.seed, grounding, cacheLookup.reference);
|
|
3217
3296
|
logPromptToStderr('stage1', stage1Messages, input.options);
|
|
@@ -3229,6 +3308,7 @@ async function buildAiPlanEnvelope(input) {
|
|
|
3229
3308
|
repromptInstruction: STAGE1_REPROMPT,
|
|
3230
3309
|
stageLabel: 'stage 1',
|
|
3231
3310
|
options: input.options,
|
|
3311
|
+
timeoutMs: aiPlanStageTimeoutMs,
|
|
3232
3312
|
});
|
|
3233
3313
|
let stage1Request;
|
|
3234
3314
|
let stage1Retried = false;
|
|
@@ -3275,6 +3355,7 @@ async function buildAiPlanEnvelope(input) {
|
|
|
3275
3355
|
repromptInstruction: STAGE2_REPROMPT,
|
|
3276
3356
|
stageLabel: 'stage 2',
|
|
3277
3357
|
options: input.options,
|
|
3358
|
+
timeoutMs: aiPlanStageTimeoutMs,
|
|
3278
3359
|
});
|
|
3279
3360
|
const conversationTurns = [];
|
|
3280
3361
|
if (stage1Call) {
|
|
@@ -3424,6 +3505,7 @@ async function callProviderWithRetry(input) {
|
|
|
3424
3505
|
maxTokens: input.maxTokens,
|
|
3425
3506
|
...(input.model ? { model: input.model } : {}),
|
|
3426
3507
|
...(input.responseFormat ? { responseFormat: input.responseFormat } : {}),
|
|
3508
|
+
...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),
|
|
3427
3509
|
});
|
|
3428
3510
|
if (!first.ok)
|
|
3429
3511
|
return { kind: 'call-failed', error: first.error };
|
|
@@ -3449,6 +3531,7 @@ async function callProviderWithRetry(input) {
|
|
|
3449
3531
|
maxTokens: input.maxTokens,
|
|
3450
3532
|
...(input.model ? { model: input.model } : {}),
|
|
3451
3533
|
...(input.responseFormat ? { responseFormat: input.responseFormat } : {}),
|
|
3534
|
+
...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),
|
|
3452
3535
|
});
|
|
3453
3536
|
if (!second.ok) {
|
|
3454
3537
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"spec.command.d.ts","sourceRoot":"","sources":["../../src/commands/spec.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AA0CH,OAAO,
|
|
1
|
+
{"version":3,"file":"spec.command.d.ts","sourceRoot":"","sources":["../../src/commands/spec.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AA0CH,OAAO,EAUL,gBAAgB,EAMjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAShC,eAAO,MAAM,qBAAqB,iCAAiC,CAAC;AACpE,eAAO,MAAM,wBAAwB,oCAAoC,CAAC;AAY1E,eAAO,MAAM,iBAAiB,EAAE,eA2K/B,CAAC;AAcF,eAAO,MAAM,iBAAiB,EAAE,eAiE/B,CAAC;AAqBF,eAAO,MAAM,eAAe,EAAE,eAmC7B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,eA8B7B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,eA2C7B,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,eAoE/B,CAAC;AASF,eAAO,MAAM,oBAAoB,EAAE,eAsRlC,CAAC;AAyDF,eAAO,MAAM,iBAAiB,EAAE,eAqP/B,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,eA4B/B,CAAC;AAkBF,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
|
|
@@ -17,7 +17,7 @@ import { createHash } from 'node:crypto';
|
|
|
17
17
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
18
18
|
import * as nodePath from 'node:path';
|
|
19
19
|
import { appendSpecEvent, buildSavedPlan, buildSpecId, canonicalJson, deriveSpecJson, FileChangeType, generate, listSpecIds, loadSpec, OverwriteStrategy, persistSpecArtifacts, PLAN_SECRET_ENV, readSpecEvents, renderSpecMd, savePlanToFile, signPlan, specDir, specMdPath, specPlanPath, specVerificationPath, splitSpecMd, SPEC_SCHEMA_V1, SpecStatus, validateSpecStructural, verifyPlan, writeSpecJson, writeSpecMd, } from '@shrkcrft/generator';
|
|
20
|
-
import { AssetProvenanceOperation, AssetProvenanceSource, buildSpecList, buildSpecReview, buildTaskPacket, inspectSharkcraft, recordProvenance, SPEC_LIST_SCHEMA, } from '@shrkcrft/inspector';
|
|
20
|
+
import { AssetProvenanceOperation, AssetProvenanceSource, buildSpecList, buildSpecReview, buildTaskPacket, inspectSharkcraft, mapChecklistToEvidence, recordProvenance, SPEC_LIST_SCHEMA, } from '@shrkcrft/inspector';
|
|
21
21
|
import { flagBool, flagList, flagString, resolveCwd, } from "../command-registry.js";
|
|
22
22
|
import { collectChangedPaths } from "../diff/collect-changed-paths.js";
|
|
23
23
|
import { asJson, header } from "../output/format-output.js";
|
|
@@ -778,8 +778,8 @@ function shortHash(text) {
|
|
|
778
778
|
}
|
|
779
779
|
export const specVerifyCommand = {
|
|
780
780
|
name: 'verify',
|
|
781
|
-
description: 'Run the spec\'s trusted verification commands, check acceptance-criteria coverage, run diff-aware boundary + drift checks. Returns sharkcraft.spec-verification/v1.',
|
|
782
|
-
usage: 'shrk spec verify <id> [--since <ref>] [--strict] [--skip-verification-commands] [--json] [--write]',
|
|
781
|
+
description: 'Run the spec\'s trusted verification commands, check acceptance-criteria coverage, run diff-aware boundary + drift checks. Pass --coverage to map each acceptance criterion to backing changeset evidence and flag UNMET (claimed-but-zero-footprint) criteria. Returns sharkcraft.spec-verification/v1.',
|
|
782
|
+
usage: 'shrk spec verify <id> [--since <ref>] [--coverage] [--strict] [--skip-verification-commands] [--json] [--write]',
|
|
783
783
|
async run(args) {
|
|
784
784
|
const ref = args.positional[0];
|
|
785
785
|
if (!ref) {
|
|
@@ -886,18 +886,51 @@ export const specVerifyCommand = {
|
|
|
886
886
|
planIntegrity = 'invalid-signature';
|
|
887
887
|
}
|
|
888
888
|
}
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
889
|
+
// --coverage: map each acceptance criterion to backing changeset
|
|
890
|
+
// evidence (new exported symbol / registration / route / companion
|
|
891
|
+
// file / test). UNMET = a claimed criterion with zero footprint.
|
|
892
|
+
const wantCoverage = flagBool(args, 'coverage');
|
|
893
|
+
let coverage = null;
|
|
894
|
+
if (wantCoverage) {
|
|
895
|
+
const fileContents = {};
|
|
896
|
+
if (changed.isAvailable) {
|
|
897
|
+
for (const rel of changed.changed) {
|
|
898
|
+
const abs = nodePath.join(cwd, rel);
|
|
899
|
+
if (existsSync(abs)) {
|
|
900
|
+
try {
|
|
901
|
+
fileContents[rel] = readFileSync(abs, 'utf8');
|
|
902
|
+
}
|
|
903
|
+
catch {
|
|
904
|
+
fileContents[rel] = '';
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
coverage = mapChecklistToEvidence({
|
|
910
|
+
criteria: spec.acceptanceCriteria.map((ac) => ({ id: ac.id, text: ac.text })),
|
|
911
|
+
changedFiles: changed.changed,
|
|
912
|
+
fileContents,
|
|
913
|
+
});
|
|
914
|
+
}
|
|
915
|
+
const coverageById = new Map((coverage?.criteria ?? []).map((c) => [c.id, c]));
|
|
916
|
+
const acceptanceResults = spec.acceptanceCriteria.map((ac) => {
|
|
917
|
+
const cov = coverageById.get(ac.id);
|
|
918
|
+
return {
|
|
919
|
+
id: ac.id,
|
|
920
|
+
status: ac.verifiedBy.includes('manual')
|
|
921
|
+
? 'manual'
|
|
922
|
+
: commandFailed
|
|
923
|
+
? 'fail'
|
|
924
|
+
: 'pass',
|
|
925
|
+
evidence: cov?.evidence ?? [],
|
|
926
|
+
...(wantCoverage ? { covered: cov?.covered ?? false } : {}),
|
|
927
|
+
};
|
|
928
|
+
});
|
|
898
929
|
const verdict = commandFailed
|
|
899
930
|
? 'fail'
|
|
900
|
-
: scopeDrift.length > 0 ||
|
|
931
|
+
: scopeDrift.length > 0 ||
|
|
932
|
+
planIntegrity === 'missing-signature' ||
|
|
933
|
+
(coverage !== null && coverage.unmetCount > 0)
|
|
901
934
|
? 'warn'
|
|
902
935
|
: 'pass';
|
|
903
936
|
const report = {
|
|
@@ -911,6 +944,17 @@ export const specVerifyCommand = {
|
|
|
911
944
|
boundaries: { since: changed.ref, violations: [] },
|
|
912
945
|
scopeDrift: { outsideScope: scopeDrift },
|
|
913
946
|
planIntegrity: { status: planIntegrity },
|
|
947
|
+
...(coverage !== null
|
|
948
|
+
? {
|
|
949
|
+
coverage: {
|
|
950
|
+
checked: true,
|
|
951
|
+
total: coverage.criteria.length,
|
|
952
|
+
covered: coverage.coveredCount,
|
|
953
|
+
unmet: coverage.unmetCount,
|
|
954
|
+
changesetAvailable: changed.isAvailable,
|
|
955
|
+
},
|
|
956
|
+
}
|
|
957
|
+
: {}),
|
|
914
958
|
};
|
|
915
959
|
if (flagBool(args, 'write') || flagBool(args, 'apply')) {
|
|
916
960
|
writeFileSync(specVerificationPath(cwd, spec.id), JSON.stringify(report, null, 2) + '\n', 'utf8');
|
|
@@ -935,6 +979,22 @@ export const specVerifyCommand = {
|
|
|
935
979
|
}
|
|
936
980
|
process.stdout.write(` scope drift: ${scopeDrift.length}\n`);
|
|
937
981
|
process.stdout.write(` plan integrity: ${planIntegrity}\n`);
|
|
982
|
+
if (coverage !== null) {
|
|
983
|
+
process.stdout.write(` coverage: ${coverage.coveredCount}/${coverage.criteria.length} covered, ${coverage.unmetCount} UNMET\n`);
|
|
984
|
+
if (!changed.isAvailable) {
|
|
985
|
+
process.stdout.write(' (no changeset available — pass --since <ref> for evidence)\n');
|
|
986
|
+
}
|
|
987
|
+
for (const c of coverage.criteria) {
|
|
988
|
+
if (c.covered) {
|
|
989
|
+
const ev = c.evidence[0];
|
|
990
|
+
const note = ev ? ` (${ev.kind}: ${ev.detail})` : '';
|
|
991
|
+
process.stdout.write(` [ok] ${c.id}${note}\n`);
|
|
992
|
+
}
|
|
993
|
+
else {
|
|
994
|
+
process.stdout.write(` UNMET ${c.id} — ${c.text.slice(0, 60)}\n`);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
}
|
|
938
998
|
}
|
|
939
999
|
if (verdict === 'fail')
|
|
940
1000
|
return 1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-context.command.d.ts","sourceRoot":"","sources":["../../src/commands/task-context.command.ts"],"names":[],"mappings":"AAuBA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"task-context.command.d.ts","sourceRoot":"","sources":["../../src/commands/task-context.command.ts"],"names":[],"mappings":"AAuBA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAchC,eAAO,MAAM,qBAAqB,EAAE,eAyEnC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,eAsEnC,CAAC;AAIF,eAAO,MAAM,mBAAmB,EAAE,eA2DjC,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,eAqBnC,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,eA8ClC,CAAC"}
|
|
@@ -4,6 +4,7 @@ import { cliCommandNameSet } from "./command-catalog.js";
|
|
|
4
4
|
import { analyzeImportGraph, buildAgentBrief, buildContradictionReport, buildGeneratedCodeReport, buildRepositoryKnowledgeModel, buildTaskPacket, buildTaskRiskReport, classifyChangeIntent, detectLanguageProfiles, getChangedFiles, getStatusSummary, inspectSharkcraft, isGitRepo, listConstructs, loadRepositoryMemory, } from '@shrkcrft/inspector';
|
|
5
5
|
import { flagBool, flagString, flagList, resolveCwd, } from "../command-registry.js";
|
|
6
6
|
import { asJson, header, kv } from "../output/format-output.js";
|
|
7
|
+
import { computeMtimeFreshness } from "../status/freshness.js";
|
|
7
8
|
const CONTEXT_BASE = nodePath.join('.sharkcraft', 'context');
|
|
8
9
|
function slug(s) {
|
|
9
10
|
return s
|
|
@@ -244,11 +245,34 @@ export const contextStatusCommand = {
|
|
|
244
245
|
}
|
|
245
246
|
const { readFileSync } = await import('node:fs');
|
|
246
247
|
const body = readFileSync(file, 'utf8');
|
|
248
|
+
let status;
|
|
249
|
+
try {
|
|
250
|
+
status = JSON.parse(body);
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
status = {};
|
|
254
|
+
}
|
|
255
|
+
// Honest freshness: the saved context is stale once a source file changed
|
|
256
|
+
// after it was built. Adds `state` + a warn line in both shapes.
|
|
257
|
+
const fresh = computeMtimeFreshness(cwd, status.lastBuilt);
|
|
247
258
|
if (flagBool(args, 'json')) {
|
|
248
|
-
process.stdout.write(
|
|
259
|
+
process.stdout.write(asJson({
|
|
260
|
+
...status,
|
|
261
|
+
state: fresh.state,
|
|
262
|
+
lastBuiltAt: fresh.lastBuiltAt,
|
|
263
|
+
lastChangedAt: fresh.lastChangedAt,
|
|
264
|
+
behindMs: fresh.behindMs,
|
|
265
|
+
...(fresh.state === 'stale' ? { nextCommand: 'shrk context refresh' } : {}),
|
|
266
|
+
}) + '\n');
|
|
249
267
|
return 0;
|
|
250
268
|
}
|
|
251
|
-
process.stdout.write(
|
|
269
|
+
process.stdout.write(header('Context status'));
|
|
270
|
+
process.stdout.write(kv('last task', status.lastTask ?? '-') + '\n');
|
|
271
|
+
process.stdout.write(kv('last built', status.lastBuilt ?? '-') + '\n');
|
|
272
|
+
process.stdout.write(kv('state', fresh.state) + '\n');
|
|
273
|
+
if (fresh.state === 'stale') {
|
|
274
|
+
process.stdout.write(`! stale — files changed since this context was built${fresh.lastChangedAt ? ` (last change ${fresh.lastChangedAt})` : ''}; re-run \`shrk context refresh\`\n`);
|
|
275
|
+
}
|
|
252
276
|
return 0;
|
|
253
277
|
},
|
|
254
278
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test.command.d.ts","sourceRoot":"","sources":["../../src/commands/test.command.ts"],"names":[],"mappings":"AAUA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"test.command.d.ts","sourceRoot":"","sources":["../../src/commands/test.command.ts"],"names":[],"mappings":"AAUA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAiShC,eAAO,MAAM,WAAW,EAAE,eA4BzB,CAAC"}
|
|
@@ -3,6 +3,7 @@ import * as nodePath from 'node:path';
|
|
|
3
3
|
import { buildTaskPacket, inspectSharkcraft, loadAgentContractTests, loadContextTests, runAgentContractTest, runContextTest, } from '@shrkcrft/inspector';
|
|
4
4
|
import { flagBool, flagList, flagString, resolveCwd, } from "../command-registry.js";
|
|
5
5
|
import { asJson, header, kv } from "../output/format-output.js";
|
|
6
|
+
import { agentTestMissingExpectedHints, renderFailureHints } from "../output/failure-hints.js";
|
|
6
7
|
import { maybeRunInWatchMode } from "../output/watch-loop.js";
|
|
7
8
|
async function runContextTests(args) {
|
|
8
9
|
const inspection = await inspectSharkcraft({ cwd: resolveCwd(args) });
|
|
@@ -114,6 +115,22 @@ async function runAgentTests(args) {
|
|
|
114
115
|
}
|
|
115
116
|
}
|
|
116
117
|
process.stdout.write(`\nSummary: ${results.length - failed.length}/${results.length} passed.\n`);
|
|
118
|
+
// Surface explain/why hints when a failure lists an expected construct the
|
|
119
|
+
// packet didn't include — that's the case the `shrk why`/`why-not` family
|
|
120
|
+
// is built to diagnose.
|
|
121
|
+
const anyMissingExpected = failed.some((r) => (r.missingTemplates ?? []).length > 0 ||
|
|
122
|
+
(r.missingRules ?? []).length > 0 ||
|
|
123
|
+
(r.missingForbiddenActions ?? []).length > 0 ||
|
|
124
|
+
(r.missingVerificationCommands ?? []).length > 0 ||
|
|
125
|
+
(r.missingHelpers ?? []).length > 0 ||
|
|
126
|
+
(r.missingPlaybooks ?? []).length > 0 ||
|
|
127
|
+
(r.missingPolicies ?? []).length > 0 ||
|
|
128
|
+
(r.missingConstructs ?? []).length > 0 ||
|
|
129
|
+
(r.missingCommands ?? []).length > 0 ||
|
|
130
|
+
(r.missingKnowledge ?? []).length > 0);
|
|
131
|
+
if (anyMissingExpected) {
|
|
132
|
+
process.stdout.write(renderFailureHints(agentTestMissingExpectedHints()));
|
|
133
|
+
}
|
|
117
134
|
return failed.length > 0 ? 1 : 0;
|
|
118
135
|
}
|
|
119
136
|
async function generateContextTest(args) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace.command.d.ts","sourceRoot":"","sources":["../../src/commands/trace.command.ts"],"names":[],"mappings":"AAcA,OAAO,
|
|
1
|
+
{"version":3,"file":"trace.command.d.ts","sourceRoot":"","sources":["../../src/commands/trace.command.ts"],"names":[],"mappings":"AAcA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAOhC,eAAO,MAAM,YAAY,EAAE,eAqH1B,CAAC"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* shared query resolver and prints structured trace output.
|
|
7
7
|
*/
|
|
8
8
|
import { findSymbolInProject, inspectSharkcraft, QueryMatchKind, resolveQuery, } from '@shrkcrft/inspector';
|
|
9
|
-
import { flagBool, flagNumber, flagString, resolveCwd, } from "../command-registry.js";
|
|
9
|
+
import { flagBool, flagList, flagNumber, flagString, resolveCwd, } from "../command-registry.js";
|
|
10
10
|
import { asJson, header } from "../output/format-output.js";
|
|
11
11
|
function describeMatch(m) {
|
|
12
12
|
return `${m.kind.padEnd(12)} ${m.id}${m.label && m.label !== m.id ? ` — ${m.label}` : ''} [${m.score.toFixed(0)}]`;
|
|
@@ -58,10 +58,23 @@ export const traceCommand = {
|
|
|
58
58
|
process.stderr.write('Usage: shrk trace <query> | shrk trace --symbol <Name>\n');
|
|
59
59
|
return 2;
|
|
60
60
|
}
|
|
61
|
+
// `--kind` restricts the result set to specific match kinds — the engine
|
|
62
|
+
// (resolveQuery) filters by them. Validate up front so an unknown kind
|
|
63
|
+
// fails fast with the valid list, instead of being silently ignored (which
|
|
64
|
+
// returned a wrong-kind best match, e.g. a knowledge hit for
|
|
65
|
+
// `--kind template`).
|
|
66
|
+
const kinds = flagList(args, 'kind');
|
|
67
|
+
const validKinds = new Set(Object.values(QueryMatchKind));
|
|
68
|
+
const unknownKind = kinds.find((k) => !validKinds.has(k));
|
|
69
|
+
if (unknownKind) {
|
|
70
|
+
process.stderr.write(`trace: unknown --kind "${unknownKind}". Valid kinds: ${[...validKinds].sort().join(', ')}.\n`);
|
|
71
|
+
return 2;
|
|
72
|
+
}
|
|
61
73
|
const cwd = resolveCwd(args);
|
|
62
74
|
const inspection = await inspectSharkcraft({ cwd });
|
|
63
75
|
const limit = flagNumber(args, 'limit');
|
|
64
76
|
const resolution = resolveQuery(inspection, query, {
|
|
77
|
+
...(kinds.length > 0 ? { kinds: kinds } : {}),
|
|
65
78
|
...(typeof limit === 'number' ? { limit } : {}),
|
|
66
79
|
});
|
|
67
80
|
if (flagBool(args, 'json')) {
|
package/dist/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AAEA,OAAO,EACL,eAAe,EAKhB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AAEA,OAAO,EACL,eAAe,EAKhB,MAAM,uBAAuB,CAAC;AA0X/B,wBAAgB,aAAa,IAAI,eAAe,CA8X/C;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA2BrE;AAqID;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAoDxE"}
|
package/dist/main.js
CHANGED
|
@@ -32,6 +32,8 @@ import { archCommand } from "./commands/arch.command.js";
|
|
|
32
32
|
import { frameworkCommand } from "./commands/framework.command.js";
|
|
33
33
|
import { apiDiffCommand } from "./commands/api-diff.command.js";
|
|
34
34
|
import { gateCommand } from "./commands/gate.command.js";
|
|
35
|
+
import { policyLintCommand } from "./commands/policy-lint.command.js";
|
|
36
|
+
import { reuseCommand } from "./commands/reuse.command.js";
|
|
35
37
|
import { migrateCommand } from "./commands/migrate.command.js";
|
|
36
38
|
import { coverageCommand } from "./commands/coverage.command.js";
|
|
37
39
|
import { statsCommand } from "./commands/stats.command.js";
|
|
@@ -198,6 +200,8 @@ export function buildRegistry() {
|
|
|
198
200
|
registry.register(frameworkCommand);
|
|
199
201
|
registry.register(apiDiffCommand);
|
|
200
202
|
registry.register(gateCommand);
|
|
203
|
+
registry.register(policyLintCommand);
|
|
204
|
+
registry.register(reuseCommand);
|
|
201
205
|
registry.register(migrateCommand);
|
|
202
206
|
registry.register(coverageCommand);
|
|
203
207
|
registry.register(statsCommand);
|
|
@@ -597,7 +601,21 @@ async function runCliInner(argv) {
|
|
|
597
601
|
// (`shrk doctor`, `shrk packs list`, `shrk pack author status`). The
|
|
598
602
|
// resolver stops when it hits a flag or a token that isn't a child of the
|
|
599
603
|
// current node, returning the deepest handler + the leftover tokens.
|
|
600
|
-
|
|
604
|
+
let { handler, matchedPath, rest: leftover, node } = registry.resolve(cleanArgv);
|
|
605
|
+
// A multi-word command passed as a SINGLE quoted token (`shrk "graph status"`)
|
|
606
|
+
// arrives as one argv element with internal whitespace. The trie has no atomic
|
|
607
|
+
// `graph status` node, so the first descent misses (handler undefined) — yet
|
|
608
|
+
// did-you-mean would still list `graph status` as the #1 closest match, a
|
|
609
|
+
// self-contradiction. Re-split the lone token on whitespace and retry so the
|
|
610
|
+
// quoted form behaves identically to the unquoted `shrk graph status`.
|
|
611
|
+
// Genuinely unknown tokens leave `handler` undefined and fall through to the
|
|
612
|
+
// existing unknown-command path unchanged.
|
|
613
|
+
if (!handler && cleanArgv.length === 1 && /\s/.test(cleanArgv[0])) {
|
|
614
|
+
const retry = registry.resolve(cleanArgv[0].trim().split(/\s+/));
|
|
615
|
+
if (retry.handler) {
|
|
616
|
+
({ handler, matchedPath, rest: leftover, node } = retry);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
601
619
|
// `--help` / `-h` immediately after a (sub)group → render help for that
|
|
602
620
|
// path. This works at any depth.
|
|
603
621
|
if (leftover[0] === '--help' || leftover[0] === '-h') {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Source-mtime freshness — a small shared CLI helper.
|
|
3
|
+
*
|
|
4
|
+
* Several `… status` commands (`framework status`, `context status`) need to
|
|
5
|
+
* answer the same question: "is this stored artefact stale relative to the
|
|
6
|
+
* source tree it was built from?" They all walk the workspace for the newest
|
|
7
|
+
* source-file mtime and compare it to a stored build timestamp. This module
|
|
8
|
+
* is the single home for that walk so the commands stay byte-identical.
|
|
9
|
+
*
|
|
10
|
+
* NOTE: this is deliberately CLI-local and distinct from
|
|
11
|
+
* `@shrkcrft/graph`'s `detectGraphFreshness` — that one models the graph
|
|
12
|
+
* store's own freshness and graph sits BELOW cli in the layer order, so it
|
|
13
|
+
* cannot be reused here.
|
|
14
|
+
*/
|
|
15
|
+
export declare const FRESHNESS_SKIP_DIRS: Set<string>;
|
|
16
|
+
export declare const FRESHNESS_SOURCE_EXTS: Set<string>;
|
|
17
|
+
export interface IStatusFreshness {
|
|
18
|
+
state: 'fresh' | 'stale' | 'unknown';
|
|
19
|
+
lastBuiltAt: string | null;
|
|
20
|
+
lastChangedAt: string | null;
|
|
21
|
+
behindMs: number;
|
|
22
|
+
}
|
|
23
|
+
/** Newest source-file mtime (ms) under `root`, ignoring build/output dirs. 0
|
|
24
|
+
* when nothing matched. */
|
|
25
|
+
export declare function newestSourceMtimeMs(root: string): number;
|
|
26
|
+
/** Compare a stored build timestamp to the newest source mtime in `scope`.
|
|
27
|
+
* `stale` when a source file is newer than the build; `unknown` when the
|
|
28
|
+
* build timestamp can't be parsed. */
|
|
29
|
+
export declare function computeMtimeFreshness(scope: string, builtAtIso: string | undefined | null): IStatusFreshness;
|
|
30
|
+
//# sourceMappingURL=freshness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"freshness.d.ts","sourceRoot":"","sources":["../../src/status/freshness.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;GAaG;AAEH,eAAO,MAAM,mBAAmB,aAG9B,CAAC;AACH,eAAO,MAAM,qBAAqB,aAGhC,CAAC;AAEH,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC;IACrC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;4BAC4B;AAC5B,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA8BxD;AAED;;uCAEuC;AACvC,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,gBAAgB,CAc5G"}
|