@shrkcrft/cli 0.1.0-alpha.8 → 0.1.0-alpha.9

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.
Files changed (74) hide show
  1. package/README.md +1 -1
  2. package/dist/commands/boundaries.command.d.ts.map +1 -1
  3. package/dist/commands/boundaries.command.js +12 -0
  4. package/dist/commands/check.command.d.ts.map +1 -1
  5. package/dist/commands/check.command.js +30 -20
  6. package/dist/commands/command-catalog.d.ts +3 -7
  7. package/dist/commands/command-catalog.d.ts.map +1 -1
  8. package/dist/commands/command-catalog.js +47 -113
  9. package/dist/commands/commands.command.d.ts.map +1 -1
  10. package/dist/commands/commands.command.js +4 -4
  11. package/dist/commands/constructs.command.d.ts.map +1 -1
  12. package/dist/commands/constructs.command.js +22 -5
  13. package/dist/commands/doctor.command.d.ts.map +1 -1
  14. package/dist/commands/doctor.command.js +9 -42
  15. package/dist/commands/export.command.d.ts.map +1 -1
  16. package/dist/commands/export.command.js +3 -76
  17. package/dist/commands/help.command.d.ts +3 -4
  18. package/dist/commands/help.command.d.ts.map +1 -1
  19. package/dist/commands/help.command.js +21 -77
  20. package/dist/commands/helper.command.js +1 -1
  21. package/dist/commands/import.command.d.ts.map +1 -1
  22. package/dist/commands/import.command.js +5 -121
  23. package/dist/commands/init.command.d.ts.map +1 -1
  24. package/dist/commands/init.command.js +16 -184
  25. package/dist/commands/mcp.command.d.ts.map +1 -1
  26. package/dist/commands/mcp.command.js +131 -2
  27. package/dist/commands/onboard.command.d.ts.map +1 -1
  28. package/dist/commands/onboard.command.js +15 -3
  29. package/dist/commands/packs-new.d.ts +1 -1
  30. package/dist/commands/packs-new.d.ts.map +1 -1
  31. package/dist/commands/packs-new.js +36 -5
  32. package/dist/commands/packs.command.d.ts.map +1 -1
  33. package/dist/commands/packs.command.js +17 -3
  34. package/dist/commands/plugin.command.d.ts +11 -0
  35. package/dist/commands/plugin.command.d.ts.map +1 -0
  36. package/dist/commands/plugin.command.js +394 -0
  37. package/dist/commands/profiles.command.js +4 -4
  38. package/dist/commands/release.command.js +13 -13
  39. package/dist/commands/review.command.d.ts.map +1 -1
  40. package/dist/commands/review.command.js +28 -2
  41. package/dist/commands/search.command.js +1 -1
  42. package/dist/commands/task-context.command.js +16 -0
  43. package/dist/export/export-formats.d.ts +1 -1
  44. package/dist/export/export-formats.d.ts.map +1 -1
  45. package/dist/export/export-formats.js +12 -139
  46. package/dist/init/init-templates.d.ts.map +1 -1
  47. package/dist/init/init-templates.js +113 -133
  48. package/dist/main.d.ts +1 -1
  49. package/dist/main.d.ts.map +1 -1
  50. package/dist/main.js +46 -117
  51. package/dist/output/failure-hints.d.ts +9 -1
  52. package/dist/output/failure-hints.d.ts.map +1 -1
  53. package/dist/output/failure-hints.js +8 -2
  54. package/dist/output/watch-loop.d.ts +1 -9
  55. package/dist/output/watch-loop.d.ts.map +1 -1
  56. package/dist/output/watch-loop.js +3 -13
  57. package/dist/schemas/json-schemas.d.ts +36 -36
  58. package/dist/schemas/json-schemas.js +36 -36
  59. package/dist/surface/about.d.ts.map +1 -1
  60. package/dist/surface/about.js +15 -37
  61. package/dist/surface/no-args-landing.d.ts.map +1 -1
  62. package/dist/surface/no-args-landing.js +13 -9
  63. package/dist/surface/surface-config-writer.d.ts.map +1 -1
  64. package/dist/surface/surface-config-writer.js +11 -23
  65. package/package.json +25 -26
  66. package/dist/commands/diff-check.command.d.ts +0 -30
  67. package/dist/commands/diff-check.command.d.ts.map +0 -1
  68. package/dist/commands/diff-check.command.js +0 -210
  69. package/dist/export/claude-commands-export.d.ts +0 -60
  70. package/dist/export/claude-commands-export.d.ts.map +0 -1
  71. package/dist/export/claude-commands-export.js +0 -276
  72. package/dist/init/paths-advisory.d.ts +0 -20
  73. package/dist/init/paths-advisory.d.ts.map +0 -1
  74. package/dist/init/paths-advisory.js +0 -88
