@shrkcrft/cli 0.1.0-alpha.2 → 0.1.0-alpha.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"init.command.d.ts","sourceRoot":"","sources":["../../src/commands/init.command.ts"],"names":[],"mappings":"AAeA,OAAO,EAAwB,KAAK,eAAe,EAA+B,MAAM,wBAAwB,CAAC;AAuQjH,eAAO,MAAM,WAAW,EAAE,eA2FzB,CAAC"}
1
+ {"version":3,"file":"init.command.d.ts","sourceRoot":"","sources":["../../src/commands/init.command.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAwB,KAAK,eAAe,EAA+B,MAAM,wBAAwB,CAAC;AAmSjH,eAAO,MAAM,WAAW,EAAE,eA2FzB,CAAC"}
@@ -6,6 +6,7 @@ import { listBuiltInSurfaceProfiles, suggestSurfaceProfile } from '@shrkcrft/ins
6
6
  import { INIT_FILES } from "../init/init-templates.js";
7
7
  import { buildDetectedBlock, renderDetectedBlockText } from "../init/detected-block.js";
8
8
  import { ensureSharkcraftGitignore, renderGitignorePatch } from "../init/gitignore.js";
9
+ import { annotatePathsAgainstDisk } from "../init/paths-advisory.js";
9
10
  import { applySurfaceTextEdit } from "../surface/surface-config-writer.js";
10
11
  import { flagBool, flagString, resolveCwd } from "../command-registry.js";
11
12
  import { bullet, header } from "../output/format-output.js";
@@ -133,6 +134,11 @@ async function applyPresetInit(cwd, mode) {
133
134
  source: 'override',
134
135
  });
135
136
  }
137
+ // Workspace-shape advisory: when the preset emits path conventions
138
+ // that don't exist in this repo (common with generic presets in
139
+ // framework-specific repos), annotate paths.ts so the user knows
140
+ // which defaults to fix.
141
+ const pathsAdvisory = annotatePathsAgainstDisk(cwd, plan.sharkcraftDir);
136
142
  process.stdout.write(header('SharkCraft initialized'));
137
143
  process.stdout.write(`Preset: ${preset.id} — ${preset.title}\n`);
138
144
  process.stdout.write(`Folder: ${plan.sharkcraftDir}\n`);
@@ -169,6 +175,9 @@ async function applyPresetInit(cwd, mode) {
169
175
  process.stdout.write('\n' + renderGitignorePatch(patch, false));
170
176
  }
171
177
  }
178
+ if (pathsAdvisory.annotated) {
179
+ renderPathsAdvisory(pathsAdvisory);
180
+ }
172
181
  process.stdout.write('\nNext:\n');
