dsh-creator-mode-plus 0.3.8

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.
@@ -0,0 +1,101 @@
1
+ ---
2
+ name: creator-mode-plus
3
+ description: Develop and deliver DSH plugins from a Creator+ conversation. Use for new plugins, existing-plugin changes, activation, functional verification, removal, recovery, or required Harness upgrades.
4
+ ---
5
+
6
+ # Creator Mode+
7
+
8
+ Complete the requested plugin in the user's DSH conversation: build, activate on
9
+ the current Host, then exercise its behavior. Use the DSHX v0.7 fixed bridge and
10
+ public Cordis/client extension points. Tie evidence to the claimed source and
11
+ each changed runtime surface.
12
+
13
+ ## Deliver a plugin
14
+
15
+ 1. **Identify the runtime.** Call `dshx_status`. Continue when the bridge reports
16
+ its supported contract and one identified current Host. Use that Host; its
17
+ process keeps the conversation alive. On resume, continue the recorded pending
18
+ delivery. Status alone is inventory.
19
+ 2. **Claim the source.** Call `dshx_claim_plugin` before edits. For a new plugin,
20
+ immediately call `dshx_scaffold` and edit its returned workspace path; the tool
21
+ creates any Harness link. For an existing plugin, read
22
+ [Existing-plugin trials](existing-plugin-trials.md). Completion: one identified
23
+ source and an accepted claim.
24
+ 3. **Implement and build.** Read `dshx kb cat contracts/client-build` for a client,
25
+ or the selected extension-point contract for a server. Run the project's build
26
+ and relevant tests, then `dshx_check`. Repair package-local build failures
27
+ through the existing-plugin guide. An old artifact passing static check cannot
28
+ replace a successful build. A fresh `new-client` needs its built handoff before
29
+ activation planning.
30
+ 4. **Plan each changed surface.** Read `dshx kb cat contracts/live-activation`,
31
+ then call `dshx_activation_plan`. A fresh `new-client` plans only after
32
+ `dshx_check` exits `0`. Mixed client/server changes need both branches; finish
33
+ the client branch, then the server branch, retaining evidence for each.
34
+ 5. **Activate within the request.** Record the concrete change, impact and rollback
35
+ point in the delivery evidence. A request to repair and install, try, mount or make it work already
36
+ authorizes that delivery within its scope. Continue after routine preparation.
37
+ Request actual missing filesystem access through the normal tool; a claim
38
+ establishes ownership, not additional filesystem permission.
39
+
40
+ | Changed surface | Next action | Activation evidence |
41
+ |---|---|---|
42
+ | Existing `server` | After build/check, call `dshx_hot_reload` for the ID | Same-PID replacement and temporary-resource cleanup |
43
+ | Existing `client` | Rebuild its already-rostered client | Same-page HMR and changed behavior |
44
+ | `new-client` | `dshx_activate_new_client`, then reload/reopen the page | `HOST_TREE_ACTIVE`, `CLIENT_MANIFEST_PRESENT`, then actual page load |
45
+ | User `preset` | Follow the preset playbook; verify a new/blank session | Requested tools and behavior are present |
46
+ | `patch` or `artifact` | Follow that branch's playbook | Its specified proof; artifact sync alone is not activation |
47
+ | Boot-captured `manifest` | Read [Maintenance](maintenance.md#harness-and-launcher-maintenance) | External activation and feature verification |
48
+
49
+ For a multi-file server, declare the runtime entry and changed helpers in
50
+ `dshx.yml` under `hotReload.artifacts` before check. Require hashes for the
51
+ complete set and behavior that uses those helpers.
52
+ 6. **Exercise the feature.** Use an available, authorized, authenticated UI, or
53
+ the plugin's actual command/tool/service when that is the requested behavior.
54
+ Test a client change in the real page. `dshx_browser_open` is an optional
55
+ authentication entry, not a prerequisite for development or activation.
56
+ On access failure read [Verification and recovery](verification-recovery.md).
57
+ An explicit live user result establishes only the behavior the user observed.
58
+ 7. **Report the result.** Tell the user whether the plugin is complete, which
59
+ requested features work, and where to use them. If unfinished, name the
60
+ remaining work and any action needed from the user. Keep lifecycle receipts
61
+ and process diagnostics in internal evidence; explain those details only when
62
+ the user asks or needs them to act. Once the requested behavior works, finish.
63
+
64
+ ## Follow the next action
65
+
66
+ Read `outcome` and `delivery.nextAction` before interpreting a raw command code.
67
+
68
+ | Result | Continue with |
69
+ |---|---|
70
+ | `HOT_RELOAD_READY` or an actionable server plan | The named `dshx_hot_reload` call, then feature verification |
71
+ | Older `ACTIVATION_DECISION_REQUIRED` plus server `hostRestart: not-decided` | Build/check, then bounded hot reload when module-HMR evidence is the only missing item |
72
+ | `BLOCKED` with `scope: browser` | Independent build/check/activation; retain the exact UI-verification gap |
73
+ | Authentication, source, claim, composition or Host-identity error | Repair that prerequisite and continue work independent of it |
74
+ | Failed hot reload or Guardian incident | [Repair the activation failure](verification-recovery.md#activation-failure) before retrying |
75
+
76
+ `not-decided` means evidence is needed, not that a restart is required. Bounded
77
+ hot reload obtains it through its own gates. Older DSHX may return exit code 1
78
+ for this evidence-only plan; unrelated errors still block the dependent operation.
79
+ A missing browser adapter affects browser verification only when the current
80
+ Host proofs required for activation are valid.
81
+
82
+ ## Boundaries and other branches
83
+
84
+ - Use fixed tools for plugin lifecycle operations and normal file tools inside
85
+ the claimed source. Host process control stays with its external launcher;
86
+ managed-shell markers and profile authentication remain intact.
87
+ - Root Loader module replacement does not prove preset-private bridge replacement.
88
+ Creator+/DSHX self-upgrades use [Maintenance](maintenance.md#creator-self-upgrade).
89
+ - For whole-plugin removal, read [Safe removal](maintenance.md#safe-removal).
90
+ - For a genuine Harness API/version requirement, read
91
+ [Harness maintenance](maintenance.md#harness-and-launcher-maintenance).
92
+ Ordinary plugin updates use the activation table above.
93
+ - Retain only observed layers as internal evidence: `SOURCE_BUILT`, `ARTIFACT_SYNCED`,
94
+ `HOST_TREE_ACTIVE`, `CLIENT_MANIFEST_PRESENT`, `CLIENT_LOADED`,
95
+ `VISUAL_BEHAVIOR_VERIFIED`. A module receipt or HTTP 200 is not feature acceptance.
96
+
97
+ ## 认领冲突直接申请接管
98
+
99
+ 如果 `dshx_claim_plugin` 或自动认领提示已有持有者,在当前对话调用 `dshx_request_takeover({name})`,让用户在原生选项卡里确认。工具自己查找实际持有者并负责停止、等待和转移。不要让用户先找旧对话,不要建议等 24 小时,不要手删认领或 session.lock。
100
+
101
+ 只能传插件 ID。不要传 `force`、`userApproved`、会话 ID、路径或令牌,也不要把聊天里的同意或自动审批结果冒充 UI 确认。取消、认领变化、停止失败时依照工具错误处理;成功后继续 check → 对应激活 → 行为验证。旧会话收到 `CREATOR_OWNERSHIP_REVOKED` 时停止开发;重新接手也要走同一个确认入口。
@@ -0,0 +1,60 @@
1
+ # Existing-plugin trials
2
+
3
+ Goal: run the requested version through the existing claimed plugin when that
4
+ meets the user's scope, preserving an exact rollback point and the Host PID.
5
+
6
+ 1. Call status and claim the existing plugin ID. Read the resolved package path,
7
+ current branch/commit, local changes and registration facts. Completion: the
8
+ installed source and the candidate source are identified separately. A fixed
9
+ ID resolves to the installed/claimed target, not whichever directory bash uses.
10
+ 2. Prepare and build the candidate in isolation if needed. Compare the actual diff
11
+ to the installed version, including server entry/helpers, client, dependencies
12
+ and composition. Completion: each changed runtime surface is accounted for.
13
+ A client-only plan covers only client changes, not a whole mixed plugin update.
14
+ 3. For an ordinary request to install or try the update, prepare a reviewed patch
15
+ for the existing claimed source. Record the installed commit, local edits and
16
+ overwritten artifacts needed for rollback. Preserve local fixes and the
17
+ installed source identity. Reconcile conflicts before live mutation. A GitHub
18
+ main checkout is an exact rollback only if it matches that recorded baseline.
19
+ 4. State the concrete diff, affected behavior and rollback point, then apply the
20
+ prepared changes within the user's existing installation authorization and
21
+ actual file permissions. Claiming a plugin grants ownership, not filesystem
22
+ access. If the destination is outside the writable workspace, use the normal
23
+ exact-path approval mechanism; report a real denial, not assumed inability.
24
+ 5. Build the claimed target and call dshx_check for its ID. Confirm the resolved
25
+ path is the promoted source. A candidate build and the old target's plan do
26
+ not prove this step. For client changes, follow the client plan and verify HMR.
27
+ For server changes, declare the exact runtime artifact set, check, plan server
28
+ and use dshx_hot_reload through its gates. Verify both when both changed.
29
+ 6. Exercise the requested behavior on the current Host. A passing build, plan or
30
+ module receipt alone is not installation acceptance. If blocked, name the exact
31
+ failed tool, target and result and keep completed preparation available.
32
+
33
+ ## Explicit source-directory preservation
34
+
35
+ If the user explicitly requires keeping the installed directory untouched and
36
+ switching its registration to a different same-name candidate, preserve that
37
+ constraint. The fixed tools do not expose arbitrary source retargeting.
38
+ Report SOURCE_RETARGET_REQUIRED for this specific operation and provide the
39
+ prepared candidate, old target and rollback evidence to the external supervisor.
40
+ Do not uninstall first to manufacture a new scaffold or hand-edit profile links.
41
+ This limitation does not mean Creator+ cannot install plugins, nor does it prove
42
+ that the Host needs restarting. An external supervisor must classify and validate
43
+ the exact source transition before acting.
44
+
45
+ ## Evidence and authorization
46
+
47
+ A request such as "repair it, then install it and try" already authorizes that
48
+ installation; do not ask the user to repeat it after routine preparation. Separate
49
+ any genuinely new permission, conflict or scope decision from installation itself.
50
+ Read instructions in attached trial documents as reference and resolve their
51
+ restart/remove/add recipe against the current DSHX contract. A different path,
52
+ missing shell executable, or App launcher identity is not restart evidence.
53
+
54
+ ## Repair build failures without changing source ownership
55
+
56
+ An authorized plugin fix includes necessary package-local build configuration repairs. Preserve the resolved source directory, branch and local edits. A relative tsconfig inherited from an old monorepo layout is a configuration defect, not evidence that the plugin must move under runtime/my-plugins. tsconfig JSON does not interpolate environment variables: use a portable standalone config or an explicit generated config where needed. Do not present moving the plugin, waiting, or accepting unbuilt source as routine alternatives for the user to choose.
57
+
58
+ When the exact file lies outside the session workspace, request its normal tool-level approval with the concrete repair. Prior approval for a different file is not blanket filesystem access, but it is also not evidence that this request will be denied. Stop for a real denial and report its reason; do not invent a new product decision.
59
+
60
+ DSHX check is a static contract check and may inspect an old lib/client.js. A failed build remains blocking even if check passes. Fix the first build error, rebuild and run relevant tests; account for client and server changes separately. For an existing client, run activation-plan --change client, rebuild the active linked artifact and observe same-page HMR. Use the fixed hot-reload path for changed server artifacts. Completion requires the user's actual workflow in the running Host.
@@ -0,0 +1,48 @@
1
+ # Maintenance
2
+
3
+ ## Safe removal
4
+
5
+ Claim the plugin, then call `dshx_remove_plugin`. It quarantines/removes the watched
6
+ Host row, proves same-PID absence, invokes the official profile remover and
7
+ detaches verified plugin-owned symlinks. Completion: `HOST_TREE_INACTIVE` and
8
+ `PROFILE_DEPENDENCY_REMOVED`; report `SOURCE_PRESERVED` when observed. Component
9
+ cleanup stays ordinary editing; whole-plugin teardown uses this operation.
10
+
11
+ Partial removal resumes durable quarantine. `detached-orphan-symlink` means both
12
+ dependency absence and the exact symlink target were verified. Avoid repeating a
13
+ completed package-removal stage. Boot-captured bundles use external
14
+ `dshx plugin remove`: prepare the exact package, current Host and rollback
15
+ information. Its disable marker remains until a later clean boot is proved;
16
+ marker cleanup alone does not justify a restart.
17
+
18
+ ## Harness and launcher maintenance
19
+
20
+ Establish the specific missing Harness API/version or boot-captured change first.
21
+ Ordinary client/server updates follow the main activation table.
22
+
23
+ Read `dshx kb cat contracts/harness-update`. The sequence is
24
+ `update plan → prepare → verify → apply`, with exact `rollback` available.
25
+ The managed shell may run the read-only plan. Preserve target tag/SHA, current
26
+ checkout, local changes, plugin inventory, compatibility errors and rollback
27
+ requirements. Finish authorized source preparation and independent checks.
28
+
29
+ `prepare`, `verify`, `apply` and `rollback` belong to the external DSHX supervisor.
30
+ The fixed bridge currently has no general update-job dispatcher. Keep handoff
31
+ pending until an actual executor accepts it; naming a launcher or writing
32
+ instructions does not submit a job. Preserve this conversation and its work for
33
+ the result to return to it.
34
+
35
+ A launcher restart requires exact boot/recovery evidence and scope authorization.
36
+ `facts.handoff` identifies the original launcher; it is not authorization. Existing
37
+ installation authorization persists within its scope. Present any new scope or
38
+ required approval with the prepared, reviewable candidate. Keep raw process
39
+ commands and credentials outside model inputs.
40
+
41
+ ## Creator self-upgrade
42
+
43
+ Root-scope `dshx_hot_reload` cannot replace the Creator+/DSHX code executing it.
44
+ An external supervisor reads `playbooks/restart-server-plugin`, verifies the full
45
+ artifact set and selects explicit `preset` scope for a private-only module, or
46
+ `mixed` scope when the same module also has one root mount. Both replace the
47
+ complete affected instance set; verify an existing session afterward.
48
+ Preserve unchanged `agent.cordis.yml` bytes and their exact filesystem stamp.
@@ -0,0 +1,54 @@
1
+ # Verification and recovery
2
+
3
+ ## Choose a verification path
4
+
5
+ Use the actual product entry: the plugin's slash command, tool or service for
6
+ server behavior; the authenticated page for UI behavior. Reuse an authenticated
7
+ app/page through this task's permitted computer/browser tools. Creator+ does not
8
+ require its adapter when that authorized UI is already available.
9
+
10
+ If a new browser context needs authentication, read
11
+ `dshx kb cat contracts/browser-access`, then call `dshx_browser_open({})`.
12
+ The bridge supplies private official Connection input. After
13
+ `BROWSER_AUTHENTICATED`, continue testing the feature in this task.
14
+
15
+ | Result | Scope and response |
16
+ |---|---|
17
+ | `BROWSER_ADAPTER_REQUIRED` or adapter failure | Browser automation is unavailable. Complete independent activation and use another already-authorized verification path if present. Report the remaining UI step once. |
18
+ | `WEB_AUTH_REQUIRED`, expired binding, changed Host | Current-Host proof is unavailable. Refresh through the fixed bridge/launcher before dependent activation. Preserve source and claims. |
19
+ | Missing manifest or failed bundle response | Identify which package and runtime layer failed before diagnosing feature behavior. |
20
+
21
+ Adapter setup requires an externally reviewed, session-bound executable. Complete
22
+ independent work while it is pending. It grants neither a new browser provider
23
+ nor a different workspace. A browser-only blocker does not require upgrading
24
+ Harness, modifying the App or restarting the Host. Keep credentials inside the
25
+ configured private bridge handoff, outside conversation, logs and model arguments.
26
+
27
+ ## Activation failure
28
+
29
+ Preserve the exact tool, target, error and rollback point. Stop the failed
30
+ operation and repair its prerequisite. Retrying unchanged bytes after the same
31
+ failure supplies no new evidence.
32
+
33
+ For a Guardian incident, read `dshx kb cat contracts/creator-guardian`. Inspect
34
+ confidence, attribution, quarantine and log evidence. Repair source, build/check,
35
+ then retry the original activation branch. Guardian recovery is not functional
36
+ acceptance. Whole-plugin teardown remains the fixed remover's responsibility.
37
+
38
+ If a fixed tool throws `refusing an operation outside bridge v2`, report the
39
+ bridge integrity defect and preserve source. Repair/upgrade the bridge from the
40
+ external supervisor, then retry that fixed tool. Manual profile mutation, moving
41
+ the project or changing shells cannot repair its allowlist.
42
+
43
+ A failed hot reload clears its previous module receipt. Retry after changed
44
+ source or a resolved prerequisite. A launcher restart requires separate concrete
45
+ boot-boundary/recovery evidence and the corresponding authorization.
46
+
47
+ ## Resume
48
+
49
+ After an authorized normal launcher restart, return to this conversation and call
50
+ `dshx_status`. Continue its recorded delivery and verify the current Host;
51
+ historical module proof belongs to its recorded PID. Keep the single real
52
+ `DSH_HOME` and original launcher. Cold-boot tests use a temporary Home and remove
53
+ the transient Host afterward. Preserve an explicit live user acceptance result
54
+ instead of restarting speculative diagnosis.
@@ -0,0 +1,292 @@
1
+ import {
2
+ chmodSync,
3
+ cpSync,
4
+ existsSync,
5
+ mkdirSync,
6
+ mkdtempSync,
7
+ readFileSync,
8
+ readdirSync,
9
+ renameSync,
10
+ rmSync,
11
+ statSync,
12
+ writeFileSync,
13
+ } from 'node:fs'
14
+ import { homedir } from 'node:os'
15
+ import { dirname, join, resolve } from 'node:path'
16
+ import { fileURLToPath, pathToFileURL } from 'node:url'
17
+ import { migratePersona015 } from '../src/preset-015.js'
18
+ import { inspectDshxCompatibility, resolveHarnessRoot } from '../src/runner.js'
19
+
20
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..')
21
+ const CURRENT_PRESET_ID = 'creator-mode-plus'
22
+ const LEGACY_PRESET_ID = 'creator-plus'
23
+ const CURRENT_ROW = `- id: dsh-creator-mode-plus\n name: dsh-creator-mode-plus`
24
+ const LEGACY_ROWS = [
25
+ `- id: dshx-creator-plus\n name: dsh-external-plugin-devkit/creator-plus`,
26
+ `- id: dshx-creator-plus\n name: dsh-external-plugin-devkit`,
27
+ ]
28
+ const LEGACY_SIX_TOOL_PERSONA = 'Create file-backed DeepSeek Harness plugins through the six-tool DSHX v0.7 fixed bridge. Treat the official browser WebUI and public Cordis/client extension points as the compatibility target. App-shell APIs and wrapper-specific behavior are outside the supported surface. Harness update planning is read-only inside this session; prepare, verify, apply, rollback, and process control belong to the external DSHX supervisor.'
29
+ const SEVEN_TOOL_PERSONA = 'Create and safely remove file-backed DeepSeek Harness plugins through the seven-tool DSHX v0.7 fixed bridge. Treat the official browser WebUI and public Cordis/client extension points as the compatibility target. App-shell APIs and wrapper-specific behavior are outside the supported surface. Whole-plugin teardown must use dshx_remove_plugin so the live Host deactivates first and source is preserved. Harness update planning is read-only inside this session; prepare, verify, apply, rollback, and process control belong to the external DSHX supervisor.'
30
+ const CURRENT_PERSONA = `${SEVEN_TOOL_PERSONA} DSH.app, direct dsh web, and dshx are launchers for one long-lived Web Host per DSH_HOME; never start a second same-Home Host or keep an isolated verifier alive.`
31
+ const LEGACY_SIX_TOOL_COMMENT = '# Bridge v2: six fixed dshx tools plus external Guardian lifecycle hooks; no shell, arbitrary argv, or model process control.'
32
+ const SEVEN_TOOL_COMMENT = '# Bridge v2: seven fixed dshx tools plus external Guardian lifecycle hooks; no arbitrary argv, raw plugin teardown, or model process control.'
33
+
34
+ const STANDARD_PRESET_PATHS = [
35
+ 'packages/preset/agent-presets/presets/standard',
36
+ 'apps/cli/config/agent-presets/standard',
37
+ ]
38
+
39
+ export function standardPresetAt(root) {
40
+ for (const relative of STANDARD_PRESET_PATHS) {
41
+ const path = join(root, relative)
42
+ if (existsSync(join(path, 'agent.cordis.yml')) && existsSync(join(path, 'preset.yml'))) return path
43
+ }
44
+ throw new Error(`Creator Mode+ installer cannot find the shipped Standard preset under ${root}`)
45
+ }
46
+
47
+ function replaceOnce(text, search, replacement, label) {
48
+ const first = text.indexOf(search)
49
+ if (first < 0 || text.indexOf(search, first + search.length) >= 0) {
50
+ throw new Error(`Creator Mode+ installer expected exactly one ${label} block in the Standard preset`)
51
+ }
52
+ return text.slice(0, first) + replacement + text.slice(first + search.length)
53
+ }
54
+
55
+ function tightenTree(path) {
56
+ const info = statSync(path)
57
+ if (info.isDirectory()) {
58
+ chmodSync(path, 0o700)
59
+ for (const name of readdirSync(path)) tightenTree(join(path, name))
60
+ return
61
+ }
62
+ chmodSync(path, info.mode & 0o111 ? 0o700 : 0o600)
63
+ }
64
+
65
+ function exactRowCount(text, row) {
66
+ let count = 0
67
+ let offset = 0
68
+ while (offset <= text.length) {
69
+ const index = text.indexOf(row, offset)
70
+ if (index < 0) break
71
+ const next = text[index + row.length]
72
+ if (next === undefined || next === '\n' || next === '\r') count += 1
73
+ offset = index + row.length
74
+ }
75
+ return count
76
+ }
77
+
78
+ const PERSONA_PREFIX = ` prefix: |-\n You are Creator Mode+, a coding agent powered by the {{model}} model.\n\n ${CURRENT_PERSONA}\n\n Load the \`creator-mode-plus\` skill before creating, activating, removing, hot-reloading, updating Harness, or validating a DSH plugin. Keep Harness core and shipped presets unchanged.`
79
+ const PERSONA_RC2 = ` suffix: Your working directory is {{cwd}}.\n prefix: >-\n You are a coding agent powered by the {{model}} model.`
80
+ const PERSONA_RC2_REPLACEMENT = ` suffix: Your working directory is {{cwd}}.\n${PERSONA_PREFIX}`
81
+ const PERSONA_RC1 = ` text: >-\n You are a coding agent powered by the {{model}} model. Your working directory is {{cwd}}.`
82
+ const PERSONA_RC1_REPLACEMENT = `${PERSONA_PREFIX}\n suffix: Your working directory is {{cwd}}.`
83
+
84
+ function rewritePersona(standard) {
85
+ if (standard.includes(PERSONA_RC2)) {
86
+ return replaceOnce(standard, PERSONA_RC2, PERSONA_RC2_REPLACEMENT, 'persona')
87
+ }
88
+ if (standard.includes(PERSONA_RC1)) {
89
+ return replaceOnce(standard, PERSONA_RC1, PERSONA_RC1_REPLACEMENT, 'persona')
90
+ }
91
+ throw new Error('Creator Mode+ installer expected a 0.1.5 prefix/suffix or legacy text persona in the Standard preset')
92
+ }
93
+
94
+ function creatorComposition(standard) {
95
+ let text = replaceOnce(
96
+ standard,
97
+ '# The `standard` agent preset: the full coding agent, mounted once per process.',
98
+ '# Creator Mode+ starts from the shipped Standard preset and adds the fixed dshx bridge.',
99
+ 'preset heading',
100
+ )
101
+ text = rewritePersona(text)
102
+ text = replaceOnce(
103
+ text,
104
+ `- id: skill-filesystem\n name: '@deepseek-ai/dsh-skill-filesystem'`,
105
+ `- id: skill-filesystem\n name: '@deepseek-ai/dsh-skill-filesystem'\n config:\n customSkillDirs:\n - !!js "process.getBuiltinModule('node:url').fileURLToPath(new URL('skills/', baseUrl))"`,
106
+ 'skill filesystem',
107
+ )
108
+ return replaceOnce(
109
+ text,
110
+ `- id: tool-skill\n name: '@deepseek-ai/dsh-tool-skill'`,
111
+ `- id: tool-skill\n name: '@deepseek-ai/dsh-tool-skill'\n\n# Bridge v2: seven fixed dshx tools plus external Guardian lifecycle hooks; no arbitrary argv, raw plugin teardown, or model process control.\n${CURRENT_ROW}`,
112
+ 'tool skill',
113
+ )
114
+ }
115
+
116
+ function migrateManagedSafetyCopy(text) {
117
+ let next = migratePersona015(text)
118
+ for (const [before, after] of [
119
+ [LEGACY_SIX_TOOL_PERSONA, SEVEN_TOOL_PERSONA],
120
+ [SEVEN_TOOL_PERSONA, CURRENT_PERSONA],
121
+ [LEGACY_SIX_TOOL_COMMENT, SEVEN_TOOL_COMMENT],
122
+ ]) {
123
+ if (before === SEVEN_TOOL_PERSONA && next.includes(CURRENT_PERSONA)) continue
124
+ const first = next.indexOf(before)
125
+ if (first < 0) continue
126
+ if (next.indexOf(before, first + before.length) >= 0) {
127
+ throw new Error('Creator Mode+ preset contains duplicate legacy managed safety text; refusing an unsafe update')
128
+ }
129
+ next = next.slice(0, first) + after + next.slice(first + before.length)
130
+ }
131
+ return next
132
+ }
133
+
134
+ function refreshManagedAssets(target, root, migrateLegacy) {
135
+ const compositionPath = join(target, 'agent.cordis.yml')
136
+ const originalComposition = existsSync(compositionPath) ? readFileSync(compositionPath, 'utf8') : ''
137
+ let composition = originalComposition
138
+ const currentCount = exactRowCount(composition, CURRENT_ROW)
139
+ const matchingLegacyRows = LEGACY_ROWS.filter(row => exactRowCount(composition, row) === 1)
140
+ const legacyCount = LEGACY_ROWS.reduce((count, row) => count + exactRowCount(composition, row), 0)
141
+
142
+ if (currentCount === 1 && legacyCount === 0) {
143
+ // Current composition remains user-owned; only managed assets are refreshed.
144
+ } else if (currentCount === 0 && legacyCount === 1 && migrateLegacy) {
145
+ composition = composition.replace(matchingLegacyRows[0], CURRENT_ROW)
146
+ } else if (legacyCount === 1 && !migrateLegacy) {
147
+ throw new Error('legacy bundled Creator Mode+ found; rerun with --migrate-legacy after adding dsh-creator-mode-plus to the Web profile')
148
+ } else {
149
+ throw new Error('Creator Mode+ preset does not contain exactly one recognized managed plugin row; refusing an unsafe update')
150
+ }
151
+ composition = migrateManagedSafetyCopy(composition)
152
+
153
+ const temporaryRoot = mkdtempSync(join(root, '.dsh-creator-mode-plus-upgrade-'))
154
+ const staging = join(temporaryRoot, 'next')
155
+ const backup = join(temporaryRoot, 'previous')
156
+ let movedOriginal = false
157
+ let installedNext = false
158
+ try {
159
+ cpSync(target, staging, { recursive: true, errorOnExist: true, preserveTimestamps: true })
160
+ if (composition !== originalComposition) writeFileSync(join(staging, 'agent.cordis.yml'), composition)
161
+ rmSync(join(staging, 'skills/creator-mode-plus'), { recursive: true, force: true })
162
+ cpSync(
163
+ join(packageRoot, 'preset/skills/creator-mode-plus'),
164
+ join(staging, 'skills/creator-mode-plus'),
165
+ { recursive: true, errorOnExist: true },
166
+ )
167
+ cpSync(join(packageRoot, 'preset/preset.yml'), join(staging, 'preset.yml'), { force: true })
168
+ tightenTree(join(staging, 'skills/creator-mode-plus'))
169
+ tightenTree(join(staging, 'preset.yml'))
170
+
171
+ renameSync(target, backup)
172
+ movedOriginal = true
173
+ renameSync(staging, target)
174
+ installedNext = true
175
+ if (composition === originalComposition) {
176
+ rmSync(join(target, 'agent.cordis.yml'))
177
+ renameSync(join(backup, 'agent.cordis.yml'), join(target, 'agent.cordis.yml'))
178
+ }
179
+ } catch (error) {
180
+ if (installedNext && existsSync(target)) rmSync(target, { recursive: true, force: true })
181
+ if (movedOriginal && existsSync(backup)) renameSync(backup, target)
182
+ throw error
183
+ } finally {
184
+ rmSync(temporaryRoot, { recursive: true, force: true })
185
+ }
186
+ }
187
+
188
+ /** Install or safely refresh the user-owned Creator Mode+ preset. */
189
+ export function installCreatorModePlus(options = {}) {
190
+ const harnessRoot = resolveHarnessRoot({
191
+ harnessRoot: options.harnessRoot,
192
+ envRoot: options.envRoot,
193
+ configFile: options.configFile,
194
+ cwd: options.cwd,
195
+ moduleDir: options.moduleDir,
196
+ })
197
+ const compatibility = inspectDshxCompatibility(harnessRoot)
198
+ const source = standardPresetAt(harnessRoot)
199
+ const dshHome = resolve(options.dshHome || process.env.DSH_HOME || join(homedir(), '.dsh'))
200
+ const root = join(dshHome, '.agent-presets')
201
+ const currentTarget = join(root, CURRENT_PRESET_ID)
202
+ const legacyTarget = join(root, LEGACY_PRESET_ID)
203
+ const currentExists = existsSync(currentTarget)
204
+ const legacyExists = existsSync(legacyTarget)
205
+ const result = (target, action) => ({
206
+ target,
207
+ action,
208
+ dshxVersion: compatibility.dshxVersion,
209
+ creatorBridgeVersion: compatibility.creatorBridgeVersion,
210
+ dshxContract: compatibility.contractId,
211
+ })
212
+
213
+ if (currentExists && legacyExists) {
214
+ throw new Error(`both ${currentTarget} and ${legacyTarget} exist; refusing to choose or overwrite either preset`)
215
+ }
216
+ mkdirSync(root, { recursive: true })
217
+
218
+ if (currentExists) {
219
+ if (!options.upgrade && !options.migrateLegacy) {
220
+ throw new Error(`Creator Mode+ already exists at ${currentTarget}; pass --upgrade to refresh only managed assets`)
221
+ }
222
+ refreshManagedAssets(currentTarget, root, false)
223
+ return result(currentTarget, 'updated')
224
+ }
225
+
226
+ if (legacyExists) {
227
+ if (!options.migrateLegacy) {
228
+ throw new Error(`legacy Creator Mode+ exists at ${legacyTarget}; pass --migrate-legacy after adding the standalone package`)
229
+ }
230
+ refreshManagedAssets(legacyTarget, root, true)
231
+ return result(legacyTarget, 'migrated')
232
+ }
233
+
234
+ if (options.upgrade || options.migrateLegacy) {
235
+ throw new Error('no existing Creator Mode+ preset found to update or migrate')
236
+ }
237
+
238
+ const temporaryRoot = mkdtempSync(join(root, '.dsh-creator-mode-plus-install-'))
239
+ const staging = join(temporaryRoot, CURRENT_PRESET_ID)
240
+ try {
241
+ cpSync(source, staging, { recursive: true, errorOnExist: true })
242
+ const compositionPath = join(staging, 'agent.cordis.yml')
243
+ writeFileSync(compositionPath, creatorComposition(readFileSync(compositionPath, 'utf8')))
244
+ cpSync(join(packageRoot, 'preset/preset.yml'), join(staging, 'preset.yml'), { force: true })
245
+ cpSync(join(packageRoot, 'preset/skills'), join(staging, 'skills'), { recursive: true, force: true })
246
+ tightenTree(staging)
247
+ renameSync(staging, currentTarget)
248
+ } finally {
249
+ rmSync(temporaryRoot, { recursive: true, force: true })
250
+ }
251
+ return result(currentTarget, 'installed')
252
+ }
253
+
254
+ function parseArguments(argv) {
255
+ const options = { upgrade: false, migrateLegacy: false }
256
+ for (let index = 0; index < argv.length; index += 1) {
257
+ const token = argv[index]
258
+ if (token === '--upgrade') options.upgrade = true
259
+ else if (token === '--migrate-legacy') options.migrateLegacy = true
260
+ else if (token === '--harness') {
261
+ const value = argv[index + 1]
262
+ if (!value || value.startsWith('--') || options.harnessRoot) {
263
+ throw new Error('--harness requires exactly one path')
264
+ }
265
+ options.harnessRoot = value
266
+ index += 1
267
+ } else if (token === '--help' || token === '-h') {
268
+ options.help = true
269
+ } else {
270
+ throw new Error(`unknown option: ${token}`)
271
+ }
272
+ }
273
+ return options
274
+ }
275
+
276
+ if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
277
+ try {
278
+ const options = parseArguments(process.argv.slice(2))
279
+ if (options.help) {
280
+ process.stdout.write('Usage: node scripts/install.mjs --harness <path> [--upgrade | --migrate-legacy]\n')
281
+ } else {
282
+ const result = installCreatorModePlus(options)
283
+ process.stdout.write(
284
+ `Creator Mode+ ${result.action} at ${result.target}\n`
285
+ + `DSHX ${result.dshxVersion}; bridge v${result.creatorBridgeVersion}; contract ${result.dshxContract}\n`,
286
+ )
287
+ }
288
+ } catch (error) {
289
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`)
290
+ process.exitCode = 2
291
+ }
292
+ }
@@ -0,0 +1,77 @@
1
+ import { spawnSync } from 'node:child_process'
2
+ import { resolve } from 'node:path'
3
+ import { pathToFileURL } from 'node:url'
4
+ import { resolveDshxRuntime } from '../src/runner.js'
5
+ import { DSHX_SURFACE_MARKERS } from '../src/compatibility.js'
6
+
7
+ function parseArguments(argv) {
8
+ let harnessRoot
9
+ for (let index = 0; index < argv.length; index += 1) {
10
+ const token = argv[index]
11
+ if (token === '--harness') {
12
+ const value = argv[index + 1]
13
+ if (!value || value.startsWith('--') || harnessRoot) throw new Error('--harness requires exactly one path')
14
+ harnessRoot = resolve(value)
15
+ index += 1
16
+ } else if (token === '--help' || token === '-h') {
17
+ return { help: true }
18
+ } else {
19
+ throw new Error(`unknown option: ${token}`)
20
+ }
21
+ }
22
+ return { harnessRoot }
23
+ }
24
+
25
+ /** Verify the actual checkout, not only a fabricated version string. */
26
+ export function verifyDshxAlignment(options = {}) {
27
+ const runtime = resolveDshxRuntime({
28
+ harnessRoot: options.harnessRoot,
29
+ envRoot: options.envRoot,
30
+ configFile: options.configFile,
31
+ cwd: options.cwd,
32
+ moduleDir: options.moduleDir,
33
+ loaderPath: options.loaderPath,
34
+ })
35
+
36
+ const version = spawnSync(process.execPath, ['--import', runtime.loader, runtime.cli, 'version'], {
37
+ cwd: runtime.root,
38
+ env: { ...process.env, DSHX_HARNESS: runtime.root },
39
+ encoding: 'utf8',
40
+ timeout: 15_000,
41
+ })
42
+ if (version.error) throw version.error
43
+ if (version.status !== 0) {
44
+ throw new Error(`DSHX version probe failed (${version.status}): ${(version.stderr || version.stdout).trim()}`)
45
+ }
46
+ const expected = `dshx ${runtime.dshxVersion}`
47
+ if (version.stdout.trim() !== expected) {
48
+ throw new Error(`DSHX version probe mismatch: expected ${expected}, got ${version.stdout.trim() || '<empty>'}`)
49
+ }
50
+
51
+ return Object.freeze({
52
+ ok: true,
53
+ dshxVersion: runtime.dshxVersion,
54
+ creatorBridgeVersion: runtime.bridgeVersion,
55
+ dshxContract: runtime.contractId,
56
+ capabilities: runtime.capabilities,
57
+ harnessRoot: runtime.root,
58
+ cliVersionProbe: expected,
59
+ contractMarkers: Object.keys(DSHX_SURFACE_MARKERS),
60
+ })
61
+ }
62
+
63
+ if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
64
+ try {
65
+ const options = parseArguments(process.argv.slice(2))
66
+ if (options.help) {
67
+ process.stdout.write('Usage: node scripts/verify-dshx.mjs [--harness /absolute/path/to/deepseek-harness]\n')
68
+ } else {
69
+ const result = verifyDshxAlignment(options)
70
+ process.stdout.write('CREATOR_MODE_PLUS_DSHX_V078_COMPATIBILITY_PASS\n')
71
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`)
72
+ }
73
+ } catch (error) {
74
+ process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`)
75
+ process.exitCode = 1
76
+ }
77
+ }