@@ -1,28 +1,11 @@
1
1
  import { aggregateActionHints, KnowledgeType } from '@shrkcrft/knowledge';
2
2
  import { priorityWeight } from '@shrkcrft/knowledge';
3
- function projectSlug(name) {
4
- if (!name)
5
- return 'project';
6
- return (name
7
- .toLowerCase()
8
- .replace(/[^a-z0-9]+/g, '-')
9
- .replace(/^-+|-+$/g, '')
10
- .slice(0, 48) || 'project');
11
- }
12
- function defaultOutputFor(format, inspection) {
13
- switch (format) {
14
- case 'agents-md':
15
- return 'AGENTS.md';
16
- case 'claude-md':
17
- return 'CLAUDE.md';
18
- case 'claude-skill':
19
- return `.claude/skills/${projectSlug(inspection.config?.projectName)}/SKILL.md`;
20
- case 'cursor-rules':
21
- return '.cursor/rules/sharkcraft.mdc';
22
- case 'copilot-instructions':
23
- return '.github/copilot-instructions.md';
24
- }
25
- }
3
+ const DEFAULT_OUTPUT_PATH = {
4
+ 'agents-md': 'AGENTS.md',
5
+ 'claude-md': 'CLAUDE.md',
6
+ 'cursor-rules': '.cursor/rules/sharkcraft.mdc',
7
+ 'copilot-instructions': '.github/copilot-instructions.md',
8
+ };
26
9
  function selectTopByPriority(entries, limit) {
27
10
  return [...entries]
28
11
  .sort((a, b) => priorityWeight(b.priority) - priorityWeight(a.priority))
@@ -117,131 +100,23 @@ function renderBody(inspection, options) {
117
100
  }
118
101
  return sections.join('\n\n');
119
102
  }
