@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
|
@@ -8,7 +8,6 @@ const VALID_KINDS = new Set([
|
|
|
8
8
|
'framework',
|
|
9
9
|
'architecture',
|
|
10
10
|
'enterprise',
|
|
11
|
-
'platform-adopter',
|
|
12
11
|
]);
|
|
13
12
|
/** Pure: compute the file set to write. No IO. */
|
|
14
13
|
export function planPackScaffold(input) {
|
|
@@ -101,12 +100,6 @@ export function planPackScaffold(input) {
|
|
|
101
100
|
body: renderBoundariesAsset(),
|
|
102
101
|
});
|
|
103
102
|
}
|
|
104
|
-
if (input.kind === 'platform-adopter' || input.withExamples) {
|
|
105
|
-
files.push({
|
|
106
|
-
relativePath: 'src/assets/contracts/plugin-contract.example.ts',
|
|
107
|
-
body: renderPluginContractExample(),
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
103
|
if (input.kind === 'enterprise') {
|
|
111
104
|
files.push({
|
|
112
105
|
relativePath: 'docs/review-workflow.md',
|
|
@@ -232,12 +225,6 @@ function renderKnowledgeAsset(input) {
|
|
|
232
225
|
['framework.overview', 'Framework overview', 'High', 'What this framework is for.'],
|
|
233
226
|
]);
|
|
234
227
|
}
|
|
235
|
-
if (input.kind === 'platform-adopter') {
|
|
236
|
-
return knowledgeBody([
|
|
237
|
-
['platform.policy', 'Policy capability', 'Medium', 'Describe the policy/capability model your platform exposes.'],
|
|
238
|
-
['platform.adapter', 'Adapter contract', 'Medium', 'Describe the adapter contract your platform expects.'],
|
|
239
|
-
]);
|
|
240
|
-
}
|
|
241
228
|
return knowledgeBody([
|
|
242
229
|
['pack.overview', 'Pack overview', 'Medium', 'Short overview of what this pack contributes.'],
|
|
243
230
|
]);
|
|
@@ -350,17 +337,6 @@ function renderBoundariesAsset() {
|
|
|
350
337
|
``,
|
|
351
338
|
].join('\n');
|
|
352
339
|
}
|
|
353
|
-
function renderPluginContractExample() {
|
|
354
|
-
return [
|
|
355
|
-
`// Example: an adapter-style plugin contract.`,
|
|
356
|
-
`// Replace with the actual interface your platform expects.`,
|
|
357
|
-
`export interface IPluginCapability {`,
|
|
358
|
-
` id: string;`,
|
|
359
|
-
` invoke(input: unknown): Promise<unknown>;`,
|
|
360
|
-
`}`,
|
|
361
|
-
``,
|
|
362
|
-
].join('\n');
|
|
363
|
-
}
|
|
364
340
|
function renderEnterpriseReviewDocs() {
|
|
365
341
|
return [
|
|
366
342
|
`# Review workflow`,
|
|
@@ -407,11 +383,11 @@ function renderDocsOverview(input, fullName) {
|
|
|
407
383
|
export const packsNewCommand = {
|
|
408
384
|
name: 'new',
|
|
409
385
|
description: 'Scaffold a new SharkCraft pack package (rules / paths / templates / pipelines / presets / boundaries). Dry-run by default — pass --write to materialize. No install, no publish, no overwrite without --force.',
|
|
410
|
-
usage: 'shrk [--cwd <dir>] packs new <name> [--scope @org] [--preset <id>] [--kind generic|framework|architecture|enterprise
|
|
386
|
+
usage: 'shrk [--cwd <dir>] packs new <name> [--scope @org] [--preset <id>] [--kind generic|framework|architecture|enterprise] [--with-examples] [--write] [--force] [--json]',
|
|
411
387
|
async run(args) {
|
|
412
388
|
const name = args.positional[0];
|
|
413
389
|
if (!name) {
|
|
414
|
-
process.stderr.write('Usage: shrk packs new <name> [--scope @org] [--preset <id>] [--kind generic|framework|architecture|enterprise
|
|
390
|
+
process.stderr.write('Usage: shrk packs new <name> [--scope @org] [--preset <id>] [--kind generic|framework|architecture|enterprise] [--with-examples] [--write]\n');
|
|
415
391
|
return 2;
|
|
416
392
|
}
|
|
417
393
|
const cwd = resolveCwd(args);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `shrk profiles ...` — unified read-only surface for all pack-/local-
|
|
3
|
-
* contributed profiles (
|
|
3
|
+
* contributed profiles (migration, and future kinds).
|
|
4
4
|
*/
|
|
5
5
|
import { findProfile, inspectSharkcraft, listProfileIssues, listProfiles, ProfileKind, } from '@shrkcrft/inspector';
|
|
6
6
|
import { flagBool, flagString, resolveCwd, } from "../command-registry.js";
|
|
@@ -15,7 +15,7 @@ function parseKind(value) {
|
|
|
15
15
|
}
|
|
16
16
|
export const profilesListCommand = {
|
|
17
17
|
name: 'list',
|
|
18
|
-
description: 'List all registered profiles (
|
|
18
|
+
description: 'List all registered profiles (migration, ...).',
|
|
19
19
|
usage: 'shrk profiles list [--kind <kind>] [--json]',
|
|
20
20
|
async run(args) {
|
|
21
21
|
const cwd = resolveCwd(args);
|
|
@@ -28,7 +28,7 @@ export const profilesListCommand = {
|
|
|
28
28
|
}
|
|
29
29
|
process.stdout.write(header(`Profiles (${entries.length}${kind ? `, kind=${kind}` : ''})`));
|
|
30
30
|
if (entries.length === 0) {
|
|
31
|
-
process.stdout.write(' (none — contribute via packs:
|
|
31
|
+
process.stdout.write(' (none — contribute via packs: migrationProfileFiles, etc.)\n');
|
|
32
32
|
return 0;
|
|
33
33
|
}
|
|
34
34
|
for (const e of entries) {
|
|
@@ -132,7 +132,7 @@ export const profilesSearchCommand = {
|
|
|
132
132
|
};
|
|
133
133
|
export const profilesCommand = {
|
|
134
134
|
name: 'profiles',
|
|
135
|
-
description: 'List / inspect pack-contributed profiles (
|
|
135
|
+
description: 'List / inspect pack-contributed profiles (migration, conventions, …).',
|
|
136
136
|
usage: 'shrk profiles list|get|doctor|search ...',
|
|
137
137
|
async run(args) {
|
|
138
138
|
const sub = args.positional[0];
|
|
@@ -204,8 +204,8 @@ function resolveSmokeFlags(args) {
|
|
|
204
204
|
const noAssertions = flagBool(args, 'no-assertions');
|
|
205
205
|
return { assertionsEnabled: !noAssertions };
|
|
206
206
|
}
|
|
207
|
-
// Generic
|
|
208
|
-
const BUILTIN_TARGET_IDS = ['sharkcraft', 'dogfood', 'synthetic', '
|
|
207
|
+
// Generic consumer target replaces the previous hardcoded project target.
|
|
208
|
+
const BUILTIN_TARGET_IDS = ['sharkcraft', 'dogfood', 'synthetic', 'consumer'];
|
|
209
209
|
function resolveTargets(cwd, args, requested) {
|
|
210
210
|
const want = (id) => requested.length === 0 || requested.includes(id);
|
|
211
211
|
const out = [];
|
|
@@ -224,19 +224,19 @@ function resolveTargets(cwd, args, requested) {
|
|
|
224
224
|
writeFileSync(nodePath.join(root, 'package.json'), JSON.stringify({ name: 'sharkcraft-synth', version: '0.0.0', type: 'module' }, null, 2), 'utf8');
|
|
225
225
|
out.push({ id: 'synthetic', cwd: root, label: 'synthetic-fixture' });
|
|
226
226
|
}
|
|
227
|
-
if (want('
|
|
228
|
-
const
|
|
229
|
-
process.env['
|
|
227
|
+
if (want('consumer')) {
|
|
228
|
+
const consumerRoot = flagString(args, 'consumer-root') ??
|
|
229
|
+
process.env['SHARKCRAFT_CONSUMER_ROOT'] ??
|
|
230
230
|
'';
|
|
231
|
-
if (
|
|
232
|
-
out.push({ id: '
|
|
231
|
+
if (consumerRoot && existsSync(consumerRoot)) {
|
|
232
|
+
out.push({ id: 'consumer', cwd: consumerRoot, label: `consumer:${consumerRoot}` });
|
|
233
233
|
}
|
|
234
234
|
else {
|
|
235
235
|
out.push({
|
|
236
|
-
id: '
|
|
237
|
-
cwd:
|
|
238
|
-
label: '
|
|
239
|
-
warning: '
|
|
236
|
+
id: 'consumer',
|
|
237
|
+
cwd: consumerRoot || '(unset)',
|
|
238
|
+
label: 'consumer target',
|
|
239
|
+
warning: 'Consumer root not set (pass --consumer-root or set SHARKCRAFT_CONSUMER_ROOT) — skipped.',
|
|
240
240
|
});
|
|
241
241
|
}
|
|
242
242
|
}
|
|
@@ -292,7 +292,7 @@ async function runReleaseSmoke(args) {
|
|
|
292
292
|
* examples/dogfood-target as the fixture.
|
|
293
293
|
* - `synthetic` runs the scenarios that don't depend on a prepared source
|
|
294
294
|
* (unconfigured-repo + pack-authoring).
|
|
295
|
-
* - `
|
|
295
|
+
* - `consumer` runs the read-only scenarios that don't write outside the
|
|
296
296
|
* fixture (e.g. an external project that consumes a SharkCraft pack).
|
|
297
297
|
*/
|
|
298
298
|
function isScenarioApplicableTo(scenario, target) {
|
|
@@ -304,7 +304,7 @@ function isScenarioApplicableTo(scenario, target) {
|
|
|
304
304
|
if (target.id === 'dogfood') {
|
|
305
305
|
return scenario === 'dev-workflow' || scenario === 'pr-review' || scenario === 'governance';
|
|
306
306
|
}
|
|
307
|
-
if (target.id === '
|
|
307
|
+
if (target.id === 'consumer') {
|
|
308
308
|
return scenario === 'unconfigured-repo' || scenario === 'pack-authoring' || scenario === 'governance';
|
|
309
309
|
}
|
|
310
310
|
return true;
|
|
@@ -27,7 +27,7 @@ function parseSources(args) {
|
|
|
27
27
|
}
|
|
28
28
|
export const searchCommand = {
|
|
29
29
|
name: 'search',
|
|
30
|
-
description: 'Universal search across commands, MCP tools, knowledge, rules, paths, conventions, templates, helpers, playbooks, constructs, policies, decisions, scaffold patterns, contract templates, migration profiles,
|
|
30
|
+
description: 'Universal search across commands, MCP tools, knowledge, rules, paths, conventions, templates, helpers, playbooks, constructs, policies, decisions, scaffold patterns, contract templates, migration profiles, feedback rules, task routing hints, docs, recent reports. Default emits the 7-section unified output; pass --legacy for the flat output.',
|
|
31
31
|
usage: 'shrk search <query> [--kind <kind>] [--source local|pack|...] [--limit N] [--explain] [--commands-only] [--actions-only] [--format text|markdown|json] [--legacy]',
|
|
32
32
|
async run(args) {
|
|
33
33
|
// Sub-dispatch for `shrk search tuning [list|doctor]`.
|
|
@@ -399,22 +399,6 @@ async function collectLikelyFilesV2(input) {
|
|
|
399
399
|
}
|
|
400
400
|
}
|
|
401
401
|
}
|
|
402
|
-
// Boost files that sit on registered plugin-lifecycle profile barrels.
|
|
403
|
-
try {
|
|
404
|
-
const { listPluginLifecycleProfiles } = await import('@shrkcrft/inspector');
|
|
405
|
-
const profiles = await listPluginLifecycleProfiles(inspection);
|
|
406
|
-
for (const entry of profiles) {
|
|
407
|
-
for (const b of entry.profile.barrels ?? []) {
|
|
408
|
-
for (const f of [...scoreByPath.keys()]) {
|
|
409
|
-
if (f.includes(b.path))
|
|
410
|
-
bump(f, 3, `lifecycle profile barrel: ${b.id}`);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
catch {
|
|
416
|
-
// Profile registry unavailable — skip the boost.
|
|
417
|
-
}
|
|
418
402
|
// 10) Tests — files that look like they test the matched files.
|
|
419
403
|
const tests = [];
|
|
420
404
|
for (const f of [...scoreByPath.keys()]) {
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `.claude/commands/` generator — emits per-project slash commands
|
|
3
|
+
* for Claude Code. Companion to `claude-skill` export, but with a
|
|
4
|
+
* different inversion semantics:
|
|
5
|
+
*
|
|
6
|
+
* - **claude-skill** loads rules into Claude's prompt automatically
|
|
7
|
+
* based on description match. Passive — Claude reads the rules.
|
|
8
|
+
* - **claude-commands** registers slash commands the USER invokes
|
|
9
|
+
* (`/new-service`, `/check-changes`). Active — the command IS the
|
|
10
|
+
* recipe; Claude follows it step-by-step.
|
|
11
|
+
*
|
|
12
|
+
* Generated commands fall into two buckets:
|
|
13
|
+
*
|
|
14
|
+
* **Static commands** (always present, project-agnostic recipes):
|
|
15
|
+
* - `/follow-shrk` — short reminder of the apply-gate flow.
|
|
16
|
+
* - `/check-changes` — runs `shrk check boundaries --changed-only`
|
|
17
|
+
* scoped to the current diff and reports back.
|
|
18
|
+
* - `/shrk-brief` — runs `shrk brief` and uses the output for the
|
|
19
|
+
* current task.
|
|
20
|
+
* - `/explain-file <path>` — per-file rules / paths / boundary
|
|
21
|
+
* lookup (pairs with `shrk advise <path>` from Phase 3).
|
|
22
|
+
*
|
|
23
|
+
* **Per-template commands** (one per id in `sharkcraft/templates.ts`):
|
|
24
|
+
* - `/new-<template-id>` — Claude runs `shrk gen <template-id>
|
|
25
|
+
* <name> --dry-run --save-plan ...`, reviews the plan, and
|
|
26
|
+
* applies via `shrk apply ... --verify-signature --validate`.
|
|
27
|
+
*
|
|
28
|
+
* Generated files are self-contained markdown — no `@shrkcrft/*`
|
|
29
|
+
* imports, no shell expansions. Each one is a complete "recipe in a
|
|
30
|
+
* file" that Claude Code reads when the user types the slash command.
|
|
31
|
+
*/
|
|
32
|
+
import type { ISharkcraftInspection } from '@shrkcrft/inspector';
|
|
33
|
+
export interface IClaudeCommandFile {
|
|
34
|
+
/** Path relative to project root (e.g. `.claude/commands/new-service.md`). */
|
|
35
|
+
path: string;
|
|
36
|
+
/** Full markdown body, including YAML frontmatter. */
|
|
37
|
+
content: string;
|
|
38
|
+
/** The slash name users type (e.g. `new-service` → `/new-service`). */
|
|
39
|
+
slash: string;
|
|
40
|
+
/** Why this command was generated — surfaces in the dry-run summary. */
|
|
41
|
+
source: 'static' | 'template';
|
|
42
|
+
}
|
|
43
|
+
export interface IClaudeCommandsResult {
|
|
44
|
+
files: readonly IClaudeCommandFile[];
|
|
45
|
+
}
|
|
46
|
+
export interface IClaudeCommandsOptions {
|
|
47
|
+
/**
|
|
48
|
+
* Cap on the number of per-template commands to emit. Default 20.
|
|
49
|
+
* Templates are sorted by id; the first N are kept.
|
|
50
|
+
*/
|
|
51
|
+
maxTemplateCommands?: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Build the full set of `.claude/commands/*.md` files for a project.
|
|
55
|
+
*
|
|
56
|
+
* Pure — caller writes the bytes. Same shape as the `synthesize-*`
|
|
57
|
+
* functions in this codebase: input inspection → output file list.
|
|
58
|
+
*/
|
|
59
|
+
export declare function buildClaudeCommands(inspection: ISharkcraftInspection, options?: IClaudeCommandsOptions): IClaudeCommandsResult;
|
|
60
|
+
//# sourceMappingURL=claude-commands-export.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-commands-export.d.ts","sourceRoot":"","sources":["../../src/export/claude-commands-export.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAEjE,MAAM,WAAW,kBAAkB;IACjC,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,MAAM,EAAE,QAAQ,GAAG,UAAU,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACtC;AA0MD,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,qBAAqB,EACjC,OAAO,GAAE,sBAA2B,GACnC,qBAAqB,CAsDvB"}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `.claude/commands/` generator — emits per-project slash commands
|
|
3
|
+
* for Claude Code. Companion to `claude-skill` export, but with a
|
|
4
|
+
* different inversion semantics:
|
|
5
|
+
*
|
|
6
|
+
* - **claude-skill** loads rules into Claude's prompt automatically
|
|
7
|
+
* based on description match. Passive — Claude reads the rules.
|
|
8
|
+
* - **claude-commands** registers slash commands the USER invokes
|
|
9
|
+
* (`/new-service`, `/check-changes`). Active — the command IS the
|
|
10
|
+
* recipe; Claude follows it step-by-step.
|
|
11
|
+
*
|
|
12
|
+
* Generated commands fall into two buckets:
|
|
13
|
+
*
|
|
14
|
+
* **Static commands** (always present, project-agnostic recipes):
|
|
15
|
+
* - `/follow-shrk` — short reminder of the apply-gate flow.
|
|
16
|
+
* - `/check-changes` — runs `shrk check boundaries --changed-only`
|
|
17
|
+
* scoped to the current diff and reports back.
|
|
18
|
+
* - `/shrk-brief` — runs `shrk brief` and uses the output for the
|
|
19
|
+
* current task.
|
|
20
|
+
* - `/explain-file <path>` — per-file rules / paths / boundary
|
|
21
|
+
* lookup (pairs with `shrk advise <path>` from Phase 3).
|
|
22
|
+
*
|
|
23
|
+
* **Per-template commands** (one per id in `sharkcraft/templates.ts`):
|
|
24
|
+
* - `/new-<template-id>` — Claude runs `shrk gen <template-id>
|
|
25
|
+
* <name> --dry-run --save-plan ...`, reviews the plan, and
|
|
26
|
+
* applies via `shrk apply ... --verify-signature --validate`.
|
|
27
|
+
*
|
|
28
|
+
* Generated files are self-contained markdown — no `@shrkcrft/*`
|
|
29
|
+
* imports, no shell expansions. Each one is a complete "recipe in a
|
|
30
|
+
* file" that Claude Code reads when the user types the slash command.
|
|
31
|
+
*/
|
|
32
|
+
// ─── Static commands (always emitted) ────────────────────────────────────────
|
|
33
|
+
const STATIC_FOLLOW_SHRK = `---
|
|
34
|
+
description: Reminder of the shrk apply-gate flow for this project. Use when generating or modifying code so the change passes the same boundary / validation gates the project CI uses.
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
# Follow the shrk apply-gate flow
|
|
38
|
+
|
|
39
|
+
This repo uses [SharkCraft](https://github.com/shrkcrft/sharkcraft) as the
|
|
40
|
+
gate for AI-written code. Skip the gate and CI will fail with the same
|
|
41
|
+
errors anyway — save the round-trip.
|
|
42
|
+
|
|
43
|
+
## The loop
|
|
44
|
+
|
|
45
|
+
1. **Get focused context first.**
|
|
46
|
+
- Run: \`shrk task "<one-sentence task>"\` for a task packet (relevant rules + templates + verification commands).
|
|
47
|
+
- Or: \`shrk brief\` for the single-page project brief.
|
|
48
|
+
|
|
49
|
+
2. **Scaffold via a template (not freehand).**
|
|
50
|
+
- List options: \`shrk templates list\`.
|
|
51
|
+
- Dry-run + save plan: \`shrk gen <template-id> <name> --dry-run --save-plan /tmp/plan.json\`.
|
|
52
|
+
|
|
53
|
+
3. **Apply the plan through the CLI.**
|
|
54
|
+
- \`shrk apply /tmp/plan.json --verify-signature --validate\`.
|
|
55
|
+
- Never write files directly through MCP — MCP is read-only in this repo.
|
|
56
|
+
|
|
57
|
+
4. **Verify before declaring done.**
|
|
58
|
+
- \`shrk check boundaries --changed-only\` — fails if the diff broke any layer rule.
|
|
59
|
+
- \`shrk check imports\` — fails on lazy requires / cross-package deep imports.
|
|
60
|
+
- Project verification commands (from \`shrk task\`'s \`actionHints.verificationCommands\`).
|
|
61
|
+
|
|
62
|
+
## When this loop doesn't apply
|
|
63
|
+
|
|
64
|
+
- Tiny changes that don't touch source files (docs, comments).
|
|
65
|
+
- Read-only investigations.
|
|
66
|
+
- Anything where shrk would clearly be in the way.
|
|
67
|
+
|
|
68
|
+
In all other cases: **follow the loop**. The gates are short; the rework if you skip them is long.
|
|
69
|
+
`;
|
|
70
|
+
const STATIC_CHECK_CHANGES = `---
|
|
71
|
+
description: Run shrk's boundary + import-hygiene checks on the current git diff. Use after making any file change to confirm the change didn't violate the project's architecture rules before declaring the task done.
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
# Check the current diff for boundary violations
|
|
75
|
+
|
|
76
|
+
Run the diff-scoped boundary check:
|
|
77
|
+
|
|
78
|
+
\`\`\`bash
|
|
79
|
+
shrk check boundaries --changed-only
|
|
80
|
+
\`\`\`
|
|
81
|
+
|
|
82
|
+
Run the import-hygiene check on the changed files:
|
|
83
|
+
|
|
84
|
+
\`\`\`bash
|
|
85
|
+
shrk check imports
|
|
86
|
+
\`\`\`
|
|
87
|
+
|
|
88
|
+
## Interpreting the output
|
|
89
|
+
|
|
90
|
+
- **Exit 0, "0 violations":** safe to declare done.
|
|
91
|
+
- **Exit non-zero with violations listed:** fix each violation before continuing. Each violation has a \`suggestedFix\` line — apply it.
|
|
92
|
+
- **A violation on a file you didn't change:** that's a pre-existing violation in the repo. Ignore it; the \`--changed-only\` filter only fails on violations your diff introduced.
|
|
93
|
+
|
|
94
|
+
If the violations look like false positives, consult \`sharkcraft/boundaries.ts\` (the rule definitions) — don't disable them ad-hoc.
|
|
95
|
+
`;
|
|
96
|
+
const STATIC_SHRK_BRIEF = `---
|
|
97
|
+
description: Pull shrk's single-page project brief — focused rules, paths, verification commands. Use when starting work in this codebase so you have the project's actual conventions in context before writing any code.
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
# Pull the shrk brief for this project
|
|
101
|
+
|
|
102
|
+
Run the project brief:
|
|
103
|
+
|
|
104
|
+
\`\`\`bash
|
|
105
|
+
shrk brief
|
|
106
|
+
\`\`\`
|
|
107
|
+
|
|
108
|
+
This returns a compact markdown brief covering:
|
|
109
|
+
- Project overview (name, frameworks, package manager).
|
|
110
|
+
- Top rules that apply to code generation in this repo.
|
|
111
|
+
- Path conventions (where different file types belong).
|
|
112
|
+
- Action hints (commands, MCP tools, verification commands).
|
|
113
|
+
- Forbidden actions (what NOT to do).
|
|
114
|
+
|
|
115
|
+
For a per-task version (only the rules + paths + templates relevant to one task):
|
|
116
|
+
|
|
117
|
+
\`\`\`bash
|
|
118
|
+
shrk task "<one-sentence task description>"
|
|
119
|
+
\`\`\`
|
|
120
|
+
|
|
121
|
+
Both are read-only — no files are touched. Use the output to shape your plan before making any changes.
|
|
122
|
+
`;
|
|
123
|
+
const STATIC_EXPLAIN_FILE = `---
|
|
124
|
+
description: Look up the rules, path conventions, and boundary rules that apply to a specific file in this codebase. Use before editing an unfamiliar file so you follow the project's per-area conventions instead of generic patterns.
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
# Explain what applies to a file in this codebase
|
|
128
|
+
|
|
129
|
+
For a given file path (e.g. \`apps/users/src/profile.service.ts\`):
|
|
130
|
+
|
|
131
|
+
\`\`\`bash
|
|
132
|
+
shrk why <file-path>
|
|
133
|
+
\`\`\`
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
- Which package / layer the file belongs to.
|
|
137
|
+
- Which path conventions apply (e.g. "services live in \`apps/<x>/src/services/\`").
|
|
138
|
+
- Which rules are scoped to this file's path.
|
|
139
|
+
- Which boundary rules constrain this file's imports.
|
|
140
|
+
- Cross-references to related knowledge entries.
|
|
141
|
+
|
|
142
|
+
Use this *before* editing. The output is the project's actual conventions for that area, not your guess based on the file's content.
|
|
143
|
+
`;
|
|
144
|
+
// ─── Per-template command generator ──────────────────────────────────────────
|
|
145
|
+
/**
|
|
146
|
+
* Slugify a template id into a slash-command name. Template ids
|
|
147
|
+
* conventionally look like `typescript.service`; the slash command is
|
|
148
|
+
* `new-typescript-service` (or `new-service` if the segment after the
|
|
149
|
+
* dot is unique and shorter).
|
|
150
|
+
*/
|
|
151
|
+
function templateSlash(templateId) {
|
|
152
|
+
// Use the LAST dot-separated segment as the primary name —
|
|
153
|
+
// `typescript.service` → `new-service`. Falls back to the full id
|
|
154
|
+
// when there's no dot or the segment is too generic (single char).
|
|
155
|
+
const parts = templateId.split('.');
|
|
156
|
+
const tail = parts[parts.length - 1] ?? templateId;
|
|
157
|
+
const safeName = tail.length >= 3 ? tail : templateId.replace(/\./g, '-');
|
|
158
|
+
return `new-${safeName}`
|
|
159
|
+
.toLowerCase()
|
|
160
|
+
.replace(/[^a-z0-9-]+/g, '-')
|
|
161
|
+
.replace(/-+/g, '-')
|
|
162
|
+
.replace(/^-+|-+$/g, '');
|
|
163
|
+
}
|
|
164
|
+
function templateCommandBody(template) {
|
|
165
|
+
const displayName = template.name ?? template.id;
|
|
166
|
+
const description = template.description
|
|
167
|
+
? template.description.replace(/\s+/g, ' ').trim()
|
|
168
|
+
: `Scaffold a new ${displayName} using the project's actual template (\`${template.id}\`). Follows the shrk plan → apply → validate flow.`;
|
|
169
|
+
return `---
|
|
170
|
+
description: ${JSON.stringify(description)}
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
# /${templateSlash(template.id)} — scaffold ${displayName}
|
|
174
|
+
|
|
175
|
+
This command scaffolds a new ${displayName} using the project's actual template
|
|
176
|
+
defined in \`sharkcraft/templates.ts\`. The template encodes this repo's
|
|
177
|
+
conventions for path, naming, and structure — the result will match how
|
|
178
|
+
the rest of the codebase is organized, not generic patterns.
|
|
179
|
+
|
|
180
|
+
## The flow
|
|
181
|
+
|
|
182
|
+
When the user invokes \`/${templateSlash(template.id)} <name>\`:
|
|
183
|
+
|
|
184
|
+
1. **Generate a plan (no writes yet):**
|
|
185
|
+
\`\`\`bash
|
|
186
|
+
shrk gen ${template.id} <name> --dry-run --save-plan /tmp/${templateSlash(template.id)}.plan.json
|
|
187
|
+
\`\`\`
|
|
188
|
+
|
|
189
|
+
2. **Read the plan back** from \`/tmp/${templateSlash(template.id)}.plan.json\` and show the user which files will be created.
|
|
190
|
+
|
|
191
|
+
3. **Confirm.** Wait for the user to approve. If they want changes, adjust the plan or re-run \`shrk gen\` with different flags.
|
|
192
|
+
|
|
193
|
+
4. **Apply through the validated CLI path:**
|
|
194
|
+
\`\`\`bash
|
|
195
|
+
shrk apply /tmp/${templateSlash(template.id)}.plan.json --verify-signature --validate
|
|
196
|
+
\`\`\`
|
|
197
|
+
|
|
198
|
+
5. **Verify** the diff didn't break any boundary rules:
|
|
199
|
+
\`\`\`bash
|
|
200
|
+
shrk check boundaries --changed-only
|
|
201
|
+
\`\`\`
|
|
202
|
+
|
|
203
|
+
## If the template doesn't fit
|
|
204
|
+
|
|
205
|
+
If the user's request doesn't match the \`${template.id}\` template
|
|
206
|
+
shape, fall back to:
|
|
207
|
+
|
|
208
|
+
- \`shrk templates list\` — see all available templates.
|
|
209
|
+
- \`shrk gen <other-template> <name> --dry-run\` — try a different template.
|
|
210
|
+
- Hand-author only as last resort, and run \`/check-changes\` after.
|
|
211
|
+
|
|
212
|
+
The whole point of using the template is consistency with the rest of
|
|
213
|
+
this codebase. Skipping it means the new code will look different from
|
|
214
|
+
what's already there.
|
|
215
|
+
`;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Build the full set of `.claude/commands/*.md` files for a project.
|
|
219
|
+
*
|
|
220
|
+
* Pure — caller writes the bytes. Same shape as the `synthesize-*`
|
|
221
|
+
* functions in this codebase: input inspection → output file list.
|
|
222
|
+
*/
|
|
223
|
+
export function buildClaudeCommands(inspection, options = {}) {
|
|
224
|
+
const files = [];
|
|
225
|
+
files.push({
|
|
226
|
+
path: '.claude/commands/follow-shrk.md',
|
|
227
|
+
slash: 'follow-shrk',
|
|
228
|
+
source: 'static',
|
|
229
|
+
content: STATIC_FOLLOW_SHRK,
|
|
230
|
+
});
|
|
231
|
+
files.push({
|
|
232
|
+
path: '.claude/commands/check-changes.md',
|
|
233
|
+
slash: 'check-changes',
|
|
234
|
+
source: 'static',
|
|
235
|
+
content: STATIC_CHECK_CHANGES,
|
|
236
|
+
});
|
|
237
|
+
files.push({
|
|
238
|
+
path: '.claude/commands/shrk-brief.md',
|
|
239
|
+
slash: 'shrk-brief',
|
|
240
|
+
source: 'static',
|
|
241
|
+
content: STATIC_SHRK_BRIEF,
|
|
242
|
+
});
|
|
243
|
+
files.push({
|
|
244
|
+
path: '.claude/commands/explain-file.md',
|
|
245
|
+
slash: 'explain-file',
|
|
246
|
+
source: 'static',
|
|
247
|
+
content: STATIC_EXPLAIN_FILE,
|
|
248
|
+
});
|
|
249
|
+
// Per-template commands — bounded so a pack with 50 templates
|
|
250
|
+
// doesn't dump 50 slash commands into the user's palette. Sorted
|
|
251
|
+
// by id for deterministic emit order.
|
|
252
|
+
const cap = options.maxTemplateCommands ?? 20;
|
|
253
|
+
const templates = [...inspection.templates].sort((a, b) => a.id.localeCompare(b.id));
|
|
254
|
+
const seenSlash = new Set(files.map((f) => f.slash));
|
|
255
|
+
for (const t of templates) {
|
|
256
|
+
if (files.filter((f) => f.source === 'template').length >= cap)
|
|
257
|
+
break;
|
|
258
|
+
let slash = templateSlash(t.id);
|
|
259
|
+
if (seenSlash.has(slash)) {
|
|
260
|
+
// Two templates with the same tail (e.g. `ts.service` and
|
|
261
|
+
// `py.service` both → `new-service`) — fall back to the full id
|
|
262
|
+
// to disambiguate the second one.
|
|
263
|
+
slash = `new-${t.id.replace(/\./g, '-').toLowerCase()}`;
|
|
264
|
+
if (seenSlash.has(slash))
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
seenSlash.add(slash);
|
|
268
|
+
files.push({
|
|
269
|
+
path: `.claude/commands/${slash}.md`,
|
|
270
|
+
slash,
|
|
271
|
+
source: 'template',
|
|
272
|
+
content: templateCommandBody(t),
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
return { files };
|
|
276
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ISharkcraftInspection } from '@shrkcrft/inspector';
|
|
2
|
-
export type ExportFormat = 'agents-md' | 'claude-md' | 'cursor-rules' | 'copilot-instructions';
|
|
2
|
+
export type ExportFormat = 'agents-md' | 'claude-md' | 'claude-skill' | 'cursor-rules' | 'copilot-instructions';
|
|
3
3
|
export interface ExportOptions {
|
|
4
4
|
format: ExportFormat;
|
|
5
5
|
/** Optional task to scope the export. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export-formats.d.ts","sourceRoot":"","sources":["../../src/export/export-formats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAIjE,MAAM,MAAM,YAAY,GACpB,WAAW,GACX,WAAW,GACX,cAAc,GACd,sBAAsB,CAAC;AAE3B,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,YAAY,CAAC;IACrB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,YAAY,CAAC;IACrB,yDAAyD;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,wBAAwB;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB;
|
|
1
|
+
{"version":3,"file":"export-formats.d.ts","sourceRoot":"","sources":["../../src/export/export-formats.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAIjE,MAAM,MAAM,YAAY,GACpB,WAAW,GACX,WAAW,GACX,cAAc,GACd,cAAc,GACd,sBAAsB,CAAC;AAE3B,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,YAAY,CAAC;IACrB,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,YAAY,CAAC;IACrB,yDAAyD;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,wBAAwB;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB;AAqPD,wBAAgB,YAAY,CAC1B,UAAU,EAAE,qBAAqB,EACjC,OAAO,EAAE,aAAa,GACrB,YAAY,CAmCd;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,YAAY,CAQnE;AAED,eAAO,MAAM,kBAAkB,EAAE,SAAS,YAAY,EAMpD,CAAC"}
|