@shrkcrft/importer 0.1.0-alpha.6 → 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.
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert imported `IImportedEntry[]` into populated `sharkcraft/*.ts`
|
|
3
|
+
* file contents — the engine behind `shrk import <format> --populate`.
|
|
4
|
+
*
|
|
5
|
+
* Why this exists separately from `emitKnowledgeTs`:
|
|
6
|
+
*
|
|
7
|
+
* - `emitKnowledgeTs` produces ONE draft TS file under
|
|
8
|
+
* `sharkcraft/imports/<format>-import.draft.ts` — advisory only,
|
|
9
|
+
* user adopts by hand.
|
|
10
|
+
* - `synthesizePopulated` distributes entries across `rules.ts` /
|
|
11
|
+
* `paths.ts` / `knowledge.ts` by type AND emits a confidence
|
|
12
|
+
* report (.imported-report.md). The user gets a working repo
|
|
13
|
+
* from a single command, not a TODO list.
|
|
14
|
+
*
|
|
15
|
+
* Mirror of `synthesize-from-onboarding.ts` in `@shrkcrft/inspector`:
|
|
16
|
+
* same shape, same honesty contract, same self-contained output (no
|
|
17
|
+
* `@shrkcrft/*` imports in generated files).
|
|
18
|
+
*/
|
|
19
|
+
import type { IImportedEntry } from '../model/imported-entry.js';
|
|
20
|
+
export interface IPopulatedFile {
|
|
21
|
+
/** Path relative to sharkcraft/. */
|
|
22
|
+
path: string;
|
|
23
|
+
content: string;
|
|
24
|
+
/** Which kind of file this is — drives report naming. */
|
|
25
|
+
kind: 'config' | 'rules' | 'paths' | 'knowledge' | 'report-md' | 'report-json';
|
|
26
|
+
}
|
|
27
|
+
export interface IImportConfidenceLine {
|
|
28
|
+
kind: 'rule' | 'path' | 'knowledge' | 'template';
|
|
29
|
+
id: string;
|
|
30
|
+
title: string;
|
|
31
|
+
/** What signal made us pick this confidence tier. */
|
|
32
|
+
reason: string;
|
|
33
|
+
/** The source file the entry came from. */
|
|
34
|
+
origin: string;
|
|
35
|
+
}
|
|
36
|
+
export interface IImportConfidenceReport {
|
|
37
|
+
/** Entries adopted directly (high confidence). */
|
|
38
|
+
adoptedHigh: readonly IImportConfidenceLine[];
|
|
39
|
+
/** Entries adopted with a `// TODO: review` marker (medium). */
|
|
40
|
+
adoptedMedium: readonly IImportConfidenceLine[];
|
|
41
|
+
/** Entries dropped — body too thin / type not safe to auto-populate. */
|
|
42
|
+
dropped: readonly IImportConfidenceLine[];
|
|
43
|
+
/** Reminder: things shrk import doesn't try to recover from markdown. */
|
|
44
|
+
notInferable: readonly string[];
|
|
45
|
+
}
|
|
46
|
+
export interface IPopulatedImportResult {
|
|
47
|
+
files: readonly IPopulatedFile[];
|
|
48
|
+
report: IImportConfidenceReport;
|
|
49
|
+
}
|
|
50
|
+
export interface ISynthesizePopulatedOptions {
|
|
51
|
+
/** Project name for the generated sharkcraft.config.ts. */
|
|
52
|
+
projectName: string;
|
|
53
|
+
/** Free-form project description (passed through to knowledge.ts overview). */
|
|
54
|
+
description?: string;
|
|
55
|
+
/** Source label (e.g. "CLAUDE.md", ".cursor/rules") for the report header. */
|
|
56
|
+
sourceLabel: string;
|
|
57
|
+
/** When set, only entries whose tags intersect this list are populated. */
|
|
58
|
+
filterTags?: readonly string[];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Convert a parsed `IImportResult` into populated sharkcraft/*.ts
|
|
62
|
+
* files plus a companion confidence report.
|
|
63
|
+
*
|
|
64
|
+
* Pure function — caller writes the bytes. Same shape as
|
|
65
|
+
* `synthesizeFromOnboarding` in `@shrkcrft/inspector`.
|
|
66
|
+
*/
|
|
67
|
+
export declare function synthesizePopulatedFromImport(entries: readonly IImportedEntry[], options: ISynthesizePopulatedOptions): IPopulatedImportResult;
|
|
68
|
+
//# sourceMappingURL=synthesize-populated.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"synthesize-populated.d.ts","sourceRoot":"","sources":["../../src/emit/synthesize-populated.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAIjE,MAAM,WAAW,cAAc;IAC7B,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,WAAW,GAAG,WAAW,GAAG,aAAa,CAAC;CAChF;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,WAAW,GAAG,UAAU,CAAC;IACjD,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,qDAAqD;IACrD,MAAM,EAAE,MAAM,CAAC;IACf,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,kDAAkD;IAClD,WAAW,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAC9C,gEAAgE;IAChE,aAAa,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAChD,wEAAwE;IACxE,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;IAC1C,yEAAyE;IACzE,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,SAAS,cAAc,EAAE,CAAC;IACjC,MAAM,EAAE,uBAAuB,CAAC;CACjC;AAED,MAAM,WAAW,2BAA2B;IAC1C,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,WAAW,EAAE,MAAM,CAAC;IACpB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAChC;AAgUD;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,SAAS,cAAc,EAAE,EAClC,OAAO,EAAE,2BAA2B,GACnC,sBAAsB,CAkIxB"}
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Convert imported `IImportedEntry[]` into populated `sharkcraft/*.ts`
|
|
3
|
+
* file contents — the engine behind `shrk import <format> --populate`.
|
|
4
|
+
*
|
|
5
|
+
* Why this exists separately from `emitKnowledgeTs`:
|
|
6
|
+
*
|
|
7
|
+
* - `emitKnowledgeTs` produces ONE draft TS file under
|
|
8
|
+
* `sharkcraft/imports/<format>-import.draft.ts` — advisory only,
|
|
9
|
+
* user adopts by hand.
|
|
10
|
+
* - `synthesizePopulated` distributes entries across `rules.ts` /
|
|
11
|
+
* `paths.ts` / `knowledge.ts` by type AND emits a confidence
|
|
12
|
+
* report (.imported-report.md). The user gets a working repo
|
|
13
|
+
* from a single command, not a TODO list.
|
|
14
|
+
*
|
|
15
|
+
* Mirror of `synthesize-from-onboarding.ts` in `@shrkcrft/inspector`:
|
|
16
|
+
* same shape, same honesty contract, same self-contained output (no
|
|
17
|
+
* `@shrkcrft/*` imports in generated files).
|
|
18
|
+
*/
|
|
19
|
+
import { KnowledgePriority, KnowledgeType } from '@shrkcrft/knowledge';
|
|
20
|
+
// ─── Local-mirror preamble (mirrors synthesize-from-onboarding.ts) ──────────
|
|
21
|
+
const KNOWLEDGE_HELPERS = `// Local helpers — keep this file self-contained (no @shrkcrft/* imports).
|
|
22
|
+
// Generated by \`shrk import --populate\`. Edit freely.
|
|
23
|
+
|
|
24
|
+
const KnowledgePriority = {
|
|
25
|
+
Critical: 'critical',
|
|
26
|
+
High: 'high',
|
|
27
|
+
Medium: 'medium',
|
|
28
|
+
Low: 'low',
|
|
29
|
+
} as const;
|
|
30
|
+
|
|
31
|
+
const KnowledgeType = {
|
|
32
|
+
Rule: 'rule',
|
|
33
|
+
Path: 'path',
|
|
34
|
+
Template: 'template',
|
|
35
|
+
Architecture: 'architecture',
|
|
36
|
+
Technical: 'technical',
|
|
37
|
+
Convention: 'convention',
|
|
38
|
+
Workflow: 'workflow',
|
|
39
|
+
Warning: 'warning',
|
|
40
|
+
Security: 'security',
|
|
41
|
+
Testing: 'testing',
|
|
42
|
+
Feature: 'feature',
|
|
43
|
+
Decision: 'decision',
|
|
44
|
+
Custom: 'custom',
|
|
45
|
+
} as const;
|
|
46
|
+
|
|
47
|
+
function defineKnowledgeEntry<T>(entry: T): T {
|
|
48
|
+
return entry;
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
/**
|
|
52
|
+
* Triage rule:
|
|
53
|
+
* - High: priority Critical or High AND non-trivial body (≥40 chars
|
|
54
|
+
* beyond title) — the author actually wrote something.
|
|
55
|
+
* - Medium: priority Medium, OR non-trivial body but Low priority.
|
|
56
|
+
* - Low: priority Low AND thin body. Dropped.
|
|
57
|
+
*
|
|
58
|
+
* Title-only entries (no real content) drop regardless of priority —
|
|
59
|
+
* a one-line title isn't enough to ship as an enforceable rule.
|
|
60
|
+
*/
|
|
61
|
+
function tierOf(entry) {
|
|
62
|
+
const body = entry.content.trim();
|
|
63
|
+
const meaningful = body.length > 40 && body !== entry.title.trim();
|
|
64
|
+
const p = entry.priority;
|
|
65
|
+
if (!meaningful)
|
|
66
|
+
return 'low';
|
|
67
|
+
if (p === KnowledgePriority.Critical || p === KnowledgePriority.High)
|
|
68
|
+
return 'high';
|
|
69
|
+
if (p === KnowledgePriority.Medium)
|
|
70
|
+
return 'medium';
|
|
71
|
+
return 'low';
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Route an imported entry to a destination file based on its
|
|
75
|
+
* KnowledgeType. Templates go to "dropped" — they need a runnable
|
|
76
|
+
* body, which isn't recoverable from markdown.
|
|
77
|
+
*/
|
|
78
|
+
function destinationOf(entry) {
|
|
79
|
+
switch (entry.type) {
|
|
80
|
+
case KnowledgeType.Rule:
|
|
81
|
+
return 'rules';
|
|
82
|
+
case KnowledgeType.Path:
|
|
83
|
+
return 'paths';
|
|
84
|
+
case KnowledgeType.Template:
|
|
85
|
+
return 'drop';
|
|
86
|
+
default:
|
|
87
|
+
// Everything else (Convention / Architecture / Warning / Workflow
|
|
88
|
+
// / Technical / Security / Testing / Feature / Decision / etc.)
|
|
89
|
+
// belongs in knowledge.ts.
|
|
90
|
+
return 'knowledge';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// ─── Renderers ──────────────────────────────────────────────────────────────
|
|
94
|
+
function priorityConst(p) {
|
|
95
|
+
switch (p) {
|
|
96
|
+
case KnowledgePriority.Critical:
|
|
97
|
+
return 'KnowledgePriority.Critical';
|
|
98
|
+
case KnowledgePriority.High:
|
|
99
|
+
return 'KnowledgePriority.High';
|
|
100
|
+
case KnowledgePriority.Medium:
|
|
101
|
+
return 'KnowledgePriority.Medium';
|
|
102
|
+
default:
|
|
103
|
+
return 'KnowledgePriority.Low';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function typeConst(t) {
|
|
107
|
+
// Map the (lowercased) KnowledgeType value back to its PascalCase
|
|
108
|
+
// identifier on the local KnowledgeType const. Anything we don't
|
|
109
|
+
// recognise falls back to Custom — safe default; doesn't change
|
|
110
|
+
// loader semantics.
|
|
111
|
+
switch (t) {
|
|
112
|
+
case KnowledgeType.Rule: return 'KnowledgeType.Rule';
|
|
113
|
+
case KnowledgeType.Path: return 'KnowledgeType.Path';
|
|
114
|
+
case KnowledgeType.Convention: return 'KnowledgeType.Convention';
|
|
115
|
+
case KnowledgeType.Architecture: return 'KnowledgeType.Architecture';
|
|
116
|
+
case KnowledgeType.Warning: return 'KnowledgeType.Warning';
|
|
117
|
+
case KnowledgeType.Workflow: return 'KnowledgeType.Workflow';
|
|
118
|
+
case KnowledgeType.Technical: return 'KnowledgeType.Technical';
|
|
119
|
+
case KnowledgeType.Security: return 'KnowledgeType.Security';
|
|
120
|
+
case KnowledgeType.Testing: return 'KnowledgeType.Testing';
|
|
121
|
+
case KnowledgeType.Feature: return 'KnowledgeType.Feature';
|
|
122
|
+
case KnowledgeType.Decision: return 'KnowledgeType.Decision';
|
|
123
|
+
default: return 'KnowledgeType.Custom';
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function escapeBackticks(s) {
|
|
127
|
+
return s.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
|
|
128
|
+
}
|
|
129
|
+
function renderEntry(entry, tier, includeTypeField) {
|
|
130
|
+
const todo = tier === 'medium' ? ' // TODO: review — confidence: medium (imported)\n' : '';
|
|
131
|
+
const tagsArr = ['imported', ...entry.tags].slice(0, 8);
|
|
132
|
+
const typeLine = includeTypeField ? `\n type: ${typeConst(entry.type)},` : '';
|
|
133
|
+
// Multi-line content goes in a template literal so authoring later
|
|
134
|
+
// is convenient. Single-line stays in JSON for compactness.
|
|
135
|
+
const isMultiline = entry.content.includes('\n');
|
|
136
|
+
const contentLine = isMultiline
|
|
137
|
+
? `\n content: \`${escapeBackticks(entry.content.trim())}\`,`
|
|
138
|
+
: `\n content: ${JSON.stringify(entry.content.trim())},`;
|
|
139
|
+
const sectionComment = entry.section ? `// section: ${entry.section}\n ` : '';
|
|
140
|
+
return ` ${todo} ${sectionComment}defineKnowledgeEntry({
|
|
141
|
+
id: ${JSON.stringify(entry.id)},
|
|
142
|
+
title: ${JSON.stringify(entry.title)},${typeLine}
|
|
143
|
+
priority: ${priorityConst(entry.priority)},
|
|
144
|
+
tags: ${JSON.stringify(tagsArr)},
|
|
145
|
+
appliesWhen: ['generate-code', 'review'],${contentLine}
|
|
146
|
+
metadata: {
|
|
147
|
+
importedFrom: ${JSON.stringify(entry.origin)},${entry.section ? `\n sourceSection: ${JSON.stringify(entry.section)},` : ''}
|
|
148
|
+
},
|
|
149
|
+
})`;
|
|
150
|
+
}
|
|
151
|
+
// ─── File builders ──────────────────────────────────────────────────────────
|
|
152
|
+
function buildConfigFile(opts) {
|
|
153
|
+
return (`// Generated by \`shrk import --populate\`. Edit freely; SharkCraft\n` +
|
|
154
|
+
`// will not regenerate this file. Re-run \`shrk import <format> --populate --force\`\n` +
|
|
155
|
+
`// to overwrite the populated sharkcraft/*.ts files.\n\n` +
|
|
156
|
+
`const config = {\n` +
|
|
157
|
+
` projectName: ${JSON.stringify(opts.projectName)},\n` +
|
|
158
|
+
` description: ${JSON.stringify(opts.description)},\n` +
|
|
159
|
+
` pathFiles: ${JSON.stringify(opts.includePaths ? ['paths.ts'] : [])},\n` +
|
|
160
|
+
` ruleFiles: ${JSON.stringify(opts.includeRules ? ['rules.ts'] : [])},\n` +
|
|
161
|
+
` knowledgeFiles: ${JSON.stringify(['knowledge.ts'])},\n` +
|
|
162
|
+
` templateFiles: ${JSON.stringify([])},\n` +
|
|
163
|
+
` pipelineFiles: ${JSON.stringify([])},\n` +
|
|
164
|
+
` boundaryFiles: ${JSON.stringify([])},\n` +
|
|
165
|
+
` docsFiles: ${JSON.stringify([])},\n` +
|
|
166
|
+
` defaultMaxTokens: 3500,\n` +
|
|
167
|
+
`};\n\nexport default config;\n`);
|
|
168
|
+
}
|
|
169
|
+
function buildKnowledgeFile(rendered, projectName, description, sourceLabel) {
|
|
170
|
+
// Even when knowledge.ts has no imported entries to ship, we still
|
|
171
|
+
// emit the agent-briefing + safety seed so a fresh repo gets a
|
|
172
|
+
// sensible baseline.
|
|
173
|
+
const seedEntries = `
|
|
174
|
+
export const projectOverview = defineKnowledgeEntry({
|
|
175
|
+
id: 'project.overview',
|
|
176
|
+
title: ${JSON.stringify(projectName + ' — project overview')},
|
|
177
|
+
type: KnowledgeType.Architecture,
|
|
178
|
+
priority: KnowledgePriority.High,
|
|
179
|
+
tags: ['overview', 'imported'],
|
|
180
|
+
appliesWhen: ['onboard', 'plan-work'],
|
|
181
|
+
summary: ${JSON.stringify(description || 'High-level overview of this project.')},
|
|
182
|
+
content: ${JSON.stringify(`Imported from ${sourceLabel} via \`shrk import --populate\`. ${description || ''}`.trim())},
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
export const agentBriefing = defineKnowledgeEntry({
|
|
186
|
+
id: 'agent.briefing',
|
|
187
|
+
title: 'AI agent briefing',
|
|
188
|
+
type: KnowledgeType.Convention,
|
|
189
|
+
priority: KnowledgePriority.Critical,
|
|
190
|
+
tags: ['agent', 'safety'],
|
|
191
|
+
appliesWhen: ['agent-start', 'agent-plan'],
|
|
192
|
+
content: \`When working in this repo as an AI agent:
|
|
193
|
+
1. Read \\\`shrk brief\\\` for the focused project context (rules + paths + verification).
|
|
194
|
+
2. Use \\\`shrk task "<task>"\\\` to get a per-task packet (relevant rules + templates + commands).
|
|
195
|
+
3. Generate code via \\\`shrk gen <template> <name> --dry-run --save-plan plan.json\\\`, then apply with \\\`shrk apply plan.json --verify-signature --validate\\\`.
|
|
196
|
+
4. Never bypass \\\`shrk check boundaries\\\` — it gates architecture violations.\`,
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
export const generationSafety = defineKnowledgeEntry({
|
|
200
|
+
id: 'safety.generation',
|
|
201
|
+
title: 'Generation safety',
|
|
202
|
+
type: KnowledgeType.Warning,
|
|
203
|
+
priority: KnowledgePriority.Critical,
|
|
204
|
+
tags: ['safety', 'generation'],
|
|
205
|
+
appliesWhen: ['generate-code', 'overwrite-file'],
|
|
206
|
+
content: \`shrk gen defaults to dry-run. A real write requires --write AND a clean plan (no conflicts). AI agents must call create_generation_plan via MCP first — never write files directly through MCP.\`,
|
|
207
|
+
});
|
|
208
|
+
`;
|
|
209
|
+
const knowledgeBlock = rendered.length > 0
|
|
210
|
+
? `\nconst imported = [\n${rendered.join(',\n')},\n];\n\nexport default [projectOverview, agentBriefing, generationSafety, ...imported];\n`
|
|
211
|
+
: `\nexport default [projectOverview, agentBriefing, generationSafety];\n`;
|
|
212
|
+
return KNOWLEDGE_HELPERS + seedEntries + knowledgeBlock;
|
|
213
|
+
}
|
|
214
|
+
function buildRulesFile(rendered) {
|
|
215
|
+
return (KNOWLEDGE_HELPERS +
|
|
216
|
+
'\nconst rules = [\n' +
|
|
217
|
+
rendered.join(',\n') +
|
|
218
|
+
(rendered.length > 0 ? ',\n' : '') +
|
|
219
|
+
'];\n\nexport default rules;\n');
|
|
220
|
+
}
|
|
221
|
+
function buildPathsFile(rendered) {
|
|
222
|
+
return (KNOWLEDGE_HELPERS +
|
|
223
|
+
'\nconst paths = [\n' +
|
|
224
|
+
rendered.join(',\n') +
|
|
225
|
+
(rendered.length > 0 ? ',\n' : '') +
|
|
226
|
+
'];\n\nexport default paths;\n');
|
|
227
|
+
}
|
|
228
|
+
// ─── Confidence report ─────────────────────────────────────────────────────
|
|
229
|
+
function buildReportMarkdown(report, sourceLabel) {
|
|
230
|
+
const lines = [];
|
|
231
|
+
lines.push('# Imported into populated sharkcraft/');
|
|
232
|
+
lines.push('');
|
|
233
|
+
lines.push(`\`shrk import --populate\` parsed ${sourceLabel} and routed each entry into ` +
|
|
234
|
+
`the canonical \`sharkcraft/*.ts\` files. Re-run with \`--force\` to regenerate.`);
|
|
235
|
+
lines.push('');
|
|
236
|
+
lines.push(`## ✅ Adopted directly (${report.adoptedHigh.length} entries — high confidence)`);
|
|
237
|
+
lines.push('');
|
|
238
|
+
if (report.adoptedHigh.length === 0) {
|
|
239
|
+
lines.push('_None._');
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
for (const line of report.adoptedHigh) {
|
|
243
|
+
lines.push(`- **[${line.kind}] \`${line.id}\`** — ${line.title} _(${line.reason})_`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
lines.push('');
|
|
247
|
+
lines.push(`## 🟡 Adopted with review marker (${report.adoptedMedium.length} entries — medium confidence)`);
|
|
248
|
+
lines.push('');
|
|
249
|
+
lines.push('These entries had medium priority or a thin body. Written with a ' +
|
|
250
|
+
'`// TODO: review` comment — read them, decide if they\'re load-bearing, ' +
|
|
251
|
+
'then either tighten the rule or remove the marker.');
|
|
252
|
+
lines.push('');
|
|
253
|
+
if (report.adoptedMedium.length === 0) {
|
|
254
|
+
lines.push('_None._');
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
for (const line of report.adoptedMedium) {
|
|
258
|
+
lines.push(`- **[${line.kind}] \`${line.id}\`** — ${line.title} _(${line.reason})_`);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
lines.push('');
|
|
262
|
+
lines.push(`## ⚠️ Not adopted (${report.dropped.length} entries — low confidence)`);
|
|
263
|
+
lines.push('');
|
|
264
|
+
lines.push('These entries had thin bodies (title-only) or were a type that needs a ' +
|
|
265
|
+
'runnable body (templates). Inspect the original source to decide whether ' +
|
|
266
|
+
'to author them by hand.');
|
|
267
|
+
lines.push('');
|
|
268
|
+
if (report.dropped.length === 0) {
|
|
269
|
+
lines.push('_None — every entry was strong enough to adopt._');
|
|
270
|
+
}
|
|
271
|
+
else {
|
|
272
|
+
for (const line of report.dropped) {
|
|
273
|
+
lines.push(`- **[${line.kind}] \`${line.id}\`** — ${line.title} _(${line.reason})_`);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
lines.push('');
|
|
277
|
+
lines.push(`## ✍️ What \`shrk import\` deliberately doesn't try to recover`);
|
|
278
|
+
lines.push('');
|
|
279
|
+
for (const item of report.notInferable)
|
|
280
|
+
lines.push(`- ${item}`);
|
|
281
|
+
lines.push('');
|
|
282
|
+
lines.push('---');
|
|
283
|
+
lines.push('');
|
|
284
|
+
lines.push('_Generated by `shrk import --populate`. This report is regenerated each time ' +
|
|
285
|
+
'you re-run with `--force`._');
|
|
286
|
+
return lines.join('\n') + '\n';
|
|
287
|
+
}
|
|
288
|
+
const NOT_INFERABLE_DEFAULTS = [
|
|
289
|
+
'Runnable template bodies — markdown describes templates but doesn\'t give you the scaffold body. Templates land in the dropped list; author them in `sharkcraft/templates.ts`.',
|
|
290
|
+
'Boundary rules — natural-language "don\'t import X from Y" doesn\'t map cleanly to the structured `from` / `forbiddenImports` shape. Use `shrk init --infer` for layer detection from the import graph.',
|
|
291
|
+
'Pipeline workflows — sequenced steps with named ids need more structure than a markdown list. Use `shrk init --infer` for pipeline detection from package.json scripts.',
|
|
292
|
+
'Verification commands — declared in `sharkcraft.config.ts` `verificationCommands[]`, not in knowledge entries. The config file shipped here lists none; add yours.',
|
|
293
|
+
];
|
|
294
|
+
// ─── Public entry point ─────────────────────────────────────────────────────
|
|
295
|
+
/**
|
|
296
|
+
* Convert a parsed `IImportResult` into populated sharkcraft/*.ts
|
|
297
|
+
* files plus a companion confidence report.
|
|
298
|
+
*
|
|
299
|
+
* Pure function — caller writes the bytes. Same shape as
|
|
300
|
+
* `synthesizeFromOnboarding` in `@shrkcrft/inspector`.
|
|
301
|
+
*/
|
|
302
|
+
export function synthesizePopulatedFromImport(entries, options) {
|
|
303
|
+
const filtered = options.filterTags && options.filterTags.length > 0
|
|
304
|
+
? entries.filter((e) => e.tags.some((t) => options.filterTags.includes(t)))
|
|
305
|
+
: entries;
|
|
306
|
+
const rulesRendered = [];
|
|
307
|
+
const pathsRendered = [];
|
|
308
|
+
const knowledgeRendered = [];
|
|
309
|
+
const adoptedHigh = [];
|
|
310
|
+
const adoptedMedium = [];
|
|
311
|
+
const dropped = [];
|
|
312
|
+
// Sort deterministically by id for stable output.
|
|
313
|
+
const sorted = [...filtered].sort((a, b) => a.id.localeCompare(b.id));
|
|
314
|
+
for (const entry of sorted) {
|
|
315
|
+
const dest = destinationOf(entry);
|
|
316
|
+
const tier = tierOf(entry);
|
|
317
|
+
const kindForReport = dest === 'rules' ? 'rule' : dest === 'paths' ? 'path' : dest === 'knowledge' ? 'knowledge' : 'template';
|
|
318
|
+
if (dest === 'drop' || tier === 'low') {
|
|
319
|
+
dropped.push({
|
|
320
|
+
kind: kindForReport,
|
|
321
|
+
id: entry.id,
|
|
322
|
+
title: entry.title,
|
|
323
|
+
reason: dest === 'drop'
|
|
324
|
+
? 'KnowledgeType.Template — needs a runnable body, not recoverable from markdown'
|
|
325
|
+
: 'thin body or title-only entry',
|
|
326
|
+
origin: entry.origin,
|
|
327
|
+
});
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
// Always render the `type` field — the knowledge loader requires
|
|
331
|
+
// it on every entry, even in rules.ts / paths.ts where the file
|
|
332
|
+
// context might seem to imply it. Validator catches missing-type
|
|
333
|
+
// as a blocker error otherwise.
|
|
334
|
+
const rendered = renderEntry(entry, tier, true);
|
|
335
|
+
if (dest === 'rules')
|
|
336
|
+
rulesRendered.push(rendered);
|
|
337
|
+
else if (dest === 'paths')
|
|
338
|
+
pathsRendered.push(rendered);
|
|
339
|
+
else
|
|
340
|
+
knowledgeRendered.push(rendered);
|
|
341
|
+
const reason = tier === 'high'
|
|
342
|
+
? `${entry.priority} priority, ${entry.content.trim().length} chars of body`
|
|
343
|
+
: `${entry.priority} priority — review the marker`;
|
|
344
|
+
(tier === 'high' ? adoptedHigh : adoptedMedium).push({
|
|
345
|
+
kind: kindForReport,
|
|
346
|
+
id: entry.id,
|
|
347
|
+
title: entry.title,
|
|
348
|
+
reason,
|
|
349
|
+
origin: entry.origin,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
const files = [];
|
|
353
|
+
files.push({
|
|
354
|
+
path: 'sharkcraft.config.ts',
|
|
355
|
+
kind: 'config',
|
|
356
|
+
content: buildConfigFile({
|
|
357
|
+
projectName: options.projectName,
|
|
358
|
+
description: options.description ?? '',
|
|
359
|
+
includeRules: rulesRendered.length > 0,
|
|
360
|
+
includePaths: pathsRendered.length > 0,
|
|
361
|
+
}),
|
|
362
|
+
});
|
|
363
|
+
files.push({
|
|
364
|
+
path: 'knowledge.ts',
|
|
365
|
+
kind: 'knowledge',
|
|
366
|
+
content: buildKnowledgeFile(knowledgeRendered, options.projectName, options.description ?? '', options.sourceLabel),
|
|
367
|
+
});
|
|
368
|
+
if (rulesRendered.length > 0) {
|
|
369
|
+
files.push({
|
|
370
|
+
path: 'rules.ts',
|
|
371
|
+
kind: 'rules',
|
|
372
|
+
content: buildRulesFile(rulesRendered),
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
if (pathsRendered.length > 0) {
|
|
376
|
+
files.push({
|
|
377
|
+
path: 'paths.ts',
|
|
378
|
+
kind: 'paths',
|
|
379
|
+
content: buildPathsFile(pathsRendered),
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
const report = {
|
|
383
|
+
adoptedHigh,
|
|
384
|
+
adoptedMedium,
|
|
385
|
+
dropped,
|
|
386
|
+
notInferable: NOT_INFERABLE_DEFAULTS,
|
|
387
|
+
};
|
|
388
|
+
files.push({
|
|
389
|
+
path: '.imported-report.md',
|
|
390
|
+
kind: 'report-md',
|
|
391
|
+
content: buildReportMarkdown(report, options.sourceLabel),
|
|
392
|
+
});
|
|
393
|
+
files.push({
|
|
394
|
+
path: '.imported-report.json',
|
|
395
|
+
kind: 'report-json',
|
|
396
|
+
content: JSON.stringify({
|
|
397
|
+
schema: 'sharkcraft.imported-report/v1',
|
|
398
|
+
sourceLabel: options.sourceLabel,
|
|
399
|
+
adoptedHigh,
|
|
400
|
+
adoptedMedium,
|
|
401
|
+
dropped,
|
|
402
|
+
notInferable: report.notInferable,
|
|
403
|
+
}, null, 2) + '\n',
|
|
404
|
+
});
|
|
405
|
+
return { files, report };
|
|
406
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,4 +8,5 @@ export * from './api/import-agents-md.js';
|
|
|
8
8
|
export * from './api/import-claude-md.js';
|
|
9
9
|
export * from './api/import-cursor-rules.js';
|
|
10
10
|
export * from './emit/emit-knowledge-ts.js';
|
|
11
|
+
export * from './emit/synthesize-populated.js';
|
|
11
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,oBAAoB,CAAC;AACnC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gCAAgC,CAAC"}
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shrkcrft/importer",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.8",
|
|
4
4
|
"description": "SharkCraft importer: parse AGENTS.md / CLAUDE.md / .cursor/rules into structured knowledge entries.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "SharkCraft contributors",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"typecheck": "tsc --noEmit -p tsconfig.json"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@shrkcrft/core": "^0.1.0-alpha.
|
|
48
|
-
"@shrkcrft/knowledge": "^0.1.0-alpha.
|
|
47
|
+
"@shrkcrft/core": "^0.1.0-alpha.8",
|
|
48
|
+
"@shrkcrft/knowledge": "^0.1.0-alpha.8"
|
|
49
49
|
},
|
|
50
50
|
"publishConfig": {
|
|
51
51
|
"access": "public"
|