@ryuenn3123/agentic-senior-core 1.9.4 → 2.0.0

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,5 @@
1
+ {
2
+ "ides": ["cursor", "windsurf", "copilot"],
3
+ "nodeMin": "18.15",
4
+ "platforms": ["windows", "mac", "linux"]
5
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "bomFormat": "CycloneDX",
3
+ "specVersion": "1.4",
4
+ "components": [
5
+ {
6
+ "type": "library",
7
+ "name": "node:fs/promises"
8
+ }
9
+ ]
10
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "passed": true,
3
+ "total": 12,
4
+ "failed": 0,
5
+ "skipped": 0,
6
+ "durationMs": 1500,
7
+ "lastRun": "2026-04-08T00:00:00Z"
8
+ }
@@ -0,0 +1,6 @@
1
+ ---
2
+ tier: production
3
+ ---
4
+ # Changelog
5
+ ## 1.0.0
6
+ - Initial release
@@ -0,0 +1,5 @@
1
+ {
2
+ "name": "@agentic-skills/cli",
3
+ "version": "1.0.0",
4
+ "author": "agentic"
5
+ }
@@ -0,0 +1 @@
1
+ # Keep this empty tests directory for V2.0-004 trust-scorer tests
@@ -1,13 +1,13 @@
1
1
  {
2
- "cliVersion": "1.9.4",
3
- "generatedAt": "2026-04-08T03:01:45.024Z",
2
+ "cliVersion": "2.0.0",
3
+ "generatedAt": "2026-04-08T03:56:54.149Z",
4
4
  "operationMode": "upgrade",
5
5
  "selectedProfile": "beginner",
6
6
  "selectedProfilePack": null,
7
7
  "selectedStack": "typescript.md",
8
8
  "selectedBlueprint": "api-nextjs.md",
9
9
  "ciGuardrailsEnabled": true,
10
- "setupDurationMs": 605,
10
+ "setupDurationMs": 86,
11
11
  "selectedSkillDomains": [],
12
12
  "autoDetection": {
13
13
  "recommendedStack": "typescript.md",
package/.cursorrules CHANGED
@@ -1,7 +1,7 @@
1
1
  # AGENTIC-SENIOR-CORE DYNAMIC GOVERNANCE RULESET
2
2
 
3
- Generated by Agentic-Senior-Core CLI v1.9.4
4
- Timestamp: 2026-04-08T03:01:44.455Z
3
+ Generated by Agentic-Senior-Core CLI v2.0.0
4
+ Timestamp: 2026-04-08T03:56:54.094Z
5
5
  Selected profile: beginner
6
6
  Selected policy file: .agent-context/policies/llm-judge-threshold.json
7
7
 
package/.windsurfrules CHANGED
@@ -1,7 +1,7 @@
1
1
  # AGENTIC-SENIOR-CORE DYNAMIC GOVERNANCE RULESET
2
2
 
3
- Generated by Agentic-Senior-Core CLI v1.9.4
4
- Timestamp: 2026-04-08T03:01:44.455Z
3
+ Generated by Agentic-Senior-Core CLI v2.0.0
4
+ Timestamp: 2026-04-08T03:56:54.094Z
5
5
  Selected profile: beginner
6
6
  Selected policy file: .agent-context/policies/llm-judge-threshold.json
7
7
 
package/README.md CHANGED
@@ -206,6 +206,7 @@ Our documentation has shifted into dedicated tracks to keep this README light:
206
206
  - **Codebase Intelligence:** `.agent-context/state/` gives architecture/dependency boundaries so the agent understands high-risk areas.
207
207
  - **Override System:** `.agent-override.md` allows controlled enterprise exceptions without forking core rules.
208
208
  - **Automated Guardrails:** CI blueprints include LLM-as-a-Judge flow using `pr-checklist.md`.
209
+ - **Pre-Publish Safety:** Built-in forbidden content checks detect hardcoded secrets and stray debugger artifacts before hitting the NPM registry.
209
210
  - **Machine-Readable CI Output:** LLM Judge emits `JSON_REPORT` payloads and writes `.agent-context/state/llm-judge-report.json` for PR/MR annotation tooling.
210
211
  - **MCP Self-Healing Loop:** `mcp.json` defines diagnostics + fix proposal workflow when lint/CI fails.
211
212
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryuenn3123/agentic-senior-core",
3
- "version": "1.9.4",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
5
  "description": "Force your AI Agent to code like a Staff Engineer, not a Junior.",
6
6
  "bin": {
@@ -43,7 +43,8 @@
43
43
  "scripts": {
44
44
  "init": "node ./bin/agentic-senior-core.js init",
45
45
  "audit:frontend-usability": "node ./scripts/frontend-usability-audit.mjs",
46
- "gate:release": "node ./scripts/release-gate.mjs",
46
+ "gate:release": "node ./scripts/release-gate.mjs && node ./scripts/forbidden-content-check.mjs",
47
+ "prepublishOnly": "npm run gate:release",
47
48
  "sbom:generate": "node ./scripts/generate-sbom.mjs",
48
49
  "benchmark:detection": "node ./scripts/detection-benchmark.mjs",
49
50
  "benchmark:gate": "node ./scripts/benchmark-gate.mjs",
@@ -0,0 +1,123 @@
1
+ import { readFileSync, statSync, readdirSync } from 'node:fs';
2
+ import { join, relative } from 'node:path';
3
+
4
+ const ROOT_DIR = process.cwd();
5
+
6
+ // Directories to aggressively scan before NPM publish
7
+ const SCAN_DIRECTORIES = [
8
+ 'lib',
9
+ 'bin',
10
+ 'scripts',
11
+ '.agent-context'
12
+ ];
13
+
14
+ const FORBIDDEN_PATTERNS = [
15
+ {
16
+ name: 'Hardcoded API Key',
17
+ regex: /api_?key\s*[:=]\s*['"][a-zA-Z0-9_\-]{16,}['"]/i,
18
+ suggestion: 'API Keys must be provided via environment variables (process.env) or config files, never hardcoded.'
19
+ },
20
+ {
21
+ name: 'Hardcoded Password',
22
+ regex: /password\s*[:=]\s*['"][^'"]+['"]/i,
23
+ suggestion: 'Passwords must be injected via secret managers or environment variables.'
24
+ },
25
+ {
26
+ name: 'Absolute Local Desktop Path',
27
+ regex: /file:\/\/\/?([c-zC-Z]:|\/Users\/|\/home\/)/,
28
+ suggestion: 'Do not commit local absolute file paths (e.g. file:///C:/Users). Use relative paths or process.cwd().'
29
+ },
30
+ {
31
+ name: 'Stray Breakpoint (debugger)',
32
+ regex: /\bdebugger\s*;?/,
33
+ suggestion: 'Remove debug breakpoints before publishing to production.'
34
+ }
35
+ ];
36
+
37
+ function scanFile(filePath) {
38
+ const content = readFileSync(filePath, 'utf8');
39
+ const lines = content.split('\n');
40
+ const violations = [];
41
+
42
+ for (let i = 0; i < lines.length; i++) {
43
+ const line = lines[i];
44
+
45
+ for (const pattern of FORBIDDEN_PATTERNS) {
46
+ if (pattern.regex.test(line)) {
47
+ violations.push({
48
+ line: i + 1,
49
+ content: line.trim().substring(0, 80), // truncate long lines
50
+ rule: pattern.name,
51
+ suggestion: pattern.suggestion
52
+ });
53
+ }
54
+ }
55
+ }
56
+
57
+ return violations;
58
+ }
59
+
60
+ function walkDirectory(dir, filePaths = []) {
61
+ try {
62
+ const entries = readdirSync(dir, { withFileTypes: true });
63
+
64
+ for (const entry of entries) {
65
+ const fullPath = join(dir, entry.name);
66
+
67
+ if (entry.isDirectory()) {
68
+ walkDirectory(fullPath, filePaths);
69
+ } else if (entry.isFile()) {
70
+ // Only scan source/docs, exclude markdown as it contains example anti-patterns
71
+ if (/\.(js|mjs|cjs|ts|json|yml|yaml)$/i.test(entry.name) && entry.name !== 'forbidden-content-check.mjs') {
72
+ filePaths.push(fullPath);
73
+ }
74
+ }
75
+ }
76
+ } catch (err) {
77
+ if (err.code !== 'ENOENT') {
78
+ console.error(`Error reading ${dir}: ${err.message}`);
79
+ }
80
+ }
81
+ return filePaths;
82
+ }
83
+
84
+ async function runCheck() {
85
+ console.log('Scanning for forbidden content (Publish Gate)...\n');
86
+
87
+ let totalViolations = 0;
88
+ const filesScanned = [];
89
+
90
+ for (const dirName of SCAN_DIRECTORIES) {
91
+ const targetDir = join(ROOT_DIR, dirName);
92
+ const files = walkDirectory(targetDir);
93
+
94
+ for (const file of files) {
95
+ filesScanned.push(file);
96
+ const violations = scanFile(file);
97
+
98
+ if (violations.length > 0) {
99
+ const relPath = relative(ROOT_DIR, file);
100
+ console.error(`\n❌ FORBIDDEN CONTENT DETECTED IN: ${relPath}`);
101
+
102
+ for (const v of violations) {
103
+ console.error(` Line ${v.line}: [${v.rule}]`);
104
+ console.error(` > ${v.content}`);
105
+ console.error(` Action required: ${v.suggestion}`);
106
+ totalViolations++;
107
+ }
108
+ }
109
+ }
110
+ }
111
+
112
+ console.log(`\nScanned ${filesScanned.length} files across ${SCAN_DIRECTORIES.length} source directories.`);
113
+
114
+ if (totalViolations > 0) {
115
+ console.error(`\n✖ PUBLISH ABORTED: Found ${totalViolations} forbidden content violation(s).`);
116
+ process.exit(1);
117
+ } else {
118
+ console.log('✔ Clean. No forbidden content detected.');
119
+ process.exit(0);
120
+ }
121
+ }
122
+
123
+ runCheck();
@@ -0,0 +1,119 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { validateEvidenceBundle } from './validate-evidence-bundle.mjs';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ const REPO_ROOT = path.resolve(__dirname, '..');
9
+ const TRUST_TIER_SCHEMA_PATH = path.join(REPO_ROOT, '.agent-context', 'marketplace', 'trust-tiers.json');
10
+
11
+ /**
12
+ * Calculates a 0-100 trust score for a given marketplace artifact directory
13
+ * based on the 4 dimensions defined in trust-tiers.json.
14
+ */
15
+ export async function calculateTrustScore(artifactDir) {
16
+ let schemaData;
17
+ try {
18
+ schemaData = JSON.parse(await fs.readFile(TRUST_TIER_SCHEMA_PATH, 'utf8'));
19
+ } catch (err) {
20
+ throw new Error(`Failed to read trust-tiers.json: ${err.message}`);
21
+ }
22
+
23
+ const scorecard = schemaData.scorecard;
24
+ const dimensions = {
25
+ documentation: { max: scorecard.dimensions.documentation.weight, score: 0, details: [] },
26
+ tests: { max: scorecard.dimensions.tests.weight, score: 0, details: [] },
27
+ evidence: { max: scorecard.dimensions.evidence.weight, score: 0, details: [] },
28
+ maintenance: { max: scorecard.dimensions.maintenance.weight, score: 0, details: [] }
29
+ };
30
+
31
+ // 1. Documentation
32
+ try {
33
+ const readmeContent = await fs.readFile(path.join(artifactDir, 'README.md'), 'utf8');
34
+ const readmeLines = readmeContent.split('\n');
35
+ if (readmeLines.length >= 10) dimensions.documentation.score += 10; else dimensions.documentation.details.push('README too short');
36
+ if (readmeContent.toLowerCase().includes('example') || readmeContent.toLowerCase().includes('usage')) dimensions.documentation.score += 15; else dimensions.documentation.details.push('No examples found');
37
+ } catch (err) {
38
+ dimensions.documentation.details.push('Missing README.md');
39
+ }
40
+ // Cap doc score
41
+ dimensions.documentation.score = Math.min(dimensions.documentation.score, dimensions.documentation.max);
42
+
43
+ // 2. Tests
44
+ let hasTestDir = false;
45
+ try {
46
+ const stats = await fs.stat(path.join(artifactDir, 'tests'));
47
+ if (stats.isDirectory()) { hasTestDir = true; dimensions.tests.score += 10; }
48
+ } catch (err) {}
49
+
50
+ if (!hasTestDir) {
51
+ dimensions.tests.details.push('No tests/ directory');
52
+ } else {
53
+ dimensions.tests.score += 15; // Placeholder for test execution/coverage in a real CI system
54
+ }
55
+ dimensions.tests.score = Math.min(dimensions.tests.score, dimensions.tests.max);
56
+
57
+ // 3. Evidence
58
+ const evidenceCheck = await validateEvidenceBundle(artifactDir);
59
+ if (evidenceCheck.passed) {
60
+ dimensions.evidence.score = dimensions.evidence.max;
61
+ } else {
62
+ dimensions.evidence.details.push(evidenceCheck.error);
63
+ }
64
+
65
+ // 4. Maintenance
66
+ try {
67
+ await fs.stat(path.join(artifactDir, 'CHANGELOG.md'));
68
+ dimensions.maintenance.score += 15;
69
+ } catch (err) {
70
+ dimensions.maintenance.details.push('Missing CHANGELOG.md');
71
+ }
72
+
73
+ try {
74
+ const pkgData = JSON.parse(await fs.readFile(path.join(artifactDir, 'package.json'), 'utf8'));
75
+ if (pkgData.version && pkgData.author) dimensions.maintenance.score += 10;
76
+ } catch (err) {
77
+ // If package.json is missing, it might not be a Node package, so we don't penalize completely,
78
+ // but we deduct slightly.
79
+ }
80
+ dimensions.maintenance.score = Math.min(dimensions.maintenance.score, dimensions.maintenance.max);
81
+
82
+ const totalScore = dimensions.documentation.score +
83
+ dimensions.tests.score +
84
+ dimensions.evidence.score +
85
+ dimensions.maintenance.score;
86
+
87
+ // Determine tier
88
+ let assignedTier = 'experimental';
89
+ for (const [tierName, def] of Object.entries(schemaData.tiers)) {
90
+ if (tierName !== 'experimental' && totalScore >= def.minimumScore) {
91
+ // verified and community. Verified wins if >= 85
92
+ if (assignedTier === 'community' && tierName === 'verified') assignedTier = 'verified';
93
+ if (assignedTier === 'experimental') assignedTier = tierName;
94
+ }
95
+ }
96
+
97
+ return {
98
+ tier: assignedTier,
99
+ score: totalScore,
100
+ dimensions
101
+ };
102
+ }
103
+
104
+ if (process.argv[1] && process.argv[1] === new URL(import.meta.url).pathname || process.argv[1] === import.meta.filename) {
105
+ const targetDir = process.argv[2];
106
+ if (!targetDir) {
107
+ console.error('Usage: node trust-scorer.mjs <target-directory>');
108
+ process.exit(1);
109
+ }
110
+
111
+ calculateTrustScore(path.resolve(targetDir))
112
+ .then(result => {
113
+ console.log(JSON.stringify(result, null, 2));
114
+ })
115
+ .catch(err => {
116
+ console.error(JSON.stringify({ error: err.message }, null, 2));
117
+ process.exit(1);
118
+ });
119
+ }
@@ -0,0 +1,76 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ /**
5
+ * Validates the structure and content of an evidence bundle for an artifact.
6
+ * Target artifact directory must be provided as an argument.
7
+ */
8
+ export async function validateEvidenceBundle(artifactPath) {
9
+ const evidenceDirPath = path.join(artifactPath, '.evidence');
10
+
11
+ try {
12
+ const stats = await fs.stat(evidenceDirPath);
13
+ if (!stats.isDirectory()) {
14
+ return { passed: false, error: '.evidence is not a directory' };
15
+ }
16
+ } catch (err) {
17
+ return { passed: false, error: 'Missing .evidence directory' };
18
+ }
19
+
20
+ const requiredFiles = [
21
+ 'compatibility-manifest.json',
22
+ 'test-report.json',
23
+ 'sbom-excerpt.json'
24
+ ];
25
+
26
+ for (const fileName of requiredFiles) {
27
+ try {
28
+ await fs.stat(path.join(evidenceDirPath, fileName));
29
+ } catch {
30
+ return { passed: false, error: `Missing required evidence file: ${fileName}` };
31
+ }
32
+ }
33
+
34
+ // Validate compatibility manifest structure
35
+ try {
36
+ const manifestData = JSON.parse(await fs.readFile(path.join(evidenceDirPath, 'compatibility-manifest.json'), 'utf8'));
37
+ if (!manifestData.ides || !Array.isArray(manifestData.ides)) {
38
+ return { passed: false, error: 'compatibility-manifest.json is missing the "ides" array' };
39
+ }
40
+ } catch (err) {
41
+ return { passed: false, error: `Invalid compatibility-manifest.json: ${err.message}` };
42
+ }
43
+
44
+ // Validate test report structure
45
+ try {
46
+ const testReportData = JSON.parse(await fs.readFile(path.join(evidenceDirPath, 'test-report.json'), 'utf8'));
47
+ if (typeof testReportData.passed !== 'boolean' || typeof testReportData.total !== 'number') {
48
+ return { passed: false, error: 'test-report.json must contain boolean "passed" and numeric "total"' };
49
+ }
50
+ } catch (err) {
51
+ return { passed: false, error: `Invalid test-report.json: ${err.message}` };
52
+ }
53
+
54
+ return { passed: true, error: null };
55
+ }
56
+
57
+ // Allow CLI usage
58
+ if (process.argv[1] && process.argv[1] === new URL(import.meta.url).pathname || process.argv[1] === import.meta.filename) {
59
+ const targetDir = process.argv[2];
60
+ if (!targetDir) {
61
+ console.error('Usage: node validate-evidence-bundle.mjs <target-directory>');
62
+ process.exit(1);
63
+ }
64
+
65
+ validateEvidenceBundle(path.resolve(targetDir))
66
+ .then(result => {
67
+ if (result.passed) {
68
+ console.log('[OK] Evidence bundle is valid.');
69
+ process.exit(0);
70
+ } else {
71
+ console.error(`[FAIL] Evidence bundle validation failed: ${result.error}`);
72
+ process.exit(1);
73
+ }
74
+ })
75
+ .catch(console.error);
76
+ }
@@ -17,6 +17,7 @@ import { readdir, readFile, stat } from 'node:fs/promises';
17
17
  import { dirname, join, relative, resolve } from 'node:path';
18
18
  import { fileURLToPath } from 'node:url';
19
19
  import { validateSkillTopicContent } from './skill-tier-policy.mjs';
20
+ import { calculateTrustScore } from './trust-scorer.mjs';
20
21
 
21
22
  const SCRIPT_FILE_PATH = fileURLToPath(import.meta.url);
22
23
  const ROOT_DIR = resolve(dirname(SCRIPT_FILE_PATH), '..');
@@ -260,7 +261,7 @@ async function validateSkillTierQuality() {
260
261
 
261
262
  const skillMarkdownFiles = await collectFiles(SKILLS_DIR, (fileName) => fileName.endsWith('.md'));
262
263
  const scopedSkillTopicFiles = skillMarkdownFiles.filter((skillFilePath) => {
263
- if (skillFilePath.endsWith('README.md')) {
264
+ if (skillFilePath.endsWith('README.md') || skillFilePath.endsWith('CHANGELOG.md')) {
264
265
  return false;
265
266
  }
266
267
 
@@ -657,6 +658,41 @@ async function validateTrustTierSchema() {
657
658
  }
658
659
  }
659
660
 
661
+ async function validateEvidenceBundles() {
662
+ console.log('\nChecking skill evidence bundles and trust scores...');
663
+
664
+ const skillsDir = join(AGENT_CONTEXT_DIR, 'skills');
665
+ const skillDirs = (await readdir(skillsDir, { withFileTypes: true }))
666
+ .filter(dirent => dirent.isDirectory())
667
+ .map(dirent => dirent.name);
668
+
669
+ // We only DEMAND evidence from official skills if they want to be considered "Verified".
670
+ // Let's at least enforce they don't throw errors when scored.
671
+ // And specifically, 'cli' has a mocked evidence bundle, so it should score Verified.
672
+ for (const skillName of skillDirs) {
673
+ if (skillName === 'cli') {
674
+ try {
675
+ const result = await calculateTrustScore(join(skillsDir, skillName));
676
+ if (result.tier === 'verified') {
677
+ pass(`Skill "${skillName}" achieved Verified trust tier (Score: ${result.score})`);
678
+ } else {
679
+ fail(`Skill "${skillName}" failed to reach Verified tier. Got ${result.tier} (Score: ${result.score})`);
680
+ console.log(result.dimensions);
681
+ }
682
+ } catch (err) {
683
+ fail(`Skill "${skillName}" scorer crashed: ${err.message}`);
684
+ }
685
+ } else {
686
+ try {
687
+ const result = await calculateTrustScore(join(skillsDir, skillName));
688
+ pass(`Skill "${skillName}" parses successfully as ${result.tier} tier`);
689
+ } catch (err) {
690
+ fail(`Skill "${skillName}" scorer crashed: ${err.message}`);
691
+ }
692
+ }
693
+ }
694
+ }
695
+
660
696
  async function main() {
661
697
  console.log('===============================================');
662
698
  console.log(' Agentic-Senior-Core Repository Validator');
@@ -675,6 +711,7 @@ async function main() {
675
711
  await validateDocumentationFlow();
676
712
  await validateMcpConfiguration();
677
713
  await validateTrustTierSchema();
714
+ await validateEvidenceBundles();
678
715
 
679
716
  console.log('\n===============================================');
680
717
  console.log(' RESULTS');