minimal-vibe-coding-kit 0.5.0 → 0.5.1
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/.agents/skills/tutien/SKILL.md +36 -13
- package/.agents/skills/tutien/references/adaptive-response.md +68 -0
- package/.agents/skills/tutien/references/lore-sources.md +28 -0
- package/.agents/skills/tutien/references/privacy.md +13 -1
- package/.agents/skills/tutien/references/schema.md +32 -1
- package/.agents/skills/tutien/references/story-system.md +176 -0
- package/.agents/skills/tutien/references/vi-style-guide.md +89 -0
- package/.agents/skills/tutien/references/voice-and-mode.md +23 -8
- package/.agents/skills/tutien/scripts/catalog.mjs +17 -17
- package/.agents/skills/tutien/scripts/classify.mjs +64 -37
- package/.agents/skills/tutien/scripts/command.mjs +40 -21
- package/.agents/skills/tutien/scripts/project-profile.mjs +202 -0
- package/.agents/skills/tutien/scripts/render-report.mjs +128 -72
- package/.agents/skills/tutien/scripts/response-brief.mjs +103 -0
- package/.agents/skills/tutien/scripts/run-tutien.mjs +141 -63
- package/.agents/skills/tutien/scripts/story-ledger.mjs +241 -0
- package/.agents/skills/tutien/scripts/villains.mjs +14 -14
- package/.claude/skills/tutien/SKILL.md +36 -13
- package/.claude/skills/tutien/references/adaptive-response.md +68 -0
- package/.claude/skills/tutien/references/lore-sources.md +28 -0
- package/.claude/skills/tutien/references/privacy.md +13 -1
- package/.claude/skills/tutien/references/schema.md +32 -1
- package/.claude/skills/tutien/references/story-system.md +176 -0
- package/.claude/skills/tutien/references/vi-style-guide.md +89 -0
- package/.claude/skills/tutien/references/voice-and-mode.md +23 -8
- package/.claude/skills/tutien/scripts/catalog.mjs +17 -17
- package/.claude/skills/tutien/scripts/classify.mjs +64 -37
- package/.claude/skills/tutien/scripts/command.mjs +40 -21
- package/.claude/skills/tutien/scripts/project-profile.mjs +202 -0
- package/.claude/skills/tutien/scripts/render-report.mjs +128 -72
- package/.claude/skills/tutien/scripts/response-brief.mjs +103 -0
- package/.claude/skills/tutien/scripts/run-tutien.mjs +141 -63
- package/.claude/skills/tutien/scripts/story-ledger.mjs +241 -0
- package/.claude/skills/tutien/scripts/villains.mjs +14 -14
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor/skills/tutien/SKILL.md +36 -13
- package/.cursor/skills/tutien/references/adaptive-response.md +68 -0
- package/.cursor/skills/tutien/references/lore-sources.md +28 -0
- package/.cursor/skills/tutien/references/privacy.md +13 -1
- package/.cursor/skills/tutien/references/schema.md +32 -1
- package/.cursor/skills/tutien/references/story-system.md +176 -0
- package/.cursor/skills/tutien/references/vi-style-guide.md +89 -0
- package/.cursor/skills/tutien/references/voice-and-mode.md +23 -8
- package/.cursor/skills/tutien/scripts/catalog.mjs +17 -17
- package/.cursor/skills/tutien/scripts/classify.mjs +64 -37
- package/.cursor/skills/tutien/scripts/command.mjs +40 -21
- package/.cursor/skills/tutien/scripts/project-profile.mjs +202 -0
- package/.cursor/skills/tutien/scripts/render-report.mjs +128 -72
- package/.cursor/skills/tutien/scripts/response-brief.mjs +103 -0
- package/.cursor/skills/tutien/scripts/run-tutien.mjs +141 -63
- package/.cursor/skills/tutien/scripts/story-ledger.mjs +241 -0
- package/.cursor/skills/tutien/scripts/villains.mjs +14 -14
- package/.grok/skills/tutien/SKILL.md +36 -13
- package/.grok/skills/tutien/references/adaptive-response.md +68 -0
- package/.grok/skills/tutien/references/lore-sources.md +28 -0
- package/.grok/skills/tutien/references/privacy.md +13 -1
- package/.grok/skills/tutien/references/schema.md +32 -1
- package/.grok/skills/tutien/references/story-system.md +176 -0
- package/.grok/skills/tutien/references/vi-style-guide.md +89 -0
- package/.grok/skills/tutien/references/voice-and-mode.md +23 -8
- package/.grok/skills/tutien/scripts/catalog.mjs +17 -17
- package/.grok/skills/tutien/scripts/classify.mjs +64 -37
- package/.grok/skills/tutien/scripts/command.mjs +40 -21
- package/.grok/skills/tutien/scripts/project-profile.mjs +202 -0
- package/.grok/skills/tutien/scripts/render-report.mjs +128 -72
- package/.grok/skills/tutien/scripts/response-brief.mjs +103 -0
- package/.grok/skills/tutien/scripts/run-tutien.mjs +141 -63
- package/.grok/skills/tutien/scripts/story-ledger.mjs +241 -0
- package/.grok/skills/tutien/scripts/villains.mjs +14 -14
- package/.vibekit/skills/tutien/SKILL.md +36 -13
- package/.vibekit/skills/tutien/references/adaptive-response.md +68 -0
- package/.vibekit/skills/tutien/references/lore-sources.md +28 -0
- package/.vibekit/skills/tutien/references/privacy.md +13 -1
- package/.vibekit/skills/tutien/references/schema.md +32 -1
- package/.vibekit/skills/tutien/references/story-system.md +176 -0
- package/.vibekit/skills/tutien/references/vi-style-guide.md +89 -0
- package/.vibekit/skills/tutien/references/voice-and-mode.md +23 -8
- package/.vibekit/skills/tutien/scripts/catalog.mjs +17 -17
- package/.vibekit/skills/tutien/scripts/classify.mjs +64 -37
- package/.vibekit/skills/tutien/scripts/command.mjs +40 -21
- package/.vibekit/skills/tutien/scripts/project-profile.mjs +202 -0
- package/.vibekit/skills/tutien/scripts/render-report.mjs +128 -72
- package/.vibekit/skills/tutien/scripts/response-brief.mjs +103 -0
- package/.vibekit/skills/tutien/scripts/run-tutien.mjs +141 -63
- package/.vibekit/skills/tutien/scripts/story-ledger.mjs +241 -0
- package/.vibekit/skills/tutien/scripts/villains.mjs +14 -14
- package/CHANGELOG.md +22 -1
- package/README.md +5 -3
- package/docs/README.vi.md +5 -3
- package/docs/README.zh-CN.md +5 -3
- package/package.json +2 -2
|
@@ -13,6 +13,14 @@ const TONE_ALIASES = new Map([
|
|
|
13
13
|
['gentle', 'serene'],
|
|
14
14
|
['spicy', 'spirited']
|
|
15
15
|
]);
|
|
16
|
+
const STORY_LANGUAGES = new Set(['auto', 'vi', 'en', 'zh']);
|
|
17
|
+
const STORY_STYLES = new Set(['auto', 'classic-quest', 'web-serial', 'daily-life', 'clan-epic', 'comic-adventure']);
|
|
18
|
+
const STORY_FOCUSES = new Set(['balanced', 'project', 'characters', 'world', 'sect-politics']);
|
|
19
|
+
|
|
20
|
+
const allowedOr = (value, allowed, fallback) => allowed.has(String(value).toLowerCase()) ? String(value).toLowerCase() : fallback;
|
|
21
|
+
export const normalizeStoryLanguage = (value) => allowedOr(value, STORY_LANGUAGES, 'auto');
|
|
22
|
+
export const normalizeStoryStyle = (value) => allowedOr(value, STORY_STYLES, 'auto');
|
|
23
|
+
export const normalizeStoryFocus = (value) => allowedOr(value, STORY_FOCUSES, 'balanced');
|
|
16
24
|
|
|
17
25
|
export const TUTIEN_EXPERIENCE = Object.freeze({
|
|
18
26
|
kind: 'wholesome-coding-classification-game',
|
|
@@ -48,15 +56,21 @@ export function parseInvocation(argsString = '') {
|
|
|
48
56
|
score: 'hidden',
|
|
49
57
|
includeExcerpts: false,
|
|
50
58
|
range: 'all',
|
|
51
|
-
sources: []
|
|
59
|
+
sources: [],
|
|
60
|
+
story: 'on',
|
|
61
|
+
storyLanguage: 'auto',
|
|
62
|
+
storyStyle: 'auto',
|
|
63
|
+
storyFocus: 'balanced',
|
|
64
|
+
output: 'brief'
|
|
52
65
|
};
|
|
66
|
+
const providedOptions = new Set();
|
|
53
67
|
let action = null;
|
|
54
68
|
|
|
55
69
|
// A direct request to end the experience always wins over report parsing.
|
|
56
70
|
// This keeps prose mode and approval state from lingering after the user
|
|
57
71
|
// asks to return to the kit's normal voice.
|
|
58
72
|
if (isStopRequest(argsString)) {
|
|
59
|
-
return { action: 'off', isModeToggle: true, explicitAction: true, options };
|
|
73
|
+
return { action: 'off', isModeToggle: true, explicitAction: true, options, providedOptions: [] };
|
|
60
74
|
}
|
|
61
75
|
|
|
62
76
|
for (const tok of tokens) {
|
|
@@ -68,24 +82,29 @@ export function parseInvocation(argsString = '') {
|
|
|
68
82
|
const key = tok.slice(0, eq);
|
|
69
83
|
const value = tok.slice(eq + 1);
|
|
70
84
|
switch (key) {
|
|
71
|
-
case 'language': options.language = value; break;
|
|
72
|
-
case 'tone': options.tone = normalizeTone(value); break;
|
|
73
|
-
case 'villains': options.villains = value === 'off' ? 'off' : 'on'; break;
|
|
74
|
-
case 'score': options.score = value === 'show' ? 'show' : 'hidden'; break;
|
|
75
|
-
case 'scope': options.scope = value; break;
|
|
76
|
-
case 'privacy': options.privacy = value; break;
|
|
77
|
-
case 'range': options.range = value; break;
|
|
78
|
-
case 'include-excerpts': options.includeExcerpts = value === 'true'; break;
|
|
79
|
-
case 'sources': options.sources = value.split(',').filter(Boolean); break;
|
|
80
|
-
case 'previous': options.previous = value; break;
|
|
81
|
-
case 'metric': options.metric = value; break;
|
|
82
|
-
case 'approve': options.approve = value; break;
|
|
83
|
-
case 'snapshot': options.snapshot = value === 'true'; break;
|
|
84
|
-
case 'faction': options.faction = value; break;
|
|
85
|
-
case 'affiliation': options.affiliation = value; break;
|
|
86
|
-
case 'paths': options.paths = value.split(',').filter(Boolean); break;
|
|
87
|
-
case 'domains': options.domains = value.split(',').filter(Boolean); break;
|
|
88
|
-
case 'authorization': options.authorization = value; break;
|
|
85
|
+
case 'language': providedOptions.add(key); options.language = value; break;
|
|
86
|
+
case 'tone': providedOptions.add(key); options.tone = normalizeTone(value); break;
|
|
87
|
+
case 'villains': providedOptions.add(key); options.villains = value === 'off' ? 'off' : 'on'; break;
|
|
88
|
+
case 'score': providedOptions.add(key); options.score = value === 'show' ? 'show' : 'hidden'; break;
|
|
89
|
+
case 'scope': providedOptions.add(key); options.scope = value; break;
|
|
90
|
+
case 'privacy': providedOptions.add(key); options.privacy = value; break;
|
|
91
|
+
case 'range': providedOptions.add(key); options.range = value; break;
|
|
92
|
+
case 'include-excerpts': providedOptions.add(key); options.includeExcerpts = value === 'true'; break;
|
|
93
|
+
case 'sources': providedOptions.add(key); options.sources = value.split(',').filter(Boolean); break;
|
|
94
|
+
case 'previous': providedOptions.add(key); options.previous = value; break;
|
|
95
|
+
case 'metric': providedOptions.add(key); options.metric = value; break;
|
|
96
|
+
case 'approve': providedOptions.add(key); options.approve = value; break;
|
|
97
|
+
case 'snapshot': providedOptions.add(key); options.snapshot = value === 'true'; break;
|
|
98
|
+
case 'faction': providedOptions.add(key); options.faction = value; break;
|
|
99
|
+
case 'affiliation': providedOptions.add(key); options.affiliation = value; break;
|
|
100
|
+
case 'paths': providedOptions.add(key); options.paths = value.split(',').filter(Boolean); break;
|
|
101
|
+
case 'domains': providedOptions.add(key); options.domains = value.split(',').filter(Boolean); break;
|
|
102
|
+
case 'authorization': providedOptions.add(key); options.authorization = value; break;
|
|
103
|
+
case 'story': providedOptions.add(key); options.story = value === 'off' ? 'off' : 'on'; break;
|
|
104
|
+
case 'story-language': providedOptions.add(key); options.storyLanguage = normalizeStoryLanguage(value); break;
|
|
105
|
+
case 'story-style': providedOptions.add(key); options.storyStyle = normalizeStoryStyle(value); break;
|
|
106
|
+
case 'story-focus': providedOptions.add(key); options.storyFocus = normalizeStoryFocus(value); break;
|
|
107
|
+
case 'output': providedOptions.add(key); options.output = value === 'ledger' ? 'ledger' : 'brief'; break;
|
|
89
108
|
default: break;
|
|
90
109
|
}
|
|
91
110
|
}
|
|
@@ -96,7 +115,7 @@ export function parseInvocation(argsString = '') {
|
|
|
96
115
|
const explicitAction = action !== null;
|
|
97
116
|
if (!action) action = 'preview';
|
|
98
117
|
const isModeToggle = action === 'on' || action === 'off' || action === 'status';
|
|
99
|
-
return { action, isModeToggle, explicitAction, options };
|
|
118
|
+
return { action, isModeToggle, explicitAction, options, providedOptions: [...providedOptions] };
|
|
100
119
|
}
|
|
101
120
|
|
|
102
121
|
// The livelier `spirited` voice is explicit opt-in. Safety-sensitive contexts
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// Read a deliberately small, known set of project manifests and reduce them
|
|
2
|
+
// to safe metadata. Repo scripts are never executed and their bodies are never
|
|
3
|
+
// returned or persisted.
|
|
4
|
+
|
|
5
|
+
import fs from 'node:fs';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
|
|
8
|
+
const MAX_METADATA_BYTES = 1024 * 1024;
|
|
9
|
+
const MANIFESTS = Object.freeze([
|
|
10
|
+
'backbone.yml',
|
|
11
|
+
'package.json',
|
|
12
|
+
'yarn.lock',
|
|
13
|
+
'package-lock.json',
|
|
14
|
+
'pnpm-lock.yaml',
|
|
15
|
+
'bun.lock',
|
|
16
|
+
'bun.lockb',
|
|
17
|
+
'tsconfig.json',
|
|
18
|
+
'pyproject.toml',
|
|
19
|
+
'go.mod',
|
|
20
|
+
'Cargo.toml',
|
|
21
|
+
'pubspec.yaml',
|
|
22
|
+
'composer.json',
|
|
23
|
+
'pom.xml',
|
|
24
|
+
'build.gradle',
|
|
25
|
+
'build.gradle.kts'
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
const STACK_RULES = Object.freeze([
|
|
29
|
+
['react', ['react']],
|
|
30
|
+
['typescript', ['typescript']],
|
|
31
|
+
['vite', ['vite']],
|
|
32
|
+
['material-ui', ['@mui/material', '@material-ui/core']],
|
|
33
|
+
['nextjs', ['next']],
|
|
34
|
+
['vue', ['vue']],
|
|
35
|
+
['nuxt', ['nuxt']],
|
|
36
|
+
['angular', ['@angular/core']],
|
|
37
|
+
['svelte', ['svelte']],
|
|
38
|
+
['tailwind-css', ['tailwindcss']],
|
|
39
|
+
['express', ['express']],
|
|
40
|
+
['nestjs', ['@nestjs/core']]
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
const SAFE_SCRIPT = /^[A-Za-z0-9][A-Za-z0-9:_-]{0,63}$/;
|
|
44
|
+
const VALIDATION_PRIORITY = ['validate', 'check', 'test', 'test:unit', 'lint', 'typecheck', 'build'];
|
|
45
|
+
|
|
46
|
+
function regularFile(root, relative) {
|
|
47
|
+
const absolute = path.join(path.resolve(root), relative);
|
|
48
|
+
try {
|
|
49
|
+
const stat = fs.lstatSync(absolute);
|
|
50
|
+
if (!stat.isFile() || stat.isSymbolicLink()) return null;
|
|
51
|
+
return { path: relative, absolute, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) };
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function inventoryProjectMetadata(root) {
|
|
58
|
+
return MANIFESTS.map((name) => regularFile(root, name))
|
|
59
|
+
.filter(Boolean)
|
|
60
|
+
.map(({ path: relative, size, mtimeMs }) => ({ path: relative, size, mtimeMs }));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function readKnownFile(root, name, warnings) {
|
|
64
|
+
const item = regularFile(root, name);
|
|
65
|
+
if (!item) return null;
|
|
66
|
+
if (item.size > MAX_METADATA_BYTES) {
|
|
67
|
+
warnings.push({ code: 'metadata-too-large', path: name });
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
return fs.readFileSync(item.absolute, 'utf8');
|
|
72
|
+
} catch {
|
|
73
|
+
warnings.push({ code: 'metadata-unreadable', path: name });
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function safeSlug(value, fallback = null) {
|
|
79
|
+
const text = String(value ?? '').trim().toLowerCase();
|
|
80
|
+
return /^[a-z0-9][a-z0-9._-]{0,79}$/.test(text) ? text : fallback;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function yamlScalar(text, key) {
|
|
84
|
+
const match = text?.match(new RegExp(`^\\s*${key}:\\s*(.+)$`, 'm'));
|
|
85
|
+
if (!match) return null;
|
|
86
|
+
const value = match[1].trim().replace(/^['"]|['"]$/g, '');
|
|
87
|
+
return value.length <= 160 && !/[\r\n]/.test(value) ? value : null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function packageManager(pkg, files) {
|
|
91
|
+
const declared = String(pkg?.packageManager ?? '').match(/^(npm|yarn|pnpm|bun)@/i)?.[1]?.toLowerCase();
|
|
92
|
+
if (declared) return declared;
|
|
93
|
+
if (files.has('pnpm-lock.yaml')) return 'pnpm';
|
|
94
|
+
if (files.has('yarn.lock')) return 'yarn';
|
|
95
|
+
if (files.has('bun.lock') || files.has('bun.lockb')) return 'bun';
|
|
96
|
+
return 'npm';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function scriptCommand(manager, key) {
|
|
100
|
+
if (manager === 'yarn') return `yarn ${key}`;
|
|
101
|
+
if (manager === 'pnpm') return `pnpm ${key}`;
|
|
102
|
+
if (manager === 'bun') return `bun run ${key}`;
|
|
103
|
+
return `npm run ${key}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function packageFacts(pkg, files) {
|
|
107
|
+
const deps = new Set([
|
|
108
|
+
...Object.keys(pkg?.dependencies ?? {}),
|
|
109
|
+
...Object.keys(pkg?.devDependencies ?? {}),
|
|
110
|
+
...Object.keys(pkg?.peerDependencies ?? {})
|
|
111
|
+
]);
|
|
112
|
+
const stack = STACK_RULES.filter(([, names]) => names.some((name) => deps.has(name))).map(([name]) => name);
|
|
113
|
+
if (files.has('tsconfig.json') && !stack.includes('typescript')) stack.push('typescript');
|
|
114
|
+
|
|
115
|
+
const manager = packageManager(pkg, files);
|
|
116
|
+
const scripts = Object.keys(pkg?.scripts ?? {}).filter((key) => SAFE_SCRIPT.test(key));
|
|
117
|
+
const validationCommands = VALIDATION_PRIORITY
|
|
118
|
+
.filter((key) => scripts.includes(key))
|
|
119
|
+
.slice(0, 3)
|
|
120
|
+
.map((key) => scriptCommand(manager, key));
|
|
121
|
+
|
|
122
|
+
const hasWebFramework = stack.some((name) => ['react', 'nextjs', 'vue', 'nuxt', 'angular', 'svelte'].includes(name));
|
|
123
|
+
const hasLibraryEntry = Boolean(pkg?.main || pkg?.module || pkg?.exports);
|
|
124
|
+
return {
|
|
125
|
+
projectId: safeSlug(pkg?.name),
|
|
126
|
+
packageManager: manager,
|
|
127
|
+
stack,
|
|
128
|
+
primaryLanguage: stack.includes('typescript') ? 'typescript' : 'javascript',
|
|
129
|
+
projectType: hasLibraryEntry ? 'library' : (hasWebFramework ? 'web-app' : 'node-project'),
|
|
130
|
+
validationCommands
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function uniqueSlugs(values, limit = 16) {
|
|
135
|
+
return [...new Set(values.map((value) => safeSlug(value)).filter(Boolean))].slice(0, limit);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function buildProjectProfile(root, options = {}) {
|
|
139
|
+
const resolvedRoot = path.resolve(root);
|
|
140
|
+
const inventory = inventoryProjectMetadata(resolvedRoot);
|
|
141
|
+
const files = new Set(inventory.map((item) => item.path));
|
|
142
|
+
const warnings = [];
|
|
143
|
+
const profile = {
|
|
144
|
+
projectId: safeSlug(path.basename(resolvedRoot), 'repo'),
|
|
145
|
+
description: null,
|
|
146
|
+
primaryLanguage: null,
|
|
147
|
+
projectType: null,
|
|
148
|
+
domains: [],
|
|
149
|
+
stack: [],
|
|
150
|
+
packageManager: null,
|
|
151
|
+
validationCommands: [],
|
|
152
|
+
recommendedValidation: null,
|
|
153
|
+
metadataSources: inventory.map((item) => item.path),
|
|
154
|
+
kitInstalled: files.has('backbone.yml') && fs.existsSync(path.join(resolvedRoot, '.vibekit', 'skills')),
|
|
155
|
+
authorization: options.authorization ?? null,
|
|
156
|
+
declared: { faction: options.faction, affiliation: options.affiliation, paths: options.paths },
|
|
157
|
+
warnings
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const backbone = readKnownFile(resolvedRoot, 'backbone.yml', warnings);
|
|
161
|
+
if (backbone) {
|
|
162
|
+
profile.description = yamlScalar(backbone, 'description');
|
|
163
|
+
profile.primaryLanguage = safeSlug(yamlScalar(backbone, 'primary_language'));
|
|
164
|
+
profile.projectType = safeSlug(yamlScalar(backbone, 'type'));
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const packageText = readKnownFile(resolvedRoot, 'package.json', warnings);
|
|
168
|
+
if (packageText) {
|
|
169
|
+
try {
|
|
170
|
+
const pkg = JSON.parse(packageText);
|
|
171
|
+
const facts = packageFacts(pkg, files);
|
|
172
|
+
Object.assign(profile, {
|
|
173
|
+
projectId: facts.projectId ?? profile.projectId,
|
|
174
|
+
packageManager: facts.packageManager,
|
|
175
|
+
stack: facts.stack,
|
|
176
|
+
primaryLanguage: profile.primaryLanguage ?? facts.primaryLanguage,
|
|
177
|
+
projectType: profile.projectType ?? facts.projectType,
|
|
178
|
+
validationCommands: facts.validationCommands
|
|
179
|
+
});
|
|
180
|
+
} catch {
|
|
181
|
+
warnings.push({ code: 'metadata-invalid-json', path: 'package.json' });
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (!profile.primaryLanguage) {
|
|
186
|
+
if (files.has('pyproject.toml')) profile.primaryLanguage = 'python';
|
|
187
|
+
else if (files.has('go.mod')) profile.primaryLanguage = 'go';
|
|
188
|
+
else if (files.has('Cargo.toml')) profile.primaryLanguage = 'rust';
|
|
189
|
+
else if (files.has('pubspec.yaml')) profile.primaryLanguage = 'dart';
|
|
190
|
+
}
|
|
191
|
+
if (!profile.projectType) profile.projectType = files.has('package.json') ? 'node-project' : 'software-project';
|
|
192
|
+
|
|
193
|
+
const domains = [...(options.domains ?? [])];
|
|
194
|
+
if (profile.stack.some((name) => ['react', 'nextjs', 'vue', 'nuxt', 'angular', 'svelte'].includes(name))) domains.push('frontend', 'application', 'interface');
|
|
195
|
+
if (profile.stack.includes('material-ui')) domains.push('ui', 'design', 'component');
|
|
196
|
+
if (profile.projectType === 'library') domains.push('library', 'framework');
|
|
197
|
+
profile.domains = uniqueSlugs(domains, 12);
|
|
198
|
+
profile.stack = uniqueSlugs(profile.stack, 12);
|
|
199
|
+
profile.recommendedValidation = profile.validationCommands[0] ?? null;
|
|
200
|
+
return profile;
|
|
201
|
+
}
|
|
202
|
+
|