120
- /**
121
- * Filter rules / paths to the high-signal subset for an inlined skill.
122
- *
123
- * Every rule the skill carries pays in Claude's context every time the
124
- * skill is loaded. Low-priority items dilute the signal of the
125
- * load-bearing ones (`Critical` safety + `High` architecture). We keep
126
- * only Critical + High by default, falling back to "all" only if the
127
- * filtered list is empty (e.g. a repo with no high-priority entries
128
- * yet shouldn't ship an empty skill).
129
- */
130
- function filterHighSignal(entries) {
131
- const filtered = entries.filter((e) => {
132
- const p = String(e.priority).toLowerCase();
133
- return p === 'critical' || p === 'high';
134
- });
135
- return filtered.length > 0 ? filtered : [...entries];
136
- }
137
- /**
138
- * Render the body in tight, decision-driving form for the Claude Code
139
- * Skill format. Skills get loaded into Claude's prompt when the skill's
140
- * `description` matches the current task, so the body must be short and
141
- * directly actionable — no preamble, no metadata about generation, no
142
- * pipelines (Claude can run `shrk task` for those when needed).
143
- *
144
- * Sections:
145
- * 1. Where files belong (path conventions, top N) — most-leveraged signal.
146
- * 2. Rules to follow (Critical + High only) — pads context otherwise.
147
- * 3. Do / Don't — forbidden actions and verification commands.
148
- * 4. How to write code in this repo — the gen → review → apply loop
149
- * verbatim, since Claude needs to know shrk is the write path.
150
- */
151
- function renderSkillBody(inspection, options) {
152
- const { rules: allRules, paths: allPaths } = gatherRulesAndPaths(inspection, {
153
- ...options,
154
- // Pull more upfront, then filter down to high-signal — keeps the
155
- // softcap-at-15 default but the inlined skill stays terse.
156
- maxRules: options.maxRules ?? 24,
157
- maxPaths: options.maxPaths ?? 18,
158
- });
159
- // Skills are inlined into the prompt — every entry costs Claude
160
- // context. Only ship the items the user said are load-bearing.
161
- const rules = filterHighSignal(allRules).slice(0, options.maxRules ?? 12);
162
- const paths = filterHighSignal(allPaths).slice(0, options.maxPaths ?? 10);
163
- const sections = [];
164
- if (inspection.config?.description) {
165
- sections.push(`## About this codebase\n\n${inspection.config.description}`);
166
- }
167
- const pathsBody = renderPathsSection(paths);
168
- if (pathsBody) {
169
- sections.push(`## Where files belong\n\nWhen creating or moving a file, place it according to these conventions:\n\n${pathsBody}`);
170
- }
171
- const rulesBody = renderRulesSection(rules);
172
- if (rulesBody) {
173
- sections.push(`## Rules this codebase follows\n\nApply these whenever you generate, modify, or review code:\n\n${rulesBody}`);
174
- }
175
- const agg = aggregateActionHints(inspection.knowledgeEntries);
176
- const doDont = [];
177
- if (agg.forbiddenActions.length) {
178
- doDont.push('**Do not:**');
179
- for (const f of agg.forbiddenActions.slice(0, 8))
180
- doDont.push(`- ${f}`);
181
- doDont.push('');
182
- }
183
- if (agg.verificationCommands.length) {
184
- doDont.push('**Verify changes with:**');
185
- for (const c of agg.verificationCommands.slice(0, 6))
186
- doDont.push(`- \`${c}\``);
187
- }
188
- if (doDont.length) {
189
- sections.push(`## Do / Don't\n\n${doDont.join('\n').trim()}`);
190
- }
191
- sections.push(`## How to write code in this repo\n\n` +
192
- `This repository uses [SharkCraft](https://github.com/shrkcrft/sharkcraft) to gate writes — the CLI is the only write path, and writes go through a plan → review → apply loop.\n\n` +
193
- `1. **Get a task packet** before non-trivial work: \`shrk task "<one-sentence task>"\` returns the focused rules, paths, templates, and verification commands for that task.\n` +
194
- `2. **Scaffold via templates** instead of writing files freehand: \`shrk gen <template-id> <name> --dry-run --save-plan plan.json\` produces a signed plan.\n` +
195
- `3. **Apply the plan** through the CLI: \`shrk apply plan.json --verify-signature\` — never write through MCP.\n` +
196
- `4. **Check the result**: \`shrk check boundaries\` + the verification commands listed above.\n\n` +
197
- `For ad-hoc questions about the codebase, query the MCP server (\`shrk mcp serve\`) or run \`shrk context --task "<query>"\` for token-budgeted context.`);
198
- return sections.join('\n\n');
199
- }
200
- /**
201
- * Build the YAML frontmatter for a Claude Code Skill. The `description`
202
- * field is what Claude uses to decide whether to load this skill — keep
203
- * it specific (project name + what's covered) so it doesn't trigger on
204
- * unrelated tasks.
205
- */
206
- function renderSkillFrontmatter(inspection) {
207
- const slug = projectSlug(inspection.config?.projectName);
208
- const projectName = inspection.config?.projectName ?? slug;
209
- const description = `Codebase rules, path conventions, and review gates for ${projectName}. ` +
210
- `Use this skill whenever generating, modifying, or reviewing code in this repository — ` +
211
- `it tells you the per-file path conventions, the architecture boundaries, the verification commands, ` +
212
- `and the safe write path (CLI plan → review → apply).`;
213
- return `---\nname: ${slug}\ndescription: ${JSON.stringify(description)}\n---`;
214
- }
215
103
  export function renderExport(inspection, options) {
216
- const suggestedPath = defaultOutputFor(options.format, inspection);
104
+ const body = renderBody(inspection, options);
105
+ const suggestedPath = DEFAULT_OUTPUT_PATH[options.format];
217
106
  let content = '';
218
107
  switch (options.format) {
219
- case 'agents-md': {
220
- const body = renderBody(inspection, options);
108
+ case 'agents-md':
221
109
  content = `# Agents Guide\n\n${PREAMBLE}\n\n${body}\n`;
222
110
  break;
223
- }
224
- case 'claude-md': {
225
- const body = renderBody(inspection, options);
111
+ case 'claude-md':
226
112
  content = `# CLAUDE.md\n\n${PREAMBLE}\n\nThis file is read by Claude Code (claude.ai/code) at session start. Treat it as a compatibility view of the project's SharkCraft knowledge — use the MCP server (\`shrk mcp serve\`) for the live, queryable source of truth.\n\n${body}\n`;
227
113
  break;
228
- }
229
- case 'claude-skill': {
230
- // Claude Code Skill: YAML frontmatter declares when to load this,
231
- // body is tight decision-driving content. No preamble, no boilerplate
232
- // — every token in here costs Claude context when loaded.
233
- const frontmatter = renderSkillFrontmatter(inspection);
234
- const projectName = inspection.config?.projectName ?? 'this codebase';
235
- const body = renderSkillBody(inspection, options);
236
- content = `${frontmatter}\n\n# ${projectName} — codebase guide\n\n${body}\n`;
237
- break;
238
- }
239
114
  case 'cursor-rules':
240
115
  // Cursor MDC frontmatter: leave alwaysApply off so the user can tune.
241
- content = `---\ndescription: SharkCraft project rules (auto-generated)\nalwaysApply: false\n---\n\n${PREAMBLE}\n\n${renderBody(inspection, options)}\n`;
116
+ content = `---\ndescription: SharkCraft project rules (auto-generated)\nalwaysApply: false\n---\n\n${PREAMBLE}\n\n${body}\n`;
242
117
  break;
243
118
  case 'copilot-instructions':
244
- content = `# Copilot instructions\n\n${PREAMBLE}\n\n${renderBody(inspection, options)}\n`;
119
+ content = `# Copilot instructions\n\n${PREAMBLE}\n\n${body}\n`;
245
120
  break;
246
121
  }
247
122
  return { format: options.format, suggestedPath, content };
@@ -249,14 +124,12 @@ export function renderExport(inspection, options) {
249
124
  export function isExportFormat(value) {
250
125
  return (value === 'agents-md' ||
251
126
  value === 'claude-md' ||
252
- value === 'claude-skill' ||
253
127
  value === 'cursor-rules' ||
254
128
  value === 'copilot-instructions');
255
129
  }
256
130
  export const ALL_EXPORT_FORMATS = Object.freeze([
257
131
  'agents-md',
258
132
  'claude-md',
259
- 'claude-skill',
260
133
  'cursor-rules',
261
134
  'copilot-instructions',
262
135
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"init-templates.d.ts","sourceRoot":"","sources":["../../src/init/init-templates.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAUD,eAAO,MAAM,UAAU,EAAE,SAAS,SAAS,EA2W1C,CAAC"}
1
+ {"version":3,"file":"init-templates.d.ts","sourceRoot":"","sources":["../../src/init/init-templates.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,UAAU,EAAE,SAAS,SAAS,EA4Z1C,CAAC"}
@@ -1,18 +1,9 @@
1
- // Self-contained scaffolding for `shrk init --legacy`. Every generated
2
- // `sharkcraft/*.ts` file must work in a brand-new downstream repo where
3
- // no `@shrkcrft/*` packages are installed beyond the CLI itself. The
4
- // loaders (knowledge / templates / pipelines / path-conventions) are
5
- // shape-agnostic — they accept any object with the required string
6
- // fields — so each emitted file declares its own minimal helpers +
7
- // enum-like constants inline.
8
1
  export const INIT_FILES = [
9
2
  {
10
3
  relativePath: 'sharkcraft.config.ts',
11
- content: `// Generated by \`shrk init --legacy\`. Plain default export — no
12
- // @shrkcrft/* import required. The config loader validates this object
13
- // by shape (zod), so the literal works without a helper call.
4
+ content: `import { defineSharkCraftConfig } from '@shrkcrft/config';
14
5
 
15
- export default {
6
+ export default defineSharkCraftConfig({
16
7
  projectName: 'my-project',
17
8
  description: 'A SharkCraft-powered repository.',
18
9
  knowledgeFiles: ['knowledge.ts'],
@@ -22,36 +13,12 @@ export default {
22
13
  docsFiles: ['docs/overview.md', 'docs/architecture.md', 'docs/quick-start.md'],
23
14
  defaultMaxTokens: 4000,
24
15
  defaultScope: ['typescript'],
25
- };
16
+ });
26
17
  `,
27
18
  },
28
19
  {
29
20
  relativePath: 'knowledge.ts',
30
- content: `// Local helpers keep this file self-contained (no @shrkcrft/* imports).
31
- // The knowledge loader is shape-agnostic; it accepts any object whose
32
- // \`id\`, \`title\`, and \`content\` are strings.
33
-
34
- const KnowledgePriority = {
35
- Critical: 'critical',
36
- High: 'high',
37
- Medium: 'medium',
38
- Low: 'low',
39
- } as const;
40
-
41
- const KnowledgeType = {
42
- Rule: 'rule',
43
- Path: 'path',
44
- Template: 'template',
45
- Architecture: 'architecture',
46
- Technical: 'technical',
47
- Convention: 'convention',
48
- Workflow: 'workflow',
49
- Warning: 'warning',
50
- } as const;
51
-
52
- function defineKnowledgeEntry<T>(entry: T): T {
53
- return entry;
54
- }
21
+ content: `import { defineKnowledgeEntry, KnowledgeType, KnowledgePriority } from '@shrkcrft/knowledge';
55
22
 
56
23
  export const projectOverview = defineKnowledgeEntry({
57
24
  id: 'project.overview',
@@ -100,20 +67,8 @@ export default [projectOverview, aiAgentBriefing, generationSafety];
100
67
  },
101
68
  {
102
69
  relativePath: 'rules.ts',
103
- content: `// Local helpers keep this file self-contained (no @shrkcrft/* imports).
104
-
105
- const KnowledgePriority = {
106
- Critical: 'critical',
107
- High: 'high',
108
- Medium: 'medium',
109
- Low: 'low',
110
- } as const;
111
-
112
- const KnowledgeType = { Rule: 'rule' } as const;
113
-
114
- function defineRule<T>(rule: T): T {
115
- return { ...rule, type: KnowledgeType.Rule } as T;
116
- }
70
+ content: `import { defineRule } from '@shrkcrft/rules';
71
+ import { KnowledgePriority } from '@shrkcrft/knowledge';
117
72
 
118
73
  export const tsNamingClasses = defineRule({
119
74
  id: 'typescript.naming.classes',
@@ -187,15 +142,81 @@ export default [
187
142
  },
188
143
  {
189
144
  relativePath: 'paths.ts',
190
- content: PATHS_PLACEHOLDER_CONTENT(),
145
+ content: `import { definePathConvention } from '@shrkcrft/paths';
146
+ import { KnowledgePriority } from '@shrkcrft/knowledge';
147
+
148
+ export const appSrc = definePathConvention({
149
+ id: 'app.src',
150
+ title: 'Application source root',
151
+ path: 'src',
152
+ description: 'All application source lives here.',
153
+ priority: KnowledgePriority.Critical,
154
+ scope: ['typescript'],
155
+ tags: ['source-path', 'root'],
156
+ appliesWhen: ['generate-code'],
157
+ });
158
+
159
+ export const services = definePathConvention({
160
+ id: 'app.services',
161
+ title: 'Application services',
162
+ path: 'src/services',
163
+ description: 'Application services live here.',
164
+ priority: KnowledgePriority.High,
165
+ scope: ['typescript', 'backend'],
166
+ tags: ['service', 'source-path'],
167
+ appliesWhen: ['generate-service', 'create-business-logic'],
168
+ });
169
+
170
+ export const utils = definePathConvention({
171
+ id: 'app.utils',
172
+ title: 'Utilities',
173
+ path: 'src/utils',
174
+ description: 'Pure functions, no side effects.',
175
+ priority: KnowledgePriority.Medium,
176
+ scope: ['typescript'],
177
+ tags: ['util', 'source-path'],
178
+ appliesWhen: ['generate-utility'],
179
+ });
180
+
181
+ export const features = definePathConvention({
182
+ id: 'app.features',
183
+ title: 'Feature folders',
184
+ path: 'src/features',
185
+ description: 'Vertical feature slices. Use this for end-to-end features.',
186
+ priority: KnowledgePriority.Medium,
187
+ scope: ['typescript'],
188
+ tags: ['feature', 'source-path'],
189
+ appliesWhen: ['generate-feature'],
190
+ });
191
+
192
+ export const tests = definePathConvention({
193
+ id: 'app.tests',
194
+ title: 'Test files',
195
+ path: 'tests',
196
+ description: 'Test files (or co-located *.spec.ts next to the unit under test).',
197
+ priority: KnowledgePriority.Medium,
198
+ scope: ['typescript', 'testing'],
199
+ tags: ['test', 'source-path'],
200
+ appliesWhen: ['generate-test'],
201
+ });
202
+
203
+ export const docs = definePathConvention({
204
+ id: 'app.docs',
205
+ title: 'Documentation',
206
+ path: 'docs',
207
+ description: 'Long-form human-readable docs (optional).',
208
+ priority: KnowledgePriority.Low,
209
+ scope: ['typescript'],
210
+ tags: ['docs'],
211
+ appliesWhen: ['add-docs'],
212
+ });
213
+
214
+ export default [appSrc, services, utils, features, tests, docs];
215
+ `,
191
216
  },
192
217
  {
193
218
  relativePath: 'templates.ts',
194
- content: `// Local helpers keep this file self-contained (no @shrkcrft/* imports).
195
-
196
- function defineTemplate<T>(template: T): T {
197
- return template;
198
- }
219
+ content: `import { defineTemplate } from '@shrkcrft/templates';
199
220
 
200
221
  export const tsService = defineTemplate({
201
222
  id: 'typescript.service',
@@ -208,8 +229,8 @@ export const tsService = defineTemplate({
208
229
  { name: 'name', required: true, description: 'kebab-case file name (e.g. user-profile)' },
209
230
  { name: 'className', required: true, description: 'PascalCase class name (e.g. UserProfileService)' },
210
231
  ],
211
- targetPath: ({ name }: { name: string }) => \`src/services/\${name}.service.ts\`,
212
- content: ({ className }: { className: string }) => \`export class \${className} {
232
+ targetPath: ({ name }) => \`src/services/\${name}.service.ts\`,
233
+ content: ({ className }) => \`export class \${className} {
213
234
  constructor() {}
214
235
 
215
236
  init(): void {
@@ -232,14 +253,42 @@ export const tsUtility = defineTemplate({
232
253
  { name: 'name', required: true, description: 'kebab-case file name' },
233
254
  { name: 'camel', required: true, description: 'camelCase function name' },
234
255
  ],
235
- targetPath: ({ name }: { name: string }) => \`src/utils/\${name}.ts\`,
236
- content: ({ camel }: { camel: string }) => \`export function \${camel}(input: unknown): unknown {
256
+ targetPath: ({ name }) => \`src/utils/\${name}.ts\`,
257
+ content: ({ camel }) => \`export function \${camel}(input: unknown): unknown {
237
258
  return input;
238
259
  }
239
260
  \`,
240
261
  postGenerationNotes: ['Keep utilities pure. No side effects, no shared mutable state.'],
241
262
  });
242
263
 
264
+ export const tsFeatureFolder = defineTemplate({
265
+ id: 'typescript.feature',
266
+ name: 'TypeScript Feature folder',
267
+ description: 'Creates a vertical feature slice (index + service + types).',
268
+ tags: ['typescript', 'feature'],
269
+ scope: ['typescript'],
270
+ appliesWhen: ['generate-feature'],
271
+ variables: [
272
+ { name: 'name', required: true },
273
+ { name: 'pascal', required: true },
274
+ ],
275
+ files: ({ name, pascal }) => [
276
+ {
277
+ targetPath: \`src/features/\${name}/index.ts\`,
278
+ content: \`export * from './\${name}.service.ts';\nexport * from './\${name}.types.ts';\n\`,
279
+ },
280
+ {
281
+ targetPath: \`src/features/\${name}/\${name}.service.ts\`,
282
+ content: \`import type { I\${pascal}Config } from './\${name}.types.ts';\n\nexport class \${pascal}Service {\n constructor(private readonly config: I\${pascal}Config) {}\n}\n\`,
283
+ },
284
+ {
285
+ targetPath: \`src/features/\${name}/\${name}.types.ts\`,
286
+ content: \`export interface I\${pascal}Config {\n enabled: boolean;\n}\n\`,
287
+ },
288
+ ],
289
+ postGenerationNotes: ['Add tests under tests/<feature> or co-located *.spec.ts.'],
290
+ });
291
+
243
292
  export const tsTest = defineTemplate({
244
293
  id: 'typescript.test',
245
294
  name: 'TypeScript Test File',
@@ -251,18 +300,11 @@ export const tsTest = defineTemplate({
251
300
  { name: 'name', required: true },
252
301
  { name: 'pascal', required: true },
253
302
  ],
254
- targetPath: ({ name }: { name: string }) => \`tests/\${name}.spec.ts\`,
255
- content: ({ pascal }: { pascal: string }) => \`import { describe, expect, test } from 'bun:test';
256
-
257
- describe('\${pascal}', () => {
258
- test('placeholder', () => {
259
- expect(true).toBe(true);
260
- });
261
- });
262
- \`,
303
+ targetPath: ({ name }) => \`tests/\${name}.spec.ts\`,
304
+ content: ({ pascal }) => \`import { describe, expect, test } from 'bun:test';\n\ndescribe('\${pascal}', () => {\n test('placeholder', () => {\n expect(true).toBe(true);\n });\n});\n\`,
263
305
  });
264
306
 
265
- export default [tsService, tsUtility, tsTest];
307
+ export default [tsService, tsUtility, tsFeatureFolder, tsTest];
266
308
  `,
267
309
  },
268
310
  {
@@ -369,65 +411,3 @@ Lower-priority work for SharkCraft-powered knowledge in this repo.
369
411
  `,
370
412
  },
371
413
  ];
372
- function PATHS_PLACEHOLDER_CONTENT() {
373
- // The legacy seed ships generic path examples. The modern preset path
374
- // (`shrk init --preset <id>`) is workspace-aware. To avoid emitting
375
- // broken defaults like `src/services/` in repos that don't have them,
376
- // the legacy seed now ships *commented* examples plus an `// TODO:`
377
- // marker the human is expected to fill in. The runner-instructions in
378
- // docs/onboarding.md describe how to derive paths from the live repo.
379
- return `// Local helpers — keep this file self-contained (no @shrkcrft/* imports).
380
-
381
- const KnowledgePriority = {
382
- Critical: 'critical',
383
- High: 'high',
384
- Medium: 'medium',
385
- Low: 'low',
386
- } as const;
387
-
388
- const KnowledgeType = { Path: 'path' } as const;
389
-
390
- function definePathConvention<T>(convention: T): T {
391
- return { ...convention, type: KnowledgeType.Path } as T;
392
- }
393
-
394
- // TODO: replace the examples below with real path conventions for this
395
- // repository. Run \`shrk onboard --dry-run\` to see what the inference
396
- // engine detects from the workspace.
397
-
398
- export const appSrc = definePathConvention({
399
- id: 'app.src',
400
- title: 'Application source root',
401
- path: 'src',
402
- description: 'All application source lives here. Adjust to match this repo.',
403
- priority: KnowledgePriority.Critical,
404
- scope: ['typescript'],
405
- tags: ['source-path', 'root'],
406
- appliesWhen: ['generate-code'],
407
- });
408
-
409
- export const tests = definePathConvention({
410
- id: 'app.tests',
411
- title: 'Test files',
412
- path: 'tests',
413
- description: 'Test files. Many repos co-locate \`*.spec.ts\` next to the unit under test instead — pick one.',
414
- priority: KnowledgePriority.Medium,
415
- scope: ['typescript', 'testing'],
416
- tags: ['test', 'source-path'],
417
- appliesWhen: ['generate-test'],
418
- });
419
-
420
- export const docs = definePathConvention({
421
- id: 'app.docs',
422
- title: 'Documentation',
423
- path: 'docs',
424
- description: 'Long-form human-readable docs (optional).',
425
- priority: KnowledgePriority.Low,
426
- scope: ['typescript'],
427
- tags: ['docs'],
428
- appliesWhen: ['add-docs'],
429
- });
430
-
431
- export default [appSrc, tests, docs];
432
- `;
433
- }
package/dist/main.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env bun
2
2
  import { CommandRegistry } from './command-registry.js';
3
3
  export declare function buildRegistry(): CommandRegistry;
4
4
  export declare function runCli(argv: readonly string[]): Promise<number>;
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA,OAAO,EACL,eAAe,EAIhB,MAAM,uBAAuB,CAAC;AAyV/B,wBAAgB,aAAa,IAAI,eAAe,CA2V/C;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA2BrE;AAkGD;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CA4CxE"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AACA,OAAO,EACL,eAAe,EAIhB,MAAM,uBAAuB,CAAC;AAqX/B,wBAAgB,aAAa,IAAI,eAAe,CA2V/C;AAED,wBAAsB,MAAM,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA2BrE;AAqGD;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CA4CxE"}