@ryuenn3123/agentic-senior-core 1.9.1 → 1.9.3
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/.agent-context/policies/llm-judge-threshold.json +14 -5
- package/.agent-context/rules/security.md +92 -0
- package/.agent-context/state/onboarding-report.json +39 -0
- package/.cursorrules +3669 -91
- package/.windsurfrules +3716 -104
- package/bin/agentic-senior-core.js +25 -1520
- package/lib/cli/commands/init.mjs +339 -0
- package/lib/cli/commands/launch.mjs +81 -0
- package/lib/cli/commands/upgrade.mjs +165 -0
- package/lib/cli/compiler.mjs +204 -0
- package/lib/cli/constants.mjs +136 -0
- package/lib/cli/detector.mjs +211 -0
- package/lib/cli/profile-packs.mjs +94 -0
- package/lib/cli/skill-selector.mjs +210 -0
- package/lib/cli/utils.mjs +227 -0
- package/package.json +4 -2
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Selector — Skill platform interaction and domain inference.
|
|
3
|
+
* Depends on: constants.mjs, utils.mjs
|
|
4
|
+
*/
|
|
5
|
+
import fs from 'node:fs/promises';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
SKILL_PLATFORM_DIRECTORY,
|
|
10
|
+
SKILL_PLATFORM_INDEX_PATH,
|
|
11
|
+
} from './constants.mjs';
|
|
12
|
+
|
|
13
|
+
import { normalizeChoiceInput } from './utils.mjs';
|
|
14
|
+
|
|
15
|
+
export async function loadSkillPlatformIndex() {
|
|
16
|
+
const skillPlatformIndexContent = await fs.readFile(SKILL_PLATFORM_INDEX_PATH, 'utf8');
|
|
17
|
+
return JSON.parse(skillPlatformIndexContent);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function normalizeSkillTierInput(rawTierInput) {
|
|
21
|
+
const normalizedTierInput = normalizeChoiceInput(rawTierInput);
|
|
22
|
+
const allowedTierNames = new Set(['standard', 'advance', 'expert', 'above']);
|
|
23
|
+
|
|
24
|
+
if (!allowedTierNames.has(normalizedTierInput)) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return normalizedTierInput;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function findSkillDomainByInput(skillDomainInput, skillDomainEntries) {
|
|
32
|
+
const normalizedSkillDomainInput = normalizeChoiceInput(skillDomainInput);
|
|
33
|
+
|
|
34
|
+
return skillDomainEntries.find((skillDomainEntry) => {
|
|
35
|
+
const normalizedDomainName = normalizeChoiceInput(skillDomainEntry.name);
|
|
36
|
+
const normalizedDisplayName = normalizeChoiceInput(skillDomainEntry.displayName);
|
|
37
|
+
|
|
38
|
+
return normalizedSkillDomainInput === normalizedDomainName || normalizedSkillDomainInput === normalizedDisplayName;
|
|
39
|
+
}) || null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function formatSkillTierList(skillPlatformIndex) {
|
|
43
|
+
return skillPlatformIndex.tiers.map((tierDefinition) => `${tierDefinition.name} (${tierDefinition.description})`).join('\n');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function inferSkillDomainNamesFromSelection(selectedStackFileName, selectedBlueprintFileName) {
|
|
47
|
+
const inferredDomainNames = new Set();
|
|
48
|
+
|
|
49
|
+
if (selectedBlueprintFileName === 'api-nextjs.md' || selectedBlueprintFileName === 'fastapi-service.md') {
|
|
50
|
+
inferredDomainNames.add('frontend');
|
|
51
|
+
inferredDomainNames.add('fullstack');
|
|
52
|
+
inferredDomainNames.add('cli');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (selectedBlueprintFileName === 'go-service.md'
|
|
56
|
+
|| selectedBlueprintFileName === 'spring-boot-api.md'
|
|
57
|
+
|| selectedBlueprintFileName === 'laravel-api.md'
|
|
58
|
+
|| selectedBlueprintFileName === 'aspnet-api.md') {
|
|
59
|
+
inferredDomainNames.add('backend');
|
|
60
|
+
inferredDomainNames.add('fullstack');
|
|
61
|
+
inferredDomainNames.add('cli');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (selectedStackFileName === 'typescript.md') {
|
|
65
|
+
inferredDomainNames.add('frontend');
|
|
66
|
+
inferredDomainNames.add('cli');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (selectedStackFileName === 'go.md'
|
|
70
|
+
|| selectedStackFileName === 'java.md'
|
|
71
|
+
|| selectedStackFileName === 'php.md'
|
|
72
|
+
|| selectedStackFileName === 'csharp.md'
|
|
73
|
+
|| selectedStackFileName === 'python.md'
|
|
74
|
+
|| selectedStackFileName === 'ruby.md'
|
|
75
|
+
|| selectedStackFileName === 'rust.md') {
|
|
76
|
+
inferredDomainNames.add('backend');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (selectedStackFileName === 'react-native.md' || selectedStackFileName === 'flutter.md') {
|
|
80
|
+
inferredDomainNames.add('frontend');
|
|
81
|
+
inferredDomainNames.add('fullstack');
|
|
82
|
+
inferredDomainNames.add('cli');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (selectedBlueprintFileName === 'mobile-app.md') {
|
|
86
|
+
inferredDomainNames.add('frontend');
|
|
87
|
+
inferredDomainNames.add('fullstack');
|
|
88
|
+
inferredDomainNames.add('cli');
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (selectedBlueprintFileName === 'observability.md') {
|
|
92
|
+
inferredDomainNames.add('backend');
|
|
93
|
+
inferredDomainNames.add('fullstack');
|
|
94
|
+
inferredDomainNames.add('cli');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (inferredDomainNames.size === 0) {
|
|
98
|
+
inferredDomainNames.add('fullstack');
|
|
99
|
+
inferredDomainNames.add('cli');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return Array.from(inferredDomainNames);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export async function buildSkillPackSection(skillDomainEntry, selectedTierName) {
|
|
106
|
+
const resolvedPackFileName = skillDomainEntry.tierToPackFileNames?.[selectedTierName]
|
|
107
|
+
|| skillDomainEntry.tierToPackFileNames?.[skillDomainEntry.defaultTier]
|
|
108
|
+
|| skillDomainEntry.defaultPackFileName;
|
|
109
|
+
const skillPackFilePath = path.join(SKILL_PLATFORM_DIRECTORY, resolvedPackFileName);
|
|
110
|
+
const skillPackContent = await fs.readFile(skillPackFilePath, 'utf8');
|
|
111
|
+
|
|
112
|
+
return [
|
|
113
|
+
`## SKILL PACK: ${skillDomainEntry.displayName}`,
|
|
114
|
+
`Source: .agent-context/skills/${resolvedPackFileName}`,
|
|
115
|
+
`Default tier: ${skillDomainEntry.defaultTier}`,
|
|
116
|
+
`Selected tier: ${selectedTierName}`,
|
|
117
|
+
`Evidence: ${skillDomainEntry.evidence}`,
|
|
118
|
+
'',
|
|
119
|
+
skillPackContent.trim(),
|
|
120
|
+
'',
|
|
121
|
+
].join('\n');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export async function runSkillCommand(commandArguments) {
|
|
125
|
+
const parsedSkillOptions = {
|
|
126
|
+
domain: null,
|
|
127
|
+
tier: null,
|
|
128
|
+
tierProvided: false,
|
|
129
|
+
json: false,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
for (let argumentIndex = 0; argumentIndex < commandArguments.length; argumentIndex++) {
|
|
133
|
+
const currentArgument = commandArguments[argumentIndex];
|
|
134
|
+
|
|
135
|
+
if (!currentArgument.startsWith('--')) {
|
|
136
|
+
parsedSkillOptions.domain = currentArgument;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (currentArgument === '--tier') {
|
|
141
|
+
parsedSkillOptions.tier = normalizeSkillTierInput(commandArguments[argumentIndex + 1] || '');
|
|
142
|
+
parsedSkillOptions.tierProvided = true;
|
|
143
|
+
argumentIndex += 1;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (currentArgument.startsWith('--tier=')) {
|
|
148
|
+
parsedSkillOptions.tier = normalizeSkillTierInput(currentArgument.split('=')[1]);
|
|
149
|
+
parsedSkillOptions.tierProvided = true;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (currentArgument === '--json') {
|
|
154
|
+
parsedSkillOptions.json = true;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
throw new Error(`Unknown option: ${currentArgument}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const skillPlatformIndex = await loadSkillPlatformIndex();
|
|
162
|
+
const skillDomainEntries = Object.values(skillPlatformIndex.domains || {});
|
|
163
|
+
const selectedSkillDomain = parsedSkillOptions.domain
|
|
164
|
+
? findSkillDomainByInput(parsedSkillOptions.domain, skillDomainEntries)
|
|
165
|
+
: null;
|
|
166
|
+
|
|
167
|
+
if (parsedSkillOptions.domain && !selectedSkillDomain) {
|
|
168
|
+
throw new Error(`Unknown skill domain: ${parsedSkillOptions.domain}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (parsedSkillOptions.tierProvided && !parsedSkillOptions.tier) {
|
|
172
|
+
throw new Error(`Unknown skill tier: ${commandArguments.join(' ')}`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const selectedTierName = parsedSkillOptions.tier || skillPlatformIndex.defaultTier || 'advance';
|
|
176
|
+
const recommendedPackFileName = selectedSkillDomain
|
|
177
|
+
? selectedSkillDomain.tierToPackFileNames?.[selectedTierName]
|
|
178
|
+
|| selectedSkillDomain.tierToPackFileNames?.[selectedSkillDomain.defaultTier]
|
|
179
|
+
|| selectedSkillDomain.defaultPackFileName
|
|
180
|
+
|| null
|
|
181
|
+
: null;
|
|
182
|
+
|
|
183
|
+
if (parsedSkillOptions.json) {
|
|
184
|
+
console.log(JSON.stringify({
|
|
185
|
+
defaultTier: skillPlatformIndex.defaultTier,
|
|
186
|
+
selectedTier: selectedTierName,
|
|
187
|
+
selectedDomain: selectedSkillDomain,
|
|
188
|
+
recommendedPackFileName,
|
|
189
|
+
}, null, 2));
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
console.log('Skill platform selector');
|
|
194
|
+
console.log(`Default tier: ${skillPlatformIndex.defaultTier}`);
|
|
195
|
+
console.log(`Available tiers:\n${formatSkillTierList(skillPlatformIndex)}`);
|
|
196
|
+
|
|
197
|
+
if (!selectedSkillDomain) {
|
|
198
|
+
console.log('\nAvailable domains:');
|
|
199
|
+
for (const skillDomainEntry of skillDomainEntries) {
|
|
200
|
+
console.log(`- ${skillDomainEntry.name}: ${skillDomainEntry.description}`);
|
|
201
|
+
}
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
console.log(`\nSelected domain: ${selectedSkillDomain.displayName}`);
|
|
206
|
+
console.log(`Selected tier: ${selectedTierName}`);
|
|
207
|
+
console.log(`Recommended pack: ${recommendedPackFileName}`);
|
|
208
|
+
console.log(`Purpose: ${selectedSkillDomain.description}`);
|
|
209
|
+
console.log(`Evidence: ${selectedSkillDomain.evidence}`);
|
|
210
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI Utilities — Shared helper functions.
|
|
3
|
+
* Depends on: constants.mjs
|
|
4
|
+
*/
|
|
5
|
+
import fs from 'node:fs/promises';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
REPO_ROOT,
|
|
10
|
+
ALLOWED_SEVERITY_LEVELS,
|
|
11
|
+
PROFILE_PRESETS,
|
|
12
|
+
entryPointFiles,
|
|
13
|
+
directoryCopies,
|
|
14
|
+
} from './constants.mjs';
|
|
15
|
+
|
|
16
|
+
export function printUsage() {
|
|
17
|
+
console.log('Agentic-Senior-Core CLI');
|
|
18
|
+
console.log('');
|
|
19
|
+
console.log('Local runtime:');
|
|
20
|
+
console.log(' npm exec --yes @ryuenn3123/agentic-senior-core init');
|
|
21
|
+
console.log(' npx @ryuenn3123/agentic-senior-core init');
|
|
22
|
+
console.log(' npm install -g @ryuenn3123/agentic-senior-core && agentic-senior-core init');
|
|
23
|
+
console.log(' bunx @ryuenn3123/agentic-senior-core init # optional Bun path');
|
|
24
|
+
console.log(' open GitHub template: https://github.com/fatidaprilian/Agentic-Senior-Core/generate');
|
|
25
|
+
console.log('');
|
|
26
|
+
console.log('Usage:');
|
|
27
|
+
console.log(' agentic-senior-core launch');
|
|
28
|
+
console.log(' agentic-senior-core init [target-directory] [--preset <name>] [--profile <beginner|balanced|strict>] [--profile-pack <name>] [--stack <name>] [--blueprint <name>] [--ci <true|false>] [--newbie]');
|
|
29
|
+
console.log(' agentic-senior-core upgrade [target-directory] [--dry-run] [--yes]');
|
|
30
|
+
console.log(' agentic-senior-core skill [domain] [--tier <standard|advance|expert|above>] [--json]');
|
|
31
|
+
console.log(' agentic-senior-core --version');
|
|
32
|
+
console.log('');
|
|
33
|
+
console.log('Options:');
|
|
34
|
+
console.log(' --help Show help');
|
|
35
|
+
console.log(' --version Show CLI version');
|
|
36
|
+
console.log(' --profile Choose beginner, balanced, or strict');
|
|
37
|
+
console.log(' --preset Use a plug-and-play starter preset (frontend-web, backend-api, fullstack-product, platform-governance, mobile-react-native, mobile-flutter, observability-platform)');
|
|
38
|
+
console.log(' --profile-pack Apply a team profile pack (startup, regulated, platform)');
|
|
39
|
+
console.log(' --newbie Alias for --profile beginner');
|
|
40
|
+
console.log(' --stack Override stack selection');
|
|
41
|
+
console.log(' --blueprint Override blueprint selection');
|
|
42
|
+
console.log(' --ci Override CI/CD guardrails (true|false)');
|
|
43
|
+
console.log(' --dry-run Preview upgrade without writing files');
|
|
44
|
+
console.log(' --yes Skip confirmation prompts for upgrade');
|
|
45
|
+
console.log(' --tier Choose a skill tier for the skill selector');
|
|
46
|
+
console.log(' --json Emit machine-readable skill selection output');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function pathExists(targetPath) {
|
|
50
|
+
try {
|
|
51
|
+
await fs.stat(targetPath);
|
|
52
|
+
return true;
|
|
53
|
+
} catch {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function ensureDirectory(directoryPath) {
|
|
59
|
+
await fs.mkdir(directoryPath, { recursive: true });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function copyDirectory(sourceDirectoryPath, targetDirectoryPath) {
|
|
63
|
+
if (path.resolve(sourceDirectoryPath) === path.resolve(targetDirectoryPath)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
await ensureDirectory(targetDirectoryPath);
|
|
68
|
+
const directoryEntries = await fs.readdir(sourceDirectoryPath, { withFileTypes: true });
|
|
69
|
+
|
|
70
|
+
for (const directoryEntry of directoryEntries) {
|
|
71
|
+
const sourceEntryPath = path.join(sourceDirectoryPath, directoryEntry.name);
|
|
72
|
+
const targetEntryPath = path.join(targetDirectoryPath, directoryEntry.name);
|
|
73
|
+
|
|
74
|
+
if (directoryEntry.isDirectory()) {
|
|
75
|
+
await copyDirectory(sourceEntryPath, targetEntryPath);
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (path.resolve(sourceEntryPath) === path.resolve(targetEntryPath)) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
await fs.copyFile(sourceEntryPath, targetEntryPath);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function copyGovernanceAssetsToTarget(resolvedTargetDirectoryPath) {
|
|
88
|
+
for (const sourceDirectoryName of directoryCopies) {
|
|
89
|
+
const sourceDirectoryPath = path.join(REPO_ROOT, sourceDirectoryName);
|
|
90
|
+
if (!(await pathExists(sourceDirectoryPath))) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
await copyDirectory(sourceDirectoryPath, path.join(resolvedTargetDirectoryPath, sourceDirectoryName));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
for (const entryPointFileName of entryPointFiles) {
|
|
98
|
+
const sourceFilePath = path.join(REPO_ROOT, entryPointFileName);
|
|
99
|
+
const targetFilePath = path.join(resolvedTargetDirectoryPath, entryPointFileName);
|
|
100
|
+
|
|
101
|
+
if (!(await pathExists(sourceFilePath))) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (path.resolve(sourceFilePath) === path.resolve(targetFilePath)) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
await ensureDirectory(path.dirname(targetFilePath));
|
|
110
|
+
await fs.copyFile(sourceFilePath, targetFilePath);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export async function askChoice(promptMessage, options, userInterface) {
|
|
115
|
+
console.log(`\n${promptMessage}`);
|
|
116
|
+
options.forEach((choiceLabel, choiceIndex) => {
|
|
117
|
+
console.log(` ${choiceIndex + 1}. ${choiceLabel}`);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
while (true) {
|
|
121
|
+
const selectedRawInput = await userInterface.question('Choose a number: ');
|
|
122
|
+
const selectedIndex = Number.parseInt(selectedRawInput.trim(), 10) - 1;
|
|
123
|
+
|
|
124
|
+
if (Number.isNaN(selectedIndex) || selectedIndex < 0 || selectedIndex >= options.length) {
|
|
125
|
+
console.log('Invalid choice. Please select a valid number.');
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return options[selectedIndex];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export async function askYesNo(promptMessage, userInterface, defaultValue) {
|
|
134
|
+
const suffix = typeof defaultValue === 'boolean'
|
|
135
|
+
? defaultValue ? ' (Y/n): ' : ' (y/N): '
|
|
136
|
+
: ' (y/n): ';
|
|
137
|
+
|
|
138
|
+
while (true) {
|
|
139
|
+
const answer = await userInterface.question(`\n${promptMessage}${suffix}`);
|
|
140
|
+
const normalizedAnswer = answer.trim().toLowerCase();
|
|
141
|
+
|
|
142
|
+
if (!normalizedAnswer && typeof defaultValue === 'boolean') {
|
|
143
|
+
return defaultValue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (normalizedAnswer === 'y' || normalizedAnswer === 'yes') return true;
|
|
147
|
+
if (normalizedAnswer === 'n' || normalizedAnswer === 'no') return false;
|
|
148
|
+
|
|
149
|
+
console.log("Please answer with 'y' or 'n'.");
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function toTitleCase(fileName) {
|
|
154
|
+
return fileName
|
|
155
|
+
.replace(/\.md$/i, '')
|
|
156
|
+
.split(/[-_]/g)
|
|
157
|
+
.map((wordPart) => wordPart.charAt(0).toUpperCase() + wordPart.slice(1))
|
|
158
|
+
.join(' ');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function normalizeChoiceInput(rawInput) {
|
|
162
|
+
return rawInput.trim().toLowerCase().replace(/\s+/g, '-');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export function matchFileNameFromInput(rawInput, fileNames) {
|
|
166
|
+
const normalizedInput = normalizeChoiceInput(rawInput);
|
|
167
|
+
|
|
168
|
+
return fileNames.find((fileName) => {
|
|
169
|
+
const normalizedFileName = normalizeChoiceInput(fileName.replace(/\.md$/i, ''));
|
|
170
|
+
const normalizedTitle = normalizeChoiceInput(toTitleCase(fileName));
|
|
171
|
+
return normalizedInput === normalizedFileName || normalizedInput === normalizedTitle;
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function matchProfileNameFromInput(rawInput) {
|
|
176
|
+
const normalizedInput = normalizeChoiceInput(rawInput);
|
|
177
|
+
return Object.keys(PROFILE_PRESETS).find((profileName) => profileName === normalizedInput) || null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function parseBooleanSetting(rawBooleanValue, contextLabel) {
|
|
181
|
+
const normalizedValue = normalizeChoiceInput(rawBooleanValue);
|
|
182
|
+
|
|
183
|
+
if (normalizedValue === 'true') {
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (normalizedValue === 'false') {
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
throw new Error(`Invalid boolean value for ${contextLabel}: ${rawBooleanValue}`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export function parseBlockingSeverities(rawSeverityValues, fileName) {
|
|
195
|
+
const parsedSeverities = rawSeverityValues
|
|
196
|
+
.split(',')
|
|
197
|
+
.map((severityValue) => normalizeChoiceInput(severityValue))
|
|
198
|
+
.filter(Boolean);
|
|
199
|
+
|
|
200
|
+
if (parsedSeverities.length === 0) {
|
|
201
|
+
throw new Error(`Profile pack ${fileName} must define at least one blocking severity.`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const invalidSeverity = parsedSeverities.find((severityValue) => !ALLOWED_SEVERITY_LEVELS.has(severityValue));
|
|
205
|
+
if (invalidSeverity) {
|
|
206
|
+
throw new Error(`Profile pack ${fileName} uses unsupported severity: ${invalidSeverity}`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return parsedSeverities;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export async function collectFileNames(folderPath) {
|
|
213
|
+
const fileNames = await fs.readdir(folderPath, { withFileTypes: true });
|
|
214
|
+
return fileNames
|
|
215
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith('.md'))
|
|
216
|
+
.map((entry) => entry.name)
|
|
217
|
+
.sort((leftName, rightName) => leftName.localeCompare(rightName));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function formatBlockingSeverities(blockingSeverities) {
|
|
221
|
+
return blockingSeverities.join(', ');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function formatDuration(durationMs) {
|
|
225
|
+
const durationInSeconds = (durationMs / 1000).toFixed(1);
|
|
226
|
+
return `${durationInSeconds}s`;
|
|
227
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ryuenn3123/agentic-senior-core",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.3",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"description": "Force your AI Agent to code like a Staff Engineer, not a Junior.",
|
|
5
6
|
"bin": {
|
|
6
7
|
"agentic-senior-core": "bin/agentic-senior-core.js"
|
|
7
8
|
},
|
|
8
9
|
"files": [
|
|
9
10
|
"bin/",
|
|
11
|
+
"lib/",
|
|
10
12
|
"scripts/",
|
|
11
13
|
".agent-context/",
|
|
12
14
|
".agents/",
|
|
@@ -49,4 +51,4 @@
|
|
|
49
51
|
"validate": "node ./scripts/validate.mjs",
|
|
50
52
|
"test": "node --test ./tests/cli-smoke.test.mjs ./tests/llm-judge.test.mjs ./tests/enterprise-ops.test.mjs ./tests/skill-tier-gate.test.mjs"
|
|
51
53
|
}
|
|
52
|
-
}
|
|
54
|
+
}
|