173
182
  for (const cmd of preset.recommendedNextCommands ?? [
174
183
  'shrk doctor',
@@ -179,6 +188,16 @@ async function applyPresetInit(cwd, mode) {
179
188
  process.stdout.write(bullet('Start the MCP server: `shrk mcp serve` (or run it via Claude Code)') + '\n');
180
189
  return 0;
181
190
  }
191
+ function renderPathsAdvisory(advisory) {
192
+ process.stdout.write('\n');
193
+ process.stdout.write(header('Paths advisory'));
194
+ process.stdout.write('These preset-default paths do not exist in this repo:\n');
195
+ for (const p of advisory.missingPaths) {
196
+ process.stdout.write(bullet(p) + '\n');
197
+ }
198
+ process.stdout.write('\nEdit sharkcraft/paths.ts to match your real layout. ' +
199
+ 'Run `shrk onboard --dry-run` to see what the engine infers.\n');
200
+ }
182
201
  function applyLegacyInit(cwd, force) {
183
202
  const { root } = detectProjectRoot(cwd);
184
203
  const sharkcraftDir = nodePath.join(root, 'sharkcraft');
@@ -212,6 +231,10 @@ function applyLegacyInit(cwd, force) {
212
231
  for (const s of skipped)
213
232
  process.stdout.write(bullet(s) + '\n');
214
233
  }
234
+ const legacyAdvisory = annotatePathsAgainstDisk(cwd, sharkcraftDir);
235
+ if (legacyAdvisory.annotated) {
236
+ renderPathsAdvisory(legacyAdvisory);
237
+ }
215
238
  process.stdout.write('\nNext:\n');
216
239
  process.stdout.write(bullet('Run `shrk inspect` to see your project summary.') + '\n');
217
240
  process.stdout.write(bullet('Run `shrk knowledge list` to see what was seeded.') + '\n');
@@ -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;AAED,eAAO,MAAM,UAAU,EAAE,SAAS,SAAS,EA4Z1C,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;AAUD,eAAO,MAAM,UAAU,EAAE,SAAS,SAAS,EA2W1C,CAAC"}
@@ -1,9 +1,18 @@
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.
1
8
  export const INIT_FILES = [
2
9
  {
3
10
  relativePath: 'sharkcraft.config.ts',
4
- content: `import { defineSharkCraftConfig } from '@shrkcrft/config';
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.
5
14
 
6
- export default defineSharkCraftConfig({
15
+ export default {
7
16
  projectName: 'my-project',
8
17
  description: 'A SharkCraft-powered repository.',
9
18
  knowledgeFiles: ['knowledge.ts'],
@@ -13,12 +22,36 @@ export default defineSharkCraftConfig({
13
22
  docsFiles: ['docs/overview.md', 'docs/architecture.md', 'docs/quick-start.md'],
14
23
  defaultMaxTokens: 4000,
15
24
  defaultScope: ['typescript'],
16
- });
25
+ };
17
26
  `,
18
27
  },
19
28
  {
20
29
  relativePath: 'knowledge.ts',
21
- content: `import { defineKnowledgeEntry, KnowledgeType, KnowledgePriority } from '@shrkcrft/knowledge';
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
+ }
22
55
 
23
56
  export const projectOverview = defineKnowledgeEntry({
24
57
  id: 'project.overview',
@@ -67,8 +100,20 @@ export default [projectOverview, aiAgentBriefing, generationSafety];
67
100
  },
68
101
  {
69
102
  relativePath: 'rules.ts',
70
- content: `import { defineRule } from '@shrkcrft/rules';
71
- import { KnowledgePriority } from '@shrkcrft/knowledge';
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
+ }
72
117
 
73
118
  export const tsNamingClasses = defineRule({
74
119
  id: 'typescript.naming.classes',
@@ -142,81 +187,15 @@ export default [
142
187
  },
143
188
  {
144
189
  relativePath: 'paths.ts',
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
- `,
190
+ content: PATHS_PLACEHOLDER_CONTENT(),
216
191
  },
217
192
  {
218
193
  relativePath: 'templates.ts',
219
- content: `import { defineTemplate } from '@shrkcrft/templates';
194
+ content: `// Local helpers keep this file self-contained (no @shrkcrft/* imports).
195
+
196
+ function defineTemplate<T>(template: T): T {
197
+ return template;
198
+ }
220
199
 
221
200
  export const tsService = defineTemplate({
222
201
  id: 'typescript.service',
@@ -229,8 +208,8 @@ export const tsService = defineTemplate({
229
208
  { name: 'name', required: true, description: 'kebab-case file name (e.g. user-profile)' },
230
209
  { name: 'className', required: true, description: 'PascalCase class name (e.g. UserProfileService)' },
231
210
  ],
232
- targetPath: ({ name }) => \`src/services/\${name}.service.ts\`,
233
- content: ({ className }) => \`export class \${className} {
211
+ targetPath: ({ name }: { name: string }) => \`src/services/\${name}.service.ts\`,
212
+ content: ({ className }: { className: string }) => \`export class \${className} {
234
213
  constructor() {}
235
214
 
236
215
  init(): void {
@@ -253,42 +232,14 @@ export const tsUtility = defineTemplate({
253
232
  { name: 'name', required: true, description: 'kebab-case file name' },
254
233
  { name: 'camel', required: true, description: 'camelCase function name' },
255
234
  ],
256
- targetPath: ({ name }) => \`src/utils/\${name}.ts\`,
257
- content: ({ camel }) => \`export function \${camel}(input: unknown): unknown {
235
+ targetPath: ({ name }: { name: string }) => \`src/utils/\${name}.ts\`,
236
+ content: ({ camel }: { camel: string }) => \`export function \${camel}(input: unknown): unknown {
258
237
  return input;
259
238
  }
260
239
  \`,
261
240
  postGenerationNotes: ['Keep utilities pure. No side effects, no shared mutable state.'],
262
241
  });
263
242
 
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
-
292
243
  export const tsTest = defineTemplate({
293
244
  id: 'typescript.test',
294
245
  name: 'TypeScript Test File',
@@ -300,11 +251,18 @@ export const tsTest = defineTemplate({
300
251
  { name: 'name', required: true },
301
252
  { name: 'pascal', required: true },
302
253
  ],
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\`,
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
+ \`,
305
263
  });
306
264
 
307
- export default [tsService, tsUtility, tsFeatureFolder, tsTest];
265
+ export default [tsService, tsUtility, tsTest];
308
266
  `,
309
267
  },
310
268
  {
@@ -411,3 +369,65 @@ Lower-priority work for SharkCraft-powered knowledge in this repo.
411
369
  `,
412
370
  },
413
371
  ];
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
+ }
@@ -0,0 +1,20 @@
1
+ export interface IPathsAdvisory {
2
+ missingPaths: readonly string[];
3
+ existingPaths: readonly string[];
4
+ /** True when an advisory comment was prepended to the file. */
5
+ annotated: boolean;
6
+ pathsFile: string;
7
+ }
8
+ /**
9
+ * Scan the generated `sharkcraft/paths.ts` for `path: '<x>'` references
10
+ * and check each `<x>` against the live workspace. When any path is
11
+ * missing, prepend a clearly-labeled comment block listing the missing
12
+ * paths so the user knows which defaults to adjust.
13
+ *
14
+ * Idempotent — if the file already starts with the advisory marker,
15
+ * it is left untouched. Non-destructive — never edits, comments, or
16
+ * removes the original entries. The user is expected to revise them
17
+ * based on the advisory + `shrk onboard --dry-run` output.
18
+ */
19
+ export declare function annotatePathsAgainstDisk(cwd: string, sharkcraftDir: string): IPathsAdvisory;
20
+ //# sourceMappingURL=paths-advisory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths-advisory.d.ts","sourceRoot":"","sources":["../../src/init/paths-advisory.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,+DAA+D;IAC/D,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,GACpB,cAAc,CAwChB"}
@@ -0,0 +1,88 @@
1
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import * as nodePath from 'node:path';
3
+ const ADVISORY_MARKER = '// ⚠️ Workspace-shape advisory (added by `shrk init`):';
4
+ /**
5
+ * Scan the generated `sharkcraft/paths.ts` for `path: '<x>'` references
6
+ * and check each `<x>` against the live workspace. When any path is
7
+ * missing, prepend a clearly-labeled comment block listing the missing
8
+ * paths so the user knows which defaults to adjust.
9
+ *
10
+ * Idempotent — if the file already starts with the advisory marker,
11
+ * it is left untouched. Non-destructive — never edits, comments, or
12
+ * removes the original entries. The user is expected to revise them
13
+ * based on the advisory + `shrk onboard --dry-run` output.
14
+ */
15
+ export function annotatePathsAgainstDisk(cwd, sharkcraftDir) {
16
+ const pathsFile = nodePath.join(sharkcraftDir, 'paths.ts');
17
+ if (!existsSync(pathsFile)) {
18
+ return { missingPaths: [], existingPaths: [], annotated: false, pathsFile };
19
+ }
20
+ const original = readFileSync(pathsFile, 'utf8');
21
+ if (original.startsWith(ADVISORY_MARKER)) {
22
+ // Already annotated. Re-derive sets for caller diagnostics.
23
+ return classifyOnly(cwd, pathsFile, original);
24
+ }
25
+ const { existing, missing } = classifyPathReferences(cwd, original);
26
+ if (missing.length === 0) {
27
+ return {
28
+ missingPaths: [],
29
+ existingPaths: existing,
30
+ annotated: false,
31
+ pathsFile,
32
+ };
33
+ }
34
+ const lines = [
35
+ ADVISORY_MARKER,
36
+ '//',
37
+ '// The following paths referenced below do NOT exist in this repository:',
38
+ ...missing.map((p) => `// - ${p}`),
39
+ '//',
40
+ '// They are conservative defaults from the chosen preset. Adjust them to',
41
+ '// match your actual layout. Run `shrk onboard --dry-run` to see what',
42
+ '// the inference engine detects from your workspace.',
43
+ '//',
44
+ '',
45
+ ];
46
+ writeFileSync(pathsFile, lines.join('\n') + original, 'utf8');
47
+ return {
48
+ missingPaths: missing,
49
+ existingPaths: existing,
50
+ annotated: true,
51
+ pathsFile,
52
+ };
53
+ }
54
+ function classifyOnly(cwd, pathsFile, original) {
55
+ const { existing, missing } = classifyPathReferences(cwd, original);
56
+ return {
57
+ missingPaths: missing,
58
+ existingPaths: existing,
59
+ annotated: false,
60
+ pathsFile,
61
+ };
62
+ }
63
+ function classifyPathReferences(cwd, source) {
64
+ // Match `path: '<x>'` / `path: "<x>"`. Ignore obvious code-context
65
+ // references (e.g. inside template literals); we only consume the
66
+ // first plain-string occurrence per entry.
67
+ const re = /\bpath\s*:\s*['"]([^'"\n]+)['"]/g;
68
+ const seen = new Set();
69
+ let m;
70
+ while ((m = re.exec(source)) !== null) {
71
+ if (m[1])
72
+ seen.add(m[1]);
73
+ }
74
+ const existing = [];
75
+ const missing = [];
76
+ for (const p of [...seen].sort()) {
77
+ if (isReachable(cwd, p))
78
+ existing.push(p);
79
+ else
80
+ missing.push(p);
81
+ }
82
+ return { existing, missing };
83
+ }
84
+ function isReachable(cwd, p) {
85
+ // Absolute or rooted-relative — resolve as-is.
86
+ const full = nodePath.isAbsolute(p) ? p : nodePath.resolve(cwd, p);
87
+ return existsSync(full);
88
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"surface-config-writer.d.ts","sourceRoot":"","sources":["../../src/surface/surface-config-writer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,MAAM,WAAW,kBAAkB;IACjC,oBAAoB;IACpB,KAAK,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC5B,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB;IACjB,SAAS,EAAE,KAAK,GAAG,QAAQ,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACrC,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACrC,iDAAiD;IACjD,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,cAAc,GAAG,SAAS,EAClC,KAAK,EAAE,SAAS,kBAAkB,EAAE,GACnC,kBAAkB,CA0BpB;AAOD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,kBAAkB,GACvB,yBAAyB,CAe3B;AAmBD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,SAAO,GAAG,MAAM,CAsBjF;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,MAAM,CAgBtF;AAED,8CAA8C;AAC9C,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE/D"}
1
+ {"version":3,"file":"surface-config-writer.d.ts","sourceRoot":"","sources":["../../src/surface/surface-config-writer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,MAAM,WAAW,kBAAkB;IACjC,oBAAoB;IACpB,KAAK,EAAE,SAAS,GAAG,QAAQ,CAAC;IAC5B,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB;IACjB,SAAS,EAAE,KAAK,GAAG,QAAQ,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,cAAc,CAAC;IACtB,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACrC,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACrC,iDAAiD;IACjD,WAAW,EAAE,OAAO,CAAC;CACtB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,cAAc,GAAG,SAAS,EAClC,KAAK,EAAE,SAAS,kBAAkB,EAAE,GACnC,kBAAkB,CA0BpB;AAOD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,kBAAkB,GACvB,yBAAyB,CAe3B;AAoBD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,EAAE,MAAM,SAAO,GAAG,MAAM,CAsBjF;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,MAAM,CA6BtF;AAED,8CAA8C;AAC9C,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE/D"}
@@ -72,10 +72,11 @@ function fileExists(p) {
72
72
  return false;
73
73
  }
74
74
  }
75
- const DEFAULT_CONFIG_BODY = `import { defineSharkCraftConfig } from '@shrkcrft/config';
76
-
77
- export default defineSharkCraftConfig({
78
- });
75
+ // Plain default export no @shrkcrft/* import required. The config
76
+ // loader (packages/config/src/config-loader.ts) validates by shape, so a
77
+ // literal object works the same as a `defineSharkCraftConfig()` call.
78
+ const DEFAULT_CONFIG_BODY = `export default {
79
+ };
79
80
  `;
80
81
  const SURFACE_BLOCK_REGEX = /(^\s*surface\s*:\s*\{[\s\S]*?\}\s*,?\s*\n)/m;
81
82
  /**
@@ -120,14 +121,25 @@ export function applySurfaceTextEdit(original, surface) {
120
121
  if (SURFACE_BLOCK_REGEX.test(original)) {
121
122
  return original.replace(SURFACE_BLOCK_REGEX, block);
122
123
  }
123
- // Insert before the closing `})` / `});` of the config object.
124
- // Prefer the last `})` so nested objects don't trip us up.
125
- const closeRegex = /(\n)(\}\)\s*;?\s*)$/m;
126
- if (closeRegex.test(original)) {
127
- return original.replace(closeRegex, `\n${block}$2`);
124
+ // (A) `defineSharkCraftConfig({ ... })` style insert before the `})`.
125
+ const closeFn = /(\n)(\}\)\s*;?\s*)$/m;
126
+ if (closeFn.test(original)) {
127
+ return original.replace(closeFn, `\n${block}$2`);
128
128
  }
129
- // Fallback: append at end with a defineSharkCraftConfig wrapper.
130
- return `${original}\n${DEFAULT_CONFIG_BODY.replace('})', `${block}})`)}`;
129
+ // (B) `const config = { ... };\nexport default config;` plain style —
130
+ // insert before the `};` that closes the literal.
131
+ const closePlain = /(\n)(\};?\s*\nexport\s+default\s+\w+\s*;?\s*\n*)$/m;
132
+ if (closePlain.test(original)) {
133
+ return original.replace(closePlain, `\n${block}$2`);
134
+ }
135
+ // (C) `export default { ... };` direct style — insert before the `};`.
136
+ const closeDirect = /(\n)(\};?\s*\n*)$/m;
137
+ if (closeDirect.test(original)) {
138
+ return original.replace(closeDirect, `\n${block}$2`);
139
+ }
140
+ // Fallback: append at end with a plain default-exported config (no
141
+ // `@shrkcrft/*` import required).
142
+ return `${original}\n${DEFAULT_CONFIG_BODY.replace('};', `${block}};`)}`;
131
143
  }
132
144
  /** Default config file path for a project. */
