@foggy-projects/deepseek-harness-plugin 0.4.0-beta.3 → 0.4.0-beta.5
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.md +18 -4
- package/experience/linux/prepare.sh +1 -1
- package/lib/client.js +12 -9
- package/lib/index.js +33 -7
- package/lib/skill-provider.js +111 -0
- package/package.json +3 -2
- package/skills/foggy-deepseek-onboarding/SKILL.md +24 -5
- package/skills/foggy-deepseek-onboarding/assets/versions.json +2 -2
- package/skills/foggy-deepseek-onboarding/scripts/onboarding.py +172 -53
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ only when the user selects **Initialize Foggy**.
|
|
|
8
8
|
## Local beta installation
|
|
9
9
|
|
|
10
10
|
```powershell
|
|
11
|
-
dsh plugin --profile web add --workspace-root ./foggy-projects-deepseek-harness-plugin-0.4.0-beta.
|
|
11
|
+
dsh plugin --profile web add --workspace-root ./foggy-projects-deepseek-harness-plugin-0.4.0-beta.5.tgz
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
Restart `dsh web`, open Settings → Plugins → Foggy Data Analysis, and initialize
|
|
@@ -20,13 +20,27 @@ After npm beta publication, the corresponding one-line install is:
|
|
|
20
20
|
dsh plugin --profile web add --workspace-root @foggy-projects/deepseek-harness-plugin@beta
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
For development, `
|
|
24
|
-
|
|
25
|
-
asset-cache directories. Neither variable is required for a normal install.
|
|
23
|
+
For development, `FOGGY_ASSET_CACHE_DIRS` can contain platform-delimited verified
|
|
24
|
+
asset-cache directories. It is not required for a normal install.
|
|
26
25
|
|
|
27
26
|
Database credentials are deliberately outside the ordinary DSH settings
|
|
28
27
|
document. The database and semantic-layer wizard is the next Bundle milestone.
|
|
29
28
|
|
|
29
|
+
## Native Skill and workspace contract
|
|
30
|
+
|
|
31
|
+
The Bundle registers `foggy-deepseek-onboarding` and the downloaded
|
|
32
|
+
`foggy-ai-analysis` through DeepSeek Harness's native Skill provider API. Skills
|
|
33
|
+
are available in every DSH workspace without copying or symlinking `.agents`.
|
|
34
|
+
The current session `cwd` remains the workspace boundary for semantic drafts and
|
|
35
|
+
evidence.
|
|
36
|
+
|
|
37
|
+
CLI, Launcher, the analysis Skill, install state, and Runtime state live in the
|
|
38
|
+
user-level Foggy component directories. The managed CLI is intentionally isolated
|
|
39
|
+
and does not need to be on `PATH`. **Re-download / Repair** verifies the global
|
|
40
|
+
analysis Skill, backs up modified or outdated managed content, restores it, and
|
|
41
|
+
invalidates DSH's Skill catalog. The onboarding Skill is bundled with the plugin
|
|
42
|
+
and is restored by reinstalling or upgrading the plugin package.
|
|
43
|
+
|
|
30
44
|
## Linux and WSL2 experience
|
|
31
45
|
|
|
32
46
|
Ubuntu and WSL2 users can use the checked-in preflighted installer under
|
package/lib/client.js
CHANGED
|
@@ -41,6 +41,8 @@ window.__ModuleLoader__.load({
|
|
|
41
41
|
cli: 'CLI',
|
|
42
42
|
launcher: 'Launcher',
|
|
43
43
|
analysisSkill: '分析 Skill',
|
|
44
|
+
onboardingSkill: '引导 Skill',
|
|
45
|
+
skillRegistry: 'DSH Skill 注册',
|
|
44
46
|
python: 'Python',
|
|
45
47
|
java: 'Java',
|
|
46
48
|
installed: '已安装',
|
|
@@ -57,7 +59,7 @@ window.__ModuleLoader__.load({
|
|
|
57
59
|
progressCli: '准备 CLI',
|
|
58
60
|
progressLauncher: '准备 Launcher',
|
|
59
61
|
progressAnalysis: '准备分析 Skill',
|
|
60
|
-
progressSkills: '
|
|
62
|
+
progressSkills: '注册 DSH Skills',
|
|
61
63
|
progressState: '写入安装状态',
|
|
62
64
|
progressComplete: '初始化完成',
|
|
63
65
|
progressFiles: '文件',
|
|
@@ -67,9 +69,8 @@ window.__ModuleLoader__.load({
|
|
|
67
69
|
installRoot: '组件目录',
|
|
68
70
|
dataRoot: '数据目录',
|
|
69
71
|
runtimeUrl: 'Runtime 地址',
|
|
70
|
-
projectRoot: 'Skill 目标工作区',
|
|
71
72
|
nextTitle: '后续配置',
|
|
72
|
-
nextCopy: '
|
|
73
|
+
nextCopy: 'Skills 已通过 DeepSeek Harness 原生注册表提供给所有工作区;每个会话直接使用自己的工作目录。Runtime 启动成功后,将继续进入数据库连接与语义层向导。',
|
|
73
74
|
beta: 'Beta',
|
|
74
75
|
}
|
|
75
76
|
|
|
@@ -88,6 +89,8 @@ window.__ModuleLoader__.load({
|
|
|
88
89
|
cli: 'CLI',
|
|
89
90
|
launcher: 'Launcher',
|
|
90
91
|
analysisSkill: 'Analysis Skill',
|
|
92
|
+
onboardingSkill: 'Onboarding Skill',
|
|
93
|
+
skillRegistry: 'DSH Skill registry',
|
|
91
94
|
python: 'Python',
|
|
92
95
|
java: 'Java',
|
|
93
96
|
installed: 'Installed',
|
|
@@ -104,7 +107,7 @@ window.__ModuleLoader__.load({
|
|
|
104
107
|
progressCli: 'Preparing CLI',
|
|
105
108
|
progressLauncher: 'Preparing Launcher',
|
|
106
109
|
progressAnalysis: 'Preparing analysis Skill',
|
|
107
|
-
progressSkills: '
|
|
110
|
+
progressSkills: 'Registering DSH Skills',
|
|
108
111
|
progressState: 'Writing install state',
|
|
109
112
|
progressComplete: 'Initialization complete',
|
|
110
113
|
progressFiles: 'files',
|
|
@@ -114,9 +117,8 @@ window.__ModuleLoader__.load({
|
|
|
114
117
|
installRoot: 'Components',
|
|
115
118
|
dataRoot: 'Data',
|
|
116
119
|
runtimeUrl: 'Runtime URL',
|
|
117
|
-
projectRoot: 'Skill target workspace',
|
|
118
120
|
nextTitle: 'Next configuration',
|
|
119
|
-
nextCopy: '
|
|
121
|
+
nextCopy: 'Skills are provided to every workspace through the native DeepSeek Harness registry, and each session uses its own working directory. After Runtime starts, the database connection and semantic-layer wizard comes next.',
|
|
120
122
|
beta: 'Beta',
|
|
121
123
|
}
|
|
122
124
|
|
|
@@ -264,8 +266,8 @@ window.__ModuleLoader__.load({
|
|
|
264
266
|
jsxs('div', { className: 'foggy-state', children: [jsx('span', { className: 'foggy-dot', 'data-state': status.state }), jsx('span', { children: t(stateLabels[status.state] || 'notInstalled') })] }),
|
|
265
267
|
jsxs('div', { className: 'foggy-actions', children: [
|
|
266
268
|
jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: refresh, children: t('refresh') }),
|
|
267
|
-
|
|
268
|
-
status.installed ? jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('repair'), children: t('repair') }) : null,
|
|
269
|
+
status.state === 'not-installed' ? jsx('button', { className: 'foggy-button foggy-button-primary', type: 'button', disabled: busy, onClick: () => run('initialize'), children: t('initialize') }) : null,
|
|
270
|
+
status.state !== 'not-installed' ? jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('repair'), children: t('repair') }) : null,
|
|
269
271
|
status.installed && !status.running && status.components.java.available ? jsx('button', { className: 'foggy-button foggy-button-primary', type: 'button', disabled: busy, onClick: () => run('runtimeStart'), children: t('start') }) : null,
|
|
270
272
|
status.running ? jsx('button', { className: 'foggy-button', type: 'button', disabled: busy, onClick: () => run('runtimeStop'), children: t('stop') }) : null,
|
|
271
273
|
] }),
|
|
@@ -278,13 +280,14 @@ window.__ModuleLoader__.load({
|
|
|
278
280
|
componentCard(t('cli'), status.components.cli, 'runtime', t),
|
|
279
281
|
componentCard(t('launcher'), status.components.launcher, 'runtime', t),
|
|
280
282
|
componentCard(t('analysisSkill'), status.components.analysisSkill, 'runtime', t),
|
|
283
|
+
componentCard(t('onboardingSkill'), status.components.onboardingSkill, 'runtime', t),
|
|
284
|
+
componentCard(t('skillRegistry'), { installed: status.components.onboardingSkill?.provider === 'foggy-managed-skills', version: 'native' }, 'runtime', t),
|
|
281
285
|
] }),
|
|
282
286
|
jsxs('div', { className: 'foggy-paths', children: [
|
|
283
287
|
jsx('h4', { children: t('roots') }),
|
|
284
288
|
jsxs('dl', { children: [
|
|
285
289
|
jsx('dt', { children: t('installRoot') }), jsx('dd', { children: status.roots.installRoot }),
|
|
286
290
|
jsx('dt', { children: t('dataRoot') }), jsx('dd', { children: status.roots.dataRoot }),
|
|
287
|
-
jsx('dt', { children: t('projectRoot') }), jsx('dd', { children: status.projectRoot }),
|
|
288
291
|
status.runtimeUrl ? jsx('dt', { children: t('runtimeUrl') }) : null,
|
|
289
292
|
status.runtimeUrl ? jsx('dd', { children: status.runtimeUrl }) : null,
|
|
290
293
|
] }),
|
package/lib/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { dirname, join, delimiter } from 'node:path'
|
|
|
5
5
|
import { fileURLToPath } from 'node:url'
|
|
6
6
|
import { promisify } from 'node:util'
|
|
7
7
|
import { Remote, TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol'
|
|
8
|
+
import { createFoggySkillProvider } from './skill-provider.js'
|
|
8
9
|
import { compatible } from './version.js'
|
|
9
10
|
|
|
10
11
|
const execFileAsync = promisify(execFile)
|
|
@@ -120,10 +121,18 @@ function operationView(operation) {
|
|
|
120
121
|
}
|
|
121
122
|
|
|
122
123
|
export class FoggyIntegrationGateway extends TypertRemoteService {
|
|
124
|
+
static inject = ['skills']
|
|
125
|
+
|
|
123
126
|
operation = null
|
|
127
|
+
skillProviderControl = null
|
|
124
128
|
|
|
125
129
|
constructor(ctx) {
|
|
126
130
|
super(ctx, 'foggyIntegration')
|
|
131
|
+
const roots = defaultRoots()
|
|
132
|
+
ctx.skills.registerProvider((control) => {
|
|
133
|
+
this.skillProviderControl = control
|
|
134
|
+
return createFoggySkillProvider({ installRoot: roots.installRoot, versionsFile })
|
|
135
|
+
})
|
|
127
136
|
for (const initialize of markerInitializers) initialize.call(this)
|
|
128
137
|
}
|
|
129
138
|
|
|
@@ -147,6 +156,9 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
|
|
|
147
156
|
try { runtime = await readJson(runtimeStatePath) } catch {}
|
|
148
157
|
const cliPath = state?.cli?.command
|
|
149
158
|
const launcherPath = state?.launcher?.path
|
|
159
|
+
const analysisSkillPath = state?.skills?.analysis?.path || join(roots.installRoot, 'skills', 'foggy-ai-analysis')
|
|
160
|
+
const onboardingSkillPath = join(packageRoot, 'skills', 'foggy-deepseek-onboarding')
|
|
161
|
+
const analysisMarker = await readOptionalJson(join(analysisSkillPath, '.foggy-managed-skill.json'))
|
|
150
162
|
const components = {
|
|
151
163
|
python,
|
|
152
164
|
java,
|
|
@@ -159,11 +171,23 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
|
|
|
159
171
|
version: state?.launcher?.version ?? manifest.components.launcher.version,
|
|
160
172
|
},
|
|
161
173
|
analysisSkill: {
|
|
162
|
-
installed: Boolean(
|
|
174
|
+
installed: Boolean(
|
|
175
|
+
await exists(join(analysisSkillPath, 'SKILL.md'))
|
|
176
|
+
&& analysisMarker?.schemaVersion === 'foggy-managed-skill/v1'
|
|
177
|
+
&& analysisMarker?.componentVersion === manifest.components.analysisSkill.version
|
|
178
|
+
),
|
|
163
179
|
version: state?.skills?.analysis?.version ?? manifest.components.analysisSkill.version,
|
|
164
180
|
},
|
|
181
|
+
onboardingSkill: {
|
|
182
|
+
installed: await exists(join(onboardingSkillPath, 'SKILL.md')),
|
|
183
|
+
version: manifest.packageVersion,
|
|
184
|
+
provider: 'foggy-managed-skills',
|
|
185
|
+
},
|
|
165
186
|
}
|
|
166
|
-
const installed = components.cli.installed
|
|
187
|
+
const installed = components.cli.installed
|
|
188
|
+
&& components.launcher.installed
|
|
189
|
+
&& components.analysisSkill.installed
|
|
190
|
+
&& components.onboardingSkill.installed
|
|
167
191
|
const running = Boolean(runtime && processRunning(Number(runtime.pid)))
|
|
168
192
|
const progress = await readOptionalJson(progressPath)
|
|
169
193
|
let operation = operationView(this.operation)
|
|
@@ -189,7 +213,6 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
|
|
|
189
213
|
running,
|
|
190
214
|
runtimeUrl: running ? runtime.runtimeUrl ?? null : null,
|
|
191
215
|
roots,
|
|
192
|
-
projectRoot: process.env.FOGGY_PROJECT_ROOT || process.cwd(),
|
|
193
216
|
components,
|
|
194
217
|
operation,
|
|
195
218
|
next: installed ? (running ? 'configure-database' : 'start-runtime') : 'initialize',
|
|
@@ -202,13 +225,14 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
|
|
|
202
225
|
return {
|
|
203
226
|
success: true,
|
|
204
227
|
roots,
|
|
205
|
-
|
|
228
|
+
workspaceMode: 'dsh-session-cwd',
|
|
206
229
|
versions: Object.fromEntries(Object.entries(manifest.components).map(([name, value]) => [name, value.version])),
|
|
207
230
|
operations: [
|
|
208
231
|
'create isolated Python environment',
|
|
209
232
|
'download and verify pinned CLI and Launcher assets',
|
|
210
|
-
'install Foggy analysis
|
|
211
|
-
'
|
|
233
|
+
'install the Foggy analysis Skill into the global managed component directory',
|
|
234
|
+
'register onboarding and analysis Skills through the native DSH Skill registry',
|
|
235
|
+
'write global install state; resolve each workspace from the DSH session cwd',
|
|
212
236
|
],
|
|
213
237
|
secretsInDshSettings: false,
|
|
214
238
|
}
|
|
@@ -235,7 +259,7 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
|
|
|
235
259
|
return { success: false, accepted: false, operation: operationView(this.operation), error: 'another Foggy operation is running' }
|
|
236
260
|
}
|
|
237
261
|
const id = `${Date.now()}-${Math.random().toString(16).slice(2, 10)}`
|
|
238
|
-
const args = explicitArgs ?? ['install'
|
|
262
|
+
const args = explicitArgs ?? ['install']
|
|
239
263
|
if (!explicitArgs) {
|
|
240
264
|
const roots = defaultRoots()
|
|
241
265
|
args.push('--progress-file', join(roots.dataRoot, 'operation-progress.json'), '--operation-id', id, '--operation-kind', kind)
|
|
@@ -270,6 +294,7 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
|
|
|
270
294
|
operation.state = result.success === false ? 'failed' : 'succeeded'
|
|
271
295
|
operation.result = result
|
|
272
296
|
operation.finishedAt = new Date().toISOString()
|
|
297
|
+
this.skillProviderControl?.invalidate()
|
|
273
298
|
if (operation.progress) {
|
|
274
299
|
operation.progress = {
|
|
275
300
|
...operation.progress,
|
|
@@ -284,6 +309,7 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
|
|
|
284
309
|
operation.state = 'failed'
|
|
285
310
|
operation.error = String(error.message ?? error)
|
|
286
311
|
operation.finishedAt = new Date().toISOString()
|
|
312
|
+
this.skillProviderControl?.invalidate()
|
|
287
313
|
if (operation.progress) {
|
|
288
314
|
operation.progress = {
|
|
289
315
|
...operation.progress,
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { access, readFile } from 'node:fs/promises'
|
|
2
|
+
import { constants as fsConstants } from 'node:fs'
|
|
3
|
+
import { dirname, join } from 'node:path'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
import { BUNDLED_SKILL_RANK } from '@deepseek-ai/dsh-skill'
|
|
6
|
+
|
|
7
|
+
const packageRoot = dirname(dirname(fileURLToPath(import.meta.url)))
|
|
8
|
+
const onboardingRoot = join(packageRoot, 'skills', 'foggy-deepseek-onboarding')
|
|
9
|
+
const onboardingPath = join(onboardingRoot, 'SKILL.md')
|
|
10
|
+
const providerName = 'foggy-managed-skills'
|
|
11
|
+
const invocation = { modelInvocable: true, userInvocable: true }
|
|
12
|
+
|
|
13
|
+
async function exists(path) {
|
|
14
|
+
try {
|
|
15
|
+
await access(path, fsConstants.F_OK)
|
|
16
|
+
return true
|
|
17
|
+
} catch {
|
|
18
|
+
return false
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function readOptionalJson(path) {
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(await readFile(path, 'utf8'))
|
|
25
|
+
} catch {
|
|
26
|
+
return null
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function parseSkill(source, expectedName) {
|
|
31
|
+
const match = source.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/)
|
|
32
|
+
if (!match) throw new Error(`Foggy Skill ${expectedName} has no valid frontmatter`)
|
|
33
|
+
const metadata = Object.fromEntries(match[1].split(/\r?\n/).flatMap((line) => {
|
|
34
|
+
const separator = line.indexOf(':')
|
|
35
|
+
if (separator < 1) return []
|
|
36
|
+
return [[line.slice(0, separator).trim(), line.slice(separator + 1).trim().replace(/^(['\"])(.*)\1$/, '$2')]]
|
|
37
|
+
}))
|
|
38
|
+
if (metadata.name !== expectedName || !metadata.description) {
|
|
39
|
+
throw new Error(`Foggy Skill ${expectedName} has invalid name or description metadata`)
|
|
40
|
+
}
|
|
41
|
+
return { metadata, content: match[2] }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function candidate(name, description, path, source) {
|
|
45
|
+
const root = dirname(path)
|
|
46
|
+
return {
|
|
47
|
+
name,
|
|
48
|
+
description,
|
|
49
|
+
invocation,
|
|
50
|
+
provider: providerName,
|
|
51
|
+
source,
|
|
52
|
+
resourceBase: { kind: 'directory', path: root },
|
|
53
|
+
rank: BUNDLED_SKILL_RANK,
|
|
54
|
+
locator: { name, path },
|
|
55
|
+
path,
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function managedAnalysisSkill(installRoot, manifest) {
|
|
60
|
+
const state = await readOptionalJson(join(installRoot, 'install-state.json'))
|
|
61
|
+
const path = state?.skills?.analysis?.path
|
|
62
|
+
if (!path || !await exists(join(path, 'SKILL.md'))) return null
|
|
63
|
+
const marker = await readOptionalJson(join(path, '.foggy-managed-skill.json'))
|
|
64
|
+
if (
|
|
65
|
+
marker?.schemaVersion !== 'foggy-managed-skill/v1'
|
|
66
|
+
|| marker?.kind !== 'analysis'
|
|
67
|
+
|| marker?.componentVersion !== manifest.components.analysisSkill.version
|
|
68
|
+
) return null
|
|
69
|
+
return join(path, 'SKILL.md')
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function createFoggySkillProvider({ installRoot, versionsFile }) {
|
|
73
|
+
return {
|
|
74
|
+
name: providerName,
|
|
75
|
+
async list(options = {}) {
|
|
76
|
+
if (options.signal?.aborted) return []
|
|
77
|
+
const manifest = await readOptionalJson(versionsFile)
|
|
78
|
+
const onboarding = parseSkill(await readFile(onboardingPath, 'utf8'), 'foggy-deepseek-onboarding')
|
|
79
|
+
const result = [candidate(
|
|
80
|
+
'foggy-deepseek-onboarding',
|
|
81
|
+
onboarding.metadata.description,
|
|
82
|
+
onboardingPath,
|
|
83
|
+
'bundled',
|
|
84
|
+
)]
|
|
85
|
+
if (!manifest) return result
|
|
86
|
+
const analysisPath = await managedAnalysisSkill(installRoot, manifest)
|
|
87
|
+
if (analysisPath) {
|
|
88
|
+
const analysis = parseSkill(await readFile(analysisPath, 'utf8'), 'foggy-ai-analysis')
|
|
89
|
+
result.push(candidate('foggy-ai-analysis', analysis.metadata.description, analysisPath, 'bundled'))
|
|
90
|
+
}
|
|
91
|
+
return result
|
|
92
|
+
},
|
|
93
|
+
async get(selected, options = {}) {
|
|
94
|
+
if (options.signal?.aborted) return undefined
|
|
95
|
+
const locator = selected?.locator
|
|
96
|
+
if (!locator?.name || !locator?.path || !await exists(locator.path)) return undefined
|
|
97
|
+
const parsed = parseSkill(await readFile(locator.path, 'utf8'), locator.name)
|
|
98
|
+
return {
|
|
99
|
+
name: locator.name,
|
|
100
|
+
description: parsed.metadata.description,
|
|
101
|
+
invocation,
|
|
102
|
+
provider: providerName,
|
|
103
|
+
source: selected.source,
|
|
104
|
+
resourceBase: { kind: 'directory', path: dirname(locator.path) },
|
|
105
|
+
content: parsed.content,
|
|
106
|
+
path: locator.path,
|
|
107
|
+
metadata: parsed.metadata,
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
}
|
|
111
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foggy-projects/deepseek-harness-plugin",
|
|
3
|
-
"version": "0.4.0-beta.
|
|
3
|
+
"version": "0.4.0-beta.5",
|
|
4
4
|
"description": "Foggy Java data analysis engine integration for DeepSeek Harness",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -50,10 +50,11 @@
|
|
|
50
50
|
"@deepseek-ai/dsh-client-runtime": "^0.1.1-rc.2",
|
|
51
51
|
"@deepseek-ai/dsh-client-ui-settings": "^0.1.1-rc.2",
|
|
52
52
|
"@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.1-rc.2",
|
|
53
|
+
"@deepseek-ai/dsh-skill": "^0.1.1-rc.2",
|
|
53
54
|
"@deepseek-ai/dsh-typert-protocol": "^0.1.1-rc.2"
|
|
54
55
|
},
|
|
55
56
|
"scripts": {
|
|
56
|
-
"check": "node --check lib/index.js && node --check lib/client.js && node --check lib/typert.js && node --check lib/remote.js",
|
|
57
|
+
"check": "node --check lib/index.js && node --check lib/skill-provider.js && node --check lib/client.js && node --check lib/typert.js && node --check lib/remote.js",
|
|
57
58
|
"test": "node --test test/package.test.js"
|
|
58
59
|
}
|
|
59
60
|
}
|
|
@@ -7,6 +7,24 @@ description: Install and operate a pinned Foggy CLI-first dev/test environment f
|
|
|
7
7
|
|
|
8
8
|
Set up Foggy through shell and `foggy-runtime` CLI. Do not configure Foggy MCP for this local workflow.
|
|
9
9
|
|
|
10
|
+
## Native registry and installed-state gate
|
|
11
|
+
|
|
12
|
+
This Skill is registered by the Foggy plugin through DeepSeek Harness's native Skill registry. Its
|
|
13
|
+
resource base is the authoritative location for these scripts and references; do not search for or
|
|
14
|
+
copy this Skill into the current workspace.
|
|
15
|
+
|
|
16
|
+
- Resolve the global install state from the platform default component directory: on Linux use
|
|
17
|
+
`${XDG_DATA_HOME:-$HOME/.local/share}/foggy/deepseek-harness/install-state.json`; on Windows use
|
|
18
|
+
`%LOCALAPPDATA%\Foggy\DeepSeekHarness\install-state.json`.
|
|
19
|
+
- Confirm the installation with this Skill's `doctor` wrapper and pass the current DSH session
|
|
20
|
+
workspace as `--project-root`. The absence of `foggy-runtime` from `PATH` is not evidence that the
|
|
21
|
+
managed CLI is missing; the plugin intentionally installs it in an isolated environment and records
|
|
22
|
+
its absolute command in the global install state.
|
|
23
|
+
- Do not independently download or reinstall the CLI. If the global install state, managed marker, or
|
|
24
|
+
analysis Skill is missing or invalid, ask the user to open the Foggy plugin settings and use
|
|
25
|
+
**Re-download / Repair**. Repair restores the global managed analysis Skill and invalidates the
|
|
26
|
+
native DSH Skill catalog. Reinstall or upgrade the plugin itself to restore this bundled Skill.
|
|
27
|
+
|
|
10
28
|
## Mandatory orchestration boundary
|
|
11
29
|
|
|
12
30
|
For every new-database onboarding session, this Skill is the orchestration authority until
|
|
@@ -42,13 +60,14 @@ For every new-database onboarding session, this Skill is the orchestration autho
|
|
|
42
60
|
|
|
43
61
|
## Workflow
|
|
44
62
|
|
|
45
|
-
1. Run `scripts/doctor.ps1 --project-root <
|
|
46
|
-
`bash scripts/doctor.sh --project-root <
|
|
47
|
-
2. If the pinned CLI, Launcher, or analysis Skill is missing,
|
|
48
|
-
|
|
63
|
+
1. Run `scripts/doctor.ps1 --project-root <current-session-workspace>` on Windows or
|
|
64
|
+
`bash scripts/doctor.sh --project-root <current-session-workspace>` on Linux.
|
|
65
|
+
2. If the pinned CLI, Launcher, or global managed analysis Skill is missing, use the Foggy
|
|
66
|
+
plugin's Repair action. Use the matching install script only when the plugin UI is unavailable;
|
|
67
|
+
use `--dry-run` first when paths or permissions are uncertain.
|
|
49
68
|
3. Run `runtime-start` and require successful `wait-ready` plus `capabilities`. Record engine,
|
|
50
69
|
Runtime API version, schema version, security mode, URL, namespace, PID, and evidence path.
|
|
51
|
-
4.
|
|
70
|
+
4. Load `foggy-ai-analysis` from the native DSH Skill registry. Do not require a workspace copy.
|
|
52
71
|
5. For a new business database, read [references/onboarding-workflow.md](references/onboarding-workflow.md)
|
|
53
72
|
and prefer its two composite `onboard-datasource-run` / `onboard-semantic-run` commands. Require the
|
|
54
73
|
trusted operator to create the private CLI profile outside Harness. Accept only the opaque profile
|
|
@@ -25,6 +25,9 @@ import zipfile
|
|
|
25
25
|
STATE_SCHEMA = "foggy-deepseek-onboarding-install/v1"
|
|
26
26
|
RUNTIME_STATE_SCHEMA = "foggy-deepseek-onboarding-runtime/v1"
|
|
27
27
|
ONBOARDING_STATE_SCHEMA = "foggy-deepseek-onboarding-state/v1"
|
|
28
|
+
MANAGED_SKILL_SCHEMA = "foggy-managed-skill/v1"
|
|
29
|
+
MANAGED_SKILL_MARKER = ".foggy-managed-skill.json"
|
|
30
|
+
LEGACY_SKILL_MARKER = ".foggy-onboarding-install.json"
|
|
28
31
|
CONNECTION_SCHEMA = "foggy-deepseek-connection/v1"
|
|
29
32
|
SEMANTIC_PLAN_SCHEMA = "foggy-deepseek-semantic-plan/v1"
|
|
30
33
|
PROFILE_PATTERN = re.compile(r"^[a-z0-9][a-z0-9._-]{0,62}$")
|
|
@@ -331,6 +334,98 @@ def atomic_json(path: Path, payload: dict) -> None:
|
|
|
331
334
|
os.replace(temporary, path)
|
|
332
335
|
|
|
333
336
|
|
|
337
|
+
def skill_tree_digest(root: Path) -> str:
|
|
338
|
+
"""Hash managed Skill content while ignoring generated and management files."""
|
|
339
|
+
digest = hashlib.sha256()
|
|
340
|
+
ignored_names = {MANAGED_SKILL_MARKER, LEGACY_SKILL_MARKER}
|
|
341
|
+
files = sorted(
|
|
342
|
+
path for path in root.rglob("*")
|
|
343
|
+
if path.is_file()
|
|
344
|
+
and path.name not in ignored_names
|
|
345
|
+
and "__pycache__" not in path.parts
|
|
346
|
+
and path.suffix != ".pyc"
|
|
347
|
+
)
|
|
348
|
+
for path in files:
|
|
349
|
+
relative = path.relative_to(root).as_posix().encode("utf-8")
|
|
350
|
+
digest.update(len(relative).to_bytes(4, "big"))
|
|
351
|
+
digest.update(relative)
|
|
352
|
+
with path.open("rb") as stream:
|
|
353
|
+
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
|
|
354
|
+
digest.update(chunk)
|
|
355
|
+
return digest.hexdigest()
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def read_skill_marker(destination: Path) -> dict | None:
|
|
359
|
+
for name in (MANAGED_SKILL_MARKER, LEGACY_SKILL_MARKER):
|
|
360
|
+
marker = destination / name
|
|
361
|
+
if marker.is_file():
|
|
362
|
+
try:
|
|
363
|
+
payload = json.loads(marker.read_text(encoding="utf-8"))
|
|
364
|
+
if isinstance(payload, dict):
|
|
365
|
+
return payload
|
|
366
|
+
except (OSError, json.JSONDecodeError):
|
|
367
|
+
return None
|
|
368
|
+
return None
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def managed_skill_status(destination: Path, kind: str, expected_version: str) -> dict:
|
|
372
|
+
marker = read_skill_marker(destination)
|
|
373
|
+
present = (destination / "SKILL.md").is_file()
|
|
374
|
+
actual_digest = skill_tree_digest(destination) if destination.is_dir() else None
|
|
375
|
+
marker_digest = marker.get("installedDigest") if marker else None
|
|
376
|
+
managed = bool(
|
|
377
|
+
marker
|
|
378
|
+
and marker.get("schemaVersion") == MANAGED_SKILL_SCHEMA
|
|
379
|
+
and marker.get("kind") == kind
|
|
380
|
+
)
|
|
381
|
+
version = marker.get("componentVersion") if marker else None
|
|
382
|
+
return {
|
|
383
|
+
"path": str(destination),
|
|
384
|
+
"present": present,
|
|
385
|
+
"markerPresent": bool(marker),
|
|
386
|
+
"managed": managed,
|
|
387
|
+
"version": version,
|
|
388
|
+
"versionMatches": version == expected_version,
|
|
389
|
+
"integrityValid": bool(marker_digest and actual_digest == marker_digest),
|
|
390
|
+
"valid": bool(present and managed and version == expected_version and marker_digest and actual_digest == marker_digest),
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def write_skill_marker(
|
|
395
|
+
destination: Path,
|
|
396
|
+
*,
|
|
397
|
+
kind: str,
|
|
398
|
+
package_version: str,
|
|
399
|
+
component_version: str,
|
|
400
|
+
source_digest: str,
|
|
401
|
+
archive_sha256: str | None = None,
|
|
402
|
+
) -> dict:
|
|
403
|
+
marker = {
|
|
404
|
+
"schemaVersion": MANAGED_SKILL_SCHEMA,
|
|
405
|
+
"kind": kind,
|
|
406
|
+
"managedBy": "@foggy-projects/deepseek-harness-plugin",
|
|
407
|
+
"packageVersion": package_version,
|
|
408
|
+
"componentVersion": component_version,
|
|
409
|
+
"sourceDigest": source_digest,
|
|
410
|
+
"installedDigest": skill_tree_digest(destination),
|
|
411
|
+
"installedAt": now_utc(),
|
|
412
|
+
}
|
|
413
|
+
if archive_sha256:
|
|
414
|
+
marker["archiveSha256"] = archive_sha256
|
|
415
|
+
atomic_json(destination / MANAGED_SKILL_MARKER, marker)
|
|
416
|
+
legacy = destination / LEGACY_SKILL_MARKER
|
|
417
|
+
if legacy.is_file():
|
|
418
|
+
legacy.unlink()
|
|
419
|
+
return marker
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def backup_skill(destination: Path, backup_root: Path) -> Path:
|
|
423
|
+
backup_root.mkdir(parents=True, exist_ok=True)
|
|
424
|
+
backup = backup_root / f"{destination.name}-{dt.datetime.now().strftime('%Y%m%d-%H%M%S-%f')}"
|
|
425
|
+
shutil.move(str(destination), str(backup))
|
|
426
|
+
return backup
|
|
427
|
+
|
|
428
|
+
|
|
334
429
|
def read_json_object(path: Path, label: str) -> dict:
|
|
335
430
|
if not path.is_file():
|
|
336
431
|
raise OnboardingError(f"{label} not found: {path}")
|
|
@@ -621,22 +716,16 @@ def safe_extract(zip_path: Path, destination: Path) -> None:
|
|
|
621
716
|
archive.extractall(destination)
|
|
622
717
|
|
|
623
718
|
|
|
624
|
-
def install_analysis_skill(
|
|
625
|
-
|
|
719
|
+
def install_analysis_skill(
|
|
720
|
+
zip_path: Path,
|
|
721
|
+
install_root: Path,
|
|
722
|
+
version: str,
|
|
723
|
+
expected_hash: str,
|
|
724
|
+
package_version: str,
|
|
725
|
+
replace: bool,
|
|
726
|
+
) -> dict:
|
|
727
|
+
skills_root = install_root / "skills"
|
|
626
728
|
destination = skills_root / "foggy-ai-analysis"
|
|
627
|
-
marker = destination / ".foggy-onboarding-install.json"
|
|
628
|
-
if destination.exists():
|
|
629
|
-
if marker.is_file():
|
|
630
|
-
installed = json.loads(marker.read_text(encoding="utf-8"))
|
|
631
|
-
if installed.get("archiveSha256") == expected_hash:
|
|
632
|
-
return {"path": str(destination), "version": version, "action": "kept-matching"}
|
|
633
|
-
if not replace:
|
|
634
|
-
raise OnboardingError(f"Analysis Skill already exists and is not managed at {destination}; rerun with --replace-skill to back it up")
|
|
635
|
-
backup_root = project_root / ".foggy" / "onboarding-backups"
|
|
636
|
-
backup_root.mkdir(parents=True, exist_ok=True)
|
|
637
|
-
backup = backup_root / f"foggy-ai-analysis-{dt.datetime.now().strftime('%Y%m%d-%H%M%S')}"
|
|
638
|
-
shutil.move(str(destination), str(backup))
|
|
639
|
-
skills_root.mkdir(parents=True, exist_ok=True)
|
|
640
729
|
with tempfile.TemporaryDirectory(prefix="foggy-skill-") as temporary:
|
|
641
730
|
extract_root = Path(temporary)
|
|
642
731
|
safe_extract(zip_path, extract_root)
|
|
@@ -644,21 +733,41 @@ def install_analysis_skill(zip_path: Path, project_root: Path, version: str, exp
|
|
|
644
733
|
if len(candidates) != 1:
|
|
645
734
|
raise OnboardingError(f"Expected exactly one SKILL.md in analysis Skill archive, found {len(candidates)}")
|
|
646
735
|
source = candidates[0].parent
|
|
736
|
+
source_digest = skill_tree_digest(source)
|
|
737
|
+
if destination.exists():
|
|
738
|
+
installed = read_skill_marker(destination)
|
|
739
|
+
actual_digest = skill_tree_digest(destination)
|
|
740
|
+
marker_matches = bool(
|
|
741
|
+
installed
|
|
742
|
+
and installed.get("schemaVersion") in (MANAGED_SKILL_SCHEMA, "foggy-installed-skill/v1")
|
|
743
|
+
and installed.get("archiveSha256") == expected_hash
|
|
744
|
+
and (installed.get("sourceDigest") or installed.get("installedDigest") or actual_digest) == source_digest
|
|
745
|
+
and actual_digest == source_digest
|
|
746
|
+
)
|
|
747
|
+
if marker_matches:
|
|
748
|
+
marker = write_skill_marker(
|
|
749
|
+
destination,
|
|
750
|
+
kind="analysis",
|
|
751
|
+
package_version=package_version,
|
|
752
|
+
component_version=version,
|
|
753
|
+
source_digest=source_digest,
|
|
754
|
+
archive_sha256=expected_hash,
|
|
755
|
+
)
|
|
756
|
+
return {"path": str(destination), "version": version, "digest": marker["installedDigest"], "managed": True, "action": "kept-matching"}
|
|
757
|
+
if not replace:
|
|
758
|
+
raise OnboardingError(f"Analysis Skill is missing, modified, or outdated at {destination}; use the plugin Repair action to back it up and restore it")
|
|
759
|
+
backup_skill(destination, install_root / "skill-backups")
|
|
760
|
+
skills_root.mkdir(parents=True, exist_ok=True)
|
|
647
761
|
shutil.copytree(source, destination)
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
if destination.exists():
|
|
658
|
-
return {"path": str(destination), "action": "kept-existing"}
|
|
659
|
-
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
660
|
-
shutil.copytree(source, destination)
|
|
661
|
-
return {"path": str(destination), "action": "installed"}
|
|
762
|
+
marker = write_skill_marker(
|
|
763
|
+
destination,
|
|
764
|
+
kind="analysis",
|
|
765
|
+
package_version=package_version,
|
|
766
|
+
component_version=version,
|
|
767
|
+
source_digest=source_digest,
|
|
768
|
+
archive_sha256=expected_hash,
|
|
769
|
+
)
|
|
770
|
+
return {"path": str(destination), "version": version, "digest": marker["installedDigest"], "managed": True, "action": "installed"}
|
|
662
771
|
|
|
663
772
|
|
|
664
773
|
def read_install_state(install_root: Path, required: bool = True) -> dict | None:
|
|
@@ -687,7 +796,6 @@ def install_command(args: argparse.Namespace) -> dict:
|
|
|
687
796
|
versions = load_versions()
|
|
688
797
|
install_root = normalized(args.install_root or default_install_root())
|
|
689
798
|
data_root = normalized(args.data_root or default_data_root())
|
|
690
|
-
project_root = normalized(args.project_root or Path.cwd())
|
|
691
799
|
cache_dirs = [normalized(item) for item in args.asset_cache_dir]
|
|
692
800
|
components = versions["components"]
|
|
693
801
|
assert_managed_root(install_root, "Install root")
|
|
@@ -698,9 +806,9 @@ def install_command(args: argparse.Namespace) -> dict:
|
|
|
698
806
|
"schemaVersion": "foggy-deepseek-onboarding-plan/v1",
|
|
699
807
|
"installRoot": str(install_root),
|
|
700
808
|
"dataRoot": str(data_root),
|
|
701
|
-
"
|
|
809
|
+
"workspaceMode": "dsh-session-cwd",
|
|
702
810
|
"versions": {name: value.get("version") for name, value in components.items()},
|
|
703
|
-
"operations": ["install isolated CLI", "verify Launcher assets", "install
|
|
811
|
+
"operations": ["install isolated CLI", "verify Launcher assets", "install global analysis Skill", "write install state"],
|
|
704
812
|
"productionReady": False,
|
|
705
813
|
}
|
|
706
814
|
if args.dry_run:
|
|
@@ -714,8 +822,6 @@ def install_command(args: argparse.Namespace) -> dict:
|
|
|
714
822
|
progress.update("preflight", 0, "Checking prerequisites")
|
|
715
823
|
if sys.version_info < (3, 11):
|
|
716
824
|
raise OnboardingError(f"Python 3.11+ required, got {sys.version.split()[0]}")
|
|
717
|
-
if not project_root.is_dir():
|
|
718
|
-
raise OnboardingError(f"Project root not found: {project_root}")
|
|
719
825
|
install_root.mkdir(parents=True, exist_ok=True)
|
|
720
826
|
data_root.mkdir(parents=True, exist_ok=True)
|
|
721
827
|
downloads = install_root / "downloads"
|
|
@@ -820,19 +926,26 @@ def install_command(args: argparse.Namespace) -> dict:
|
|
|
820
926
|
zip_asset = next(item for item in analysis_assets if item["role"] == "zip")
|
|
821
927
|
progress.update("analysis-skill", 3, "Installing analysis Skill", fraction=0.9, current_file=zip_asset["file"])
|
|
822
928
|
analysis_skill = install_analysis_skill(
|
|
823
|
-
downloads / "skill" / zip_asset["file"],
|
|
929
|
+
downloads / "skill" / zip_asset["file"], install_root, components["analysisSkill"]["version"],
|
|
930
|
+
zip_asset["sha256"], versions["packageVersion"], args.replace_skill,
|
|
824
931
|
)
|
|
825
932
|
progress.update("analysis-skill", 3, "Analysis Skill ready", fraction=1.0)
|
|
826
|
-
progress.update("workspace-skills", 4, "
|
|
827
|
-
onboarding_skill =
|
|
828
|
-
|
|
933
|
+
progress.update("workspace-skills", 4, "Registering native DSH Skills", fraction=0.1)
|
|
934
|
+
onboarding_skill = {
|
|
935
|
+
"path": str(skill_root()),
|
|
936
|
+
"version": versions["packageVersion"],
|
|
937
|
+
"managed": False,
|
|
938
|
+
"action": "provided-by-plugin",
|
|
939
|
+
"provider": "foggy-managed-skills",
|
|
940
|
+
}
|
|
941
|
+
progress.update("workspace-skills", 4, "Native DSH Skills ready", fraction=1.0)
|
|
829
942
|
state = {
|
|
830
943
|
"schemaVersion": STATE_SCHEMA,
|
|
831
944
|
"installedAt": now_utc(),
|
|
832
945
|
"packageVersion": versions["packageVersion"],
|
|
833
946
|
"installRoot": str(install_root),
|
|
834
947
|
"dataRoot": str(data_root),
|
|
835
|
-
"
|
|
948
|
+
"workspaceMode": "dsh-session-cwd",
|
|
836
949
|
"cli": {"version": cli_component["version"], "command": str(cli_command), "mode": cli_mode},
|
|
837
950
|
"launcher": {"version": components["launcher"]["version"], "path": str(launcher_dir)},
|
|
838
951
|
"skills": {"onboarding": onboarding_skill, "analysis": analysis_skill},
|
|
@@ -850,7 +963,7 @@ def install_command(args: argparse.Namespace) -> dict:
|
|
|
850
963
|
"statePath": str(install_root / "install-state.json"),
|
|
851
964
|
"installRoot": str(install_root),
|
|
852
965
|
"dataRoot": str(data_root),
|
|
853
|
-
"
|
|
966
|
+
"workspaceMode": "dsh-session-cwd",
|
|
854
967
|
"cliVersion": cli_component["version"],
|
|
855
968
|
"launcherVersion": components["launcher"]["version"],
|
|
856
969
|
"analysisSkill": analysis_skill,
|
|
@@ -1112,7 +1225,7 @@ def require_opaque_profile_cli(install_state: dict) -> None:
|
|
|
1112
1225
|
def onboarding_plan_command(args: argparse.Namespace) -> dict:
|
|
1113
1226
|
install_root, install_state, data_root, runtime_state = onboarding_context(args, require_runtime=False)
|
|
1114
1227
|
profile = safe_profile(args.profile)
|
|
1115
|
-
project_root = normalized(args.project_root or
|
|
1228
|
+
project_root = normalized(args.project_root or Path.cwd())
|
|
1116
1229
|
if not project_root.is_dir():
|
|
1117
1230
|
raise OnboardingError(f"Project root not found: {project_root}")
|
|
1118
1231
|
connection_file = normalized(args.connection_file)
|
|
@@ -1772,7 +1885,7 @@ def save_composite_result(evidence_dir: Path, name: str, payload: dict, files: l
|
|
|
1772
1885
|
|
|
1773
1886
|
def datasource_run_command(args: argparse.Namespace) -> dict:
|
|
1774
1887
|
_install_root, install_state, data_root, _runtime_state = onboarding_context(args, require_runtime=True)
|
|
1775
|
-
project_root = normalized(args.project_root or
|
|
1888
|
+
project_root = normalized(args.project_root or Path.cwd())
|
|
1776
1889
|
requested_connection = validate_connection(read_json_object(normalized(args.connection_file), "Connection plan"))
|
|
1777
1890
|
if not requested_connection.get("profile"):
|
|
1778
1891
|
raise OnboardingError("Composite datasource onboarding requires connection.profile in the approved contract")
|
|
@@ -2032,8 +2145,17 @@ def doctor_command(args: argparse.Namespace) -> dict:
|
|
|
2032
2145
|
path = launcher_dir / asset["file"]
|
|
2033
2146
|
launcher_checks.append({"file": asset["file"], "present": path.is_file(), "sha256Valid": path.is_file() and sha256(path) == asset["sha256"]})
|
|
2034
2147
|
launcher_ok = bool(launcher_checks) and all(item["present"] and item["sha256Valid"] for item in launcher_checks)
|
|
2035
|
-
|
|
2036
|
-
|
|
2148
|
+
analysis_skill_root = normalized(state.get("skills", {}).get("analysis", {}).get("path") or install_root / "skills" / "foggy-ai-analysis") if state else install_root / "skills" / "foggy-ai-analysis"
|
|
2149
|
+
onboarding_skill_root = skill_root()
|
|
2150
|
+
analysis_skill = managed_skill_status(analysis_skill_root, "analysis", versions["components"]["analysisSkill"]["version"])
|
|
2151
|
+
onboarding_skill = {
|
|
2152
|
+
"path": str(onboarding_skill_root),
|
|
2153
|
+
"present": (onboarding_skill_root / "SKILL.md").is_file(),
|
|
2154
|
+
"managed": False,
|
|
2155
|
+
"version": versions["packageVersion"],
|
|
2156
|
+
"provider": "foggy-managed-skills",
|
|
2157
|
+
"valid": (onboarding_skill_root / "SKILL.md").is_file(),
|
|
2158
|
+
}
|
|
2037
2159
|
runtime = {"status": "stopped"}
|
|
2038
2160
|
if state:
|
|
2039
2161
|
data_root = normalized(state["dataRoot"])
|
|
@@ -2047,7 +2169,8 @@ def doctor_command(args: argparse.Namespace) -> dict:
|
|
|
2047
2169
|
"java": java_ok,
|
|
2048
2170
|
"cli": cli_ok,
|
|
2049
2171
|
"launcher": launcher_ok,
|
|
2050
|
-
"analysisSkill": analysis_skill
|
|
2172
|
+
"analysisSkill": analysis_skill["valid"],
|
|
2173
|
+
"onboardingSkill": onboarding_skill["valid"],
|
|
2051
2174
|
}
|
|
2052
2175
|
if args.strict_runtime:
|
|
2053
2176
|
required["runtime"] = runtime["status"] == "running"
|
|
@@ -2061,7 +2184,8 @@ def doctor_command(args: argparse.Namespace) -> dict:
|
|
|
2061
2184
|
"java": java,
|
|
2062
2185
|
"cli": cli,
|
|
2063
2186
|
"launcherAssets": launcher_checks,
|
|
2064
|
-
"skills": {"analysis":
|
|
2187
|
+
"skills": {"analysis": analysis_skill, "onboarding": onboarding_skill},
|
|
2188
|
+
"workspace": {"path": str(project_root), "mode": "dsh-session-cwd"},
|
|
2065
2189
|
"runtime": runtime,
|
|
2066
2190
|
"environmentPresence": {name: bool(os.environ.get(name)) for name in ("DEEPSEEK_API_KEY", "ALIYUN_TOKEN_PLAN_API_KEY", "FOGGY_RUNTIME_API_AUTH_CODE", "FOGGY_RUNTIME_AUTHORIZATION")},
|
|
2067
2191
|
"productionReady": False,
|
|
@@ -2077,13 +2201,8 @@ def uninstall_command(args: argparse.Namespace) -> dict:
|
|
|
2077
2201
|
assert_managed_root(data_root, "Data root")
|
|
2078
2202
|
if install_root == data_root:
|
|
2079
2203
|
raise OnboardingError("Install root and data root must be different")
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
skill_targets = [skills_root / name for name in ("foggy-deepseek-onboarding", "foggy-ai-analysis")]
|
|
2083
|
-
if args.remove_skills:
|
|
2084
|
-
for target in skill_targets:
|
|
2085
|
-
if target.exists() and (target.is_symlink() or not is_child(target, skills_root)):
|
|
2086
|
-
raise OnboardingError(f"Refusing to remove unexpected Skill path: {target}")
|
|
2204
|
+
skills_root = install_root / "skills"
|
|
2205
|
+
skill_targets = [skills_root / "foggy-ai-analysis"]
|
|
2087
2206
|
plan = {"installRoot": str(install_root), "dataRoot": str(data_root), "removeSkills": args.remove_skills, "purgeData": args.purge_data}
|
|
2088
2207
|
if args.dry_run:
|
|
2089
2208
|
return {"success": True, "dryRun": True, "plan": plan}
|