opencode-agent-skill 7.7.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/CHANGELOG.md +163 -0
- package/LICENSE +9 -0
- package/README.md +581 -0
- package/bin/ocskill.mjs +975 -0
- package/docs/DETERMINISTIC-TOOLS.md +88 -0
- package/docs/ENGINEERING-DESIGN.md +176 -0
- package/docs/EVALS.md +136 -0
- package/docs/NPM-PUBLISH.md +102 -0
- package/docs/OPENCODE-COMPAT.md +109 -0
- package/docs/RESEARCH-SOURCES.md +37 -0
- package/docs/TRACE-SCHEMA.md +109 -0
- package/docs/V7-INTELLIGENCE-RUNTIME.md +166 -0
- package/evals/live/fixtures/engineering-bench/package.json +1 -0
- package/evals/live/fixtures/engineering-bench/src/api-errors.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/authz.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/cache-tags.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/config.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/contract-consumer.mjs +6 -0
- package/evals/live/fixtures/engineering-bench/src/contract-producer.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/dedupe.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/dependency.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/discount.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/inventory.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/migration.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/money.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/pagination.mjs +5 -0
- package/evals/live/fixtures/engineering-bench/src/path-safe.mjs +5 -0
- package/evals/live/fixtures/engineering-bench/src/payment.mjs +5 -0
- package/evals/live/fixtures/engineering-bench/src/query-sort.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/react-state.mjs +8 -0
- package/evals/live/fixtures/engineering-bench/src/retry.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/rn-platform.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/upload.mjs +3 -0
- package/evals/live/fixtures/engineering-bench/src/webhook.mjs +3 -0
- package/evals/live/graders/engineering-bench.mjs +239 -0
- package/evals/live/tasks.json +126 -0
- package/evals/long/fixtures/long-horizon/package.json +1 -0
- package/evals/long/fixtures/long-horizon/src/auth.mjs +5 -0
- package/evals/long/fixtures/long-horizon/src/checkout.mjs +9 -0
- package/evals/long/fixtures/long-horizon/src/inventory.mjs +5 -0
- package/evals/long/fixtures/long-horizon/src/money.mjs +3 -0
- package/evals/long/fixtures/long-horizon/src/payment.mjs +7 -0
- package/evals/long/fixtures/long-horizon/src/product-api.mjs +10 -0
- package/evals/long/fixtures/long-horizon/src/product-cache.mjs +3 -0
- package/evals/long/fixtures/long-horizon/src/product-service.mjs +6 -0
- package/evals/long/fixtures/long-horizon/src/product-state.mjs +8 -0
- package/evals/long/fixtures/long-horizon/src/project-api.mjs +9 -0
- package/evals/long/fixtures/long-horizon/src/project-service.mjs +7 -0
- package/evals/long/fixtures/long-horizon/src/user-consumer.mjs +3 -0
- package/evals/long/fixtures/long-horizon/src/user-migration.mjs +7 -0
- package/evals/long/fixtures/long-horizon/src/user-serializer.mjs +3 -0
- package/evals/long/fixtures/long-horizon/src/user-validation.mjs +3 -0
- package/evals/long/graders/long-horizon.mjs +209 -0
- package/evals/long/tasks.json +36 -0
- package/evals/router-triggers.json +1238 -0
- package/evals/routing.json +321 -0
- package/global-config/AGENTS.md +212 -0
- package/global-config/agents/architect.md +38 -0
- package/global-config/agents/codebase-mapper.md +41 -0
- package/global-config/agents/critic.md +37 -0
- package/global-config/agents/debugger.md +36 -0
- package/global-config/agents/executor.md +48 -0
- package/global-config/agents/integration-verifier.md +43 -0
- package/global-config/agents/plan-checker.md +43 -0
- package/global-config/agents/researcher.md +30 -0
- package/global-config/agents/reviewer.md +30 -0
- package/global-config/agents/verifier.md +33 -0
- package/global-config/commands/audit.md +8 -0
- package/global-config/commands/critique.md +8 -0
- package/global-config/commands/debug.md +8 -0
- package/global-config/commands/feature.md +8 -0
- package/global-config/commands/fix.md +8 -0
- package/global-config/commands/plan.md +8 -0
- package/global-config/commands/research.md +8 -0
- package/global-config/commands/resume.md +22 -0
- package/global-config/commands/review.md +8 -0
- package/global-config/commands/run.md +29 -0
- package/global-config/commands/verify.md +8 -0
- package/global-config/plugins/ues-router/capabilities.js +20 -0
- package/global-config/plugins/ues-router/index.js +277 -0
- package/global-config/plugins/ues-router/router.js +74 -0
- package/global-config/plugins/ues-router/safety.js +16 -0
- package/global-config/skills/accessibility/SKILL.md +10 -0
- package/global-config/skills/accessibility/references/workflow.md +17 -0
- package/global-config/skills/api-contract/SKILL.md +12 -0
- package/global-config/skills/api-contract/references/workflow.md +17 -0
- package/global-config/skills/auth-security/SKILL.md +12 -0
- package/global-config/skills/auth-security/references/workflow.md +15 -0
- package/global-config/skills/bug-diagnosis/SKILL.md +28 -0
- package/global-config/skills/change-impact-analysis/SKILL.md +23 -0
- package/global-config/skills/code-review/SKILL.md +19 -0
- package/global-config/skills/context-engineering/SKILL.md +18 -0
- package/global-config/skills/context-engineering/references/large-repo.md +16 -0
- package/global-config/skills/database-engineering/SKILL.md +12 -0
- package/global-config/skills/database-engineering/references/workflow.md +15 -0
- package/global-config/skills/dependency-management/SKILL.md +12 -0
- package/global-config/skills/dependency-management/references/workflow.md +14 -0
- package/global-config/skills/devops-engineering/SKILL.md +10 -0
- package/global-config/skills/devops-engineering/references/workflow.md +11 -0
- package/global-config/skills/django-engineering/SKILL.md +10 -0
- package/global-config/skills/django-engineering/references/workflow.md +11 -0
- package/global-config/skills/documentation-engineering/SKILL.md +10 -0
- package/global-config/skills/documentation-engineering/references/workflow.md +15 -0
- package/global-config/skills/dotnet-engineering/SKILL.md +10 -0
- package/global-config/skills/dotnet-engineering/references/workflow.md +11 -0
- package/global-config/skills/ecommerce-engineering/SKILL.md +10 -0
- package/global-config/skills/ecommerce-engineering/references/workflow.md +17 -0
- package/global-config/skills/engineering-orchestrator/SKILL.md +29 -0
- package/global-config/skills/engineering-orchestrator/references/delegation.md +22 -0
- package/global-config/skills/engineering-orchestrator/references/evaluator-loop.md +18 -0
- package/global-config/skills/engineering-orchestrator/references/long-horizon.md +57 -0
- package/global-config/skills/engineering-orchestrator/references/model-escalation.md +19 -0
- package/global-config/skills/engineering-orchestrator/references/retry-policy.md +12 -0
- package/global-config/skills/engineering-orchestrator/references/routing.md +39 -0
- package/global-config/skills/engineering-orchestrator/references/verification-matrix.md +18 -0
- package/global-config/skills/fastapi-engineering/SKILL.md +10 -0
- package/global-config/skills/fastapi-engineering/references/workflow.md +13 -0
- package/global-config/skills/file-upload-engineering/SKILL.md +10 -0
- package/global-config/skills/file-upload-engineering/references/workflow.md +17 -0
- package/global-config/skills/flutter-engineering/SKILL.md +10 -0
- package/global-config/skills/flutter-engineering/references/workflow.md +13 -0
- package/global-config/skills/git-safety/SKILL.md +10 -0
- package/global-config/skills/git-safety/references/workflow.md +15 -0
- package/global-config/skills/implementation-engineer/SKILL.md +10 -0
- package/global-config/skills/implementation-engineer/references/workflow.md +14 -0
- package/global-config/skills/java-spring-engineering/SKILL.md +10 -0
- package/global-config/skills/java-spring-engineering/references/workflow.md +13 -0
- package/global-config/skills/long-task-state/SKILL.md +28 -0
- package/global-config/skills/long-task-state/references/context-ledger.md +27 -0
- package/global-config/skills/long-task-state/templates/STATE.md +48 -0
- package/global-config/skills/nestjs-engineering/SKILL.md +10 -0
- package/global-config/skills/nestjs-engineering/references/workflow.md +11 -0
- package/global-config/skills/nextjs-engineering/SKILL.md +12 -0
- package/global-config/skills/nextjs-engineering/references/workflow.md +15 -0
- package/global-config/skills/nodejs-engineering/SKILL.md +12 -0
- package/global-config/skills/nodejs-engineering/references/workflow.md +11 -0
- package/global-config/skills/payment-engineering/SKILL.md +12 -0
- package/global-config/skills/payment-engineering/references/workflow.md +19 -0
- package/global-config/skills/performance-engineering/SKILL.md +10 -0
- package/global-config/skills/performance-engineering/references/workflow.md +17 -0
- package/global-config/skills/python-engineering/SKILL.md +10 -0
- package/global-config/skills/python-engineering/references/workflow.md +11 -0
- package/global-config/skills/react-engineering/SKILL.md +14 -0
- package/global-config/skills/react-engineering/references/workflow.md +16 -0
- package/global-config/skills/react-native-engineering/SKILL.md +14 -0
- package/global-config/skills/react-native-engineering/references/workflow.md +16 -0
- package/global-config/skills/repo-explorer/SKILL.md +17 -0
- package/global-config/skills/research-verification/SKILL.md +18 -0
- package/global-config/skills/research-verification/references/source-hierarchy.md +12 -0
- package/global-config/skills/rest-api-design/SKILL.md +10 -0
- package/global-config/skills/rest-api-design/references/workflow.md +18 -0
- package/global-config/skills/software-architect/SKILL.md +10 -0
- package/global-config/skills/software-architect/references/workflow.md +18 -0
- package/global-config/skills/task-planner/SKILL.md +21 -0
- package/global-config/skills/task-planner/references/plan-schema.md +56 -0
- package/global-config/skills/test-driven-development/SKILL.md +22 -0
- package/global-config/skills/test-driven-development/references/writing-good-tests.md +21 -0
- package/global-config/skills/test-verification/SKILL.md +20 -0
- package/global-config/skills/ui-ux-engineering/SKILL.md +10 -0
- package/global-config/skills/ui-ux-engineering/references/workflow.md +17 -0
- package/global-config/skills/web-security-review/SKILL.md +10 -0
- package/global-config/skills/web-security-review/references/workflow.md +22 -0
- package/lib/context-manifest.mjs +101 -0
- package/lib/control-center.mjs +148 -0
- package/lib/eval-auth.mjs +21 -0
- package/lib/eval-report.mjs +72 -0
- package/lib/eval-telemetry.mjs +155 -0
- package/lib/evidence-receipt.mjs +50 -0
- package/lib/hermes-bridge.mjs +28 -0
- package/lib/ids.mjs +21 -0
- package/lib/installer.mjs +636 -0
- package/lib/learning-engine.mjs +161 -0
- package/lib/model-config.mjs +88 -0
- package/lib/model-policy.mjs +71 -0
- package/lib/opencode-compat.mjs +86 -0
- package/lib/orchestrator-policy.mjs +35 -0
- package/lib/process-runner.mjs +117 -0
- package/lib/repo-graph.mjs +135 -0
- package/lib/repo-inspect.mjs +264 -0
- package/lib/review-scope.mjs +98 -0
- package/lib/router-config.mjs +40 -0
- package/lib/task-engine.mjs +777 -0
- package/lib/task-graph.mjs +262 -0
- package/lib/update-resolver.mjs +43 -0
- package/lib/verification-plan.mjs +52 -0
- package/lib/version.mjs +47 -0
- package/lib/workspace-snapshot.mjs +45 -0
- package/lib/worktree-sandbox.mjs +59 -0
- package/package.json +69 -0
- package/scripts/check-working-tree.mjs +2 -0
- package/scripts/collect-evidence.mjs +2 -0
- package/scripts/control-center.mjs +37 -0
- package/scripts/detect-stack.mjs +2 -0
- package/scripts/detect-test-commands.mjs +2 -0
- package/scripts/eval-live.mjs +437 -0
- package/scripts/eval-report.mjs +49 -0
- package/scripts/eval-router.mjs +45 -0
- package/scripts/eval-skills.mjs +71 -0
- package/scripts/impact-map.mjs +4 -0
- package/scripts/install.mjs +61 -0
- package/scripts/repo-map.mjs +2 -0
- package/scripts/smoke-packed-install.mjs +326 -0
- package/scripts/syntax-check.mjs +35 -0
- package/scripts/uninstall.mjs +21 -0
- package/scripts/validate-live-suite.mjs +65 -0
- package/scripts/validate.mjs +133 -0
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
import { cp, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises"
|
|
2
|
+
import { existsSync } from "node:fs"
|
|
3
|
+
import os from "node:os"
|
|
4
|
+
import path from "node:path"
|
|
5
|
+
import { fileURLToPath } from "node:url"
|
|
6
|
+
import { SKILL_PREFIX, validManagedMarkdown, validResourceID, validSkillID } from "./ids.mjs"
|
|
7
|
+
import { adaptAgentForOpenCode, detectOpenCodeMajor } from "./opencode-compat.mjs"
|
|
8
|
+
import { applyConfiguredModel, readModelPolicy } from "./model-config.mjs"
|
|
9
|
+
|
|
10
|
+
export { SKILL_PREFIX } from "./ids.mjs"
|
|
11
|
+
|
|
12
|
+
export const PACKAGE_NAME = "opencode-agent-skill"
|
|
13
|
+
export const LEGACY_PACKAGE_NAME = "@laivannha0202/opencode-agent-skill"
|
|
14
|
+
export const MANAGED_MARKER = "<!-- managed-by: @laivannha0202/opencode-agent-skill -->"
|
|
15
|
+
export const AGENTS_BEGIN = "<!-- BEGIN OCSKILL UNIVERSAL ENGINEERING SYSTEM -->"
|
|
16
|
+
export const AGENTS_END = "<!-- END OCSKILL UNIVERSAL ENGINEERING SYSTEM -->"
|
|
17
|
+
|
|
18
|
+
const PACKAGE_ROOT = fileURLToPath(new URL("../", import.meta.url))
|
|
19
|
+
const ROUTER_PLUGIN_STATE = "ues-router/index.js"
|
|
20
|
+
|
|
21
|
+
function validManagedPlugin(value) {
|
|
22
|
+
return value === ROUTER_PLUGIN_STATE
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function isOwnedPackageName(value) {
|
|
26
|
+
return !value || value === PACKAGE_NAME || value === LEGACY_PACKAGE_NAME
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function getConfigDir() {
|
|
30
|
+
return process.env.OPENCODE_CONFIG_DIR
|
|
31
|
+
? path.resolve(process.env.OPENCODE_CONFIG_DIR)
|
|
32
|
+
: path.join(os.homedir(), ".config", "opencode")
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function readText(file, fallback = "") {
|
|
36
|
+
try {
|
|
37
|
+
return await readFile(file, "utf8")
|
|
38
|
+
} catch (error) {
|
|
39
|
+
if (error && error.code === "ENOENT") return fallback
|
|
40
|
+
throw error
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function packageVersion() {
|
|
45
|
+
const json = JSON.parse(await readFile(path.join(PACKAGE_ROOT, "package.json"), "utf8"))
|
|
46
|
+
return json.version
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function installSkillName(source, name) {
|
|
50
|
+
const next = source.replace(/^name:\s*[^\r\n]+/m, `name: ${SKILL_PREFIX}${name}`)
|
|
51
|
+
return next.includes(MANAGED_MARKER)
|
|
52
|
+
? next
|
|
53
|
+
: `${next.trimEnd()}\n\n${MANAGED_MARKER}\n`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function removeManagedBlock(source) {
|
|
57
|
+
const start = source.indexOf(AGENTS_BEGIN)
|
|
58
|
+
if (start < 0) return source
|
|
59
|
+
|
|
60
|
+
const end = source.indexOf(AGENTS_END, start)
|
|
61
|
+
if (end < 0) return source
|
|
62
|
+
|
|
63
|
+
const after = end + AGENTS_END.length
|
|
64
|
+
return (source.slice(0, start) + source.slice(after))
|
|
65
|
+
.replace(/\n{3,}/g, "\n\n")
|
|
66
|
+
.trim()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function writeManagedFile(file, content, warnings) {
|
|
70
|
+
if (existsSync(file)) {
|
|
71
|
+
const current = await readText(file)
|
|
72
|
+
if (!current.includes(MANAGED_MARKER)) {
|
|
73
|
+
warnings.push(`Skipped existing unmanaged file: ${file}`)
|
|
74
|
+
return false
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
await mkdir(path.dirname(file), { recursive: true })
|
|
79
|
+
await writeFile(file, content, "utf8")
|
|
80
|
+
return true
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function readManagedState(file, warnings = []) {
|
|
84
|
+
const raw = await readText(file)
|
|
85
|
+
if (!raw) return { kind: "empty", state: { skills: [], commands: [], agents: [], plugins: [] } }
|
|
86
|
+
|
|
87
|
+
try {
|
|
88
|
+
const parsed = JSON.parse(raw)
|
|
89
|
+
if (parsed.package && !isOwnedPackageName(parsed.package)) {
|
|
90
|
+
warnings.push(`Ignored state owned by another package: ${parsed.package}`)
|
|
91
|
+
return {
|
|
92
|
+
kind: "foreign",
|
|
93
|
+
package: parsed.package,
|
|
94
|
+
state: { skills: [], commands: [], agents: [], plugins: [] },
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
kind: "managed",
|
|
99
|
+
state: {
|
|
100
|
+
...parsed,
|
|
101
|
+
skills: Array.isArray(parsed.skills) ? parsed.skills.filter(validSkillID) : [],
|
|
102
|
+
commands: Array.isArray(parsed.commands) ? parsed.commands.filter(validManagedMarkdown) : [],
|
|
103
|
+
agents: Array.isArray(parsed.agents) ? parsed.agents.filter(validManagedMarkdown) : [],
|
|
104
|
+
plugins: Array.isArray(parsed.plugins) ? parsed.plugins.filter(validManagedPlugin) : [],
|
|
105
|
+
},
|
|
106
|
+
}
|
|
107
|
+
} catch {
|
|
108
|
+
warnings.push(`Invalid UES state file; run 'ocskill install' to rebuild it: ${file}`)
|
|
109
|
+
return { kind: "invalid", state: { skills: [], commands: [], agents: [], plugins: [] } }
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function cleanupStaleManaged(configDir, previousState, installed, warnings) {
|
|
114
|
+
const currentSkills = new Set(installed.skills)
|
|
115
|
+
const currentCommands = new Set(installed.commands)
|
|
116
|
+
const currentAgents = new Set(installed.agents)
|
|
117
|
+
const currentPlugins = new Set(installed.plugins || [])
|
|
118
|
+
|
|
119
|
+
for (const id of previousState.skills || []) {
|
|
120
|
+
if (currentSkills.has(id) || !validSkillID(id)) continue
|
|
121
|
+
const dir = path.join(configDir, "skills", id)
|
|
122
|
+
const markerFile = path.join(dir, "SKILL.md")
|
|
123
|
+
const current = await readText(markerFile)
|
|
124
|
+
if (current.includes(MANAGED_MARKER)) {
|
|
125
|
+
await rm(dir, { recursive: true, force: true })
|
|
126
|
+
} else if (existsSync(dir)) {
|
|
127
|
+
warnings.push(`Preserved stale skill without UES marker: ${dir}`)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
for (const name of previousState.commands || []) {
|
|
132
|
+
if (currentCommands.has(name) || !validManagedMarkdown(name)) continue
|
|
133
|
+
const file = path.join(configDir, "commands", name)
|
|
134
|
+
const current = await readText(file)
|
|
135
|
+
if (current.includes(MANAGED_MARKER)) {
|
|
136
|
+
await rm(file, { force: true })
|
|
137
|
+
} else if (existsSync(file)) {
|
|
138
|
+
warnings.push(`Preserved stale command without UES marker: ${file}`)
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
for (const name of previousState.agents || []) {
|
|
143
|
+
if (currentAgents.has(name) || !validManagedMarkdown(name)) continue
|
|
144
|
+
const file = path.join(configDir, "agents", name)
|
|
145
|
+
const current = await readText(file)
|
|
146
|
+
if (current.includes(MANAGED_MARKER)) {
|
|
147
|
+
await rm(file, { force: true })
|
|
148
|
+
} else if (existsSync(file)) {
|
|
149
|
+
warnings.push(`Preserved stale subagent without UES marker: ${file}`)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
for (const name of previousState.plugins || []) {
|
|
154
|
+
if (currentPlugins.has(name) || !validManagedPlugin(name)) continue
|
|
155
|
+
const file = path.join(configDir, "plugins", name)
|
|
156
|
+
const current = await readText(file)
|
|
157
|
+
if (current.includes(MANAGED_MARKER)) {
|
|
158
|
+
await rm(path.dirname(file), { recursive: true, force: true })
|
|
159
|
+
} else if (existsSync(file)) {
|
|
160
|
+
warnings.push(`Preserved stale plugin without UES marker: ${file}`)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function scanUntrackedManaged(configDir) {
|
|
166
|
+
const found = { skills: [], commands: [], agents: [], plugins: [] }
|
|
167
|
+
|
|
168
|
+
const skillsDir = path.join(configDir, "skills")
|
|
169
|
+
for (const entry of await readdir(skillsDir, { withFileTypes: true }).catch(() => [])) {
|
|
170
|
+
if (!entry.isDirectory() || !validSkillID(entry.name)) continue
|
|
171
|
+
const current = await readText(path.join(skillsDir, entry.name, "SKILL.md"))
|
|
172
|
+
if (current.includes(MANAGED_MARKER)) found.skills.push(entry.name)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
for (const [targetDir, key] of [
|
|
176
|
+
[path.join(configDir, "commands"), "commands"],
|
|
177
|
+
[path.join(configDir, "agents"), "agents"],
|
|
178
|
+
]) {
|
|
179
|
+
for (const entry of await readdir(targetDir, { withFileTypes: true }).catch(() => [])) {
|
|
180
|
+
if (!entry.isFile() || !validManagedMarkdown(entry.name)) continue
|
|
181
|
+
const current = await readText(path.join(targetDir, entry.name))
|
|
182
|
+
if (current.includes(MANAGED_MARKER)) found[key].push(entry.name)
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const routerFile = path.join(configDir, "plugins", ROUTER_PLUGIN_STATE)
|
|
187
|
+
const routerCurrent = await readText(routerFile)
|
|
188
|
+
if (routerCurrent.includes(MANAGED_MARKER)) found.plugins.push(ROUTER_PLUGIN_STATE)
|
|
189
|
+
|
|
190
|
+
return found
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function warnUntrackedManaged(configDir, installed, warnings) {
|
|
194
|
+
const currentSkills = new Set(installed.skills)
|
|
195
|
+
const currentCommands = new Set(installed.commands)
|
|
196
|
+
const currentAgents = new Set(installed.agents)
|
|
197
|
+
const currentPlugins = new Set(installed.plugins || [])
|
|
198
|
+
const untracked = await scanUntrackedManaged(configDir)
|
|
199
|
+
|
|
200
|
+
for (const id of untracked.skills) {
|
|
201
|
+
if (!currentSkills.has(id)) {
|
|
202
|
+
warnings.push(`Untracked managed skill left over from an earlier install: ${id}`)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
for (const name of untracked.commands) {
|
|
206
|
+
if (!currentCommands.has(name)) {
|
|
207
|
+
warnings.push(`Untracked managed command left over from an earlier install: ${name}`)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
for (const name of untracked.agents) {
|
|
211
|
+
if (!currentAgents.has(name)) {
|
|
212
|
+
warnings.push(`Untracked managed subagent left over from an earlier install: ${name}`)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
for (const name of untracked.plugins || []) {
|
|
216
|
+
if (!currentPlugins.has(name)) {
|
|
217
|
+
warnings.push(`Untracked managed plugin left over from an earlier install: ${name}`)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async function removeManagedAgentsBlock(configDir) {
|
|
223
|
+
const globalAgentsFile = path.join(configDir, "AGENTS.md")
|
|
224
|
+
const existingAgents = await readText(globalAgentsFile)
|
|
225
|
+
if (existingAgents.includes(AGENTS_BEGIN)) {
|
|
226
|
+
const clean = removeManagedBlock(existingAgents)
|
|
227
|
+
if (clean) await writeFile(globalAgentsFile, clean + "\n", "utf8")
|
|
228
|
+
else await rm(globalAgentsFile, { force: true })
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
async function installSkillDirectory(sourceDir, targetDir, sourceName, warnings) {
|
|
233
|
+
const targetSkill = path.join(targetDir, "SKILL.md")
|
|
234
|
+
|
|
235
|
+
if (existsSync(targetDir)) {
|
|
236
|
+
if (!existsSync(targetSkill)) {
|
|
237
|
+
warnings.push(`Skipped existing unmanaged skill directory: ${targetDir}`)
|
|
238
|
+
return false
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const current = await readText(targetSkill)
|
|
242
|
+
if (!current.includes(MANAGED_MARKER)) {
|
|
243
|
+
warnings.push(`Skipped existing unmanaged skill: ${targetSkill}`)
|
|
244
|
+
return false
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
await rm(targetDir, { recursive: true, force: true })
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
await mkdir(path.dirname(targetDir), { recursive: true })
|
|
251
|
+
await cp(sourceDir, targetDir, { recursive: true })
|
|
252
|
+
|
|
253
|
+
const copiedSkill = await readFile(targetSkill, "utf8")
|
|
254
|
+
await writeFile(targetSkill, installSkillName(copiedSkill, sourceName), "utf8")
|
|
255
|
+
return true
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async function installPluginDirectory(sourceDir, targetDir, warnings) {
|
|
259
|
+
const targetEntry = path.join(targetDir, "index.js")
|
|
260
|
+
|
|
261
|
+
if (existsSync(targetDir)) {
|
|
262
|
+
if (!existsSync(targetEntry)) {
|
|
263
|
+
warnings.push(`Skipped existing unmanaged plugin directory: ${targetDir}`)
|
|
264
|
+
return false
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const current = await readText(targetEntry)
|
|
268
|
+
if (!current.includes(MANAGED_MARKER)) {
|
|
269
|
+
warnings.push(`Skipped existing unmanaged plugin: ${targetEntry}`)
|
|
270
|
+
return false
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
await rm(targetDir, { recursive: true, force: true })
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
await mkdir(path.dirname(targetDir), { recursive: true })
|
|
277
|
+
await cp(sourceDir, targetDir, { recursive: true })
|
|
278
|
+
|
|
279
|
+
const copiedEntry = await readFile(targetEntry, "utf8")
|
|
280
|
+
await writeFile(
|
|
281
|
+
targetEntry,
|
|
282
|
+
copiedEntry.includes(MANAGED_MARKER)
|
|
283
|
+
? copiedEntry
|
|
284
|
+
: `${copiedEntry.trimEnd()}\n\n// ${MANAGED_MARKER}\n`,
|
|
285
|
+
"utf8",
|
|
286
|
+
)
|
|
287
|
+
return true
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export async function installResources(options = {}) {
|
|
291
|
+
const sourceRoot = options.sourceRoot ?? PACKAGE_ROOT
|
|
292
|
+
const configDir = getConfigDir()
|
|
293
|
+
const skillsTarget = path.join(configDir, "skills")
|
|
294
|
+
const commandsTarget = path.join(configDir, "commands")
|
|
295
|
+
const agentsTarget = path.join(configDir, "agents")
|
|
296
|
+
const pluginsTarget = path.join(configDir, "plugins")
|
|
297
|
+
const stateDir = path.join(configDir, ".ues")
|
|
298
|
+
const stateFile = path.join(stateDir, "state.json")
|
|
299
|
+
const warnings = []
|
|
300
|
+
const openCodeMajor = options.openCodeMajor ?? detectOpenCodeMajor()
|
|
301
|
+
const installed = { skills: [], commands: [], agents: [], plugins: [] }
|
|
302
|
+
const previous = await readManagedState(stateFile, warnings)
|
|
303
|
+
const previousState = previous.state
|
|
304
|
+
const modelPolicy = await readModelPolicy(configDir)
|
|
305
|
+
|
|
306
|
+
if (previous.kind === "foreign") {
|
|
307
|
+
if (options.force) {
|
|
308
|
+
const backupFile = `${stateFile}.foreign-${Date.now()}`
|
|
309
|
+
warnings.push(
|
|
310
|
+
`Backed up state owned by ${previous.package} before re-owning: ${backupFile}`,
|
|
311
|
+
)
|
|
312
|
+
await cp(stateFile, backupFile)
|
|
313
|
+
} else {
|
|
314
|
+
warnings.push(`Skipped install: state is owned by ${previous.package}`)
|
|
315
|
+
return {
|
|
316
|
+
configDir,
|
|
317
|
+
version: await packageVersion(),
|
|
318
|
+
stateError: `State belongs to another package: ${previous.package}`,
|
|
319
|
+
skills: [],
|
|
320
|
+
commands: [],
|
|
321
|
+
agents: [],
|
|
322
|
+
plugins: [],
|
|
323
|
+
warnings,
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (previous.kind === "invalid") {
|
|
329
|
+
const backupFile = `${stateFile}.invalid-${Date.now()}`
|
|
330
|
+
warnings.push(`Backed up unreadable state file before rebuilding: ${backupFile}`)
|
|
331
|
+
await cp(stateFile, backupFile)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
await mkdir(skillsTarget, { recursive: true })
|
|
335
|
+
await mkdir(commandsTarget, { recursive: true })
|
|
336
|
+
await mkdir(agentsTarget, { recursive: true })
|
|
337
|
+
await mkdir(pluginsTarget, { recursive: true })
|
|
338
|
+
await mkdir(stateDir, { recursive: true })
|
|
339
|
+
|
|
340
|
+
const sourceSkills = path.join(sourceRoot, "global-config", "skills")
|
|
341
|
+
const skillEntries = await readdir(sourceSkills, { withFileTypes: true })
|
|
342
|
+
|
|
343
|
+
for (const entry of skillEntries) {
|
|
344
|
+
if (!entry.isDirectory()) continue
|
|
345
|
+
|
|
346
|
+
const sourceDir = path.join(sourceSkills, entry.name)
|
|
347
|
+
const targetID = `${SKILL_PREFIX}${entry.name}`
|
|
348
|
+
const targetDir = path.join(skillsTarget, targetID)
|
|
349
|
+
|
|
350
|
+
if (await installSkillDirectory(sourceDir, targetDir, entry.name, warnings)) {
|
|
351
|
+
installed.skills.push(targetID)
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const sourceCommands = path.join(sourceRoot, "global-config", "commands")
|
|
356
|
+
const commandEntries = await readdir(sourceCommands, { withFileTypes: true })
|
|
357
|
+
|
|
358
|
+
for (const entry of commandEntries) {
|
|
359
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) continue
|
|
360
|
+
|
|
361
|
+
const id = entry.name.slice(0, -3)
|
|
362
|
+
if (!validResourceID(id)) {
|
|
363
|
+
warnings.push(`Skipped command with invalid resource ID: ${id}`)
|
|
364
|
+
continue
|
|
365
|
+
}
|
|
366
|
+
const targetName = `${SKILL_PREFIX}${id}.md`
|
|
367
|
+
const targetFile = path.join(commandsTarget, targetName)
|
|
368
|
+
const source = await readFile(path.join(sourceCommands, entry.name), "utf8")
|
|
369
|
+
const content = source.includes(MANAGED_MARKER)
|
|
370
|
+
? source
|
|
371
|
+
: `${source.trimEnd()}\n\n${MANAGED_MARKER}\n`
|
|
372
|
+
|
|
373
|
+
if (await writeManagedFile(targetFile, content, warnings)) {
|
|
374
|
+
installed.commands.push(targetName)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const sourceAgents = path.join(sourceRoot, "global-config", "agents")
|
|
379
|
+
const agentEntries = await readdir(sourceAgents, { withFileTypes: true })
|
|
380
|
+
|
|
381
|
+
for (const entry of agentEntries) {
|
|
382
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) continue
|
|
383
|
+
|
|
384
|
+
const id = entry.name.slice(0, -3)
|
|
385
|
+
if (!validResourceID(id)) {
|
|
386
|
+
warnings.push(`Skipped subagent with invalid resource ID: ${id}`)
|
|
387
|
+
continue
|
|
388
|
+
}
|
|
389
|
+
const targetName = `${SKILL_PREFIX}${id}.md`
|
|
390
|
+
const targetFile = path.join(agentsTarget, targetName)
|
|
391
|
+
const source = applyConfiguredModel(
|
|
392
|
+
adaptAgentForOpenCode(
|
|
393
|
+
await readFile(path.join(sourceAgents, entry.name), "utf8"),
|
|
394
|
+
openCodeMajor,
|
|
395
|
+
),
|
|
396
|
+
id,
|
|
397
|
+
modelPolicy,
|
|
398
|
+
)
|
|
399
|
+
const content = source.includes(MANAGED_MARKER)
|
|
400
|
+
? source
|
|
401
|
+
: `${source.trimEnd()}\n\n${MANAGED_MARKER}\n`
|
|
402
|
+
|
|
403
|
+
if (await writeManagedFile(targetFile, content, warnings)) {
|
|
404
|
+
installed.agents.push(targetName)
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (openCodeMajor >= 2) {
|
|
409
|
+
const sourcePluginDir = path.join(sourceRoot, "global-config", "plugins", "ues-router")
|
|
410
|
+
const targetPluginDir = path.join(pluginsTarget, "ues-router")
|
|
411
|
+
if (existsSync(path.join(sourcePluginDir, "index.js"))) {
|
|
412
|
+
if (await installPluginDirectory(sourcePluginDir, targetPluginDir, warnings)) {
|
|
413
|
+
installed.plugins.push(ROUTER_PLUGIN_STATE)
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const routerConfigFile = path.join(stateDir, "router.json")
|
|
418
|
+
if (!existsSync(routerConfigFile)) {
|
|
419
|
+
await writeFile(
|
|
420
|
+
routerConfigFile,
|
|
421
|
+
JSON.stringify({ enabled: true, maxSkills: 4 }, null, 2) + "\n",
|
|
422
|
+
"utf8",
|
|
423
|
+
)
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const globalAgentsFile = path.join(configDir, "AGENTS.md")
|
|
428
|
+
const existingAgents = await readText(globalAgentsFile)
|
|
429
|
+
const cleanAgents = removeManagedBlock(existingAgents)
|
|
430
|
+
const workflow = (await readFile(
|
|
431
|
+
path.join(sourceRoot, "global-config", "AGENTS.md"),
|
|
432
|
+
"utf8",
|
|
433
|
+
)).trim()
|
|
434
|
+
|
|
435
|
+
const managedBlock = `${AGENTS_BEGIN}\n${workflow}\n${AGENTS_END}`
|
|
436
|
+
const mergedAgents = [cleanAgents, managedBlock].filter(Boolean).join("\n\n") + "\n"
|
|
437
|
+
await writeFile(globalAgentsFile, mergedAgents, "utf8")
|
|
438
|
+
|
|
439
|
+
await cleanupStaleManaged(configDir, previousState, installed, warnings)
|
|
440
|
+
|
|
441
|
+
if (previous.kind === "invalid" || (previous.kind === "foreign" && options.force)) {
|
|
442
|
+
await warnUntrackedManaged(configDir, installed, warnings)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const state = {
|
|
446
|
+
schemaVersion: 2,
|
|
447
|
+
package: PACKAGE_NAME,
|
|
448
|
+
version: await packageVersion(),
|
|
449
|
+
configDir,
|
|
450
|
+
installedAt: new Date().toISOString(),
|
|
451
|
+
openCodeMajor,
|
|
452
|
+
skills: installed.skills.sort(),
|
|
453
|
+
commands: installed.commands.sort(),
|
|
454
|
+
agents: installed.agents.sort(),
|
|
455
|
+
plugins: installed.plugins.sort(),
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
await writeFile(stateFile, JSON.stringify(state, null, 2) + "\n", "utf8")
|
|
459
|
+
return { ...state, warnings }
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
export async function removeResources(options = {}) {
|
|
463
|
+
const configDir = getConfigDir()
|
|
464
|
+
const stateFile = path.join(configDir, ".ues", "state.json")
|
|
465
|
+
const warnings = []
|
|
466
|
+
const previous = await readManagedState(stateFile, warnings)
|
|
467
|
+
|
|
468
|
+
if (options.force && (previous.kind === "foreign" || previous.kind === "invalid")) {
|
|
469
|
+
const backupFile = path.join(
|
|
470
|
+
configDir,
|
|
471
|
+
`state.${previous.kind}-${Date.now()}.backup.json`,
|
|
472
|
+
)
|
|
473
|
+
warnings.push(`Backed up state before forced remove: ${backupFile}`)
|
|
474
|
+
await cp(stateFile, backupFile)
|
|
475
|
+
|
|
476
|
+
const untracked = await scanUntrackedManaged(configDir)
|
|
477
|
+
for (const id of untracked.skills) {
|
|
478
|
+
await rm(path.join(configDir, "skills", id), { recursive: true, force: true })
|
|
479
|
+
}
|
|
480
|
+
for (const [targetDir, key] of [
|
|
481
|
+
[path.join(configDir, "commands"), "commands"],
|
|
482
|
+
[path.join(configDir, "agents"), "agents"],
|
|
483
|
+
]) {
|
|
484
|
+
for (const name of untracked[key]) {
|
|
485
|
+
await rm(path.join(targetDir, name), { force: true })
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
for (const name of untracked.plugins || []) {
|
|
489
|
+
if (!validManagedPlugin(name)) continue
|
|
490
|
+
await rm(path.join(configDir, "plugins", "ues-router"), { recursive: true, force: true })
|
|
491
|
+
}
|
|
492
|
+
const removed = {
|
|
493
|
+
skills: untracked.skills.length,
|
|
494
|
+
commands: untracked.commands.length,
|
|
495
|
+
agents: untracked.agents.length,
|
|
496
|
+
plugins: (untracked.plugins || []).length,
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
await removeManagedAgentsBlock(configDir)
|
|
500
|
+
await rm(path.join(configDir, ".ues"), { recursive: true, force: true })
|
|
501
|
+
return { configDir, ...removed, warnings }
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const state = previous.state
|
|
505
|
+
const removed = { skills: 0, commands: 0, agents: 0, plugins: 0 }
|
|
506
|
+
|
|
507
|
+
if (previous.kind === "foreign") {
|
|
508
|
+
warnings.push(`Skipped remove: state is owned by ${previous.package}`)
|
|
509
|
+
return {
|
|
510
|
+
configDir,
|
|
511
|
+
stateError: `State belongs to another package: ${previous.package}`,
|
|
512
|
+
...removed,
|
|
513
|
+
warnings,
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (previous.kind === "invalid") {
|
|
518
|
+
warnings.push("Skipped remove: state file is unreadable")
|
|
519
|
+
return {
|
|
520
|
+
configDir,
|
|
521
|
+
stateError: "State file is invalid; refusing to modify it.",
|
|
522
|
+
...removed,
|
|
523
|
+
warnings,
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
for (const id of state.skills || []) {
|
|
528
|
+
if (!validSkillID(id)) continue
|
|
529
|
+
const dir = path.join(configDir, "skills", id)
|
|
530
|
+
const file = path.join(dir, "SKILL.md")
|
|
531
|
+
const current = await readText(file)
|
|
532
|
+
if (current.includes(MANAGED_MARKER)) {
|
|
533
|
+
await rm(dir, { recursive: true, force: true })
|
|
534
|
+
removed.skills += 1
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
for (const name of state.commands || []) {
|
|
539
|
+
if (!validManagedMarkdown(name)) continue
|
|
540
|
+
const file = path.join(configDir, "commands", name)
|
|
541
|
+
const current = await readText(file)
|
|
542
|
+
if (current.includes(MANAGED_MARKER)) {
|
|
543
|
+
await rm(file, { force: true })
|
|
544
|
+
removed.commands += 1
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
for (const name of state.agents || []) {
|
|
549
|
+
if (!validManagedMarkdown(name)) continue
|
|
550
|
+
const file = path.join(configDir, "agents", name)
|
|
551
|
+
const current = await readText(file)
|
|
552
|
+
if (current.includes(MANAGED_MARKER)) {
|
|
553
|
+
await rm(file, { force: true })
|
|
554
|
+
removed.agents += 1
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
for (const name of state.plugins || []) {
|
|
559
|
+
if (!validManagedPlugin(name)) continue
|
|
560
|
+
const file = path.join(configDir, "plugins", name)
|
|
561
|
+
const current = await readText(file)
|
|
562
|
+
if (current.includes(MANAGED_MARKER)) {
|
|
563
|
+
await rm(path.dirname(file), { recursive: true, force: true })
|
|
564
|
+
removed.plugins += 1
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
await removeManagedAgentsBlock(configDir)
|
|
569
|
+
await rm(path.join(configDir, ".ues"), { recursive: true, force: true })
|
|
570
|
+
return { configDir, ...removed, warnings }
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
export async function getStatus() {
|
|
574
|
+
const configDir = getConfigDir()
|
|
575
|
+
const stateFile = path.join(configDir, ".ues", "state.json")
|
|
576
|
+
const raw = await readText(stateFile)
|
|
577
|
+
if (!raw) return { installed: false, configDir }
|
|
578
|
+
|
|
579
|
+
let state
|
|
580
|
+
try {
|
|
581
|
+
state = JSON.parse(raw)
|
|
582
|
+
} catch {
|
|
583
|
+
return {
|
|
584
|
+
installed: false,
|
|
585
|
+
configDir,
|
|
586
|
+
stateError: "Invalid UES state file; run ocskill install to rebuild it.",
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
if (state.package && !isOwnedPackageName(state.package)) {
|
|
591
|
+
return {
|
|
592
|
+
installed: false,
|
|
593
|
+
configDir,
|
|
594
|
+
stateError: `State belongs to another package: ${state.package}`,
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
state.skills = Array.isArray(state.skills) ? state.skills.filter(validSkillID) : []
|
|
599
|
+
state.commands = Array.isArray(state.commands) ? state.commands.filter(validManagedMarkdown) : []
|
|
600
|
+
state.agents = Array.isArray(state.agents) ? state.agents.filter(validManagedMarkdown) : []
|
|
601
|
+
state.plugins = Array.isArray(state.plugins) ? state.plugins.filter(validManagedPlugin) : []
|
|
602
|
+
|
|
603
|
+
let skillsPresent = 0
|
|
604
|
+
let commandsPresent = 0
|
|
605
|
+
let agentsPresent = 0
|
|
606
|
+
let pluginsPresent = 0
|
|
607
|
+
|
|
608
|
+
for (const id of state.skills || []) {
|
|
609
|
+
if (existsSync(path.join(configDir, "skills", id, "SKILL.md"))) skillsPresent += 1
|
|
610
|
+
}
|
|
611
|
+
for (const name of state.commands || []) {
|
|
612
|
+
if (existsSync(path.join(configDir, "commands", name))) commandsPresent += 1
|
|
613
|
+
}
|
|
614
|
+
for (const name of state.agents || []) {
|
|
615
|
+
if (existsSync(path.join(configDir, "agents", name))) agentsPresent += 1
|
|
616
|
+
}
|
|
617
|
+
for (const name of state.plugins || []) {
|
|
618
|
+
if (existsSync(path.join(configDir, "plugins", name))) pluginsPresent += 1
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
const agents = await readText(path.join(configDir, "AGENTS.md"))
|
|
622
|
+
|
|
623
|
+
return {
|
|
624
|
+
installed: true,
|
|
625
|
+
...state,
|
|
626
|
+
skillsPresent,
|
|
627
|
+
commandsPresent,
|
|
628
|
+
agentsPresent,
|
|
629
|
+
pluginsPresent,
|
|
630
|
+
workflowPresent: agents.includes(AGENTS_BEGIN),
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
export async function getPackageVersion() {
|
|
635
|
+
return packageVersion()
|
|
636
|
+
}
|