133
145
  export function defaultConfigFile(sharkcraftDir) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shrkcrft/cli",
3
- "version": "0.1.0-alpha.2",
3
+ "version": "0.1.0-alpha.4",
4
4
  "description": "SharkCraft CLI (`shrk`): structured project intelligence for AI coding agents.",
5
5
  "license": "MIT",
6
6
  "author": "SharkCraft contributors",
@@ -47,25 +47,25 @@
47
47
  "typecheck": "tsc --noEmit -p tsconfig.json"
48
48
  },
49
49
  "dependencies": {
50
- "@shrkcrft/core": "^0.1.0-alpha.2",
51
- "@shrkcrft/config": "^0.1.0-alpha.2",
52
- "@shrkcrft/workspace": "^0.1.0-alpha.2",
53
- "@shrkcrft/knowledge": "^0.1.0-alpha.2",
54
- "@shrkcrft/context": "^0.1.0-alpha.2",
55
- "@shrkcrft/rules": "^0.1.0-alpha.2",
56
- "@shrkcrft/paths": "^0.1.0-alpha.2",
57
- "@shrkcrft/templates": "^0.1.0-alpha.2",
58
- "@shrkcrft/plugin-api": "^0.1.0-alpha.2",
59
- "@shrkcrft/dashboard-api": "^0.1.0-alpha.2",
60
- "@shrkcrft/pipelines": "^0.1.0-alpha.2",
61
- "@shrkcrft/presets": "^0.1.0-alpha.2",
62
- "@shrkcrft/boundaries": "^0.1.0-alpha.2",
63
- "@shrkcrft/generator": "^0.1.0-alpha.2",
64
- "@shrkcrft/importer": "^0.1.0-alpha.2",
65
- "@shrkcrft/inspector": "^0.1.0-alpha.2",
66
- "@shrkcrft/ai": "^0.1.0-alpha.2",
67
- "@shrkcrft/shared": "^0.1.0-alpha.2",
68
- "@shrkcrft/mcp-server": "^0.1.0-alpha.2"
50
+ "@shrkcrft/core": "^0.1.0-alpha.4",
51
+ "@shrkcrft/config": "^0.1.0-alpha.4",
52
+ "@shrkcrft/workspace": "^0.1.0-alpha.4",
53
+ "@shrkcrft/knowledge": "^0.1.0-alpha.4",
54
+ "@shrkcrft/context": "^0.1.0-alpha.4",
55
+ "@shrkcrft/rules": "^0.1.0-alpha.4",
56
+ "@shrkcrft/paths": "^0.1.0-alpha.4",
57
+ "@shrkcrft/templates": "^0.1.0-alpha.4",
58
+ "@shrkcrft/plugin-api": "^0.1.0-alpha.4",
59
+ "@shrkcrft/dashboard-api": "^0.1.0-alpha.4",
60
+ "@shrkcrft/pipelines": "^0.1.0-alpha.4",
61
+ "@shrkcrft/presets": "^0.1.0-alpha.4",
62
+ "@shrkcrft/boundaries": "^0.1.0-alpha.4",
63
+ "@shrkcrft/generator": "^0.1.0-alpha.4",
64
+ "@shrkcrft/importer": "^0.1.0-alpha.4",
65
+ "@shrkcrft/inspector": "^0.1.0-alpha.4",
66
+ "@shrkcrft/ai": "^0.1.0-alpha.4",
67
+ "@shrkcrft/shared": "^0.1.0-alpha.4",
68
+ "@shrkcrft/mcp-server": "^0.1.0-alpha.4"
69
69
  },
70
70
  "publishConfig": {
71
71
  "access": "public"