@hongmaple0820/scale-engine 0.18.0 → 0.20.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.
- package/README.en.md +310 -237
- package/README.md +255 -63
- package/dist/api/cli.js +2656 -1258
- package/dist/api/cli.js.map +1 -1
- package/dist/api/doctor.d.ts +4 -1
- package/dist/api/doctor.js +85 -1
- package/dist/api/doctor.js.map +1 -1
- package/dist/api/quickstart.d.ts +3 -0
- package/dist/api/quickstart.js +9 -4
- package/dist/api/quickstart.js.map +1 -1
- package/dist/cli/phaseCommands.js +7 -0
- package/dist/cli/phaseCommands.js.map +1 -1
- package/dist/codegraph/CodeIntelligence.d.ts +135 -0
- package/dist/codegraph/CodeIntelligence.js +460 -0
- package/dist/codegraph/CodeIntelligence.js.map +1 -0
- package/dist/context/ContextBudget.d.ts +90 -0
- package/dist/context/ContextBudget.js +322 -0
- package/dist/context/ContextBudget.js.map +1 -0
- package/dist/eval/WorkflowEval.d.ts +161 -0
- package/dist/eval/WorkflowEval.js +379 -0
- package/dist/eval/WorkflowEval.js.map +1 -0
- package/dist/governance/GovernanceRoi.d.ts +25 -0
- package/dist/governance/GovernanceRoi.js +70 -0
- package/dist/governance/GovernanceRoi.js.map +1 -0
- package/dist/governance/ProgressiveGovernance.d.ts +22 -0
- package/dist/governance/ProgressiveGovernance.js +159 -0
- package/dist/governance/ProgressiveGovernance.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/memory/MemoryBrain.d.ts +135 -0
- package/dist/memory/MemoryBrain.js +635 -0
- package/dist/memory/MemoryBrain.js.map +1 -0
- package/dist/memory/MemoryFabric.d.ts +118 -0
- package/dist/memory/MemoryFabric.js +281 -0
- package/dist/memory/MemoryFabric.js.map +1 -0
- package/dist/memory/MemoryLearning.d.ts +61 -0
- package/dist/memory/MemoryLearning.js +203 -0
- package/dist/memory/MemoryLearning.js.map +1 -0
- package/dist/memory/index.d.ts +3 -0
- package/dist/memory/index.js +4 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/output/GovernanceDashboard.d.ts +57 -0
- package/dist/output/GovernanceDashboard.js +250 -0
- package/dist/output/GovernanceDashboard.js.map +1 -0
- package/dist/output/HTMLArtifactLayer.js +31 -31
- package/dist/output/index.d.ts +2 -0
- package/dist/output/index.js +1 -0
- package/dist/output/index.js.map +1 -1
- package/dist/prompts/VibeTemplateGallery.js +121 -121
- package/dist/runtime/FinalReportGuard.d.ts +16 -0
- package/dist/runtime/FinalReportGuard.js +14 -0
- package/dist/runtime/FinalReportGuard.js.map +1 -0
- package/dist/runtime/RuntimeDoctor.d.ts +23 -0
- package/dist/runtime/RuntimeDoctor.js +151 -0
- package/dist/runtime/RuntimeDoctor.js.map +1 -0
- package/dist/runtime/RuntimeEvidenceLedger.d.ts +50 -0
- package/dist/runtime/RuntimeEvidenceLedger.js +89 -0
- package/dist/runtime/RuntimeEvidenceLedger.js.map +1 -0
- package/dist/runtime/SessionLedger.d.ts +53 -0
- package/dist/runtime/SessionLedger.js +104 -0
- package/dist/runtime/SessionLedger.js.map +1 -0
- package/dist/runtime/index.d.ts +4 -0
- package/dist/runtime/index.js +5 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/skills/SkillRadar.d.ts +83 -0
- package/dist/skills/SkillRadar.js +384 -0
- package/dist/skills/SkillRadar.js.map +1 -0
- package/dist/workflow/EngineeringStandards.js +69 -66
- package/dist/workflow/EngineeringStandards.js.map +1 -1
- package/dist/workflow/GovernanceTemplatePacks.js +126 -126
- package/dist/workflow/GovernanceTemplates.d.ts +1 -1
- package/dist/workflow/GovernanceTemplates.js +500 -229
- package/dist/workflow/GovernanceTemplates.js.map +1 -1
- package/dist/workflow/ResourceGovernance.js +27 -18
- package/dist/workflow/ResourceGovernance.js.map +1 -1
- package/dist/workflow/VerificationCommands.d.ts +11 -0
- package/dist/workflow/VerificationCommands.js +2 -0
- package/dist/workflow/VerificationCommands.js.map +1 -1
- package/dist/workflow/VerificationProfile.d.ts +2 -1
- package/dist/workflow/VerificationProfile.js +3 -0
- package/dist/workflow/VerificationProfile.js.map +1 -1
- package/dist/workflow/WorkflowArtifactWriter.js +2 -1
- package/dist/workflow/WorkflowArtifactWriter.js.map +1 -1
- package/dist/workflow/WorkflowEngine.js +4 -1
- package/dist/workflow/WorkflowEngine.js.map +1 -1
- package/dist/workflow/WorkspaceSafety.d.ts +9 -0
- package/dist/workflow/WorkspaceSafety.js +49 -0
- package/dist/workflow/WorkspaceSafety.js.map +1 -0
- package/dist/workflow/gates/GateSystem.d.ts +12 -1
- package/dist/workflow/gates/GateSystem.js +106 -0
- package/dist/workflow/gates/GateSystem.js.map +1 -1
- package/dist/workflow/types.d.ts +1 -1
- package/docs/CODE_INTELLIGENCE.md +138 -0
- package/docs/CONTEXT_BUDGET.md +87 -0
- package/docs/GOVERNANCE_DASHBOARD.md +69 -0
- package/docs/MEMORY_BRAIN.md +104 -0
- package/docs/MEMORY_FABRIC.md +107 -0
- package/docs/README.md +76 -0
- package/docs/RUNTIME_EVIDENCE.md +101 -0
- package/docs/SKILL_RADAR.md +115 -0
- package/docs/WORKFLOW_EVAL.md +151 -0
- package/docs/start/README.md +42 -0
- package/docs/start/agent-governance-demo.md +107 -0
- package/docs/start/quickstart.md +127 -0
- package/examples/demo-projects/agent-governance-demo/README.md +37 -0
- package/examples/demo-projects/agent-governance-demo/package.json +16 -0
- package/examples/demo-projects/agent-governance-demo/src/oauth-state.ts +39 -0
- package/examples/demo-projects/agent-governance-demo/tests/oauth-state.test.ts +52 -0
- package/package.json +14 -3
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export type RuntimeSessionLevel = 'S' | 'M' | 'L' | 'CRITICAL';
|
|
2
|
+
export type RuntimeSessionStatus = 'active' | 'completed' | 'failed' | 'abandoned';
|
|
3
|
+
export type RuntimeSessionEventType = 'session.started' | 'session.ended' | 'phase.started' | 'phase.completed' | 'tool.used' | 'evidence.recorded' | 'note';
|
|
4
|
+
export interface RuntimeSessionStartInput {
|
|
5
|
+
sessionId?: string;
|
|
6
|
+
taskId?: string;
|
|
7
|
+
agent?: string;
|
|
8
|
+
level?: RuntimeSessionLevel;
|
|
9
|
+
summary?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface RuntimeSessionRecord {
|
|
12
|
+
sessionId: string;
|
|
13
|
+
taskId?: string;
|
|
14
|
+
agent?: string;
|
|
15
|
+
level?: RuntimeSessionLevel;
|
|
16
|
+
status: RuntimeSessionStatus;
|
|
17
|
+
startedAt: string;
|
|
18
|
+
updatedAt: string;
|
|
19
|
+
endedAt?: string;
|
|
20
|
+
summary?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface RuntimeSessionEventInput {
|
|
23
|
+
type: RuntimeSessionEventType;
|
|
24
|
+
phase?: string;
|
|
25
|
+
message?: string;
|
|
26
|
+
data?: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
export interface RuntimeSessionEvent extends RuntimeSessionEventInput {
|
|
29
|
+
id: string;
|
|
30
|
+
sessionId: string;
|
|
31
|
+
createdAt: string;
|
|
32
|
+
redactionApplied: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface RuntimeSessionLedgerOptions {
|
|
35
|
+
projectDir?: string;
|
|
36
|
+
scaleDir?: string;
|
|
37
|
+
now?: () => Date;
|
|
38
|
+
createDirs?: boolean;
|
|
39
|
+
}
|
|
40
|
+
export declare class SessionLedger {
|
|
41
|
+
private sessionsDir;
|
|
42
|
+
private currentPath;
|
|
43
|
+
private now;
|
|
44
|
+
constructor(options?: RuntimeSessionLedgerOptions);
|
|
45
|
+
start(input?: RuntimeSessionStartInput): RuntimeSessionRecord;
|
|
46
|
+
append(sessionId: string, input: RuntimeSessionEventInput): RuntimeSessionEvent;
|
|
47
|
+
end(sessionId: string, status?: RuntimeSessionStatus, summary?: string): RuntimeSessionRecord;
|
|
48
|
+
current(): RuntimeSessionRecord | null;
|
|
49
|
+
listEvents(sessionId: string): RuntimeSessionEvent[];
|
|
50
|
+
sessionFile(sessionId: string): string;
|
|
51
|
+
private writeCurrent;
|
|
52
|
+
private sessionPath;
|
|
53
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
4
|
+
import { redactEvidenceValue } from '../tools/ToolEvidenceStore.js';
|
|
5
|
+
export class SessionLedger {
|
|
6
|
+
constructor(options = {}) {
|
|
7
|
+
const projectDir = resolve(options.projectDir ?? process.cwd());
|
|
8
|
+
const scaleRoot = isAbsolute(options.scaleDir ?? '')
|
|
9
|
+
? options.scaleDir
|
|
10
|
+
: join(projectDir, options.scaleDir ?? '.scale');
|
|
11
|
+
this.sessionsDir = join(scaleRoot, 'events', 'sessions');
|
|
12
|
+
this.currentPath = join(scaleRoot, 'events', 'current-session.json');
|
|
13
|
+
this.now = options.now ?? (() => new Date());
|
|
14
|
+
if (options.createDirs !== false && !existsSync(this.sessionsDir))
|
|
15
|
+
mkdirSync(this.sessionsDir, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
start(input = {}) {
|
|
18
|
+
const now = this.now().toISOString();
|
|
19
|
+
const record = {
|
|
20
|
+
sessionId: input.sessionId ?? `SESSION-${Date.now()}-${randomUUID().slice(0, 8)}`,
|
|
21
|
+
taskId: input.taskId,
|
|
22
|
+
agent: input.agent,
|
|
23
|
+
level: input.level,
|
|
24
|
+
status: 'active',
|
|
25
|
+
startedAt: now,
|
|
26
|
+
updatedAt: now,
|
|
27
|
+
summary: input.summary,
|
|
28
|
+
};
|
|
29
|
+
this.writeCurrent(record);
|
|
30
|
+
this.append(record.sessionId, {
|
|
31
|
+
type: 'session.started',
|
|
32
|
+
message: input.summary,
|
|
33
|
+
data: {
|
|
34
|
+
taskId: record.taskId,
|
|
35
|
+
agent: record.agent,
|
|
36
|
+
level: record.level,
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
return record;
|
|
40
|
+
}
|
|
41
|
+
append(sessionId, input) {
|
|
42
|
+
const data = redactEvidenceValue(input.data ?? {});
|
|
43
|
+
const event = {
|
|
44
|
+
...input,
|
|
45
|
+
id: `EVT-${Date.now()}-${randomUUID().slice(0, 8)}`,
|
|
46
|
+
sessionId,
|
|
47
|
+
createdAt: this.now().toISOString(),
|
|
48
|
+
data: data.value,
|
|
49
|
+
redactionApplied: data.redacted,
|
|
50
|
+
};
|
|
51
|
+
appendFileSync(this.sessionPath(sessionId), `${JSON.stringify(event)}\n`, 'utf-8');
|
|
52
|
+
return event;
|
|
53
|
+
}
|
|
54
|
+
end(sessionId, status = 'completed', summary) {
|
|
55
|
+
const current = this.current();
|
|
56
|
+
const now = this.now().toISOString();
|
|
57
|
+
const record = {
|
|
58
|
+
...(current?.sessionId === sessionId ? current : { sessionId, startedAt: now }),
|
|
59
|
+
status,
|
|
60
|
+
updatedAt: now,
|
|
61
|
+
endedAt: now,
|
|
62
|
+
summary: summary ?? current?.summary,
|
|
63
|
+
};
|
|
64
|
+
this.writeCurrent(record);
|
|
65
|
+
this.append(sessionId, {
|
|
66
|
+
type: 'session.ended',
|
|
67
|
+
message: summary,
|
|
68
|
+
data: { status },
|
|
69
|
+
});
|
|
70
|
+
return record;
|
|
71
|
+
}
|
|
72
|
+
current() {
|
|
73
|
+
try {
|
|
74
|
+
return JSON.parse(readFileSync(this.currentPath, 'utf-8'));
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
listEvents(sessionId) {
|
|
81
|
+
try {
|
|
82
|
+
return readFileSync(this.sessionPath(sessionId), 'utf-8')
|
|
83
|
+
.split(/\r?\n/)
|
|
84
|
+
.filter(Boolean)
|
|
85
|
+
.map(line => JSON.parse(line));
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
sessionFile(sessionId) {
|
|
92
|
+
return this.sessionPath(sessionId);
|
|
93
|
+
}
|
|
94
|
+
writeCurrent(record) {
|
|
95
|
+
writeFileSync(this.currentPath, JSON.stringify(record, null, 2), 'utf-8');
|
|
96
|
+
}
|
|
97
|
+
sessionPath(sessionId) {
|
|
98
|
+
return join(this.sessionsDir, `${safePathSegment(sessionId)}.jsonl`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function safePathSegment(value) {
|
|
102
|
+
return value.replace(/[^a-zA-Z0-9._-]/g, '-').slice(0, 120) || 'unknown-session';
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=SessionLedger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionLedger.js","sourceRoot":"","sources":["../../src/runtime/SessionLedger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAA;AAC1C,cAAc,oBAAoB,CAAA;AAClC,cAAc,oBAAoB,CAAA;AAClC,cAAc,uBAAuB,CAAA"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { type ToolCapabilityReport } from '../tools/ToolCapabilityRegistry.js';
|
|
2
|
+
import { type ResolvedToolPolicy } from '../tools/ToolPolicy.js';
|
|
3
|
+
export type SkillRadarSafetyLevel = 'trusted' | 'review-required' | 'restricted' | 'blocked';
|
|
4
|
+
export type SkillRadarAction = 'may-run-with-policy' | 'recommend-with-evidence' | 'suggest-fallback' | 'blocked';
|
|
5
|
+
export interface SkillRadarOptions {
|
|
6
|
+
projectDir?: string;
|
|
7
|
+
scaleDir?: string;
|
|
8
|
+
task: string;
|
|
9
|
+
phase?: string;
|
|
10
|
+
level?: string;
|
|
11
|
+
files?: string[];
|
|
12
|
+
services?: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface SkillRadarRecommendation {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
category: string;
|
|
18
|
+
capability: string;
|
|
19
|
+
confidence: number;
|
|
20
|
+
safetyLevel: SkillRadarSafetyLevel;
|
|
21
|
+
action: SkillRadarAction;
|
|
22
|
+
installed: boolean;
|
|
23
|
+
policyEnabled: boolean;
|
|
24
|
+
reason: string;
|
|
25
|
+
risk: string;
|
|
26
|
+
requiredEvidence: string[];
|
|
27
|
+
fallback: string;
|
|
28
|
+
installCommand: string;
|
|
29
|
+
sourceUrl: string;
|
|
30
|
+
matchedSignals: string[];
|
|
31
|
+
safetyFindings: Array<{
|
|
32
|
+
rule: string;
|
|
33
|
+
severity: 'warn' | 'block';
|
|
34
|
+
message: string;
|
|
35
|
+
}>;
|
|
36
|
+
}
|
|
37
|
+
export interface SkillRadarReport {
|
|
38
|
+
ok: boolean;
|
|
39
|
+
projectDir: string;
|
|
40
|
+
generatedAt: string;
|
|
41
|
+
task: string;
|
|
42
|
+
phase?: string;
|
|
43
|
+
level: string;
|
|
44
|
+
detectedDomains: Array<{
|
|
45
|
+
domain: string;
|
|
46
|
+
score: number;
|
|
47
|
+
reasons: string[];
|
|
48
|
+
}>;
|
|
49
|
+
recommendations: SkillRadarRecommendation[];
|
|
50
|
+
requiredEvidence: string[];
|
|
51
|
+
fallbacks: string[];
|
|
52
|
+
toolSummary: ToolCapabilityReport['summary'];
|
|
53
|
+
policyMode: ResolvedToolPolicy['mode'];
|
|
54
|
+
warnings: string[];
|
|
55
|
+
}
|
|
56
|
+
export interface SkillSupplyChainDoctorReport {
|
|
57
|
+
ok: boolean;
|
|
58
|
+
projectDir: string;
|
|
59
|
+
generatedAt: string;
|
|
60
|
+
evaluated: number;
|
|
61
|
+
blocked: number;
|
|
62
|
+
warnings: number;
|
|
63
|
+
entries: Array<{
|
|
64
|
+
id: string;
|
|
65
|
+
sourceUrl: string;
|
|
66
|
+
installCommand: string;
|
|
67
|
+
trust: string;
|
|
68
|
+
safetyLevel: SkillRadarSafetyLevel;
|
|
69
|
+
risk: string;
|
|
70
|
+
blocked: boolean;
|
|
71
|
+
findings: Array<{
|
|
72
|
+
rule: string;
|
|
73
|
+
severity: 'warn' | 'block';
|
|
74
|
+
message: string;
|
|
75
|
+
}>;
|
|
76
|
+
requiredChecks: string[];
|
|
77
|
+
}>;
|
|
78
|
+
}
|
|
79
|
+
export declare function evaluateSkillRadar(options: SkillRadarOptions): SkillRadarReport;
|
|
80
|
+
export declare function inspectSkillSupplyChain(options?: {
|
|
81
|
+
projectDir?: string;
|
|
82
|
+
}): SkillSupplyChainDoctorReport;
|
|
83
|
+
export declare function renderSkillRadarMarkdown(report: SkillRadarReport): string;
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { extname, join, resolve } from 'node:path';
|
|
3
|
+
import { evaluateSkillInstallSafety, listSkillRepositoryEntries } from './SkillRepository.js';
|
|
4
|
+
import { inspectToolCapabilities } from '../tools/ToolCapabilityRegistry.js';
|
|
5
|
+
import { loadToolPolicy } from '../tools/ToolPolicy.js';
|
|
6
|
+
const DOMAIN_CONFIG = {
|
|
7
|
+
ui: {
|
|
8
|
+
keywords: ['ui', 'ux', 'design', 'frontend', 'page', 'component', 'visual', 'layout', 'prototype', 'accessibility'],
|
|
9
|
+
filePatterns: [/\.(tsx|jsx|vue|svelte|css|scss)$/i, /(^|\/)(pages|app|components|routes)\//i],
|
|
10
|
+
categories: ['ui'],
|
|
11
|
+
evidence: ['design-rationale', 'screenshot', 'visual-review'],
|
|
12
|
+
fallback: 'Use a static UI checklist, code review, and manual screenshot capture.',
|
|
13
|
+
},
|
|
14
|
+
browserAutomation: {
|
|
15
|
+
keywords: ['browser', 'e2e', 'playwright', 'chrome', 'devtools', 'web access', 'web', 'automation', 'integration'],
|
|
16
|
+
filePatterns: [/\.(spec|test)\.(ts|tsx|js|jsx)$/i, /playwright/i],
|
|
17
|
+
categories: ['browser', 'testing'],
|
|
18
|
+
evidence: ['screenshot', 'console-summary', 'network-summary', 'scenario-result'],
|
|
19
|
+
fallback: 'Run manual smoke checks and capture route, console, and network evidence.',
|
|
20
|
+
},
|
|
21
|
+
desktopAutomation: {
|
|
22
|
+
keywords: ['desktop', 'gui', 'wps', 'wechat', 'computer', 'cua', 'client-app'],
|
|
23
|
+
filePatterns: [],
|
|
24
|
+
categories: ['desktop'],
|
|
25
|
+
evidence: ['operator-boundary', 'desktop-screenshot', 'affected-app'],
|
|
26
|
+
fallback: 'Use a manual operator checklist with screenshots and side-effect boundaries.',
|
|
27
|
+
},
|
|
28
|
+
externalCli: {
|
|
29
|
+
keywords: ['codex', 'gemini', 'opencode', 'claude code', 'external cli', 'agent cli', 'command line'],
|
|
30
|
+
filePatterns: [],
|
|
31
|
+
categories: ['agent-cli'],
|
|
32
|
+
evidence: ['cli-version-check', 'command', 'exit-code', 'output-summary'],
|
|
33
|
+
fallback: 'Use local review or built-in verification commands instead of cross-agent CLI.',
|
|
34
|
+
},
|
|
35
|
+
review: {
|
|
36
|
+
keywords: ['review', 'pr', 'merge', 'ship', 'release', 'quality', 'code review'],
|
|
37
|
+
filePatterns: [],
|
|
38
|
+
categories: ['review'],
|
|
39
|
+
evidence: ['finding-list', 'severity', 'accepted-risk-or-fix'],
|
|
40
|
+
fallback: 'Use built-in review and runtime evidence gates.',
|
|
41
|
+
},
|
|
42
|
+
docs: {
|
|
43
|
+
keywords: ['docs', 'documentation', 'readme', 'adr', 'governance asset'],
|
|
44
|
+
filePatterns: [/\.md$/i, /(^|\/)docs\//i],
|
|
45
|
+
categories: ['docs'],
|
|
46
|
+
evidence: ['changed-docs', 'source-of-truth-map'],
|
|
47
|
+
fallback: 'Update canonical Markdown directly and run doc drift checks.',
|
|
48
|
+
},
|
|
49
|
+
discovery: {
|
|
50
|
+
keywords: ['skill', 'mcp', 'tool', 'capability', 'discover', 'search'],
|
|
51
|
+
filePatterns: [],
|
|
52
|
+
categories: ['discovery'],
|
|
53
|
+
evidence: ['candidate-list', 'safety-review'],
|
|
54
|
+
fallback: 'Search docs manually and run skill safety review before install.',
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
export function evaluateSkillRadar(options) {
|
|
58
|
+
const projectDir = resolve(options.projectDir ?? process.cwd());
|
|
59
|
+
const scaleDir = options.scaleDir ?? '.scale';
|
|
60
|
+
const level = String(options.level ?? 'M').toUpperCase();
|
|
61
|
+
const files = normalizeFiles(options.files);
|
|
62
|
+
const detectedDomains = detectDomains({
|
|
63
|
+
text: `${options.task} ${options.phase ?? ''}`,
|
|
64
|
+
files,
|
|
65
|
+
});
|
|
66
|
+
const activeDomains = detectedDomains.length > 0
|
|
67
|
+
? detectedDomains
|
|
68
|
+
: [{ domain: 'discovery', score: 1, reasons: ['fallback:no-domain-match'] }];
|
|
69
|
+
const policy = loadToolPolicy(projectDir, scaleDir);
|
|
70
|
+
const toolReport = inspectToolCapabilities({ projectDir });
|
|
71
|
+
const recommendations = listSkillRepositoryEntries()
|
|
72
|
+
.filter(entry => entryMatchesDomains(entry, activeDomains))
|
|
73
|
+
.map(entry => buildRecommendation(entry, {
|
|
74
|
+
projectDir,
|
|
75
|
+
domains: activeDomains,
|
|
76
|
+
policy,
|
|
77
|
+
toolReport,
|
|
78
|
+
files,
|
|
79
|
+
}))
|
|
80
|
+
.sort((a, b) => b.confidence - a.confidence || safetyRank(a.safetyLevel) - safetyRank(b.safetyLevel) || a.id.localeCompare(b.id));
|
|
81
|
+
const requiredEvidence = unique(recommendations.flatMap(item => item.requiredEvidence));
|
|
82
|
+
const fallbacks = unique(recommendations
|
|
83
|
+
.filter(item => item.action === 'suggest-fallback' || item.action === 'blocked')
|
|
84
|
+
.map(item => item.fallback));
|
|
85
|
+
return {
|
|
86
|
+
ok: recommendations.every(item => item.safetyLevel !== 'blocked'),
|
|
87
|
+
projectDir,
|
|
88
|
+
generatedAt: new Date().toISOString(),
|
|
89
|
+
task: options.task,
|
|
90
|
+
phase: options.phase,
|
|
91
|
+
level,
|
|
92
|
+
detectedDomains: activeDomains,
|
|
93
|
+
recommendations,
|
|
94
|
+
requiredEvidence,
|
|
95
|
+
fallbacks,
|
|
96
|
+
toolSummary: toolReport.summary,
|
|
97
|
+
policyMode: policy.mode,
|
|
98
|
+
warnings: [
|
|
99
|
+
...policy.warnings,
|
|
100
|
+
...recommendations
|
|
101
|
+
.filter(item => item.confidence < 0.4)
|
|
102
|
+
.map(item => `${item.id} confidence is below auto-run threshold; use fallback.`),
|
|
103
|
+
],
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
export function inspectSkillSupplyChain(options = {}) {
|
|
107
|
+
const projectDir = resolve(options.projectDir ?? process.cwd());
|
|
108
|
+
const entries = listSkillRepositoryEntries().map(entry => {
|
|
109
|
+
const safety = evaluateSkillInstallSafety({
|
|
110
|
+
sourceUrl: entry.sourceUrl,
|
|
111
|
+
installCommand: entry.installCommand,
|
|
112
|
+
});
|
|
113
|
+
return {
|
|
114
|
+
id: entry.id,
|
|
115
|
+
sourceUrl: entry.sourceUrl,
|
|
116
|
+
installCommand: entry.installCommand,
|
|
117
|
+
trust: entry.trust,
|
|
118
|
+
safetyLevel: safety.blocked ? 'blocked' : entry.trust === 'official' ? 'trusted' : 'review-required',
|
|
119
|
+
risk: safety.risk,
|
|
120
|
+
blocked: safety.blocked,
|
|
121
|
+
findings: safety.findings,
|
|
122
|
+
requiredChecks: unique([...entry.safety.requiredChecks, ...safety.requiredChecks]),
|
|
123
|
+
};
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
ok: entries.every(entry => !entry.blocked),
|
|
127
|
+
projectDir,
|
|
128
|
+
generatedAt: new Date().toISOString(),
|
|
129
|
+
evaluated: entries.length,
|
|
130
|
+
blocked: entries.filter(entry => entry.blocked).length,
|
|
131
|
+
warnings: entries.reduce((count, entry) => count + entry.findings.filter(finding => finding.severity === 'warn').length, 0),
|
|
132
|
+
entries,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function buildRecommendation(entry, input) {
|
|
136
|
+
const safety = evaluateSkillInstallSafety({ sourceUrl: entry.sourceUrl, installCommand: entry.installCommand });
|
|
137
|
+
const tool = findToolForEntry(entry, input.toolReport);
|
|
138
|
+
const policyId = policyIdForEntry(entry, tool);
|
|
139
|
+
const policyConfig = input.policy.tools[policyId];
|
|
140
|
+
const policyEnabled = policyConfig?.enabled ?? true;
|
|
141
|
+
const installed = tool?.installed ?? skillInstalledInProject(entry.id, input.projectDir);
|
|
142
|
+
const domainHits = input.domains.filter(domain => DOMAIN_CONFIG[domain.domain]?.categories.includes(entry.category));
|
|
143
|
+
const matchedSignals = unique([
|
|
144
|
+
...domainHits.flatMap(domain => domain.reasons),
|
|
145
|
+
`category:${entry.category}`,
|
|
146
|
+
`policy:${policyId}`,
|
|
147
|
+
]);
|
|
148
|
+
const safetyLevel = resolveSafetyLevel(entry, {
|
|
149
|
+
blocked: safety.blocked || !policyEnabled,
|
|
150
|
+
policyConfig,
|
|
151
|
+
});
|
|
152
|
+
const confidence = confidenceScore(entry, {
|
|
153
|
+
installed,
|
|
154
|
+
policyEnabled,
|
|
155
|
+
safetyLevel,
|
|
156
|
+
domainScore: domainHits.reduce((sum, domain) => sum + domain.score, 0),
|
|
157
|
+
projectHasPackage: existsSync(join(input.projectDir, 'package.json')),
|
|
158
|
+
projectHasFrontendFiles: input.files.some(file => /\.(tsx|jsx|vue|svelte|css|scss)$/i.test(file)),
|
|
159
|
+
tool,
|
|
160
|
+
});
|
|
161
|
+
return {
|
|
162
|
+
id: entry.id,
|
|
163
|
+
name: entry.name,
|
|
164
|
+
category: entry.category,
|
|
165
|
+
capability: capabilityFor(entry),
|
|
166
|
+
confidence,
|
|
167
|
+
safetyLevel,
|
|
168
|
+
action: resolveAction(safetyLevel, confidence),
|
|
169
|
+
installed,
|
|
170
|
+
policyEnabled,
|
|
171
|
+
reason: reasonFor(entry, { domainHits, installed, policyEnabled }),
|
|
172
|
+
risk: riskFor(entry, safetyLevel, policyEnabled),
|
|
173
|
+
requiredEvidence: unique([
|
|
174
|
+
...entry.orchestration.requiredEvidence,
|
|
175
|
+
...domainHits.flatMap(domain => DOMAIN_CONFIG[domain.domain]?.evidence ?? []),
|
|
176
|
+
...(policyConfig?.evidenceRequired ? ['tool-evidence-record'] : []),
|
|
177
|
+
]),
|
|
178
|
+
fallback: fallbackFor(entry, domainHits),
|
|
179
|
+
installCommand: entry.installCommand,
|
|
180
|
+
sourceUrl: entry.sourceUrl,
|
|
181
|
+
matchedSignals,
|
|
182
|
+
safetyFindings: safety.findings,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function detectDomains(input) {
|
|
186
|
+
const text = input.text.toLowerCase();
|
|
187
|
+
return Object.entries(DOMAIN_CONFIG)
|
|
188
|
+
.map(([domain, config]) => {
|
|
189
|
+
let score = 0;
|
|
190
|
+
const reasons = [];
|
|
191
|
+
for (const keyword of config.keywords) {
|
|
192
|
+
if (text.includes(keyword.toLowerCase())) {
|
|
193
|
+
score += 3;
|
|
194
|
+
reasons.push(`keyword:${keyword}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
for (const file of input.files) {
|
|
198
|
+
const matched = config.filePatterns.find(pattern => pattern.test(file));
|
|
199
|
+
if (matched) {
|
|
200
|
+
score += 4;
|
|
201
|
+
reasons.push(`file:${file}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return { domain, score, reasons: unique(reasons) };
|
|
205
|
+
})
|
|
206
|
+
.filter(domain => domain.score > 0)
|
|
207
|
+
.sort((a, b) => b.score - a.score || a.domain.localeCompare(b.domain));
|
|
208
|
+
}
|
|
209
|
+
function entryMatchesDomains(entry, domains) {
|
|
210
|
+
return domains.some(domain => DOMAIN_CONFIG[domain.domain]?.categories.includes(entry.category));
|
|
211
|
+
}
|
|
212
|
+
function findToolForEntry(entry, report) {
|
|
213
|
+
const ids = toolIdsForEntry(entry);
|
|
214
|
+
return report.tools.find(item => ids.includes(item.id) || ids.includes(item.skillId ?? ''));
|
|
215
|
+
}
|
|
216
|
+
function toolIdsForEntry(entry) {
|
|
217
|
+
if (entry.id === 'cua')
|
|
218
|
+
return ['cua', 'desktop-cua'];
|
|
219
|
+
return [entry.id];
|
|
220
|
+
}
|
|
221
|
+
function policyIdForEntry(entry, tool) {
|
|
222
|
+
if (tool)
|
|
223
|
+
return tool.id;
|
|
224
|
+
if (entry.id === 'cua')
|
|
225
|
+
return 'desktop-cua';
|
|
226
|
+
return entry.id;
|
|
227
|
+
}
|
|
228
|
+
function resolveSafetyLevel(entry, input) {
|
|
229
|
+
if (input.blocked)
|
|
230
|
+
return 'blocked';
|
|
231
|
+
if (entry.category === 'browser' || entry.category === 'desktop' || entry.category === 'agent-cli')
|
|
232
|
+
return 'restricted';
|
|
233
|
+
if (input.policyConfig?.destructiveActions === 'confirm')
|
|
234
|
+
return 'restricted';
|
|
235
|
+
if (entry.trust === 'official')
|
|
236
|
+
return 'trusted';
|
|
237
|
+
return 'review-required';
|
|
238
|
+
}
|
|
239
|
+
function confidenceScore(entry, input) {
|
|
240
|
+
if (!input.policyEnabled || input.safetyLevel === 'blocked')
|
|
241
|
+
return 0.2;
|
|
242
|
+
let score = 0.28;
|
|
243
|
+
score += Math.min(0.22, input.domainScore * 0.025);
|
|
244
|
+
if (entry.trust === 'official')
|
|
245
|
+
score += 0.12;
|
|
246
|
+
if (entry.trust === 'ecosystem')
|
|
247
|
+
score += 0.06;
|
|
248
|
+
if (input.installed)
|
|
249
|
+
score += 0.22;
|
|
250
|
+
if (input.tool?.installed)
|
|
251
|
+
score += 0.08;
|
|
252
|
+
if (input.projectHasPackage && (entry.category === 'ui' || entry.category === 'browser' || entry.category === 'testing'))
|
|
253
|
+
score += 0.08;
|
|
254
|
+
if (input.projectHasFrontendFiles && entry.category === 'ui')
|
|
255
|
+
score += 0.08;
|
|
256
|
+
if (input.safetyLevel === 'review-required')
|
|
257
|
+
score -= 0.08;
|
|
258
|
+
if (input.safetyLevel === 'restricted')
|
|
259
|
+
score -= 0.05;
|
|
260
|
+
return roundConfidence(Math.max(0.05, Math.min(0.95, score)));
|
|
261
|
+
}
|
|
262
|
+
function resolveAction(safetyLevel, confidence) {
|
|
263
|
+
if (safetyLevel === 'blocked')
|
|
264
|
+
return 'blocked';
|
|
265
|
+
if (confidence < 0.4)
|
|
266
|
+
return 'suggest-fallback';
|
|
267
|
+
if (confidence <= 0.7)
|
|
268
|
+
return 'recommend-with-evidence';
|
|
269
|
+
return 'may-run-with-policy';
|
|
270
|
+
}
|
|
271
|
+
function reasonFor(entry, input) {
|
|
272
|
+
const domains = input.domainHits.map(hit => hit.domain).join(', ') || entry.category;
|
|
273
|
+
const install = input.installed ? 'installed' : 'not installed';
|
|
274
|
+
const policy = input.policyEnabled ? 'policy enabled' : 'policy disabled';
|
|
275
|
+
return `${entry.name} matches ${domains}; ${install}; ${policy}.`;
|
|
276
|
+
}
|
|
277
|
+
function riskFor(entry, safetyLevel, policyEnabled) {
|
|
278
|
+
if (!policyEnabled)
|
|
279
|
+
return 'Disabled by tool policy; do not run until explicitly enabled.';
|
|
280
|
+
if (safetyLevel === 'blocked')
|
|
281
|
+
return 'Install or execution safety blocked this capability.';
|
|
282
|
+
if (entry.category === 'desktop')
|
|
283
|
+
return 'Desktop automation can affect local applications and must stay inside an operator boundary.';
|
|
284
|
+
if (entry.category === 'browser')
|
|
285
|
+
return 'Browser automation may touch authenticated state and must capture console/network evidence.';
|
|
286
|
+
if (entry.category === 'agent-cli')
|
|
287
|
+
return 'External agent CLI can modify files or consume credentials; use dry-run or scoped commands.';
|
|
288
|
+
if (safetyLevel === 'review-required')
|
|
289
|
+
return 'Third-party skill requires supply-chain review before installation or promotion.';
|
|
290
|
+
return 'Low operational risk, but completion still requires evidence.';
|
|
291
|
+
}
|
|
292
|
+
function fallbackFor(entry, domainHits) {
|
|
293
|
+
const domainFallback = domainHits.map(domain => DOMAIN_CONFIG[domain.domain]?.fallback).find(Boolean);
|
|
294
|
+
if (domainFallback)
|
|
295
|
+
return domainFallback;
|
|
296
|
+
if (entry.category === 'review')
|
|
297
|
+
return 'Use built-in review, lint, test, and runtime evidence commands.';
|
|
298
|
+
if (entry.category === 'docs')
|
|
299
|
+
return 'Update canonical docs manually and record changed source-of-truth files.';
|
|
300
|
+
return 'Use manual verification evidence and document why the skill was skipped.';
|
|
301
|
+
}
|
|
302
|
+
function capabilityFor(entry) {
|
|
303
|
+
const map = {
|
|
304
|
+
ui: 'ui-ux-design',
|
|
305
|
+
browser: 'browser-automation',
|
|
306
|
+
desktop: 'desktop-automation',
|
|
307
|
+
testing: 'test-automation',
|
|
308
|
+
review: 'quality-review',
|
|
309
|
+
docs: 'documentation',
|
|
310
|
+
'agent-cli': 'external-agent-cli',
|
|
311
|
+
'role-library': 'role-orchestration',
|
|
312
|
+
discovery: 'skill-discovery',
|
|
313
|
+
};
|
|
314
|
+
return map[entry.category];
|
|
315
|
+
}
|
|
316
|
+
function skillInstalledInProject(skillId, projectDir) {
|
|
317
|
+
const candidates = [
|
|
318
|
+
join(projectDir, '.agents', 'skills', skillId, 'SKILL.md'),
|
|
319
|
+
join(projectDir, '.codex', 'skills', skillId, 'SKILL.md'),
|
|
320
|
+
join(projectDir, '.claude', 'skills', skillId, 'SKILL.md'),
|
|
321
|
+
];
|
|
322
|
+
if (candidates.some(path => existsSync(path)))
|
|
323
|
+
return true;
|
|
324
|
+
return localSkillIndex(projectDir).some(id => id === skillId);
|
|
325
|
+
}
|
|
326
|
+
function localSkillIndex(projectDir) {
|
|
327
|
+
const roots = [join(projectDir, '.agents', 'skills'), join(projectDir, '.codex', 'skills'), join(projectDir, '.claude', 'skills')];
|
|
328
|
+
return roots.flatMap(root => {
|
|
329
|
+
try {
|
|
330
|
+
return readdirSync(root).filter(name => existsSync(join(root, name, 'SKILL.md')));
|
|
331
|
+
}
|
|
332
|
+
catch {
|
|
333
|
+
return [];
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
function normalizeFiles(files) {
|
|
338
|
+
return (files ?? [])
|
|
339
|
+
.map(file => file.replace(/\\/g, '/').trim())
|
|
340
|
+
.filter(Boolean)
|
|
341
|
+
.filter(file => extname(file) || file.includes('/'));
|
|
342
|
+
}
|
|
343
|
+
function safetyRank(level) {
|
|
344
|
+
const rank = {
|
|
345
|
+
trusted: 0,
|
|
346
|
+
'review-required': 1,
|
|
347
|
+
restricted: 2,
|
|
348
|
+
blocked: 3,
|
|
349
|
+
};
|
|
350
|
+
return rank[level];
|
|
351
|
+
}
|
|
352
|
+
function roundConfidence(value) {
|
|
353
|
+
return Math.round(value * 100) / 100;
|
|
354
|
+
}
|
|
355
|
+
function unique(items) {
|
|
356
|
+
return [...new Set(items)];
|
|
357
|
+
}
|
|
358
|
+
export function renderSkillRadarMarkdown(report) {
|
|
359
|
+
const lines = [
|
|
360
|
+
'# Skill Radar',
|
|
361
|
+
'',
|
|
362
|
+
`Task: ${report.task}`,
|
|
363
|
+
`Level: ${report.level}`,
|
|
364
|
+
`Generated: ${report.generatedAt}`,
|
|
365
|
+
'',
|
|
366
|
+
'## Detected Domains',
|
|
367
|
+
'',
|
|
368
|
+
'| Domain | Score | Reasons |',
|
|
369
|
+
'| --- | ---: | --- |',
|
|
370
|
+
...report.detectedDomains.map(domain => `| ${domain.domain} | ${domain.score} | ${domain.reasons.join(', ')} |`),
|
|
371
|
+
'',
|
|
372
|
+
'## Recommendations',
|
|
373
|
+
'',
|
|
374
|
+
'| Skill | Capability | Confidence | Safety | Action | Evidence |',
|
|
375
|
+
'| --- | --- | ---: | --- | --- | --- |',
|
|
376
|
+
...report.recommendations.map(item => `| ${item.id} | ${item.capability} | ${item.confidence.toFixed(2)} | ${item.safetyLevel} | ${item.action} | ${item.requiredEvidence.join(', ')} |`),
|
|
377
|
+
'',
|
|
378
|
+
'## Fallbacks',
|
|
379
|
+
'',
|
|
380
|
+
...(report.fallbacks.length ? report.fallbacks.map(item => `- ${item}`) : ['- none']),
|
|
381
|
+
];
|
|
382
|
+
return lines.join('\n');
|
|
383
|
+
}
|
|
384
|
+
//# sourceMappingURL=SkillRadar.js.map
|