@foggy-projects/deepseek-harness-plugin 0.4.0-beta.4 → 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 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.4.tgz
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,27 +20,26 @@ 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, `FOGGY_PROJECT_ROOT` can override the workspace that receives
24
- the Skills and `FOGGY_ASSET_CACHE_DIRS` can contain platform-delimited verified
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
 
30
- ## Managed workspace contract
29
+ ## Native Skill and workspace contract
31
30
 
32
- Initialization writes a non-secret discovery document at
33
- `.foggy/deepseek-harness/context.json` in the selected project. It points agents
34
- to the authoritative global install state, the managed CLI's absolute command,
35
- the Runtime state, and the two workspace Skills. The managed CLI is intentionally
36
- isolated and does not need to be on `PATH`.
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.
37
36
 
38
- Both `foggy-deepseek-onboarding` and `foggy-ai-analysis` carry a
39
- `.foggy-managed-skill.json` marker. **Re-download / Repair** verifies their
40
- managed content, backs up a modified or outdated Skill under
41
- `.foggy/onboarding-backups`, restores missing content, and regenerates the
42
- project context. If a Skill is restored while Harness is already running, start
43
- a new task or restart Harness so its Skill registry can reload it.
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.
44
43
 
45
44
  ## Linux and WSL2 experience
46
45
 
