@shrkcrft/cli 0.1.0-alpha.7 → 0.1.0-alpha.8
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/commands/command-catalog.d.ts +7 -3
- package/dist/commands/command-catalog.d.ts.map +1 -1
- package/dist/commands/command-catalog.js +112 -33
- package/dist/commands/commands.command.d.ts.map +1 -1
- package/dist/commands/commands.command.js +4 -4
- package/dist/commands/constructs.command.d.ts.map +1 -1
- package/dist/commands/constructs.command.js +5 -22
- 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 +34 -2
- package/dist/commands/export.command.d.ts.map +1 -1
- package/dist/commands/export.command.js +76 -3
- 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 +74 -16
- package/dist/commands/helper.command.js +1 -1
- package/dist/commands/import.command.d.ts.map +1 -1
- package/dist/commands/import.command.js +121 -5
- package/dist/commands/init.command.d.ts.map +1 -1
- package/dist/commands/init.command.js +151 -7
- 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 +2 -26
- package/dist/commands/profiles.command.js +4 -4
- package/dist/commands/release.command.js +13 -13
- package/dist/commands/search.command.js +1 -1
- package/dist/commands/task-context.command.js +0 -16
- 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/main.d.ts.map +1 -1
- package/dist/main.js +104 -11
- package/package.json +20 -20
- 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
|
@@ -236,9 +236,13 @@ export declare function commandLifecycle(e: ICommandCatalogEntry): CommandLifecy
|
|
|
236
236
|
/**
|
|
237
237
|
* Default-help visibility rule. Honors an explicit
|
|
238
238
|
* {@link ICommandCatalogEntry.showInDefaultHelp} when set; otherwise
|
|
239
|
-
*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
239
|
+
* the {@link PRIMARY_VERBS_ALLOWLIST} gates membership in the visible
|
|
240
|
+
* surface (primary + common entries whose top-level verb pays rent
|
|
241
|
+
* make the cut; everything else is hidden from `--full-help`).
|
|
242
|
+
*
|
|
243
|
+
* Deprecated, retired, aliased commands and anything in the
|
|
244
|
+
* {@link R46_OVERLAY} are always hidden regardless of allowlist
|
|
245
|
+
* membership.
|
|
242
246
|
*/
|
|
243
247
|
export declare function defaultShowInHelp(e: ICommandCatalogEntry): boolean;
|
|
244
248
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-catalog.d.ts","sourceRoot":"","sources":["../../src/commands/command-catalog.ts"],"names":[],"mappings":"AAAA,oBAAY,WAAW;IACrB,QAAQ,cAAc;IACtB,iBAAiB,mBAAmB;IACpC,gBAAgB,kBAAkB;IAClC,YAAY,kBAAkB;IAC9B,SAAS,eAAe;IACxB,cAAc,oBAAoB;CACnC;AAED;;;;;;GAMG;AACH,oBAAY,cAAc;IACxB,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,MAAM,WAAW;CAClB;AAED,8BAA8B;AAC9B,oBAAY,eAAe;IACzB,KAAK,UAAU;IACf,KAAK,UAAU;IACf,EAAE,OAAO;IACT,UAAU,gBAAgB;IAC1B,UAAU,eAAe;CAC1B;AAED;;;;;;GAMG;AACH,oBAAY,eAAe;IACzB,KAAK,UAAU;IACf,OAAO,YAAY;IACnB,MAAM,WAAW;IACjB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,MAAM,WAAW;IACjB,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,KAAK,UAAU;IACf,MAAM,WAAW;CAClB;AAED;;;;;;GAMG;AACH,oBAAY,gBAAgB;IAC1B,MAAM,WAAW;IACjB,SAAS,cAAc;IACvB,KAAK,UAAU;IACf,UAAU,eAAe;IACzB,OAAO,YAAY;CACpB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,oBAAY,WAAW;IACrB,IAAI,SAAS;IACb,QAAQ,aAAa;IACrB,YAAY,iBAAiB;CAC9B;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,OAAO,CAAC;IACtB,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B;;;;OAIG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IAC9C,kCAAkC;IAClC,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;OAIG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,0DAA0D;IAC1D,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;;;;;;;;OAWG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,SAAS,oBAAoB,
|
|
1
|
+
{"version":3,"file":"command-catalog.d.ts","sourceRoot":"","sources":["../../src/commands/command-catalog.ts"],"names":[],"mappings":"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"}
|
|
@@ -291,6 +291,15 @@ export const COMMAND_CATALOG = Object.freeze([
|
|
|
291
291
|
surface: CommandSurface.Common,
|
|
292
292
|
taskRole: CommandTaskRole.Validate,
|
|
293
293
|
}),
|
|
294
|
+
entry({
|
|
295
|
+
command: 'diff-check',
|
|
296
|
+
description: 'Self-check current git diff against boundary + import-hygiene rules. Single-call composite for agents to validate edits before declaring done. Read-only.',
|
|
297
|
+
category: 'core',
|
|
298
|
+
safetyLevel: SafetyLevel.ReadOnly,
|
|
299
|
+
mcpAvailable: true,
|
|
300
|
+
surface: CommandSurface.Common,
|
|
301
|
+
taskRole: CommandTaskRole.Validate,
|
|
302
|
+
}),
|
|
294
303
|
entry({
|
|
295
304
|
command: 'review',
|
|
296
305
|
description: 'PR-review packet — changed files, affected rules, missing tests heuristic.',
|
|
@@ -634,6 +643,11 @@ export const COMMAND_CATALOG = Object.freeze([
|
|
|
634
643
|
category: 'task-context',
|
|
635
644
|
safetyLevel: SafetyLevel.ReadOnly,
|
|
636
645
|
mcpAvailable: true,
|
|
646
|
+
// Overlaps with `shrk task` (machine packet) and `shrk recommend`
|
|
647
|
+
// (human routing). Pointing at `task` as canonical so users who
|
|
648
|
+
// hit this via tab-complete see the canonical machine-pipe form.
|
|
649
|
+
overlapsWith: ['task', 'recommend'],
|
|
650
|
+
preferredCommand: 'shrk task "<task>"',
|
|
637
651
|
}),
|
|
638
652
|
entry({
|
|
639
653
|
command: 'validate-change',
|
|
@@ -1007,7 +1021,7 @@ export const COMMAND_CATALOG = Object.freeze([
|
|
|
1007
1021
|
// `commands doctor` is honest.
|
|
1008
1022
|
entry({
|
|
1009
1023
|
command: 'profiles',
|
|
1010
|
-
description: 'List / inspect pack-contributed profiles (
|
|
1024
|
+
description: 'List / inspect pack-contributed profiles (migration, conventions, …). Subcommands: list, get, doctor, search.',
|
|
1011
1025
|
category: 'core',
|
|
1012
1026
|
safetyLevel: SafetyLevel.ReadOnly,
|
|
1013
1027
|
mcpAvailable: true,
|
|
@@ -2316,7 +2330,7 @@ export const COMMAND_CATALOG = Object.freeze([
|
|
|
2316
2330
|
}),
|
|
2317
2331
|
entry({
|
|
2318
2332
|
command: 'release smoke --matrix',
|
|
2319
|
-
description: 'Run smoke scenarios across multiple repo targets (sharkcraft/dogfood/synthetic/
|
|
2333
|
+
description: 'Run smoke scenarios across multiple repo targets (sharkcraft/dogfood/synthetic/consumer).',
|
|
2320
2334
|
category: 'release',
|
|
2321
2335
|
safetyLevel: SafetyLevel.WritesDraftsOnly,
|
|
2322
2336
|
writesFiles: true,
|
|
@@ -2708,34 +2722,6 @@ export const COMMAND_CATALOG = Object.freeze([
|
|
|
2708
2722
|
safetyLevel: SafetyLevel.ReadOnly,
|
|
2709
2723
|
mcpAvailable: true,
|
|
2710
2724
|
}),
|
|
2711
|
-
entry({
|
|
2712
|
-
command: 'plugin rename',
|
|
2713
|
-
description: 'Plan-only plugin rename (profile-driven) — emits replace ops + manual folder rename checklist.',
|
|
2714
|
-
category: 'lifecycle',
|
|
2715
|
-
safetyLevel: SafetyLevel.WritesDraftsOnly,
|
|
2716
|
-
mcpAvailable: true,
|
|
2717
|
-
}),
|
|
2718
|
-
entry({
|
|
2719
|
-
command: 'plugin remove',
|
|
2720
|
-
description: 'Plan-only destructive plugin remove (profile-driven) — destructive; requires human approval.',
|
|
2721
|
-
category: 'lifecycle',
|
|
2722
|
-
safetyLevel: SafetyLevel.WritesDraftsOnly,
|
|
2723
|
-
mcpAvailable: true,
|
|
2724
|
-
}),
|
|
2725
|
-
entry({
|
|
2726
|
-
command: 'plugin lifecycle list',
|
|
2727
|
-
description: 'List plugins detected under the active profile roots and key-table. Read-only.',
|
|
2728
|
-
category: 'lifecycle',
|
|
2729
|
-
safetyLevel: SafetyLevel.ReadOnly,
|
|
2730
|
-
mcpAvailable: false,
|
|
2731
|
-
}),
|
|
2732
|
-
entry({
|
|
2733
|
-
command: 'plugin lifecycle inspect',
|
|
2734
|
-
description: 'Inspect references to a plugin across the profile layers + key-table. Read-only.',
|
|
2735
|
-
category: 'lifecycle',
|
|
2736
|
-
safetyLevel: SafetyLevel.ReadOnly,
|
|
2737
|
-
mcpAvailable: false,
|
|
2738
|
-
}),
|
|
2739
2725
|
entry({
|
|
2740
2726
|
command: 'helper list',
|
|
2741
2727
|
description: 'List available helper plan generators. Read-only.',
|
|
@@ -3137,12 +3123,99 @@ export function commandLifecycle(e) {
|
|
|
3137
3123
|
return CommandLifecycle.Deprecated;
|
|
3138
3124
|
return CommandLifecycle.Active;
|
|
3139
3125
|
}
|
|
3126
|
+
/**
|
|
3127
|
+
* The "Primary verbs" allowlist — top-level commands that pay rent in
|
|
3128
|
+
* the default help surface. Everything else stays callable but is
|
|
3129
|
+
* hidden from `--full-help` and from MCP tool advertising; users find
|
|
3130
|
+
* them via `shrk surface list`, `shrk help <cmd>`, or `--full-help`.
|
|
3131
|
+
*
|
|
3132
|
+
* Picked to cover the canonical agent-flow journeys:
|
|
3133
|
+
* - bootstrap a repo, verify it
|
|
3134
|
+
* - get a brief / context / task packet for an agent
|
|
3135
|
+
* - generate safely (plan → apply → check)
|
|
3136
|
+
* - browse what shrk knows (rules / paths / templates / presets)
|
|
3137
|
+
* - inline the rules into the agent's prompt (export, mcp, dashboard)
|
|
3138
|
+
* - daily operations (review, impact, search, explain, why, surface)
|
|
3139
|
+
*
|
|
3140
|
+
* Anything not in this set falls back to the legacy
|
|
3141
|
+
* surface-tier-based visibility, with deprecated / retired / aliased
|
|
3142
|
+
* commands hidden regardless. Adding a verb here is the explicit
|
|
3143
|
+
* "this is rent-paying" decision.
|
|
3144
|
+
*/
|
|
3145
|
+
const PRIMARY_VERBS_ALLOWLIST = new Set([
|
|
3146
|
+
// Bootstrap
|
|
3147
|
+
'init',
|
|
3148
|
+
'doctor',
|
|
3149
|
+
'inspect',
|
|
3150
|
+
'onboard',
|
|
3151
|
+
'version',
|
|
3152
|
+
'help',
|
|
3153
|
+
'preflight',
|
|
3154
|
+
'self-config',
|
|
3155
|
+
// Per-task
|
|
3156
|
+
'brief',
|
|
3157
|
+
'recommend',
|
|
3158
|
+
'context',
|
|
3159
|
+
'task',
|
|
3160
|
+
'coverage',
|
|
3161
|
+
'explain',
|
|
3162
|
+
'why',
|
|
3163
|
+
'search',
|
|
3164
|
+
'impact',
|
|
3165
|
+
'graph',
|
|
3166
|
+
// Generate code safely
|
|
3167
|
+
'gen',
|
|
3168
|
+
'apply',
|
|
3169
|
+
'check',
|
|
3170
|
+
'quality',
|
|
3171
|
+
'plan',
|
|
3172
|
+
'fix',
|
|
3173
|
+
// Browse / inline
|
|
3174
|
+
'knowledge',
|
|
3175
|
+
'rules',
|
|
3176
|
+
'templates',
|
|
3177
|
+
'paths',
|
|
3178
|
+
'import',
|
|
3179
|
+
'export',
|
|
3180
|
+
// Architecture & quality gates
|
|
3181
|
+
'drift',
|
|
3182
|
+
'safety',
|
|
3183
|
+
// Run for an agent
|
|
3184
|
+
'mcp',
|
|
3185
|
+
'dashboard',
|
|
3186
|
+
// Ops
|
|
3187
|
+
'surface',
|
|
3188
|
+
'presets',
|
|
3189
|
+
'review',
|
|
3190
|
+
'packs',
|
|
3191
|
+
'ci',
|
|
3192
|
+
'commands',
|
|
3193
|
+
]);
|
|
3194
|
+
function topLevelVerb(command) {
|
|
3195
|
+
// Strip leading `--cwd <dir>` / `--<flag>` tokens and angle-bracket
|
|
3196
|
+
// placeholders; the canonical first verb is whatever leads.
|
|
3197
|
+
const tokens = command.split(/\s+/);
|
|
3198
|
+
for (const t of tokens) {
|
|
3199
|
+
if (!t)
|
|
3200
|
+
continue;
|
|
3201
|
+
if (t.startsWith('--') || t.startsWith('-'))
|
|
3202
|
+
continue;
|
|
3203
|
+
if (t.startsWith('<') || t.startsWith('"'))
|
|
3204
|
+
continue;
|
|
3205
|
+
return t;
|
|
3206
|
+
}
|
|
3207
|
+
return command;
|
|
3208
|
+
}
|
|
3140
3209
|
/**
|
|
3141
3210
|
* Default-help visibility rule. Honors an explicit
|
|
3142
3211
|
* {@link ICommandCatalogEntry.showInDefaultHelp} when set; otherwise
|
|
3143
|
-
*
|
|
3144
|
-
*
|
|
3145
|
-
*
|
|
3212
|
+
* the {@link PRIMARY_VERBS_ALLOWLIST} gates membership in the visible
|
|
3213
|
+
* surface (primary + common entries whose top-level verb pays rent
|
|
3214
|
+
* make the cut; everything else is hidden from `--full-help`).
|
|
3215
|
+
*
|
|
3216
|
+
* Deprecated, retired, aliased commands and anything in the
|
|
3217
|
+
* {@link R46_OVERLAY} are always hidden regardless of allowlist
|
|
3218
|
+
* membership.
|
|
3146
3219
|
*/
|
|
3147
3220
|
export function defaultShowInHelp(e) {
|
|
3148
3221
|
if (e.showInDefaultHelp !== undefined)
|
|
@@ -3155,6 +3228,12 @@ export function defaultShowInHelp(e) {
|
|
|
3155
3228
|
return false;
|
|
3156
3229
|
if (lc === CommandLifecycle.Alias)
|
|
3157
3230
|
return false;
|
|
3231
|
+
// Allowlist gate — the top-level verb must be one of the ~30
|
|
3232
|
+
// rent-paying verbs. Subcommands of an allowlisted verb inherit
|
|
3233
|
+
// visibility (so `check boundaries` shows under `check`).
|
|
3234
|
+
const verb = topLevelVerb(e.command);
|
|
3235
|
+
if (!PRIMARY_VERBS_ALLOWLIST.has(verb))
|
|
3236
|
+
return false;
|
|
3158
3237
|
const surface = commandSurface(e);
|
|
3159
3238
|
return surface === CommandSurface.Primary || surface === CommandSurface.Common;
|
|
3160
3239
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.command.d.ts","sourceRoot":"","sources":["../../src/commands/commands.command.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"commands.command.d.ts","sourceRoot":"","sources":["../../src/commands/commands.command.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { flagBool, flagString
|
|
1
|
+
import { flagBool, flagString } from "../command-registry.js";
|
|
2
2
|
import { asJson, header } from "../output/format-output.js";
|
|
3
|
-
import { COMMAND_CATALOG, CommandLifecycle,
|
|
4
|
-
import { buildCommandTaxonomy, buildPrimaryCommandsReport, renderCommandTaxonomyMarkdown, renderCommandTaxonomyText, renderPrimaryCommandsText
|
|
3
|
+
import { buildCommandSafetyMatrix, COMMAND_CATALOG, CommandLifecycle, commandLifecycle, CommandSurface, commandSurface, commandTaskRole, defaultShowInHelp, R46_OVERLAY, renderCommandSafetyMatrixMarkdown, SafetyLevel } from "./command-catalog.js";
|
|
4
|
+
import { buildCommandTaxonomy, buildPrimaryCommandsReport, renderCommandTaxonomyMarkdown, renderCommandTaxonomyText, renderPrimaryCommandsText } from '@shrkcrft/inspector';
|
|
5
5
|
export function makeCommandsCommand(registry) {
|
|
6
6
|
return {
|
|
7
7
|
name: 'commands',
|
|
@@ -589,7 +589,7 @@ export function buildCommandsUxReport() {
|
|
|
589
589
|
// issue is clearly structural (none of these currently rise to error).
|
|
590
590
|
for (const e of COMMAND_CATALOG) {
|
|
591
591
|
const surface = commandSurface(e);
|
|
592
|
-
// (a) primary commands should declare an audience (so
|
|
592
|
+
// (a) primary commands should declare an audience (so consumers can see
|
|
593
593
|
// who the command is for at a glance).
|
|
594
594
|
if (surface === CommandSurface.Primary && (!e.intendedAudience || e.intendedAudience.length === 0)) {
|
|
595
595
|
issues.push({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constructs.command.d.ts","sourceRoot":"","sources":["../../src/commands/constructs.command.ts"],"names":[],"mappings":"AA6BA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAahC,eAAO,MAAM,qBAAqB,EAAE,eAqBnC,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,eAoDlC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,eA8DpC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,
|
|
1
|
+
{"version":3,"file":"constructs.command.d.ts","sourceRoot":"","sources":["../../src/commands/constructs.command.ts"],"names":[],"mappings":"AA6BA,OAAO,EAKL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAahC,eAAO,MAAM,qBAAqB,EAAE,eAqBnC,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,eAoDlC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,eA8DpC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,eAoErC,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,eAmCtC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,eAwBpC,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,eAiClC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,eAiBrC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,eAiBrC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,eA4BrC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,eA8BrC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,eA0DpC,CAAC;AAEF,eAAO,MAAM,sBAAsB,EAAE,eA0LpC,CAAC"}
|
|
@@ -91,7 +91,7 @@ export const constructsGetCommand = {
|
|
|
91
91
|
};
|
|
92
92
|
export const constructsTraceCommand = {
|
|
93
93
|
name: 'trace',
|
|
94
|
-
description: 'Trace all files / publicApi / events / tokens belonging to a construct. --deep adds related/
|
|
94
|
+
description: 'Trace all files / publicApi / events / tokens belonging to a construct. --deep adds related / test pointers when present.',
|
|
95
95
|
usage: 'shrk constructs trace <id> [--deep] [--json]',
|
|
96
96
|
async run(args) {
|
|
97
97
|
const id = args.positional[0];
|
|
@@ -118,7 +118,7 @@ export const constructsTraceCommand = {
|
|
|
118
118
|
? {
|
|
119
119
|
related: relatedAll,
|
|
120
120
|
tags: c.tags ?? [],
|
|
121
|
-
registryHints: c.tags?.filter((t) => /(registry|
|
|
121
|
+
registryHints: c.tags?.filter((t) => /(registry|barrel)/i.test(t)) ?? [],
|
|
122
122
|
}
|
|
123
123
|
: undefined;
|
|
124
124
|
if (flagBool(args, 'json')) {
|
|
@@ -175,28 +175,11 @@ export const constructsImpactCommand = {
|
|
|
175
175
|
}
|
|
176
176
|
const trace = traceConstruct(c);
|
|
177
177
|
/**
|
|
178
|
-
* Registry touch-points are inferred
|
|
179
|
-
*
|
|
180
|
-
*
|
|
178
|
+
* Registry touch-points are not inferred — pack-contributed
|
|
179
|
+
* touch-point hints can be added in the future via the convention
|
|
180
|
+
* registry.
|
|
181
181
|
*/
|
|
182
182
|
const registryTouchPoints = [];
|
|
183
|
-
try {
|
|
184
|
-
const { listPluginLifecycleProfiles } = await import('@shrkcrft/inspector');
|
|
185
|
-
const profiles = await listPluginLifecycleProfiles(inspection);
|
|
186
|
-
for (const entry of profiles) {
|
|
187
|
-
const p = entry.profile;
|
|
188
|
-
if (c.tags?.some((t) => /plugin-key|key-table/i.test(t)) && p.keyTable) {
|
|
189
|
-
registryTouchPoints.push(p.keyTable.path);
|
|
190
|
-
}
|
|
191
|
-
if (c.tags?.some((t) => /barrel|public-api/i.test(t)) && p.barrels) {
|
|
192
|
-
for (const b of p.barrels)
|
|
193
|
-
registryTouchPoints.push(b.path);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
catch {
|
|
198
|
-
// Best-effort enrichment; absence of profiles just means no touch-points.
|
|
199
|
-
}
|
|
200
183
|
const verificationCommands = [
|
|
201
184
|
'shrk check boundaries --changed-only',
|
|
202
185
|
'shrk doctor',
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `shrk diff-check` — agent self-validation after edits.
|
|
3
|
+
*
|
|
4
|
+
* The story this command tells:
|
|
5
|
+
* 1. An AI agent (Claude Code, Cursor, etc.) makes some file changes.
|
|
6
|
+
* 2. Before declaring "done", the agent runs `shrk diff-check`.
|
|
7
|
+
* 3. The command scopes both the boundary check and the
|
|
8
|
+
* import-hygiene check to only the files the agent touched in the
|
|
9
|
+
* current git diff.
|
|
10
|
+
* 4. The output is a single agent-friendly JSON envelope with a
|
|
11
|
+
* verdict (ok / warnings / errors) and a one-line next action.
|
|
12
|
+
*
|
|
13
|
+
* Why a new command instead of "just run `shrk check boundaries
|
|
14
|
+
* --changed-only` and `shrk check imports --changed-only`":
|
|
15
|
+
*
|
|
16
|
+
* - One command instead of two — agents reliably run the *one* tool
|
|
17
|
+
* they're told to run; chained-command workflows get skipped.
|
|
18
|
+
* - One verdict — no need to OR two separate JSON outputs.
|
|
19
|
+
* - Stable, narrow schema — designed for agent consumption, not
|
|
20
|
+
* human terminals. Won't grow flags over time.
|
|
21
|
+
* - Concrete `nextAction` line — the agent knows exactly what to do
|
|
22
|
+
* next (declare done, fix N things, or re-run after a manual fix).
|
|
23
|
+
*
|
|
24
|
+
* This is a pure composer — all real logic stays in
|
|
25
|
+
* `@shrkcrft/inspector` and `@shrkcrft/boundaries`. We just stitch
|
|
26
|
+
* their outputs together with consistent scoping.
|
|
27
|
+
*/
|
|
28
|
+
import { type ICommandHandler } from '../command-registry.js';
|
|
29
|
+
export declare const diffCheckCommand: ICommandHandler;
|
|
30
|
+
//# sourceMappingURL=diff-check.command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-check.command.d.ts","sourceRoot":"","sources":["../../src/commands/diff-check.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAcH,OAAO,EAIL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AA0GhC,eAAO,MAAM,gBAAgB,EAAE,eAiI9B,CAAC"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `shrk diff-check` — agent self-validation after edits.
|
|
3
|
+
*
|
|
4
|
+
* The story this command tells:
|
|
5
|
+
* 1. An AI agent (Claude Code, Cursor, etc.) makes some file changes.
|
|
6
|
+
* 2. Before declaring "done", the agent runs `shrk diff-check`.
|
|
7
|
+
* 3. The command scopes both the boundary check and the
|
|
8
|
+
* import-hygiene check to only the files the agent touched in the
|
|
9
|
+
* current git diff.
|
|
10
|
+
* 4. The output is a single agent-friendly JSON envelope with a
|
|
11
|
+
* verdict (ok / warnings / errors) and a one-line next action.
|
|
12
|
+
*
|
|
13
|
+
* Why a new command instead of "just run `shrk check boundaries
|
|
14
|
+
* --changed-only` and `shrk check imports --changed-only`":
|
|
15
|
+
*
|
|
16
|
+
* - One command instead of two — agents reliably run the *one* tool
|
|
17
|
+
* they're told to run; chained-command workflows get skipped.
|
|
18
|
+
* - One verdict — no need to OR two separate JSON outputs.
|
|
19
|
+
* - Stable, narrow schema — designed for agent consumption, not
|
|
20
|
+
* human terminals. Won't grow flags over time.
|
|
21
|
+
* - Concrete `nextAction` line — the agent knows exactly what to do
|
|
22
|
+
* next (declare done, fix N things, or re-run after a manual fix).
|
|
23
|
+
*
|
|
24
|
+
* This is a pure composer — all real logic stays in
|
|
25
|
+
* `@shrkcrft/inspector` and `@shrkcrft/boundaries`. We just stitch
|
|
26
|
+
* their outputs together with consistent scoping.
|
|
27
|
+
*/
|
|
28
|
+
import { buildImportHygieneReport, filterViolationsToChangedScope, inspectSharkcraft, resolveChangedFiles, } from '@shrkcrft/inspector';
|
|
29
|
+
import { evaluateBoundaries, loadTsconfigPaths, scanImports, } from '@shrkcrft/boundaries';
|
|
30
|
+
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
31
|
+
import { asJson, bullet, header, kv } from "../output/format-output.js";
|
|
32
|
+
const SCHEMA = 'sharkcraft.diff-check/v1';
|
|
33
|
+
function resolveScope(args, cwd) {
|
|
34
|
+
const staged = flagBool(args, 'staged');
|
|
35
|
+
const since = flagString(args, 'since');
|
|
36
|
+
const filesRaw = flagString(args, 'files');
|
|
37
|
+
const files = filesRaw
|
|
38
|
+
? filesRaw.split(',').map((s) => s.trim()).filter((s) => s.length > 0)
|
|
39
|
+
: [];
|
|
40
|
+
if (files.length > 0) {
|
|
41
|
+
return { mode: 'files', options: { projectRoot: cwd, files } };
|
|
42
|
+
}
|
|
43
|
+
if (staged) {
|
|
44
|
+
return { mode: 'staged', options: { projectRoot: cwd, staged: true } };
|
|
45
|
+
}
|
|
46
|
+
if (since) {
|
|
47
|
+
return { mode: 'since', options: { projectRoot: cwd, since } };
|
|
48
|
+
}
|
|
49
|
+
// Default: worktree (== `--changed-only` from `shrk check boundaries`).
|
|
50
|
+
return {
|
|
51
|
+
mode: 'worktree',
|
|
52
|
+
options: { projectRoot: cwd, includeWorktree: true },
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function deriveVerdict(env) {
|
|
56
|
+
const bErr = env.boundaries.counts.error;
|
|
57
|
+
const bWarn = env.boundaries.counts.warning;
|
|
58
|
+
const iErr = env.imports.verdict === 'errors' ? (env.imports.counts.error ?? env.imports.findings.length) : 0;
|
|
59
|
+
const iWarn = env.imports.verdict === 'warnings' ? (env.imports.counts.warning ?? env.imports.findings.length) : 0;
|
|
60
|
+
if (env.scope.fileCount === 0) {
|
|
61
|
+
return {
|
|
62
|
+
verdict: 'ok',
|
|
63
|
+
summary: 'No files changed in the current diff scope.',
|
|
64
|
+
nextAction: 'Nothing to check. If you expected changes, verify your `--staged` / `--since <ref>` flag or save your edits first.',
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (bErr > 0 || iErr > 0) {
|
|
68
|
+
const parts = [];
|
|
69
|
+
if (bErr > 0)
|
|
70
|
+
parts.push(`${bErr} boundary violation${bErr === 1 ? '' : 's'}`);
|
|
71
|
+
if (iErr > 0)
|
|
72
|
+
parts.push(`${iErr} import-hygiene error${iErr === 1 ? '' : 's'}`);
|
|
73
|
+
return {
|
|
74
|
+
verdict: 'errors',
|
|
75
|
+
summary: `Diff fails the gate: ${parts.join(', ')}.`,
|
|
76
|
+
nextAction: 'Fix every error in `boundaries.violations` and `imports.findings` (look at each entry\'s `suggestedFix` line), then re-run `shrk diff-check`.',
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (bWarn > 0 || iWarn > 0) {
|
|
80
|
+
const parts = [];
|
|
81
|
+
if (bWarn > 0)
|
|
82
|
+
parts.push(`${bWarn} boundary warning${bWarn === 1 ? '' : 's'}`);
|
|
83
|
+
if (iWarn > 0)
|
|
84
|
+
parts.push(`${iWarn} import-hygiene warning${iWarn === 1 ? '' : 's'}`);
|
|
85
|
+
return {
|
|
86
|
+
verdict: 'warnings',
|
|
87
|
+
summary: `Diff passes the gate with ${parts.join(', ')}.`,
|
|
88
|
+
nextAction: 'Safe to declare done. Review warnings if the diff touches a sensitive area; otherwise these are non-blocking.',
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
verdict: 'ok',
|
|
93
|
+
summary: `Diff passes the gate (${env.scope.fileCount} file${env.scope.fileCount === 1 ? '' : 's'}, 0 violations).`,
|
|
94
|
+
nextAction: 'Safe to declare done.',
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export const diffCheckCommand = {
|
|
98
|
+
name: 'diff-check',
|
|
99
|
+
description: 'Self-check the current git diff against this project\'s boundary + import-hygiene rules. Single-call composite of `shrk check boundaries --changed-only` + `shrk check imports --changed-only`, with one verdict and one nextAction line. Designed for AI agents to run after editing — pass --json for the structured envelope.',
|
|
100
|
+
usage: 'shrk [--cwd <dir>] diff-check [--staged | --since <ref> | --files a.ts,b.ts] [--json]',
|
|
101
|
+
async run(args) {
|
|
102
|
+
const cwd = resolveCwd(args);
|
|
103
|
+
const wantJson = flagBool(args, 'json');
|
|
104
|
+
const { mode, options: scopeOptions } = resolveScope(args, cwd);
|
|
105
|
+
// 1. Resolve the changed file set once. Both engines re-use it.
|
|
106
|
+
const changed = resolveChangedFiles(scopeOptions);
|
|
107
|
+
const changedFiles = changed.files;
|
|
108
|
+
// 2. Boundary engine — only if rules exist.
|
|
109
|
+
const inspection = await inspectSharkcraft({ cwd });
|
|
110
|
+
const rules = inspection.boundaryRegistry.list();
|
|
111
|
+
let boundaryBlock = {
|
|
112
|
+
ran: false,
|
|
113
|
+
rulesEvaluated: 0,
|
|
114
|
+
counts: { error: 0, warning: 0, info: 0 },
|
|
115
|
+
violations: [],
|
|
116
|
+
};
|
|
117
|
+
if (rules.length > 0 && changedFiles.length > 0) {
|
|
118
|
+
const scan = scanImports({ projectRoot: cwd });
|
|
119
|
+
const tsconfigPaths = loadTsconfigPaths(cwd);
|
|
120
|
+
const evalResult = evaluateBoundaries(scan, rules, {
|
|
121
|
+
...(tsconfigPaths.aliases.size > 0 ? { tsconfigPaths } : {}),
|
|
122
|
+
});
|
|
123
|
+
const filtered = filterViolationsToChangedScope(evalResult.violations, scopeOptions);
|
|
124
|
+
boundaryBlock = {
|
|
125
|
+
ran: true,
|
|
126
|
+
rulesEvaluated: evalResult.rulesEvaluated,
|
|
127
|
+
counts: {
|
|
128
|
+
error: filtered.includedViolations.filter((v) => v.severity === 'error').length,
|
|
129
|
+
warning: filtered.includedViolations.filter((v) => v.severity === 'warning').length,
|
|
130
|
+
info: filtered.includedViolations.filter((v) => v.severity === 'info').length,
|
|
131
|
+
},
|
|
132
|
+
violations: filtered.includedViolations,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
else if (rules.length > 0 && changedFiles.length === 0) {
|
|
136
|
+
boundaryBlock = { ...boundaryBlock, ran: true, rulesEvaluated: rules.length };
|
|
137
|
+
}
|
|
138
|
+
// 3. Import-hygiene engine — always runs, but scoped to changed files.
|
|
139
|
+
let importsBlock = {
|
|
140
|
+
ran: false,
|
|
141
|
+
verdict: 'skipped',
|
|
142
|
+
counts: {},
|
|
143
|
+
findings: [],
|
|
144
|
+
};
|
|
145
|
+
if (changedFiles.length > 0) {
|
|
146
|
+
const report = buildImportHygieneReport(cwd, { files: changedFiles });
|
|
147
|
+
importsBlock = {
|
|
148
|
+
ran: true,
|
|
149
|
+
verdict: report.verdict,
|
|
150
|
+
counts: report.counts ?? {},
|
|
151
|
+
findings: report.findings,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
// 4. Build envelope + derive verdict.
|
|
155
|
+
const partial = {
|
|
156
|
+
schema: SCHEMA,
|
|
157
|
+
generatedAt: new Date().toISOString(),
|
|
158
|
+
scope: {
|
|
159
|
+
mode,
|
|
160
|
+
files: changedFiles,
|
|
161
|
+
fileCount: changedFiles.length,
|
|
162
|
+
},
|
|
163
|
+
boundaries: boundaryBlock,
|
|
164
|
+
imports: importsBlock,
|
|
165
|
+
};
|
|
166
|
+
const { verdict, summary, nextAction } = deriveVerdict(partial);
|
|
167
|
+
const envelope = { ...partial, verdict, summary, nextAction };
|
|
168
|
+
// 5. Render.
|
|
169
|
+
if (wantJson) {
|
|
170
|
+
process.stdout.write(asJson(envelope) + '\n');
|
|
171
|
+
return verdict === 'errors' ? 1 : 0;
|
|
172
|
+
}
|
|
173
|
+
process.stdout.write(header('Diff check'));
|
|
174
|
+
process.stdout.write(kv('scope', `${envelope.scope.mode} (${envelope.scope.fileCount} file${envelope.scope.fileCount === 1 ? '' : 's'})`) + '\n');
|
|
175
|
+
process.stdout.write(kv('boundaries', envelope.boundaries.ran
|
|
176
|
+
? `${envelope.boundaries.counts.error} errors, ${envelope.boundaries.counts.warning} warnings`
|
|
177
|
+
: '(no rules configured or no scoped files)') + '\n');
|
|
178
|
+
process.stdout.write(kv('imports', envelope.imports.ran
|
|
179
|
+
? `verdict=${envelope.imports.verdict} (${envelope.imports.findings.length} finding${envelope.imports.findings.length === 1 ? '' : 's'})`
|
|
180
|
+
: '(no scoped files)') + '\n');
|
|
181
|
+
process.stdout.write(kv('verdict', envelope.verdict) + '\n');
|
|
182
|
+
process.stdout.write('\n');
|
|
183
|
+
process.stdout.write(envelope.summary + '\n');
|
|
184
|
+
if (envelope.boundaries.violations.length > 0) {
|
|
185
|
+
process.stdout.write('\nBoundary violations:\n');
|
|
186
|
+
for (const v of envelope.boundaries.violations.slice(0, 10)) {
|
|
187
|
+
const file = String(v.file ?? '');
|
|
188
|
+
const rule = String(v.ruleId ?? '');
|
|
189
|
+
const fix = v.suggestedFix ? ` — ${String(v.suggestedFix)}` : '';
|
|
190
|
+
process.stdout.write(bullet(`${rule} in ${file}${fix}`) + '\n');
|
|
191
|
+
}
|
|
192
|
+
if (envelope.boundaries.violations.length > 10) {
|
|
193
|
+
process.stdout.write(` … and ${envelope.boundaries.violations.length - 10} more (pass --json for full list).\n`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (envelope.imports.findings.length > 0) {
|
|
197
|
+
process.stdout.write('\nImport findings:\n');
|
|
198
|
+
for (const f of envelope.imports.findings.slice(0, 10)) {
|
|
199
|
+
const file = String(f.path ?? f.file ?? '');
|
|
200
|
+
const kind = String(f.kind ?? '');
|
|
201
|
+
process.stdout.write(bullet(`${kind} in ${file}`) + '\n');
|
|
202
|
+
}
|
|
203
|
+
if (envelope.imports.findings.length > 10) {
|
|
204
|
+
process.stdout.write(` … and ${envelope.imports.findings.length - 10} more (pass --json for full list).\n`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
process.stdout.write(`\nNext: ${envelope.nextAction}\n`);
|
|
208
|
+
return verdict === 'errors' ? 1 : 0;
|
|
209
|
+
},
|
|
210
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"doctor.command.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.command.ts"],"names":[],"mappings":"AAqBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AA0JhC,eAAO,MAAM,aAAa,EAAE,eAW3B,CAAC;
|
|
1
|
+
{"version":3,"file":"doctor.command.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.command.ts"],"names":[],"mappings":"AAqBA,OAAO,EAML,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AA0JhC,eAAO,MAAM,aAAa,EAAE,eAW3B,CAAC;AA+bF,eAAO,MAAM,qBAAqB,EAAE,eAmCnC,CAAC;AAuDF,eAAO,MAAM,yBAAyB,EAAE,eAavC,CAAC;AAIF,eAAO,MAAM,wBAAwB,EAAE,eA2CtC,CAAC;AAgCF,eAAO,MAAM,6BAA6B,EAAE,eAa3C,CAAC"}
|
|
@@ -460,10 +460,27 @@ async function doctorCommandImpl(args) {
|
|
|
460
460
|
if (!inspection.sharkcraftDir) {
|
|
461
461
|
process.stdout.write('\nNothing here yet — try `shrk init --zero-config` to detect your stack and pick a preset.\n');
|
|
462
462
|
}
|
|
463
|
-
|
|
463
|
+
// Honest binary verdicts come first — yes/no on the two questions
|
|
464
|
+
// users actually want answered (can I let an agent write here? can
|
|
465
|
+
// I let an agent read here?). The 0..100 score follows, scoped to
|
|
466
|
+
// the workspace shape so libraries don't get dinged for "no
|
|
467
|
+
// pipelines" and apps don't get dinged for "no published API".
|
|
468
|
+
process.stdout.write(`\nShape: ${report.workspaceShape.label}` +
|
|
469
|
+
` (score counts ${report.dimensions.filter((d) => d.applies === 'core').length} of ${report.dimensions.length} dimensions)\n`);
|
|
470
|
+
process.stdout.write(` ${report.verdicts.readyForAgentReads ? '✓' : '✗'} Ready for agent reads (context / task lookups)\n`);
|
|
471
|
+
process.stdout.write(` ${report.verdicts.readyForAgentWrites ? '✓' : '✗'} Ready for agent writes (apply / generate)\n`);
|
|
472
|
+
if (report.verdicts.blockers.length > 0) {
|
|
473
|
+
process.stdout.write(` Blockers:\n`);
|
|
474
|
+
for (const b of report.verdicts.blockers) {
|
|
475
|
+
process.stdout.write(` • ${b}\n`);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
process.stdout.write(`\nAI-readiness: ${report.score} / 100 (${report.grade}, shape-aware)\n`);
|
|
464
479
|
if (report.topRecommendations.length) {
|
|
465
480
|
// Keep the default doctor output short: top 3 recommendations,
|
|
466
|
-
// pass `--verbose` for the full list.
|
|
481
|
+
// pass `--verbose` for the full list. Recommendations only fire
|
|
482
|
+
// from `core` dimensions now — n/a-for-shape dimensions stop
|
|
483
|
+
// generating the misleading "add a pipeline" advice for libraries.
|
|
467
484
|
const verbose = flagBool(args, 'verbose');
|
|
468
485
|
const visible = verbose ? report.topRecommendations : report.topRecommendations.slice(0, 3);
|
|
469
486
|
process.stdout.write(`Top recommendations${verbose ? '' : ` (top ${visible.length})`}:\n`);
|
|
@@ -473,6 +490,21 @@ async function doctorCommandImpl(args) {
|
|
|
473
490
|
process.stdout.write(` … (${report.topRecommendations.length - visible.length} more — pass --verbose to see all)\n`);
|
|
474
491
|
}
|
|
475
492
|
}
|
|
493
|
+
// Surface N/A dimensions when --show-na is passed, so users can see
|
|
494
|
+
// what was deliberately skipped and disagree if they want to.
|
|
495
|
+
if (flagBool(args, 'show-na')) {
|
|
496
|
+
const skipped = report.dimensions.filter((d) => d.applies !== 'core');
|
|
497
|
+
if (skipped.length > 0) {
|
|
498
|
+
process.stdout.write(`\nNot counted in score (${skipped.length} dimensions):\n`);
|
|
499
|
+
for (const d of skipped) {
|
|
500
|
+
const tag = d.applies === 'n/a-for-shape' ? 'n/a' : 'advisory';
|
|
501
|
+
process.stdout.write(` [${tag}] ${d.title}: ${d.note}`);
|
|
502
|
+
if (d.appliesReason)
|
|
503
|
+
process.stdout.write(` — ${d.appliesReason}`);
|
|
504
|
+
process.stdout.write('\n');
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
476
508
|
if (strictEval.failed) {
|
|
477
509
|
process.stdout.write(`\nStrict mode: failing because ${strictEval.countedWarnings} warning(s) + ${result.summary.errors} error(s) exist (${strictEval.reason}).\n`);
|
|
478
510
|
}
|
|
@@ -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"}
|