peaks-cli 1.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.
- package/LICENSE +52 -0
- package/README.md +417 -0
- package/bin/peaks.js +2 -0
- package/dist/src/cli/cli-helpers.d.ts +25 -0
- package/dist/src/cli/cli-helpers.js +78 -0
- package/dist/src/cli/commands/capability-commands.d.ts +5 -0
- package/dist/src/cli/commands/capability-commands.js +46 -0
- package/dist/src/cli/commands/capability-worker-config-sc-commands.d.ts +3 -0
- package/dist/src/cli/commands/capability-worker-config-sc-commands.js +10 -0
- package/dist/src/cli/commands/config-commands.d.ts +3 -0
- package/dist/src/cli/commands/config-commands.js +212 -0
- package/dist/src/cli/commands/core-artifact-commands.d.ts +3 -0
- package/dist/src/cli/commands/core-artifact-commands.js +200 -0
- package/dist/src/cli/commands/sc-commands.d.ts +3 -0
- package/dist/src/cli/commands/sc-commands.js +37 -0
- package/dist/src/cli/commands/worker-commands.d.ts +3 -0
- package/dist/src/cli/commands/worker-commands.js +52 -0
- package/dist/src/cli/commands/workflow-commands.d.ts +3 -0
- package/dist/src/cli/commands/workflow-commands.js +257 -0
- package/dist/src/cli/index.d.ts +1 -0
- package/dist/src/cli/index.js +14 -0
- package/dist/src/cli/program.d.ts +4 -0
- package/dist/src/cli/program.js +13 -0
- package/dist/src/services/artifacts/artifact-service.d.ts +43 -0
- package/dist/src/services/artifacts/artifact-service.js +97 -0
- package/dist/src/services/artifacts/workspace-service.d.ts +33 -0
- package/dist/src/services/artifacts/workspace-service.js +254 -0
- package/dist/src/services/config/config-service.d.ts +29 -0
- package/dist/src/services/config/config-service.js +501 -0
- package/dist/src/services/config/config-types.d.ts +63 -0
- package/dist/src/services/config/config-types.js +16 -0
- package/dist/src/services/config/model-routing.d.ts +4 -0
- package/dist/src/services/config/model-routing.js +15 -0
- package/dist/src/services/doctor/doctor-service.d.ts +18 -0
- package/dist/src/services/doctor/doctor-service.js +68 -0
- package/dist/src/services/memory/project-memory-service.d.ts +79 -0
- package/dist/src/services/memory/project-memory-service.js +306 -0
- package/dist/src/services/profiles/profile-service.d.ts +6 -0
- package/dist/src/services/profiles/profile-service.js +19 -0
- package/dist/src/services/providers/minimax-provider-service.d.ts +24 -0
- package/dist/src/services/providers/minimax-provider-service.js +143 -0
- package/dist/src/services/providers/minimax-worker-service.d.ts +21 -0
- package/dist/src/services/providers/minimax-worker-service.js +80 -0
- package/dist/src/services/proxy/proxy-service.d.ts +7 -0
- package/dist/src/services/proxy/proxy-service.js +31 -0
- package/dist/src/services/rd/rd-service.d.ts +88 -0
- package/dist/src/services/rd/rd-service.js +370 -0
- package/dist/src/services/recommendations/capability-availability.d.ts +5 -0
- package/dist/src/services/recommendations/capability-availability.js +40 -0
- package/dist/src/services/recommendations/capability-map-service.d.ts +7 -0
- package/dist/src/services/recommendations/capability-map-service.js +131 -0
- package/dist/src/services/recommendations/capability-seed-items.d.ts +2 -0
- package/dist/src/services/recommendations/capability-seed-items.js +131 -0
- package/dist/src/services/recommendations/capability-seed-mappings.d.ts +2 -0
- package/dist/src/services/recommendations/capability-seed-mappings.js +42 -0
- package/dist/src/services/recommendations/capability-seed-sources.d.ts +2 -0
- package/dist/src/services/recommendations/capability-seed-sources.js +35 -0
- package/dist/src/services/recommendations/recommendation-service.d.ts +8 -0
- package/dist/src/services/recommendations/recommendation-service.js +106 -0
- package/dist/src/services/recommendations/recommendation-types.d.ts +129 -0
- package/dist/src/services/recommendations/recommendation-types.js +1 -0
- package/dist/src/services/recommendations/seed-capability-catalog.d.ts +3 -0
- package/dist/src/services/recommendations/seed-capability-catalog.js +3 -0
- package/dist/src/services/refactor/refactor-service.d.ts +9 -0
- package/dist/src/services/refactor/refactor-service.js +33 -0
- package/dist/src/services/sc/index.d.ts +1 -0
- package/dist/src/services/sc/index.js +1 -0
- package/dist/src/services/sc/sc-service.d.ts +79 -0
- package/dist/src/services/sc/sc-service.js +223 -0
- package/dist/src/services/skills/skill-registry.d.ts +17 -0
- package/dist/src/services/skills/skill-registry.js +40 -0
- package/dist/src/services/standards/project-standards-service.d.ts +82 -0
- package/dist/src/services/standards/project-standards-service.js +383 -0
- package/dist/src/services/tech/tech-service.d.ts +69 -0
- package/dist/src/services/tech/tech-service.js +236 -0
- package/dist/src/services/workflow/workflow-autonomous-service.d.ts +99 -0
- package/dist/src/services/workflow/workflow-autonomous-service.js +526 -0
- package/dist/src/services/workflow/workflow-router-service.d.ts +85 -0
- package/dist/src/services/workflow/workflow-router-service.js +213 -0
- package/dist/src/shared/change-id.d.ts +15 -0
- package/dist/src/shared/change-id.js +76 -0
- package/dist/src/shared/frontmatter.d.ts +6 -0
- package/dist/src/shared/frontmatter.js +47 -0
- package/dist/src/shared/fs-utils.d.ts +4 -0
- package/dist/src/shared/fs-utils.js +16 -0
- package/dist/src/shared/fs.d.ts +4 -0
- package/dist/src/shared/fs.js +26 -0
- package/dist/src/shared/path-utils.d.ts +13 -0
- package/dist/src/shared/path-utils.js +56 -0
- package/dist/src/shared/paths.d.ts +6 -0
- package/dist/src/shared/paths.js +40 -0
- package/dist/src/shared/planner-response.d.ts +21 -0
- package/dist/src/shared/planner-response.js +26 -0
- package/dist/src/shared/platform.d.ts +6 -0
- package/dist/src/shared/platform.js +11 -0
- package/dist/src/shared/process.d.ts +5 -0
- package/dist/src/shared/process.js +12 -0
- package/dist/src/shared/result.d.ts +13 -0
- package/dist/src/shared/result.js +32 -0
- package/package.json +49 -0
- package/schemas/approval-record.schema.json +14 -0
- package/schemas/artifact-manifest.schema.json +16 -0
- package/schemas/artifact-retention-report.schema.json +17 -0
- package/schemas/artifact-workspace.schema.json +22 -0
- package/schemas/capability-availability.schema.json +36 -0
- package/schemas/capability-item.schema.json +37 -0
- package/schemas/capability-source.schema.json +30 -0
- package/schemas/change-impact.schema.json +15 -0
- package/schemas/context-capsule.schema.json +16 -0
- package/schemas/recommendation-plan.schema.json +37 -0
- package/schemas/refactor-slice-spec.schema.json +19 -0
- package/scripts/clean-dist.mjs +8 -0
- package/scripts/install-skills.mjs +76 -0
- package/scripts/watch.mjs +389 -0
- package/skills/peaks-prd/SKILL.md +42 -0
- package/skills/peaks-prd/references/artifact-contracts.md +3 -0
- package/skills/peaks-prd/references/command-migration.md +3 -0
- package/skills/peaks-prd/references/workflow.md +11 -0
- package/skills/peaks-qa/SKILL.md +45 -0
- package/skills/peaks-qa/references/artifact-contracts.md +3 -0
- package/skills/peaks-qa/references/command-migration.md +3 -0
- package/skills/peaks-qa/references/regression-gates.md +16 -0
- package/skills/peaks-rd/SKILL.md +56 -0
- package/skills/peaks-rd/references/artifact-contracts.md +3 -0
- package/skills/peaks-rd/references/command-migration.md +3 -0
- package/skills/peaks-rd/references/refactor-workflow.md +31 -0
- package/skills/peaks-sc/SKILL.md +30 -0
- package/skills/peaks-sc/references/artifact-contracts.md +3 -0
- package/skills/peaks-sc/references/artifact-retention.md +14 -0
- package/skills/peaks-sc/references/command-migration.md +3 -0
- package/skills/peaks-solo/SKILL.md +63 -0
- package/skills/peaks-solo/references/artifact-contracts.md +3 -0
- package/skills/peaks-solo/references/command-migration.md +3 -0
- package/skills/peaks-solo/references/refactor-mode.md +22 -0
- package/skills/peaks-solo/references/workflow.md +14 -0
- package/skills/peaks-txt/SKILL.md +48 -0
- package/skills/peaks-txt/references/artifact-contracts.md +3 -0
- package/skills/peaks-txt/references/command-migration.md +3 -0
- package/skills/peaks-txt/references/context-capsule.md +20 -0
- package/skills/peaks-ui/SKILL.md +35 -0
- package/skills/peaks-ui/references/artifact-contracts.md +3 -0
- package/skills/peaks-ui/references/command-migration.md +3 -0
- package/skills/peaks-ui/references/workflow.md +11 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
function shellQuote(value) {
|
|
2
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
3
|
+
}
|
|
4
|
+
function validateUrl(value, label) {
|
|
5
|
+
if (!value.startsWith('http://') && !value.startsWith('https://')) {
|
|
6
|
+
throw new Error(`${label} URL must start with http:// or https://`);
|
|
7
|
+
}
|
|
8
|
+
let url;
|
|
9
|
+
try {
|
|
10
|
+
url = new URL(value);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
throw new Error(`${label} URL must be a valid URL`);
|
|
14
|
+
}
|
|
15
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
16
|
+
throw new Error(`${label} URL must start with http:// or https://`);
|
|
17
|
+
}
|
|
18
|
+
if (url.username || url.password) {
|
|
19
|
+
throw new Error(`${label} URL must not include credentials`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function planProxyTest(proxy, target = 'https://www.google.com', dryRun = true) {
|
|
23
|
+
validateUrl(proxy, 'Proxy');
|
|
24
|
+
validateUrl(target, 'Target');
|
|
25
|
+
return {
|
|
26
|
+
proxy,
|
|
27
|
+
target,
|
|
28
|
+
dryRun,
|
|
29
|
+
commandPreview: `curl -x ${shellQuote(proxy)} -I ${shellQuote(target)}`
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { WorkspaceConfig } from '../config/config-types.js';
|
|
2
|
+
export type RdSkill = 'rd';
|
|
3
|
+
export type RdWaveName = 'discovery' | 'planning' | 'implementation candidates' | 'unit-test execution' | 'quality gates' | 'reducer';
|
|
4
|
+
export type RdModelRole = 'strongest' | 'execution';
|
|
5
|
+
export type RdSwarmPlanRequest = {
|
|
6
|
+
skill: RdSkill;
|
|
7
|
+
changeId: string;
|
|
8
|
+
goal: string;
|
|
9
|
+
maxWorkers: number;
|
|
10
|
+
dryRun: true;
|
|
11
|
+
swarmMode?: boolean;
|
|
12
|
+
artifactWorkspacePath?: string;
|
|
13
|
+
workspace?: WorkspaceConfig;
|
|
14
|
+
requiresTechApproval?: boolean;
|
|
15
|
+
executionModelId?: string;
|
|
16
|
+
};
|
|
17
|
+
export type RdTask = {
|
|
18
|
+
taskId: string;
|
|
19
|
+
wave: RdWaveName;
|
|
20
|
+
workerKind: string;
|
|
21
|
+
purpose: string;
|
|
22
|
+
modelRole: RdModelRole;
|
|
23
|
+
modelId: string;
|
|
24
|
+
inputs: string[];
|
|
25
|
+
outputs: [string, ...string[]];
|
|
26
|
+
dependsOn: string[];
|
|
27
|
+
conflictGroup: string;
|
|
28
|
+
targetArea: string;
|
|
29
|
+
expectedEvidence: string;
|
|
30
|
+
};
|
|
31
|
+
export type RdWave = {
|
|
32
|
+
name: RdWaveName;
|
|
33
|
+
taskIds: string[];
|
|
34
|
+
};
|
|
35
|
+
export type RdConflictGroup = {
|
|
36
|
+
groupId: string;
|
|
37
|
+
ownedPaths: string[];
|
|
38
|
+
parallelismPolicy: string;
|
|
39
|
+
reason: string;
|
|
40
|
+
};
|
|
41
|
+
export type RdPlanResult = {
|
|
42
|
+
available: true;
|
|
43
|
+
changeId: string;
|
|
44
|
+
goal: string;
|
|
45
|
+
swarmMode: boolean;
|
|
46
|
+
workerTarget: number;
|
|
47
|
+
waves: RdWave[];
|
|
48
|
+
tasks: RdTask[];
|
|
49
|
+
conflictGroups: RdConflictGroup[];
|
|
50
|
+
artifactRoot: string;
|
|
51
|
+
outputs: {
|
|
52
|
+
taskGraph: string;
|
|
53
|
+
waveManifests: string[];
|
|
54
|
+
workerBriefs: string[];
|
|
55
|
+
reducerReport: string;
|
|
56
|
+
};
|
|
57
|
+
gateStatus: {
|
|
58
|
+
techApprovalRequired: boolean;
|
|
59
|
+
techStatus: string;
|
|
60
|
+
skipReason?: string;
|
|
61
|
+
};
|
|
62
|
+
blockedReasons: string[];
|
|
63
|
+
nextActions: string[];
|
|
64
|
+
} | {
|
|
65
|
+
available: false;
|
|
66
|
+
behavior: 'preview' | 'blocked';
|
|
67
|
+
reason: string;
|
|
68
|
+
swarmMode: boolean;
|
|
69
|
+
workerTarget: number;
|
|
70
|
+
waves: RdWave[];
|
|
71
|
+
tasks: RdTask[];
|
|
72
|
+
conflictGroups: RdConflictGroup[];
|
|
73
|
+
artifactRoot: string;
|
|
74
|
+
outputs: {
|
|
75
|
+
taskGraph: string;
|
|
76
|
+
waveManifests: string[];
|
|
77
|
+
workerBriefs: string[];
|
|
78
|
+
reducerReport: string;
|
|
79
|
+
};
|
|
80
|
+
gateStatus: {
|
|
81
|
+
techApprovalRequired: boolean;
|
|
82
|
+
techStatus: string;
|
|
83
|
+
skipReason?: string;
|
|
84
|
+
};
|
|
85
|
+
blockedReasons: string[];
|
|
86
|
+
nextActions: string[];
|
|
87
|
+
};
|
|
88
|
+
export declare function createRdSwarmPlan(request: RdSwarmPlanRequest): RdPlanResult;
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { closeSync, fstatSync, lstatSync, openSync, readSync, statSync } from 'node:fs';
|
|
2
|
+
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
3
|
+
import { stableRealPath } from '../../shared/path-utils.js';
|
|
4
|
+
import { validateChangeIdOrThrow, buildArtifactRelativePath } from '../../shared/change-id.js';
|
|
5
|
+
import { WORKSPACE_UNAVAILABLE_NEXT_ACTIONS } from '../../shared/planner-response.js';
|
|
6
|
+
import { getLocalArtifactPath, hasValidArtifactWorkspace } from '../artifacts/workspace-service.js';
|
|
7
|
+
import { getConfiguredExecutionModelId, STRONGEST_MODEL_ID } from '../config/model-routing.js';
|
|
8
|
+
import { getTechStatus, TECH_REQUIRED_ARTIFACTS } from '../tech/tech-service.js';
|
|
9
|
+
function normalizeGoal(goal) {
|
|
10
|
+
const normalized = goal.trim();
|
|
11
|
+
if (!normalized) {
|
|
12
|
+
throw new Error('Goal must be non-empty');
|
|
13
|
+
}
|
|
14
|
+
return normalized;
|
|
15
|
+
}
|
|
16
|
+
function isClearLowRiskGoal(goal) {
|
|
17
|
+
return /^fix\b/i.test(goal) && /\b(typo|spelling|comment|docs?|test|lint|format|copy)\b/i.test(goal);
|
|
18
|
+
}
|
|
19
|
+
const MIN_SAFE_SWARM_WORKERS = 25;
|
|
20
|
+
const MAX_SAFE_SWARM_WORKERS = 80;
|
|
21
|
+
function resolveWorkerTarget(maxWorkers) {
|
|
22
|
+
if (!Number.isInteger(maxWorkers) || maxWorkers < 1) {
|
|
23
|
+
throw new Error('max-workers must be a positive integer');
|
|
24
|
+
}
|
|
25
|
+
if (maxWorkers < MIN_SAFE_SWARM_WORKERS) {
|
|
26
|
+
return { workerTarget: maxWorkers, blockedReasons: ['worker-count-below-target'] };
|
|
27
|
+
}
|
|
28
|
+
if (maxWorkers > MAX_SAFE_SWARM_WORKERS) {
|
|
29
|
+
return { workerTarget: MAX_SAFE_SWARM_WORKERS, blockedReasons: ['worker-count-capped'] };
|
|
30
|
+
}
|
|
31
|
+
return { workerTarget: maxWorkers, blockedReasons: [] };
|
|
32
|
+
}
|
|
33
|
+
function resolveArtifactWorkspacePath(request) {
|
|
34
|
+
return request.artifactWorkspacePath ?? (request.workspace ? getLocalArtifactPath(request.workspace) : undefined);
|
|
35
|
+
}
|
|
36
|
+
function hasPlannerArtifactWorkspace(request, artifactWorkspacePath) {
|
|
37
|
+
return !!request.workspace && !!artifactWorkspacePath && hasValidArtifactWorkspace(request.workspace, artifactWorkspacePath);
|
|
38
|
+
}
|
|
39
|
+
function buildTaskIds(workerTarget) {
|
|
40
|
+
const fixed = [
|
|
41
|
+
'rd-discovery-1', 'rd-discovery-2', 'rd-discovery-3', 'rd-discovery-4', 'rd-discovery-5', 'rd-discovery-6', 'rd-discovery-7', 'rd-discovery-8',
|
|
42
|
+
'rd-planning-1', 'rd-planning-2', 'rd-planning-3', 'rd-planning-4', 'rd-planning-5', 'rd-planning-6', 'rd-planning-7', 'rd-planning-8',
|
|
43
|
+
'rd-test-1', 'rd-test-2', 'rd-test-3',
|
|
44
|
+
'peaks-qa-1', 'peaks-qa-2', 'peaks-qa-3', 'peaks-qa-4',
|
|
45
|
+
'rd-reducer-1',
|
|
46
|
+
];
|
|
47
|
+
const implementationCount = Math.max(workerTarget - fixed.length, 1);
|
|
48
|
+
const implementation = Array.from({ length: implementationCount }, (_, index) => `rd-impl-${String(index + 1).padStart(3, '0')}`);
|
|
49
|
+
return [...fixed.slice(0, 16), ...implementation, ...fixed.slice(16)];
|
|
50
|
+
}
|
|
51
|
+
function isInsidePath(childPath, parentPath) {
|
|
52
|
+
const relativePath = relative(parentPath, childPath);
|
|
53
|
+
return relativePath === '' || (!relativePath.startsWith('..') && !isAbsolute(relativePath));
|
|
54
|
+
}
|
|
55
|
+
function isSafeRepoRelativePath(candidate) {
|
|
56
|
+
if (!candidate || candidate.includes('\\') || candidate.startsWith('/') || /^[A-Za-z]:/.test(candidate) || candidate.includes(':') || isAbsolute(candidate)) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return candidate.split('/').every((segment) => segment.length > 0 && segment !== '.' && segment !== '..');
|
|
60
|
+
}
|
|
61
|
+
function extractCandidatePath(line) {
|
|
62
|
+
const trimmed = line.trim().replace(/^[-*+]\s+/, '').trim();
|
|
63
|
+
const codeSpan = trimmed.match(/^`([^`]+)`(?:\s|$)/);
|
|
64
|
+
const firstToken = trimmed.split(/\s+/)[0];
|
|
65
|
+
const candidate = codeSpan === null ? firstToken : codeSpan[1];
|
|
66
|
+
const normalized = candidate.replace(/[),.;:]+$/, '');
|
|
67
|
+
return normalized.length > 0 ? normalized : null;
|
|
68
|
+
}
|
|
69
|
+
function extractImplementationTargetAreas(content) {
|
|
70
|
+
const lines = content.split(/\r?\n/);
|
|
71
|
+
const sectionStart = lines.findIndex((line) => /^#{0,6}\s*Implementation target areas\s*:?\s*$/i.test(line.trim()));
|
|
72
|
+
if (sectionStart === -1) {
|
|
73
|
+
return [];
|
|
74
|
+
}
|
|
75
|
+
const sectionLines = lines.slice(sectionStart + 1);
|
|
76
|
+
const sectionEnd = sectionLines.findIndex((line) => /^#{1,6}\s+\S/.test(line.trim()));
|
|
77
|
+
return (sectionEnd === -1 ? sectionLines : sectionLines.slice(0, sectionEnd))
|
|
78
|
+
.map(extractCandidatePath)
|
|
79
|
+
.filter((candidate) => candidate !== null)
|
|
80
|
+
.filter((candidate) => /^(?:packages|src|tests|apps|libs)\//.test(candidate))
|
|
81
|
+
.filter(isSafeRepoRelativePath);
|
|
82
|
+
}
|
|
83
|
+
function hasConcreteTargetAreas(targetAreas) {
|
|
84
|
+
return targetAreas.length > 0;
|
|
85
|
+
}
|
|
86
|
+
function selectConcreteTargetArea(targetAreas, index) {
|
|
87
|
+
return targetAreas[index % targetAreas.length];
|
|
88
|
+
}
|
|
89
|
+
function getTaskModelRole(wave) {
|
|
90
|
+
return wave === 'implementation candidates' || wave === 'unit-test execution' ? 'execution' : 'strongest';
|
|
91
|
+
}
|
|
92
|
+
function getTaskModelId(modelRole, executionModelId) {
|
|
93
|
+
return modelRole === 'execution' ? executionModelId : STRONGEST_MODEL_ID;
|
|
94
|
+
}
|
|
95
|
+
const MAX_ARTIFACT_BYTES = 256_000;
|
|
96
|
+
function readArtifactFile(rootPath, artifactWorkspacePath, artifact) {
|
|
97
|
+
const artifactPath = resolve(rootPath, artifact);
|
|
98
|
+
try {
|
|
99
|
+
const artifactWorkspaceRealPath = stableRealPath(artifactWorkspacePath);
|
|
100
|
+
const rootRealPath = stableRealPath(rootPath);
|
|
101
|
+
if (!isInsidePath(rootRealPath, artifactWorkspaceRealPath)) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
const artifactStat = lstatSync(artifactPath);
|
|
105
|
+
if (artifactStat.isSymbolicLink() || !artifactStat.isFile() || artifactStat.size > MAX_ARTIFACT_BYTES) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
if (!isInsidePath(stableRealPath(artifactPath), rootRealPath)) {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const fd = openSync(artifactPath, 'r');
|
|
112
|
+
try {
|
|
113
|
+
const openedStat = fstatSync(fd);
|
|
114
|
+
const currentStat = statSync(artifactPath);
|
|
115
|
+
if (!openedStat.isFile() || openedStat.size > MAX_ARTIFACT_BYTES || openedStat.dev !== artifactStat.dev || openedStat.ino !== artifactStat.ino || openedStat.dev !== currentStat.dev || openedStat.ino !== currentStat.ino) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const buffer = Buffer.alloc(MAX_ARTIFACT_BYTES + 1);
|
|
119
|
+
const bytesRead = readSync(fd, buffer, 0, buffer.length, 0);
|
|
120
|
+
if (bytesRead > MAX_ARTIFACT_BYTES) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
const finalStat = fstatSync(fd);
|
|
124
|
+
if (finalStat.dev !== openedStat.dev || finalStat.ino !== openedStat.ino) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
return buffer.subarray(0, bytesRead).toString('utf8');
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
closeSync(fd);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function getConcreteTargetAreas(request, artifactWorkspacePath, hasApprovedTechArtifacts) {
|
|
138
|
+
if (!artifactWorkspacePath || !hasApprovedTechArtifacts || !hasPlannerArtifactWorkspace(request, artifactWorkspacePath)) {
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
const architectureRoot = join(artifactWorkspacePath, '.peaks', 'changes', request.changeId, 'architecture');
|
|
142
|
+
const candidates = TECH_REQUIRED_ARTIFACTS.flatMap((artifact) => {
|
|
143
|
+
if (artifact === 'tech-approval-record.md') {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
const content = readArtifactFile(architectureRoot, artifactWorkspacePath, artifact);
|
|
147
|
+
return content ? extractImplementationTargetAreas(content) : [];
|
|
148
|
+
});
|
|
149
|
+
return [...new Set(candidates)];
|
|
150
|
+
}
|
|
151
|
+
function buildPlan(request) {
|
|
152
|
+
validateChangeIdOrThrow(request.changeId);
|
|
153
|
+
const goal = normalizeGoal(request.goal);
|
|
154
|
+
const swarmMode = request.swarmMode ?? true;
|
|
155
|
+
const executionModelId = request.executionModelId?.trim() || getConfiguredExecutionModelId(undefined);
|
|
156
|
+
const { workerTarget, blockedReasons } = resolveWorkerTarget(request.maxWorkers);
|
|
157
|
+
const artifactWorkspacePath = resolveArtifactWorkspacePath(request);
|
|
158
|
+
const artifactRoot = buildArtifactRelativePath(request.changeId, 'swarm');
|
|
159
|
+
const techStatus = getTechStatus({
|
|
160
|
+
changeId: request.changeId,
|
|
161
|
+
...(artifactWorkspacePath ? { artifactWorkspacePath } : {}),
|
|
162
|
+
...(request.workspace ? { workspace: request.workspace } : {}),
|
|
163
|
+
});
|
|
164
|
+
const requiresTechApproval = request.requiresTechApproval ?? !isClearLowRiskGoal(goal);
|
|
165
|
+
const techGateSkipped = !requiresTechApproval;
|
|
166
|
+
if (!swarmMode) {
|
|
167
|
+
return {
|
|
168
|
+
changeId: request.changeId,
|
|
169
|
+
goal,
|
|
170
|
+
swarmMode,
|
|
171
|
+
workerTarget,
|
|
172
|
+
waves: [],
|
|
173
|
+
tasks: [],
|
|
174
|
+
conflictGroups: [],
|
|
175
|
+
artifactRoot,
|
|
176
|
+
outputs: {
|
|
177
|
+
taskGraph: buildArtifactRelativePath(request.changeId, 'swarm', 'task-graph.json'),
|
|
178
|
+
waveManifests: [],
|
|
179
|
+
workerBriefs: [],
|
|
180
|
+
reducerReport: buildArtifactRelativePath(request.changeId, 'swarm', 'reducer-report.md'),
|
|
181
|
+
},
|
|
182
|
+
gateStatus: {
|
|
183
|
+
techApprovalRequired: requiresTechApproval,
|
|
184
|
+
techStatus: techStatus.status,
|
|
185
|
+
...(techGateSkipped ? { skipReason: 'tech-gate-skipped-clear-implementation-path' } : {}),
|
|
186
|
+
},
|
|
187
|
+
blockedReasons,
|
|
188
|
+
nextActions: [],
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
if (requiresTechApproval && techStatus.status !== 'approved') {
|
|
192
|
+
return {
|
|
193
|
+
changeId: request.changeId,
|
|
194
|
+
goal,
|
|
195
|
+
swarmMode,
|
|
196
|
+
workerTarget,
|
|
197
|
+
waves: [],
|
|
198
|
+
tasks: [],
|
|
199
|
+
conflictGroups: [],
|
|
200
|
+
artifactRoot,
|
|
201
|
+
outputs: {
|
|
202
|
+
taskGraph: buildArtifactRelativePath(request.changeId, 'swarm', 'task-graph.json'),
|
|
203
|
+
waveManifests: [],
|
|
204
|
+
workerBriefs: [],
|
|
205
|
+
reducerReport: buildArtifactRelativePath(request.changeId, 'swarm', 'reducer-report.md'),
|
|
206
|
+
},
|
|
207
|
+
gateStatus: {
|
|
208
|
+
techApprovalRequired: true,
|
|
209
|
+
techStatus: techStatus.status,
|
|
210
|
+
},
|
|
211
|
+
blockedReasons: ['tech-approval-required', ...blockedReasons],
|
|
212
|
+
nextActions: ['Run peaks tech plan --dry-run and approve the tech plan before running peaks swarm plan.'],
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
if (blockedReasons.includes('worker-count-below-target')) {
|
|
216
|
+
return {
|
|
217
|
+
changeId: request.changeId,
|
|
218
|
+
goal,
|
|
219
|
+
swarmMode,
|
|
220
|
+
workerTarget,
|
|
221
|
+
waves: [],
|
|
222
|
+
tasks: [],
|
|
223
|
+
conflictGroups: [],
|
|
224
|
+
artifactRoot,
|
|
225
|
+
outputs: {
|
|
226
|
+
taskGraph: buildArtifactRelativePath(request.changeId, 'swarm', 'task-graph.json'),
|
|
227
|
+
waveManifests: [],
|
|
228
|
+
workerBriefs: [],
|
|
229
|
+
reducerReport: buildArtifactRelativePath(request.changeId, 'swarm', 'reducer-report.md'),
|
|
230
|
+
},
|
|
231
|
+
gateStatus: {
|
|
232
|
+
techApprovalRequired: requiresTechApproval,
|
|
233
|
+
techStatus: techStatus.status,
|
|
234
|
+
...(techGateSkipped ? { skipReason: 'tech-gate-skipped-clear-implementation-path' } : {}),
|
|
235
|
+
},
|
|
236
|
+
blockedReasons,
|
|
237
|
+
nextActions: ['Lower max-workers to match the current change scope or accept the capped target.'],
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
const taskIds = buildTaskIds(workerTarget);
|
|
241
|
+
const concreteTargetAreas = getConcreteTargetAreas(request, artifactWorkspacePath, techStatus.status === 'approved');
|
|
242
|
+
const discoveryTaskIds = taskIds.slice(0, 8);
|
|
243
|
+
const planningTaskIds = taskIds.slice(8, 16);
|
|
244
|
+
const implementationTaskIds = taskIds.slice(16, taskIds.length - 8);
|
|
245
|
+
const unitTestTaskIds = taskIds.slice(taskIds.length - 8, taskIds.length - 5);
|
|
246
|
+
const qualityTaskIds = taskIds.slice(taskIds.length - 5, taskIds.length - 1);
|
|
247
|
+
const reducerTaskIds = taskIds.slice(taskIds.length - 1);
|
|
248
|
+
const waves = [
|
|
249
|
+
{ name: 'discovery', taskIds: [...discoveryTaskIds] },
|
|
250
|
+
{ name: 'planning', taskIds: [...planningTaskIds] },
|
|
251
|
+
{ name: 'implementation candidates', taskIds: [...implementationTaskIds] },
|
|
252
|
+
{ name: 'unit-test execution', taskIds: [...unitTestTaskIds] },
|
|
253
|
+
{ name: 'quality gates', taskIds: [...qualityTaskIds] },
|
|
254
|
+
{ name: 'reducer', taskIds: [...reducerTaskIds] },
|
|
255
|
+
];
|
|
256
|
+
const waveDependencies = {
|
|
257
|
+
discovery: [],
|
|
258
|
+
planning: discoveryTaskIds,
|
|
259
|
+
'implementation candidates': planningTaskIds,
|
|
260
|
+
'unit-test execution': implementationTaskIds,
|
|
261
|
+
'quality gates': unitTestTaskIds,
|
|
262
|
+
reducer: qualityTaskIds,
|
|
263
|
+
};
|
|
264
|
+
const tasks = taskIds.map((taskId, index) => {
|
|
265
|
+
const wave = index < 8 ? 'discovery' : index < 16 ? 'planning' : index < taskIds.length - 8 ? 'implementation candidates' : index < taskIds.length - 5 ? 'unit-test execution' : index < taskIds.length - 1 ? 'quality gates' : 'reducer';
|
|
266
|
+
const briefPath = buildArtifactRelativePath(request.changeId, 'swarm', 'workers', taskId, 'brief.md');
|
|
267
|
+
const implementationIndex = index - 16;
|
|
268
|
+
const targetArea = wave === 'implementation candidates' && hasConcreteTargetAreas(concreteTargetAreas)
|
|
269
|
+
? selectConcreteTargetArea(concreteTargetAreas, implementationIndex)
|
|
270
|
+
: `area-${wave}`;
|
|
271
|
+
const modelRole = getTaskModelRole(wave);
|
|
272
|
+
return {
|
|
273
|
+
taskId,
|
|
274
|
+
wave,
|
|
275
|
+
workerKind: taskId,
|
|
276
|
+
purpose: `${taskId.replace(/^rd-/, '').replace(/-/g, ' ')} for ${goal}`,
|
|
277
|
+
modelRole,
|
|
278
|
+
modelId: getTaskModelId(modelRole, executionModelId),
|
|
279
|
+
inputs: [goal, artifactRoot],
|
|
280
|
+
outputs: [briefPath],
|
|
281
|
+
dependsOn: [...waveDependencies[wave]],
|
|
282
|
+
conflictGroup: `group-${wave.replace(/\s+/g, '-')}`,
|
|
283
|
+
targetArea,
|
|
284
|
+
expectedEvidence: wave === 'reducer'
|
|
285
|
+
? 'reducer-report.md'
|
|
286
|
+
: wave === 'implementation candidates'
|
|
287
|
+
? `${taskId}-patch-summary.md`
|
|
288
|
+
: wave === 'unit-test execution'
|
|
289
|
+
? `${taskId}-test-command-result.md`
|
|
290
|
+
: wave === 'quality gates'
|
|
291
|
+
? `${taskId}-qa-review.md`
|
|
292
|
+
: `${taskId}.md`,
|
|
293
|
+
};
|
|
294
|
+
});
|
|
295
|
+
const conflictGroups = waves.map((wave) => ({
|
|
296
|
+
groupId: `group-${wave.name.replace(/\s+/g, '-')}`,
|
|
297
|
+
ownedPaths: wave.taskIds.map((taskId) => buildArtifactRelativePath(request.changeId, 'swarm', 'workers', taskId, 'brief.md')),
|
|
298
|
+
parallelismPolicy: wave.taskIds.length > 1 ? 'parallel' : 'sequential',
|
|
299
|
+
reason: `${wave.name} work is isolated by worker output path`,
|
|
300
|
+
}));
|
|
301
|
+
return {
|
|
302
|
+
changeId: request.changeId,
|
|
303
|
+
goal,
|
|
304
|
+
swarmMode,
|
|
305
|
+
workerTarget,
|
|
306
|
+
waves,
|
|
307
|
+
tasks,
|
|
308
|
+
conflictGroups,
|
|
309
|
+
artifactRoot,
|
|
310
|
+
outputs: {
|
|
311
|
+
taskGraph: buildArtifactRelativePath(request.changeId, 'swarm', 'task-graph.json'),
|
|
312
|
+
waveManifests: waves.map((wave, index) => buildArtifactRelativePath(request.changeId, 'swarm', 'waves', `wave-${index + 1}-${wave.name}.json`)),
|
|
313
|
+
workerBriefs: tasks.map((task) => task.outputs[0]),
|
|
314
|
+
reducerReport: buildArtifactRelativePath(request.changeId, 'swarm', 'reducer-report.md'),
|
|
315
|
+
},
|
|
316
|
+
gateStatus: {
|
|
317
|
+
techApprovalRequired: requiresTechApproval,
|
|
318
|
+
techStatus: techStatus.status,
|
|
319
|
+
...(techGateSkipped ? { skipReason: 'tech-gate-skipped-clear-implementation-path' } : {}),
|
|
320
|
+
},
|
|
321
|
+
blockedReasons,
|
|
322
|
+
nextActions: blockedReasons.length > 0 ? ['Lower max-workers to match the current change scope or accept the capped target.'] : [],
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
export function createRdSwarmPlan(request) {
|
|
326
|
+
if (request.skill !== 'rd') {
|
|
327
|
+
throw new Error('Unsupported skill');
|
|
328
|
+
}
|
|
329
|
+
const result = buildPlan(request);
|
|
330
|
+
const artifactWorkspacePath = resolveArtifactWorkspacePath(request);
|
|
331
|
+
if (!hasPlannerArtifactWorkspace(request, artifactWorkspacePath)) {
|
|
332
|
+
return {
|
|
333
|
+
available: false,
|
|
334
|
+
behavior: 'preview',
|
|
335
|
+
reason: 'artifact-workspace-unavailable',
|
|
336
|
+
swarmMode: result.swarmMode,
|
|
337
|
+
workerTarget: result.workerTarget,
|
|
338
|
+
waves: result.waves,
|
|
339
|
+
tasks: result.tasks,
|
|
340
|
+
conflictGroups: result.conflictGroups,
|
|
341
|
+
artifactRoot: result.artifactRoot,
|
|
342
|
+
outputs: result.outputs,
|
|
343
|
+
gateStatus: result.gateStatus,
|
|
344
|
+
blockedReasons: result.blockedReasons,
|
|
345
|
+
nextActions: [...WORKSPACE_UNAVAILABLE_NEXT_ACTIONS],
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
const blockedReason = result.blockedReasons[0];
|
|
349
|
+
if (blockedReason) {
|
|
350
|
+
return {
|
|
351
|
+
available: false,
|
|
352
|
+
behavior: 'blocked',
|
|
353
|
+
reason: blockedReason,
|
|
354
|
+
swarmMode: result.swarmMode,
|
|
355
|
+
workerTarget: result.workerTarget,
|
|
356
|
+
waves: result.waves,
|
|
357
|
+
tasks: result.tasks,
|
|
358
|
+
conflictGroups: result.conflictGroups,
|
|
359
|
+
artifactRoot: result.artifactRoot,
|
|
360
|
+
outputs: result.outputs,
|
|
361
|
+
gateStatus: result.gateStatus,
|
|
362
|
+
blockedReasons: result.blockedReasons,
|
|
363
|
+
nextActions: result.nextActions,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
return {
|
|
367
|
+
available: true,
|
|
368
|
+
...result,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CapabilityAvailability, CapabilityItem } from './recommendation-types.js';
|
|
2
|
+
export type CapabilityAvailabilityOptions = {
|
|
3
|
+
installedCapabilityIds?: string[];
|
|
4
|
+
};
|
|
5
|
+
export declare function resolveCapabilityAvailability(items: CapabilityItem[], options?: CapabilityAvailabilityOptions): CapabilityAvailability[];
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
function getAvailabilityType(item) {
|
|
2
|
+
if (item.itemType === 'mcp') {
|
|
3
|
+
return 'mcp';
|
|
4
|
+
}
|
|
5
|
+
if (item.itemType === 'agent') {
|
|
6
|
+
return 'agent';
|
|
7
|
+
}
|
|
8
|
+
if (item.itemType === 'cli') {
|
|
9
|
+
return 'cli';
|
|
10
|
+
}
|
|
11
|
+
return 'skill';
|
|
12
|
+
}
|
|
13
|
+
function getMissingStatus(item) {
|
|
14
|
+
if (item.itemType === 'mcp' || item.itemType === 'skill' || item.itemType === 'agent') {
|
|
15
|
+
return 'installable';
|
|
16
|
+
}
|
|
17
|
+
return 'unknown';
|
|
18
|
+
}
|
|
19
|
+
export function resolveCapabilityAvailability(items, options = {}) {
|
|
20
|
+
const installedCapabilityIds = new Set(options.installedCapabilityIds ?? []);
|
|
21
|
+
return items.map((item) => {
|
|
22
|
+
const isInstalled = installedCapabilityIds.has(item.capabilityId);
|
|
23
|
+
const status = isInstalled ? 'available' : getMissingStatus(item);
|
|
24
|
+
const installPlan = isInstalled
|
|
25
|
+
? undefined
|
|
26
|
+
: {
|
|
27
|
+
available: status === 'installable',
|
|
28
|
+
requiresApproval: true
|
|
29
|
+
};
|
|
30
|
+
return {
|
|
31
|
+
capabilityId: item.capabilityId,
|
|
32
|
+
type: getAvailabilityType(item),
|
|
33
|
+
status,
|
|
34
|
+
requiredFor: item.workflows,
|
|
35
|
+
...(installPlan ? { installPlan } : {}),
|
|
36
|
+
fallback: item.fallback,
|
|
37
|
+
risk: item.riskLevel
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CapabilityMapPlan, CapabilityMapSourceFilter } from './recommendation-types.js';
|
|
2
|
+
export type CapabilityMapOptions = {
|
|
3
|
+
source?: CapabilityMapSourceFilter;
|
|
4
|
+
installedCapabilityIds?: string[];
|
|
5
|
+
httpProxy?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function createCapabilityMapPlan(options?: CapabilityMapOptions): CapabilityMapPlan;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { seedCapabilityItems, seedCapabilityLandingMappings, seedCapabilitySources } from './seed-capability-catalog.js';
|
|
2
|
+
export function createCapabilityMapPlan(options = {}) {
|
|
3
|
+
const sourceFilter = options.source ?? 'all';
|
|
4
|
+
const httpProxy = normalizeProxyUrl(options.httpProxy);
|
|
5
|
+
const sources = sortSources(filterSources(sourceFilter));
|
|
6
|
+
const sourceIds = new Set(sources.map((source) => source.sourceId));
|
|
7
|
+
const items = sortItems(seedCapabilityItems.filter((item) => sourceIds.has(item.sourceId)));
|
|
8
|
+
const mappings = sortMappings(seedCapabilityLandingMappings.filter((mapping) => sourceIds.has(mapping.sourceId)));
|
|
9
|
+
const constraints = [
|
|
10
|
+
'dry-run only: do not install MCP servers, skills, hooks, agents, or browser tooling from this map',
|
|
11
|
+
'do not clone external repositories or write Claude settings from this map',
|
|
12
|
+
'do not write Peaks config or credentials from this map',
|
|
13
|
+
'do not send secrets, private code, or business data to external resources without explicit approval'
|
|
14
|
+
];
|
|
15
|
+
if (httpProxy) {
|
|
16
|
+
constraints.push(`use HTTP proxy ${httpProxy} for GitHub, registries, MCP directories, and external web access`);
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
dryRunOnly: true,
|
|
20
|
+
executionPolicy: {
|
|
21
|
+
allowInstall: false,
|
|
22
|
+
allowClone: false,
|
|
23
|
+
allowConfigWrite: false,
|
|
24
|
+
allowSecretExfiltration: false
|
|
25
|
+
},
|
|
26
|
+
...(httpProxy
|
|
27
|
+
? {
|
|
28
|
+
proxyPolicy: {
|
|
29
|
+
requiredForExternalAccess: true,
|
|
30
|
+
httpProxy
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
: {}),
|
|
34
|
+
sources: cloneSources(sources),
|
|
35
|
+
items: cloneItems(items),
|
|
36
|
+
mappings: cloneMappings(mappings),
|
|
37
|
+
availability: resolveDryRunAvailability(items, options.installedCapabilityIds ?? []),
|
|
38
|
+
constraints,
|
|
39
|
+
warnings: [
|
|
40
|
+
'Browser automation capabilities require explicit app target approval and must not submit forms, purchase, delete, or mutate authenticated state without confirmation.',
|
|
41
|
+
'Database capabilities require explicit credentials and confirmation before any write query.',
|
|
42
|
+
'Figma and design-source capabilities require user-authorized project access and must not persist tokens in project artifacts.',
|
|
43
|
+
'Cloud capabilities can affect cost or infrastructure and require explicit confirmation and credential boundaries.',
|
|
44
|
+
'External skill packs stay catalog-only until inspected for license, safety, and project fit.'
|
|
45
|
+
]
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function normalizeProxyUrl(value) {
|
|
49
|
+
if (value === undefined)
|
|
50
|
+
return undefined;
|
|
51
|
+
try {
|
|
52
|
+
const url = new URL(value);
|
|
53
|
+
return (url.protocol === 'http:' || url.protocol === 'https:') && url.username.length === 0 && url.password.length === 0 && url.pathname === '/' && url.search.length === 0 && url.hash.length === 0 ? value : undefined;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function filterSources(sourceFilter) {
|
|
60
|
+
if (sourceFilter === 'all') {
|
|
61
|
+
return seedCapabilitySources;
|
|
62
|
+
}
|
|
63
|
+
return seedCapabilitySources.filter((source) => source.sourceGroup === sourceFilter);
|
|
64
|
+
}
|
|
65
|
+
function sortSources(sources) {
|
|
66
|
+
return [...sources].sort((left, right) => left.sourceId.localeCompare(right.sourceId));
|
|
67
|
+
}
|
|
68
|
+
function sortItems(items) {
|
|
69
|
+
return [...items].sort((left, right) => left.capabilityId.localeCompare(right.capabilityId));
|
|
70
|
+
}
|
|
71
|
+
function sortMappings(mappings) {
|
|
72
|
+
return [...mappings].sort((left, right) => `${left.sourceId}:${left.capabilityId}`.localeCompare(`${right.sourceId}:${right.capabilityId}`));
|
|
73
|
+
}
|
|
74
|
+
function resolveDryRunAvailability(items, installedCapabilityIds) {
|
|
75
|
+
const installedIds = new Set(installedCapabilityIds);
|
|
76
|
+
return items.map((item) => ({
|
|
77
|
+
capabilityId: item.capabilityId,
|
|
78
|
+
type: getAvailabilityType(item),
|
|
79
|
+
status: installedIds.has(item.capabilityId) ? 'available' : 'unknown',
|
|
80
|
+
requiredFor: [...item.workflows],
|
|
81
|
+
fallback: cloneFallback(item.fallback),
|
|
82
|
+
risk: item.riskLevel
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
function getAvailabilityType(item) {
|
|
86
|
+
if (item.itemType === 'mcp') {
|
|
87
|
+
return 'mcp';
|
|
88
|
+
}
|
|
89
|
+
if (item.itemType === 'agent') {
|
|
90
|
+
return 'agent';
|
|
91
|
+
}
|
|
92
|
+
if (item.itemType === 'cli') {
|
|
93
|
+
return 'cli';
|
|
94
|
+
}
|
|
95
|
+
return 'skill';
|
|
96
|
+
}
|
|
97
|
+
function cloneSources(sources) {
|
|
98
|
+
return sources.map((source) => ({
|
|
99
|
+
...source,
|
|
100
|
+
...(source.trustSignals
|
|
101
|
+
? {
|
|
102
|
+
trustSignals: {
|
|
103
|
+
...source.trustSignals,
|
|
104
|
+
...(source.trustSignals.notes ? { notes: [...source.trustSignals.notes] } : {})
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
: {}),
|
|
108
|
+
items: [...source.items]
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
111
|
+
function cloneItems(items) {
|
|
112
|
+
return items.map((item) => ({
|
|
113
|
+
...item,
|
|
114
|
+
workflows: [...item.workflows],
|
|
115
|
+
audience: [...item.audience],
|
|
116
|
+
fallback: cloneFallback(item.fallback),
|
|
117
|
+
presentation: {
|
|
118
|
+
displayName: cloneLocalizedText(item.presentation.displayName),
|
|
119
|
+
description: cloneLocalizedText(item.presentation.description)
|
|
120
|
+
}
|
|
121
|
+
}));
|
|
122
|
+
}
|
|
123
|
+
function cloneMappings(mappings) {
|
|
124
|
+
return mappings.map((mapping) => ({ ...mapping }));
|
|
125
|
+
}
|
|
126
|
+
function cloneFallback(fallback) {
|
|
127
|
+
return { ...fallback };
|
|
128
|
+
}
|
|
129
|
+
function cloneLocalizedText(text) {
|
|
130
|
+
return { ...text };
|
|
131
|
+
}
|