package/lib/client.js CHANGED
@@ -42,7 +42,7 @@ window.__ModuleLoader__.load({
42
42
  launcher: 'Launcher',
43
43
  analysisSkill: '分析 Skill',
44
44
  onboardingSkill: '引导 Skill',
45
- projectContext: '项目安装上下文',
45
+ skillRegistry: 'DSH Skill 注册',
46
46
  python: 'Python',
47
47
  java: 'Java',
48
48
  installed: '已安装',
@@ -59,7 +59,7 @@ window.__ModuleLoader__.load({
59
59
  progressCli: '准备 CLI',
60
60
  progressLauncher: '准备 Launcher',
61
61
  progressAnalysis: '准备分析 Skill',
62
- progressSkills: '安装工作区 Skills',
62
+ progressSkills: '注册 DSH Skills',
63
63
  progressState: '写入安装状态',
64
64
  progressComplete: '初始化完成',
65
65
  progressFiles: '文件',
@@ -69,10 +69,8 @@ window.__ModuleLoader__.load({
69
69
  installRoot: '组件目录',
70
70
  dataRoot: '数据目录',
71
71
  runtimeUrl: 'Runtime 地址',
72
- projectRoot: 'Skill 目标工作区',
73
- contextPath: '对话发现文件',
74
72
  nextTitle: '后续配置',
75
- nextCopy: '对话会从项目内固定的 context.json 读取 CLI、Runtime 与 Skill 安装状态,不再依赖 PATH 猜测。Runtime 启动成功后,将继续进入数据库连接与语义层向导。',
73
+ nextCopy: 'Skills 已通过 DeepSeek Harness 原生注册表提供给所有工作区;每个会话直接使用自己的工作目录。Runtime 启动成功后,将继续进入数据库连接与语义层向导。',
76
74
  beta: 'Beta',
77
75
  }
78
76
 
@@ -92,7 +90,7 @@ window.__ModuleLoader__.load({
92
90
  launcher: 'Launcher',
93
91
  analysisSkill: 'Analysis Skill',
94
92
  onboardingSkill: 'Onboarding Skill',
95
- projectContext: 'Project install context',
93
+ skillRegistry: 'DSH Skill registry',
96
94
  python: 'Python',
97
95
  java: 'Java',
98
96
  installed: 'Installed',
@@ -109,7 +107,7 @@ window.__ModuleLoader__.load({
109
107
  progressCli: 'Preparing CLI',
110
108
  progressLauncher: 'Preparing Launcher',
111
109
  progressAnalysis: 'Preparing analysis Skill',
112
- progressSkills: 'Installing workspace Skills',
110
+ progressSkills: 'Registering DSH Skills',
113
111
  progressState: 'Writing install state',
114
112
  progressComplete: 'Initialization complete',
115
113
  progressFiles: 'files',
@@ -119,10 +117,8 @@ window.__ModuleLoader__.load({
119
117
  installRoot: 'Components',
120
118
  dataRoot: 'Data',
121
119
  runtimeUrl: 'Runtime URL',
122
- projectRoot: 'Skill target workspace',
123
- contextPath: 'Conversation discovery file',
124
120
  nextTitle: 'Next configuration',
125
- nextCopy: 'Conversations discover the installed CLI, Runtime, and Skills from the fixed project context.json instead of guessing from PATH. After Runtime starts, the database connection and semantic-layer wizard comes next.',
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.',
126
122
  beta: 'Beta',
127
123
  }
128
124
 
@@ -285,15 +281,13 @@ window.__ModuleLoader__.load({
285
281
  componentCard(t('launcher'), status.components.launcher, 'runtime', t),
286
282
  componentCard(t('analysisSkill'), status.components.analysisSkill, 'runtime', t),
287
283
  componentCard(t('onboardingSkill'), status.components.onboardingSkill, 'runtime', t),
288
- componentCard(t('projectContext'), status.components.projectContext, 'runtime', t),
284
+ componentCard(t('skillRegistry'), { installed: status.components.onboardingSkill?.provider === 'foggy-managed-skills', version: 'native' }, 'runtime', t),
289
285
  ] }),
290
286
  jsxs('div', { className: 'foggy-paths', children: [
291
287
  jsx('h4', { children: t('roots') }),
292
288
  jsxs('dl', { children: [
293
289
  jsx('dt', { children: t('installRoot') }), jsx('dd', { children: status.roots.installRoot }),
294
290
  jsx('dt', { children: t('dataRoot') }), jsx('dd', { children: status.roots.dataRoot }),
295
- jsx('dt', { children: t('projectRoot') }), jsx('dd', { children: status.projectRoot }),
296
- jsx('dt', { children: t('contextPath') }), jsx('dd', { children: status.contextPath }),
297
291
  status.runtimeUrl ? jsx('dt', { children: t('runtimeUrl') }) : null,
298
292
  status.runtimeUrl ? jsx('dd', { children: status.runtimeUrl }) : null,
299
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,20 +121,26 @@ 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
 
130
139
  async status() {
131
140
  const roots = defaultRoots()
132
- const projectRoot = process.env.FOGGY_PROJECT_ROOT || process.cwd()
133
141
  const statePath = join(roots.installRoot, 'install-state.json')
134
142
  const runtimeStatePath = join(roots.dataRoot, 'runtime-state.json')
135
143
  const progressPath = join(roots.dataRoot, 'operation-progress.json')
136
- const contextPath = join(projectRoot, '.foggy', 'deepseek-harness', 'context.json')
137
144
  const manifest = await readJson(versionsFile)
138
145
  let pythonProbe
139
146
  try {
@@ -145,16 +152,13 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
145
152
  const java = compatible(await commandVersion(process.env.JAVA_EXE || 'java', ['-version']), '17.0')
146
153
  let state = null
147
154
  let runtime = null
148
- let context = null
149
155
  try { state = await readJson(statePath) } catch {}
150
156
  try { runtime = await readJson(runtimeStatePath) } catch {}
151
- try { context = await readJson(contextPath) } catch {}
152
157
  const cliPath = state?.cli?.command
153
158
  const launcherPath = state?.launcher?.path
154
- const analysisSkillPath = state?.skills?.analysis?.path || join(projectRoot, '.agents', 'skills', 'foggy-ai-analysis')
155
- const onboardingSkillPath = state?.skills?.onboarding?.path || join(projectRoot, '.agents', 'skills', 'foggy-deepseek-onboarding')
159
+ const analysisSkillPath = state?.skills?.analysis?.path || join(roots.installRoot, 'skills', 'foggy-ai-analysis')
160
+ const onboardingSkillPath = join(packageRoot, 'skills', 'foggy-deepseek-onboarding')
156
161
  const analysisMarker = await readOptionalJson(join(analysisSkillPath, '.foggy-managed-skill.json'))
157
- const onboardingMarker = await readOptionalJson(join(onboardingSkillPath, '.foggy-managed-skill.json'))
158
162
  const components = {
159
163
  python,
160
164
  java,
@@ -175,28 +179,15 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
175
179
  version: state?.skills?.analysis?.version ?? manifest.components.analysisSkill.version,
176
180
  },
177
181
  onboardingSkill: {
178
- installed: Boolean(
179
- await exists(join(onboardingSkillPath, 'SKILL.md'))
180
- && onboardingMarker?.schemaVersion === 'foggy-managed-skill/v1'
181
- && onboardingMarker?.packageVersion === manifest.packageVersion
182
- ),
183
- version: state?.skills?.onboarding?.version ?? state?.packageVersion ?? manifest.packageVersion,
184
- },
185
- projectContext: {
186
- installed: Boolean(
187
- context?.schemaVersion === 'foggy-deepseek-harness-context/v1'
188
- && context?.packageVersion === manifest.packageVersion
189
- && context?.projectRoot === projectRoot
190
- && context?.installStatePath === statePath
191
- ),
192
- version: context?.schemaVersion ?? 'foggy-deepseek-harness-context/v1',
182
+ installed: await exists(join(onboardingSkillPath, 'SKILL.md')),
183
+ version: manifest.packageVersion,
184
+ provider: 'foggy-managed-skills',
193
185
  },
194
186
  }
195
187
  const installed = components.cli.installed
196
188
  && components.launcher.installed
197
189
  && components.analysisSkill.installed
198
190
  && components.onboardingSkill.installed
199
- && components.projectContext.installed
200
191
  const running = Boolean(runtime && processRunning(Number(runtime.pid)))
201
192
  const progress = await readOptionalJson(progressPath)
202
193
  let operation = operationView(this.operation)
@@ -222,8 +213,6 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
222
213
  running,
223
214
  runtimeUrl: running ? runtime.runtimeUrl ?? null : null,
224
215
  roots,
225
- projectRoot,
226
- contextPath,
227
216
  components,
228
217
  operation,
229
218
  next: installed ? (running ? 'configure-database' : 'start-runtime') : 'initialize',
@@ -236,13 +225,14 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
236
225
  return {
237
226
  success: true,
238
227
  roots,
239
- projectRoot: process.env.FOGGY_PROJECT_ROOT || process.cwd(),
228
+ workspaceMode: 'dsh-session-cwd',
240
229
  versions: Object.fromEntries(Object.entries(manifest.components).map(([name, value]) => [name, value.version])),
241
230
  operations: [
242
231
  'create isolated Python environment',
243
232
  'download and verify pinned CLI and Launcher assets',
244
- 'install Foggy analysis and onboarding Skills into the workspace',
245
- 'write global install state and project-relative discovery context',
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',
246
236
  ],
247
237
  secretsInDshSettings: false,
248
238
  }
@@ -269,7 +259,7 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
269
259
  return { success: false, accepted: false, operation: operationView(this.operation), error: 'another Foggy operation is running' }
270
260
  }
271
261
  const id = `${Date.now()}-${Math.random().toString(16).slice(2, 10)}`
272
- const args = explicitArgs ?? ['install', '--project-root', process.env.FOGGY_PROJECT_ROOT || process.cwd()]
262
+ const args = explicitArgs ?? ['install']
273
263
  if (!explicitArgs) {
274
264
  const roots = defaultRoots()
275
265
  args.push('--progress-file', join(roots.dataRoot, 'operation-progress.json'), '--operation-id', id, '--operation-kind', kind)
@@ -304,6 +294,7 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
304
294
  operation.state = result.success === false ? 'failed' : 'succeeded'
305
295
  operation.result = result
306
296
  operation.finishedAt = new Date().toISOString()
297
+ this.skillProviderControl?.invalidate()
307
298
  if (operation.progress) {
308
299
  operation.progress = {
309
300
  ...operation.progress,
@@ -318,6 +309,7 @@ export class FoggyIntegrationGateway extends TypertRemoteService {
318
309
  operation.state = 'failed'
319
310
  operation.error = String(error.message ?? error)
320
311
  operation.finishedAt = new Date().toISOString()
312
+ this.skillProviderControl?.invalidate()
321
313
  if (operation.progress) {
322
314
  operation.progress = {
323
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.4",
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,22 +7,23 @@ 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
- ## Installed-context gate
11
-
12
- Before deciding that Foggy, its CLI, or either Skill is missing, look for the project-relative file
13
- `.foggy/deepseek-harness/context.json` from the current workspace root:
14
-
15
- - Treat a valid `foggy-deepseek-harness-context/v1` document as the discovery pointer to the global
16
- install state, absolute managed CLI command, Runtime state, and both installed Skills.
17
- - Confirm the installation with this Skill's `doctor` wrapper. The absence of `foggy-runtime` from
18
- `PATH` is not evidence that the managed CLI is missing; the plugin intentionally installs it in an
19
- isolated environment and records its absolute command in the context and install-state files.
20
- - Do not independently download or reinstall the CLI when valid project context exists. If the
21
- context, a managed marker, or a Skill is missing or invalid, ask the user to open the Foggy plugin
22
- settings and use **Re-download / Repair**. That operation regenerates context and restores managed
23
- Skills, preserving a backup of modified or outdated Skill directories.
24
- - If the plugin reports ready but this Skill was restored during the current Harness task, tell the
25
- user that a new task or Harness restart may be needed for the Skill registry to reload.
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.
26
27
 
27
28
  ## Mandatory orchestration boundary
28
29
 
@@ -59,14 +60,14 @@ For every new-database onboarding session, this Skill is the orchestration autho
59
60
 
60
61
  ## Workflow
61
62
 
62
- 1. Read `.foggy/deepseek-harness/context.json`, then run `scripts/doctor.ps1 --project-root <root>` on Windows or
63
- `bash scripts/doctor.sh --project-root <root>` on Linux.
64
- 2. If the pinned CLI, Launcher, project context, or either managed Skill is missing, use the Foggy
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
65
66
  plugin's Repair action. Use the matching install script only when the plugin UI is unavailable;
66
67
  use `--dry-run` first when paths or permissions are uncertain.
67
68
  3. Run `runtime-start` and require successful `wait-ready` plus `capabilities`. Record engine,
68
69
  Runtime API version, schema version, security mode, URL, namespace, PID, and evidence path.
69
- 4. Confirm the project contains `.agents/skills/foggy-ai-analysis/SKILL.md`.
70
+ 4. Load `foggy-ai-analysis` from the native DSH Skill registry. Do not require a workspace copy.
70
71
  5. For a new business database, read [references/onboarding-workflow.md](references/onboarding-workflow.md)
71
72
  and prefer its two composite `onboard-datasource-run` / `onboard-semantic-run` commands. Require the
72
73
  trusted operator to create the private CLI profile outside Harness. Accept only the opaque profile
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": "foggy-deepseek-onboarding-versions/v1",
3
- "packageVersion": "0.4.0-beta.4",
4
- "validatedAt": "2026-08-28",
3
+ "packageVersion": "0.4.0-beta.5",
4
+ "validatedAt": "2026-09-01",
5
5
  "components": {
6
6
  "deepseekHarness": {
7
7
  "version": "0.1.1-rc.2",
@@ -25,7 +25,6 @@ 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
- CONTEXT_SCHEMA = "foggy-deepseek-harness-context/v1"
29
28
  MANAGED_SKILL_SCHEMA = "foggy-managed-skill/v1"
30
29
  MANAGED_SKILL_MARKER = ".foggy-managed-skill.json"
31
30
  LEGACY_SKILL_MARKER = ".foggy-onboarding-install.json"
@@ -420,60 +419,13 @@ def write_skill_marker(
420
419
  return marker
421
420
 
422
421
 
423
- def backup_skill(destination: Path, project_root: Path) -> Path:
424
- backup_root = project_root / ".foggy" / "onboarding-backups"
422
+ def backup_skill(destination: Path, backup_root: Path) -> Path:
425
423
  backup_root.mkdir(parents=True, exist_ok=True)
426
424
  backup = backup_root / f"{destination.name}-{dt.datetime.now().strftime('%Y%m%d-%H%M%S-%f')}"
427
425
  shutil.move(str(destination), str(backup))
428
426
  return backup
429
427
 
430
428
 
431
- def project_context_path(project_root: Path) -> Path:
432
- return project_root / ".foggy" / "deepseek-harness" / "context.json"
433
-
434
-
435
- def project_relative(path: Path, project_root: Path) -> str:
436
- try:
437
- return path.resolve(strict=False).relative_to(project_root.resolve(strict=False)).as_posix()
438
- except ValueError:
439
- return str(path)
440
-
441
-
442
- def write_project_context(state: dict) -> Path:
443
- project_root = normalized(state["projectRoot"])
444
- install_root = normalized(state["installRoot"])
445
- data_root = normalized(state["dataRoot"])
446
- context_path = project_context_path(project_root)
447
- skills = {}
448
- for kind in ("onboarding", "analysis"):
449
- item = state["skills"][kind]
450
- skill_path = normalized(item["path"])
451
- skills[kind] = {
452
- "version": item.get("version"),
453
- "path": project_relative(skill_path, project_root),
454
- "absolutePath": str(skill_path),
455
- "markerPath": project_relative(skill_path / MANAGED_SKILL_MARKER, project_root),
456
- "managed": bool(item.get("managed")),
457
- }
458
- payload = {
459
- "schemaVersion": CONTEXT_SCHEMA,
460
- "managedBy": "@foggy-projects/deepseek-harness-plugin",
461
- "packageVersion": state["packageVersion"],
462
- "generatedAt": now_utc(),
463
- "projectRoot": str(project_root),
464
- "installStatePath": str(install_root / "install-state.json"),
465
- "runtimeStatePath": str(data_root / "runtime-state.json"),
466
- "operationProgressPath": str(data_root / "operation-progress.json"),
467
- "cli": state["cli"],
468
- "launcher": state["launcher"],
469
- "skills": skills,
470
- "securityMode": state["securityMode"],
471
- "productionReady": False,
472
- }
473
- atomic_json(context_path, payload)
474
- return context_path
475
-
476
-
477
429
  def read_json_object(path: Path, label: str) -> dict:
478
430
  if not path.is_file():
479
431
  raise OnboardingError(f"{label} not found: {path}")
@@ -766,13 +718,13 @@ def safe_extract(zip_path: Path, destination: Path) -> None:
766
718
 
767
719
  def install_analysis_skill(
768
720
  zip_path: Path,
769
- project_root: Path,
721
+ install_root: Path,
770
722
  version: str,
771
723
  expected_hash: str,
772
724
  package_version: str,
773
725
  replace: bool,
774
726
  ) -> dict:
775
- skills_root = project_root / ".agents" / "skills"
727
+ skills_root = install_root / "skills"
776
728
  destination = skills_root / "foggy-ai-analysis"
777
729
  with tempfile.TemporaryDirectory(prefix="foggy-skill-") as temporary:
778
730
  extract_root = Path(temporary)
@@ -804,7 +756,7 @@ def install_analysis_skill(
804
756
  return {"path": str(destination), "version": version, "digest": marker["installedDigest"], "managed": True, "action": "kept-matching"}
805
757
  if not replace:
806
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")
807
- backup_skill(destination, project_root)
759
+ backup_skill(destination, install_root / "skill-backups")
808
760
  skills_root.mkdir(parents=True, exist_ok=True)
809
761
  shutil.copytree(source, destination)
810
762
  marker = write_skill_marker(
@@ -818,46 +770,6 @@ def install_analysis_skill(
818
770
  return {"path": str(destination), "version": version, "digest": marker["installedDigest"], "managed": True, "action": "installed"}
819
771
 
820
772
 
821
- def install_onboarding_skill(project_root: Path, package_version: str, replace: bool) -> dict:
822
- destination = project_root / ".agents" / "skills" / "foggy-deepseek-onboarding"
823
- source = skill_root()
824
- source_digest = skill_tree_digest(source)
825
- if source.resolve() == destination.resolve(strict=False):
826
- return {"path": str(destination), "version": package_version, "digest": source_digest, "managed": True, "action": "already-running-from-target"}
827
- if destination.exists():
828
- marker = read_skill_marker(destination)
829
- actual_digest = skill_tree_digest(destination)
830
- if (
831
- marker
832
- and marker.get("schemaVersion") == MANAGED_SKILL_SCHEMA
833
- and marker.get("kind") == "onboarding"
834
- and actual_digest == source_digest
835
- and marker.get("sourceDigest") in (None, source_digest)
836
- and marker.get("installedDigest") in (None, source_digest)
837
- ):
838
- written = write_skill_marker(
839
- destination,
840
- kind="onboarding",
841
- package_version=package_version,
842
- component_version=package_version,
843
- source_digest=source_digest,
844
- )
845
- return {"path": str(destination), "version": package_version, "digest": written["installedDigest"], "managed": True, "action": "kept-matching"}
846
- if not replace:
847
- raise OnboardingError(f"Onboarding Skill is missing, modified, or outdated at {destination}; use the plugin Repair action to back it up and restore it")
848
- backup_skill(destination, project_root)
849
- destination.parent.mkdir(parents=True, exist_ok=True)
850
- shutil.copytree(source, destination)
851
- marker = write_skill_marker(
852
- destination,
853
- kind="onboarding",
854
- package_version=package_version,
855
- component_version=package_version,
856
- source_digest=source_digest,
857
- )
858
- return {"path": str(destination), "version": package_version, "digest": marker["installedDigest"], "managed": True, "action": "installed"}
859
-
860
-
861
773
  def read_install_state(install_root: Path, required: bool = True) -> dict | None:
862
774
  path = install_root / "install-state.json"
863
775
  if not path.is_file():
@@ -884,7 +796,6 @@ def install_command(args: argparse.Namespace) -> dict:
884
796
  versions = load_versions()
885
797
  install_root = normalized(args.install_root or default_install_root())
886
798
  data_root = normalized(args.data_root or default_data_root())
887
- project_root = normalized(args.project_root or Path.cwd())
888
799
  cache_dirs = [normalized(item) for item in args.asset_cache_dir]
889
800
  components = versions["components"]
890
801
  assert_managed_root(install_root, "Install root")
@@ -895,9 +806,9 @@ def install_command(args: argparse.Namespace) -> dict:
895
806
  "schemaVersion": "foggy-deepseek-onboarding-plan/v1",
896
807
  "installRoot": str(install_root),
897
808
  "dataRoot": str(data_root),
898
- "projectRoot": str(project_root),
809
+ "workspaceMode": "dsh-session-cwd",
899
810
  "versions": {name: value.get("version") for name, value in components.items()},
900
- "operations": ["install isolated CLI", "verify Launcher assets", "install project Skills", "write install state"],
811
+ "operations": ["install isolated CLI", "verify Launcher assets", "install global analysis Skill", "write install state"],
901
812
  "productionReady": False,
902
813
  }
903
814
  if args.dry_run:
@@ -911,8 +822,6 @@ def install_command(args: argparse.Namespace) -> dict:
911
822
  progress.update("preflight", 0, "Checking prerequisites")
912
823
  if sys.version_info < (3, 11):
913
824
  raise OnboardingError(f"Python 3.11+ required, got {sys.version.split()[0]}")
914
- if not project_root.is_dir():
915
- raise OnboardingError(f"Project root not found: {project_root}")
916
825
  install_root.mkdir(parents=True, exist_ok=True)
917
826
  data_root.mkdir(parents=True, exist_ok=True)
918
827
  downloads = install_root / "downloads"
@@ -1017,21 +926,26 @@ def install_command(args: argparse.Namespace) -> dict:
1017
926
  zip_asset = next(item for item in analysis_assets if item["role"] == "zip")
1018
927
  progress.update("analysis-skill", 3, "Installing analysis Skill", fraction=0.9, current_file=zip_asset["file"])
1019
928
  analysis_skill = install_analysis_skill(
1020
- downloads / "skill" / zip_asset["file"], project_root, components["analysisSkill"]["version"],
929
+ downloads / "skill" / zip_asset["file"], install_root, components["analysisSkill"]["version"],
1021
930
  zip_asset["sha256"], versions["packageVersion"], args.replace_skill,
1022
931
  )
1023
932
  progress.update("analysis-skill", 3, "Analysis Skill ready", fraction=1.0)
1024
- progress.update("workspace-skills", 4, "Installing onboarding Skill", fraction=0.1)
1025
- onboarding_skill = install_onboarding_skill(project_root, versions["packageVersion"], args.replace_skill)
1026
- progress.update("workspace-skills", 4, "Workspace Skills ready", fraction=1.0)
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)
1027
942
  state = {
1028
943
  "schemaVersion": STATE_SCHEMA,
1029
944
  "installedAt": now_utc(),
1030
945
  "packageVersion": versions["packageVersion"],
1031
946
  "installRoot": str(install_root),
1032
947
  "dataRoot": str(data_root),
1033
- "projectRoot": str(project_root),
1034
- "contextPath": str(project_context_path(project_root)),
948
+ "workspaceMode": "dsh-session-cwd",
1035
949
  "cli": {"version": cli_component["version"], "command": str(cli_command), "mode": cli_mode},
1036
950
  "launcher": {"version": components["launcher"]["version"], "path": str(launcher_dir)},
1037
951
  "skills": {"onboarding": onboarding_skill, "analysis": analysis_skill},
@@ -1041,8 +955,6 @@ def install_command(args: argparse.Namespace) -> dict:
1041
955
  }
1042
956
  progress.update("state", 5, "Writing install state", fraction=0.2, current_file="install-state.json")
1043
957
  atomic_json(install_root / "install-state.json", state)
1044
- progress.update("state", 5, "Writing project context", fraction=0.7, current_file=".foggy/deepseek-harness/context.json")
1045
- context_path = write_project_context(state)
1046
958
  progress.finish()
1047
959
  ACTIVE_PROGRESS = None
1048
960
  return {
@@ -1051,8 +963,7 @@ def install_command(args: argparse.Namespace) -> dict:
1051
963
  "statePath": str(install_root / "install-state.json"),
1052
964
  "installRoot": str(install_root),
1053
965
  "dataRoot": str(data_root),
1054
- "projectRoot": str(project_root),
1055
- "contextPath": str(context_path),
966
+ "workspaceMode": "dsh-session-cwd",
1056
967
  "cliVersion": cli_component["version"],
1057
968
  "launcherVersion": components["launcher"]["version"],
1058
969
  "analysisSkill": analysis_skill,
@@ -1314,7 +1225,7 @@ def require_opaque_profile_cli(install_state: dict) -> None:
1314
1225
  def onboarding_plan_command(args: argparse.Namespace) -> dict:
1315
1226
  install_root, install_state, data_root, runtime_state = onboarding_context(args, require_runtime=False)
1316
1227
  profile = safe_profile(args.profile)
1317
- project_root = normalized(args.project_root or install_state["projectRoot"])
1228
+ project_root = normalized(args.project_root or Path.cwd())
1318
1229
  if not project_root.is_dir():
1319
1230
  raise OnboardingError(f"Project root not found: {project_root}")
1320
1231
  connection_file = normalized(args.connection_file)
@@ -1974,7 +1885,7 @@ def save_composite_result(evidence_dir: Path, name: str, payload: dict, files: l
1974
1885
 
1975
1886
  def datasource_run_command(args: argparse.Namespace) -> dict:
1976
1887
  _install_root, install_state, data_root, _runtime_state = onboarding_context(args, require_runtime=True)
1977
- project_root = normalized(args.project_root or install_state["projectRoot"])
1888
+ project_root = normalized(args.project_root or Path.cwd())
1978
1889
  requested_connection = validate_connection(read_json_object(normalized(args.connection_file), "Connection plan"))
1979
1890
  if not requested_connection.get("profile"):
1980
1891
  raise OnboardingError("Composite datasource onboarding requires connection.profile in the approved contract")
@@ -2234,24 +2145,17 @@ def doctor_command(args: argparse.Namespace) -> dict:
2234
2145
  path = launcher_dir / asset["file"]
2235
2146
  launcher_checks.append({"file": asset["file"], "present": path.is_file(), "sha256Valid": path.is_file() and sha256(path) == asset["sha256"]})
2236
2147
  launcher_ok = bool(launcher_checks) and all(item["present"] and item["sha256Valid"] for item in launcher_checks)
2237
- analysis_skill_root = project_root / ".agents" / "skills" / "foggy-ai-analysis"
2238
- onboarding_skill_root = project_root / ".agents" / "skills" / "foggy-deepseek-onboarding"
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()
2239
2150
  analysis_skill = managed_skill_status(analysis_skill_root, "analysis", versions["components"]["analysisSkill"]["version"])
2240
- onboarding_skill = managed_skill_status(onboarding_skill_root, "onboarding", versions["packageVersion"])
2241
- context_path = project_context_path(project_root)
2242
- context = None
2243
- if context_path.is_file():
2244
- try:
2245
- context = json.loads(context_path.read_text(encoding="utf-8"))
2246
- except (OSError, json.JSONDecodeError):
2247
- context = None
2248
- context_ok = bool(
2249
- context
2250
- and context.get("schemaVersion") == CONTEXT_SCHEMA
2251
- and context.get("packageVersion") == versions["packageVersion"]
2252
- and normalized(context.get("projectRoot", "")) == project_root
2253
- and normalized(context.get("installStatePath", "")) == install_root / "install-state.json"
2254
- )
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
+ }
2255
2159
  runtime = {"status": "stopped"}
2256
2160
  if state:
2257
2161
  data_root = normalized(state["dataRoot"])
@@ -2267,7 +2171,6 @@ def doctor_command(args: argparse.Namespace) -> dict:
2267
2171
  "launcher": launcher_ok,
2268
2172
  "analysisSkill": analysis_skill["valid"],
2269
2173
  "onboardingSkill": onboarding_skill["valid"],
2270
- "projectContext": context_ok,
2271
2174
  }
2272
2175
  if args.strict_runtime:
2273
2176
  required["runtime"] = runtime["status"] == "running"
@@ -2282,7 +2185,7 @@ def doctor_command(args: argparse.Namespace) -> dict:
2282
2185
  "cli": cli,
2283
2186
  "launcherAssets": launcher_checks,
2284
2187
  "skills": {"analysis": analysis_skill, "onboarding": onboarding_skill},
2285
- "projectContext": {"path": str(context_path), "valid": context_ok},
2188
+ "workspace": {"path": str(project_root), "mode": "dsh-session-cwd"},
2286
2189
  "runtime": runtime,
2287
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")},
2288
2191
  "productionReady": False,
@@ -2298,13 +2201,8 @@ def uninstall_command(args: argparse.Namespace) -> dict:
2298
2201
  assert_managed_root(data_root, "Data root")
2299
2202
  if install_root == data_root:
2300
2203
  raise OnboardingError("Install root and data root must be different")
2301
- project_root = normalized(state["projectRoot"])
2302
- skills_root = project_root / ".agents" / "skills"
2303
- skill_targets = [skills_root / name for name in ("foggy-deepseek-onboarding", "foggy-ai-analysis")]
2304
- if args.remove_skills:
2305
- for target in skill_targets:
2306
- if target.exists() and (target.is_symlink() or not is_child(target, skills_root)):
2307
- raise OnboardingError(f"Refusing to remove unexpected Skill path: {target}")
2204
+ skills_root = install_root / "skills"
2205
+ skill_targets = [skills_root / "foggy-ai-analysis"]
2308
2206
  plan = {"installRoot": str(install_root), "dataRoot": str(data_root), "removeSkills": args.remove_skills, "purgeData": args.purge_data}
2309
2207
  if args.dry_run:
2310
2208
  return {"success": True, "dryRun": True, "plan": plan}