@shrkcrft/cli 0.1.0-alpha.2 → 0.1.0-alpha.20
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/audit/knowledge-audit-llm.d.ts +19 -0
- package/dist/audit/knowledge-audit-llm.d.ts.map +1 -0
- package/dist/audit/knowledge-audit-llm.js +164 -0
- package/dist/audit/knowledge-audit.d.ts +61 -0
- package/dist/audit/knowledge-audit.d.ts.map +1 -0
- package/dist/audit/knowledge-audit.js +203 -0
- package/dist/audit/knowledge-fix-plan-llm.d.ts +11 -0
- package/dist/audit/knowledge-fix-plan-llm.d.ts.map +1 -0
- package/dist/audit/knowledge-fix-plan-llm.js +141 -0
- package/dist/audit/knowledge-fix-plan.d.ts +41 -0
- package/dist/audit/knowledge-fix-plan.d.ts.map +1 -0
- package/dist/audit/knowledge-fix-plan.js +125 -0
- package/dist/audit/pipeline-audit-llm.d.ts +11 -0
- package/dist/audit/pipeline-audit-llm.d.ts.map +1 -0
- package/dist/audit/pipeline-audit-llm.js +134 -0
- package/dist/audit/pipeline-audit.d.ts +69 -0
- package/dist/audit/pipeline-audit.d.ts.map +1 -0
- package/dist/audit/pipeline-audit.js +166 -0
- package/dist/audit/templates-audit-llm.d.ts +19 -0
- package/dist/audit/templates-audit-llm.d.ts.map +1 -0
- package/dist/audit/templates-audit-llm.js +207 -0
- package/dist/audit/templates-audit.d.ts +63 -0
- package/dist/audit/templates-audit.d.ts.map +1 -0
- package/dist/audit/templates-audit.js +171 -0
- package/dist/audit/templates-fix-plan-llm.d.ts +19 -0
- package/dist/audit/templates-fix-plan-llm.d.ts.map +1 -0
- package/dist/audit/templates-fix-plan-llm.js +162 -0
- package/dist/audit/templates-fix-plan.d.ts +37 -0
- package/dist/audit/templates-fix-plan.d.ts.map +1 -0
- package/dist/audit/templates-fix-plan.js +174 -0
- package/dist/command-registry.d.ts +28 -0
- package/dist/command-registry.d.ts.map +1 -1
- package/dist/command-registry.js +91 -1
- package/dist/commands/ai-status.command.d.ts +19 -0
- package/dist/commands/ai-status.command.d.ts.map +1 -0
- package/dist/commands/ai-status.command.js +94 -0
- package/dist/commands/api-diff.command.d.ts +11 -0
- package/dist/commands/api-diff.command.d.ts.map +1 -0
- package/dist/commands/api-diff.command.js +144 -0
- package/dist/commands/apply.command.d.ts.map +1 -1
- package/dist/commands/apply.command.js +10 -2
- package/dist/commands/arch.command.d.ts +9 -0
- package/dist/commands/arch.command.d.ts.map +1 -0
- package/dist/commands/arch.command.js +186 -0
- package/dist/commands/ask.command.d.ts.map +1 -1
- package/dist/commands/ask.command.js +10 -9
- package/dist/commands/cache-align.command.d.ts +12 -0
- package/dist/commands/cache-align.command.d.ts.map +1 -0
- package/dist/commands/cache-align.command.js +78 -0
- package/dist/commands/check.command.d.ts.map +1 -1
- package/dist/commands/check.command.js +19 -2
- package/dist/commands/code-intel.command.d.ts +18 -0
- package/dist/commands/code-intel.command.d.ts.map +1 -0
- package/dist/commands/code-intel.command.js +146 -0
- package/dist/commands/codemod.command.d.ts.map +1 -1
- package/dist/commands/codemod.command.js +27 -6
- package/dist/commands/command-catalog.d.ts +15 -3
- package/dist/commands/command-catalog.d.ts.map +1 -1
- package/dist/commands/command-catalog.js +387 -34
- package/dist/commands/commands.command.d.ts.map +1 -1
- package/dist/commands/commands.command.js +4 -4
- package/dist/commands/completion.command.d.ts +10 -0
- package/dist/commands/completion.command.d.ts.map +1 -0
- package/dist/commands/completion.command.js +121 -0
- package/dist/commands/compress.command.d.ts +8 -0
- package/dist/commands/compress.command.d.ts.map +1 -0
- package/dist/commands/compress.command.js +147 -0
- package/dist/commands/constructs.command.d.ts.map +1 -1
- package/dist/commands/constructs.command.js +89 -23
- package/dist/commands/context.command.d.ts.map +1 -1
- package/dist/commands/context.command.js +121 -1
- package/dist/commands/contract-gate.command.d.ts.map +1 -1
- package/dist/commands/contract-gate.command.js +5 -1
- package/dist/commands/delegate.command.d.ts +65 -0
- package/dist/commands/delegate.command.d.ts.map +1 -0
- package/dist/commands/delegate.command.js +657 -0
- package/dist/commands/deps-audit.command.d.ts +23 -0
- package/dist/commands/deps-audit.command.d.ts.map +1 -0
- package/dist/commands/deps-audit.command.js +270 -0
- package/dist/commands/dev.command.d.ts.map +1 -1
- package/dist/commands/dev.command.js +5 -1
- package/dist/commands/diff-check.command.d.ts +30 -0
- package/dist/commands/diff-check.command.d.ts.map +1 -0
- package/dist/commands/diff-check.command.js +210 -0
- package/dist/commands/doctor.command.d.ts.map +1 -1
- package/dist/commands/doctor.command.js +162 -10
- package/dist/commands/export.command.d.ts.map +1 -1
- package/dist/commands/export.command.js +76 -3
- package/dist/commands/framework.command.d.ts +12 -0
- package/dist/commands/framework.command.d.ts.map +1 -0
- package/dist/commands/framework.command.js +180 -0
- package/dist/commands/gate.command.d.ts +15 -0
- package/dist/commands/gate.command.d.ts.map +1 -0
- package/dist/commands/gate.command.js +300 -0
- package/dist/commands/gen.command.d.ts.map +1 -1
- package/dist/commands/gen.command.js +13 -1
- package/dist/commands/graph-code-subverbs.d.ts +33 -0
- package/dist/commands/graph-code-subverbs.d.ts.map +1 -0
- package/dist/commands/graph-code-subverbs.js +1366 -0
- package/dist/commands/graph.command.d.ts.map +1 -1
- package/dist/commands/graph.command.js +31 -2
- package/dist/commands/help.command.d.ts +4 -3
- package/dist/commands/help.command.d.ts.map +1 -1
- package/dist/commands/help.command.js +86 -18
- package/dist/commands/helper.command.js +1 -1
- package/dist/commands/impact.command.d.ts.map +1 -1
- package/dist/commands/impact.command.js +171 -1
- package/dist/commands/import.command.d.ts.map +1 -1
- package/dist/commands/import.command.js +121 -5
- package/dist/commands/ingest.command.d.ts.map +1 -1
- package/dist/commands/ingest.command.js +5 -1
- package/dist/commands/init.command.d.ts.map +1 -1
- package/dist/commands/init.command.js +174 -7
- package/dist/commands/knowledge-author.command.d.ts.map +1 -1
- package/dist/commands/knowledge-author.command.js +9 -0
- package/dist/commands/knowledge-propose.command.d.ts.map +1 -1
- package/dist/commands/knowledge-propose.command.js +4 -2
- package/dist/commands/knowledge.command.d.ts.map +1 -1
- package/dist/commands/knowledge.command.js +26 -3
- package/dist/commands/migrate.command.d.ts +13 -0
- package/dist/commands/migrate.command.d.ts.map +1 -0
- package/dist/commands/migrate.command.js +152 -0
- package/dist/commands/move-plan.command.d.ts +23 -0
- package/dist/commands/move-plan.command.d.ts.map +1 -0
- package/dist/commands/move-plan.command.js +360 -0
- package/dist/commands/packs-new.d.ts +1 -1
- package/dist/commands/packs-new.d.ts.map +1 -1
- package/dist/commands/packs-new.js +5 -36
- package/dist/commands/packs.command.d.ts.map +1 -1
- package/dist/commands/packs.command.js +2 -10
- package/dist/commands/plan-context.command.d.ts +11 -0
- package/dist/commands/plan-context.command.d.ts.map +1 -0
- package/dist/commands/plan-context.command.js +85 -0
- package/dist/commands/preflight.command.d.ts.map +1 -1
- package/dist/commands/preflight.command.js +15 -0
- package/dist/commands/profiles.command.js +4 -4
- package/dist/commands/recommend.command.d.ts +6 -0
- package/dist/commands/recommend.command.d.ts.map +1 -1
- package/dist/commands/recommend.command.js +119 -5
- package/dist/commands/release.command.js +13 -13
- package/dist/commands/rule-graph-subverbs.d.ts +3 -0
- package/dist/commands/rule-graph-subverbs.d.ts.map +1 -0
- package/dist/commands/rule-graph-subverbs.js +132 -0
- package/dist/commands/rules.command.d.ts.map +1 -1
- package/dist/commands/rules.command.js +20 -3
- package/dist/commands/scaffold-validate.command.d.ts +22 -0
- package/dist/commands/scaffold-validate.command.d.ts.map +1 -0
- package/dist/commands/scaffold-validate.command.js +215 -0
- package/dist/commands/search-structural.command.d.ts +18 -0
- package/dist/commands/search-structural.command.d.ts.map +1 -0
- package/dist/commands/search-structural.command.js +376 -0
- package/dist/commands/search.command.js +1 -1
- package/dist/commands/smart-context.command.d.ts +67 -0
- package/dist/commands/smart-context.command.d.ts.map +1 -0
- package/dist/commands/smart-context.command.js +4728 -0
- package/dist/commands/spike.command.d.ts +22 -0
- package/dist/commands/spike.command.d.ts.map +1 -0
- package/dist/commands/spike.command.js +235 -0
- package/dist/commands/surface.command.d.ts +1 -0
- package/dist/commands/surface.command.d.ts.map +1 -1
- package/dist/commands/surface.command.js +10 -3
- package/dist/commands/task-context.command.d.ts.map +1 -1
- package/dist/commands/task-context.command.js +5 -17
- package/dist/commands/task.command.d.ts.map +1 -1
- package/dist/commands/task.command.js +8 -2
- package/dist/commands/template-quality.command.d.ts.map +1 -1
- package/dist/commands/template-quality.command.js +39 -3
- package/dist/commands/templates.command.d.ts.map +1 -1
- package/dist/commands/templates.command.js +37 -2
- package/dist/commands/tests.command.d.ts.map +1 -1
- package/dist/commands/tests.command.js +13 -2
- package/dist/commands/watch.command.d.ts +26 -0
- package/dist/commands/watch.command.d.ts.map +1 -0
- package/dist/commands/watch.command.js +456 -0
- package/dist/dashboard/code-intelligence-data.d.ts +33 -0
- package/dist/dashboard/code-intelligence-data.d.ts.map +1 -0
- package/dist/dashboard/code-intelligence-data.js +329 -0
- package/dist/dashboard/dashboard-api-server.d.ts.map +1 -1
- package/dist/dashboard/dashboard-api-server.js +256 -2
- package/dist/dashboard/knowledge-ask.d.ts +4 -0
- package/dist/dashboard/knowledge-ask.d.ts.map +1 -0
- package/dist/dashboard/knowledge-ask.js +112 -0
- package/dist/env/load-dotenv.d.ts +15 -0
- package/dist/env/load-dotenv.d.ts.map +1 -0
- package/dist/env/load-dotenv.js +70 -0
- package/dist/export/claude-commands-export.d.ts +60 -0
- package/dist/export/claude-commands-export.d.ts.map +1 -0
- package/dist/export/claude-commands-export.js +276 -0
- package/dist/export/export-formats.d.ts +1 -1
- package/dist/export/export-formats.d.ts.map +1 -1
- package/dist/export/export-formats.js +139 -12
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/init/init-templates.d.ts.map +1 -1
- package/dist/init/init-templates.js +133 -113
- package/dist/init/paths-advisory.d.ts +20 -0
- package/dist/init/paths-advisory.d.ts.map +1 -0
- package/dist/init/paths-advisory.js +88 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +331 -17
- package/dist/output/ccr-store-config.d.ts +18 -0
- package/dist/output/ccr-store-config.d.ts.map +1 -0
- package/dist/output/ccr-store-config.js +41 -0
- package/dist/output/format-output.d.ts.map +1 -1
- package/dist/output/format-output.js +6 -1
- package/dist/output/output-compression.d.ts +15 -0
- package/dist/output/output-compression.d.ts.map +1 -0
- package/dist/output/output-compression.js +60 -0
- package/dist/output/resolve-compress-type.d.ts +22 -0
- package/dist/output/resolve-compress-type.d.ts.map +1 -0
- package/dist/output/resolve-compress-type.js +21 -0
- package/dist/output/watch-loop.d.ts +9 -1
- package/dist/output/watch-loop.d.ts.map +1 -1
- package/dist/output/watch-loop.js +13 -3
- package/dist/schemas/json-schemas.d.ts +384 -36
- package/dist/schemas/json-schemas.d.ts.map +1 -1
- package/dist/schemas/json-schemas.js +247 -36
- package/dist/surface/profiles.d.ts.map +1 -1
- package/dist/surface/profiles.js +54 -9
- package/dist/surface/surface-config-writer.d.ts.map +1 -1
- package/dist/surface/surface-config-writer.js +23 -11
- package/dist/validation/run-validation-loop.d.ts.map +1 -1
- package/dist/validation/run-validation-loop.js +5 -1
- package/package.json +35 -21
- package/dist/commands/plugin.command.d.ts +0 -11
- package/dist/commands/plugin.command.d.ts.map +0 -1
- package/dist/commands/plugin.command.js +0 -394
|
@@ -5,10 +5,12 @@ import { buildSurfaceSummary } from "../surface/surface-summary.js";
|
|
|
5
5
|
import { renderShapeLine } from "../surface/shape-defaults.js";
|
|
6
6
|
import { existsSync } from 'node:fs';
|
|
7
7
|
import { flagBool, flagNumber, flagString, flagList, resolveCwd, } from "../command-registry.js";
|
|
8
|
+
import { SemanticIndex, listIndexableFiles } from '@shrkcrft/embeddings';
|
|
8
9
|
import { asJson, header, kv } from "../output/format-output.js";
|
|
9
10
|
import { maybeRunInWatchMode } from "../output/watch-loop.js";
|
|
10
11
|
import { doctorHints, renderFailureHints } from "../output/failure-hints.js";
|
|
11
12
|
import { foldDoctorChecks, renderFoldedSummary, DoctorState, } from "../doctor/doctor-tags.js";
|
|
13
|
+
import { enrichWithLlmRecommendations, renderRecommendationsMarkdown, } from '@shrkcrft/ai';
|
|
12
14
|
const SEVERITY_LABEL = {
|
|
13
15
|
[DoctorSeverity.Ok]: 'OK ',
|
|
14
16
|
[DoctorSeverity.Info]: 'INFO ',
|
|
@@ -38,7 +40,7 @@ function describeStrictMode(mode) {
|
|
|
38
40
|
case 'all':
|
|
39
41
|
return 'strict=all (every warning fails)';
|
|
40
42
|
case 'warnings':
|
|
41
|
-
return 'strict=warnings (structural warnings fail,
|
|
43
|
+
return 'strict=warnings (structural warnings fail, advisory excluded)';
|
|
42
44
|
case 'errors':
|
|
43
45
|
return 'strict=errors (only errors fail)';
|
|
44
46
|
case 'off':
|
|
@@ -59,8 +61,10 @@ function evaluateStrict(mode, checks, errorCount) {
|
|
|
59
61
|
for (const c of checks) {
|
|
60
62
|
if (c.severity !== DoctorSeverity.Warning)
|
|
61
63
|
continue;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
+
// `--strict=warnings` excludes anything the inspector flagged as
|
|
65
|
+
// advisory (action-hint quality today, any future advisory category
|
|
66
|
+
// tomorrow). `--strict=all` counts every warning, advisory or not.
|
|
67
|
+
if (mode === 'warnings' && c.advisory === true) {
|
|
64
68
|
excludedWarnings += 1;
|
|
65
69
|
}
|
|
66
70
|
else {
|
|
@@ -74,7 +78,7 @@ function evaluateStrict(mode, checks, errorCount) {
|
|
|
74
78
|
excludedWarnings,
|
|
75
79
|
reason: mode === 'all'
|
|
76
80
|
? 'any warning'
|
|
77
|
-
: 'structural warnings only (
|
|
81
|
+
: 'structural warnings only (advisory excluded)',
|
|
78
82
|
};
|
|
79
83
|
}
|
|
80
84
|
function buildFilterOptions(args, suppressions) {
|
|
@@ -137,10 +141,81 @@ function isBlockerCheck(check) {
|
|
|
137
141
|
async function runDoctorOnce(args) {
|
|
138
142
|
return doctorCommandImpl(args);
|
|
139
143
|
}
|
|
144
|
+
function augmentWithSemanticIndexCheck(result, cwd) {
|
|
145
|
+
const current = listIndexableFiles(cwd, 5000);
|
|
146
|
+
const report = SemanticIndex.freshnessReport(cwd, current);
|
|
147
|
+
const check = renderSemanticIndexCheck(report);
|
|
148
|
+
const checks = [...result.checks, check];
|
|
149
|
+
const summary = { ...result.summary };
|
|
150
|
+
if (check.severity === DoctorSeverity.Ok)
|
|
151
|
+
summary.ok = (summary.ok ?? 0) + 1;
|
|
152
|
+
else if (check.severity === DoctorSeverity.Info)
|
|
153
|
+
summary.info = (summary.info ?? 0) + 1;
|
|
154
|
+
else if (check.severity === DoctorSeverity.Warning)
|
|
155
|
+
summary.warnings = (summary.warnings ?? 0) + 1;
|
|
156
|
+
else if (check.severity === DoctorSeverity.Error)
|
|
157
|
+
summary.errors = (summary.errors ?? 0) + 1;
|
|
158
|
+
return { ...result, checks, summary };
|
|
159
|
+
}
|
|
160
|
+
function renderSemanticIndexCheck(report) {
|
|
161
|
+
if (!report.hasIndex) {
|
|
162
|
+
return {
|
|
163
|
+
id: 'semantic-index-missing',
|
|
164
|
+
title: 'Semantic embedding index',
|
|
165
|
+
severity: DoctorSeverity.Info,
|
|
166
|
+
message: `No semantic index found — ${report.untracked} indexable files on disk. ` +
|
|
167
|
+
'Run `shrk smart-context embeddings-build` to enable embedding-backed retrieval in smart-context.',
|
|
168
|
+
category: 'semantic-index',
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
if (report.corrupt) {
|
|
172
|
+
return {
|
|
173
|
+
id: 'semantic-index-corrupt',
|
|
174
|
+
title: 'Semantic embedding index',
|
|
175
|
+
severity: DoctorSeverity.Error,
|
|
176
|
+
message: 'Semantic index meta is corrupt.',
|
|
177
|
+
fix: 'shrk smart-context embeddings-build --rebuild',
|
|
178
|
+
category: 'semantic-index',
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const driftCount = report.stale + report.missing + report.untracked;
|
|
182
|
+
if (driftCount === 0) {
|
|
183
|
+
return {
|
|
184
|
+
id: 'semantic-index-fresh',
|
|
185
|
+
title: 'Semantic embedding index',
|
|
186
|
+
severity: DoctorSeverity.Ok,
|
|
187
|
+
message: `Index fresh — ${report.indexed} files (model ${report.model}).`,
|
|
188
|
+
category: 'semantic-index',
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
// Severity is driven only by *rot* — indexed entries that changed (stale)
|
|
192
|
+
// or were deleted (missing), as a fraction of the indexed set. Newly added
|
|
193
|
+
// files (`untracked`) are benign growth, not drift: they aren't in the
|
|
194
|
+
// index yet so they can't return wrong hits, and they don't belong in a
|
|
195
|
+
// ratio whose denominator is the indexed set. The old fused "% drift"
|
|
196
|
+
// (stale+missing+untracked over indexed) tripped a yellow WARN purely on
|
|
197
|
+
// repo growth — exactly the "scary number" noise. The index is a derived
|
|
198
|
+
// cache, so this is always `advisory` (folds into the calm "N advisory"
|
|
199
|
+
// line and nudges a refresh) rather than a code-health defect; a deleted
|
|
200
|
+
// file can no longer reach a caller either, because searchFiles prunes
|
|
201
|
+
// on-disk-missing paths at query time.
|
|
202
|
+
const rotPct = report.indexed > 0 ? ((report.stale + report.missing) * 100) / report.indexed : 0;
|
|
203
|
+
const severity = rotPct >= 10 ? DoctorSeverity.Warning : DoctorSeverity.Info;
|
|
204
|
+
return {
|
|
205
|
+
id: 'semantic-index-stale',
|
|
206
|
+
title: 'Semantic embedding index',
|
|
207
|
+
severity,
|
|
208
|
+
advisory: true,
|
|
209
|
+
message: `Index drifted from disk — ${report.untracked} new, ${report.missing} deleted, ${report.stale} changed ` +
|
|
210
|
+
`(${report.indexed} indexed; ${Math.round(rotPct)}% stale/deleted).`,
|
|
211
|
+
fix: 'shrk smart-context embeddings-build',
|
|
212
|
+
category: 'semantic-index',
|
|
213
|
+
};
|
|
214
|
+
}
|
|
140
215
|
export const doctorCommand = {
|
|
141
216
|
name: 'doctor',
|
|
142
|
-
description: 'Validate the local SharkCraft setup (config, knowledge, templates, project). `--focus errors|warnings-new|info`, `--hide <category,...>`, `--quiet-known` filter the headline view using `sharkcraft/doctor.suppressions.json`. `--watch`/`--once`/`--debounce` for live mode. `--explain-quality` shows the per-warning "why this matters" line so warnings stop being permanent yellow noise. `--blockers` shows only must-fix findings (errors + warning-category in {config-invalid, pack-signature-invalid, plan-signature-divergent, asset-load-failed}); exit code is non-zero iff a blocker remains. Subcommands: `suppress`, `suppressions list|check`, `watch`.',
|
|
143
|
-
usage: 'shrk [--cwd <dir>] doctor [--no-config] [--json] [--strict[=errors|warnings|all]] [--blockers] [--show-advisory] [--min-score <0-100>] [--focus errors,warnings-new,info] [--hide action-hint-quality,...] [--quiet-known] [--explain-quality] [--watch [--once] [--debounce N]]',
|
|
217
|
+
description: 'Validate the local SharkCraft setup (config, knowledge, templates, project). `--focus errors|warnings-new|info`, `--hide <category,...>`, `--quiet-known` filter the headline view using `sharkcraft/doctor.suppressions.json`. `--watch`/`--once`/`--debounce` for live mode. `--explain-quality` shows the per-warning "why this matters" line so warnings stop being permanent yellow noise. `--blockers` shows only must-fix findings (errors + warning-category in {config-invalid, pack-signature-invalid, plan-signature-divergent, asset-load-failed}); exit code is non-zero iff a blocker remains. `--llm-recommendations` layers a local-LLM-derived list of concrete next-steps onto the deterministic output (no-op when no provider is reachable). Subcommands: `suppress`, `suppressions list|check`, `watch`.',
|
|
218
|
+
usage: 'shrk [--cwd <dir>] doctor [--no-config] [--json] [--strict[=errors|warnings|all]] [--blockers] [--show-advisory] [--min-score <0-100>] [--focus errors,warnings-new,info] [--hide action-hint-quality,...] [--quiet-known] [--explain-quality] [--llm-recommendations] [--provider auto|ollama|llamacpp] [--watch [--once] [--debounce N]]',
|
|
144
219
|
async run(args) {
|
|
145
220
|
const watchExit = await maybeRunInWatchMode(args, runDoctorOnce);
|
|
146
221
|
if (watchExit !== null)
|
|
@@ -161,7 +236,7 @@ async function doctorCommandImpl(args) {
|
|
|
161
236
|
inspectOpts.loaderTimeoutMs = loaderTimeout;
|
|
162
237
|
}
|
|
163
238
|
const inspection = await inspectSharkcraft(inspectOpts);
|
|
164
|
-
const result = runDoctor(inspection);
|
|
239
|
+
const result = augmentWithSemanticIndexCheck(runDoctor(inspection), cwd);
|
|
165
240
|
const report = buildAiReadinessReport(inspection);
|
|
166
241
|
if (debug) {
|
|
167
242
|
process.stderr.write(`[debug] inspection elapsed ${inspection.inspectionElapsedMs}ms cache=${inspection.cacheEnabled ? 'on' : 'off'} loaders=${inspection.loaderDiagnostics.length}\n`);
|
|
@@ -235,6 +310,19 @@ async function doctorCommandImpl(args) {
|
|
|
235
310
|
return !isSharkcraftMissing;
|
|
236
311
|
});
|
|
237
312
|
}
|
|
313
|
+
// Optional LLM enrichment: never alters the deterministic emission below;
|
|
314
|
+
// only appended at the end. No-op when the flag is off or no provider is
|
|
315
|
+
// reachable — keeps the deterministic baseline byte-stable.
|
|
316
|
+
const wantLlmRecs = flagBool(args, 'llm-recommendations');
|
|
317
|
+
const llmEnvelope = wantLlmRecs
|
|
318
|
+
? await enrichWithLlmRecommendations({
|
|
319
|
+
surface: 'doctor',
|
|
320
|
+
deterministicSummary: summariseDoctorChecks(visibleChecks),
|
|
321
|
+
providerKind: flagString(args, 'provider') ?? undefined,
|
|
322
|
+
ask: 'For each warning or error, propose ONE concrete next-step the user can execute from a shell — name the `shrk` subcommand, file path, or config key. If a finding has no useful next-step, skip it.',
|
|
323
|
+
maxTokens: 1024,
|
|
324
|
+
})
|
|
325
|
+
: null;
|
|
238
326
|
const ackExpired = ackSummary.expired.length > 0 && failOnExpiredAcknowledgement;
|
|
239
327
|
// Under --no-config + missing sharkcraft, treat the run as advisory: do not
|
|
240
328
|
// red-fail on the inspector's "no sharkcraft" errors / warnings.
|
|
@@ -327,6 +415,7 @@ async function doctorCommandImpl(args) {
|
|
|
327
415
|
})),
|
|
328
416
|
...result,
|
|
329
417
|
...(filtered ? { filtered } : {}),
|
|
418
|
+
...(llmEnvelope ? { llmRecommendations: llmEnvelope } : {}),
|
|
330
419
|
}) + '\n');
|
|
331
420
|
return overallExitCode;
|
|
332
421
|
}
|
|
@@ -402,6 +491,11 @@ async function doctorCommandImpl(args) {
|
|
|
402
491
|
process.stdout.write('\n' + summary);
|
|
403
492
|
}
|
|
404
493
|
process.stdout.write('\n');
|
|
494
|
+
// How many of the warnings are routine index/cache maintenance (advisory
|
|
495
|
+
// staleness) rather than code-health defects. Surfaced as an additive
|
|
496
|
+
// sub-line so the headline count isn't misread as N real problems —
|
|
497
|
+
// these fold out under --show-advisory.
|
|
498
|
+
const maintenanceWarnings = visibleChecks.filter((c) => c.severity === DoctorSeverity.Warning && c.advisory).length;
|
|
405
499
|
if (filtered) {
|
|
406
500
|
const s = filtered.summary;
|
|
407
501
|
process.stdout.write(`Summary: ${s.ok} ok, ${s.info} info, ${s.warnings} active warnings, ${s.errors} errors\n`);
|
|
@@ -415,12 +509,15 @@ async function doctorCommandImpl(args) {
|
|
|
415
509
|
else {
|
|
416
510
|
process.stdout.write(`Summary: ${result.summary.ok} ok, ${result.summary.info} info, ${result.summary.warnings} warnings, ${result.summary.errors} errors\n`);
|
|
417
511
|
}
|
|
512
|
+
if (maintenanceWarnings > 0) {
|
|
513
|
+
process.stdout.write(` (${maintenanceWarnings} ${maintenanceWarnings === 1 ? 'warning is' : 'warnings are'} routine index/cache maintenance, not code-health defects — fold-out via --show-advisory)\n`);
|
|
514
|
+
}
|
|
418
515
|
void buildSuppressionEntry;
|
|
419
516
|
void doctorSuppressionsFile;
|
|
420
517
|
void saveDoctorSuppressions;
|
|
421
518
|
void existsSync;
|
|
422
519
|
if (strictMode === 'warnings' && strictEval.excludedWarnings > 0) {
|
|
423
|
-
process.stdout.write(` (strict=warnings excluded ${strictEval.excludedWarnings}
|
|
520
|
+
process.stdout.write(` (strict=warnings excluded ${strictEval.excludedWarnings} advisory warning(s); use --strict=all to include)\n`);
|
|
424
521
|
}
|
|
425
522
|
// Surface acknowledgement state. Bare suppressions don't qualify as
|
|
426
523
|
// acknowledgements; expiring/expired ones get a callout so authors don't
|
|
@@ -458,10 +555,27 @@ async function doctorCommandImpl(args) {
|
|
|
458
555
|
if (!inspection.sharkcraftDir) {
|
|
459
556
|
process.stdout.write('\nNothing here yet — try `shrk init --zero-config` to detect your stack and pick a preset.\n');
|
|
460
557
|
}
|
|
461
|
-
|
|
558
|
+
// Honest binary verdicts come first — yes/no on the two questions
|
|
559
|
+
// users actually want answered (can I let an agent write here? can
|
|
560
|
+
// I let an agent read here?). The 0..100 score follows, scoped to
|
|
561
|
+
// the workspace shape so libraries don't get dinged for "no
|
|
562
|
+
// pipelines" and apps don't get dinged for "no published API".
|
|
563
|
+
process.stdout.write(`\nShape: ${report.workspaceShape.label}` +
|
|
564
|
+
` (score counts ${report.dimensions.filter((d) => d.applies === 'core').length} of ${report.dimensions.length} dimensions)\n`);
|
|
565
|
+
process.stdout.write(` ${report.verdicts.readyForAgentReads ? '✓' : '✗'} Ready for agent reads (context / task lookups)\n`);
|
|
566
|
+
process.stdout.write(` ${report.verdicts.readyForAgentWrites ? '✓' : '✗'} Ready for agent writes (apply / generate)\n`);
|
|
567
|
+
if (report.verdicts.blockers.length > 0) {
|
|
568
|
+
process.stdout.write(` Blockers:\n`);
|
|
569
|
+
for (const b of report.verdicts.blockers) {
|
|
570
|
+
process.stdout.write(` • ${b}\n`);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
process.stdout.write(`\nAI-readiness: ${report.score} / 100 (${report.grade}, shape-aware)\n`);
|
|
462
574
|
if (report.topRecommendations.length) {
|
|
463
575
|
// Keep the default doctor output short: top 3 recommendations,
|
|
464
|
-
// pass `--verbose` for the full list.
|
|
576
|
+
// pass `--verbose` for the full list. Recommendations only fire
|
|
577
|
+
// from `core` dimensions now — n/a-for-shape dimensions stop
|
|
578
|
+
// generating the misleading "add a pipeline" advice for libraries.
|
|
465
579
|
const verbose = flagBool(args, 'verbose');
|
|
466
580
|
const visible = verbose ? report.topRecommendations : report.topRecommendations.slice(0, 3);
|
|
467
581
|
process.stdout.write(`Top recommendations${verbose ? '' : ` (top ${visible.length})`}:\n`);
|
|
@@ -471,6 +585,21 @@ async function doctorCommandImpl(args) {
|
|
|
471
585
|
process.stdout.write(` … (${report.topRecommendations.length - visible.length} more — pass --verbose to see all)\n`);
|
|
472
586
|
}
|
|
473
587
|
}
|
|
588
|
+
// Surface N/A dimensions when --show-na is passed, so users can see
|
|
589
|
+
// what was deliberately skipped and disagree if they want to.
|
|
590
|
+
if (flagBool(args, 'show-na')) {
|
|
591
|
+
const skipped = report.dimensions.filter((d) => d.applies !== 'core');
|
|
592
|
+
if (skipped.length > 0) {
|
|
593
|
+
process.stdout.write(`\nNot counted in score (${skipped.length} dimensions):\n`);
|
|
594
|
+
for (const d of skipped) {
|
|
595
|
+
const tag = d.applies === 'n/a-for-shape' ? 'n/a' : 'advisory';
|
|
596
|
+
process.stdout.write(` [${tag}] ${d.title}: ${d.note}`);
|
|
597
|
+
if (d.appliesReason)
|
|
598
|
+
process.stdout.write(` — ${d.appliesReason}`);
|
|
599
|
+
process.stdout.write('\n');
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
474
603
|
if (strictEval.failed) {
|
|
475
604
|
process.stdout.write(`\nStrict mode: failing because ${strictEval.countedWarnings} warning(s) + ${result.summary.errors} error(s) exist (${strictEval.reason}).\n`);
|
|
476
605
|
}
|
|
@@ -497,8 +626,31 @@ async function doctorCommandImpl(args) {
|
|
|
497
626
|
if (previewEligible) {
|
|
498
627
|
process.stdout.write('\nDraft patch available — run `shrk fix preview` for a preview-only patch under `.sharkcraft/fixes/`.\n');
|
|
499
628
|
}
|
|
629
|
+
if (llmEnvelope) {
|
|
630
|
+
process.stdout.write('\n');
|
|
631
|
+
process.stdout.write(renderRecommendationsMarkdown(llmEnvelope));
|
|
632
|
+
}
|
|
500
633
|
return overallExitCode;
|
|
501
634
|
}
|
|
635
|
+
function summariseDoctorChecks(checks) {
|
|
636
|
+
const lines = [];
|
|
637
|
+
const order = [DoctorSeverity.Error, DoctorSeverity.Warning, DoctorSeverity.Info, DoctorSeverity.Ok];
|
|
638
|
+
for (const sev of order) {
|
|
639
|
+
const grouped = checks.filter((c) => c.severity === sev);
|
|
640
|
+
if (grouped.length === 0)
|
|
641
|
+
continue;
|
|
642
|
+
const label = SEVERITY_LABEL[sev].trim();
|
|
643
|
+
lines.push(`## ${label} (${grouped.length})`);
|
|
644
|
+
for (const c of grouped) {
|
|
645
|
+
const fix = c.recommendedFix ?? c.fix;
|
|
646
|
+
lines.push(`- **${c.title}**${c.category ? ` (${c.category})` : ''}: ${c.message}${fix ? ` — suggested fix: ${fix}` : ''}`);
|
|
647
|
+
}
|
|
648
|
+
lines.push('');
|
|
649
|
+
}
|
|
650
|
+
if (lines.length === 0)
|
|
651
|
+
lines.push('(no findings — all checks passed)');
|
|
652
|
+
return lines.join('\n');
|
|
653
|
+
}
|
|
502
654
|
export const doctorSuppressCommand = {
|
|
503
655
|
name: 'suppress',
|
|
504
656
|
description: 'Add a doctor finding to sharkcraft/doctor.suppressions.json. Requires --reason.',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export.command.d.ts","sourceRoot":"","sources":["../../src/commands/export.command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"export.command.d.ts","sourceRoot":"","sources":["../../src/commands/export.command.ts"],"names":[],"mappings":"AASA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAgBhC,eAAO,MAAM,aAAa,EAAE,eAuF3B,CAAC"}
|
|
@@ -2,8 +2,9 @@ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
3
|
import { inspectSharkcraft } from '@shrkcrft/inspector';
|
|
4
4
|
import { ALL_EXPORT_FORMATS, isExportFormat, renderExport, } from "../export/export-formats.js";
|
|
5
|
+
import { buildClaudeCommands } from "../export/claude-commands-export.js";
|
|
5
6
|
import { flagBool, flagNumber, flagString, resolveCwd, } from "../command-registry.js";
|
|
6
|
-
import { asJson, header } from "../output/format-output.js";
|
|
7
|
+
import { asJson, bullet, header } from "../output/format-output.js";
|
|
7
8
|
import { exportBundleCommand, exportSessionCommand, exportQualityCommand, exportReviewCommand, } from "./export-bundle.command.js";
|
|
8
9
|
const ARCHIVE_SUBCOMMANDS = {
|
|
9
10
|
bundle: exportBundleCommand,
|
|
@@ -13,7 +14,7 @@ const ARCHIVE_SUBCOMMANDS = {
|
|
|
13
14
|
};
|
|
14
15
|
export const exportCommand = {
|
|
15
16
|
name: 'export',
|
|
16
|
-
description: '
|
|
17
|
+
description: 'Inversion — pull SharkCraft rules into the agent\'s prompt instead of the agent calling back to shrk. Single-file outputs: claude-skill (.claude/skills/<name>/SKILL.md, recommended), agents-md (AGENTS.md), claude-md (CLAUDE.md), cursor-rules (.cursor/rules/*.mdc), copilot-instructions. Multi-file output: claude-commands (.claude/commands/*.md — per-project slash commands like /new-service, /check-changes, /follow-shrk). Dry-run by default; pass --write to save.',
|
|
17
18
|
usage: 'shrk [--cwd <dir>] export <format> [--write] [--output <path>] [--task "<task>"] [--max-rules N] [--max-paths N] [--json]',
|
|
18
19
|
async run(args) {
|
|
19
20
|
const format = args.positional[0];
|
|
@@ -27,8 +28,13 @@ export const exportCommand = {
|
|
|
27
28
|
const sub = { ...args, positional: args.positional.slice(1) };
|
|
28
29
|
return archive.run(sub);
|
|
29
30
|
}
|
|
31
|
+
// Multi-file `claude-commands` dispatches separately — it emits
|
|
32
|
+
// one .md per slash command, not a single rendered file.
|
|
33
|
+
if (format === 'claude-commands') {
|
|
34
|
+
return runClaudeCommandsExport(args);
|
|
35
|
+
}
|
|
30
36
|
if (!isExportFormat(format)) {
|
|
31
|
-
process.stderr.write(`Unknown export format "${format}".\nFormats: ${ALL_EXPORT_FORMATS.join(', ')}, bundle, session, quality, review\n`);
|
|
37
|
+
process.stderr.write(`Unknown export format "${format}".\nFormats: ${ALL_EXPORT_FORMATS.join(', ')}, claude-commands, bundle, session, quality, review\n`);
|
|
32
38
|
return 2;
|
|
33
39
|
}
|
|
34
40
|
const cwd = resolveCwd(args);
|
|
@@ -81,3 +87,70 @@ export const exportCommand = {
|
|
|
81
87
|
return 0;
|
|
82
88
|
},
|
|
83
89
|
};
|
|
90
|
+
/**
|
|
91
|
+
* `shrk export claude-commands` — multi-file generator for Claude
|
|
92
|
+
* Code's native `.claude/commands/` slash-command primitive. Produces
|
|
93
|
+
* one .md per command (static + per-template).
|
|
94
|
+
*
|
|
95
|
+
* Unlike single-file exports (claude-skill / claude-md / etc.) this
|
|
96
|
+
* writes a SET of files. Each file is a complete recipe Claude Code
|
|
97
|
+
* loads when the user types the matching slash command.
|
|
98
|
+
*/
|
|
99
|
+
async function runClaudeCommandsExport(args) {
|
|
100
|
+
const cwd = resolveCwd(args);
|
|
101
|
+
const inspection = await inspectSharkcraft({ cwd });
|
|
102
|
+
const result = buildClaudeCommands(inspection);
|
|
103
|
+
const wantJson = flagBool(args, 'json');
|
|
104
|
+
const doWrite = flagBool(args, 'write');
|
|
105
|
+
const force = flagBool(args, 'force');
|
|
106
|
+
if (wantJson) {
|
|
107
|
+
process.stdout.write(asJson({
|
|
108
|
+
format: 'claude-commands',
|
|
109
|
+
write: doWrite,
|
|
110
|
+
files: result.files.map((f) => ({
|
|
111
|
+
path: f.path,
|
|
112
|
+
slash: f.slash,
|
|
113
|
+
source: f.source,
|
|
114
|
+
})),
|
|
115
|
+
}) + '\n');
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
if (!doWrite) {
|
|
119
|
+
process.stdout.write(header('Export (claude-commands) — dry-run'));
|
|
120
|
+
process.stdout.write(`Would write ${result.files.length} command file(s):\n\n`);
|
|
121
|
+
for (const f of result.files) {
|
|
122
|
+
process.stdout.write(` ${f.path}\n`);
|
|
123
|
+
process.stdout.write(` → users type \`/${f.slash}\` in Claude Code (${f.source})\n`);
|
|
124
|
+
}
|
|
125
|
+
process.stdout.write('\nRe-run with --write to save.\n');
|
|
126
|
+
return 0;
|
|
127
|
+
}
|
|
128
|
+
const written = [];
|
|
129
|
+
const skipped = [];
|
|
130
|
+
for (const file of result.files) {
|
|
131
|
+
const fullPath = join(cwd, file.path);
|
|
132
|
+
mkdirSync(dirname(fullPath), { recursive: true });
|
|
133
|
+
if (existsSync(fullPath) && !force) {
|
|
134
|
+
skipped.push(file.path);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
writeFileSync(fullPath, file.content, 'utf8');
|
|
138
|
+
written.push(file.path);
|
|
139
|
+
}
|
|
140
|
+
process.stdout.write(header('Claude commands exported'));
|
|
141
|
+
if (written.length) {
|
|
142
|
+
process.stdout.write(`Wrote ${written.length} command file(s):\n`);
|
|
143
|
+
for (const p of written) {
|
|
144
|
+
const f = result.files.find((x) => x.path === p);
|
|
145
|
+
process.stdout.write(bullet(`${p} → \`/${f.slash}\``) + '\n');
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (skipped.length) {
|
|
149
|
+
process.stdout.write(`\nSkipped ${skipped.length} (already exist; use --force to overwrite):\n`);
|
|
150
|
+
for (const p of skipped)
|
|
151
|
+
process.stdout.write(bullet(p) + '\n');
|
|
152
|
+
}
|
|
153
|
+
process.stdout.write('\nClaude Code picks up `.claude/commands/*.md` automatically. ' +
|
|
154
|
+
'Open the project in Claude Code, type `/` — the slash commands are in the palette.\n');
|
|
155
|
+
return 0;
|
|
156
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type ICommandHandler } from '../command-registry.js';
|
|
2
|
+
/**
|
|
3
|
+
* `shrk framework` — run / inspect the framework-aware extractors.
|
|
4
|
+
*
|
|
5
|
+
* Sub-verbs:
|
|
6
|
+
* - shrk framework index run extractors over the project
|
|
7
|
+
* - shrk framework status report store health
|
|
8
|
+
* - shrk framework list [filters] list entities (--framework, --subtype, --file)
|
|
9
|
+
* - shrk framework routes NestJS route table (method, path, handler, file)
|
|
10
|
+
*/
|
|
11
|
+
export declare const frameworkCommand: ICommandHandler;
|
|
12
|
+
//# sourceMappingURL=framework.command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"framework.command.d.ts","sourceRoot":"","sources":["../../src/commands/framework.command.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,EAAE,eAe9B,CAAC"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { buildRegistryWithPacks, defaultRegistry, FrameworkQueryApi, runExtractors, } from '@shrkcrft/framework-scanners';
|
|
2
|
+
import { inspectSharkcraft } from '@shrkcrft/inspector';
|
|
3
|
+
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
4
|
+
import { asJson, header, kv } from "../output/format-output.js";
|
|
5
|
+
/**
|
|
6
|
+
* `shrk framework` — run / inspect the framework-aware extractors.
|
|
7
|
+
*
|
|
8
|
+
* Sub-verbs:
|
|
9
|
+
* - shrk framework index run extractors over the project
|
|
10
|
+
* - shrk framework status report store health
|
|
11
|
+
* - shrk framework list [filters] list entities (--framework, --subtype, --file)
|
|
12
|
+
* - shrk framework routes NestJS route table (method, path, handler, file)
|
|
13
|
+
*/
|
|
14
|
+
export const frameworkCommand = {
|
|
15
|
+
name: 'framework',
|
|
16
|
+
description: 'Framework-aware extractors: NestJS (controllers/modules/providers/routes), React (components/hook usages). Output: shrk framework list / routes / status / index.',
|
|
17
|
+
usage: 'shrk framework index [--only nestjs,react] [--json] | shrk framework status [--json] | shrk framework list [--framework <name>] [--subtype <s>] [--file <path>] [--limit N] [--json] | shrk framework routes [--json]',
|
|
18
|
+
async run(args) {
|
|
19
|
+
const sub = args.positional[0];
|
|
20
|
+
if (sub === 'index')
|
|
21
|
+
return runIndex(args);
|
|
22
|
+
if (sub === 'status')
|
|
23
|
+
return runStatus(args);
|
|
24
|
+
if (sub === 'list')
|
|
25
|
+
return runList(args);
|
|
26
|
+
if (sub === 'routes')
|
|
27
|
+
return runRoutes(args);
|
|
28
|
+
process.stderr.write(this.usage + '\n');
|
|
29
|
+
return 2;
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
async function runIndex(args) {
|
|
33
|
+
const cwd = resolveCwd(args);
|
|
34
|
+
const wantJson = flagBool(args, 'json');
|
|
35
|
+
const only = flagString(args, 'only');
|
|
36
|
+
const noPacks = flagBool(args, 'no-packs');
|
|
37
|
+
try {
|
|
38
|
+
// Build the registry: built-ins + optional pack-contributed extractors.
|
|
39
|
+
const registry = defaultRegistry();
|
|
40
|
+
let packPackages = [];
|
|
41
|
+
let packDiagnostics = [];
|
|
42
|
+
if (!noPacks) {
|
|
43
|
+
try {
|
|
44
|
+
const inspection = await inspectSharkcraft({ cwd });
|
|
45
|
+
const merged = await buildRegistryWithPacks(registry, inspection.packs);
|
|
46
|
+
packPackages = merged.packs;
|
|
47
|
+
packDiagnostics = merged.diagnostics;
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
// Pack loading is best-effort. Surface the error as a diagnostic
|
|
51
|
+
// but don't block the index — built-in extractors still run.
|
|
52
|
+
packDiagnostics = [`pack discovery failed: ${e.message}`];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const r = runExtractors({
|
|
56
|
+
projectRoot: cwd,
|
|
57
|
+
registry,
|
|
58
|
+
...(only ? { only: only.split(',').map((s) => s.trim()).filter(Boolean) } : {}),
|
|
59
|
+
});
|
|
60
|
+
const diagnostics = [...packDiagnostics, ...r.diagnostics];
|
|
61
|
+
if (wantJson) {
|
|
62
|
+
process.stdout.write(asJson({
|
|
63
|
+
ok: true,
|
|
64
|
+
manifest: r.manifest,
|
|
65
|
+
filesScanned: r.filesScanned,
|
|
66
|
+
durationMs: r.durationMs,
|
|
67
|
+
diagnostics,
|
|
68
|
+
packExtractors: packPackages,
|
|
69
|
+
}) + '\n');
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
process.stdout.write(header('Framework index'));
|
|
73
|
+
process.stdout.write(kv('schema', r.manifest.schema) + '\n');
|
|
74
|
+
process.stdout.write(kv('frameworks', r.manifest.frameworks.join(', ')) + '\n');
|
|
75
|
+
process.stdout.write(kv('files scanned', String(r.filesScanned)) + '\n');
|
|
76
|
+
for (const [framework, count] of Object.entries(r.manifest.countsByFramework)) {
|
|
77
|
+
process.stdout.write(kv(`entities (${framework})`, String(count)) + '\n');
|
|
78
|
+
}
|
|
79
|
+
if (packPackages.length > 0) {
|
|
80
|
+
process.stdout.write(kv('pack extractors', packPackages.join(', ')) + '\n');
|
|
81
|
+
}
|
|
82
|
+
process.stdout.write(kv('duration', `${r.durationMs}ms`) + '\n');
|
|
83
|
+
for (const d of diagnostics.slice(0, 5))
|
|
84
|
+
process.stdout.write(`! ${d}\n`);
|
|
85
|
+
return 0;
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
const msg = e.message;
|
|
89
|
+
if (wantJson) {
|
|
90
|
+
process.stdout.write(asJson({ ok: false, error: msg, nextCommand: 'shrk graph index' }) + '\n');
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
process.stderr.write(msg + '\n');
|
|
94
|
+
}
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async function runStatus(args) {
|
|
99
|
+
const cwd = resolveCwd(args);
|
|
100
|
+
const wantJson = flagBool(args, 'json');
|
|
101
|
+
const missing = FrameworkQueryApi.missingDescription(cwd);
|
|
102
|
+
if (missing) {
|
|
103
|
+
if (wantJson) {
|
|
104
|
+
process.stdout.write(asJson({ ok: false, state: 'missing', message: missing, nextCommand: 'shrk framework index' }) + '\n');
|
|
105
|
+
return 1;
|
|
106
|
+
}
|
|
107
|
+
process.stderr.write(missing + '\n');
|
|
108
|
+
return 1;
|
|
109
|
+
}
|
|
110
|
+
const api = FrameworkQueryApi.fromStore(cwd);
|
|
111
|
+
const m = api.manifest();
|
|
112
|
+
if (wantJson) {
|
|
113
|
+
process.stdout.write(asJson({ ok: true, manifest: m }) + '\n');
|
|
114
|
+
return 0;
|
|
115
|
+
}
|
|
116
|
+
process.stdout.write(header('Framework status'));
|
|
117
|
+
process.stdout.write(kv('schema', m.schema) + '\n');
|
|
118
|
+
process.stdout.write(kv('frameworks', m.frameworks.join(', ')) + '\n');
|
|
119
|
+
for (const [k, v] of Object.entries(m.countsBySubtype)) {
|
|
120
|
+
process.stdout.write(kv(` ${k}`, String(v)) + '\n');
|
|
121
|
+
}
|
|
122
|
+
process.stdout.write(kv('last built', m.lastBuiltAt) + '\n');
|
|
123
|
+
return 0;
|
|
124
|
+
}
|
|
125
|
+
async function runList(args) {
|
|
126
|
+
const cwd = resolveCwd(args);
|
|
127
|
+
const wantJson = flagBool(args, 'json');
|
|
128
|
+
const missing = FrameworkQueryApi.missingDescription(cwd);
|
|
129
|
+
if (missing) {
|
|
130
|
+
process.stderr.write(missing + '\n');
|
|
131
|
+
return 1;
|
|
132
|
+
}
|
|
133
|
+
const api = FrameworkQueryApi.fromStore(cwd);
|
|
134
|
+
const framework = flagString(args, 'framework');
|
|
135
|
+
const subtype = flagString(args, 'subtype');
|
|
136
|
+
const file = flagString(args, 'file');
|
|
137
|
+
const limit = Number(flagString(args, 'limit') ?? '50');
|
|
138
|
+
const entities = api.list({
|
|
139
|
+
...(framework ? { framework } : {}),
|
|
140
|
+
...(subtype ? { subtype } : {}),
|
|
141
|
+
...(file ? { file } : {}),
|
|
142
|
+
limit,
|
|
143
|
+
});
|
|
144
|
+
if (wantJson) {
|
|
145
|
+
process.stdout.write(asJson({
|
|
146
|
+
schema: 'sharkcraft.framework-list/v1',
|
|
147
|
+
filters: { framework, subtype, file },
|
|
148
|
+
total: entities.length,
|
|
149
|
+
entities: entities.map((n) => ({ id: n.id, label: n.label, path: n.path, data: n.data })),
|
|
150
|
+
}) + '\n');
|
|
151
|
+
return 0;
|
|
152
|
+
}
|
|
153
|
+
process.stdout.write(header('Framework entities'));
|
|
154
|
+
process.stdout.write(kv('total', String(entities.length)) + '\n');
|
|
155
|
+
for (const n of entities) {
|
|
156
|
+
process.stdout.write(` ${n.data?.['framework'] ?? '?'}:${n.data?.['subtype'] ?? '?'} ${n.label} ${n.path ?? ''}\n`);
|
|
157
|
+
}
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
160
|
+
async function runRoutes(args) {
|
|
161
|
+
const cwd = resolveCwd(args);
|
|
162
|
+
const wantJson = flagBool(args, 'json');
|
|
163
|
+
const missing = FrameworkQueryApi.missingDescription(cwd);
|
|
164
|
+
if (missing) {
|
|
165
|
+
process.stderr.write(missing + '\n');
|
|
166
|
+
return 1;
|
|
167
|
+
}
|
|
168
|
+
const api = FrameworkQueryApi.fromStore(cwd);
|
|
169
|
+
const routes = api.routes();
|
|
170
|
+
if (wantJson) {
|
|
171
|
+
process.stdout.write(asJson({ schema: 'sharkcraft.framework-routes/v1', total: routes.length, routes }) + '\n');
|
|
172
|
+
return 0;
|
|
173
|
+
}
|
|
174
|
+
process.stdout.write(header('NestJS routes'));
|
|
175
|
+
process.stdout.write(kv('total', String(routes.length)) + '\n');
|
|
176
|
+
for (const r of routes.slice(0, 100)) {
|
|
177
|
+
process.stdout.write(` ${r.method.padEnd(6)} ${r.path.padEnd(36)} → ${r.handler} (${r.file})\n`);
|
|
178
|
+
}
|
|
179
|
+
return 0;
|
|
180
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ICommandHandler } from '../command-registry.js';
|
|
2
|
+
/**
|
|
3
|
+
* `shrk gate` — run all code-intelligence quality gates and emit one
|
|
4
|
+
* pass/fail report. Designed as the single command CI should call
|
|
5
|
+
* before merge.
|
|
6
|
+
*
|
|
7
|
+
* Exit codes:
|
|
8
|
+
* - 0 if overall status is `pass` (no failures, no warnings)
|
|
9
|
+
* - 0 if overall is `warn` (default — opt-in to fail via --strict)
|
|
10
|
+
* - 1 if overall is `fail`
|
|
11
|
+
*
|
|
12
|
+
* Pass `--strict` to treat `warn` as failure.
|
|
13
|
+
*/
|
|
14
|
+
export declare const gateCommand: ICommandHandler;
|
|
15
|
+
//# sourceMappingURL=gate.command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gate.command.d.ts","sourceRoot":"","sources":["../../src/commands/gate.command.ts"],"names":[],"mappings":"AAOA,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAGhC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,EAAE,eA+FzB,CAAC"}
|